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

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ func createExporter(opts component.Options, args component.Arguments) (integrati
fipsEnabled := !a.FIPSDisabled

if a.DecoupledScrape.Enabled {
exp, err := cloudwatch_exporter.NewDecoupledCloudwatchExporter(opts.ID, opts.Logger, exporterConfig, a.DecoupledScrape.ScrapeInterval, fipsEnabled, a.Debug, a.UseAWSSDKVersion2)
exp, err := cloudwatch_exporter.NewDecoupledCloudwatchExporter(opts.ID, opts.Logger, exporterConfig, a.DecoupledScrape.ScrapeInterval, fipsEnabled, a.LabelsSnakeCase, a.Debug, a.UseAWSSDKVersion2)
return exp, getHash(a), err
}

exp, err := cloudwatch_exporter.NewCloudwatchExporter(opts.ID, opts.Logger, exporterConfig, fipsEnabled, a.Debug, a.UseAWSSDKVersion2)
exp, err := cloudwatch_exporter.NewCloudwatchExporter(opts.ID, opts.Logger, exporterConfig, fipsEnabled, a.LabelsSnakeCase, a.Debug, a.UseAWSSDKVersion2)
return exp, getHash(a), err
}
92 changes: 64 additions & 28 deletions internal/component/prometheus/exporter/cloudwatch/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/grafana/alloy/syntax"
)

// Avoid producing absence of values in metrics
// Avoid producing absence of values in metrics.
var defaultNilToZero = true

var defaults = Arguments{
Expand All @@ -26,6 +26,7 @@ var defaults = Arguments{
Enabled: false,
ScrapeInterval: 5 * time.Minute,
},
LabelsSnakeCase: false,
UseAWSSDKVersion2: false,
}

Expand All @@ -39,6 +40,7 @@ type Arguments struct {
Static []StaticJob `alloy:"static,block,optional"`
CustomNamespace []CustomNamespaceJob `alloy:"custom_namespace,block,optional"`
DecoupledScrape DecoupledScrapeConfig `alloy:"decoupled_scraping,block,optional"`
LabelsSnakeCase bool `alloy:"labels_snake_case,attr,optional"`
UseAWSSDKVersion2 bool `alloy:"aws_sdk_version_v2,attr,optional"`
}

Expand All @@ -60,9 +62,11 @@ type DiscoveryJob struct {
DimensionNameRequirements []string `alloy:"dimension_name_requirements,attr,optional"`
RecentlyActiveOnly bool `alloy:"recently_active_only,attr,optional"`
Metrics []Metric `alloy:"metric,block"`
Period time.Duration `alloy:"period,attr,optional"`
Length time.Duration `alloy:"length,attr,optional"`
Delay time.Duration `alloy:"delay,attr,optional"`
//TODO: Remove NilToZero, because it is deprecated upstream.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: I actually could not find any reference to this being a deprecated options, not in the YACE docs nor in the code itself so decided to remove these TODOs.

NilToZero *bool `alloy:"nil_to_zero,attr,optional"`
AddCloudwatchTimestamp *bool `alloy:"add_cloudwatch_timestamp,attr,optional"`
NilToZero *bool `alloy:"nil_to_zero,attr,optional"`
}

// Tags represents a series of tags configured on an AWS resource. Each tag is a
Expand All @@ -77,8 +81,14 @@ type StaticJob struct {
Namespace string `alloy:"namespace,attr"`
Dimensions Dimensions `alloy:"dimensions,attr"`
Metrics []Metric `alloy:"metric,block"`
Period time.Duration `alloy:"period,attr,optional"`
Length time.Duration `alloy:"length,attr,optional"`
Delay time.Duration `alloy:"delay,attr,optional"`
//TODO: Remove NilToZero, because it is deprecated upstream.
// NOTE: This field is actually not supported as a configuration option in YACE!
// https://github.com/prometheus-community/yet-another-cloudwatch-exporter/blob/0c9677d91836f0a4150a55172a0ce5081574b407/docs/configuration.md?plain=1#L177
// It should either be removed from Alloy in some major release, or
// contributed to YACE.
// We currently patch it in toStaticJob func to make it work and not break existing configs.
NilToZero *bool `alloy:"nil_to_zero,attr,optional"`
}

