Skip to content

Commit e02f709

Browse files
Restrict SetCapabilities after Start()
Only certain capabilities can be changed after Start(), see spec change: open-telemetry/opamp-spec#306
1 parent 1c36d7a commit e02f709

File tree

3 files changed

+102
-9
lines changed

3 files changed

+102
-9
lines changed

client/clientimpl_test.go

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1928,10 +1928,10 @@ func TestSetCapabilities(t *testing.T) {
19281928
}
19291929
})
19301930

1931-
newCapabilities = protobufs.AgentCapabilities_AgentCapabilities_AcceptsRestartCommand |
1931+
// Remove AcceptsRemoteConfig
1932+
newCapabilities =
19321933
protobufs.AgentCapabilities_AgentCapabilities_ReportsEffectiveConfig |
1933-
protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig |
1934-
protobufs.AgentCapabilities_AgentCapabilities_ReportsStatus
1934+
protobufs.AgentCapabilities_AgentCapabilities_ReportsStatus
19351935
newSetErr := client.SetCapabilities(&newCapabilities)
19361936
assert.NoError(t, newSetErr)
19371937

@@ -1941,10 +1941,8 @@ func TestSetCapabilities(t *testing.T) {
19411941
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_ReportsStatus != 0)
19421942
// ReportsEffectiveConfig should present.
19431943
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_ReportsEffectiveConfig != 0)
1944-
// AcceptsRemoteConfig should now be present
1945-
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig != 0)
1946-
// AcceptsRestartCommand should now be present
1947-
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_AcceptsRestartCommand != 0)
1944+
// AcceptsRemoteConfig should not be present
1945+
assert.False(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig != 0)
19481946
// Send a custom message response and ask client for full state again.
19491947
return &protobufs.ServerToAgent{
19501948
InstanceUid: msg.InstanceUid,
@@ -2630,7 +2628,7 @@ func TestSetAvailableComponents(t *testing.T) {
26302628
}
26312629
}
26322630

