|
| 1 | +// Copyright The Prometheus Authors |
| 2 | +// Licensed under the Apache License, Version 2.0 (the "License"); |
| 3 | +// you may not use this file except in compliance with the License. |
| 4 | +// You may obtain a copy of the License at |
| 5 | +// |
| 6 | +// http://www.apache.org/licenses/LICENSE-2.0 |
| 7 | +// |
| 8 | +// Unless required by applicable law or agreed to in writing, software |
| 9 | +// distributed under the License is distributed on an "AS IS" BASIS, |
| 10 | +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 11 | +// See the License for the specific language governing permissions and |
| 12 | +// limitations under the License. |
| 13 | + |
| 14 | +package config |
| 15 | + |
| 16 | +import ( |
| 17 | + "context" |
| 18 | + "fmt" |
| 19 | + "net/http" |
| 20 | + "slices" |
| 21 | + "time" |
| 22 | + |
| 23 | + "github.com/PuerkitoBio/rehttp" |
| 24 | + "github.com/prometheus-community/stackdriver_exporter/collectors" |
| 25 | + "github.com/prometheus-community/stackdriver_exporter/utils" |
| 26 | + "golang.org/x/oauth2/google" |
| 27 | + "google.golang.org/api/compute/v1" |
| 28 | + "google.golang.org/api/monitoring/v3" |
| 29 | + "google.golang.org/api/option" |
| 30 | +) |
| 31 | + |
| 32 | +type Option struct { |
| 33 | + CLIFlag string |
| 34 | + OTelKey string |
| 35 | + Default any |
| 36 | +} |
| 37 | + |
| 38 | +const ( |
| 39 | + DefaultUniverseDomain = "googleapis.com" |
| 40 | + DefaultMaxRetries = 0 |
| 41 | + DefaultHTTPTimeout = "10s" |
| 42 | + DefaultMaxBackoff = "5s" |
| 43 | + DefaultBackoffJitter = "1s" |
| 44 | + DefaultMetricsInterval = "5m" |
| 45 | + DefaultMetricsOffset = "0s" |
| 46 | + DefaultMetricsIngest = false |
| 47 | + DefaultFillMissing = true |
| 48 | + DefaultDropDelegated = false |
| 49 | + DefaultAggregateDeltas = false |
| 50 | + DefaultDeltasTTL = "30m" |
| 51 | + DefaultDescriptorTTL = "0s" |
| 52 | + DefaultDescriptorGoogleOnly = true |
| 53 | +) |
| 54 | + |
| 55 | +// DefaultRetryStatuses must be treated as immutable after declaration. |
| 56 | +var DefaultRetryStatuses = []int{http.StatusServiceUnavailable} |
| 57 | + |
| 58 | +var ( |
| 59 | + ProjectIDs = Option{CLIFlag: "google.project-ids", OTelKey: "project_ids"} |
| 60 | + ProjectsFilter = Option{CLIFlag: "google.projects.filter", OTelKey: "projects_filter"} |
| 61 | + UniverseDomain = Option{CLIFlag: "google.universe-domain", OTelKey: "universe_domain", Default: DefaultUniverseDomain} |
| 62 | + MaxRetries = Option{CLIFlag: "stackdriver.max-retries", OTelKey: "max_retries", Default: DefaultMaxRetries} |
| 63 | + HTTPTimeout = Option{CLIFlag: "stackdriver.http-timeout", OTelKey: "http_timeout", Default: DefaultHTTPTimeout} |
| 64 | + MaxBackoff = Option{CLIFlag: "stackdriver.max-backoff", OTelKey: "max_backoff", Default: DefaultMaxBackoff} |
| 65 | + BackoffJitter = Option{CLIFlag: "stackdriver.backoff-jitter", OTelKey: "backoff_jitter", Default: DefaultBackoffJitter} |
| 66 | + RetryStatuses = Option{CLIFlag: "stackdriver.retry-statuses", OTelKey: "retry_statuses", Default: DefaultRetryStatuses} |
| 67 | + MetricsPrefixes = Option{CLIFlag: "monitoring.metrics-prefixes", OTelKey: "metrics_prefixes"} |
| 68 | + MetricsInterval = Option{CLIFlag: "monitoring.metrics-interval", OTelKey: "metrics_interval", Default: DefaultMetricsInterval} |
| 69 | + MetricsOffset = Option{CLIFlag: "monitoring.metrics-offset", OTelKey: "metrics_offset", Default: DefaultMetricsOffset} |
| 70 | + MetricsIngest = Option{CLIFlag: "monitoring.metrics-ingest-delay", OTelKey: "metrics_ingest_delay", Default: DefaultMetricsIngest} |
| 71 | + FillMissing = Option{CLIFlag: "collector.fill-missing-labels", OTelKey: "fill_missing_labels", Default: DefaultFillMissing} |
| 72 | + DropDelegated = Option{CLIFlag: "monitoring.drop-delegated-projects", OTelKey: "drop_delegated_projects", Default: DefaultDropDelegated} |
| 73 | + Filters = Option{CLIFlag: "monitoring.filters", OTelKey: "filters"} |
| 74 | + AggregateDeltas = Option{CLIFlag: "monitoring.aggregate-deltas", OTelKey: "aggregate_deltas", Default: DefaultAggregateDeltas} |
| 75 | + DeltasTTL = Option{CLIFlag: "monitoring.aggregate-deltas-ttl", OTelKey: "aggregate_deltas_ttl", Default: DefaultDeltasTTL} |
| 76 | + DescriptorTTL = Option{CLIFlag: "monitoring.descriptor-cache-ttl", OTelKey: "descriptor_cache_ttl", Default: DefaultDescriptorTTL} |
| 77 | + DescriptorGoogleOnly = Option{CLIFlag: "monitoring.descriptor-cache-only-google", OTelKey: "descriptor_cache_only_google", Default: DefaultDescriptorGoogleOnly} |
| 78 | + |
| 79 | + AllOptions = []Option{ |
| 80 | + ProjectIDs, |
| 81 | + ProjectsFilter, |
| 82 | + UniverseDomain, |
| 83 | + MaxRetries, |
| 84 | + HTTPTimeout, |
| 85 | + MaxBackoff, |
| 86 | + BackoffJitter, |
| 87 | + RetryStatuses, |
| 88 | + MetricsPrefixes, |
| 89 | + MetricsInterval, |
| 90 | + MetricsOffset, |
| 91 | + MetricsIngest, |
| 92 | + FillMissing, |
| 93 | + DropDelegated, |
| 94 | + Filters, |
| 95 | + AggregateDeltas, |
| 96 | + DeltasTTL, |
| 97 | + DescriptorTTL, |
| 98 | + DescriptorGoogleOnly, |
| 99 | + } |
| 100 | +) |
| 101 | + |
| 102 | +type RuntimeConfig struct { |
| 103 | + ProjectIDs []string |
| 104 | + ProjectsFilter string |
| 105 | + UniverseDomain string |
| 106 | + MaxRetries int |
| 107 | + HTTPTimeout time.Duration |
| 108 | + MaxBackoff time.Duration |
| 109 | + BackoffJitter time.Duration |
| 110 | + RetryStatuses []int |
| 111 | + MetricsPrefixes []string |
| 112 | + MetricsInterval time.Duration |
| 113 | + MetricsOffset time.Duration |
| 114 | + MetricsIngest bool |
| 115 | + FillMissing bool |
| 116 | + DropDelegated bool |
| 117 | + Filters []string |
| 118 | + AggregateDeltas bool |
| 119 | + DeltasTTL time.Duration |
| 120 | + DescriptorTTL time.Duration |
| 121 | + DescriptorGoogleOnly bool |
| 122 | +} |
| 123 | + |
| 124 | +func OTelComponentDefaults() map[string]interface{} { |
| 125 | + defaults := make(map[string]interface{}, len(AllOptions)) |
| 126 | + for _, option := range AllOptions { |
| 127 | + if option.Default == nil { |
| 128 | + continue |
| 129 | + } |
| 130 | + // Option defaults are shared values and must not be mutated by callers. |
| 131 | + defaults[option.OTelKey] = option.Default |
| 132 | + } |
| 133 | + return defaults |
| 134 | +} |
| 135 | + |
| 136 | +func ParseDuration(name, raw string) (time.Duration, error) { |
| 137 | + duration, err := time.ParseDuration(raw) |
| 138 | + if err != nil { |
| 139 | + return 0, fmt.Errorf("%s: invalid duration %q: %w", name, raw, err) |
| 140 | + } |
| 141 | + return duration, nil |
| 142 | +} |
| 143 | + |
| 144 | +func ValidateRetryStatuses(codes []int) error { |
| 145 | + for _, code := range codes { |
| 146 | + if code < http.StatusContinue || code > 599 { |
| 147 | + return fmt.Errorf("retry status %d is not a valid HTTP status code", code) |
| 148 | + } |
| 149 | + } |
| 150 | + return nil |
| 151 | +} |
| 152 | + |
| 153 | +func ParseMetricPrefixes(prefixes []string) []string { |
| 154 | + return utils.ParseMetricTypePrefixes(prefixes) |
| 155 | +} |
| 156 | + |
| 157 | +func ParseMetricFilters(filters []string) []collectors.MetricFilter { |
| 158 | + return collectors.ParseMetricExtraFilters(filters) |
| 159 | +} |
| 160 | + |
| 161 | +func DeduplicateProjectIDs(projectIDs []string) []string { |
| 162 | + normalized := slices.Clone(projectIDs) |
| 163 | + slices.Sort(normalized) |
| 164 | + return slices.Compact(normalized) |
| 165 | +} |
| 166 | + |
| 167 | +func (c RuntimeConfig) MonitoringCollectorOptions() collectors.MonitoringCollectorOptions { |
| 168 | + return c.MonitoringCollectorOptionsForPrefixes(ParseMetricPrefixes(c.MetricsPrefixes)) |
| 169 | +} |
| 170 | + |
| 171 | +func (c RuntimeConfig) MonitoringCollectorOptionsForPrefixes(metricPrefixes []string) collectors.MonitoringCollectorOptions { |
| 172 | + return collectors.MonitoringCollectorOptions{ |
| 173 | + MetricTypePrefixes: metricPrefixes, |
| 174 | + ExtraFilters: ParseMetricFilters(c.Filters), |
| 175 | + RequestInterval: c.MetricsInterval, |
| 176 | + RequestOffset: c.MetricsOffset, |
| 177 | + IngestDelay: c.MetricsIngest, |
| 178 | + FillMissingLabels: c.FillMissing, |
| 179 | + DropDelegatedProjects: c.DropDelegated, |
| 180 | + AggregateDeltas: c.AggregateDeltas, |
| 181 | + DescriptorCacheTTL: c.DescriptorTTL, |
| 182 | + DescriptorCacheOnlyGoogle: c.DescriptorGoogleOnly, |
| 183 | + } |
| 184 | +} |
| 185 | + |
| 186 | +func (c RuntimeConfig) CollectorCacheTTL() time.Duration { |
| 187 | + if c.AggregateDeltas || c.DescriptorTTL > 0 { |
| 188 | + ttl := c.DeltasTTL |
| 189 | + if c.DescriptorTTL > ttl { |
| 190 | + ttl = c.DescriptorTTL |
| 191 | + } |
| 192 | + return ttl |
| 193 | + } |
| 194 | + |
| 195 | + return 2 * time.Hour |
| 196 | +} |
| 197 | + |
| 198 | +func DiscoverDefaultProjectID(ctx context.Context) (string, error) { |
| 199 | + credentials, err := google.FindDefaultCredentials(ctx, compute.ComputeScope) |
| 200 | + if err != nil { |
| 201 | + return "", err |
| 202 | + } |
| 203 | + if credentials.ProjectID == "" { |
| 204 | + return "", fmt.Errorf("unable to identify default GCP project") |
| 205 | + } |
| 206 | + return credentials.ProjectID, nil |
| 207 | +} |
| 208 | + |
| 209 | +func (c RuntimeConfig) CreateMonitoringService(ctx context.Context) (*monitoring.Service, error) { |
| 210 | + googleClient, err := google.DefaultClient(ctx, monitoring.MonitoringReadScope) |
| 211 | + if err != nil { |
| 212 | + return nil, fmt.Errorf("error creating Google client: %w", err) |
| 213 | + } |
| 214 | + |
| 215 | + googleClient.Timeout = c.HTTPTimeout |
| 216 | + googleClient.Transport = rehttp.NewTransport( |
| 217 | + googleClient.Transport, |
| 218 | + rehttp.RetryAll( |
| 219 | + rehttp.RetryMaxRetries(c.MaxRetries), |
| 220 | + rehttp.RetryStatuses(c.RetryStatuses...), |
| 221 | + ), |
| 222 | + rehttp.ExpJitterDelay(c.BackoffJitter, c.MaxBackoff), |
| 223 | + ) |
| 224 | + |
| 225 | + service, err := monitoring.NewService(ctx, option.WithHTTPClient(googleClient), option.WithUniverseDomain(c.UniverseDomain)) |
| 226 | + if err != nil { |
| 227 | + return nil, fmt.Errorf("error creating Google Stackdriver Monitoring service: %w", err) |
| 228 | + } |
| 229 | + return service, nil |
| 230 | +} |
0 commit comments