Fix: service LocallyRegisteredAsSidecar property is not persisted
When a service is deregistered, we check whever matching services were registered as sidecar along with it and deregister them as well. To determine if a service is indeed a sidecar we check the structs.ServiceNode.LocallyRegisteredAsSidecar property. However, to avoid interal API leakage, it is excluded from JSON serialization, meaning it is not persisted to disk either. When the agent is restarted, this property lost and sidecars are no longer deregistered along with their parent service. To fix this, we now specifically save this property in the persisted service file.
This commit is contained in:
parent
5524a43f10
commit
a1ec792acc
3
.changelog/8924.txt
Normal file
3
.changelog/8924.txt
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
```release-note:bug
|
||||||
|
connect: fix connect sidecars registered via the API not being automatically deregistered with their parent service after an agent restart by persisting the LocallyRegisteredAsSidecar property.
|
||||||
|
```
|
|
@ -1701,6 +1701,11 @@ type persistedService struct {
|
||||||
Token string
|
Token string
|
||||||
Service *structs.NodeService
|
Service *structs.NodeService
|
||||||
Source string
|
Source string
|
||||||
|
// whether this service was registered as a sidecar, see structs.NodeService
|
||||||
|
// we store this field here because it is excluded from json serialization
|
||||||
|
// to exclude it from API output, but we need it to properly deregister
|
||||||
|
// persisted sidecars.
|
||||||
|
LocallyRegisteredAsSidecar bool `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// persistService saves a service definition to a JSON file in the data dir
|
// persistService saves a service definition to a JSON file in the data dir
|
||||||
|
@ -1709,9 +1714,10 @@ func (a *Agent) persistService(service *structs.NodeService, source configSource
|
||||||
svcPath := filepath.Join(a.config.DataDir, servicesDir, svcID.StringHash())
|
svcPath := filepath.Join(a.config.DataDir, servicesDir, svcID.StringHash())
|
||||||
|
|
||||||
wrapped := persistedService{
|
wrapped := persistedService{
|
||||||
Token: a.State.ServiceToken(service.CompoundServiceID()),
|
Token: a.State.ServiceToken(service.CompoundServiceID()),
|
||||||
Service: service,
|
Service: service,
|
||||||
Source: source.String(),
|
Source: source.String(),
|
||||||
|
LocallyRegisteredAsSidecar: service.LocallyRegisteredAsSidecar,
|
||||||
}
|
}
|
||||||
encoded, err := json.Marshal(wrapped)
|
encoded, err := json.Marshal(wrapped)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -3160,6 +3166,10 @@ func (a *Agent) loadServices(conf *config.RuntimeConfig, snap map[structs.CheckI
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore LocallyRegisteredAsSidecar, see persistedService.LocallyRegisteredAsSidecar
|
||||||
|
p.Service.LocallyRegisteredAsSidecar = p.LocallyRegisteredAsSidecar
|
||||||
|
|
||||||
serviceID := p.Service.CompoundServiceID()
|
serviceID := p.Service.CompoundServiceID()
|
||||||
|
|
||||||
source, ok := ConfigSourceFromName(p.Source)
|
source, ok := ConfigSourceFromName(p.Source)
|
||||||
|
|
|
@ -2442,6 +2442,75 @@ func TestAgent_PurgeCheckOnDuplicate(t *testing.T) {
|
||||||
require.Equal(t, expected, result)
|
require.Equal(t, expected, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAgent_DeregisterPersistedSidecarAfterRestart(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
nodeID := NodeID()
|
||||||
|
a := StartTestAgent(t, TestAgent{
|
||||||
|
HCL: `
|
||||||
|
node_id = "` + nodeID + `"
|
||||||
|
node_name = "Node ` + nodeID + `"
|
||||||
|
server = false
|
||||||
|
bootstrap = false
|
||||||
|
enable_central_service_config = false
|
||||||
|
`})
|
||||||
|
defer a.Shutdown()
|
||||||
|
|
||||||
|
srv := &structs.NodeService{
|
||||||
|
ID: "svc",
|
||||||
|
Service: "svc",
|
||||||
|
Weights: &structs.Weights{
|
||||||
|
Passing: 2,
|
||||||
|
Warning: 1,
|
||||||
|
},
|
||||||
|
Tags: []string{"tag2"},
|
||||||
|
Port: 8200,
|
||||||
|
EnterpriseMeta: *structs.DefaultEnterpriseMeta(),
|
||||||
|
|
||||||
|
Connect: structs.ServiceConnect{
|
||||||
|
SidecarService: &structs.ServiceDefinition{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
connectSrv, _, _, err := a.sidecarServiceFromNodeService(srv, "")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// First persist the check
|
||||||
|
err = a.AddService(srv, nil, true, "", ConfigSourceLocal)
|
||||||
|
require.NoError(t, err)
|
||||||
|
err = a.AddService(connectSrv, nil, true, "", ConfigSourceLocal)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// check both services were registered
|
||||||
|
require.NotNil(t, a.State.Service(srv.CompoundServiceID()))
|
||||||
|
require.NotNil(t, a.State.Service(connectSrv.CompoundServiceID()))
|
||||||
|
|
||||||
|
a.Shutdown()
|
||||||
|
|
||||||
|
// Start again with the check registered in config
|
||||||
|
a2 := StartTestAgent(t, TestAgent{
|
||||||
|
Name: "Agent2",
|
||||||
|
DataDir: a.DataDir,
|
||||||
|
HCL: `
|
||||||
|
node_id = "` + nodeID + `"
|
||||||
|
node_name = "Node ` + nodeID + `"
|
||||||
|
server = false
|
||||||
|
bootstrap = false
|
||||||
|
enable_central_service_config = false
|
||||||
|
`})
|
||||||
|
defer a2.Shutdown()
|
||||||
|
|
||||||
|
// check both services were restored
|
||||||
|
require.NotNil(t, a2.State.Service(srv.CompoundServiceID()))
|
||||||
|
require.NotNil(t, a2.State.Service(connectSrv.CompoundServiceID()))
|
||||||
|
|
||||||
|
err = a2.RemoveService(srv.CompoundServiceID())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// check both services were deregistered
|
||||||
|
require.Nil(t, a2.State.Service(srv.CompoundServiceID()))
|
||||||
|
require.Nil(t, a2.State.Service(connectSrv.CompoundServiceID()))
|
||||||
|
}
|
||||||
|
|
||||||
func TestAgent_loadChecks_token(t *testing.T) {
|
func TestAgent_loadChecks_token(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
a := NewTestAgent(t, `
|
a := NewTestAgent(t, `
|
||||||
|
|
Loading…
Reference in a new issue