2633-
func TestValidateCapabilities(t *testing.T) {
2631+
func TestValidateCapabilitiesBeforeStart(t *testing.T) {
26342632
testCases := []struct {
26352633
name string
26362634
capabilities protobufs.AgentCapabilities
@@ -2714,7 +2712,89 @@ func TestValidateCapabilities(t *testing.T) {
27142712
tc.setupFunc(t, client)
27152713
require.NoError(t, client.SetAgentDescription(createAgentDescr()))
27162714

2717-
// Minimal caps for PrepareStart; validateCapabilities on SetCapabilities runs only once started.
2715+
caps := tc.capabilities
2716+
err := client.SetCapabilities(&caps)
2717+
require.NoError(t, err)
2718+
2719+
settings := types.StartSettings{
2720+
OpAMPServerURL: "ws://" + srv.Endpoint,
2721+
Callbacks: types.Callbacks{
2722+
OnMessage: func(ctx context.Context, msg *types.MessageData) {},
2723+
},
2724+
}
2725+
prepareSettings(t, &settings, client)
2726+
2727+
err = client.Start(context.Background(), settings)
2728+
assert.Equal(t, tc.expectedError, err)
2729+
2730+
if err == nil {
2731+
assert.NoError(t, client.Stop(context.Background()))
2732+
}
2733+
})
2734+
})
2735+
}
2736+
}
2737+
2738+
func TestSetCapabilitiesAfterStart(t *testing.T) {
2739+
testCases := []struct {
2740+
name string
2741+
capabilities protobufs.AgentCapabilities
2742+
setupFunc func(t *testing.T, client OpAMPClient)
2743+
expectedError error
2744+
}{
2745+
{
2746+
// These are the only capabilities that are permitted to be changed after Start()
2747+
name: "Change AcceptsRemoteConfig and ReportsRemoteConfig",
2748+
capabilities: protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig |
2749+
protobufs.AgentCapabilities_AgentCapabilities_ReportsRemoteConfig,
2750+
setupFunc: func(t *testing.T, client OpAMPClient) {
2751+
},
2752+
expectedError: nil,
2753+
},
2754+
{
2755+
name: "No capabilities set",
2756+
capabilities: protobufs.AgentCapabilities_AgentCapabilities_Unspecified,
2757+
setupFunc: func(t *testing.T, client OpAMPClient) {
2758+
// No setup needed
2759+
},
2760+
expectedError: nil,
2761+
},
2762+
// Test a few other attempts to change capabilities that must fail. We will create
2763+
// the correct condition for ErrCapabilityChangeNotPermitted to be returned
2764+
// instead of some other specific validation error. Those specific validation errors
2765+
// are checked by corresponding test cases in TestValidateCapabilitiesBeforeStart.
2766+
{
2767+
name: "Change ReportsHealth",
2768+
capabilities: protobufs.AgentCapabilities_AgentCapabilities_ReportsHealth,
2769+
setupFunc: func(t *testing.T, client OpAMPClient) {
2770+
// Need to SetHealth otherwise will get ErrHealthMissing
2771+
err := client.SetHealth(&protobufs.ComponentHealth{})
2772+
require.NoError(t, err)
2773+
},
2774+
expectedError: internal.ErrCapabilityChangeNotPermitted,
2775+
},
2776+
{
2777+
name: "Change ReportsAvailableComponents",
2778+
capabilities: protobufs.AgentCapabilities_AgentCapabilities_ReportsAvailableComponents,
2779+
setupFunc: func(t *testing.T, client OpAMPClient) {
2780+
// Need to SetAvailableComponents otherwise will get ErrAvailableComponentsMissing
2781+
err := client.SetAvailableComponents(generateTestAvailableComponents())
2782+
require.NoError(t, err)
2783+
},
2784+
expectedError: internal.ErrCapabilityChangeNotPermitted,
2785+
},
2786+
}
2787+
2788+
for _, tc := range testCases {
2789+
t.Run(tc.name, func(t *testing.T) {
2790+
testClients(t, func(t *testing.T, client OpAMPClient) {
2791+
srv := internal.StartMockServer(t)
2792+
defer srv.Close()
2793+
2794+
tc.setupFunc(t, client)
2795+
require.NoError(t, client.SetAgentDescription(createAgentDescr()))
2796+
2797+
// Minimal caps for PrepareStart; validateCapabilities runs only once started.
27182798
initialCaps := coreCapabilities
27192799
err := client.SetCapabilities(&initialCaps)
27202800
require.NoError(t, err)

client/internal/clientcommon.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ var (
2323
ErrCapabilitiesNotSet = errors.New("Capabilities is not set")
2424
ErrAcceptsPackagesNotSet = errors.New("AcceptsPackages and ReportsPackageStatuses must be set")
2525
ErrAvailableComponentsMissing = errors.New("AvailableComponents is nil")
26+
ErrCapabilityChangeNotPermitted = errors.New("only changes to AcceptsRemoteConfig and ReportsRemoteConfig capabilities are permitted after Start")
2627

2728
errAlreadyStarted = errors.New("already started")
2829
errCannotStopNotStarted = errors.New("cannot stop because not started")
@@ -555,6 +556,11 @@ func (c *ClientCommon) SetCapabilities(capabilities *protobufs.AgentCapabilities
555556
if err := c.validateCapabilities(*capabilities); err != nil {
556557
return err
557558
}
559+
// Verify that only permitted capabilities are changing.
560+
changedCapabilities := c.ClientSyncedState.Capabilities() ^ *capabilities
561+
if (changedCapabilities & ^permittedCapabilityChanges) != 0 {
562+
return ErrCapabilityChangeNotPermitted
563+
}
558564
}
559565
// store the capabilities to send
560566
if err := c.ClientSyncedState.SetCapabilities(capabilities); err != nil {

client/internal/consts.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
package internal
22

3+
import "github.com/open-telemetry/opamp-go/protobufs"
4+
35
const (
46
headerContentType = "Content-Type"
57
contentTypeProtobuf = "application/x-protobuf"
68
headerOpAMPInstanceUID = "OpAMP-Instance-UID"
79
)
10+
11+
// Only these capabilities are allowed to be changed after Start().
12+
// See https://github.com/open-telemetry/opamp-spec/pull/306
13+
const permittedCapabilityChanges = protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig |
14+
protobufs.AgentCapabilities_AgentCapabilities_ReportsRemoteConfig

0 commit comments

Comments
 (0)