diff --git a/.chloggen/feat-27293-reports-effective-config.yaml b/.chloggen/feat-27293-reports-effective-config.yaml new file mode 100755 index 000000000000..e436f97aaeed --- /dev/null +++ b/.chloggen/feat-27293-reports-effective-config.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: extension/opampextension + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Implement `extension.NotifyConfig` to be notified of the Collector's effective config and report it to the OpAMP server. + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [27293] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/cmd/opampsupervisor/specification/README.md b/cmd/opampsupervisor/specification/README.md index 5ca8f9f62889..909ca86f58e5 100644 --- a/cmd/opampsupervisor/specification/README.md +++ b/cmd/opampsupervisor/specification/README.md @@ -84,33 +84,33 @@ server: # If enabled the Supervisor will also report RemoteConfig status # to the Server. capabilities: - AcceptsRemoteConfig: # false if unspecified + accepts_remote_config: # false if unspecified # The Supervisor will report EffectiveConfig to the Server. - ReportsEffectiveConfig: # true if unspecified + reports_effective_config: # true if unspecified # The Supervisor can accept Collector executable package updates. # If enabled the Supervisor will also report package status to the # Server. - AcceptsPackages: # false if unspecified + accepts_packages: # false if unspecified # The Collector will report own metrics to the destination specified by # the Server. - ReportsOwnMetrics: # true if unspecified + reports_own_metrics: # true if unspecified # The Collector will report own logs to the destination specified by # the Server. - ReportsOwnLogs: # true if unspecified + reports_own_logs: # true if unspecified # The Collector will accept connections settings for exporters # from the Server. - AcceptsOtherConnectionSettings: # false if unspecified + accepts_other_connection_settings: # false if unspecified # The Supervisor will accept restart requests. - AcceptsRestartCommand: # true if unspecified + accepts_restart_command: # true if unspecified # The Collector will report Health. - ReportsHealth: # true if unspecified + reports_health: # true if unspecified storage: # A writable directory where the Supervisor can store data diff --git a/extension/opampextension/README.md b/extension/opampextension/README.md index 40ec053883a7..d6ecb6a2f9b3 100644 --- a/extension/opampextension/README.md +++ b/extension/opampextension/README.md @@ -27,6 +27,8 @@ The following settings are optional: - `instance_uid`: A ULID formatted as a 26 character string in canonical representation. Auto-generated on start if missing. Setting this ensures the instance UID remains constant across process restarts. +- `capabilities`: Keys with boolean true/false values that enable a particular OpAMP capability. + - `reports_effective_config`: Whether to enable the OpAMP ReportsEffectiveConfig capability. Default is `true`. ### Example diff --git a/extension/opampextension/config.go b/extension/opampextension/config.go index 6ff9946fc4b3..825c52a9ab1b 100644 --- a/extension/opampextension/config.go +++ b/extension/opampextension/config.go @@ -7,6 +7,7 @@ import ( "errors" "github.com/oklog/ulid/v2" + "github.com/open-telemetry/opamp-go/protobufs" "go.opentelemetry.io/collector/config/configopaque" "go.opentelemetry.io/collector/config/configtls" ) @@ -19,6 +20,25 @@ type Config struct { // InstanceUID is a ULID formatted as a 26 character string in canonical // representation. Auto-generated on start if missing. InstanceUID string `mapstructure:"instance_uid"` + + // Capabilities contains options to enable a particular OpAMP capability + Capabilities Capabilities `mapstructure:"capabilities"` +} + +type Capabilities struct { + // ReportsEffectiveConfig enables the OpAMP ReportsEffectiveConfig Capability. (default: true) + ReportsEffectiveConfig bool `mapstructure:"reports_effective_config"` +} + +func (caps Capabilities) toAgentCapabilities() protobufs.AgentCapabilities { + // All Agents MUST report status. + agentCapabilities := protobufs.AgentCapabilities_AgentCapabilities_ReportsStatus + + if caps.ReportsEffectiveConfig { + agentCapabilities |= protobufs.AgentCapabilities_AgentCapabilities_ReportsEffectiveConfig + } + + return agentCapabilities } // OpAMPServer contains the OpAMP transport configuration. diff --git a/extension/opampextension/config_test.go b/extension/opampextension/config_test.go index 44a33dbcf46e..184c97b387d1 100644 --- a/extension/opampextension/config_test.go +++ b/extension/opampextension/config_test.go @@ -35,6 +35,9 @@ func TestUnmarshalConfig(t *testing.T) { }, }, InstanceUID: "01BX5ZZKBKACTAV9WEVGEMMVRZ", + Capabilities: Capabilities{ + ReportsEffectiveConfig: true, + }, }, cfg) } diff --git a/extension/opampextension/factory.go b/extension/opampextension/factory.go index a468fe48f141..91c39e9a011b 100644 --- a/extension/opampextension/factory.go +++ b/extension/opampextension/factory.go @@ -26,6 +26,9 @@ func createDefaultConfig() component.Config { Server: &OpAMPServer{ WS: &OpAMPWebsocket{}, }, + Capabilities: Capabilities{ + ReportsEffectiveConfig: true, + }, } } diff --git a/extension/opampextension/go.mod b/extension/opampextension/go.mod index 8669e3c5a20d..284207260b40 100644 --- a/extension/opampextension/go.mod +++ b/extension/opampextension/go.mod @@ -15,6 +15,7 @@ require ( go.opentelemetry.io/collector/pdata v1.0.0-rcv0018.0.20231127181443-575c5f5e2531 go.opentelemetry.io/collector/semconv v0.89.1-0.20231127181443-575c5f5e2531 go.uber.org/zap v1.26.0 + gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -44,5 +45,4 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/extension/opampextension/opamp_agent.go b/extension/opampextension/opamp_agent.go index aba04a7edeaf..3fdc4c5348ed 100644 --- a/extension/opampextension/opamp_agent.go +++ b/extension/opampextension/opamp_agent.go @@ -8,6 +8,7 @@ import ( "net/http" "os" "runtime" + "sync" "github.com/google/uuid" "github.com/oklog/ulid/v2" @@ -15,29 +16,13 @@ import ( "github.com/open-telemetry/opamp-go/client/types" "github.com/open-telemetry/opamp-go/protobufs" "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/pdata/pcommon" semconv "go.opentelemetry.io/collector/semconv/v1.18.0" "go.uber.org/zap" + "gopkg.in/yaml.v3" ) -// TODO: Replace with https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/27293 -const localConfig = ` -exporters: - otlp: - endpoint: localhost:1111 -receivers: - otlp: - protocols: - grpc: {} - http: {} -service: - pipelines: - traces: - receivers: [otlp] - processors: [] - exporters: [otlp] -` - type opampAgent struct { cfg *Config logger *zap.Logger @@ -47,7 +32,10 @@ type opampAgent struct { instanceID ulid.ULID - effectiveConfig string + eclk sync.RWMutex + effectiveConfig *confmap.Conf + + capabilities Capabilities agentDescription *protobufs.AgentDescription @@ -88,9 +76,7 @@ func (o *opampAgent) Start(_ context.Context, _ component.Host) error { }, OnMessageFunc: o.onMessage, }, - // TODO: Include ReportsEffectiveConfig once the extension has access to the - // collector's effective configuration. - Capabilities: protobufs.AgentCapabilities_AgentCapabilities_ReportsStatus, + Capabilities: o.capabilities.toAgentCapabilities(), } if err := o.createAgentDescription(); err != nil { @@ -121,6 +107,21 @@ func (o *opampAgent) Shutdown(ctx context.Context) error { return o.opampClient.Stop(ctx) } +func (o *opampAgent) NotifyConfig(ctx context.Context, conf *confmap.Conf) error { + if o.capabilities.ReportsEffectiveConfig { + o.updateEffectiveConfig(conf) + return o.opampClient.UpdateEffectiveConfig(ctx) + } + return nil +} + +func (o *opampAgent) updateEffectiveConfig(conf *confmap.Conf) { + o.eclk.Lock() + defer o.eclk.Unlock() + + o.effectiveConfig = conf +} + func newOpampAgent(cfg *Config, logger *zap.Logger, build component.BuildInfo, res pcommon.Resource) (*opampAgent, error) { agentType := build.Command @@ -156,12 +157,12 @@ func newOpampAgent(cfg *Config, logger *zap.Logger, build component.BuildInfo, r } agent := &opampAgent{ - cfg: cfg, - logger: logger, - agentType: agentType, - agentVersion: agentVersion, - instanceID: uid, - effectiveConfig: localConfig, // TODO: Replace with https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/27293 + cfg: cfg, + logger: logger, + agentType: agentType, + agentVersion: agentVersion, + instanceID: uid, + capabilities: cfg.Capabilities, } return agent, nil @@ -210,10 +211,23 @@ func (o *opampAgent) updateAgentIdentity(instanceID ulid.ULID) { } func (o *opampAgent) composeEffectiveConfig() *protobufs.EffectiveConfig { + o.eclk.RLock() + defer o.eclk.RUnlock() + + if !o.capabilities.ReportsEffectiveConfig || o.effectiveConfig == nil { + return nil + } + + conf, err := yaml.Marshal(o.effectiveConfig.ToStringMap()) + if err != nil { + o.logger.Error("cannot unmarshal effectiveConfig", zap.Any("conf", o.effectiveConfig), zap.Error(err)) + return nil + } + return &protobufs.EffectiveConfig{ ConfigMap: &protobufs.AgentConfigMap{ ConfigMap: map[string]*protobufs.AgentConfigFile{ - "": {Body: []byte(o.effectiveConfig)}, + "": {Body: conf}, }, }, } diff --git a/extension/opampextension/opamp_agent_test.go b/extension/opampextension/opamp_agent_test.go index c6bcede0f733..5332d7769f80 100644 --- a/extension/opampextension/opamp_agent_test.go +++ b/extension/opampextension/opamp_agent_test.go @@ -5,12 +5,15 @@ package opampextension import ( "context" + "os" + "path/filepath" "testing" "github.com/oklog/ulid/v2" "github.com/stretchr/testify/assert" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" + "go.opentelemetry.io/collector/confmap/confmaptest" "go.opentelemetry.io/collector/extension/extensiontest" semconv "go.opentelemetry.io/collector/semconv/v1.18.0" ) @@ -24,7 +27,8 @@ func TestNewOpampAgent(t *testing.T) { assert.Equal(t, "otelcoltest", o.agentType) assert.Equal(t, "test version", o.agentVersion) assert.NotEmpty(t, o.instanceID.String()) - assert.NotEmpty(t, o.effectiveConfig) + assert.True(t, o.capabilities.ReportsEffectiveConfig) + assert.Empty(t, o.effectiveConfig) assert.Nil(t, o.agentDescription) } @@ -75,10 +79,21 @@ func TestComposeEffectiveConfig(t *testing.T) { set := extensiontest.NewNopCreateSettings() o, err := newOpampAgent(cfg.(*Config), set.Logger, set.BuildInfo, set.Resource) assert.NoError(t, err) - assert.NotEmpty(t, o.effectiveConfig) + assert.Empty(t, o.effectiveConfig) ec := o.composeEffectiveConfig() + assert.Nil(t, ec) + + ecFileName := filepath.Join("testdata", "effective.yaml") + cm, err := confmaptest.LoadConf(ecFileName) + assert.NoError(t, err) + expected, err := os.ReadFile(ecFileName) + assert.NoError(t, err) + + o.updateEffectiveConfig(cm) + ec = o.composeEffectiveConfig() assert.NotNil(t, ec) + assert.YAMLEq(t, string(expected), string(ec.ConfigMap.ConfigMap[""].Body)) } func TestShutdown(t *testing.T) { diff --git a/extension/opampextension/testdata/effective.yaml b/extension/opampextension/testdata/effective.yaml new file mode 100644 index 000000000000..69261ddf6dd3 --- /dev/null +++ b/extension/opampextension/testdata/effective.yaml @@ -0,0 +1,14 @@ +exporters: + otlp: + endpoint: localhost:1111 +receivers: + otlp: + protocols: + grpc: {} + http: {} +service: + pipelines: + traces: + receivers: [otlp] + processors: [] + exporters: [otlp]