Skip to content

Commit

Permalink
[extension/opampextension] reports effective config (#29277)
Browse files Browse the repository at this point in the history
**Link to tracking Issue:** resolve #27293

**Testing:**
unittest
manual test with opamp-go example:
* collector version: modified otelcontribcol including opampextension
* collector config: 
```yaml
receivers:
  # otlp:
  #   protocols:
  #     grpc:
  filelog:
    include:
      - /tmp/xxxxx.log

exporters:
  debug:
    #verbosity: debug

extensions:
  opamp:
    server:
      ws:
        endpoint: ws://127.0.0.1:4320/v1/opamp
        tls:
          insecure_skip_verify: true

service:
  pipelines:
    logs:
      receivers: [filelog]
      exporters: [debug]
  telemetry:
    logs:
      level: info
  extensions: [opamp]
```
* server ui: ![Screenshot 2023-11-28 at 22-14-59 OpAMP
Server](https://github.com/open-telemetry/opentelemetry-collector-contrib/assets/9400582/38ea49bb-f2f7-4618-859f-2b66257ef89a)

**Documentation:** README.md

---------

Co-authored-by: Jared Tan <[email protected]>
  • Loading branch information
haoqixu and JaredTan95 authored Nov 30, 2023
1 parent e7b1307 commit 16760c7
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 40 deletions.
27 changes: 27 additions & 0 deletions .chloggen/feat-27293-reports-effective-config.yaml
Original file line number Diff line number Diff line change
@@ -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: []
16 changes: 8 additions & 8 deletions cmd/opampsupervisor/specification/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions extension/opampextension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
20 changes: 20 additions & 0 deletions extension/opampextension/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions extension/opampextension/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ func TestUnmarshalConfig(t *testing.T) {
},
},
InstanceUID: "01BX5ZZKBKACTAV9WEVGEMMVRZ",
Capabilities: Capabilities{
ReportsEffectiveConfig: true,
},
}, cfg)
}

Expand Down
3 changes: 3 additions & 0 deletions extension/opampextension/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ func createDefaultConfig() component.Config {
Server: &OpAMPServer{
WS: &OpAMPWebsocket{},
},
Capabilities: Capabilities{
ReportsEffectiveConfig: true,
},
}
}

Expand Down
2 changes: 1 addition & 1 deletion extension/opampextension/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
go.opentelemetry.io/collector/pdata v1.0.0
go.opentelemetry.io/collector/semconv v0.90.0
go.uber.org/zap v1.26.0
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down Expand Up @@ -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
)
72 changes: 43 additions & 29 deletions extension/opampextension/opamp_agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,36 +8,21 @@ import (
"net/http"
"os"
"runtime"
"sync"

"github.com/google/uuid"
"github.com/oklog/ulid/v2"
"github.com/open-telemetry/opamp-go/client"
"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
Expand All @@ -47,7 +32,10 @@ type opampAgent struct {

instanceID ulid.ULID

effectiveConfig string
eclk sync.RWMutex
effectiveConfig *confmap.Conf

capabilities Capabilities

agentDescription *protobufs.AgentDescription

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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},
},
},
}
Expand Down
19 changes: 17 additions & 2 deletions extension/opampextension/opamp_agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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)
}

Expand Down Expand Up @@ -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) {
Expand Down
14 changes: 14 additions & 0 deletions extension/opampextension/testdata/effective.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
exporters:
otlp:
endpoint: localhost:1111
receivers:
otlp:
protocols:
grpc: {}
http: {}
service:
pipelines:
traces:
receivers: [otlp]
processors: []
exporters: [otlp]

0 comments on commit 16760c7

Please sign in to comment.