Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions .chloggen/44430-datadog-extension-add-deployment_type.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# 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. receiver/filelog)
component: extension/datadog

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Adds deployment_type configuration option to the Datadog Extension.

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [44430]

# (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: |
Users may specify the deployment type of the collector in Datadog Extension configuration to view in Datadog app.
If the collector is deployed as a gateway (i.e. receiving pipeline telemetry from multiple hosts/sources),
user should specify "gateway" as the deployment type.
If the collector is deployed as a daemonset/agent, user should specify "daemonset" as the deployment type.
The default setting is "unknown" if not set.

# 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: [user]
19 changes: 19 additions & 0 deletions extension/datadogextension/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,25 @@ extensions:
- `timeout`: Timeout for HTTP requests to Datadog.
- `tls`: TLS settings for outbound connections.

## Deployment Type Configuration

The Datadog Extension supports a `deployment_type` configuration option to indicate how the OpenTelemetry Collector is deployed. This information is included in the metadata payload sent to Datadog and can be used for better observability and fleet management.

**Available options:**
- `gateway`: Use this when the collector is deployed as a centralized gateway that receives telemetry from multiple sources (e.g., a standalone collector deployment that aggregates data from multiple application instances).
- `daemonset`: Use this when the collector is deployed as a DaemonSet in Kubernetes or as an agent running on each host (e.g., one collector instance per node).
- `unknown` (default): Use this when the deployment type doesn't fit the other categories or is not specified.

**Example configuration for a DaemonSet deployment:**
```yaml
extensions:
datadog:
api:
key: <YOUR_DATADOG_API_KEY>
site: "datadoghq.com"
deployment_type: daemonset
```

## Notes
- The extension is in active development. Functionality and configuration options may change as Datadog OpenTelemetry monitoring features evolve.
- Please see [official documentation on Datadog's website](https://docs.datadoghq.com/opentelemetry/integrations/datadog_extension/) for more details.
11 changes: 11 additions & 0 deletions extension/datadogextension/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ type Config struct {
Hostname string `mapstructure:"hostname"`
// HTTPConfig is v2 config for the http metadata service.
HTTPConfig *httpserver.Config `mapstructure:"http"`
// DeploymentType indicates the type of deployment (gateway, daemonset, or unknown).
// Defaults to "unknown" if not set.
DeploymentType string `mapstructure:"deployment_type"`
}

// Validate ensures that the configuration is valid.
Expand All @@ -37,5 +40,13 @@ func (c *Config) Validate() error {
if c.HTTPConfig == nil {
return errors.New("http config is required")
}
// Validate deployment_type if set
if c.DeploymentType != "" && c.DeploymentType != "gateway" && c.DeploymentType != "daemonset" && c.DeploymentType != "unknown" {
return errors.New("deployment_type must be one of: gateway, daemonset, or unknown")
}
// Set default if not provided
if c.DeploymentType == "" {
c.DeploymentType = "unknown"
}
return nil
}
165 changes: 165 additions & 0 deletions extension/datadogextension/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,91 @@ func TestConfig_Validate(t *testing.T) {
},
wantErr: errors.New("http config is required"),
},
{
name: "Valid configuration with gateway deployment type",
config: Config{
API: datadogconfig.APIConfig{
Site: datadogconfig.DefaultSite,
Key: "1234567890abcdef1234567890abcdef",
},
HTTPConfig: &httpserver.Config{
ServerConfig: confighttp.ServerConfig{
Endpoint: "http://localhost:8080",
},
Path: "/metadata",
},
DeploymentType: "gateway",
},
wantErr: nil,
},
{
name: "Valid configuration with daemonset deployment type",
config: Config{
API: datadogconfig.APIConfig{
Site: datadogconfig.DefaultSite,
Key: "1234567890abcdef1234567890abcdef",
},
HTTPConfig: &httpserver.Config{
ServerConfig: confighttp.ServerConfig{
Endpoint: "http://localhost:8080",
},
Path: "/metadata",
},
DeploymentType: "daemonset",
},
wantErr: nil,
},
{
name: "Valid configuration with unknown deployment type",
config: Config{
API: datadogconfig.APIConfig{
Site: datadogconfig.DefaultSite,
Key: "1234567890abcdef1234567890abcdef",
},
HTTPConfig: &httpserver.Config{
ServerConfig: confighttp.ServerConfig{
Endpoint: "http://localhost:8080",
},
Path: "/metadata",
},
DeploymentType: "unknown",
},
wantErr: nil,
},
{
name: "Invalid deployment type",
config: Config{
API: datadogconfig.APIConfig{
Site: datadogconfig.DefaultSite,
Key: "1234567890abcdef1234567890abcdef",
},
HTTPConfig: &httpserver.Config{
ServerConfig: confighttp.ServerConfig{
Endpoint: "http://localhost:8080",
},
Path: "/metadata",
},
DeploymentType: "invalid-mode",
},
wantErr: errors.New("deployment_type must be one of: gateway, daemonset, or unknown"),
},
{
name: "Empty deployment type defaults to unknown",
config: Config{
API: datadogconfig.APIConfig{
Site: datadogconfig.DefaultSite,
Key: "1234567890abcdef1234567890abcdef",
},
HTTPConfig: &httpserver.Config{
ServerConfig: confighttp.ServerConfig{
Endpoint: "http://localhost:8080",
},
Path: "/metadata",
},
DeploymentType: "",
},
wantErr: nil,
},
}

