mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-15 07:27:30 +00:00
health: add an ETag to UnhealthyState for change detection
Updates tailscale/corp#30596 Signed-off-by: James Sanderson <jsanderson@tailscale.com>
This commit is contained in:

committed by
James 'zofrex' Sanderson

parent
5154bbb0b3
commit
5731869565
@@ -13,8 +13,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tstest"
|
||||
"tailscale.com/tstime"
|
||||
"tailscale.com/types/opt"
|
||||
"tailscale.com/util/usermetric"
|
||||
"tailscale.com/version"
|
||||
@@ -517,7 +519,7 @@ func TestControlHealth(t *testing.T) {
|
||||
delete(gotWarns, k)
|
||||
}
|
||||
}
|
||||
if diff := cmp.Diff(wantWarns, gotWarns); diff != "" {
|
||||
if diff := cmp.Diff(wantWarns, gotWarns, cmpopts.IgnoreFields(UnhealthyState{}, "ETag")); diff != "" {
|
||||
t.Fatalf(`CurrentState().Warnings["control-health-*"] wrong (-want +got):\n%s`, diff)
|
||||
}
|
||||
})
|
||||
@@ -664,3 +666,175 @@ func TestControlHealthIgnoredOutsideMapPoll(t *testing.T) {
|
||||
t.Error("watcher got called, want it to not be called")
|
||||
}
|
||||
}
|
||||
|
||||
// TestCurrentStateETagControlHealth tests that the ETag on an [UnhealthyState]
|
||||
// created from Control health & returned by [Tracker.CurrentState] is different
|
||||
// when the details of the [tailcfg.DisplayMessage] are different.
|
||||
func TestCurrentStateETagControlHealth(t *testing.T) {
|
||||
ht := Tracker{}
|
||||
ht.SetIPNState("NeedsLogin", true)
|
||||
ht.GotStreamedMapResponse()
|
||||
|
||||
msg := tailcfg.DisplayMessage{
|
||||
Title: "Test Warning",
|
||||
Text: "This is a test warning.",
|
||||
Severity: tailcfg.SeverityHigh,
|
||||
ImpactsConnectivity: true,
|
||||
PrimaryAction: &tailcfg.DisplayMessageAction{
|
||||
URL: "https://example.com/",
|
||||
Label: "open",
|
||||
},
|
||||
}
|
||||
|
||||
type test struct {
|
||||
name string
|
||||
change func(tailcfg.DisplayMessage) tailcfg.DisplayMessage
|
||||
wantChangedETag bool
|
||||
}
|
||||
tests := []test{
|
||||
{
|
||||
name: "same_value",
|
||||
change: func(m tailcfg.DisplayMessage) tailcfg.DisplayMessage { return m },
|
||||
wantChangedETag: false,
|
||||
},
|
||||
{
|
||||
name: "different_severity",
|
||||
change: func(m tailcfg.DisplayMessage) tailcfg.DisplayMessage {
|
||||
m.Severity = tailcfg.SeverityLow
|
||||
return m
|
||||
},
|
||||
wantChangedETag: true,
|
||||
},
|
||||
{
|
||||
name: "different_title",
|
||||
change: func(m tailcfg.DisplayMessage) tailcfg.DisplayMessage {
|
||||
m.Title = "Different Title"
|
||||
return m
|
||||
},
|
||||
wantChangedETag: true,
|
||||
},
|
||||
{
|
||||
name: "different_text",
|
||||
change: func(m tailcfg.DisplayMessage) tailcfg.DisplayMessage {
|
||||
m.Text = "This is a different text."
|
||||
return m
|
||||
},
|
||||
wantChangedETag: true,
|
||||
},
|
||||
{
|
||||
name: "different_impacts_connectivity",
|
||||
change: func(m tailcfg.DisplayMessage) tailcfg.DisplayMessage {
|
||||
m.ImpactsConnectivity = false
|
||||
return m
|
||||
},
|
||||
wantChangedETag: true,
|
||||
},
|
||||
{
|
||||
name: "different_primary_action_label",
|
||||
change: func(m tailcfg.DisplayMessage) tailcfg.DisplayMessage {
|
||||
m.PrimaryAction.Label = "new_label"
|
||||
return m
|
||||
},
|
||||
wantChangedETag: true,
|
||||
},
|
||||
{
|
||||
name: "different_primary_action_url",
|
||||
change: func(m tailcfg.DisplayMessage) tailcfg.DisplayMessage {
|
||||
m.PrimaryAction.URL = "https://new.example.com/"
|
||||
return m
|
||||
},
|
||||
wantChangedETag: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ht.SetControlHealth(map[tailcfg.DisplayMessageID]tailcfg.DisplayMessage{
|
||||
"test-message": msg,
|
||||
})
|
||||
state := ht.CurrentState().Warnings["control-health.test-message"]
|
||||
|
||||
newMsg := test.change(msg)
|
||||
ht.SetControlHealth(map[tailcfg.DisplayMessageID]tailcfg.DisplayMessage{
|
||||
"test-message": newMsg,
|
||||
})
|
||||
newState := ht.CurrentState().Warnings["control-health.test-message"]
|
||||
|
||||
if (state.ETag != newState.ETag) != test.wantChangedETag {
|
||||
if test.wantChangedETag {
|
||||
t.Errorf("got unchanged ETag, want changed (ETag was %q)", newState.ETag)
|
||||
} else {
|
||||
t.Errorf("got changed ETag, want unchanged")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestCurrentStateETagWarnable tests that the ETag on an [UnhealthyState]
|
||||
// created from a Warnable & returned by [Tracker.CurrentState] is different
|
||||
// when the details of the Warnable are different.
|
||||
func TestCurrentStateETagWarnable(t *testing.T) {
|
||||
newTracker := func(clock tstime.Clock) *Tracker {
|
||||
ht := &Tracker{
|
||||
testClock: clock,
|
||||
}
|
||||
ht.SetIPNState("NeedsLogin", true)
|
||||
ht.GotStreamedMapResponse()
|
||||
return ht
|
||||
}
|
||||
|
||||
t.Run("new_args", func(t *testing.T) {
|
||||
ht := newTracker(nil)
|
||||
|
||||
ht.SetUnhealthy(testWarnable, Args{ArgError: "initial value"})
|
||||
state := ht.CurrentState().Warnings[testWarnable.Code]
|
||||
|
||||
ht.SetUnhealthy(testWarnable, Args{ArgError: "new value"})
|
||||
newState := ht.CurrentState().Warnings[testWarnable.Code]
|
||||
|
||||
if state.ETag == newState.ETag {
|
||||
t.Errorf("got unchanged ETag, want changed (ETag was %q)", newState.ETag)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("new_broken_since", func(t *testing.T) {
|
||||
clock1 := tstest.NewClock(tstest.ClockOpts{
|
||||
Start: time.Unix(123, 0),
|
||||
})
|
||||
ht1 := newTracker(clock1)
|
||||
|
||||
ht1.SetUnhealthy(testWarnable, Args{})
|
||||
state := ht1.CurrentState().Warnings[testWarnable.Code]
|
||||
|
||||
// Use a second tracker to get a different broken since time
|
||||
clock2 := tstest.NewClock(tstest.ClockOpts{
|
||||
Start: time.Unix(456, 0),
|
||||
})
|
||||
ht2 := newTracker(clock2)
|
||||
|
||||
ht2.SetUnhealthy(testWarnable, Args{})
|
||||
newState := ht2.CurrentState().Warnings[testWarnable.Code]
|
||||
|
||||
if state.ETag == newState.ETag {
|
||||
t.Errorf("got unchanged ETag, want changed (ETag was %q)", newState.ETag)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("no_change", func(t *testing.T) {
|
||||
clock := tstest.NewClock(tstest.ClockOpts{})
|
||||
ht1 := newTracker(clock)
|
||||
|
||||
ht1.SetUnhealthy(testWarnable, Args{})
|
||||
state := ht1.CurrentState().Warnings[testWarnable.Code]
|
||||
|
||||
// Using a second tracker because SetUnhealthy with no changes is a no-op
|
||||
ht2 := newTracker(clock)
|
||||
ht2.SetUnhealthy(testWarnable, Args{})
|
||||
newState := ht2.CurrentState().Warnings[testWarnable.Code]
|
||||
|
||||
if state.ETag != newState.ETag {
|
||||
t.Errorf("got changed ETag, want unchanged")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user