From ff2073bea359b1803eef26c5951a5aa2a3e874c3 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Wed, 4 Mar 2026 15:23:35 -0300 Subject: [PATCH 1/3] Implement prometheus-collector-bridge Signed-off-by: Arthur Silva Sens --- otelcollector/config.go | 160 +++++++++++++++++++++++++ otelcollector/config_test.go | 154 ++++++++++++++++++++++++ otelcollector/factory.go | 27 +++++ otelcollector/factory_test.go | 43 +++++++ otelcollector/go.mod | 75 ++++++++++++ otelcollector/go.sum | 191 ++++++++++++++++++++++++++++++ otelcollector/lifecycle.go | 199 ++++++++++++++++++++++++++++++++ otelcollector/lifecycle_test.go | 131 +++++++++++++++++++++ 8 files changed, 980 insertions(+) create mode 100644 otelcollector/config.go create mode 100644 otelcollector/config_test.go create mode 100644 otelcollector/factory.go create mode 100644 otelcollector/factory_test.go create mode 100644 otelcollector/go.mod create mode 100644 otelcollector/go.sum create mode 100644 otelcollector/lifecycle.go create mode 100644 otelcollector/lifecycle_test.go diff --git a/otelcollector/config.go b/otelcollector/config.go new file mode 100644 index 0000000..8f6a34a --- /dev/null +++ b/otelcollector/config.go @@ -0,0 +1,160 @@ +package otelcollector + +import ( + "fmt" + "net/http" + "time" + + prombridge "github.com/ArthurSens/prometheus-collector-bridge" +) + +// Config maps stackdriver_exporter runtime settings into exporter_config. +type Config struct { + ProjectIDs []string `mapstructure:"project_ids"` + ProjectsFilter string `mapstructure:"projects_filter"` + UniverseDomain string `mapstructure:"universe_domain"` + MaxRetries int `mapstructure:"max_retries"` + HTTPTimeout string `mapstructure:"http_timeout"` + MaxBackoff string `mapstructure:"max_backoff"` + BackoffJitter string `mapstructure:"backoff_jitter"` + RetryStatuses []int `mapstructure:"retry_statuses"` + MetricsPrefixes []string `mapstructure:"metrics_prefixes"` + MetricsInterval string `mapstructure:"metrics_interval"` + MetricsOffset string `mapstructure:"metrics_offset"` + MetricsIngest bool `mapstructure:"metrics_ingest_delay"` + FillMissing bool `mapstructure:"fill_missing_labels"` + DropDelegated bool `mapstructure:"drop_delegated_projects"` + Filters []string `mapstructure:"filters"` + AggregateDeltas bool `mapstructure:"aggregate_deltas"` + DeltasTTL string `mapstructure:"aggregate_deltas_ttl"` + DescriptorTTL string `mapstructure:"descriptor_cache_ttl"` + DescriptorGoogleOnly bool `mapstructure:"descriptor_cache_only_google"` +} + +var _ prombridge.Config = (*Config)(nil) + +func defaultConfig() *Config { + return &Config{ + UniverseDomain: "googleapis.com", + MaxRetries: 0, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + RetryStatuses: []int{503}, + MetricsInterval: "5m", + MetricsOffset: "0s", + MetricsIngest: false, + FillMissing: true, + DropDelegated: false, + AggregateDeltas: false, + DeltasTTL: "30m", + DescriptorTTL: "0s", + DescriptorGoogleOnly: true, + } +} + +func defaultComponentDefaults() map[string]interface{} { + cfg := defaultConfig() + return map[string]interface{}{ + "max_retries": cfg.MaxRetries, + "http_timeout": cfg.HTTPTimeout, + "max_backoff": cfg.MaxBackoff, + "backoff_jitter": cfg.BackoffJitter, + "retry_statuses": cfg.RetryStatuses, + "universe_domain": cfg.UniverseDomain, + "metrics_interval": cfg.MetricsInterval, + "metrics_offset": cfg.MetricsOffset, + "metrics_ingest_delay": cfg.MetricsIngest, + "fill_missing_labels": cfg.FillMissing, + "drop_delegated_projects": cfg.DropDelegated, + "aggregate_deltas": cfg.AggregateDeltas, + "aggregate_deltas_ttl": cfg.DeltasTTL, + "descriptor_cache_ttl": cfg.DescriptorTTL, + "descriptor_cache_only_google": cfg.DescriptorGoogleOnly, + } +} + +func (c *Config) Validate() error { + if len(c.MetricsPrefixes) == 0 { + return fmt.Errorf("metrics_prefixes must have at least one entry") + } + + _, err := c.parsedDurations() + if err != nil { + return err + } + + for _, code := range c.RetryStatuses { + if code < http.StatusContinue || code > 599 { + return fmt.Errorf("retry status %d is not a valid HTTP status code", code) + } + } + + return nil +} + +type parsedConfig struct { + HTTPTimeout time.Duration + MaxBackoff time.Duration + BackoffJitter time.Duration + MetricsInterval time.Duration + MetricsOffset time.Duration + DeltasTTL time.Duration + DescriptorTTL time.Duration +} + +func (c *Config) parsedDurations() (parsedConfig, error) { + parse := func(name, raw string) (time.Duration, error) { + d, err := time.ParseDuration(raw) + if err != nil { + return 0, fmt.Errorf("%s: invalid duration %q: %w", name, raw, err) + } + return d, nil + } + + httpTimeout, err := parse("http_timeout", c.HTTPTimeout) + if err != nil { + return parsedConfig{}, err + } + maxBackoff, err := parse("max_backoff", c.MaxBackoff) + if err != nil { + return parsedConfig{}, err + } + backoffJitter, err := parse("backoff_jitter", c.BackoffJitter) + if err != nil { + return parsedConfig{}, err + } + metricsInterval, err := parse("metrics_interval", c.MetricsInterval) + if err != nil { + return parsedConfig{}, err + } + metricsOffset, err := parse("metrics_offset", c.MetricsOffset) + if err != nil { + return parsedConfig{}, err + } + deltasTTL, err := parse("aggregate_deltas_ttl", c.DeltasTTL) + if err != nil { + return parsedConfig{}, err + } + descriptorTTL, err := parse("descriptor_cache_ttl", c.DescriptorTTL) + if err != nil { + return parsedConfig{}, err + } + + return parsedConfig{ + HTTPTimeout: httpTimeout, + MaxBackoff: maxBackoff, + BackoffJitter: backoffJitter, + MetricsInterval: metricsInterval, + MetricsOffset: metricsOffset, + DeltasTTL: deltasTTL, + DescriptorTTL: descriptorTTL, + }, nil +} + +type configUnmarshaler struct{} + +func (configUnmarshaler) GetConfigStruct() prombridge.Config { + return defaultConfig() +} + diff --git a/otelcollector/config_test.go b/otelcollector/config_test.go new file mode 100644 index 0000000..61bcb0a --- /dev/null +++ b/otelcollector/config_test.go @@ -0,0 +1,154 @@ +package otelcollector + +import ( + "testing" + "time" +) + +func TestConfig_Validate(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + cfg Config + wantErr bool + }{ + { + name: "valid with project IDs", + cfg: Config{ + ProjectIDs: []string{"my-project"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + }, + }, + { + name: "valid with projects filter", + cfg: Config{ + ProjectsFilter: "parent.type:folder parent.id:123", + MetricsPrefixes: []string{"pubsub.googleapis.com/subscription"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + }, + }, + { + name: "valid with implicit default project discovery", + cfg: Config{ + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + }, + }, + { + name: "invalid without metrics prefixes", + cfg: Config{ + ProjectIDs: []string{"my-project"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + }, + wantErr: true, + }, + { + name: "invalid timeout", + cfg: Config{ + ProjectIDs: []string{"my-project"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "not-a-duration", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + }, + wantErr: true, + }, + { + name: "invalid retry status", + cfg: Config{ + ProjectIDs: []string{"my-project"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + RetryStatuses: []int{99}, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + err := tt.cfg.Validate() + if (err != nil) != tt.wantErr { + t.Fatalf("Validate() error = %v, wantErr = %v", err, tt.wantErr) + } + }) + } +} + +func TestConfig_Durations(t *testing.T) { + t.Parallel() + + cfg := Config{ + ProjectIDs: []string{"my-project"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "2m", + DeltasTTL: "30m", + DescriptorTTL: "0s", + } + + parsed, err := cfg.parsedDurations() + if err != nil { + t.Fatalf("parsedDurations() error = %v", err) + } + + if parsed.HTTPTimeout != 10*time.Second { + t.Fatalf("HTTPTimeout = %v, want %v", parsed.HTTPTimeout, 10*time.Second) + } + if parsed.MaxBackoff != 5*time.Second { + t.Fatalf("MaxBackoff = %v, want %v", parsed.MaxBackoff, 5*time.Second) + } + if parsed.BackoffJitter != 1*time.Second { + t.Fatalf("BackoffJitter = %v, want %v", parsed.BackoffJitter, 1*time.Second) + } + if parsed.MetricsInterval != 5*time.Minute { + t.Fatalf("MetricsInterval = %v, want %v", parsed.MetricsInterval, 5*time.Minute) + } + if parsed.MetricsOffset != 2*time.Minute { + t.Fatalf("MetricsOffset = %v, want %v", parsed.MetricsOffset, 2*time.Minute) + } + if parsed.DeltasTTL != 30*time.Minute { + t.Fatalf("DeltasTTL = %v, want %v", parsed.DeltasTTL, 30*time.Minute) + } + if parsed.DescriptorTTL != 0 { + t.Fatalf("DescriptorTTL = %v, want %v", parsed.DescriptorTTL, 0*time.Second) + } +} + diff --git a/otelcollector/factory.go b/otelcollector/factory.go new file mode 100644 index 0000000..48a3573 --- /dev/null +++ b/otelcollector/factory.go @@ -0,0 +1,27 @@ +package otelcollector + +import ( + "log/slog" + + prombridge "github.com/ArthurSens/prometheus-collector-bridge" + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/receiver" +) + +var receiverType = component.MustNewType("stackdriver_exporter") + +func NewFactory() receiver.Factory { + return prombridge.NewFactory( + receiverType, + newLifecycleManager(slog.Default()), + configUnmarshaler{}, + prombridge.WithComponentDefaults(defaultComponentDefaults()), + ) +} + +// Keep compiler checks close to factory wiring. +var ( + _ prombridge.ExporterLifecycleManager = (*lifecycleManager)(nil) + _ prombridge.ConfigUnmarshaler = (configUnmarshaler{}) +) + diff --git a/otelcollector/factory_test.go b/otelcollector/factory_test.go new file mode 100644 index 0000000..1923374 --- /dev/null +++ b/otelcollector/factory_test.go @@ -0,0 +1,43 @@ +package otelcollector + +import ( + "context" + "testing" + + prombridge "github.com/ArthurSens/prometheus-collector-bridge" + "go.opentelemetry.io/collector/consumer/consumertest" + "go.opentelemetry.io/collector/receiver/receivertest" +) + +func TestNewFactory(t *testing.T) { + t.Parallel() + + factory := NewFactory() + if factory == nil { + t.Fatal("NewFactory() returned nil") + } +} + +func TestFactory_CreateMetrics(t *testing.T) { + t.Parallel() + + factory := NewFactory() + cfg := factory.CreateDefaultConfig().(*prombridge.ReceiverConfig) + + cfg.ExporterConfig = map[string]interface{}{ + "project_ids": []string{"my-project"}, + "metrics_prefixes": []string{"compute.googleapis.com/instance"}, + } + + settings := receivertest.NewNopSettings(receiverType) + consumer := new(consumertest.MetricsSink) + + recv, err := factory.CreateMetrics(context.Background(), settings, cfg, consumer) + if err != nil { + t.Fatalf("CreateMetrics() error = %v", err) + } + if recv == nil { + t.Fatal("CreateMetrics() returned nil receiver") + } +} + diff --git a/otelcollector/go.mod b/otelcollector/go.mod new file mode 100644 index 0000000..f7f60f7 --- /dev/null +++ b/otelcollector/go.mod @@ -0,0 +1,75 @@ +module github.com/prometheus-community/stackdriver_exporter/otelcollector + +go 1.25.0 + +require ( + github.com/ArthurSens/prometheus-collector-bridge v0.0.0-20260227220117-1744a07e66f4 + github.com/PuerkitoBio/rehttp v1.4.0 + github.com/prometheus-community/stackdriver_exporter v0.0.0 + github.com/prometheus/client_golang v1.23.2 + go.opentelemetry.io/collector/component v1.53.0 + go.opentelemetry.io/collector/consumer/consumertest v0.147.0 + go.opentelemetry.io/collector/receiver v1.53.0 + go.opentelemetry.io/collector/receiver/receivertest v0.147.0 + golang.org/x/oauth2 v0.34.0 + google.golang.org/api v0.251.0 +) + +require ( + cloud.google.com/go/auth v0.16.5 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fatih/camelcase v1.0.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/gax-go/v2 v2.15.0 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.19.2 // indirect + github.com/stretchr/testify v1.11.1 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/collector/component/componenttest v0.147.0 // indirect + go.opentelemetry.io/collector/consumer v1.53.0 // indirect + go.opentelemetry.io/collector/consumer/consumererror v0.147.0 // indirect + go.opentelemetry.io/collector/consumer/xconsumer v0.147.0 // indirect + go.opentelemetry.io/collector/featuregate v1.53.0 // indirect + go.opentelemetry.io/collector/internal/componentalias v0.147.0 // indirect + go.opentelemetry.io/collector/pdata v1.53.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.147.0 // indirect + go.opentelemetry.io/collector/pipeline v1.53.0 // indirect + go.opentelemetry.io/collector/receiver/xreceiver v0.147.0 // indirect + go.opentelemetry.io/contrib/bridges/prometheus v0.65.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect + go.opentelemetry.io/otel v1.40.0 // indirect + go.opentelemetry.io/otel/metric v1.40.0 // indirect + go.opentelemetry.io/otel/sdk v1.40.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.40.0 // indirect + go.opentelemetry.io/otel/trace v1.40.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.1 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + golang.org/x/crypto v0.48.0 // indirect + golang.org/x/net v0.51.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/text v0.34.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/grpc v1.79.1 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +replace github.com/prometheus-community/stackdriver_exporter => ../ diff --git a/otelcollector/go.sum b/otelcollector/go.sum new file mode 100644 index 0000000..c983bb8 --- /dev/null +++ b/otelcollector/go.sum @@ -0,0 +1,191 @@ +cloud.google.com/go/auth v0.16.5 h1:mFWNQ2FEVWAliEQWpAdH80omXFokmrnbDhUS9cBywsI= +cloud.google.com/go/auth v0.16.5/go.mod h1:utzRfHMP+Vv0mpOkTRQoWD2q3BatTOoWbA7gCc2dUhQ= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +github.com/ArthurSens/prometheus-collector-bridge v0.0.0-20260227220117-1744a07e66f4 h1:Cxm7zt1opKBW2EVoQi4D6THdQofmgcUsFNGy+HaZwmA= +github.com/ArthurSens/prometheus-collector-bridge v0.0.0-20260227220117-1744a07e66f4/go.mod h1:542FBzGmxriMHXZj6/PLgg1OXJW93TOIitB43+8sq/Y= +github.com/PuerkitoBio/rehttp v1.4.0 h1:rIN7A2s+O9fmHUM1vUcInvlHj9Ysql4hE+Y0wcl/xk8= +github.com/PuerkitoBio/rehttp v1.4.0/go.mod h1:LUwKPoDbDIA2RL5wYZCNsQ90cx4OJ4AWBmq6KzWZL1s= +github.com/aybabtme/iocontrol v0.0.0-20150809002002-ad15bcfc95a0 h1:0NmehRCgyk5rljDQLKUO+cRJCnduDyn11+zGZIc9Z48= +github.com/aybabtme/iocontrol v0.0.0-20150809002002-ad15bcfc95a0/go.mod h1:6L7zgvqo0idzI7IO8de6ZC051AfXb5ipkIJ7bIA2tGA= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= +github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/collector/component v1.53.0 h1:A+GU9n4eKnFVmrr7NPpbVvJ1kp985jXtachb9gy12mk= +go.opentelemetry.io/collector/component v1.53.0/go.mod h1:yqyFwDuP4JKwOFaxdqoWj25aVthtavGkSDp2K42x+YY= +go.opentelemetry.io/collector/component/componenttest v0.147.0 h1:9XTwUT87gFWScoP29GEyMjKjr0jycVon6u/EVLrw08w= +go.opentelemetry.io/collector/component/componenttest v0.147.0/go.mod h1:ph5UnCbKUeX3xBg9eSdueRnGmNB4DmhQ0KC6lTsGYTs= +go.opentelemetry.io/collector/consumer v1.53.0 h1:Gyy80dX5r1Lv9lvQk8XFtUkWs1eniicOzzCQBejLseg= +go.opentelemetry.io/collector/consumer v1.53.0/go.mod h1:f5U6ibd+XpC5eOSeEYhERAQJ2a5bp1d2RzW3MFddMDM= +go.opentelemetry.io/collector/consumer/consumererror v0.147.0 h1:c4jjAEke6AEqoxalOAIEudGuN4rnnheaLWdpJXPCAPQ= +go.opentelemetry.io/collector/consumer/consumererror v0.147.0/go.mod h1:9MwE9k6xHd3TGBSAeKSmt42dwWyxwUhYqfwPUx1ZQJY= +go.opentelemetry.io/collector/consumer/consumertest v0.147.0 h1:AU3sUm2L3pezrg6hzPJAO19ZANQoCcfgbyanN0q360g= +go.opentelemetry.io/collector/consumer/consumertest v0.147.0/go.mod h1:QWGFRmeYNbKaseDTNT3a2iGDmjl+DCZnLzMP7Rjj0JM= +go.opentelemetry.io/collector/consumer/xconsumer v0.147.0 h1:XJVQc2dYyalaFXMTa4/RE+aweQTiBpw1edfwdCIJSxw= +go.opentelemetry.io/collector/consumer/xconsumer v0.147.0/go.mod h1:mtwh1VsUoGjxwdmXEzjbswH7KAGByJNCIMHmhqwXeK0= +go.opentelemetry.io/collector/featuregate v1.53.0 h1:cgjXdtl7jezWxq6V0eohe/JqjY4PBotZGb5+bTR2OJw= +go.opentelemetry.io/collector/featuregate v1.53.0/go.mod h1:PS7zY/zaCb28EqciePVwRHVhc3oKortTFXsi3I6ee4g= +go.opentelemetry.io/collector/internal/componentalias v0.147.0 h1:cC1gEQwzQnDvbELVjE3FXqgBkrsUl5JhzOT+6hISaLI= +go.opentelemetry.io/collector/internal/componentalias v0.147.0/go.mod h1:RxuMjMy1j+2jZcY1Ej0E+NC6DnoqTMEvIwRiXtk82rc= +go.opentelemetry.io/collector/internal/testutil v0.147.0 h1:DFlRxBRp23/sZnpTITK25yqe0d56yNvK+63IaWc6OsU= +go.opentelemetry.io/collector/internal/testutil v0.147.0/go.mod h1:Jkjs6rkqs973LqgZ0Fe3zrokQRKULYXPIf4HuqStiEE= +go.opentelemetry.io/collector/pdata v1.53.0 h1:DlYDbRwammEZaxDZHINx5v0n8SEOVNniPbi6FRTlVkA= +go.opentelemetry.io/collector/pdata v1.53.0/go.mod h1:LRSYGNjKXaUrZEwZv3Yl+8/zV2HmRGKXW62zB2bysms= +go.opentelemetry.io/collector/pdata/pprofile v0.147.0 h1:yQS3RBvcvRcy9N7AnJvsxmse0AxJcRqBZfwMA22xBA8= +go.opentelemetry.io/collector/pdata/pprofile v0.147.0/go.mod h1:pm9mUqHNpT1SaCkxILu4FW1BvMAelh7EKhpSKe2KJIQ= +go.opentelemetry.io/collector/pdata/testdata v0.147.0 h1:fZB5jY5F+zC/oeGYBa92IknhPQIlLSwoxDUMzhrpTP4= +go.opentelemetry.io/collector/pdata/testdata v0.147.0/go.mod h1:+AB6qTXrYEBvqrv394SEXzuWxtL9LLrnVgIjYpP9HHU= +go.opentelemetry.io/collector/pipeline v1.53.0 h1:+RrNuAmHnzldGOzCCYLJv0qTFoi9QJGrLm+MEYMozmo= +go.opentelemetry.io/collector/pipeline v1.53.0/go.mod h1:RD90NG3Jbk965Xaqym3JyHkuol4uZJjQVUkD9ddXJIs= +go.opentelemetry.io/collector/receiver v1.53.0 h1:FACspX7EMj91g8OY3twlJKzw2LKj0g5wZAXT4Ys2XRU= +go.opentelemetry.io/collector/receiver v1.53.0/go.mod h1:rhBr1+X3N9ijDBBKrVCiRMfVTUlOSWj+Gj0A6qevmoA= +go.opentelemetry.io/collector/receiver/receivertest v0.147.0 h1:t+AqCUJT0ivO1eE09f8gIqnO73UeEFqjvL/annt6rWg= +go.opentelemetry.io/collector/receiver/receivertest v0.147.0/go.mod h1:8kZCwsG8KNpWRf+2izpoY8iIOyfC2cQ2CLSZc9LgOP0= +go.opentelemetry.io/collector/receiver/xreceiver v0.147.0 h1:/KAxTban2sQhiksAu/EG+ri0mNgSxldhJ4lj/XGT+xQ= +go.opentelemetry.io/collector/receiver/xreceiver v0.147.0/go.mod h1:DCjNMipiIv59Jc/YfWFxAvgonurJET9cw3D79U1yLMc= +go.opentelemetry.io/contrib/bridges/prometheus v0.65.0 h1:I/7S/yWobR3QHFLqHsJ8QOndoiFsj1VgHpQiq43KlUI= +go.opentelemetry.io/contrib/bridges/prometheus v0.65.0/go.mod h1:jPF6gn3y1E+nozCAEQj3c6NZ8KY+tvAgSVfvoOJUFac= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= +go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.opentelemetry.io/proto/slim/otlp v1.9.0 h1:fPVMv8tP3TrsqlkH1HWYUpbCY9cAIemx184VGkS6vlE= +go.opentelemetry.io/proto/slim/otlp v1.9.0/go.mod h1:xXdeJJ90Gqyll+orzUkY4bOd2HECo5JofeoLpymVqdI= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0 h1:o13nadWDNkH/quoDomDUClnQBpdQQ2Qqv0lQBjIXjE8= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0/go.mod h1:Gyb6Xe7FTi/6xBHwMmngGoHqL0w29Y4eW8TGFzpefGA= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0 h1:EiUYvtwu6PMrMHVjcPfnsG3v+ajPkbUeH+IL93+QYyk= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0/go.mod h1:mUUHKFiN2SST3AhJ8XhJxEoeVW12oqfXog0Bo8W3Ec4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= +golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/api v0.251.0 h1:6lea5nHRT8RUmpy9kkC2PJYnhnDAB13LqrLSVQlMIE8= +google.golang.org/api v0.251.0/go.mod h1:Rwy0lPf/TD7+T2VhYcffCHhyyInyuxGjICxdfLqT7KI= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= +google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY= +google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/otelcollector/lifecycle.go b/otelcollector/lifecycle.go new file mode 100644 index 0000000..6cc1fb3 --- /dev/null +++ b/otelcollector/lifecycle.go @@ -0,0 +1,199 @@ +package otelcollector + +import ( + "context" + "fmt" + "log/slog" + "slices" + "strings" + "time" + + prombridge "github.com/ArthurSens/prometheus-collector-bridge" + "github.com/PuerkitoBio/rehttp" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus-community/stackdriver_exporter/collectors" + "github.com/prometheus-community/stackdriver_exporter/delta" + "github.com/prometheus-community/stackdriver_exporter/utils" + "golang.org/x/oauth2/google" + "google.golang.org/api/compute/v1" + "google.golang.org/api/monitoring/v3" + "google.golang.org/api/option" +) + +type collectorFactoryFunc func(projectID string, service *monitoring.Service, opts collectors.MonitoringCollectorOptions, deltasTTL time.Duration, logger *slog.Logger) (prometheus.Collector, error) + +type lifecycleManager struct { + logger *slog.Logger + + monitoringServiceFactory func(ctx context.Context, parsed parsedConfig, cfg *Config) (*monitoring.Service, error) + collectorFactory collectorFactoryFunc + filterProjectDiscoverer func(ctx context.Context, filter string) ([]string, error) + defaultProjectDiscoverer func(ctx context.Context) (string, error) +} + +func newLifecycleManager(logger *slog.Logger) *lifecycleManager { + return &lifecycleManager{ + logger: logger, + monitoringServiceFactory: createMonitoringService, + collectorFactory: func(projectID string, service *monitoring.Service, opts collectors.MonitoringCollectorOptions, deltasTTL time.Duration, logger *slog.Logger) (prometheus.Collector, error) { + return collectors.NewMonitoringCollector( + projectID, + service, + opts, + logger, + delta.NewInMemoryCounterStore(logger, deltasTTL), + delta.NewInMemoryHistogramStore(logger, deltasTTL), + ) + }, + filterProjectDiscoverer: utils.GetProjectIDsFromFilter, + defaultProjectDiscoverer: discoverDefaultProjectID, + } +} + +func (m *lifecycleManager) Start(ctx context.Context, exporterConfig prombridge.Config) (*prometheus.Registry, error) { + cfg, ok := exporterConfig.(*Config) + if !ok { + return nil, fmt.Errorf("invalid exporter config type: %T", exporterConfig) + } + + parsed, err := cfg.parsedDurations() + if err != nil { + return nil, err + } + + projectIDs, err := m.resolveProjectIDs(ctx, cfg) + if err != nil { + return nil, err + } + + monitoringService, err := m.monitoringServiceFactory(ctx, parsed, cfg) + if err != nil { + return nil, err + } + + registry := prometheus.NewRegistry() + metricPrefixes := parseMetricTypePrefixes(cfg.MetricsPrefixes) + extraFilters := parseMetricExtraFilters(cfg.Filters) + + for _, projectID := range projectIDs { + opts := collectors.MonitoringCollectorOptions{ + MetricTypePrefixes: metricPrefixes, + ExtraFilters: extraFilters, + RequestInterval: parsed.MetricsInterval, + RequestOffset: parsed.MetricsOffset, + IngestDelay: cfg.MetricsIngest, + FillMissingLabels: cfg.FillMissing, + DropDelegatedProjects: cfg.DropDelegated, + AggregateDeltas: cfg.AggregateDeltas, + DescriptorCacheTTL: parsed.DescriptorTTL, + DescriptorCacheOnlyGoogle: cfg.DescriptorGoogleOnly, + } + + collector, err := m.collectorFactory(projectID, monitoringService, opts, parsed.DeltasTTL, m.logger) + if err != nil { + return nil, fmt.Errorf("failed to create collector for project %q: %w", projectID, err) + } + if err := registry.Register(collector); err != nil { + return nil, fmt.Errorf("failed to register collector for project %q: %w", projectID, err) + } + } + + return registry, nil +} + +func (m *lifecycleManager) Shutdown(context.Context) error { + return nil +} + +func (m *lifecycleManager) resolveProjectIDs(ctx context.Context, cfg *Config) ([]string, error) { + var projectIDs []string + + if cfg.ProjectsFilter != "" { + ids, err := m.filterProjectDiscoverer(ctx, cfg.ProjectsFilter) + if err != nil { + return nil, fmt.Errorf("failed to resolve project IDs from projects_filter: %w", err) + } + projectIDs = append(projectIDs, ids...) + } + + projectIDs = append(projectIDs, cfg.ProjectIDs...) + + if len(projectIDs) == 0 { + projectID, err := m.defaultProjectDiscoverer(ctx) + if err != nil { + return nil, fmt.Errorf("failed to discover default GCP project: %w", err) + } + projectIDs = append(projectIDs, projectID) + } + + slices.Sort(projectIDs) + return slices.Compact(projectIDs), nil +} + +func discoverDefaultProjectID(ctx context.Context) (string, error) { + credentials, err := google.FindDefaultCredentials(ctx, compute.ComputeScope) + if err != nil { + return "", err + } + if credentials.ProjectID == "" { + return "", fmt.Errorf("unable to identify default GCP project") + } + return credentials.ProjectID, nil +} + +func createMonitoringService(ctx context.Context, parsed parsedConfig, cfg *Config) (*monitoring.Service, error) { + googleClient, err := google.DefaultClient(ctx, monitoring.MonitoringReadScope) + if err != nil { + return nil, fmt.Errorf("error creating Google client: %w", err) + } + + googleClient.Timeout = parsed.HTTPTimeout + googleClient.Transport = rehttp.NewTransport( + googleClient.Transport, + rehttp.RetryAll( + rehttp.RetryMaxRetries(cfg.MaxRetries), + rehttp.RetryStatuses(cfg.RetryStatuses...), + ), + rehttp.ExpJitterDelay(parsed.BackoffJitter, parsed.MaxBackoff), + ) + + service, err := monitoring.NewService(ctx, option.WithHTTPClient(googleClient), option.WithUniverseDomain(cfg.UniverseDomain)) + if err != nil { + return nil, fmt.Errorf("error creating Google Stackdriver Monitoring service: %w", err) + } + return service, nil +} + +func parseMetricTypePrefixes(input []string) []string { + in := append([]string(nil), input...) + slices.Sort(in) + unique := slices.Compact(in) + out := make([]string, 0, len(unique)) + + for i, prefix := range unique { + if i > 0 && len(out) > 0 { + prev := out[len(out)-1] + if strings.HasPrefix(prefix, prev) { + continue + } + } + out = append(out, prefix) + } + return out +} + +func parseMetricExtraFilters(raw []string) []collectors.MetricFilter { + out := make([]collectors.MetricFilter, 0, len(raw)) + for _, entry := range raw { + prefix, filter := utils.SplitExtraFilter(entry, ":") + if prefix == "" { + continue + } + out = append(out, collectors.MetricFilter{ + TargetedMetricPrefix: strings.ToLower(prefix), + FilterQuery: filter, + }) + } + return out +} + diff --git a/otelcollector/lifecycle_test.go b/otelcollector/lifecycle_test.go new file mode 100644 index 0000000..87c6190 --- /dev/null +++ b/otelcollector/lifecycle_test.go @@ -0,0 +1,131 @@ +package otelcollector + +import ( + "context" + "errors" + "log/slog" + "strings" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus-community/stackdriver_exporter/collectors" + "google.golang.org/api/monitoring/v3" +) + +func TestLifecycleManager_Start(t *testing.T) { + t.Parallel() + + cfg := &Config{ + ProjectIDs: []string{"project-a", "project-b"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + } + + var createdProjects []string + mgr := newLifecycleManager(slog.Default()) + mgr.monitoringServiceFactory = func(context.Context, parsedConfig, *Config) (*monitoring.Service, error) { + return &monitoring.Service{}, nil + } + mgr.collectorFactory = func(projectID string, _ *monitoring.Service, _ collectors.MonitoringCollectorOptions, _ time.Duration, _ *slog.Logger) (prometheus.Collector, error) { + createdProjects = append(createdProjects, projectID) + return prometheus.NewGauge(prometheus.GaugeOpts{ + Name: "test_metric_" + strings.ReplaceAll(projectID, "-", "_"), + Help: "test", + }), nil + } + + reg, err := mgr.Start(context.Background(), cfg) + if err != nil { + t.Fatalf("Start() error = %v", err) + } + if reg == nil { + t.Fatal("Start() returned nil registry") + } + if len(createdProjects) != 2 { + t.Fatalf("collectorFactory called %d times, want 2", len(createdProjects)) + } + + // Ensure the created registry can gather metrics. + if _, err := reg.Gather(); err != nil { + t.Fatalf("registry.Gather() error = %v", err) + } +} + +func TestLifecycleManager_Start_UsesDefaultProjectDiscovery(t *testing.T) { + t.Parallel() + + cfg := &Config{ + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + } + + mgr := newLifecycleManager(slog.Default()) + mgr.defaultProjectDiscoverer = func(context.Context) (string, error) { + return "auto-project", nil + } + mgr.monitoringServiceFactory = func(context.Context, parsedConfig, *Config) (*monitoring.Service, error) { + return &monitoring.Service{}, nil + } + mgr.collectorFactory = func(projectID string, _ *monitoring.Service, _ collectors.MonitoringCollectorOptions, _ time.Duration, _ *slog.Logger) (prometheus.Collector, error) { + if projectID != "auto-project" { + t.Fatalf("projectID = %q, want %q", projectID, "auto-project") + } + return prometheus.NewGauge(prometheus.GaugeOpts{Name: "auto_project_metric", Help: "test"}), nil + } + + if _, err := mgr.Start(context.Background(), cfg); err != nil { + t.Fatalf("Start() error = %v", err) + } +} + +func TestLifecycleManager_Start_ReturnsErrorFromCollectorFactory(t *testing.T) { + t.Parallel() + + cfg := &Config{ + ProjectIDs: []string{"project-a"}, + MetricsPrefixes: []string{"compute.googleapis.com/instance"}, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + MetricsInterval: "5m", + MetricsOffset: "0s", + DeltasTTL: "30m", + DescriptorTTL: "0s", + } + + mgr := newLifecycleManager(slog.Default()) + mgr.monitoringServiceFactory = func(context.Context, parsedConfig, *Config) (*monitoring.Service, error) { + return &monitoring.Service{}, nil + } + mgr.collectorFactory = func(string, *monitoring.Service, collectors.MonitoringCollectorOptions, time.Duration, *slog.Logger) (prometheus.Collector, error) { + return nil, errors.New("boom") + } + + if _, err := mgr.Start(context.Background(), cfg); err == nil { + t.Fatal("Start() expected error, got nil") + } +} + +func TestLifecycleManager_Shutdown(t *testing.T) { + t.Parallel() + + mgr := newLifecycleManager(slog.Default()) + if err := mgr.Shutdown(context.Background()); err != nil { + t.Fatalf("Shutdown() error = %v", err) + } +} + + From 56ffa00e0368dbc2ed4a1090b3558dc7fe63419a Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Wed, 4 Mar 2026 15:37:41 -0300 Subject: [PATCH 2/3] lint Signed-off-by: Arthur Silva Sens --- otelcollector/config.go | 67 ++++++++++++++++----------------- otelcollector/config_test.go | 1 - otelcollector/factory.go | 1 - otelcollector/factory_test.go | 1 - otelcollector/lifecycle.go | 5 +-- otelcollector/lifecycle_test.go | 4 +- 6 files changed, 36 insertions(+), 43 deletions(-) diff --git a/otelcollector/config.go b/otelcollector/config.go index 8f6a34a..a0646e9 100644 --- a/otelcollector/config.go +++ b/otelcollector/config.go @@ -10,45 +10,45 @@ import ( // Config maps stackdriver_exporter runtime settings into exporter_config. type Config struct { - ProjectIDs []string `mapstructure:"project_ids"` - ProjectsFilter string `mapstructure:"projects_filter"` - UniverseDomain string `mapstructure:"universe_domain"` - MaxRetries int `mapstructure:"max_retries"` - HTTPTimeout string `mapstructure:"http_timeout"` - MaxBackoff string `mapstructure:"max_backoff"` - BackoffJitter string `mapstructure:"backoff_jitter"` - RetryStatuses []int `mapstructure:"retry_statuses"` - MetricsPrefixes []string `mapstructure:"metrics_prefixes"` - MetricsInterval string `mapstructure:"metrics_interval"` - MetricsOffset string `mapstructure:"metrics_offset"` - MetricsIngest bool `mapstructure:"metrics_ingest_delay"` - FillMissing bool `mapstructure:"fill_missing_labels"` - DropDelegated bool `mapstructure:"drop_delegated_projects"` - Filters []string `mapstructure:"filters"` - AggregateDeltas bool `mapstructure:"aggregate_deltas"` - DeltasTTL string `mapstructure:"aggregate_deltas_ttl"` - DescriptorTTL string `mapstructure:"descriptor_cache_ttl"` - DescriptorGoogleOnly bool `mapstructure:"descriptor_cache_only_google"` + ProjectIDs []string `mapstructure:"project_ids"` + ProjectsFilter string `mapstructure:"projects_filter"` + UniverseDomain string `mapstructure:"universe_domain"` + MaxRetries int `mapstructure:"max_retries"` + HTTPTimeout string `mapstructure:"http_timeout"` + MaxBackoff string `mapstructure:"max_backoff"` + BackoffJitter string `mapstructure:"backoff_jitter"` + RetryStatuses []int `mapstructure:"retry_statuses"` + MetricsPrefixes []string `mapstructure:"metrics_prefixes"` + MetricsInterval string `mapstructure:"metrics_interval"` + MetricsOffset string `mapstructure:"metrics_offset"` + MetricsIngest bool `mapstructure:"metrics_ingest_delay"` + FillMissing bool `mapstructure:"fill_missing_labels"` + DropDelegated bool `mapstructure:"drop_delegated_projects"` + Filters []string `mapstructure:"filters"` + AggregateDeltas bool `mapstructure:"aggregate_deltas"` + DeltasTTL string `mapstructure:"aggregate_deltas_ttl"` + DescriptorTTL string `mapstructure:"descriptor_cache_ttl"` + DescriptorGoogleOnly bool `mapstructure:"descriptor_cache_only_google"` } var _ prombridge.Config = (*Config)(nil) func defaultConfig() *Config { return &Config{ - UniverseDomain: "googleapis.com", - MaxRetries: 0, - HTTPTimeout: "10s", - MaxBackoff: "5s", - BackoffJitter: "1s", - RetryStatuses: []int{503}, - MetricsInterval: "5m", - MetricsOffset: "0s", - MetricsIngest: false, - FillMissing: true, - DropDelegated: false, - AggregateDeltas: false, - DeltasTTL: "30m", - DescriptorTTL: "0s", + UniverseDomain: "googleapis.com", + MaxRetries: 0, + HTTPTimeout: "10s", + MaxBackoff: "5s", + BackoffJitter: "1s", + RetryStatuses: []int{503}, + MetricsInterval: "5m", + MetricsOffset: "0s", + MetricsIngest: false, + FillMissing: true, + DropDelegated: false, + AggregateDeltas: false, + DeltasTTL: "30m", + DescriptorTTL: "0s", DescriptorGoogleOnly: true, } } @@ -157,4 +157,3 @@ type configUnmarshaler struct{} func (configUnmarshaler) GetConfigStruct() prombridge.Config { return defaultConfig() } - diff --git a/otelcollector/config_test.go b/otelcollector/config_test.go index 61bcb0a..e3f2129 100644 --- a/otelcollector/config_test.go +++ b/otelcollector/config_test.go @@ -151,4 +151,3 @@ func TestConfig_Durations(t *testing.T) { t.Fatalf("DescriptorTTL = %v, want %v", parsed.DescriptorTTL, 0*time.Second) } } - diff --git a/otelcollector/factory.go b/otelcollector/factory.go index 48a3573..784155c 100644 --- a/otelcollector/factory.go +++ b/otelcollector/factory.go @@ -24,4 +24,3 @@ var ( _ prombridge.ExporterLifecycleManager = (*lifecycleManager)(nil) _ prombridge.ConfigUnmarshaler = (configUnmarshaler{}) ) - diff --git a/otelcollector/factory_test.go b/otelcollector/factory_test.go index 1923374..631db13 100644 --- a/otelcollector/factory_test.go +++ b/otelcollector/factory_test.go @@ -40,4 +40,3 @@ func TestFactory_CreateMetrics(t *testing.T) { t.Fatal("CreateMetrics() returned nil receiver") } } - diff --git a/otelcollector/lifecycle.go b/otelcollector/lifecycle.go index 6cc1fb3..587e721 100644 --- a/otelcollector/lifecycle.go +++ b/otelcollector/lifecycle.go @@ -10,10 +10,10 @@ import ( prombridge "github.com/ArthurSens/prometheus-collector-bridge" "github.com/PuerkitoBio/rehttp" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus-community/stackdriver_exporter/collectors" "github.com/prometheus-community/stackdriver_exporter/delta" "github.com/prometheus-community/stackdriver_exporter/utils" + "github.com/prometheus/client_golang/prometheus" "golang.org/x/oauth2/google" "google.golang.org/api/compute/v1" "google.golang.org/api/monitoring/v3" @@ -33,7 +33,7 @@ type lifecycleManager struct { func newLifecycleManager(logger *slog.Logger) *lifecycleManager { return &lifecycleManager{ - logger: logger, + logger: logger, monitoringServiceFactory: createMonitoringService, collectorFactory: func(projectID string, service *monitoring.Service, opts collectors.MonitoringCollectorOptions, deltasTTL time.Duration, logger *slog.Logger) (prometheus.Collector, error) { return collectors.NewMonitoringCollector( @@ -196,4 +196,3 @@ func parseMetricExtraFilters(raw []string) []collectors.MetricFilter { } return out } - diff --git a/otelcollector/lifecycle_test.go b/otelcollector/lifecycle_test.go index 87c6190..28c0f8f 100644 --- a/otelcollector/lifecycle_test.go +++ b/otelcollector/lifecycle_test.go @@ -8,8 +8,8 @@ import ( "testing" "time" - "github.com/prometheus/client_golang/prometheus" "github.com/prometheus-community/stackdriver_exporter/collectors" + "github.com/prometheus/client_golang/prometheus" "google.golang.org/api/monitoring/v3" ) @@ -127,5 +127,3 @@ func TestLifecycleManager_Shutdown(t *testing.T) { t.Fatalf("Shutdown() error = %v", err) } } - - From e62b3b0e88f77183ddb185f0b85be5fd985c8e89 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Wed, 4 Mar 2026 15:47:01 -0300 Subject: [PATCH 3/3] Add license headers to otelcollector files Signed-off-by: Arthur Silva Sens Made-with: Cursor --- otelcollector/config.go | 13 +++++++++++++ otelcollector/config_test.go | 13 +++++++++++++ otelcollector/factory.go | 13 +++++++++++++ otelcollector/factory_test.go | 13 +++++++++++++ otelcollector/lifecycle.go | 13 +++++++++++++ otelcollector/lifecycle_test.go | 13 +++++++++++++ 6 files changed, 78 insertions(+) diff --git a/otelcollector/config.go b/otelcollector/config.go index a0646e9..ff812a5 100644 --- a/otelcollector/config.go +++ b/otelcollector/config.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import ( diff --git a/otelcollector/config_test.go b/otelcollector/config_test.go index e3f2129..403c0ab 100644 --- a/otelcollector/config_test.go +++ b/otelcollector/config_test.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import ( diff --git a/otelcollector/factory.go b/otelcollector/factory.go index 784155c..fac8c9d 100644 --- a/otelcollector/factory.go +++ b/otelcollector/factory.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import ( diff --git a/otelcollector/factory_test.go b/otelcollector/factory_test.go index 631db13..1f700d4 100644 --- a/otelcollector/factory_test.go +++ b/otelcollector/factory_test.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import ( diff --git a/otelcollector/lifecycle.go b/otelcollector/lifecycle.go index 587e721..6751f98 100644 --- a/otelcollector/lifecycle.go +++ b/otelcollector/lifecycle.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import ( diff --git a/otelcollector/lifecycle_test.go b/otelcollector/lifecycle_test.go index 28c0f8f..89729d6 100644 --- a/otelcollector/lifecycle_test.go +++ b/otelcollector/lifecycle_test.go @@ -1,3 +1,16 @@ +// Copyright 2020 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package otelcollector import (