for _, tt := range tests {
Expand Down Expand Up @@ -152,3 +237,83 @@ func TestExtensionWithProxyConfig(t *testing.T) {
err = ext.Shutdown(t.Context())
require.NoError(t, err)
}

func TestConfig_DeploymentTypeDefault(t *testing.T) {
cfg := Config{
API: datadogconfig.APIConfig{
Site: datadogconfig.DefaultSite,
Key: "1234567890abcdef1234567890abcdef",
},
HTTPConfig: &httpserver.Config{
ServerConfig: confighttp.ServerConfig{
Endpoint: "http://localhost:8080",
},
Path: "/metadata",
},
DeploymentType: "",
}

err := cfg.Validate()
require.NoError(t, err)
assert.Equal(t, "unknown", cfg.DeploymentType, "DeploymentType should default to 'unknown'")
}

func TestConfig_DeploymentTypeValidValues(t *testing.T) {
tests := []struct {
name string
deploymentType string
expectError bool
}{
{
name: "gateway is valid",
deploymentType: "gateway",
expectError: false,
},
{
name: "daemonset is valid",
deploymentType: "daemonset",
expectError: false,
},
{
name: "unknown is valid",
deploymentType: "unknown",
expectError: false,
},
{
name: "invalid value fails",
deploymentType: "invalid",
expectError: true,
},
{
name: "sidecar is invalid",
deploymentType: "sidecar",
expectError: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cfg := Config{
API: datadogconfig.APIConfig{
Site: datadogconfig.DefaultSite,
Key: "1234567890abcdef1234567890abcdef",
},
HTTPConfig: &httpserver.Config{
ServerConfig: confighttp.ServerConfig{
Endpoint: "http://localhost:8080",
},
Path: "/metadata",
},
DeploymentType: tt.deploymentType,
}

err := cfg.Validate()
if tt.expectError {
assert.Error(t, err)
assert.Contains(t, err.Error(), "deployment_type must be one of")
} else {
assert.NoError(t, err)
}
})
}
}
1 change: 1 addition & 0 deletions extension/datadogextension/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ func (e *datadogExtension) NotifyConfig(_ context.Context, conf *confmap.Conf) e
e.info.build.Version, // This is the same version from buildInfo; it is possible we could want to set a different version here in the future
e.configs.extension.API.Site,
fullConfig,
e.configs.extension.DeploymentType,
buildInfo,
)

