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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 11 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.24.0

require (
github.com/aws/aws-sdk-go v1.55.7
github.com/aws/aws-sdk-go-v2 v1.41.1
github.com/aws/aws-sdk-go-v2 v1.41.3
github.com/aws/aws-sdk-go-v2/config v1.32.7
github.com/aws/aws-sdk-go-v2/credentials v1.19.7
github.com/aws/aws-sdk-go-v2/service/amp v1.42.5
Expand All @@ -20,6 +20,8 @@ require (
github.com/aws/aws-sdk-go-v2/service/lambda v1.87.1
github.com/aws/aws-sdk-go-v2/service/rds v1.114.0
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.31.6
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0
github.com/aws/aws-sdk-go-v2/service/servicequotas v1.34.3
github.com/aws/aws-sdk-go-v2/service/shield v1.34.17
github.com/aws/aws-sdk-go-v2/service/storagegateway v1.43.10
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6
Expand All @@ -40,14 +42,17 @@ require (
require (
github.com/alecthomas/kingpin/v2 v2.4.0 // indirect
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.20 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 // indirect
Expand Down
34 changes: 22 additions & 12 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@ github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vS
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE=
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4=
github.com/aws/aws-sdk-go-v2 v1.41.3 h1:4kQ/fa22KjDt13QCy1+bYADvdgcxpfH18f0zP542kZA=
github.com/aws/aws-sdk-go-v2 v1.41.3/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6 h1:N4lRUXZpZ1KVEUn6hxtco/1d2lgYhNn1fHkkl8WhlyQ=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.6/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
github.com/aws/aws-sdk-go-v2/config v1.32.7 h1:vxUyWGUwmkQ2g19n7JY/9YL8MfAIl7bTesIUykECXmY=
github.com/aws/aws-sdk-go-v2/config v1.32.7/go.mod h1:2/Qm5vKUU/r7Y+zUk/Ptt2MDAEKAfUtKc1+3U1Mo3oY=
github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8=
github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 h1:/sECfyq2JTifMI2JPyZ4bdRN77zJmr6SrS1eL3augIA=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19/go.mod h1:dMf8A5oAqr9/oxOfLkC/c2LU/uMcALP0Rgn2BD5LWn0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 h1:AWeJMk33GTBf6J20XJe6qZoRSJo0WfUhsMdUKhoODXE=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19/go.mod h1:+GWrYoaAsV7/4pNHpwh1kiNLXkKaSoppxQq9lbH8Ejw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.20 h1:qi3e/dmpdONhj1RyIZdi6DKKpDXS5Lb8ftr3p7cyHJc=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.20/go.mod h1:V1K+TeJVD5JOk3D9e5tsX2KUdL7BlB+FV6cBhdobN8c=
github.com/aws/aws-sdk-go-v2/service/amp v1.42.5 h1:Pd07a2Tdhl3591h+hbJZCC+50NGraSyt/I6yLx4FDak=
github.com/aws/aws-sdk-go-v2/service/amp v1.42.5/go.mod h1:6q5j2wH8o1tf4glByj2hBDIEiOAKDh0x5QpjLKmIi40=
github.com/aws/aws-sdk-go-v2/service/apigateway v1.38.4 h1:V8gcFwJPP3eXZXpeui+p97JmO7WtCkQlEAHrE6Kyt0k=
Expand All @@ -40,18 +42,26 @@ github.com/aws/aws-sdk-go-v2/service/elasticache v1.51.9 h1:hTgZLyNoDWphZUtTtcvQ
github.com/aws/aws-sdk-go-v2/service/elasticache v1.51.9/go.mod h1:91RkIYy9ubykxB50XGYDsbljLZnrZ6rp/Urt4rZrbwQ=
github.com/aws/aws-sdk-go-v2/service/iam v1.53.2 h1:62G6btFUwAa5uR5iPlnlNVAM0zJSLbWgDfKOfUC7oW4=
github.com/aws/aws-sdk-go-v2/service/iam v1.53.2/go.mod h1:av9clChrbZbJ5E21msSsiT2oghl2BJHfQGhCkXmhyu8=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 h1:XAq62tBTJP/85lFD5oqOOe7YYgWxY9LvWq8plyDvDVg=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11 h1:BYf7XNsJMzl4mObARUBUib+j2tf0U//JAAtTnYqvqCw=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.11/go.mod h1:aEUS4WrNk/+FxkBZZa7tVgp4pGH+kFGW40Y8rCPqt5g=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.17 h1:Nhx/OYX+ukejm9t/MkWI8sucnsiroNYNGb5ddI9ungQ=
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.17/go.mod h1:AjmK8JWnlAevq1b1NBtv5oQVG4iqnYXUufdgol+q9wg=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 h1:X1Tow7suZk9UCJHE1Iw9GMZJJl0dAnKXXP1NaSDHwmw=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19/go.mod h1:/rARO8psX+4sfjUQXp5LLifjUt8DuATZ31WptNJTyQA=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19 h1:JnQeStZvPHFHeyky/7LbMlyQjUa+jIBj36OlWm0pzIk=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.19/go.mod h1:HGyasyHvYdFQeJhvDHfH7HXkHh57htcJGKDZ+7z+I24=
github.com/aws/aws-sdk-go-v2/service/lambda v1.87.1 h1:QBdmTXWwqVgx0PueT/Xgp2+al5HR0gAV743pTzYeBRw=
github.com/aws/aws-sdk-go-v2/service/lambda v1.87.1/go.mod h1:ogjbkxFgFOjG3dYFQ8irC92gQfpfMDcy1RDKNSZWXNU=
github.com/aws/aws-sdk-go-v2/service/rds v1.114.0 h1:p9c6HDzx6sTf7uyc9xsQd693uzArsPrsVr9n0oRk7DU=
github.com/aws/aws-sdk-go-v2/service/rds v1.114.0/go.mod h1:JBRYWpz5oXQtHgQC+X8LX9lh0FBCwRHJlWEIT+TTLaE=
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.31.6 h1:gd7YMnFZQGdy4lERF9ffz9kbc6K/IPhCu5CrJDJr8XY=
github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.31.6/go.mod h1:lnTv81am9e2C2SjX3VKyUrKEzDADD9lKST9ou96UBoY=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0 h1:zyKY4OxzUImu+DigelJI9o49QQv8CjREs5E1CywjtIA=
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.0/go.mod h1:NF3JcMGOiARAss1ld3WGORCw71+4ExDD2cbbdKS5PpA=
github.com/aws/aws-sdk-go-v2/service/servicequotas v1.34.3 h1:sImEaE1KtqSb+y7SZh71y4O0mIgXndXEYDxqGQ5TEyI=
github.com/aws/aws-sdk-go-v2/service/servicequotas v1.34.3/go.mod h1:zOPufbmfmRDQw0ewok+EoPkLSBjFdMnkDtNocC723Ns=
github.com/aws/aws-sdk-go-v2/service/shield v1.34.17 h1:XOqXVwczmfk6/GtGW7eee1RvCp7NhPKn8wYbZp+yTa8=
github.com/aws/aws-sdk-go-v2/service/shield v1.34.17/go.mod h1:eQV3cCW6J6J+cpBitDt/tDvVTmBFTdlZdEGNKsB76O8=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y=
Expand Down
13 changes: 11 additions & 2 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"gopkg.in/yaml.v2"

"github.com/prometheus-community/yet-another-cloudwatch-exporter/pkg/internal/enhancedmetrics"
"github.com/prometheus-community/yet-another-cloudwatch-exporter/pkg/internal/quotametrics"
"github.com/prometheus-community/yet-another-cloudwatch-exporter/pkg/model"
)

Expand Down Expand Up @@ -68,6 +69,7 @@ type Job struct {
RecentlyActiveOnly bool `yaml:"recentlyActiveOnly"`
IncludeContextOnInfoMetrics bool `yaml:"includeContextOnInfoMetrics"`
EnhancedMetrics []*EnhancedMetric `yaml:"enhancedMetrics"`
QuotaMetrics bool `yaml:"quotaMetrics"`
JobLevelMetricFields `yaml:",inline"`
}

Expand Down Expand Up @@ -244,8 +246,8 @@ func (j *Job) validateDiscoveryJob(logger *slog.Logger, jobIdx int) error {
if len(j.Regions) == 0 {
return fmt.Errorf("Discovery job [%s/%d]: Regions should not be empty", j.Type, jobIdx)
}
if len(j.Metrics) == 0 && len(j.EnhancedMetrics) == 0 {
return fmt.Errorf("Discovery job [%s/%d]: Metrics and EnhancedMetrics should not both be empty", j.Type, jobIdx)
if len(j.Metrics) == 0 && len(j.EnhancedMetrics) == 0 && !j.QuotaMetrics {
return fmt.Errorf("Discovery job [%s/%d]: Metrics, EnhancedMetrics, and QuotaMetrics should not all be empty", j.Type, jobIdx)
}
for metricIdx, metric := range j.Metrics {
err := metric.validateMetric(logger, metricIdx, parent, &j.JobLevelMetricFields)
Expand Down Expand Up @@ -277,6 +279,12 @@ func (j *Job) validateDiscoveryJob(logger *slog.Logger, jobIdx int) error {
}
}

if j.QuotaMetrics {
if !quotametrics.DefaultQuotaMetricServiceRegistry.IsNamespaceSupported(j.Type) {
return fmt.Errorf("Discovery job [%s/%d]: quota metrics are not supported for namespace %q", j.Type, jobIdx, j.Type)
}
}

return nil
}

Expand Down Expand Up @@ -458,6 +466,7 @@ func (c *ScrapeConf) toModelConfig() model.JobsConfig {
job.IncludeContextOnInfoMetrics = discoveryJob.IncludeContextOnInfoMetrics
job.DimensionsRegexps = svc.ToModelDimensionsRegexp()
job.EnhancedMetrics = svc.toModelEnhancedMetricsConfig(discoveryJob.EnhancedMetrics)
job.QuotaMetrics = discoveryJob.QuotaMetrics

job.ExportedTagsOnMetrics = []string{}
if len(c.Discovery.ExportedTagsOnMetrics) > 0 {
Expand Down
69 changes: 69 additions & 0 deletions pkg/config/config_quota_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2026 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 config

import (
"fmt"
"strings"
"testing"

"github.com/prometheus/common/promslog"
"github.com/stretchr/testify/require"
)

func TestQuotaMetricsConfig(t *testing.T) {
testCases := []struct {
name string
configFile string
wantErr bool
errorMsg string
}{
{
name: "valid config with quotaMetrics for supported namespace",
configFile: "quota_metrics_ec2.ok.yml",
wantErr: false,
},
{
name: "invalid config with quotaMetrics for unsupported namespace",
configFile: "quota_metrics_unsupported.bad.yml",
wantErr: true,
errorMsg: "quota metrics are not supported for namespace",
},
{
name: "config with only quotaMetrics is valid",
configFile: "quota_metrics_only.ok.yml",
wantErr: false,
},
{
name: "empty config with no metrics, no enhanced, no quota is invalid",
configFile: "quota_metrics_empty.bad.yml",
wantErr: true,
errorMsg: "Metrics, EnhancedMetrics, and QuotaMetrics should not all be empty",
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
config := ScrapeConf{}
configFile := fmt.Sprintf("testdata/%s", tc.configFile)
_, err := config.Load(configFile, promslog.NewNopLogger())
if tc.wantErr {
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), tc.errorMsg),
"expected error to contain %q but got: %s", tc.errorMsg, err)
} else {
require.NoError(t, err)
}
})
}
}
15 changes: 15 additions & 0 deletions pkg/config/testdata/quota_metrics_ec2.ok.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1alpha1
discovery:
jobs:
- type: AWS/EC2
regions:
- us-east-1
roles:
- {}
metrics:
- name: CPUUtilization
statistics:
- Average
period: 300
length: 300
quotaMetrics: true
8 changes: 8 additions & 0 deletions pkg/config/testdata/quota_metrics_empty.bad.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
apiVersion: v1alpha1
discovery:
jobs:
- type: AWS/EC2
regions:
- us-east-1
roles:
- {}
9 changes: 9 additions & 0 deletions pkg/config/testdata/quota_metrics_only.ok.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: v1alpha1
discovery:
jobs:
- type: AWS/EC2
regions:
- us-east-1
roles:
- {}
quotaMetrics: true
15 changes: 15 additions & 0 deletions pkg/config/testdata/quota_metrics_unsupported.bad.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: v1alpha1
discovery:
jobs:
- type: AWS/Billing
regions:
- us-east-1
roles:
- {}
metrics:
- name: EstimatedCharges
statistics:
- Sum
period: 3600
length: 87600
quotaMetrics: true
15 changes: 14 additions & 1 deletion pkg/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func UpdateMetrics(
// add feature flags to context passed down to all other layers
ctx = config.CtxWithFlags(ctx, options.featureFlags)

tagsData, cloudwatchData := job.ScrapeAwsData(
tagsData, cloudwatchData, quotaData := job.ScrapeAwsData(
ctx,
logger,
jobsCfg,
Expand All @@ -212,6 +212,19 @@ func UpdateMetrics(
return nil
}
metrics, observedMetricLabels = promutil.BuildNamespaceInfoMetrics(tagsData, metrics, observedMetricLabels, options.labelsSnakeCase, logger)

quotaMetrics, quotaLabels := promutil.BuildQuotaMetrics(quotaData, options.labelsSnakeCase, logger)
metrics = append(metrics, quotaMetrics...)
for k, v := range quotaLabels {
if existing, ok := observedMetricLabels[k]; ok {
for label := range v {
existing[label] = struct{}{}
}
} else {
observedMetricLabels[k] = v
}
}

metrics = promutil.EnsureLabelConsistencyAndRemoveDuplicates(metrics, observedMetricLabels)

registry.MustRegister(promutil.NewPrometheusCollector(metrics))
Expand Down
Loading
Loading