Expand All @@ -91,8 +101,10 @@ type CustomNamespaceJob struct {
RecentlyActiveOnly bool `alloy:"recently_active_only,attr,optional"`
Metrics []Metric `alloy:"metric,block"`
Delay time.Duration `alloy:"delay,attr,optional"`
//TODO: Remove NilToZero, because it is deprecated upstream.
NilToZero *bool `alloy:"nil_to_zero,attr,optional"`
Period time.Duration `alloy:"period,attr,optional"`
Length time.Duration `alloy:"length,attr,optional"`
AddCloudwatchTimestamp *bool `alloy:"add_cloudwatch_timestamp,attr,optional"`
NilToZero *bool `alloy:"nil_to_zero,attr,optional"`
}

// RegionAndRoles exposes for each supported job, the AWS regions and IAM roles
Expand All @@ -114,7 +126,7 @@ type Dimensions map[string]string
type Metric struct {
Name string `alloy:"name,attr"`
Statistics []string `alloy:"statistics,attr"`
Period time.Duration `alloy:"period,attr"`
Period time.Duration `alloy:"period,attr,optional"`
Length time.Duration `alloy:"length,attr,optional"`
NilToZero *bool `alloy:"nil_to_zero,attr,optional"`
AddCloudwatchTimestamp *bool `alloy:"add_cloudwatch_timestamp,attr,optional"`
Expand Down Expand Up @@ -243,19 +255,29 @@ func toYACERoles(rs []Role) []yaceConf.Role {
return yaceRoles
}

func toYACEMetrics(ms []Metric, jobNilToZero *bool) []*yaceConf.Metric {
func toYACEMetrics(ms []Metric, jobPeriod time.Duration, jobLength time.Duration) []*yaceConf.Metric {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question/thought: AFAICT, basically to only reason we still need this function is because in Alloy we decided to automatically align the length with the period window if the length is not defined.

As this is something that YACE does not do, do we actively want to patch a different behavior from upstream in Alloy?

yaceMetrics := []*yaceConf.Metric{}
for _, m := range ms {
if m.Period == 0 {
m.Period = jobPeriod
}
if m.Length == 0 {
m.Length = jobLength
}

periodSeconds := int64(m.Period.Seconds())
lengthSeconds := periodSeconds
// If length is other than zero, that is, it is configured, override the default period value
// If length is other than zero, that is, it is configured, override the default length value
if m.Length != 0 {
lengthSeconds = int64(m.Length.Seconds())
}
nilToZero := m.NilToZero
if nilToZero == nil {
nilToZero = jobNilToZero
}

/* Scenarios:
- Period and length are zero (not set) -> Period = "5m", Length = "5m". These defaults are set by YACE.
- Period = 1m, Length = 0m -> Period = "1m", Length = "1m". Length is set equal to Period by this function.
- Period = 0, Length = 10m -> Period = "5m", Length = "10m". Period is set to the default value by YACE.
- Period = 10m, Length = 2m -> Period = "10m", Length = "2m". This is not a valid configuration and will cause an error produced by YACE. See https://github.com/prometheus-community/yet-another-cloudwatch-exporter/blob/292db29c1537af84a5e831b007bc9ff501708eaa/pkg/config/config.go#L390
*/
yaceMetrics = append(yaceMetrics, &yaceConf.Metric{
Name: m.Name,
Statistics: m.Statistics,
Expand All @@ -265,10 +287,11 @@ func toYACEMetrics(ms []Metric, jobNilToZero *bool) []*yaceConf.Metric {
// by Alloy every so often, dictated by the scrapedInterval, CloudWatch should return a single datapoint
// for each requested metric. That is if Period >= Length, but is Period > Length, we will be getting not enough
// data to fill the whole aggregation bucket. Therefore, Period == Length.
// NOTE: Period > Length is not a valid configuration and will cause an error. See https://github.com/prometheus-community/yet-another-cloudwatch-exporter/blob/292db29c1537af84a5e831b007bc9ff501708eaa/pkg/config/config.go#L390
Period: periodSeconds,
Length: lengthSeconds,

NilToZero: nilToZero,
NilToZero: m.NilToZero,
AddCloudwatchTimestamp: m.AddCloudwatchTimestamp,
})
}
Expand All @@ -283,22 +306,32 @@ func toYACEStaticJob(sj StaticJob) *yaceConf.Static {
Value: value,
})
}
nilToZero := sj.NilToZero
if nilToZero == nil {
nilToZero = &defaultNilToZero

// For each metric in sj.Metrics, if the NilToZero is not set, set the NilToZero to the job level NilToZero or DefaultNilToZero.
// This is needed to make the `nil_to_zero` option work in static jobs as this is not natively supported by YACE.
for i, m := range sj.Metrics {
if m.NilToZero == nil {
if sj.NilToZero == nil {
sj.Metrics[i].NilToZero = &defaultNilToZero
} else {
sj.Metrics[i].NilToZero = sj.NilToZero
}
}
}

return &yaceConf.Static{
Name: sj.Name,
Regions: sj.Auth.Regions,
Roles: toYACERoles(sj.Auth.Roles),
Namespace: sj.Namespace,
CustomTags: sj.CustomTags.toYACE(),
Dimensions: dims,
Metrics: toYACEMetrics(sj.Metrics, nilToZero),
Metrics: toYACEMetrics(sj.Metrics, 0, 0),
}
}

func toYACEDiscoveryJob(rj DiscoveryJob) *yaceConf.Job {
// The default of YACE is false, but for Alloy we want to default to true.
nilToZero := rj.NilToZero
if nilToZero == nil {
nilToZero = &defaultNilToZero
Expand All @@ -310,19 +343,21 @@ func toYACEDiscoveryJob(rj DiscoveryJob) *yaceConf.Job {
CustomTags: rj.CustomTags.toYACE(),
SearchTags: rj.SearchTags.toYACE(),
DimensionNameRequirements: rj.DimensionNameRequirements,
// By setting RoundingPeriod to nil, the exporter will align the start and end times for retrieving CloudWatch
// metrics, with the smallest period in the retrieved batch.
RoundingPeriod: nil,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RecentlyActiveOnly: rj.RecentlyActiveOnly,
JobLevelMetricFields: yaceConf.JobLevelMetricFields{
Delay: int64(rj.Delay.Seconds()),
AddCloudwatchTimestamp: rj.AddCloudwatchTimestamp,
Period: int64(rj.Period.Seconds()),
Length: int64(rj.Length.Seconds()),
Delay: int64(rj.Delay.Seconds()),
NilToZero: nilToZero,
},
Metrics: toYACEMetrics(rj.Metrics, nilToZero),
Metrics: toYACEMetrics(rj.Metrics, rj.Period, rj.Length),
}
return job
}

func toYACECustomNamespaceJob(cn CustomNamespaceJob) *yaceConf.CustomNamespace {
// The default of YACE is false, but for Alloy we want to default to true.
nilToZero := cn.NilToZero
if nilToZero == nil {
nilToZero = &defaultNilToZero
Expand All @@ -334,14 +369,15 @@ func toYACECustomNamespaceJob(cn CustomNamespaceJob) *yaceConf.CustomNamespace {
Roles: toYACERoles(cn.Auth.Roles),
CustomTags: cn.CustomTags.toYACE(),
DimensionNameRequirements: cn.DimensionNameRequirements,
// By setting RoundingPeriod to nil, the exporter will align the start and end times for retrieving CloudWatch
// metrics, with the smallest period in the retrieved batch.
RoundingPeriod: nil,
RecentlyActiveOnly: cn.RecentlyActiveOnly,
JobLevelMetricFields: yaceConf.JobLevelMetricFields{
Delay: int64(cn.Delay.Seconds()),
AddCloudwatchTimestamp: cn.AddCloudwatchTimestamp,
Period: int64(cn.Period.Seconds()),
Length: int64(cn.Length.Seconds()),
Delay: int64(cn.Delay.Seconds()),
NilToZero: nilToZero,
},
Metrics: toYACEMetrics(cn.Metrics, nilToZero),
Metrics: toYACEMetrics(cn.Metrics, cn.Period, cn.Length),
}
}

Expand Down
Loading