Expand Down
69 changes: 69 additions & 0 deletions extension/datadogextension/extension_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,75 @@ func TestSimpleInterfaceMethods(t *testing.T) {
assert.NotPanics(t, func() { ext.ComponentStatusChanged(nil, nil) })
}

func TestExtension_DeploymentTypeInPayload(t *testing.T) {
tests := []struct {
name string
deploymentType string
expected string
}{
{
name: "gateway deployment type",
deploymentType: "gateway",
expected: "gateway",
},
{
name: "daemonset deployment type",
deploymentType: "daemonset",
expected: "daemonset",
},
{
name: "unknown deployment type",
deploymentType: "unknown",
expected: "unknown",
},
{
name: "empty defaults to unknown",
deploymentType: "",
expected: "unknown",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
set := extension.Settings{
TelemetrySettings: componenttest.NewNopTelemetrySettings(),
BuildInfo: component.BuildInfo{Version: "1.2.3"},
}
hostProvider := &mockSourceProvider{source: source.Source{Kind: source.HostnameKind, Identifier: "test-host"}}
uuidProvider := &mockUUIDProvider{mockUUID: "test-uuid"}
cfg := &Config{
API: datadogconfig.APIConfig{Key: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Site: "datadoghq.com"},
HTTPConfig: &httpserver.Config{
ServerConfig: confighttp.ServerConfig{Endpoint: "localhost:0"},
Path: "/test-path",
},
DeploymentType: tt.deploymentType,
}

// Validate will set the default if empty
err := cfg.Validate()
require.NoError(t, err)

ext, err := newExtension(t.Context(), cfg, set, hostProvider, uuidProvider)
require.NoError(t, err)
ext.serializer = &mockSerializer{}
require.NoError(t, ext.Start(t.Context(), componenttest.NewNopHost()))

// Minimal config to trigger NotifyConfig
conf := confmap.NewFromStringMap(map[string]any{})
err = ext.NotifyConfig(t.Context(), conf)
require.NoError(t, err)

// Verify the deployment type is set correctly in the payload
require.NotNil(t, ext.otelCollectorMetadata)
assert.Equal(t, tt.expected, ext.otelCollectorMetadata.CollectorDeploymentType, "CollectorDeploymentType should be set correctly")

// Cleanup
assert.NoError(t, ext.Shutdown(t.Context()))
})
}
}

func TestRealUUIDProvider(t *testing.T) {
p := &realUUIDProvider{}
uuid := p.NewString()
Expand Down
4 changes: 4 additions & 0 deletions extension/datadogextension/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ func createTestOtelCollectorPayload() *payload.OtelCollectorPayload {
version,
site,
fullConfig,
"unknown",
buildInfo,
)

Expand Down Expand Up @@ -563,6 +564,7 @@ func TestHTTPServerIntegration(t *testing.T) {
"0.127.0",
"datadoghq.com",
fullConfig,
"unknown",
buildInfo,
)
if activeComponents != nil {
Expand Down Expand Up @@ -721,6 +723,7 @@ func TestHTTPServerConfigIntegration(t *testing.T) {
"1.0.0",
"datadoghq.com",
"{}",
"unknown",
buildInfo,
)

Expand Down Expand Up @@ -809,6 +812,7 @@ func TestHTTPServerConcurrentAccess(t *testing.T) {
"1.0.0",
"datadoghq.com",
"{}",
"unknown",
buildInfo,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@ const successfulInstanceResponse = `{
},
"full_configuration": "",
"health_status": "",
"collector_resource_attributes": {}
"collector_resource_attributes": {},
"collector_deployment_type": "unknown"
},
"uuid": "test-uuid"
}`
Expand Down Expand Up @@ -295,6 +296,7 @@ func TestHandleMetadata(t *testing.T) {
FullComponents: []payload.CollectorModule{},
ActiveComponents: []payload.ServiceComponent{},
CollectorResourceAttributes: map[string]string{},
CollectorDeploymentType: "unknown", // Default value set by config validation
},
},
}
Expand Down
Loading