-
Notifications
You must be signed in to change notification settings - Fork 86
Make it possible to set capabilities #366
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
adcb1be
cf4a028
21667d9
3f61b9c
4b81c39
a130307
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,8 @@ import ( | |
|
||
const retryAfterHTTPHeader = "Retry-After" | ||
|
||
var coreCapabilities = protobufs.AgentCapabilities_AgentCapabilities_ReportsStatus | ||
|
||
func createAgentDescr() *protobufs.AgentDescription { | ||
agentDescr := &protobufs.AgentDescription{ | ||
IdentifyingAttributes: []*protobufs.KeyValue{ | ||
|
@@ -129,6 +131,12 @@ func prepareClient(t *testing.T, settings *types.StartSettings, c OpAMPClient) { | |
prepareSettings(t, settings, c) | ||
err := c.SetAgentDescription(createAgentDescr()) | ||
assert.NoError(t, err) | ||
// We ignore the error here. | ||
if settings.Capabilities != 0 { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i had to ignore the error here because a later test depended on the checks happening in the start client |
||
c.SetCapabilities(&settings.Capabilities) | ||
} else { | ||
c.SetCapabilities(&coreCapabilities) | ||
} | ||
} | ||
|
||
func startClient(t *testing.T, settings types.StartSettings, client OpAMPClient) { | ||
|
@@ -218,13 +226,26 @@ func TestStopCancellation(t *testing.T) { | |
|
||
func TestStartNoDescription(t *testing.T) { | ||
testClients(t, func(t *testing.T, client OpAMPClient) { | ||
setErr := client.SetCapabilities(&coreCapabilities) | ||
assert.NoError(t, setErr) | ||
settings := createNoServerSettings() | ||
prepareSettings(t, &settings, client) | ||
err := client.Start(context.Background(), settings) | ||
assert.EqualValues(t, err, internal.ErrAgentDescriptionMissing) | ||
}) | ||
} | ||
|
||
func TestStartNoCapabilities(t *testing.T) { | ||
testClients(t, func(t *testing.T, client OpAMPClient) { | ||
setErr := client.SetAgentDescription(createAgentDescr()) | ||
require.NoError(t, setErr) | ||
settings := createNoServerSettings() | ||
prepareSettings(t, &settings, client) | ||
err := client.Start(context.Background(), settings) | ||
assert.EqualValues(t, internal.ErrCapabilitiesNotSet, err) | ||
}) | ||
} | ||
|
||
func TestSetInvalidAgentDescription(t *testing.T) { | ||
testClients(t, func(t *testing.T, client OpAMPClient) { | ||
settings := createNoServerSettings() | ||
|
@@ -611,7 +632,8 @@ func TestSetEffectiveConfig(t *testing.T) { | |
|
||
// Now change the config. | ||
sendConfig.ConfigMap.ConfigMap["key2"] = &protobufs.AgentConfigFile{} | ||
_ = client.UpdateEffectiveConfig(context.Background()) | ||
updateErr := client.UpdateEffectiveConfig(context.Background()) | ||
require.NoError(t, updateErr) | ||
|
||
// Verify change is delivered. | ||
eventually( | ||
|
@@ -1621,7 +1643,6 @@ func TestMissingPackagesStateProvider(t *testing.T) { | |
protobufs.AgentCapabilities_AgentCapabilities_ReportsPackageStatuses, | ||
} | ||
prepareClient(t, &settings, client) | ||
|
||
assert.ErrorIs(t, client.Start(context.Background(), settings), internal.ErrPackagesStateProviderNotSet) | ||
|
||
// Start a client. | ||
|
@@ -1738,6 +1759,98 @@ func TestOfferUpdatedVersion(t *testing.T) { | |
}) | ||
} | ||
|
||
func TestSetCapabilities(t *testing.T) { | ||
testClients(t, func(t *testing.T, client OpAMPClient) { | ||
// Start a Server. | ||
srv := internal.StartMockServer(t) | ||
srv.EnableExpectMode() | ||
|
||
var clientRcvCustomMessage atomic.Value | ||
|
||
// Start a client. | ||
settings := types.StartSettings{ | ||
OpAMPServerURL: "ws://" + srv.Endpoint, | ||
Callbacks: types.Callbacks{ | ||
OnMessage: func(ctx context.Context, msg *types.MessageData) { | ||
clientRcvCustomMessage.Store(msg.CustomMessage) | ||
}, | ||
}, | ||
Capabilities: protobufs.AgentCapabilities_AgentCapabilities_ReportsEffectiveConfig, | ||
} | ||
prepareClient(t, &settings, client) | ||
|
||
// Client ---> | ||
assert.NoError(t, client.Start(context.Background(), settings)) | ||
|
||
// ---> Server | ||
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent { | ||
assert.EqualValues(t, 0, msg.SequenceNum) | ||
// The first status report after Start must have the ReportsStatus. | ||
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_ReportsStatus != 0) | ||
// The first status report after Start must have the ReportsEffectiveConfig. | ||
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_ReportsEffectiveConfig != 0) | ||
// The first status report after Start must not have the AcceptsRemoteConfig. | ||
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig == 0) | ||
return &protobufs.ServerToAgent{ | ||
InstanceUid: msg.InstanceUid, | ||
} | ||
}) | ||
|
||
newCapabilities := protobufs.AgentCapabilities_AgentCapabilities_ReportsEffectiveConfig | | ||
protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig | | ||
protobufs.AgentCapabilities_AgentCapabilities_ReportsStatus | ||
err := client.SetCapabilities(&newCapabilities) | ||
assert.NoError(t, err) | ||
|
||
// ---> Server | ||
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent { | ||
// Check ReportsStatus is still true. | ||
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_ReportsStatus != 0) | ||
// ReportsEffectiveConfig should no longer be present. | ||
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_ReportsEffectiveConfig != 0) | ||
// AcceptsRemoteConfig should now be present | ||
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig != 0) | ||
|
||
// Send a custom message response and ask client for full state again. | ||
return &protobufs.ServerToAgent{ | ||
InstanceUid: msg.InstanceUid, | ||
Flags: uint64(protobufs.ServerToAgentFlags_ServerToAgentFlags_ReportFullState), | ||
} | ||
}) | ||
|
||
newCapabilities = protobufs.AgentCapabilities_AgentCapabilities_AcceptsRestartCommand | | ||
protobufs.AgentCapabilities_AgentCapabilities_ReportsEffectiveConfig | | ||
protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig | | ||
protobufs.AgentCapabilities_AgentCapabilities_ReportsStatus | ||
newSetErr := client.SetCapabilities(&newCapabilities) | ||
assert.NoError(t, newSetErr) | ||
|
||
// ---> Server | ||
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent { | ||
// Check ReportsStatus is still true. | ||
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_ReportsStatus != 0) | ||
// ReportsEffectiveConfig should present. | ||
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_ReportsEffectiveConfig != 0) | ||
// AcceptsRemoteConfig should now be present | ||
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_AcceptsRemoteConfig != 0) | ||
// AcceptsRestartCommand should now be present | ||
assert.True(t, protobufs.AgentCapabilities(msg.Capabilities)&protobufs.AgentCapabilities_AgentCapabilities_AcceptsRestartCommand != 0) | ||
// Send a custom message response and ask client for full state again. | ||
return &protobufs.ServerToAgent{ | ||
InstanceUid: msg.InstanceUid, | ||
Flags: uint64(protobufs.ServerToAgentFlags_ServerToAgentFlags_ReportFullState), | ||
} | ||
}) | ||
|
||
// Shutdown the Server. | ||
srv.Close() | ||
|
||
// Shutdown the client. | ||
err = client.Stop(context.Background()) | ||
assert.NoError(t, err) | ||
}) | ||
} | ||
|
||
func TestReportCustomCapabilities(t *testing.T) { | ||
testClients(t, func(t *testing.T, client OpAMPClient) { | ||
// Start a Server. | ||
|
@@ -2407,6 +2520,119 @@ func TestSetAvailableComponents(t *testing.T) { | |
} | ||
} | ||
|
||
func TestValidateCapabilities(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
capabilities protobufs.AgentCapabilities | ||
setupFunc func(t *testing.T, client OpAMPClient) | ||
expectedError error | ||
}{ | ||
{ | ||
name: "ReportsHealth capability without health", | ||
capabilities: protobufs.AgentCapabilities_AgentCapabilities_ReportsHealth, | ||
setupFunc: func(t *testing.T, client OpAMPClient) { | ||
// Do not set health | ||
}, | ||
expectedError: internal.ErrHealthMissing, | ||
}, | ||
{ | ||
name: "ReportsHealth capability with health", | ||
capabilities: protobufs.AgentCapabilities_AgentCapabilities_ReportsHealth, | ||
setupFunc: func(t *testing.T, client OpAMPClient) { | ||
err := client.SetHealth(&protobufs.ComponentHealth{}) | ||
require.NoError(t, err) | ||
}, | ||
expectedError: nil, | ||
}, | ||
{ | ||
name: "ReportsAvailableComponents capability without available components", | ||
capabilities: protobufs.AgentCapabilities_AgentCapabilities_ReportsAvailableComponents, | ||
setupFunc: func(t *testing.T, client OpAMPClient) { | ||
// Do not set available components | ||
}, | ||
expectedError: internal.ErrAvailableComponentsMissing, | ||
}, | ||
{ | ||
name: "ReportsAvailableComponents capability with available components", | ||
capabilities: protobufs.AgentCapabilities_AgentCapabilities_ReportsAvailableComponents, | ||
setupFunc: func(t *testing.T, client OpAMPClient) { | ||
err := client.SetAvailableComponents(generateTestAvailableComponents()) | ||
require.NoError(t, err) | ||
}, | ||
expectedError: nil, | ||
}, | ||
{ | ||
name: "AcceptsPackages capability without PackagesStateProvider", | ||
capabilities: protobufs.AgentCapabilities_AgentCapabilities_AcceptsPackages, | ||
setupFunc: func(t *testing.T, client OpAMPClient) { | ||
// Do not set PackagesStateProvider | ||
}, | ||
expectedError: internal.ErrPackagesStateProviderNotSet, | ||
}, | ||
// { | ||
// name: "AcceptsPackages capability with PackagesStateProvider", | ||
// capabilities: protobufs.AgentCapabilities_AgentCapabilities_AcceptsPackages, | ||
// setupFunc: func(t *testing.T, client OpAMPClient) { | ||
// client.SetPackageStatuses(statuses *protobufs.PackageStatuses) | ||
// }, | ||
// expectedError: nil, | ||
// }, | ||
{ | ||
name: "ReportsPackageStatuses capability without PackagesStateProvider", | ||
capabilities: protobufs.AgentCapabilities_AgentCapabilities_ReportsPackageStatuses, | ||
setupFunc: func(t *testing.T, client OpAMPClient) { | ||
// Do not set PackagesStateProvider | ||
}, | ||
expectedError: internal.ErrPackagesStateProviderNotSet, | ||
}, | ||
// { | ||
// name: "ReportsPackageStatuses capability with PackagesStateProvider", | ||
// capabilities: protobufs.AgentCapabilities_AgentCapabilities_ReportsPackageStatuses, | ||
// setupFunc: func(t *testing.T, client OpAMPClient) { | ||
// client.(*ClientCommon).PackagesStateProvider = internal.NewInMemPackagesStore() | ||
// }, | ||
// expectedError: nil, | ||
// }, | ||
{ | ||
name: "AcceptsPackages and ReportsPackageStatuses capabilities without PackagesStateProvider", | ||
capabilities: protobufs.AgentCapabilities_AgentCapabilities_AcceptsPackages | protobufs.AgentCapabilities_AgentCapabilities_ReportsPackageStatuses, | ||
setupFunc: func(t *testing.T, client OpAMPClient) { | ||
// Do not set PackagesStateProvider | ||
}, | ||
expectedError: internal.ErrPackagesStateProviderNotSet, | ||
}, | ||
// { | ||
// name: "AcceptsPackages and ReportsPackageStatuses capabilities with PackagesStateProvider", | ||
// capabilities: protobufs.AgentCapabilities_AgentCapabilities_AcceptsPackages | protobufs.AgentCapabilities_AgentCapabilities_ReportsPackageStatuses, | ||
// setupFunc: func(t *testing.T, client OpAMPClient) { | ||
// client.(*ClientCommon).PackagesStateProvider = internal.NewInMemPackagesStore() | ||
// }, | ||
// expectedError: nil, | ||
// }, | ||
{ | ||
name: "No capabilities set", | ||
capabilities: protobufs.AgentCapabilities_AgentCapabilities_Unspecified, | ||
setupFunc: func(t *testing.T, client OpAMPClient) { | ||
// No setup needed | ||
}, | ||
expectedError: nil, | ||
}, | ||
} | ||
|
||
for _, tc := range testCases { | ||
t.Run(tc.name, func(t *testing.T) { | ||
testClients(t, func(t *testing.T, client OpAMPClient) { | ||
// Setup the client state as per the test case | ||
tc.setupFunc(t, client) | ||
|
||
// Validate capabilities | ||
err := client.SetCapabilities(&tc.capabilities) | ||
assert.Equal(t, tc.expectedError, err) | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
func generateTestAvailableComponents() *protobufs.AvailableComponents { | ||
return &protobufs.AvailableComponents{ | ||
Hash: []byte("fake-hash"), | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -10,6 +10,8 @@ import ( | |||||
"github.com/open-telemetry/opamp-go/protobufs" | ||||||
) | ||||||
|
||||||
var _ OpAMPClient = &httpClient{} | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: We use both styles in the code, but this one is more common.
Suggested change
|
||||||
|
||||||
// httpClient is an OpAMP Client implementation for plain HTTP transport. | ||||||
// See specification: https://github.com/open-telemetry/opamp-spec/blob/main/specification.md#plain-http-transport | ||||||
type httpClient struct { | ||||||
|
@@ -125,6 +127,11 @@ func (c *httpClient) SetAvailableComponents(components *protobufs.AvailableCompo | |||||
return c.common.SetAvailableComponents(components) | ||||||
} | ||||||
|
||||||
// SetCapabilities implements OpAMPClient. | ||||||
func (c *httpClient) SetCapabilities(capabilities *protobufs.AgentCapabilities) error { | ||||||
return c.common.SetCapabilities(capabilities) | ||||||
} | ||||||
|
||||||
func (c *httpClient) runUntilStopped(ctx context.Context) { | ||||||
// Start the HTTP sender. This will make request/responses with retries for | ||||||
// failures and will wait with configured polling interval if there is nothing | ||||||
|
@@ -135,7 +142,7 @@ func (c *httpClient) runUntilStopped(ctx context.Context) { | |||||
c.common.Callbacks, | ||||||
&c.common.ClientSyncedState, | ||||||
c.common.PackagesStateProvider, | ||||||
c.common.Capabilities, | ||||||
c.common.ClientSyncedState.Capabilities(), | ||||||
&c.common.PackageSyncMutex, | ||||||
) | ||||||
} | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This results in inconsistent way of setting capabilities depending on when you want to do it: before Start() requires setting a field in StartSettings, after Start() requires calling this method.
For functionality that MUST be set before Start() and MAY be set after Start() we previously used only a method. See for example SetAgentDescription().
Should we do the same for SetCapabilities() and deprecate Capabilities field from StartSettings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm... I think that makes sense to me. @andykellr does that sound alright to you?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jaronoff97 I am not sure if you updated the PR to make this change. Is it ready for another review round?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i was waiting on andy's review and when you said "we decided to go ahead with this feature" I thought it was dismissing this comment. I see now, i'll update the PR based on what you said above in this thread.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, no problem, I just wasn't sure if I need to take another look.
I would like @andykellr's opinion too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jaronoff97 in case I wasn't clear, I am waiting for you to tell me if this is ready for another round of review.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i was waiting on andy's opinion here, but either way i can make your requested changes from above before its ready for more review. apologies.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jaronoff97 sounds good, thanks. Ping me when you update the PR, I will take a look.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tigrannajaryan i had two questions for the implementation below, but i think this should be good! Thank you for your patience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach is fine with me.