Skip to content

Commit f89df6b

Browse files
committed
bugfix: Prevent default TCP checks from being re-added on reload/restart (#23088)
* fix(agent): prevents default TCP checks from being re-added on service reload when they were explicitly disabled or when custom checks were specified during initial registration.
1 parent 6109e36 commit f89df6b

File tree

3 files changed

+130
-19
lines changed

3 files changed

+130
-19
lines changed

.changelog/23088.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:bug
2+
agent: fix bug prevents default TCP checks from being re-added on service reload when they were explicitly disabled or when custom checks were specified during initial registration.
3+
```

agent/agent.go

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2159,22 +2159,27 @@ type persistedService struct {
21592159
// to exclude it from API output, but we need it to properly deregister
21602160
// persisted sidecars.
21612161
LocallyRegisteredAsSidecar bool `json:",omitempty"`
2162+
// we need it tracks whether default sidecar checks (TCP + alias) were
2163+
// disabled at registration time. Persisted to maintain consistent check configuration
2164+
// across agent restarts/reloads. Excluded from API responses but required for state restoration.
2165+
DisableSidecarDefaultChecks bool `json:",omitempty"`
21622166
}
21632167

21642168
func (a *Agent) makeServiceFilePath(svcID structs.ServiceID) string {
21652169
return filepath.Join(a.config.DataDir, servicesDir, svcID.StringHashSHA256())
21662170
}
21672171

21682172
// persistService saves a service definition to a JSON file in the data dir
2169-
func (a *Agent) persistService(service *structs.NodeService, source configSource) error {
2173+
func (a *Agent) persistService(service *structs.NodeService, source configSource, disableSidecarDefaultChecks bool) error {
21702174
svcID := service.CompoundServiceID()
21712175
svcPath := a.makeServiceFilePath(svcID)
21722176

21732177
wrapped := persistedService{
2174-
Token: a.State.ServiceToken(service.CompoundServiceID()),
2175-
Service: service,
2176-
Source: source.String(),
2177-
LocallyRegisteredAsSidecar: service.LocallyRegisteredAsSidecar,
2178+
Token: a.State.ServiceToken(service.CompoundServiceID()),
2179+
Service: service,
2180+
Source: source.String(),
2181+
LocallyRegisteredAsSidecar: service.LocallyRegisteredAsSidecar,
2182+
DisableSidecarDefaultChecks: disableSidecarDefaultChecks,
21782183
}
21792184
encoded, err := json.Marshal(wrapped)
21802185
if err != nil {
@@ -2374,8 +2379,10 @@ func (a *Agent) addServiceLocked(req addServiceLockedRequest) error {
23742379
req.Service.Port = port
23752380
}
23762381
// Setup default check if none given.
2377-
if len(req.chkTypes) < 1 {
2382+
if len(req.chkTypes) < 1 && !req.disableSidecarDefaultChecks {
23782383
req.chkTypes = sidecarDefaultChecks(req.Service.ID, req.Service.Address, req.Service.Proxy.LocalServiceAddress, req.Service.Port)
2384+
} else {
2385+
req.disableSidecarDefaultChecks = true
23792386
}
23802387
}
23812388

@@ -2416,12 +2423,13 @@ type addServiceLockedRequest struct {
24162423
// AddServiceRequest contains the fields used to register a service on the local
24172424
// agent using Agent.AddService.
24182425
type AddServiceRequest struct {
2419-
Service *structs.NodeService
2420-
chkTypes []*structs.CheckType
2421-
persist bool
2422-
token string
2423-
replaceExistingChecks bool
2424-
Source configSource
2426+
Service *structs.NodeService
2427+
chkTypes []*structs.CheckType
2428+
persist bool
2429+
token string
2430+
replaceExistingChecks bool
2431+
Source configSource
2432+
disableSidecarDefaultChecks bool
24252433
}
24262434

24272435
type addServiceInternalRequest struct {
@@ -2614,7 +2622,7 @@ func (a *Agent) addServiceInternal(req addServiceInternalRequest) error {
26142622
req.persistService = service
26152623
}
26162624

2617-
if err := a.persistService(req.persistService, source); err != nil {
2625+
if err := a.persistService(req.persistService, source, req.disableSidecarDefaultChecks); err != nil {
26182626
a.cleanupRegistration(cleanupServices, cleanupChecks)
26192627
return err
26202628
}
@@ -3882,12 +3890,13 @@ func (a *Agent) loadServices(conf *config.RuntimeConfig, snap map[structs.CheckI
38823890
)
38833891
err = a.addServiceLocked(addServiceLockedRequest{
38843892
AddServiceRequest: AddServiceRequest{
3885-
Service: p.Service,
3886-
chkTypes: nil,
3887-
persist: false, // don't rewrite the file with the same data we just read
3888-
token: p.Token,
3889-
replaceExistingChecks: false, // do default behavior
3890-
Source: source,
3893+
Service: p.Service,
3894+
chkTypes: nil,
3895+
persist: false, // don't rewrite the file with the same data we just read
3896+
token: p.Token,
3897+
replaceExistingChecks: false, // do default behavior
3898+
Source: source,
3899+
disableSidecarDefaultChecks: p.DisableSidecarDefaultChecks,
38913900
},
38923901
serviceDefaults: serviceDefaultsFromStruct(persistedServiceConfigs[serviceID]),
38933902
persistServiceConfig: false, // don't rewrite the file with the same data we just read

agent/agent_test.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2469,6 +2469,105 @@ func testAgent_PersistService(t *testing.T, extraHCL string) {
24692469
}
24702470
}
24712471

2472+
func TestAgent_Reload_HonorsDisableDefaultSidecarChecks(t *testing.T) {
2473+
if testing.Short() {
2474+
t.Skip("too slow for testing.Short")
2475+
}
2476+
2477+
t.Run("no custom checks - should add default TCP and alias checks", func(t *testing.T) {
2478+
t.Parallel()
2479+
testAgent_Reload_HonorsDisableDefaultSidecarChecks(t, nil, false)
2480+
})
2481+
2482+
t.Run("with custom alias check - should not add default checks", func(t *testing.T) {
2483+
t.Parallel()
2484+
testAgent_Reload_HonorsDisableDefaultSidecarChecks(t, []*structs.CheckType{
2485+
{
2486+
Name: "Custom Connect Sidecar Aliasing redis",
2487+
AliasService: "redis",
2488+
},
2489+
}, true)
2490+
})
2491+
}
2492+
2493+
func testAgent_Reload_HonorsDisableDefaultSidecarChecks(t *testing.T, checks []*structs.CheckType, disable_default_tcp_check bool) {
2494+
t.Helper()
2495+
2496+
cfg := `
2497+
server = false
2498+
bootstrap = false
2499+
`
2500+
a := StartTestAgent(t, TestAgent{HCL: cfg})
2501+
defer a.Shutdown()
2502+
2503+
svc := &structs.NodeService{
2504+
Kind: "connect-proxy",
2505+
ID: "redis-proxy",
2506+
Service: "redis",
2507+
Tags: []string{"foo"},
2508+
Port: 8000,
2509+
LocallyRegisteredAsSidecar: true,
2510+
}
2511+
2512+
file := filepath.Join(a.Config.DataDir, servicesDir, structs.NewServiceID(svc.ID, nil).StringHashSHA256())
2513+
2514+
if err := a.addServiceFromSource(svc, checks, true, "mytoken", ConfigSourceLocal); err != nil {
2515+
t.Fatalf("err: %v", err)
2516+
}
2517+
2518+
expected, err := json.Marshal(persistedService{
2519+
Token: "mytoken",
2520+
Service: svc,
2521+
Source: "local",
2522+
LocallyRegisteredAsSidecar: true,
2523+
DisableSidecarDefaultChecks: disable_default_tcp_check,
2524+
})
2525+
if err != nil {
2526+
t.Fatalf("err: %s", err)
2527+
}
2528+
content, err := os.ReadFile(file)
2529+
if err != nil {
2530+
t.Fatalf("err: %s", err)
2531+
}
2532+
if !bytes.Equal(expected, content) {
2533+
t.Fatalf("bad: %s %s", string(expected), string(content))
2534+
}
2535+
2536+
// Shutdown the agent
2537+
a.Shutdown()
2538+
2539+
// Restart the agent
2540+
a2 := StartTestAgent(t, TestAgent{HCL: cfg, DataDir: a.DataDir})
2541+
defer a2.Shutdown()
2542+
2543+
restored := a2.State.ServiceState(structs.NewServiceID(svc.ID, nil))
2544+
if restored == nil {
2545+
t.Fatalf("service %q missing", svc.ID)
2546+
}
2547+
// check service does not have default sidecar TCP checks after restart
2548+
checkStateSnapshot := a2.State.AllChecks()
2549+
if disable_default_tcp_check {
2550+
require.Len(t, checkStateSnapshot, 1)
2551+
} else {
2552+
require.Len(t, checkStateSnapshot, 2)
2553+
}
2554+
2555+
// Now reload the config and ensure the flag is still honored
2556+
a2.reloadConfig(false)
2557+
2558+
reloadRestored := a2.State.ServiceState(structs.NewServiceID(svc.ID, nil))
2559+
if reloadRestored == nil {
2560+
t.Fatalf("service %q missing", svc.ID)
2561+
}
2562+
// check service does not have default sidecar TCP checks after config reload
2563+
checkStateSnapshotAfterReload := a2.State.AllChecks()
2564+
if disable_default_tcp_check {
2565+
require.Len(t, checkStateSnapshotAfterReload, 1)
2566+
} else {
2567+
require.Len(t, checkStateSnapshotAfterReload, 2)
2568+
}
2569+
}
2570+
24722571
func TestAgent_persistedService_compat(t *testing.T) {
24732572
if testing.Short() {
24742573
t.Skip("too slow for testing.Short")

0 commit comments

Comments
 (0)