diff --git a/CHANGELOG.md b/CHANGELOG.md index ce961df65105..b9e2bf9bb0f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ Main (unreleased) - A new `pyroscope.java` component for profiling Java processes using async-profiler. (@korniltsev) +- A new `otelcol.processor.resourcedetection` component which inserts resource attributes + to OTLP telemetry based on the host on which Grafana Agent is running. (@ptodev) + ### Enhancements - Include line numbers in profiles produced by `pyrsocope.java` component. (@korniltsev) diff --git a/component/all/all.go b/component/all/all.go index 437a7a07e59b..0bf3da725bbf 100644 --- a/component/all/all.go +++ b/component/all/all.go @@ -82,6 +82,7 @@ import ( _ "github.com/grafana/agent/component/otelcol/processor/k8sattributes" // Import otelcol.processor.k8sattributes _ "github.com/grafana/agent/component/otelcol/processor/memorylimiter" // Import otelcol.processor.memory_limiter _ "github.com/grafana/agent/component/otelcol/processor/probabilistic_sampler" // Import otelcol.processor.probabilistic_sampler + _ "github.com/grafana/agent/component/otelcol/processor/resourcedetection" // Import otelcol.processor.resourcedetection _ "github.com/grafana/agent/component/otelcol/processor/span" // Import otelcol.processor.span _ "github.com/grafana/agent/component/otelcol/processor/tail_sampling" // Import otelcol.processor.tail_sampling _ "github.com/grafana/agent/component/otelcol/processor/transform" // Import otelcol.processor.transform diff --git a/component/otelcol/config_k8s.go b/component/otelcol/config_k8s.go new file mode 100644 index 000000000000..b20407fd41fb --- /dev/null +++ b/component/otelcol/config_k8s.go @@ -0,0 +1,35 @@ +package otelcol + +import "fmt" + +const ( + KubernetesAPIConfig_AuthType_None = "none" + KubernetesAPIConfig_AuthType_ServiceAccount = "serviceAccount" + KubernetesAPIConfig_AuthType_KubeConfig = "kubeConfig" + KubernetesAPIConfig_AuthType_TLS = "tls" +) + +// KubernetesAPIConfig contains options relevant to connecting to the K8s API +type KubernetesAPIConfig struct { + // How to authenticate to the K8s API server. This can be one of `none` + // (for no auth), `serviceAccount` (to use the standard service account + // token provided to the agent pod), or `kubeConfig` to use credentials + // from `~/.kube/config`. + AuthType string `river:"auth_type,attr,optional"` + + // When using auth_type `kubeConfig`, override the current context. + Context string `river:"context,attr,optional"` +} + +// Validate returns an error if the config is invalid. +func (c *KubernetesAPIConfig) Validate() error { + switch c.AuthType { + case KubernetesAPIConfig_AuthType_None, + KubernetesAPIConfig_AuthType_ServiceAccount, + KubernetesAPIConfig_AuthType_KubeConfig, + KubernetesAPIConfig_AuthType_TLS: + return nil + default: + return fmt.Errorf("invalid auth_type %q", c.AuthType) + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/aws/ec2/config.go b/component/otelcol/processor/resourcedetection/internal/aws/ec2/config.go new file mode 100644 index 000000000000..9b715eac4a12 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/aws/ec2/config.go @@ -0,0 +1,72 @@ +package ec2 + +import ( + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "ec2" + +// Config defines user-specified configurations unique to the EC2 detector +type Config struct { + // Tags is a list of regex's to match ec2 instance tag keys that users want + // to add as resource attributes to processed data + Tags []string `river:"tags,attr,optional"` + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + CloudAccountID: rac.ResourceAttributeConfig{Enabled: true}, + CloudAvailabilityZone: rac.ResourceAttributeConfig{Enabled: true}, + CloudPlatform: rac.ResourceAttributeConfig{Enabled: true}, + CloudProvider: rac.ResourceAttributeConfig{Enabled: true}, + CloudRegion: rac.ResourceAttributeConfig{Enabled: true}, + HostID: rac.ResourceAttributeConfig{Enabled: true}, + HostImageID: rac.ResourceAttributeConfig{Enabled: true}, + HostName: rac.ResourceAttributeConfig{Enabled: true}, + HostType: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + "tags": append([]string{}, args.Tags...), + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config to enable and disable resource attributes. +type ResourceAttributesConfig struct { + CloudAccountID rac.ResourceAttributeConfig `river:"cloud.account.id,block,optional"` + CloudAvailabilityZone rac.ResourceAttributeConfig `river:"cloud.availability_zone,block,optional"` + CloudPlatform rac.ResourceAttributeConfig `river:"cloud.platform,block,optional"` + CloudProvider rac.ResourceAttributeConfig `river:"cloud.provider,block,optional"` + CloudRegion rac.ResourceAttributeConfig `river:"cloud.region,block,optional"` + HostID rac.ResourceAttributeConfig `river:"host.id,block,optional"` + HostImageID rac.ResourceAttributeConfig `river:"host.image.id,block,optional"` + HostName rac.ResourceAttributeConfig `river:"host.name,block,optional"` + HostType rac.ResourceAttributeConfig `river:"host.type,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "cloud.account.id": r.CloudAccountID.Convert(), + "cloud.availability_zone": r.CloudAvailabilityZone.Convert(), + "cloud.platform": r.CloudPlatform.Convert(), + "cloud.provider": r.CloudProvider.Convert(), + "cloud.region": r.CloudRegion.Convert(), + "host.id": r.HostID.Convert(), + "host.image.id": r.HostImageID.Convert(), + "host.name": r.HostName.Convert(), + "host.type": r.HostType.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/aws/ecs/config.go b/component/otelcol/processor/resourcedetection/internal/aws/ecs/config.go new file mode 100644 index 000000000000..1532bd376567 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/aws/ecs/config.go @@ -0,0 +1,86 @@ +package ecs + +import ( + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "ecs" + +type Config struct { + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + AwsEcsClusterArn: rac.ResourceAttributeConfig{Enabled: true}, + AwsEcsLaunchtype: rac.ResourceAttributeConfig{Enabled: true}, + AwsEcsTaskArn: rac.ResourceAttributeConfig{Enabled: true}, + AwsEcsTaskFamily: rac.ResourceAttributeConfig{Enabled: true}, + AwsEcsTaskRevision: rac.ResourceAttributeConfig{Enabled: true}, + AwsLogGroupArns: rac.ResourceAttributeConfig{Enabled: true}, + AwsLogGroupNames: rac.ResourceAttributeConfig{Enabled: true}, + AwsLogStreamArns: rac.ResourceAttributeConfig{Enabled: true}, + AwsLogStreamNames: rac.ResourceAttributeConfig{Enabled: true}, + CloudAccountID: rac.ResourceAttributeConfig{Enabled: true}, + CloudAvailabilityZone: rac.ResourceAttributeConfig{Enabled: true}, + CloudPlatform: rac.ResourceAttributeConfig{Enabled: true}, + CloudProvider: rac.ResourceAttributeConfig{Enabled: true}, + CloudRegion: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args *Config) Convert() map[string]interface{} { + if args == nil { + return nil + } + + return map[string]interface{}{ + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for ecs resource attributes. +type ResourceAttributesConfig struct { + AwsEcsClusterArn rac.ResourceAttributeConfig `river:"aws.ecs.cluster.arn,block,optional"` + AwsEcsLaunchtype rac.ResourceAttributeConfig `river:"aws.ecs.launchtype,block,optional"` + AwsEcsTaskArn rac.ResourceAttributeConfig `river:"aws.ecs.task.arn,block,optional"` + AwsEcsTaskFamily rac.ResourceAttributeConfig `river:"aws.ecs.task.family,block,optional"` + AwsEcsTaskRevision rac.ResourceAttributeConfig `river:"aws.ecs.task.revision,block,optional"` + AwsLogGroupArns rac.ResourceAttributeConfig `river:"aws.log.group.arns,block,optional"` + AwsLogGroupNames rac.ResourceAttributeConfig `river:"aws.log.group.names,block,optional"` + AwsLogStreamArns rac.ResourceAttributeConfig `river:"aws.log.stream.arns,block,optional"` + AwsLogStreamNames rac.ResourceAttributeConfig `river:"aws.log.stream.names,block,optional"` + CloudAccountID rac.ResourceAttributeConfig `river:"cloud.account.id,block,optional"` + CloudAvailabilityZone rac.ResourceAttributeConfig `river:"cloud.availability_zone,block,optional"` + CloudPlatform rac.ResourceAttributeConfig `river:"cloud.platform,block,optional"` + CloudProvider rac.ResourceAttributeConfig `river:"cloud.provider,block,optional"` + CloudRegion rac.ResourceAttributeConfig `river:"cloud.region,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "aws.ecs.cluster.arn": r.AwsEcsClusterArn.Convert(), + "aws.ecs.launchtype": r.AwsEcsLaunchtype.Convert(), + "aws.ecs.task.arn": r.AwsEcsTaskArn.Convert(), + "aws.ecs.task.family": r.AwsEcsTaskFamily.Convert(), + "aws.ecs.task.revision": r.AwsEcsTaskRevision.Convert(), + "aws.log.group.arns": r.AwsLogGroupArns.Convert(), + "aws.log.group.names": r.AwsLogGroupNames.Convert(), + "aws.log.stream.arns": r.AwsLogStreamArns.Convert(), + "aws.log.stream.names": r.AwsLogStreamNames.Convert(), + "cloud.account.id": r.CloudAccountID.Convert(), + "cloud.availability_zone": r.CloudAvailabilityZone.Convert(), + "cloud.platform": r.CloudPlatform.Convert(), + "cloud.provider": r.CloudProvider.Convert(), + "cloud.region": r.CloudRegion.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/aws/eks/config.go b/component/otelcol/processor/resourcedetection/internal/aws/eks/config.go new file mode 100644 index 000000000000..6290180b3086 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/aws/eks/config.go @@ -0,0 +1,46 @@ +package eks + +import ( + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "eks" + +type Config struct { + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + CloudPlatform: rac.ResourceAttributeConfig{Enabled: true}, + CloudProvider: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for eks resource attributes. +type ResourceAttributesConfig struct { + CloudPlatform rac.ResourceAttributeConfig `river:"cloud.platform,block,optional"` + CloudProvider rac.ResourceAttributeConfig `river:"cloud.provider,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "cloud.platform": r.CloudPlatform.Convert(), + "cloud.provider": r.CloudProvider.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/aws/elasticbeanstalk/config.go b/component/otelcol/processor/resourcedetection/internal/aws/elasticbeanstalk/config.go new file mode 100644 index 000000000000..dd670372cee7 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/aws/elasticbeanstalk/config.go @@ -0,0 +1,55 @@ +package elasticbeanstalk + +import ( + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "elasticbeanstalk" + +type Config struct { + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + CloudPlatform: rac.ResourceAttributeConfig{Enabled: true}, + CloudProvider: rac.ResourceAttributeConfig{Enabled: true}, + DeploymentEnvironment: rac.ResourceAttributeConfig{Enabled: true}, + ServiceInstanceID: rac.ResourceAttributeConfig{Enabled: true}, + ServiceVersion: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for elastic_beanstalk resource attributes. +type ResourceAttributesConfig struct { + CloudPlatform rac.ResourceAttributeConfig `river:"cloud.platform,block,optional"` + CloudProvider rac.ResourceAttributeConfig `river:"cloud.provider,block,optional"` + DeploymentEnvironment rac.ResourceAttributeConfig `river:"deployment.environment,block,optional"` + ServiceInstanceID rac.ResourceAttributeConfig `river:"service.instance.id,block,optional"` + ServiceVersion rac.ResourceAttributeConfig `river:"service.version,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "cloud.platform": r.CloudPlatform.Convert(), + "cloud.provider": r.CloudProvider.Convert(), + "deployment.environment": r.DeploymentEnvironment.Convert(), + "service.instance.id": r.ServiceInstanceID.Convert(), + "service.version": r.ServiceVersion.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/aws/lambda/config.go b/component/otelcol/processor/resourcedetection/internal/aws/lambda/config.go new file mode 100644 index 000000000000..19a4cc7b4e80 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/aws/lambda/config.go @@ -0,0 +1,67 @@ +package lambda + +import ( + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "lambda" + +type Config struct { + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + AwsLogGroupNames: rac.ResourceAttributeConfig{Enabled: true}, + AwsLogStreamNames: rac.ResourceAttributeConfig{Enabled: true}, + CloudPlatform: rac.ResourceAttributeConfig{Enabled: true}, + CloudProvider: rac.ResourceAttributeConfig{Enabled: true}, + CloudRegion: rac.ResourceAttributeConfig{Enabled: true}, + FaasInstance: rac.ResourceAttributeConfig{Enabled: true}, + FaasMaxMemory: rac.ResourceAttributeConfig{Enabled: true}, + FaasName: rac.ResourceAttributeConfig{Enabled: true}, + FaasVersion: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for lambda resource attributes. +type ResourceAttributesConfig struct { + AwsLogGroupNames rac.ResourceAttributeConfig `river:"aws.log.group.names,block,optional"` + AwsLogStreamNames rac.ResourceAttributeConfig `river:"aws.log.stream.names,block,optional"` + CloudPlatform rac.ResourceAttributeConfig `river:"cloud.platform,block,optional"` + CloudProvider rac.ResourceAttributeConfig `river:"cloud.provider,block,optional"` + CloudRegion rac.ResourceAttributeConfig `river:"cloud.region,block,optional"` + FaasInstance rac.ResourceAttributeConfig `river:"faas.instance,block,optional"` + FaasMaxMemory rac.ResourceAttributeConfig `river:"faas.max_memory,block,optional"` + FaasName rac.ResourceAttributeConfig `river:"faas.name,block,optional"` + FaasVersion rac.ResourceAttributeConfig `river:"faas.version,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "aws.log.group.names": r.AwsLogGroupNames.Convert(), + "aws.log.stream.names": r.AwsLogStreamNames.Convert(), + "cloud.platform": r.CloudPlatform.Convert(), + "cloud.provider": r.CloudProvider.Convert(), + "cloud.region": r.CloudRegion.Convert(), + "faas.instance": r.FaasInstance.Convert(), + "faas.max_memory": r.FaasMaxMemory.Convert(), + "faas.name": r.FaasName.Convert(), + "faas.version": r.FaasVersion.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/azure/aks/config.go b/component/otelcol/processor/resourcedetection/internal/azure/aks/config.go new file mode 100644 index 000000000000..4501c4e33a6f --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/azure/aks/config.go @@ -0,0 +1,46 @@ +package aks + +import ( + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "aks" + +type Config struct { + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + CloudPlatform: rac.ResourceAttributeConfig{Enabled: true}, + CloudProvider: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for aks resource attributes. +type ResourceAttributesConfig struct { + CloudPlatform rac.ResourceAttributeConfig `river:"cloud.platform,block,optional"` + CloudProvider rac.ResourceAttributeConfig `river:"cloud.provider,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "cloud.platform": r.CloudPlatform.Convert(), + "cloud.provider": r.CloudProvider.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/azure/config.go b/component/otelcol/processor/resourcedetection/internal/azure/config.go new file mode 100644 index 000000000000..05e612d1d2d0 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/azure/config.go @@ -0,0 +1,70 @@ +package azure + +import ( + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "azure" + +type Config struct { + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + AzureResourcegroupName: rac.ResourceAttributeConfig{Enabled: true}, + AzureVMName: rac.ResourceAttributeConfig{Enabled: true}, + AzureVMScalesetName: rac.ResourceAttributeConfig{Enabled: true}, + AzureVMSize: rac.ResourceAttributeConfig{Enabled: true}, + CloudAccountID: rac.ResourceAttributeConfig{Enabled: true}, + CloudPlatform: rac.ResourceAttributeConfig{Enabled: true}, + CloudProvider: rac.ResourceAttributeConfig{Enabled: true}, + CloudRegion: rac.ResourceAttributeConfig{Enabled: true}, + HostID: rac.ResourceAttributeConfig{Enabled: true}, + HostName: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for azure resource attributes. +type ResourceAttributesConfig struct { + AzureResourcegroupName rac.ResourceAttributeConfig `river:"azure.resourcegroup.name,block,optional"` + AzureVMName rac.ResourceAttributeConfig `river:"azure.vm.name,block,optional"` + AzureVMScalesetName rac.ResourceAttributeConfig `river:"azure.vm.scaleset.name,block,optional"` + AzureVMSize rac.ResourceAttributeConfig `river:"azure.vm.size,block,optional"` + CloudAccountID rac.ResourceAttributeConfig `river:"cloud.account.id,block,optional"` + CloudPlatform rac.ResourceAttributeConfig `river:"cloud.platform,block,optional"` + CloudProvider rac.ResourceAttributeConfig `river:"cloud.provider,block,optional"` + CloudRegion rac.ResourceAttributeConfig `river:"cloud.region,block,optional"` + HostID rac.ResourceAttributeConfig `river:"host.id,block,optional"` + HostName rac.ResourceAttributeConfig `river:"host.name,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "azure.resourcegroup.name": r.AzureResourcegroupName.Convert(), + "azure.vm.name": r.AzureVMName.Convert(), + "azure.vm.scaleset.name": r.AzureVMScalesetName.Convert(), + "azure.vm.size": r.AzureVMSize.Convert(), + "cloud.account.id": r.CloudAccountID.Convert(), + "cloud.platform": r.CloudPlatform.Convert(), + "cloud.provider": r.CloudProvider.Convert(), + "cloud.region": r.CloudRegion.Convert(), + "host.id": r.HostID.Convert(), + "host.name": r.HostName.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/consul/config.go b/component/otelcol/processor/resourcedetection/internal/consul/config.go new file mode 100644 index 000000000000..4cc2e9b5beb3 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/consul/config.go @@ -0,0 +1,94 @@ +package consul + +import ( + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" + "github.com/grafana/river/rivertypes" + "go.opentelemetry.io/collector/config/configopaque" +) + +const Name = "consul" + +// The struct requires no user-specified fields by default as consul agent's default +// configuration will be provided to the API client. +// See `consul.go#NewDetector` for more information. +type Config struct { + // Address is the address of the Consul server + Address string `river:"address,attr,optional"` + + // Datacenter to use. If not provided, the default agent datacenter is used. + Datacenter string `river:"datacenter,attr,optional"` + + // Token is used to provide a per-request ACL token which overrides the + // agent's default (empty) token. Token is only required if + // [Consul's ACL System](https://www.consul.io/docs/security/acl/acl-system) + // is enabled. + Token rivertypes.Secret `river:"token,attr,optional"` + + // TokenFile is not necessary in River because users can use the local.file + // Flow component instead. + // + // TokenFile string `river:"token_file"` + + // Namespace is the name of the namespace to send along for the request + // when no other Namespace is present in the QueryOptions + Namespace string `river:"namespace,attr,optional"` + + // Allowlist of [Consul Metadata](https://www.consul.io/docs/agent/options#node_meta) + // keys to use as resource attributes. + MetaLabels []string `river:"meta,attr,optional"` + + // ResourceAttributes configuration for Consul detector + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + CloudRegion: rac.ResourceAttributeConfig{Enabled: true}, + HostID: rac.ResourceAttributeConfig{Enabled: true}, + HostName: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + //TODO(ptodev): Change the OTel Collector's "meta" param to be a slice instead of a map. + var metaLabels map[string]string + if args.MetaLabels != nil { + metaLabels = make(map[string]string, len(args.MetaLabels)) + for _, label := range args.MetaLabels { + metaLabels[label] = "" + } + } + + return map[string]interface{}{ + "address": args.Address, + "datacenter": args.Datacenter, + "token": configopaque.String(args.Token), + "namespace": args.Namespace, + "meta": metaLabels, + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for consul resource attributes. +type ResourceAttributesConfig struct { + CloudRegion rac.ResourceAttributeConfig `river:"cloud.region,block,optional"` + HostID rac.ResourceAttributeConfig `river:"host.id,block,optional"` + HostName rac.ResourceAttributeConfig `river:"host.name,block,optional"` +} + +func (r *ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "cloud.region": r.CloudRegion.Convert(), + "host.id": r.HostID.Convert(), + "host.name": r.HostName.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/docker/config.go b/component/otelcol/processor/resourcedetection/internal/docker/config.go new file mode 100644 index 000000000000..f8c1bdc39b82 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/docker/config.go @@ -0,0 +1,46 @@ +package docker + +import ( + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "docker" + +type Config struct { + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + HostName: rac.ResourceAttributeConfig{Enabled: true}, + OsType: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for docker resource attributes. +type ResourceAttributesConfig struct { + HostName rac.ResourceAttributeConfig `river:"host.name,block,optional"` + OsType rac.ResourceAttributeConfig `river:"os.type,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "host.name": r.HostName.Convert(), + "os.type": r.OsType.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/gcp/config.go b/component/otelcol/processor/resourcedetection/internal/gcp/config.go new file mode 100644 index 000000000000..76395828a97c --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/gcp/config.go @@ -0,0 +1,91 @@ +package gcp + +import ( + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "gcp" + +type Config struct { + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + CloudAccountID: rac.ResourceAttributeConfig{Enabled: true}, + CloudAvailabilityZone: rac.ResourceAttributeConfig{Enabled: true}, + CloudPlatform: rac.ResourceAttributeConfig{Enabled: true}, + CloudProvider: rac.ResourceAttributeConfig{Enabled: true}, + CloudRegion: rac.ResourceAttributeConfig{Enabled: true}, + FaasID: rac.ResourceAttributeConfig{Enabled: true}, + FaasInstance: rac.ResourceAttributeConfig{Enabled: true}, + FaasName: rac.ResourceAttributeConfig{Enabled: true}, + FaasVersion: rac.ResourceAttributeConfig{Enabled: true}, + GcpCloudRunJobExecution: rac.ResourceAttributeConfig{Enabled: true}, + GcpCloudRunJobTaskIndex: rac.ResourceAttributeConfig{Enabled: true}, + GcpGceInstanceHostname: rac.ResourceAttributeConfig{Enabled: false}, + GcpGceInstanceName: rac.ResourceAttributeConfig{Enabled: false}, + HostID: rac.ResourceAttributeConfig{Enabled: true}, + HostName: rac.ResourceAttributeConfig{Enabled: true}, + HostType: rac.ResourceAttributeConfig{Enabled: true}, + K8sClusterName: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for gcp resource attributes. +type ResourceAttributesConfig struct { + CloudAccountID rac.ResourceAttributeConfig `river:"cloud.account.id,block,optional"` + CloudAvailabilityZone rac.ResourceAttributeConfig `river:"cloud.availability_zone,block,optional"` + CloudPlatform rac.ResourceAttributeConfig `river:"cloud.platform,block,optional"` + CloudProvider rac.ResourceAttributeConfig `river:"cloud.provider,block,optional"` + CloudRegion rac.ResourceAttributeConfig `river:"cloud.region,block,optional"` + FaasID rac.ResourceAttributeConfig `river:"faas.id,block,optional"` + FaasInstance rac.ResourceAttributeConfig `river:"faas.instance,block,optional"` + FaasName rac.ResourceAttributeConfig `river:"faas.name,block,optional"` + FaasVersion rac.ResourceAttributeConfig `river:"faas.version,block,optional"` + GcpCloudRunJobExecution rac.ResourceAttributeConfig `river:"gcp.cloud_run.job.execution,block,optional"` + GcpCloudRunJobTaskIndex rac.ResourceAttributeConfig `river:"gcp.cloud_run.job.task_index,block,optional"` + GcpGceInstanceHostname rac.ResourceAttributeConfig `river:"gcp.gce.instance.hostname,block,optional"` + GcpGceInstanceName rac.ResourceAttributeConfig `river:"gcp.gce.instance.name,block,optional"` + HostID rac.ResourceAttributeConfig `river:"host.id,block,optional"` + HostName rac.ResourceAttributeConfig `river:"host.name,block,optional"` + HostType rac.ResourceAttributeConfig `river:"host.type,block,optional"` + K8sClusterName rac.ResourceAttributeConfig `river:"k8s.cluster.name,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "cloud.account.id": r.CloudAccountID.Convert(), + "cloud.availability_zone": r.CloudAvailabilityZone.Convert(), + "cloud.platform": r.CloudPlatform.Convert(), + "cloud.provider": r.CloudProvider.Convert(), + "cloud.region": r.CloudRegion.Convert(), + "faas.id": r.FaasID.Convert(), + "faas.instance": r.FaasInstance.Convert(), + "faas.name": r.FaasName.Convert(), + "faas.version": r.FaasVersion.Convert(), + "gcp.cloud_run.job.execution": r.GcpCloudRunJobExecution.Convert(), + "gcp.cloud_run.job.task_index": r.GcpCloudRunJobTaskIndex.Convert(), + "gcp.gce.instance.hostname": r.GcpGceInstanceHostname.Convert(), + "gcp.gce.instance.name": r.GcpGceInstanceName.Convert(), + "host.id": r.HostID.Convert(), + "host.name": r.HostName.Convert(), + "host.type": r.HostType.Convert(), + "k8s.cluster.name": r.K8sClusterName.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/heroku/config.go b/component/otelcol/processor/resourcedetection/internal/heroku/config.go new file mode 100644 index 000000000000..6e7681269abb --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/heroku/config.go @@ -0,0 +1,64 @@ +package heroku + +import ( + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "heroku" + +type Config struct { + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + CloudProvider: rac.ResourceAttributeConfig{Enabled: true}, + HerokuAppID: rac.ResourceAttributeConfig{Enabled: true}, + HerokuDynoID: rac.ResourceAttributeConfig{Enabled: true}, + HerokuReleaseCommit: rac.ResourceAttributeConfig{Enabled: true}, + HerokuReleaseCreationTimestamp: rac.ResourceAttributeConfig{Enabled: true}, + ServiceInstanceID: rac.ResourceAttributeConfig{Enabled: true}, + ServiceName: rac.ResourceAttributeConfig{Enabled: true}, + ServiceVersion: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for heroku resource attributes. +type ResourceAttributesConfig struct { + CloudProvider rac.ResourceAttributeConfig `river:"cloud.provider,block,optional"` + HerokuAppID rac.ResourceAttributeConfig `river:"heroku.app.id,block,optional"` + HerokuDynoID rac.ResourceAttributeConfig `river:"heroku.dyno.id,block,optional"` + HerokuReleaseCommit rac.ResourceAttributeConfig `river:"heroku.release.commit,block,optional"` + HerokuReleaseCreationTimestamp rac.ResourceAttributeConfig `river:"heroku.release.creation_timestamp,block,optional"` + ServiceInstanceID rac.ResourceAttributeConfig `river:"service.instance.id,block,optional"` + ServiceName rac.ResourceAttributeConfig `river:"service.name,block,optional"` + ServiceVersion rac.ResourceAttributeConfig `river:"service.version,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "cloud.provider": r.CloudProvider.Convert(), + "heroku.app.id": r.HerokuAppID.Convert(), + "heroku.dyno.id": r.HerokuDynoID.Convert(), + "heroku.release.commit": r.HerokuReleaseCommit.Convert(), + "heroku.release.creation_timestamp": r.HerokuReleaseCreationTimestamp.Convert(), + "service.instance.id": r.ServiceInstanceID.Convert(), + "service.name": r.ServiceName.Convert(), + "service.version": r.ServiceVersion.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/k8snode/config.go b/component/otelcol/processor/resourcedetection/internal/k8snode/config.go new file mode 100644 index 000000000000..8d47362eecb6 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/k8snode/config.go @@ -0,0 +1,75 @@ +package k8snode + +import ( + "github.com/grafana/agent/component/otelcol" + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "kubernetes_node" + +type Config struct { + KubernetesAPIConfig otelcol.KubernetesAPIConfig `river:",squash"` + // NodeFromEnv can be used to extract the node name from an environment + // variable. The value must be the name of the environment variable. + // This is useful when the node where an Agent will run on cannot be + // predicted. In such cases, the Kubernetes downward API can be used to + // add the node name to each pod as an environment variable. K8s tagger + // can then read this value and filter pods by it. + // + // For example, node name can be passed to each agent with the downward API as follows + // + // env: + // - name: K8S_NODE_NAME + // valueFrom: + // fieldRef: + // fieldPath: spec.nodeName + // + // Then the NodeFromEnv field can be set to `K8S_NODE_NAME` to filter all pods by the node that + // the agent is running on. + // + // More on downward API here: https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/ + NodeFromEnvVar string `river:"node_from_env_var,attr,optional"` + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +var DefaultArguments = Config{ + KubernetesAPIConfig: otelcol.KubernetesAPIConfig{ + AuthType: otelcol.KubernetesAPIConfig_AuthType_None, + }, + NodeFromEnvVar: "K8S_NODE_NAME", + ResourceAttributes: ResourceAttributesConfig{ + K8sNodeName: rac.ResourceAttributeConfig{Enabled: true}, + K8sNodeUID: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (c *Config) SetToDefault() { + *c = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + //TODO: K8sAPIConfig is squashed - is there a better way to "convert" it? + "auth_type": args.KubernetesAPIConfig.AuthType, + "context": args.KubernetesAPIConfig.Context, + "node_from_env_var": args.NodeFromEnvVar, + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for k8snode resource attributes. +type ResourceAttributesConfig struct { + K8sNodeName rac.ResourceAttributeConfig `river:"k8s.node.name,block,optional"` + K8sNodeUID rac.ResourceAttributeConfig `river:"k8s.node.uid,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "k8s.node.name": r.K8sNodeName.Convert(), + "k8s.node.uid": r.K8sNodeUID.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/openshift/config.go b/component/otelcol/processor/resourcedetection/internal/openshift/config.go new file mode 100644 index 000000000000..362cd9bff459 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/openshift/config.go @@ -0,0 +1,68 @@ +package openshift + +import ( + "github.com/grafana/agent/component/otelcol" + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "openshift" + +// Config can contain user-specified inputs to overwrite default values. +// See `openshift.go#NewDetector` for more information. +type Config struct { + // Address is the address of the openshift api server + Address string `river:"address,attr,optional"` + + // Token is used to identify against the openshift api server + Token string `river:"token,attr,optional"` + + // TLSSettings contains TLS configurations that are specific to client + // connection used to communicate with the Openshift API. + TLSSettings otelcol.TLSClientArguments `river:"tls,block,optional"` + + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +// DefaultArguments holds default settings for Config. +var DefaultArguments = Config{ + ResourceAttributes: ResourceAttributesConfig{ + CloudPlatform: rac.ResourceAttributeConfig{Enabled: true}, + CloudProvider: rac.ResourceAttributeConfig{Enabled: true}, + CloudRegion: rac.ResourceAttributeConfig{Enabled: true}, + K8sClusterName: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (args *Config) SetToDefault() { + *args = DefaultArguments +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + "address": args.Address, + "token": args.Token, + "tls": args.TLSSettings.Convert(), + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for openshift resource attributes. +type ResourceAttributesConfig struct { + CloudPlatform rac.ResourceAttributeConfig `river:"cloud.platform,block,optional"` + CloudProvider rac.ResourceAttributeConfig `river:"cloud.provider,block,optional"` + CloudRegion rac.ResourceAttributeConfig `river:"cloud.region,block,optional"` + K8sClusterName rac.ResourceAttributeConfig `river:"k8s.cluster.name,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "cloud.platform": r.CloudPlatform.Convert(), + "cloud.provider": r.CloudProvider.Convert(), + "cloud.region": r.CloudRegion.Convert(), + "k8s.cluster.name": r.K8sClusterName.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/resource_attribute_config/resource_attribute_config.go b/component/otelcol/processor/resourcedetection/internal/resource_attribute_config/resource_attribute_config.go new file mode 100644 index 000000000000..ff5540a2f539 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/resource_attribute_config/resource_attribute_config.go @@ -0,0 +1,12 @@ +package resource_attribute_config + +// Configures whether a resource attribute should be enabled or not. +type ResourceAttributeConfig struct { + Enabled bool `river:"enabled,attr"` +} + +func (r ResourceAttributeConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "enabled": r.Enabled, + } +} diff --git a/component/otelcol/processor/resourcedetection/internal/system/config.go b/component/otelcol/processor/resourcedetection/internal/system/config.go new file mode 100644 index 000000000000..82e25cb45e97 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/internal/system/config.go @@ -0,0 +1,95 @@ +package system + +import ( + "fmt" + + rac "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/resource_attribute_config" + "github.com/grafana/river" +) + +const Name = "system" + +// Config defines user-specified configurations unique to the system detector +type Config struct { + // The HostnameSources is a priority list of sources from which hostname will be fetched. + // In case of the error in fetching hostname from source, + // the next source from the list will be considered. + HostnameSources []string `river:"hostname_sources,attr,optional"` + + ResourceAttributes ResourceAttributesConfig `river:"resource_attributes,block,optional"` +} + +var DefaultArguments = Config{ + HostnameSources: []string{"dns", "os"}, + ResourceAttributes: ResourceAttributesConfig{ + HostArch: rac.ResourceAttributeConfig{Enabled: false}, + HostCPUCacheL2Size: rac.ResourceAttributeConfig{Enabled: false}, + HostCPUFamily: rac.ResourceAttributeConfig{Enabled: false}, + HostCPUModelID: rac.ResourceAttributeConfig{Enabled: false}, + HostCPUModelName: rac.ResourceAttributeConfig{Enabled: false}, + HostCPUStepping: rac.ResourceAttributeConfig{Enabled: false}, + HostCPUVendorID: rac.ResourceAttributeConfig{Enabled: false}, + HostID: rac.ResourceAttributeConfig{Enabled: false}, + HostName: rac.ResourceAttributeConfig{Enabled: true}, + OsDescription: rac.ResourceAttributeConfig{Enabled: false}, + OsType: rac.ResourceAttributeConfig{Enabled: true}, + }, +} + +var _ river.Defaulter = (*Config)(nil) + +// SetToDefault implements river.Defaulter. +func (c *Config) SetToDefault() { + *c = DefaultArguments +} + +// Validate config +func (cfg *Config) Validate() error { + for _, hostnameSource := range cfg.HostnameSources { + switch hostnameSource { + case "os", "dns", "cname", "lookup": + // Valid option - nothing to do + default: + return fmt.Errorf("invalid hostname source: %s", hostnameSource) + } + } + return nil +} + +func (args Config) Convert() map[string]interface{} { + return map[string]interface{}{ + "hostname_sources": args.HostnameSources, + "resource_attributes": args.ResourceAttributes.Convert(), + } +} + +// ResourceAttributesConfig provides config for system resource attributes. +type ResourceAttributesConfig struct { + HostArch rac.ResourceAttributeConfig `river:"host.arch,block,optional"` + HostCPUCacheL2Size rac.ResourceAttributeConfig `river:"host.cpu.cache.l2.size,block,optional"` + HostCPUFamily rac.ResourceAttributeConfig `river:"host.cpu.family,block,optional"` + HostCPUModelID rac.ResourceAttributeConfig `river:"host.cpu.model.id,block,optional"` + HostCPUModelName rac.ResourceAttributeConfig `river:"host.cpu.model.name,block,optional"` + HostCPUStepping rac.ResourceAttributeConfig `river:"host.cpu.stepping,block,optional"` + HostCPUVendorID rac.ResourceAttributeConfig `river:"host.cpu.vendor.id,block,optional"` + HostID rac.ResourceAttributeConfig `river:"host.id,block,optional"` + HostName rac.ResourceAttributeConfig `river:"host.name,block,optional"` + OsDescription rac.ResourceAttributeConfig `river:"os.description,block,optional"` + OsType rac.ResourceAttributeConfig `river:"os.type,block,optional"` +} + +func (r ResourceAttributesConfig) Convert() map[string]interface{} { + return map[string]interface{}{ + "host.arch": r.HostArch.Convert(), + "host.cpu.cache.l2.size": r.HostCPUCacheL2Size.Convert(), + "host.cpu.family": r.HostCPUFamily.Convert(), + "host.cpu.model.id": r.HostCPUModelID.Convert(), + "host.cpu.model.name": r.HostCPUModelName.Convert(), + "host.cpu.stepping": r.HostCPUStepping.Convert(), + "host.cpu.vendor.id": r.HostCPUVendorID.Convert(), + "host.id": r.HostID.Convert(), + "host.name": r.HostName.Convert(), + "os.description": r.OsDescription.Convert(), + "os.type": r.OsType.Convert(), + } +} diff --git a/component/otelcol/processor/resourcedetection/resourcedetection.go b/component/otelcol/processor/resourcedetection/resourcedetection.go new file mode 100644 index 000000000000..806d72c9d2e5 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/resourcedetection.go @@ -0,0 +1,247 @@ +package resourcedetection + +import ( + "fmt" + "time" + + "github.com/grafana/agent/component" + "github.com/grafana/agent/component/otelcol" + "github.com/grafana/agent/component/otelcol/processor" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/aws/ec2" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/aws/ecs" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/aws/eks" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/aws/elasticbeanstalk" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/aws/lambda" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/azure" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/azure/aks" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/consul" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/docker" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/gcp" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/heroku" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/k8snode" + kubernetes_node "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/k8snode" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/openshift" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/system" + "github.com/grafana/river" + "github.com/mitchellh/mapstructure" + "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor" + otelcomponent "go.opentelemetry.io/collector/component" + otelextension "go.opentelemetry.io/collector/extension" +) + +func init() { + component.Register(component.Registration{ + Name: "otelcol.processor.resourcedetection", + Args: Arguments{}, + Exports: otelcol.ConsumerExports{}, + + Build: func(opts component.Options, args component.Arguments) (component.Component, error) { + fact := resourcedetectionprocessor.NewFactory() + return processor.New(opts, fact, args.(Arguments)) + }, + }) +} + +// Arguments configures the otelcol.processor.resourcedetection component. +type Arguments struct { + // Detectors is an ordered list of named detectors that should be + // run to attempt to detect resource information. + Detectors []string `river:"detectors,attr,optional"` + + // Override indicates whether any existing resource attributes + // should be overridden or preserved. Defaults to true. + Override bool `river:"override,attr,optional"` + + // DetectorConfig is a list of settings specific to all detectors + DetectorConfig DetectorConfig `river:",squash"` + + // HTTP client settings for the detector + // Timeout default is 5s + Timeout time.Duration `river:"timeout,attr,optional"` + // Client otelcol.HTTPClientArguments `river:",squash"` + //TODO: Uncomment this later, and remove Timeout? + // Can we just get away with a timeout, or do we need all the http client settings? + // It seems that HTTP client settings are only used in the ec2 detection via ClientFromContext. + // This seems like a very niche use case, so for now I won't implement it in the Agent. + // If we do implement it in the Agent, I am not sure how to document the HTTP client settings. + // We'd have to mention that they're only for a very specific use case. + + // Output configures where to send processed data. Required. + Output *otelcol.ConsumerArguments `river:"output,block"` +} + +// DetectorConfig contains user-specified configurations unique to all individual detectors +type DetectorConfig struct { + // EC2Config contains user-specified configurations for the EC2 detector + EC2Config ec2.Config `river:"ec2,block,optional"` + + // ECSConfig contains user-specified configurations for the ECS detector + ECSConfig ecs.Config `river:"ecs,block,optional"` + + // EKSConfig contains user-specified configurations for the EKS detector + EKSConfig eks.Config `river:"eks,block,optional"` + + // Elasticbeanstalk contains user-specified configurations for the elasticbeanstalk detector + ElasticbeanstalkConfig elasticbeanstalk.Config `river:"elasticbeanstalk,block,optional"` + + // Lambda contains user-specified configurations for the lambda detector + LambdaConfig lambda.Config `river:"lambda,block,optional"` + + // Azure contains user-specified configurations for the azure detector + AzureConfig azure.Config `river:"azure,block,optional"` + + // Aks contains user-specified configurations for the aks detector + AksConfig aks.Config `river:"aks,block,optional"` + + // ConsulConfig contains user-specified configurations for the Consul detector + ConsulConfig consul.Config `river:"consul,block,optional"` + + // DockerConfig contains user-specified configurations for the docker detector + DockerConfig docker.Config `river:"docker,block,optional"` + + // GcpConfig contains user-specified configurations for the gcp detector + GcpConfig gcp.Config `river:"gcp,block,optional"` + + // HerokuConfig contains user-specified configurations for the heroku detector + HerokuConfig heroku.Config `river:"heroku,block,optional"` + + // SystemConfig contains user-specified configurations for the System detector + SystemConfig system.Config `river:"system,block,optional"` + + // OpenShift contains user-specified configurations for the Openshift detector + OpenShiftConfig openshift.Config `river:"openshift,block,optional"` + + // KubernetesNode contains user-specified configurations for the K8SNode detector + KubernetesNodeConfig kubernetes_node.Config `river:"kubernetes_node,block,optional"` +} + +var ( + _ processor.Arguments = Arguments{} + _ river.Validator = (*Arguments)(nil) + _ river.Defaulter = (*Arguments)(nil) +) + +// DefaultArguments holds default settings for Arguments. +var DefaultArguments = Arguments{ + Detectors: []string{"env"}, + Override: true, + Timeout: 5 * time.Second, + DetectorConfig: DetectorConfig{ + EC2Config: ec2.DefaultArguments, + ECSConfig: ecs.DefaultArguments, + EKSConfig: eks.DefaultArguments, + ElasticbeanstalkConfig: elasticbeanstalk.DefaultArguments, + LambdaConfig: lambda.DefaultArguments, + AzureConfig: azure.DefaultArguments, + AksConfig: aks.DefaultArguments, + ConsulConfig: consul.DefaultArguments, + DockerConfig: docker.DefaultArguments, + GcpConfig: gcp.DefaultArguments, + HerokuConfig: heroku.DefaultArguments, + SystemConfig: system.DefaultArguments, + OpenShiftConfig: openshift.DefaultArguments, + KubernetesNodeConfig: kubernetes_node.DefaultArguments, + }, +} + +// SetToDefault implements river.Defaulter. +func (args *Arguments) SetToDefault() { + *args = DefaultArguments +} + +// Validate implements river.Validator. +func (args *Arguments) Validate() error { + if len(args.Detectors) == 0 { + return fmt.Errorf("at least one detector must be specified") + } + + for _, detector := range args.Detectors { + switch detector { + case "env", + ec2.Name, + ecs.Name, + eks.Name, + elasticbeanstalk.Name, + lambda.Name, + azure.Name, + aks.Name, + consul.Name, + docker.Name, + gcp.Name, + heroku.Name, + system.Name, + openshift.Name, + k8snode.Name: + // Valid option - nothing to do + default: + return fmt.Errorf("invalid detector: %s", detector) + } + } + + return nil +} + +func (args Arguments) ConvertDetectors() []string { + if args.Detectors == nil { + return nil + } + + res := make([]string, 0, len(args.Detectors)) + for _, detector := range args.Detectors { + switch detector { + case k8snode.Name: + res = append(res, "k8snode") + default: + res = append(res, detector) + } + } + return res +} + +// Convert implements processor.Arguments. +func (args Arguments) Convert() (otelcomponent.Config, error) { + input := make(map[string]interface{}) + + input["detectors"] = args.ConvertDetectors() + input["override"] = args.Override + input["timeout"] = args.Timeout + + input["ec2"] = args.DetectorConfig.EC2Config.Convert() + input["ecs"] = args.DetectorConfig.ECSConfig.Convert() + input["eks"] = args.DetectorConfig.EKSConfig.Convert() + input["elasticbeanstalk"] = args.DetectorConfig.ElasticbeanstalkConfig.Convert() + input["lambda"] = args.DetectorConfig.LambdaConfig.Convert() + input["azure"] = args.DetectorConfig.AzureConfig.Convert() + input["aks"] = args.DetectorConfig.AksConfig.Convert() + input["consul"] = args.DetectorConfig.ConsulConfig.Convert() + input["docker"] = args.DetectorConfig.DockerConfig.Convert() + input["gcp"] = args.DetectorConfig.GcpConfig.Convert() + input["heroku"] = args.DetectorConfig.HerokuConfig.Convert() + input["system"] = args.DetectorConfig.SystemConfig.Convert() + input["openshift"] = args.DetectorConfig.OpenShiftConfig.Convert() + input["k8snode"] = args.DetectorConfig.KubernetesNodeConfig.Convert() + + var result resourcedetectionprocessor.Config + err := mapstructure.Decode(input, &result) + + if err != nil { + return nil, err + } + + return &result, nil +} + +// Extensions implements processor.Arguments. +func (args Arguments) Extensions() map[otelcomponent.ID]otelextension.Extension { + return nil +} + +// Exporters implements processor.Arguments. +func (args Arguments) Exporters() map[otelcomponent.DataType]map[otelcomponent.ID]otelcomponent.Component { + return nil +} + +// NextConsumers implements processor.Arguments. +func (args Arguments) NextConsumers() *otelcol.ConsumerArguments { + return args.Output +} diff --git a/component/otelcol/processor/resourcedetection/resourcedetection_test.go b/component/otelcol/processor/resourcedetection/resourcedetection_test.go new file mode 100644 index 000000000000..6fbbf0280e06 --- /dev/null +++ b/component/otelcol/processor/resourcedetection/resourcedetection_test.go @@ -0,0 +1,1527 @@ +package resourcedetection_test + +import ( + "testing" + "time" + + "github.com/grafana/agent/component/otelcol/processor/resourcedetection" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/aws/ec2" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/aws/ecs" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/aws/eks" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/aws/elasticbeanstalk" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/aws/lambda" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/azure" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/azure/aks" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/consul" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/docker" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/gcp" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/heroku" + kubernetes_node "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/k8snode" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/openshift" + "github.com/grafana/agent/component/otelcol/processor/resourcedetection/internal/system" + "github.com/grafana/river" + "github.com/mitchellh/mapstructure" + "github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor" + "github.com/stretchr/testify/require" +) + +func TestArguments_UnmarshalRiver(t *testing.T) { + tests := []struct { + testName string + cfg string + expected map[string]interface{} + errorMsg string + }{ + { + testName: "err_no_detector", + cfg: ` + detectors = [] + output {} + `, + errorMsg: "at least one detector must be specified", + }, + { + testName: "invalid_detector", + cfg: ` + detectors = ["non-existent-detector"] + output {} + `, + errorMsg: "invalid detector: non-existent-detector", + }, + { + testName: "invalid_detector_and_all_valid_ones", + cfg: ` + detectors = ["non-existent-detector2", "env", "ec2", "ecs", "eks", "elasticbeanstalk", "lambda", "azure", "aks", "consul", "docker", "gcp", "heroku", "system", "openshift", "kubernetes_node"] + output {} + `, + errorMsg: "invalid detector: non-existent-detector2", + }, + { + testName: "all_detectors_with_defaults", + cfg: ` + detectors = ["env", "ec2", "ecs", "eks", "elasticbeanstalk", "lambda", "azure", "aks", "consul", "docker", "gcp", "heroku", "system", "openshift", "kubernetes_node"] + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"env", "ec2", "ecs", "eks", "elasticbeanstalk", "lambda", "azure", "aks", "consul", "docker", "gcp", "heroku", "system", "openshift", "k8snode"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "default_detector", + cfg: ` + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"env"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "ec2_defaults", + cfg: ` + detectors = ["ec2"] + ec2 { + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"ec2"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": map[string]interface{}{ + "tags": []string{}, + "resource_attributes": map[string]interface{}{ + "cloud.account.id": map[string]interface{}{"enabled": true}, + "cloud.availability_zone": map[string]interface{}{"enabled": true}, + "cloud.platform": map[string]interface{}{"enabled": true}, + "cloud.provider": map[string]interface{}{"enabled": true}, + "cloud.region": map[string]interface{}{"enabled": true}, + "host.id": map[string]interface{}{"enabled": true}, + "host.image.id": map[string]interface{}{"enabled": true}, + "host.name": map[string]interface{}{"enabled": true}, + "host.type": map[string]interface{}{"enabled": true}, + }, + }, + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "ec2_defaults_empty_resource_attributes", + cfg: ` + detectors = ["ec2"] + ec2 { + resource_attributes {} + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"ec2"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": map[string]interface{}{ + "tags": []string{}, + "resource_attributes": map[string]interface{}{ + "cloud.account.id": map[string]interface{}{"enabled": true}, + "cloud.availability_zone": map[string]interface{}{"enabled": true}, + "cloud.platform": map[string]interface{}{"enabled": true}, + "cloud.provider": map[string]interface{}{"enabled": true}, + "cloud.region": map[string]interface{}{"enabled": true}, + "host.id": map[string]interface{}{"enabled": true}, + "host.image.id": map[string]interface{}{"enabled": true}, + "host.name": map[string]interface{}{"enabled": true}, + "host.type": map[string]interface{}{"enabled": true}, + }, + }, + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "ec2_explicit", + cfg: ` + detectors = ["ec2"] + ec2 { + tags = ["^tag1$", "^tag2$", "^label.*$"] + resource_attributes { + cloud.account.id { enabled = true } + cloud.availability_zone { enabled = true } + cloud.platform { enabled = true } + cloud.provider { enabled = true } + cloud.region { enabled = true } + host.id { enabled = true } + host.image.id { enabled = false } + host.name { enabled = false } + host.type { enabled = false } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"ec2"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": map[string]interface{}{ + "tags": []string{"^tag1$", "^tag2$", "^label.*$"}, + "resource_attributes": map[string]interface{}{ + "cloud.account.id": map[string]interface{}{"enabled": true}, + "cloud.availability_zone": map[string]interface{}{"enabled": true}, + "cloud.platform": map[string]interface{}{"enabled": true}, + "cloud.provider": map[string]interface{}{"enabled": true}, + "cloud.region": map[string]interface{}{"enabled": true}, + "host.id": map[string]interface{}{"enabled": true}, + "host.image.id": map[string]interface{}{"enabled": false}, + "host.name": map[string]interface{}{"enabled": false}, + "host.type": map[string]interface{}{"enabled": false}, + }, + }, + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "ecs_defaults", + cfg: ` + detectors = ["ecs"] + ecs { + resource_attributes { + aws.ecs.cluster.arn { enabled = true } + aws.ecs.launchtype { enabled = true } + aws.ecs.task.arn { enabled = true } + aws.ecs.task.family { enabled = true } + aws.ecs.task.revision { enabled = true } + aws.log.group.arns { enabled = true } + aws.log.group.names { enabled = false } + // aws.log.stream.arns { enabled = true } + // aws.log.stream.names { enabled = true } + // cloud.account.id { enabled = true } + // cloud.availability_zone { enabled = true } + // cloud.platform { enabled = true } + // cloud.provider { enabled = true } + // cloud.region { enabled = true } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"ecs"}, + "timeout": 5 * time.Second, + "override": true, + "ecs": map[string]interface{}{ + "tags": []string{}, + "resource_attributes": map[string]interface{}{ + "aws.ecs.cluster.arn": map[string]interface{}{"enabled": true}, + "aws.ecs.launchtype": map[string]interface{}{"enabled": true}, + "aws.ecs.task.arn": map[string]interface{}{"enabled": true}, + "aws.ecs.task.family": map[string]interface{}{"enabled": true}, + "aws.ecs.task.revision": map[string]interface{}{"enabled": true}, + "aws.log.group.arns": map[string]interface{}{"enabled": true}, + "aws.log.group.names": map[string]interface{}{"enabled": false}, + "aws.log.stream.arns": map[string]interface{}{"enabled": true}, + "aws.log.stream.names": map[string]interface{}{"enabled": true}, + "cloud.account.id": map[string]interface{}{"enabled": true}, + "cloud.availability_zone": map[string]interface{}{"enabled": true}, + "cloud.platform": map[string]interface{}{"enabled": true}, + "cloud.provider": map[string]interface{}{"enabled": true}, + "cloud.region": map[string]interface{}{"enabled": true}, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "ecs_explicit", + cfg: ` + detectors = ["ecs"] + ecs { + resource_attributes { + aws.ecs.cluster.arn { enabled = true } + aws.ecs.launchtype { enabled = true } + aws.ecs.task.arn { enabled = true } + aws.ecs.task.family { enabled = true } + aws.ecs.task.revision { enabled = true } + aws.log.group.arns { enabled = true } + aws.log.group.names { enabled = false } + // aws.log.stream.arns { enabled = true } + // aws.log.stream.names { enabled = true } + // cloud.account.id { enabled = true } + // cloud.availability_zone { enabled = true } + // cloud.platform { enabled = true } + // cloud.provider { enabled = true } + // cloud.region { enabled = true } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"ecs"}, + "timeout": 5 * time.Second, + "override": true, + "ecs": map[string]interface{}{ + "tags": []string{}, + "resource_attributes": map[string]interface{}{ + "aws.ecs.cluster.arn": map[string]interface{}{"enabled": true}, + "aws.ecs.launchtype": map[string]interface{}{"enabled": true}, + "aws.ecs.task.arn": map[string]interface{}{"enabled": true}, + "aws.ecs.task.family": map[string]interface{}{"enabled": true}, + "aws.ecs.task.revision": map[string]interface{}{"enabled": true}, + "aws.log.group.arns": map[string]interface{}{"enabled": true}, + "aws.log.group.names": map[string]interface{}{"enabled": false}, + "aws.log.stream.arns": map[string]interface{}{"enabled": true}, + "aws.log.stream.names": map[string]interface{}{"enabled": true}, + "cloud.account.id": map[string]interface{}{"enabled": true}, + "cloud.availability_zone": map[string]interface{}{"enabled": true}, + "cloud.platform": map[string]interface{}{"enabled": true}, + "cloud.provider": map[string]interface{}{"enabled": true}, + "cloud.region": map[string]interface{}{"enabled": true}, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "eks_defaults", + cfg: ` + detectors = ["eks"] + eks {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"eks"}, + "timeout": 5 * time.Second, + "override": true, + "eks": map[string]interface{}{ + "tags": []string{}, + "resource_attributes": map[string]interface{}{ + "cloud.platform": map[string]interface{}{ + "enabled": true, + }, + "cloud.provider": map[string]interface{}{ + "enabled": true, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "eks_explicit", + cfg: ` + detectors = ["eks"] + eks { + resource_attributes { + cloud.platform { enabled = true } + cloud.provider { enabled = false } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"eks"}, + "timeout": 5 * time.Second, + "override": true, + "eks": map[string]interface{}{ + "tags": []string{}, + "resource_attributes": map[string]interface{}{ + "cloud.platform": map[string]interface{}{ + "enabled": true, + }, + "cloud.provider": map[string]interface{}{ + "enabled": false, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "azure_defaults", + cfg: ` + detectors = ["azure"] + azure {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"azure"}, + "timeout": 5 * time.Second, + "override": true, + "azure": map[string]interface{}{ + "resource_attributes": map[string]interface{}{ + "tags": []string{}, + "azure.resourcegroup.name": map[string]interface{}{ + "enabled": true, + }, + "azure.vm.name": map[string]interface{}{ + "enabled": true, + }, + "azure.vm.scaleset.name": map[string]interface{}{ + "enabled": true, + }, + "azure.vm.size": map[string]interface{}{ + "enabled": true, + }, + "cloud.account.id": map[string]interface{}{ + "enabled": true, + }, + "cloud.platform": map[string]interface{}{ + "enabled": true, + }, + "cloud.provider": map[string]interface{}{ + "enabled": true, + }, + "cloud.region": map[string]interface{}{ + "enabled": true, + }, + "host.id": map[string]interface{}{ + "enabled": true, + }, + "host.name": map[string]interface{}{ + "enabled": true, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "azure_explicit", + cfg: ` + detectors = ["azure"] + azure { + resource_attributes { + azure.resourcegroup.name { enabled = true } + azure.vm.name { enabled = true } + azure.vm.scaleset.name { enabled = true } + azure.vm.size { enabled = true } + cloud.account.id { enabled = false } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"azure"}, + "timeout": 5 * time.Second, + "override": true, + "azure": map[string]interface{}{ + "resource_attributes": map[string]interface{}{ + "tags": []string{}, + "azure.resourcegroup.name": map[string]interface{}{ + "enabled": true, + }, + "azure.vm.name": map[string]interface{}{ + "enabled": true, + }, + "azure.vm.scaleset.name": map[string]interface{}{ + "enabled": true, + }, + "azure.vm.size": map[string]interface{}{ + "enabled": true, + }, + "cloud.account.id": map[string]interface{}{ + "enabled": false, + }, + "cloud.platform": map[string]interface{}{ + "enabled": true, + }, + "cloud.provider": map[string]interface{}{ + "enabled": true, + }, + "cloud.region": map[string]interface{}{ + "enabled": true, + }, + "host.id": map[string]interface{}{ + "enabled": true, + }, + "host.name": map[string]interface{}{ + "enabled": true, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "aks_defaults", + cfg: ` + detectors = ["aks"] + aks {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"aks"}, + "timeout": 5 * time.Second, + "override": true, + "aks": map[string]interface{}{ + "tags": []string{}, + "resource_attributes": map[string]interface{}{ + "cloud.platform": map[string]interface{}{ + "enabled": true, + }, + "cloud.provider": map[string]interface{}{ + "enabled": true, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "aks_explicit", + cfg: ` + detectors = ["aks"] + aks { + resource_attributes { + cloud.platform { enabled = true } + cloud.provider { enabled = false } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"aks"}, + "timeout": 5 * time.Second, + "override": true, + "aks": map[string]interface{}{ + "tags": []string{}, + "resource_attributes": map[string]interface{}{ + "cloud.platform": map[string]interface{}{ + "enabled": true, + }, + "cloud.provider": map[string]interface{}{ + "enabled": false, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "gcp_defaults", + cfg: ` + detectors = ["gcp"] + gcp {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"gcp"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "gcp_explicit", + cfg: ` + detectors = ["gcp"] + gcp { + resource_attributes { + cloud.account.id { enabled = true } + cloud.availability_zone { enabled = true } + cloud.platform { enabled = true } + cloud.provider { enabled = true } + cloud.region { enabled = false } + faas.id { enabled = false } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"gcp"}, + "timeout": 5 * time.Second, + "override": true, + "gcp": map[string]interface{}{ + "resource_attributes": map[string]interface{}{ + "cloud.account.id": map[string]interface{}{ + "enabled": true, + }, + "cloud.availability_zone": map[string]interface{}{ + "enabled": true, + }, + "cloud.platform": map[string]interface{}{ + "enabled": true, + }, + "cloud.provider": map[string]interface{}{ + "enabled": true, + }, + "cloud.region": map[string]interface{}{ + "enabled": false, + }, + "faas.id": map[string]interface{}{ + "enabled": false, + }, + "faas.instance": map[string]interface{}{ + "enabled": true, + }, + "faas.name": map[string]interface{}{ + "enabled": true, + }, + "faas.version": map[string]interface{}{ + "enabled": true, + }, + "gcp.cloud_run.job.execution": map[string]interface{}{ + "enabled": true, + }, + "gcp.cloud_run.job.task_index": map[string]interface{}{ + "enabled": true, + }, + "gcp.gce.instance.hostname": map[string]interface{}{ + "enabled": false, + }, + "gcp.gce.instance.name": map[string]interface{}{ + "enabled": false, + }, + "host.id": map[string]interface{}{ + "enabled": true, + }, + "host.name": map[string]interface{}{ + "enabled": true, + }, + "host.type": map[string]interface{}{ + "enabled": true, + }, + "k8s.cluster.name": map[string]interface{}{ + "enabled": true, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "docker_defaults", + cfg: ` + detectors = ["docker"] + docker {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"docker"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "docker_explicit", + cfg: ` + detectors = ["docker"] + docker { + resource_attributes { + host.name { enabled = true } + os.type { enabled = false } + + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"docker"}, + "timeout": 5 * time.Second, + "override": true, + "docker": map[string]interface{}{ + "resource_attributes": map[string]interface{}{ + "host.name": map[string]interface{}{ + "enabled": true, + }, + "os.type": map[string]interface{}{ + "enabled": false, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "lambda_defaults", + cfg: ` + detectors = ["lambda"] + lambda {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"lambda"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "lambda_explicit", + cfg: ` + detectors = ["lambda"] + lambda { + resource_attributes { + aws.log.group.names { enabled = true } + aws.log.stream.names { enabled = true } + cloud.platform { enabled = true } + cloud.provider { enabled = false } + cloud.region { enabled = false } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"lambda"}, + "timeout": 5 * time.Second, + "override": true, + "lambda": map[string]interface{}{ + "resource_attributes": map[string]interface{}{ + "aws.log.group.names": map[string]interface{}{ + "enabled": true, + }, + "aws.log.stream.names": map[string]interface{}{ + "enabled": true, + }, + "cloud.platform": map[string]interface{}{ + "enabled": true, + }, + "cloud.provider": map[string]interface{}{ + "enabled": false, + }, + "cloud.region": map[string]interface{}{ + "enabled": false, + }, + "faas.instance": map[string]interface{}{ + "enabled": true, + }, + "faas.max_memory": map[string]interface{}{ + "enabled": true, + }, + "faas.name": map[string]interface{}{ + "enabled": true, + }, + "faas.version": map[string]interface{}{ + "enabled": true, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "elasticbeanstalk_defaults", + cfg: ` + detectors = ["elasticbeanstalk"] + elasticbeanstalk {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"elasticbeanstalk"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "elasticbeanstalk_explicit", + cfg: ` + detectors = ["elasticbeanstalk"] + elasticbeanstalk { + resource_attributes { + cloud.platform { enabled = true } + cloud.provider { enabled = true } + deployment.environment { enabled = true } + service.instance.id { enabled = false } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"elasticbeanstalk"}, + "timeout": 5 * time.Second, + "override": true, + "elasticbeanstalk": map[string]interface{}{ + "resource_attributes": map[string]interface{}{ + "cloud.platform": map[string]interface{}{ + "enabled": true, + }, + "cloud.provider": map[string]interface{}{ + "enabled": true, + }, + "deployment.environment": map[string]interface{}{ + "enabled": true, + }, + "service.instance.id": map[string]interface{}{ + "enabled": false, + }, + "service.version": map[string]interface{}{ + "enabled": true, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "consul_defaults", + cfg: ` + detectors = ["consul"] + consul {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"consul"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "consul_explicit", + cfg: ` + detectors = ["consul"] + consul { + address = "localhost:8500" + datacenter = "dc1" + token = "secret_token" + namespace = "test_namespace" + meta = ["test"] + resource_attributes { + cloud.region { enabled = false } + host.id { enabled = false } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"consul"}, + "timeout": 5 * time.Second, + "override": true, + "consul": map[string]interface{}{ + "address": "localhost:8500", + "datacenter": "dc1", + "token": "secret_token", + "namespace": "test_namespace", + "meta": map[string]string{"test": ""}, + "resource_attributes": map[string]interface{}{ + "cloud.region": map[string]interface{}{ + "enabled": false, + }, + "host.id": map[string]interface{}{ + "enabled": false, + }, + "host.name": map[string]interface{}{ + "enabled": true, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "heroku_defaults", + cfg: ` + detectors = ["heroku"] + heroku {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"heroku"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "heroku_explicit", + cfg: ` + detectors = ["heroku"] + heroku { + resource_attributes { + cloud.provider { enabled = true } + heroku.app.id { enabled = true } + heroku.dyno.id { enabled = true } + heroku.release.commit { enabled = true } + heroku.release.creation_timestamp { enabled = false } + service.instance.id { enabled = false } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"heroku"}, + "timeout": 5 * time.Second, + "override": true, + "heroku": map[string]interface{}{ + "resource_attributes": map[string]interface{}{ + "cloud.provider": map[string]interface{}{ + "enabled": true, + }, + "heroku.app.id": map[string]interface{}{ + "enabled": true, + }, + "heroku.dyno.id": map[string]interface{}{ + "enabled": true, + }, + "heroku.release.commit": map[string]interface{}{ + "enabled": true, + }, + "heroku.release.creation_timestamp": map[string]interface{}{ + "enabled": false, + }, + "service.instance.id": map[string]interface{}{ + "enabled": false, + }, + "service.name": map[string]interface{}{ + "enabled": true, + }, + "service.version": map[string]interface{}{ + "enabled": true, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "kubernetes_node_defaults", + cfg: ` + detectors = ["kubernetes_node"] + kubernetes_node {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"k8snode"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "kubernetes_node_explicit", + cfg: ` + detectors = ["kubernetes_node"] + kubernetes_node { + auth_type = "kubeConfig" + context = "fake_ctx" + node_from_env_var = "MY_CUSTOM_VAR" + resource_attributes { + k8s.node.name { enabled = true } + k8s.node.uid { enabled = false } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"k8snode"}, + "timeout": 5 * time.Second, + "override": true, + "k8snode": map[string]interface{}{ + "auth_type": "kubeConfig", + "context": "fake_ctx", + "node_from_env_var": "MY_CUSTOM_VAR", + "resource_attributes": map[string]interface{}{ + "k8s.node.name": map[string]interface{}{ + "enabled": true, + }, + "k8s.node.uid": map[string]interface{}{ + "enabled": false, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + }, + }, + { + testName: "system_invalid_hostname_source", + cfg: ` + detectors = ["system"] + system { + hostname_sources = ["asdf"] + resource_attributes { } + } + output {} + `, + errorMsg: "invalid hostname source: asdf", + }, + { + testName: "system_defaults", + cfg: ` + detectors = ["system"] + system {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"system"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "system_explicit", + cfg: ` + detectors = ["system"] + system { + hostname_sources = ["cname","lookup"] + resource_attributes { + host.arch { enabled = true } + host.cpu.cache.l2.size { enabled = true } + host.cpu.family { enabled = true } + host.cpu.model.id { enabled = true } + host.cpu.model.name { enabled = true } + host.cpu.stepping { enabled = true } + host.cpu.vendor.id { enabled = false } + host.id { enabled = false } + host.name { enabled = false } + // os.description { enabled = false } + // os.type { enabled = true } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"system"}, + "timeout": 5 * time.Second, + "override": true, + "system": map[string]interface{}{ + "hostname_sources": []string{"cname", "lookup"}, + "resource_attributes": map[string]interface{}{ + "host.arch": map[string]interface{}{ + "enabled": true, + }, + "host.cpu.cache.l2.size": map[string]interface{}{ + "enabled": true, + }, + "host.cpu.family": map[string]interface{}{ + "enabled": true, + }, + "host.cpu.model.id": map[string]interface{}{ + "enabled": true, + }, + "host.cpu.model.name": map[string]interface{}{ + "enabled": true, + }, + "host.cpu.stepping": map[string]interface{}{ + "enabled": true, + }, + "host.cpu.vendor.id": map[string]interface{}{ + "enabled": false, + }, + "host.id": map[string]interface{}{ + "enabled": false, + }, + "host.name": map[string]interface{}{ + "enabled": false, + }, + "os.description": map[string]interface{}{ + "enabled": false, + }, + "os.type": map[string]interface{}{ + "enabled": true, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "openshift_default", + cfg: ` + detectors = ["openshift"] + openshift {} + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"openshift"}, + "timeout": 5 * time.Second, + "override": true, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "openshift_explicit", + cfg: ` + detectors = ["openshift"] + timeout = "7s" + override = false + openshift { + address = "127.0.0.1:4444" + token = "some_token" + tls { + insecure = true + } + resource_attributes { + cloud.platform { + enabled = true + } + cloud.provider { + enabled = true + } + cloud.region { + enabled = false + } + k8s.cluster.name { + enabled = false + } + } + } + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"openshift"}, + "timeout": 7 * time.Second, + "override": false, + "openshift": map[string]interface{}{ + "address": "127.0.0.1:4444", + "token": "some_token", + "tls": map[string]interface{}{ + "insecure": true, + }, + "resource_attributes": map[string]interface{}{ + "cloud.platform": map[string]interface{}{ + "enabled": true, + }, + "cloud.provider": map[string]interface{}{ + "enabled": true, + }, + "cloud.region": map[string]interface{}{ + "enabled": false, + }, + "k8s.cluster.name": map[string]interface{}{ + "enabled": false, + }, + }, + }, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + { + testName: "env", + cfg: ` + detectors = ["env"] + timeout = "7s" + override = false + output {} + `, + expected: map[string]interface{}{ + "detectors": []string{"env"}, + "timeout": 7 * time.Second, + "override": false, + "ec2": ec2.DefaultArguments.Convert(), + "ecs": ecs.DefaultArguments.Convert(), + "eks": eks.DefaultArguments.Convert(), + "elasticbeanstalk": elasticbeanstalk.DefaultArguments.Convert(), + "lambda": lambda.DefaultArguments.Convert(), + "azure": azure.DefaultArguments.Convert(), + "aks": aks.DefaultArguments.Convert(), + "consul": consul.DefaultArguments.Convert(), + "docker": docker.DefaultArguments.Convert(), + "gcp": gcp.DefaultArguments.Convert(), + "heroku": heroku.DefaultArguments.Convert(), + "system": system.DefaultArguments.Convert(), + "openshift": openshift.DefaultArguments.Convert(), + "k8snode": kubernetes_node.DefaultArguments.Convert(), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.testName, func(t *testing.T) { + var args resourcedetection.Arguments + err := river.Unmarshal([]byte(tc.cfg), &args) + if tc.errorMsg != "" { + require.ErrorContains(t, err, tc.errorMsg) + return + } + + require.NoError(t, err) + + actualPtr, err := args.Convert() + require.NoError(t, err) + + actual := actualPtr.(*resourcedetectionprocessor.Config) + + var expected resourcedetectionprocessor.Config + err = mapstructure.Decode(tc.expected, &expected) + require.NoError(t, err) + + require.Equal(t, expected, *actual) + }) + } +} diff --git a/docs/sources/flow/reference/compatibility/_index.md b/docs/sources/flow/reference/compatibility/_index.md index 633433ef6768..44a9c5382a28 100644 --- a/docs/sources/flow/reference/compatibility/_index.md +++ b/docs/sources/flow/reference/compatibility/_index.md @@ -298,6 +298,7 @@ The following components, grouped by namespace, _export_ OpenTelemetry `otelcol. - [otelcol.processor.k8sattributes]({{< relref "../components/otelcol.processor.k8sattributes.md" >}}) - [otelcol.processor.memory_limiter]({{< relref "../components/otelcol.processor.memory_limiter.md" >}}) - [otelcol.processor.probabilistic_sampler]({{< relref "../components/otelcol.processor.probabilistic_sampler.md" >}}) +- [otelcol.processor.resourcedetection]({{< relref "../components/otelcol.processor.resourcedetection.md" >}}) - [otelcol.processor.span]({{< relref "../components/otelcol.processor.span.md" >}}) - [otelcol.processor.tail_sampling]({{< relref "../components/otelcol.processor.tail_sampling.md" >}}) - [otelcol.processor.transform]({{< relref "../components/otelcol.processor.transform.md" >}}) @@ -326,6 +327,7 @@ The following components, grouped by namespace, _consume_ OpenTelemetry `otelcol - [otelcol.processor.k8sattributes]({{< relref "../components/otelcol.processor.k8sattributes.md" >}}) - [otelcol.processor.memory_limiter]({{< relref "../components/otelcol.processor.memory_limiter.md" >}}) - [otelcol.processor.probabilistic_sampler]({{< relref "../components/otelcol.processor.probabilistic_sampler.md" >}}) +- [otelcol.processor.resourcedetection]({{< relref "../components/otelcol.processor.resourcedetection.md" >}}) - [otelcol.processor.span]({{< relref "../components/otelcol.processor.span.md" >}}) - [otelcol.processor.tail_sampling]({{< relref "../components/otelcol.processor.tail_sampling.md" >}}) - [otelcol.processor.transform]({{< relref "../components/otelcol.processor.transform.md" >}}) diff --git a/docs/sources/flow/reference/components/otelcol.processor.resourcedetection.md b/docs/sources/flow/reference/components/otelcol.processor.resourcedetection.md new file mode 100644 index 000000000000..204372aa8d6e --- /dev/null +++ b/docs/sources/flow/reference/components/otelcol.processor.resourcedetection.md @@ -0,0 +1,933 @@ +--- +aliases: +- /docs/grafana-cloud/agent/flow/reference/components/otelcol.processor.resourcedetection/ +- /docs/grafana-cloud/monitor-infrastructure/agent/flow/reference/components/otelcol.processor.resourcedetection/ +- /docs/grafana-cloud/monitor-infrastructure/integrations/agent/flow/reference/components/otelcol.processor.resourcedetection/ +- /docs/grafana-cloud/send-data/agent/flow/reference/components/otelcol.processor.resourcedetection/ +canonical: https://grafana.com/docs/agent/latest/flow/reference/components/otelcol.processor.resourcedetection/ +labels: + stage: beta +title: otelcol.processor.resourcedetection +description: Learn about otelcol.processor.resourcedetection +--- + +# otelcol.processor.resourcedetection + +{{< docs/shared lookup="flow/stability/beta.md" source="agent" version="" >}} + +`otelcol.processor.resourcedetection` detects resource information from the host +in a format that conforms to the [OpenTelemetry resource semantic conventions](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/resource/semantic_conventions/), and appends or +overrides the resource values in the telemetry data with this information. + +{{% admonition type="note" %}} +`otelcol.processor.resourcedetection` is a wrapper over the upstream +OpenTelemetry Collector Contrib `resourcedetection` processor. If necessary, +bug reports or feature requests are redirected to the upstream repository. +{{% /admonition %}} + +You can specify multiple `otelcol.processor.resourcedetection` components by giving them +different labels. + +## Usage + +```river +otelcol.processor.resourcedetection "LABEL" { + output { + logs = [...] + metrics = [...] + traces = [...] + } +} +``` + +## Arguments + +`otelcol.processor.resourcedetection` supports the following arguments: + +Name | Type | Description | Default | Required +----------- | -------------- | ----------------------------------------------------------------------------------- |---------- | -------- +`detectors` | `list(string)` | An ordered list of named detectors used to detect resource information. | `["env"]` | no +`override` | `bool` | Configures whether existing resource attributes should be overridden or preserved. | `true` | no +`timeout` | `duration` | Timeout by which all specified detectors must complete. | `"5s"` | no + +`detectors` could contain the following values: +* `env` +* `ec2` +* `ecs` +* `eks` +* `elasticbeanstalk` +* `lambda` +* `azure` +* `aks` +* `consul` +* `docker` +* `gcp` +* `heroku` +* `system` +* `openshift` +* `kubernetes_node` + +`env` is the only detector that is not configured through a River block. +The `env` detector reads resource information from the `OTEL_RESOURCE_ATTRIBUTES` environment variable. +This variable must be in the format `=,=,...`, +the details of which are currently pending confirmation in the OpenTelemetry specification. + +If a detector other than `env` is needed, you can customize it with the relevant River block. +For example, you can customize the `ec2` detector with the [ec2][] block. +If you omit the [ec2][] block, the defaults specified in the [ec2][] block documentation are used. + +If multiple detectors are inserting the same attribute name, the first detector to insert wins. +For example, if you had `detectors = ["eks", "ec2"]` then `cloud.platform` will be `aws_eks` instead of `ec2`. + +The following order is recommended for AWS: + 1. [lambda][] + 1. [elasticbeanstalk][] + 1. [eks][] + 1. [ecs][] + 1. [ec2][] + +## Blocks + +The following blocks are supported inside the definition of `otelcol.processor.resourcedetection`: + +Hierarchy | Block | Description | Required +----------------- | --------------------- | ------------------------------------------------- | -------- +output | [output][] | Configures where to send received telemetry data. | yes +ec2 | [ec2][] | | no +ecs | [ecs][] | | no +eks | [eks][] | | no +elasticbeanstalk | [elasticbeanstalk][] | | no +lambda | [lambda][] | | no +azure | [azure][] | | no +aks | [aks][] | | no +consul | [consul][] | | no +docker | [docker][] | | no +gcp | [gcp][] | | no +heroku | [heroku][] | | no +system | [system][] | | no +openshift | [openshift][] | | no +kubernetes_node | [kubernetes_node][] | | no + +[output]: #output +[ec2]: #ec2 +[ecs]: #ecs +[eks]: #eks +[elasticbeanstalk]: #elasticbeanstalk +[lambda]: #lambda +[azure]: #azure +[aks]: #aks +[consul]: #consul +[docker]: #docker +[gcp]: #gcp +[heroku]: #heroku +[system]: #system +[openshift]: #openshift +[kubernetes_node]: #kubernetes_node + +[res-attr-cfg]: #resource-attribute-config + +### output + +{{< docs/shared lookup="flow/reference/components/output-block.md" source="agent" version="" >}} + +### ec2 + +The `ec2` block reads resource information from the [EC2 instance metadata API] using the [AWS SDK for Go][]. + +The `ec2` block supports the following attributes: + +Attribute | Type | Description | Default | Required +----------- |----------------| --------------------------------------------------------------------------- |-------------| -------- +`tags` | `list(string)` | A list of regular expressions to match against tag keys of an EC2 instance. | `[]` | no + +If you are using a proxy server on your EC2 instance, it's important that you exempt requests for instance metadata as described in the [AWS cli user guide][]. +Failing to do so can result in proxied or missing instance data. + +If the instance is part of AWS ParallelCluster and the detector is failing to connect to the metadata server, +check the iptable and make sure the chain `PARALLELCLUSTER_IMDS` contains a rule that allows the {{< param "PRODUCT_ROOT_NAME" >}} user to access `169.254.169.254/32`. + +[AWS SDK for Go]: https://docs.aws.amazon.com/sdk-for-go/api/aws/ec2metadata/ +[EC2 instance metadata API]: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html +[AWS cli user guide]: https://github.com/awsdocs/aws-cli-user-guide/blob/a2393582590b64bd2a1d9978af15b350e1f9eb8e/doc_source/cli-configure-proxy.md#using-a-proxy-on-amazon-ec2-instances + +`tags` can be used to gather tags for the EC2 instance which {{< param "PRODUCT_ROOT_NAME" >}} is running on. +To fetch EC2 tags, the IAM role assigned to the EC2 instance must have a policy that includes the `ec2:DescribeTags` permission. + +The `ec2` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#ec2--resource_attributes) | Configures which resource attributes to add. | no + +##### ec2 > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +--------------------------------------- | --------------------------------------------------------------------------------------------------- | -------- +[cloud.account.id][res-attr-cfg] | Toggles the `cloud.account.id` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.availability_zone][res-attr-cfg] | Toggles the `cloud.availability_zone` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.platform][res-attr-cfg] | Toggles the `cloud.platform` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.provider][res-attr-cfg] | Toggles the `cloud.provider` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.region][res-attr-cfg] | Toggles the `cloud.region` resource attribute.
Sets `enabled` to `true` by default. | no +[host.id][res-attr-cfg] | Toggles the `host.id` resource attribute.
Sets `enabled` to `true` by default. | no +[host.image.id][res-attr-cfg] | Toggles the `host.image.id` resource attribute.
Sets `enabled` to `true` by default. | no +[host.name][res-attr-cfg] | Toggles the `host.name` resource attribute.
Sets `enabled` to `true` by default. | no +[host.type][res-attr-cfg] | Toggles the `host.type` resource attribute.
Sets `enabled` to `true` by default. | no + +### ecs + +The `ecs` block queries the Task Metadata Endpoint (TMDE) to record information about the current ECS Task. Only TMDE V4 and V3 are supported. + +[Task Metadata Endpoint]: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint.html + +The `ecs` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#ecs--resource_attributes) | Configures which resource attributes to add. | no + +#### ecs > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +--------------------------------------- | --------------------------------------------------------------------------------------------------- | -------- +[aws.ecs.cluster.arn][res-attr-cfg] | Toggles the `aws.ecs.cluster.arn` resource attribute.
Sets `enabled` to `true` by default. | no +[aws.ecs.launchtype][res-attr-cfg] | Toggles the `aws.ecs.launchtype` resource attribute.
Sets `enabled` to `true` by default. | no +[aws.ecs.task.arn][res-attr-cfg] | Toggles the `aws.ecs.task.arn` resource attribute.
Sets `enabled` to `true` by default. | no +[aws.ecs.task.family][res-attr-cfg] | Toggles the `aws.ecs.task.family` resource attribute.
Sets `enabled` to `true` by default. | no +[aws.ecs.task.revision][res-attr-cfg] | Toggles the `aws.ecs.task.revision` resource attribute.
Sets `enabled` to `true` by default. | no +[aws.log.group.arns][res-attr-cfg] | Toggles the `aws.log.group.arns` resource attribute.
Sets `enabled` to `true` by default. | no +[aws.log.group.names][res-attr-cfg] | Toggles the `aws.log.group.names` resource attribute.
Sets `enabled` to `true` by default. | no +[aws.log.stream.arns][res-attr-cfg] | Toggles the `aws.log.stream.arns` resource attribute.
Sets `enabled` to `true` by default. | no +[aws.log.stream.names][res-attr-cfg] | Toggles the `aws.log.stream.names` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.account.id][res-attr-cfg] | Toggles the `cloud.account.id` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.availability_zone][res-attr-cfg] | Toggles the `cloud.availability_zone` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.platform][res-attr-cfg] | Toggles the `cloud.platform` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.provider][res-attr-cfg] | Toggles the `cloud.provider` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.region][res-attr-cfg] | Toggles the `cloud.region` resource attribute.
Sets `enabled` to `true` by default. | no + +### eks + +The `eks` block adds resource attributes for Amazon EKS. + +The `eks` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#eks--resource_attributes) | Configures which resource attributes to add. | no + +#### eks > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +------------------------------- | ------------------------------------------------------------------------------------------- | -------- +[cloud.platform][res-attr-cfg] | Toggles the `cloud.platform` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.provider][res-attr-cfg] | Toggles the `cloud.provider` resource attribute.
Sets `enabled` to `true` by default. | no + +Example values: +* `cloud.provider`: `"aws"` +* `cloud.platform`: `"aws_eks"` + +### elasticbeanstalk + +The `elasticbeanstalk` block reads the AWS X-Ray configuration file available on all Beanstalk instances with [X-Ray Enabled][]. + +[X-Ray Enabled]: https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/environment-configuration-debugging.html + +The `elasticbeanstalk` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#elasticbeanstalk--resource_attributes) | Configures which resource attributes to add. | no + +#### elasticbeanstalk > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +--------------------------------- | --------------------------------------------------------------------------------------------- | -------- +[cloud.platform][res-attr-cfg] | Toggles the `cloud.platform` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.provider][res-attr-cfg] | Toggles the `cloud.provider` resource attribute.
Sets `enabled` to `true` by default. | no +[deployment.envir][res-attr-cfg] | Toggles the `deployment.envir` resource attribute.
Sets `enabled` to `true` by default. | no +[service.instance][res-attr-cfg] | Toggles the `service.instance` resource attribute.
Sets `enabled` to `true` by default. | no +[service.version][res-attr-cfg] | Toggles the `service.version` resource attribute.
Sets `enabled` to `true` by default. | no + +Example values: +* `cloud.provider`: `"aws"` +* `cloud.platform`: `"aws_elastic_beanstalk"` + +### lambda + +The `lambda` block uses the AWS Lambda [runtime environment variables][lambda-env-vars] to retrieve various resource attributes. + +[lambda-env-vars]: https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime + +The `lambda` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#lambda--resource_attributes) | Configures which resource attributes to add. | no + +#### lambda > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +------------------------------------- | --------------------------------------------------------------------------------------------------- | -------- +[aws.log.group.names][res-attr-cfg] | Toggles the `aws.log.group.names` resource attribute.
Sets `enabled` to `true` by default. | no +[aws.log.stream.names][res-attr-cfg] | Toggles the `aws.log.stream.names` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.platform][res-attr-cfg] | Toggles the `cloud.platform` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.provider][res-attr-cfg] | Toggles the `cloud.provider` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.region][res-attr-cfg] | Toggles the `cloud.region` resource attribute.
Sets `enabled` to `true` by default. | no +[faas.instance][res-attr-cfg] | Toggles the `faas.instance` resource attribute.
Sets `enabled` to `true` by default. | no +[faas.max_memory][res-attr-cfg] | Toggles the `faas.max_memory` resource attribute.
Sets `enabled` to `true` by default. | no +[faas.name][res-attr-cfg] | Toggles the `faas.name` resource attribute.
Sets `enabled` to `true` by default. | no +[faas.version][res-attr-cfg] | Toggles the `faas.version` resource attribute.
Sets `enabled` to `true` by default. | no + +[Cloud semantic conventions][]: +* `cloud.provider`: `"aws"` +* `cloud.platform`: `"aws_lambda"` +* `cloud.region`: `$AWS_REGION` + +[Function as a Service semantic conventions][] and [AWS Lambda semantic conventions][]: +* `faas.name`: `$AWS_LAMBDA_FUNCTION_NAME` +* `faas.version`: `$AWS_LAMBDA_FUNCTION_VERSION` +* `faas.instance`: `$AWS_LAMBDA_LOG_STREAM_NAME` +* `faas.max_memory`: `$AWS_LAMBDA_FUNCTION_MEMORY_SIZE` + +[AWS Logs semantic conventions][]: +* `aws.log.group.names`: `$AWS_LAMBDA_LOG_GROUP_NAME` +* `aws.log.stream.names`: `$AWS_LAMBDA_LOG_STREAM_NAME` + +[Cloud semantic conventions]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud.md +[Function as a Service semantic conventions]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/faas.md +[AWS Lambda semantic conventions]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/instrumentation/aws-lambda.md#resource-detector +[AWS Logs semantic conventions]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud_provider/aws/logs.md + +### azure + +The `azure` block queries the [Azure Instance Metadata Service][] to retrieve various resource attributes. + +[Azure Instance Metadata Service]: https://aka.ms/azureimds + +The `azure` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#azure--resource_attributes) | Configures which resource attributes to add. | no + +#### azure > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +---------------------------------------- | --------------------------------------------------------------------------------------------------- | -------- +[azure.resourcegroup.name][res-attr-cfg] | Toggles the `azure.resourcegroup.name` resource attribute.
Sets `enabled` to `true` by default. | no +[azure.vm.name][res-attr-cfg] | Toggles the `azure.vm.name` resource attribute.
Sets `enabled` to `true` by default. | no +[azure.vm.scaleset.name][res-attr-cfg] | Toggles the `azure.vm.scaleset.name` resource attribute.
Sets `enabled` to `true` by default. | no +[azure.vm.size][res-attr-cfg] | Toggles the `azure.vm.size` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.account.id][res-attr-cfg] | Toggles the `cloud.account.id` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.platform][res-attr-cfg] | Toggles the `cloud.platform` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.provider][res-attr-cfg] | Toggles the `cloud.provider` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.region][res-attr-cfg] | Toggles the `cloud.region` resource attribute.
Sets `enabled` to `true` by default. | no +[host.id][res-attr-cfg] | Toggles the `host.id` resource attribute.
Sets `enabled` to `true` by default. | no +[host.name][res-attr-cfg] | Toggles the `host.name` resource attribute.
Sets `enabled` to `true` by default. | no + +Example values: +* `cloud.provider`: `"azure"` +* `cloud.platform`: `"azure_vm"` + +### aks + +The `aks` block adds resource attributes related to Azure AKS. + +The `aks` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#aks--resource_attributes) | Configures which resource attributes to add. | no + +#### aks > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +------------------------------- | ------------------------------------------------------------------------------------------- | -------- +[cloud.platform][res-attr-cfg] | Toggles the `cloud.platform` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.provider][res-attr-cfg] | Toggles the `cloud.provider` resource attribute.
Sets `enabled` to `true` by default. | no + +Example values: +* `cloud.provider`: `"azure"` +* `cloud.platform`: `"azure_vm"` + +### consul + +The `consul` block queries a Consul agent and reads its configuration endpoint to retrieve values for resource attributes. + +The `consul` block supports the following attributes: + +Attribute | Type | Description | Default | Required +------------ |--------------- | --------------------------------------------------------------------------- |-------------| -------- +`address` | `string` | The address of the Consul server | `""` | no +`datacenter` | `string` | Datacenter to use. If not provided, the default agent datacenter is used. | `""` | no +`token` | `secret` | A per-request ACL token which overrides the Consul agent's default (empty) token. | `""` | no +`namespace` | `string` | The name of the namespace to send along for the request. | `""` | no +`meta` | `list(string)` | Allowlist of [Consul Metadata][] keys to use as resource attributes. | `[]` | no + +`token` is only required if [Consul's ACL System][] is enabled. + +[Consul Metadata]: https://www.consul.io/docs/agent/options#node_meta +[Consul's ACL System]: https://www.consul.io/docs/security/acl/acl-system + +The `consul` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#consul--resource_attributes) | Configures which resource attributes to add. | no + +#### consul > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +--------------------------------------- | --------------------------------------------------------------------------------------------------- | -------- +[cloud.region][res-attr-cfg] | Toggles the `cloud.region` resource attribute.
Sets `enabled` to `true` by default. | no +[host.id][res-attr-cfg] | Toggles the `host.id` resource attribute.
Sets `enabled` to `true` by default. | no +[host.name][res-attr-cfg] | Toggles the `host.name` resource attribute.
Sets `enabled` to `true` by default. | no + +### docker + +The `docker` block queries the Docker daemon to retrieve various resource attributes from the host machine. + +You need to mount the Docker socket (`/var/run/docker.sock` on Linux) to contact the Docker daemon. +Docker detection does not work on MacOS. + +The `docker` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#docker--resource_attributes) | Configures which resource attributes to add. | no + +#### docker > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +--------------------------------------- | --------------------------------------------------------------------------------------------------- | -------- +[host.name][res-attr-cfg] | Toggles the `host.name` resource attribute.
Sets `enabled` to `true` by default. | no +[os.type][res-attr-cfg] | Toggles the `os.type` resource attribute.
Sets `enabled` to `true` by default. | no + +### gcp + +The `gcp` block detects resource attributes using the [Google Cloud Client Libraries for Go][], which reads resource information from the [GCP metadata server][]. +The detector also uses environment variables to identify which GCP platform the application is running on, and assigns appropriate resource attributes for that platform. + +Use the `gcp` detector regardless of the GCP platform {{< param "PRODUCT_ROOT_NAME" >}} is running on. + +[Google Cloud Client Libraries for Go]: https://github.com/googleapis/google-cloud-go +[GCP metadata server]: https://cloud.google.com/compute/docs/storing-retrieving-metadata + +The `gcp` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#gcp--resource_attributes) | Configures which resource attributes to add. | no + +#### gcp > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +--------------------------------------- | --------------------------------------------------------------------------------------------------- | -------- +[cloud.account.id][res-attr-cfg] | Toggles the `cloud.account.id` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.availability_zone][res-attr-cfg] | Toggles the `cloud.availability_zone` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.platform][res-attr-cfg] | Toggles the `cloud.platform` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.provider][res-attr-cfg] | Toggles the `cloud.provider` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.region][res-attr-cfg] | Toggles the `cloud.region` resource attribute.
Sets `enabled` to `true` by default. | no +[faas.id][res-attr-cfg] | Toggles the `faas.id` resource attribute.
Sets `enabled` to `true` by default. | no +[faas.instance][res-attr-cfg] | Toggles the `faas.instance` resource attribute.
Sets `enabled` to `true` by default. | no +[faas.name][res-attr-cfg] | Toggles the `faas.name` resource attribute.
Sets `enabled` to `true` by default. | no +[faas.version][res-attr-cfg] | Toggles the `faas.version` resource attribute.
Sets `enabled` to `true` by default. | no +[gcp.cloud_run.job.execution][res-attr-cfg] | Toggles the `gcp.cloud_run.job.execution` resource attribute.
Sets `enabled` to `true` by default. | no +[gcp.cloud_run.job.task_index][res-attr-cfg] | Toggles the `gcp.cloud_run.job.task_index` resource attribute.
Sets `enabled` to `true` by default. | no +[gcp.gce.instance.hostname][res-attr-cfg] | Toggles the `gcp.gce.instance.hostname` resource attribute.
Sets `enabled` to `false` by default. | no +[gcp.gce.instance.name][res-attr-cfg] | Toggles the `gcp.gce.instance.name` resource attribute.
Sets `enabled` to `false` by default. | no +[host.id][res-attr-cfg] | Toggles the `host.id` resource attribute.
Sets `enabled` to `true` by default. | no +[host.name][res-attr-cfg] | Toggles the `host.name` resource attribute.
Sets `enabled` to `true` by default. | no +[host.type][res-attr-cfg] | Toggles the `host.type` resource attribute.
Sets `enabled` to `true` by default. | no +[k8s.cluster.name][res-attr-cfg] | Toggles the `k8s.cluster.name` resource attribute.
Sets `enabled` to `true` by default. | no + +#### Google Compute Engine (GCE) metadata + +* `cloud.provider`: `"gcp"` +* `cloud.platform`: `"gcp_compute_engine"` +* `cloud.account.id`: project id +* `cloud.region`: e.g. `"us-central1"` +* `cloud.availability_zone`: e.g. `"us-central1-c"` +* `host.id`: instance id +* `host.name`: instance name +* `host.type`: machine type +* (optional) `gcp.gce.instance.hostname` +* (optional) `gcp.gce.instance.name` + +#### Google Kubernetes Engine (GKE) metadata + +* `cloud.provider`: `"gcp"` +* `cloud.platform`: `"gcp_kubernetes_engine"` +* `cloud.account.id`: project id +* `cloud.region`: only for regional GKE clusters; e.g. `"us-central1"` +* `cloud.availability_zone`: only for zonal GKE clusters; e.g. `"us-central1-c"` +* `k8s.cluster.name` +* `host.id`: instance id +* `host.name`: instance name; only when workload identity is disabled + +One known issue happens when GKE workload identity is enabled. The GCE metadata endpoints won't be available, +and the GKE resource detector won't be able to determine `host.name`. +If this happens, you can set `host.name` from one of the following resources: +- Get the `node.name` through the [downward API][] with the `env` detector. +- Get the Kubernetes node name from the Kubernetes API (with `k8s.io/client-go`). + +[downward API]: https://kubernetes.io/docs/concepts/workloads/pods/downward-api/ + +#### Google Cloud Run Services metadata + +* `cloud.provider`: `"gcp"` +* `cloud.platform`: `"gcp_cloud_run"` +* `cloud.account.id`: project id +* `cloud.region`: e.g. `"us-central1"` +* `faas.id`: instance id +* `faas.name`: service name +* `faas.version`: service revision + +#### Cloud Run Jobs metadata + +* `cloud.provider`: `"gcp"` +* `cloud.platform`: `"gcp_cloud_run"` +* `cloud.account.id`: project id +* `cloud.region`: e.g. `"us-central1"` +* `faas.id`: instance id +* `faas.name`: service name +* `gcp.cloud_run.job.execution`: e.g. `"my-service-ajg89"` +* `gcp.cloud_run.job.task_index`: e.g. `"0"` + +#### Google Cloud Functions metadata + +* `cloud.provider`: `"gcp"` +* `cloud.platform`: `"gcp_cloud_functions"` +* `cloud.account.id`: project id +* `cloud.region`: e.g. `"us-central1"` +* `faas.id`: instance id +* `faas.name`: function name +* `faas.version`: function version + +#### Google App Engine metadata + +* `cloud.provider`: `"gcp"` +* `cloud.platform`: `"gcp_app_engine"` +* `cloud.account.id`: project id +* `cloud.region`: e.g. `"us-central1"` +* `cloud.availability_zone`: e.g. `"us-central1-c"` +* `faas.id`: instance id +* `faas.name`: service name +* `faas.version`: service version + +### heroku + +The `heroku` block adds resource attributes derived from [Heroku dyno metadata][]. + +The `heroku` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#heroku--resource_attributes) | Configures which resource attributes to add. | no + +#### heroku > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +--------------------------------------- | --------------------------------------------------------------------------------------------------- | -------- +[cloud.provider][res-attr-cfg] | Toggles the `cloud.provider` resource attribute.
Sets `enabled` to `true` by default. | no +[heroku.app.id][res-attr-cfg] | Toggles the `heroku.app.id` resource attribute.
Sets `enabled` to `true` by default. | no +[heroku.dyno.id][res-attr-cfg] | Toggles the `heroku.dyno.id` resource attribute.
Sets `enabled` to `true` by default. | no +[heroku.release.commit][res-attr-cfg] | Toggles the `heroku.release.commit` resource attribute.
Sets `enabled` to `true` by default. | no +[heroku.release.creation_timestamp][res-attr-cfg] | Toggles the `heroku.release.creation_timestamp` resource attribute.
Sets `enabled` to `true` by default. | no +[service.instance.id][res-attr-cfg] | Toggles the `service.instance.id` resource attribute.
Sets `enabled` to `true` by default. | no +[service.name][res-attr-cfg] | Toggles the `service.name` resource attribute.
Sets `enabled` to `true` by default. | no +[service.version][res-attr-cfg] | Toggles the `service.version` resource attribute.
Sets `enabled` to `true` by default. | no + +When [Heroku dyno metadata][] is active, Heroku applications publish information through environment variables. +We map these environment variables to resource attributes as follows: + +| Dyno metadata environment variable | Resource attribute | +|------------------------------------|-------------------------------------| +| `HEROKU_APP_ID` | `heroku.app.id` | +| `HEROKU_APP_NAME` | `service.name` | +| `HEROKU_DYNO_ID` | `service.instance.id` | +| `HEROKU_RELEASE_CREATED_AT` | `heroku.release.creation_timestamp` | +| `HEROKU_RELEASE_VERSION` | `service.version` | +| `HEROKU_SLUG_COMMIT` | `heroku.release.commit` | + +For more information, see the [Heroku cloud provider documentation][] under the [OpenTelemetry specification semantic conventions][]. + +[Heroku dyno metadata]: https://devcenter.heroku.com/articles/dyno-metadata +[Heroku cloud provider documentation]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/resource/semantic_conventions/cloud_provider/heroku.md +[OpenTelemetry specification semantic conventions]: https://github.com/open-telemetry/opentelemetry-specification + +### system + +The `system` block queries the host machine to retrieve various resource attributes. + +{{% admonition type="note" %}} + +Use the [Docker](#docker) detector if running {{< param "PRODUCT_ROOT_NAME" >}} as a Docker container. + +{{% /admonition %}} + +The `system` block supports the following attributes: + +Attribute | Type | Description | Default | Required +------------------ | --------------- | --------------------------------------------------------------------------- |---------------- | -------- +`hostname_sources` | `list(string)` | A priority list of sources from which the hostname will be fetched. | `["dns", "os"]` | no + +The valid options for `hostname_sources` are: +* `"dns"`: Uses multiple sources to get the fully qualified domain name. +Firstly, it looks up the host name in the local machine's `hosts` file. If that fails, it looks up the CNAME. +Lastly, if that fails, it does a reverse DNS query. Note: this hostname source may produce unreliable results on Windows. +To produce a FQDN, Windows hosts might have better results using the "lookup" hostname source, which is mentioned below. +* `"os"`: Provides the hostname provided by the local machine's kernel. +* `"cname"`: Provides the canonical name, as provided by `net.LookupCNAME` in the Go standard library. +Note: this hostname source may produce unreliable results on Windows. +* `"lookup"`: Does a reverse DNS lookup of the current host's IP address. + +In case of an error in fetching a hostname from a source, the next source from the list of `hostname_sources` will be considered. + +The `system` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#system--resource_attributes) | Configures which resource attributes to add. | no + +#### system > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +--------------------------------------- | --------------------------------------------------------------------------------------------------- | -------- +[host.arch][res-attr-cfg] | Toggles the `host.arch` resource attribute.
Sets `enabled` to `false` by default. | no +[host.cpu.cache.l2.size][res-attr-cfg] | Toggles the `host.cpu.cache.l2.size` resource attribute.
Sets `enabled` to `false` by default. | no +[host.cpu.family][res-attr-cfg] | Toggles the `host.cpu.family` resource attribute.
Sets `enabled` to `false` by default. | no +[host.cpu.model.id][res-attr-cfg] | Toggles the `host.cpu.model.id` resource attribute.
Sets `enabled` to `false` by default. | no +[host.cpu.model.name][res-attr-cfg] | Toggles the `host.cpu.model.name` resource attribute.
Sets `enabled` to `false` by default. | no +[host.cpu.stepping][res-attr-cfg] | Toggles the `host.cpu.stepping` resource attribute.
Sets `enabled` to `false` by default. | no +[host.cpu.vendor.id][res-attr-cfg] | Toggles the `host.cpu.vendor.id` resource attribute.
Sets `enabled` to `false` by default. | no +[host.id][res-attr-cfg] | Toggles the `host.id` resource attribute.
Sets `enabled` to `false` by default. | no +[host.name][res-attr-cfg] | Toggles the `host.name` resource attribute.
Sets `enabled` to `true` by default. | no +[os.description][res-attr-cfg] | Toggles the `os.description` resource attribute.
Sets `enabled` to `false` by default. | no +[os.type][res-attr-cfg] | Toggles the `os.type` resource attribute.
Sets `enabled` to `true` by default. | no + +### openshift + +The `openshift` block queries the OpenShift and Kubernetes APIs to retrieve various resource attributes. + +The `openshift` block supports the following attributes: + +Attribute | Type | Description | Default | Required +---------- |---------- | ------------------------------------------------------- |-------------| -------- +`address` | `string` | Address of the OpenShift API server. | _See below_ | no +`token` | `string` | Token used to identify against the OpenShift API server.| "" | no + +The "get", "watch", and "list" permissions are required: + +```yaml +kind: ClusterRole +metadata: + name: grafana-agent +rules: +- apiGroups: ["config.openshift.io"] + resources: ["infrastructures", "infrastructures/status"] + verbs: ["get", "watch", "list"] +``` + +By default, the API address is determined from the environment variables `KUBERNETES_SERVICE_HOST`, +`KUBERNETES_SERVICE_PORT` and the service token is read from `/var/run/secrets/kubernetes.io/serviceaccount/token`. +If TLS is not explicitly disabled and no `ca_file` is configured, `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` is used. +The determination of the API address, `ca_file`, and the service token is skipped if they are set in the configuration. + +The `openshift` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ---------------------------------------------------- | -------- +[resource_attributes](#openshift--resource_attributes) | Configures which resource attributes to add. | no +[tls](#openshift--tls) | TLS settings for the connection with the OpenShift API. | yes + +#### openshift > tls + +The `tls` block configures TLS settings used for the connection to the gRPC +server. + +{{< docs/shared lookup="flow/reference/components/otelcol-tls-config-block.md" source="agent" version="" >}} + +#### openshift > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +--------------------------------- | --------------------------------------------------------------------------------------------- | -------- +[cloud.platform][res-attr-cfg] | Toggles the `cloud.platform` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.provider][res-attr-cfg] | Toggles the `cloud.provider` resource attribute.
Sets `enabled` to `true` by default. | no +[cloud.region][res-attr-cfg] | Toggles the `cloud.region` resource attribute.
Sets `enabled` to `true` by default. | no +[k8s.cluster.name][res-attr-cfg] | Toggles the `k8s.cluster.name` resource attribute.
Sets `enabled` to `true` by default. | no + +### kubernetes_node + +The `kubernetes_node` block queries the Kubernetes API server to retrieve various node resource attributes. + +The `kubernetes_node` block supports the following attributes: + +Attribute | Type | Description | Default | Required +------------------- |--------- | ------------------------------------------------------------------------- |------------------ | -------- +`auth_type` | `string` | Configures how to authenticate to the K8s API server. | `"none"` | no +`context` | `string` | Override the current context when `auth_type` is set to `"kubeConfig"`. | `""` | no +`node_from_env_var` | `string` | The name of an environment variable from which to retrieve the node name. | `"K8S_NODE_NAME"` | no + +The "get" and "list" permissions are required: + +```yaml +kind: ClusterRole +metadata: + name: grafana-agent +rules: + - apiGroups: [""] + resources: ["nodes"] + verbs: ["get", "list"] +``` + +`auth_type` can be set to one of the following: +* `none`: no authentication. +* `serviceAccount`: use the standard service account token provided to the agent pod. +* `kubeConfig`: use credentials from `~/.kube/config`. + +The `kubernetes_node` block supports the following blocks: + +Block | Description | Required +---------------------------------------------- | ------------------------------------------------- | -------- +[resource_attributes](#kubernetes_node--resource_attributes) | Configures which resource attributes to add. | no + +#### kubernetes_node > resource_attributes + +The `resource_attributes` block supports the following blocks: + +Block | Description | Required +------------------------------ | ------------------------------------------------------------------------------------------ | -------- +[k8s.node.name][res-attr-cfg] | Toggles the `k8s.node.name` resource attribute.
Sets `enabled` to `true` by default. | no +[k8s.node.uid][res-attr-cfg] | Toggles the `k8s.node.uid` resource attribute.
Sets `enabled` to `true` by default. | no + +## Common configuration + +### Resource attribute config + +This block describes how to configure resource attributes such as `k8s.node.name` and `azure.vm.name`. +Every block is configured using the same set of attributes. +Only the default values for those attributes might differ across resource attributes. +For example, some resource attributes have `enabled` set to `true` by default, whereas others do not. + +The following attributes are supported: + +Attribute | Type | Description | Default | Required +--------- | ------- | ----------------------------------------------------------------------------------- |------------- | -------- +`enabled` | `bool` | Toggles whether to add the resource attribute to the span, log, or metric resource. | _See below_ | no + +To see the default value for `enabled`, refer to the tables in the sections above which list the resource attributes blocks. +The "Description" column will state either... + +> Sets `enabled` to `true` by default. + +... or: + +> Sets `enabled` to `false` by default. + +## Exported fields + +The following fields are exported and can be referenced by other components: + +Name | Type | Description +---- | ---- | ----------- +`input` | `otelcol.Consumer` | A value that other components can use to send telemetry data to. + +`input` accepts `otelcol.Consumer` OTLP-formatted data for any telemetry signal of these types: +* logs +* metrics +* traces + +## Component health + +`otelcol.processor.resourcedetection` is only reported as unhealthy if given an invalid +configuration. + +## Debug information + +`otelcol.processor.resourcedetection` does not expose any component-specific debug +information. + +## Examples + +### env detector + +If you set up a `OTEL_RESOURCE_ATTRIBUTES` environment variable with value of `TestKey=TestValue`, +then all logs, metrics, and traces will have a resource attribute with a key `TestKey` and value of `TestValue`. + +```river +otelcol.processor.resourcedetection "default" { + detectors = ["env"] + + output { + logs = [otelcol.exporter.otlp.default.input] + metrics = [otelcol.exporter.otlp.default.input] + traces = [otelcol.exporter.otlp.default.input] + } +} +``` + +### env and ec2 + +There is no need to put in an `ec2 {}` River block. +The `ec2` defaults will be applied automatically, as specified in [ec2][]. + +```river +otelcol.processor.resourcedetection "default" { + detectors = ["env", "ec2"] + + output { + logs = [otelcol.exporter.otlp.default.input] + metrics = [otelcol.exporter.otlp.default.input] + traces = [otelcol.exporter.otlp.default.input] + } +} +``` + +### ec2 with default resource attributes + +There is no need to put in a `ec2 {}` River block. +The `ec2` defaults will be applied automatically, as specified in [ec2][]. + +```river +otelcol.processor.resourcedetection "default" { + detectors = ["ec2"] + + output { + logs = [otelcol.exporter.otlp.default.input] + metrics = [otelcol.exporter.otlp.default.input] + traces = [otelcol.exporter.otlp.default.input] + } +} +``` + +### ec2 with explicit resource attributes + +```river +otelcol.processor.resourcedetection "default" { + detectors = ["ec2"] + ec2 { + tags = ["^tag1$", "^tag2$", "^label.*$"] + resource_attributes { + cloud.account.id { enabled = true } + cloud.availability_zone { enabled = true } + cloud.platform { enabled = true } + cloud.provider { enabled = true } + cloud.region { enabled = true } + host.id { enabled = true } + host.image.id { enabled = false } + host.name { enabled = false } + host.type { enabled = false } + } + } + + output { + logs = [otelcol.exporter.otlp.default.input] + metrics = [otelcol.exporter.otlp.default.input] + traces = [otelcol.exporter.otlp.default.input] + } +} +``` + +### kubernetes_node + +This example uses the default `node_from_env_var` option of `K8S_NODE_NAME`. + +There is no need to put in a `kubernetes_node {}` River block. +The `kubernetes_node` defaults will be applied automatically, as specified in [kubernetes_node][]. + +```river +otelcol.processor.resourcedetection "default" { + detectors = ["kubernetes_node"] + + output { + logs = [otelcol.exporter.otlp.default.input] + metrics = [otelcol.exporter.otlp.default.input] + traces = [otelcol.exporter.otlp.default.input] + } +} +``` + +You need to add this to your workload: + +```yaml + env: + - name: K8S_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName +``` + +### kubernetes_node with a custom environment variable + +This example uses a custom `node_from_env_var` set to `my_custom_var`. + +```river +otelcol.processor.resourcedetection "default" { + detectors = ["kubernetes_node"] + kubernetes_node { + node_from_env_var = "my_custom_var" + } + + output { + logs = [otelcol.exporter.otlp.default.input] + metrics = [otelcol.exporter.otlp.default.input] + traces = [otelcol.exporter.otlp.default.input] + } +} +``` + +You need to add this to your workload: + +```yaml + env: + - name: my_custom_var + valueFrom: + fieldRef: + fieldPath: spec.nodeName +``` + + +## Compatible components + +`otelcol.processor.resourcedetection` can accept arguments from the following components: + +- Components that export [OpenTelemetry `otelcol.Consumer`]({{< relref "../compatibility/#opentelemetry-otelcolconsumer-exporters" >}}) + +`otelcol.processor.resourcedetection` has exports that can be consumed by the following components: + +- Components that consume [OpenTelemetry `otelcol.Consumer`]({{< relref "../compatibility/#opentelemetry-otelcolconsumer-consumers" >}}) + +{{% admonition type="note" %}} + +Connecting some components may not be sensible or components may require further configuration to make the +connection work correctly. Refer to the linked documentation for more details. + +{{% /admonition %}} + + \ No newline at end of file diff --git a/go.mod b/go.mod index 68effdadae28..08b14e32e8f9 100644 --- a/go.mod +++ b/go.mod @@ -115,6 +115,7 @@ require ( github.com/open-telemetry/opentelemetry-collector-contrib/processor/attributesprocessor v0.87.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sattributesprocessor v0.87.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.87.0 + github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.87.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/servicegraphprocessor v0.87.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/spanmetricsprocessor v0.87.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/spanprocessor v0.87.0 @@ -620,7 +621,9 @@ require ( dario.cat/mergo v1.0.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v4 v4.2.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v2 v2.2.1 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.20.0 // indirect github.com/Shopify/sarama v1.38.1 // indirect + github.com/Showmax/go-fqdn v1.0.0 // indirect github.com/Workiva/go-datastructures v1.1.0 // indirect github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.0 // indirect github.com/channelmeter/iso8601duration v0.0.0-20150204201828-8da3af7a2a61 // indirect @@ -639,8 +642,11 @@ require ( github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/ecsutil v0.87.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.87.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8sconfig v0.87.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka v0.87.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders v0.87.0 // indirect github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/prometheusremotewrite v0.87.0 // indirect github.com/openshift/api v3.9.0+incompatible // indirect github.com/openshift/client-go v0.0.0-20210521082421-73d9475a9142 // indirect diff --git a/go.sum b/go.sum index 7c8b57ded56a..b520f196092f 100644 --- a/go.sum +++ b/go.sum @@ -183,6 +183,8 @@ github.com/DataDog/datadog-go v0.0.0-20160329135253-cc2f4770f4d6/go.mod h1:LButx github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 h1:KeNholpO2xKjgaaSyd+DyQRrsQjhbSeS7qe4nEw8aQw= github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962/go.mod h1:kC29dT1vFpj7py2OvG1khBdQpo3kInWP+6QipLbdngo= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.20.0 h1:tk85AYGwOf6VNtoOQi8w/kVDi2vmPxp3/OU2FsUpdcA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.20.0/go.mod h1:Xx0VKh7GJ4si3rmElbh19Mejxz68ibWg/J30ZOMrqzU= github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/IBM/sarama v1.42.1 h1:wugyWa15TDEHh2kvq2gAy1IHLjEjuYOYgXz/ruC/OSQ= @@ -237,6 +239,8 @@ github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWso github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/Shopify/toxiproxy/v2 v2.5.0 h1:i4LPT+qrSlKNtQf5QliVjdP08GyAH8+BUIc9gT0eahc= github.com/Shopify/toxiproxy/v2 v2.5.0/go.mod h1:yhM2epWtAmel9CB8r2+L+PCmhH6yH2pITaPAo7jxJl0= +github.com/Showmax/go-fqdn v1.0.0 h1:0rG5IbmVliNT5O19Mfuvna9LL7zlHyRfsSvBPZmF9tM= +github.com/Showmax/go-fqdn v1.0.0/go.mod h1:SfrFBzmDCtCGrnHhoDjuvFnKsWjEQX/Q9ARZvOrJAko= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= @@ -1739,6 +1743,8 @@ github.com/open-telemetry/opentelemetry-collector-contrib/extension/oauth2client github.com/open-telemetry/opentelemetry-collector-contrib/extension/oauth2clientauthextension v0.87.0/go.mod h1:DRpgdIDMa+CFE96SoEPwigGBuZbwSNWotTgkJlrZMVc= github.com/open-telemetry/opentelemetry-collector-contrib/extension/sigv4authextension v0.87.0 h1:Z4o71/rS7mmpJ/9uzta3/nTaT+vKt0CU35o4inDLA9Y= github.com/open-telemetry/opentelemetry-collector-contrib/extension/sigv4authextension v0.87.0/go.mod h1:clScLUe8m0CTZMcV0scqq+fFFvw5Q1dASkYlYsrRptM= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/ecsutil v0.87.0 h1:JJsQ6iMFIDb7W6uLh6LQ5k4XOgWolr7ugVBoeV4l7hQ= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/aws/ecsutil v0.87.0/go.mod h1:rDdtaUrMV6TJHqssyiYSfsLfFN1pIg4JOTDaE9AUapQ= github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.87.0 h1:W4Ty2pSyge/qNAOILO6HqyKrAcgALs0bn5CmpGZJXVo= github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.87.0/go.mod h1:3EFmVoLcdM8Adj75N8TGJ4txDB29oW1chTLCFiL/wxs= github.com/open-telemetry/opentelemetry-collector-contrib/internal/coreinternal v0.87.0 h1:ekT4/I9J484j4yR/0VHj5AGtgv8KmNd+e4oXxNJNR/o= @@ -1751,6 +1757,8 @@ github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8stest v0.87 github.com/open-telemetry/opentelemetry-collector-contrib/internal/k8stest v0.87.0/go.mod h1:ntSfqIeoGj0O+pXXyqDG9iTAw/PQg2JsO26EJ1GAKto= github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka v0.87.0 h1:kDamu7uZHRmeJWqaJg42LSgprRGokmQ4t8ACslzS0GU= github.com/open-telemetry/opentelemetry-collector-contrib/internal/kafka v0.87.0/go.mod h1:EAw9aBkrDIDWQvRBdJiDkaJmCqcgZpiZzYZEvOjg4uI= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders v0.87.0 h1:8pVElJ4AMIiJxS+sxnK9CX73RED7iv/FYbqkvvX01ig= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/metadataproviders v0.87.0/go.mod h1:zRQU4eN6rNXeVKD8g2p2Czb88o/Hd2BkVdar5nCk0+k= github.com/open-telemetry/opentelemetry-collector-contrib/internal/sharedcomponent v0.87.0 h1:sx1ye7Y2rJ2qi11i2ih9T7BocxaV0uaBBf7B8ijCYpU= github.com/open-telemetry/opentelemetry-collector-contrib/internal/sharedcomponent v0.87.0/go.mod h1:AobBiNPFNHUm0MJFTieajasG/xNMjMYI7BGGTSKh0xg= github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.87.0 h1:sy75u6ZwBvRwv9RjEF65SqlkBsAeZFqF4+eFOLhIsJQ= @@ -1783,6 +1791,8 @@ github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sattribute github.com/open-telemetry/opentelemetry-collector-contrib/processor/k8sattributesprocessor v0.87.0/go.mod h1:g6H0fB9TW03Lb8M+H0BXtgQp7gPncIwf3Fk73xOs9EA= github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.87.0 h1:QJKdtNcsxBhG2ZwSzYRVI0oxUqBJJvhfWf0OnjHU3jY= github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.87.0/go.mod h1:skMmFcl+gxyiOQXvwHc0IKpC73iyQ7zl9r1aRNmPMwI= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.87.0 h1:gEv7UNu4K5ptvKIpWQmVS+0XMrIzqZWczcjyhLnsx9M= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/resourcedetectionprocessor v0.87.0/go.mod h1:6Rnjwj4bZU7Ab+nLD1YqQlbdsnsKoOR/OzyI42+PyE8= github.com/open-telemetry/opentelemetry-collector-contrib/processor/servicegraphprocessor v0.87.0 h1:BIGb6dfmaTlDE7KbiQUhnD9SvL5HanbJbWJrnzURfPY= github.com/open-telemetry/opentelemetry-collector-contrib/processor/servicegraphprocessor v0.87.0/go.mod h1:EnaQxXfCCWkSEfsQbGOvYbeJ/EuqvtMYTLTq8RN6TiY= github.com/open-telemetry/opentelemetry-collector-contrib/processor/spanmetricsprocessor v0.87.0 h1:4l/QetnprIMethZYfD2RK+MfMR83f6QycYb9bhJFItc=