AWS SD: RDS Role (#18206)

This commit is contained in:
Matt 2026-03-04 11:17:38 +00:00 committed by GitHub
parent b9ce7f3be0
commit 5a02b92c0e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 1707 additions and 20 deletions

View File

@ -140,13 +140,13 @@
"-": true,
"/": true,
"<": true,
"</": true,
"<=": true,
"==": true,
"=~": true,
">": true,
">=": true,
">/": true,
"</": true,
">=": true,
"@": true,
"^": true,
"and": true,
@ -206,6 +206,7 @@
"openstack": true,
"ovhcloud": true,
"puppetdb": true,
"rds": true,
"scaleway": true,
"serverset": true,
"stackit": true,

View File

@ -48,6 +48,7 @@ const (
RoleElasticache Role = "elasticache"
RoleLightsail Role = "lightsail"
RoleMSK Role = "msk"
RoleRDS Role = "rds"
)
// UnmarshalYAML implements the yaml.Unmarshaler interface.
@ -56,7 +57,7 @@ func (c *Role) UnmarshalYAML(unmarshal func(any) error) error {
return err
}
switch *c {
case RoleEC2, RoleECS, RoleElasticache, RoleLightsail, RoleMSK:
case RoleEC2, RoleECS, RoleElasticache, RoleLightsail, RoleMSK, RoleRDS:
return nil
default:
return fmt.Errorf("unknown AWS SD role %q", *c)
@ -92,6 +93,7 @@ type SDConfig struct {
*ElasticacheSDConfig `yaml:"-"`
*LightsailSDConfig `yaml:"-"`
*MSKSDConfig `yaml:"-"`
*RDSSDConfig `yaml:"-"`
}
// UnmarshalYAML implements the yaml.Unmarshaler interface for SDConfig.
@ -264,6 +266,37 @@ func (c *SDConfig) UnmarshalYAML(unmarshal func(any) error) error {
if c.Clusters != nil {
c.MSKSDConfig.Clusters = c.Clusters
}
case RoleRDS:
if c.RDSSDConfig == nil {
rdsConfig := DefaultRDSSDConfig
c.RDSSDConfig = &rdsConfig
}
c.RDSSDConfig.HTTPClientConfig = c.HTTPClientConfig
c.RDSSDConfig.Region = c.Region
if c.Endpoint != "" {
c.RDSSDConfig.Endpoint = c.Endpoint
}
if c.AccessKey != "" {
c.RDSSDConfig.AccessKey = c.AccessKey
}
if c.SecretKey != "" {
c.RDSSDConfig.SecretKey = c.SecretKey
}
if c.Profile != "" {
c.RDSSDConfig.Profile = c.Profile
}
if c.RoleARN != "" {
c.RDSSDConfig.RoleARN = c.RoleARN
}
if c.Port != 0 {
c.RDSSDConfig.Port = c.Port
}
if c.RefreshInterval != 0 {
c.RDSSDConfig.RefreshInterval = c.RefreshInterval
}
if c.Clusters != nil {
c.RDSSDConfig.Clusters = c.Clusters
}
default:
return fmt.Errorf("unknown AWS SD role %q", c.Role)
}
@ -301,6 +334,9 @@ func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Di
case RoleMSK:
opts.Metrics = &mskMetrics{refreshMetrics: awsMetrics.refreshMetrics}
return NewMSKDiscovery(c.MSKSDConfig, opts)
case RoleRDS:
opts.Metrics = &rdsMetrics{refreshMetrics: awsMetrics.refreshMetrics}
return NewRDSDiscovery(c.RDSSDConfig, opts)
default:
return nil, fmt.Errorf("unknown AWS SD role %q", c.Role)
}

View File

@ -0,0 +1,32 @@
// Copyright 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 aws
import (
"github.com/prometheus/prometheus/discovery"
)
type rdsMetrics struct {
refreshMetrics discovery.RefreshMetricsInstantiator
}
var _ discovery.DiscovererMetrics = (*rdsMetrics)(nil)
// Register implements discovery.DiscovererMetrics.
func (*rdsMetrics) Register() error {
return nil
}
// Unregister implements discovery.DiscovererMetrics.
func (*rdsMetrics) Unregister() {}

999
discovery/aws/rds.go Normal file
View File

@ -0,0 +1,999 @@
// Copyright 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 aws
import (
"context"
"errors"
"fmt"
"log/slog"
"net"
"strconv"
"sync"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
awsConfig "github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/aws/aws-sdk-go-v2/service/rds/types"
"github.com/aws/aws-sdk-go-v2/service/sts"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/config"
"github.com/prometheus/common/model"
"github.com/prometheus/common/promslog"
"golang.org/x/sync/errgroup"
"github.com/prometheus/prometheus/discovery"
"github.com/prometheus/prometheus/discovery/refresh"
"github.com/prometheus/prometheus/discovery/targetgroup"
"github.com/prometheus/prometheus/util/strutil"
)
const (
rdsLabel = model.MetaLabelPrefix + "rds_"
// DB cluster labels.
rdsLabelCluster = rdsLabel + "cluster_"
rdsLabelClusterActivityStreamKinesisStreamName = rdsLabelCluster + "activity_stream_kinesis_stream_name"
rdsLabelClusterActivityStreamKMSKeyID = rdsLabelCluster + "activity_stream_kms_key_id"
rdsLabelClusterActivityStreamMode = rdsLabelCluster + "activity_stream_mode"
rdsLabelClusterActivityStreamStatus = rdsLabelCluster + "activity_stream_status"
rdsLabelClusterAllocatedStorage = rdsLabelCluster + "allocated_storage"
rdsLabelClusterAutoMinorVersionUpgrade = rdsLabelCluster + "auto_minor_version_upgrade"
rdsLabelClusterAutomaticRestartTime = rdsLabelCluster + "automatic_restart_time"
rdsLabelClusterAwsBackupRecoveryPointArn = rdsLabelCluster + "aws_backup_recovery_point_arn"
rdsLabelClusterBacktrackConsumedChangeRecords = rdsLabelCluster + "backtrack_consumed_change_records"
rdsLabelClusterBacktrackWindow = rdsLabelCluster + "backtrack_window"
rdsLabelClusterBackupRetentionPeriod = rdsLabelCluster + "backup_retention_period"
rdsLabelClusterCapacity = rdsLabelCluster + "capacity"
rdsLabelClusterCharacterSetName = rdsLabelCluster + "character_set_name"
rdsLabelClusterCloneGroupID = rdsLabelCluster + "clone_group_id"
rdsLabelClusterClusterCreateTime = rdsLabelCluster + "cluster_create_time"
rdsLabelClusterClusterScalabilityType = rdsLabelCluster + "cluster_scalability_type"
rdsLabelClusterCopyTagsToSnapshot = rdsLabelCluster + "copy_tags_to_snapshot"
rdsLabelClusterCrossAccountClone = rdsLabelCluster + "cross_account_clone"
rdsLabelClusterDBClusterArn = rdsLabelCluster + "arn"
rdsLabelClusterDBClusterIdentifier = rdsLabelCluster + "identifier"
rdsLabelClusterDBClusterInstanceClass = rdsLabelCluster + "instance_class"
rdsLabelClusterDBClusterParameterGroup = rdsLabelCluster + "parameter_group"
rdsLabelClusterDBSubnetGroup = rdsLabelCluster + "subnet_group"
rdsLabelClusterDBSystemID = rdsLabelCluster + "db_system_id"
rdsLabelClusterDatabaseInsightsMode = rdsLabelCluster + "database_insights_mode"
rdsLabelClusterDatabaseName = rdsLabelCluster + "database_name"
rdsLabelClusterDBClusterResourceID = rdsLabelCluster + "resource_id"
rdsLabelClusterDeletionProtection = rdsLabelCluster + "deletion_protection"
rdsLabelClusterEarliestBacktrackTime = rdsLabelCluster + "earliest_backtrack_time"
rdsLabelClusterEarliestRestorableTime = rdsLabelCluster + "earliest_restorable_time"
rdsLabelClusterEndpoint = rdsLabelCluster + "endpoint"
rdsLabelClusterEngine = rdsLabelCluster + "engine"
rdsLabelClusterEngineLifecycleSupport = rdsLabelCluster + "engine_lifecycle_support"
rdsLabelClusterEngineMode = rdsLabelCluster + "engine_mode"
rdsLabelClusterEngineVersion = rdsLabelCluster + "engine_version"
rdsLabelClusterGlobalClusterIdentifier = rdsLabelCluster + "global_cluster_identifier"
rdsLabelClusterGlobalWriteForwardingRequested = rdsLabelCluster + "global_write_forwarding_requested"
rdsLabelClusterGlobalWriteForwardingStatus = rdsLabelCluster + "global_write_forwarding_status"
rdsLabelClusterHostedZoneID = rdsLabelCluster + "hosted_zone_id"
rdsLabelClusterHTTPEndpointEnabled = rdsLabelCluster + "http_endpoint_enabled"
rdsLabelClusterIAMDatabaseAuthenticationEnabled = rdsLabelCluster + "iam_database_authentication_enabled"
rdsLabelClusterIOOptimizedNextAllowedModificationTime = rdsLabelCluster + "io_optimized_next_allowed_modification_time"
rdsLabelClusterIops = rdsLabelCluster + "iops"
rdsLabelClusterKMSKeyID = rdsLabelCluster + "kms_key_id"
rdsLabelClusterLatestRestorableTime = rdsLabelCluster + "latest_restorable_time"
rdsLabelClusterLocalWriteForwardingStatus = rdsLabelCluster + "local_write_forwarding_status"
rdsLabelClusterMasterUsername = rdsLabelCluster + "master_username"
rdsLabelClusterMonitoringInterval = rdsLabelCluster + "monitoring_interval"
rdsLabelClusterMonitoringRoleArn = rdsLabelCluster + "monitoring_role_arn"
rdsLabelClusterMultiAZ = rdsLabelCluster + "multi_az"
rdsLabelClusterNetworkType = rdsLabelCluster + "network_type"
rdsLabelClusterPercentProgress = rdsLabelCluster + "percent_progress"
rdsLabelClusterPerformanceInsightsEnabled = rdsLabelCluster + "performance_insights_enabled"
rdsLabelClusterPerformanceInsightsKMSKeyID = rdsLabelCluster + "performance_insights_kms_key_id"
rdsLabelClusterPerformanceInsightsRetentionPeriod = rdsLabelCluster + "performance_insights_retention_period"
rdsLabelClusterPort = rdsLabelCluster + "port"
rdsLabelClusterPreferredBackupWindow = rdsLabelCluster + "preferred_backup_window"
rdsLabelClusterPreferredMaintenanceWindow = rdsLabelCluster + "preferred_maintenance_window"
rdsLabelClusterPubliclyAccessible = rdsLabelCluster + "publicly_accessible"
rdsLabelClusterReaderEndpoint = rdsLabelCluster + "reader_endpoint"
rdsLabelClusterReplicationSourceIdentifier = rdsLabelCluster + "replication_source_identifier"
rdsLabelClusterServerlessV2PlatformVersion = rdsLabelCluster + "serverless_v2_platform_version"
rdsLabelClusterStatus = rdsLabelCluster + "status"
rdsLabelClusterStorageEncrypted = rdsLabelCluster + "storage_encrypted"
rdsLabelClusterStorageEncryptionType = rdsLabelCluster + "storage_encryption_type"
rdsLabelClusterStorageThroughput = rdsLabelCluster + "storage_throughput"
rdsLabelClusterStorageType = rdsLabelCluster + "storage_type"
rdsLabelClusterUpgradeRolloutOrder = rdsLabelCluster + "upgrade_rollout_order"
// DB cluster tags - create one label per tag key, with the format: rds_cluster_tag_<tagkey>.
rdsLabelClusterTag = rdsLabelCluster + "tag_"
// DB instance labels.
rdsLabelInstance = rdsLabel + "instance_"
rdsLabelInstanceIsClusterWriter = rdsLabelInstance + "is_cluster_writer"
rdsLabelInstanceActivityStreamEngineNativeAuditFieldsIncluded = rdsLabelInstance + "activity_stream_engine_native_audit_fields_included"
rdsLabelInstanceActivityStreamKinesisStreamName = rdsLabelInstance + "activity_stream_kinesis_stream_name"
rdsLabelInstanceActivityStreamKmsKeyID = rdsLabelInstance + "activity_stream_kms_key_id"
rdsLabelInstanceActivityStreamMode = rdsLabelInstance + "activity_stream_mode"
rdsLabelInstanceActivityStreamPolicyStatus = rdsLabelInstance + "activity_stream_policy_status"
rdsLabelInstanceActivityStreamStatus = rdsLabelInstance + "activity_stream_status"
rdsLabelInstanceAllocatedStorage = rdsLabelInstance + "allocated_storage"
rdsLabelInstanceAutoMinorVersionUpgrade = rdsLabelInstance + "auto_minor_version_upgrade"
rdsLabelInstanceAutomaticRestartTime = rdsLabelInstance + "automatic_restart_time"
rdsLabelInstanceAutomationMode = rdsLabelInstance + "automation_mode"
rdsLabelInstanceAvailabilityZone = rdsLabelInstance + "availability_zone"
rdsLabelInstanceAwsBackupRecoveryPointArn = rdsLabelInstance + "aws_backup_recovery_point_arn"
rdsLabelInstanceBackupRetentionPeriod = rdsLabelInstance + "backup_retention_period"
rdsLabelInstanceBackupTarget = rdsLabelInstance + "backup_target"
rdsLabelInstanceCACertificateIdentifier = rdsLabelInstance + "ca_certificate_identifier"
rdsLabelInstanceCharacterSetName = rdsLabelInstance + "character_set_name"
rdsLabelInstanceCopyTagsToSnapshot = rdsLabelInstance + "copy_tags_to_snapshot"
rdsLabelInstanceCustomIamInstanceProfile = rdsLabelInstance + "custom_iam_instance_profile"
rdsLabelInstanceCustomerOwnedIPEnabled = rdsLabelInstance + "customer_owned_ip_enabled"
rdsLabelInstanceDBClusterIdentifier = rdsLabelInstance + "db_cluster_identifier"
rdsLabelInstanceDBInstanceArn = rdsLabelInstance + "arn"
rdsLabelInstanceDBInstanceClass = rdsLabelInstance + "class"
rdsLabelInstanceDBInstanceIdentifier = rdsLabelInstance + "identifier"
rdsLabelInstanceDBInstanceStatus = rdsLabelInstance + "status"
rdsLabelInstanceDBName = rdsLabelInstance + "db_name"
rdsLabelInstanceDBSubnetGroup = rdsLabelInstance + "subnet_group"
rdsLabelInstanceDBSystemID = rdsLabelInstance + "db_system_id"
rdsLabelInstanceDatabaseInsightsMode = rdsLabelInstance + "database_insights_mode"
rdsLabelInstanceDBInstancePort = rdsLabelInstance + "port"
rdsLabelInstanceDBResourceID = rdsLabelInstance + "resource_id"
rdsLabelInstanceDedicatedLogVolume = rdsLabelInstance + "dedicated_log_volume"
rdsLabelInstanceDeletionProtection = rdsLabelInstance + "deletion_protection"
rdsLabelInstanceEndpointAddress = rdsLabelInstance + "endpoint_address"
rdsLabelInstanceEndpointHostedZoneID = rdsLabelInstance + "endpoint_hosted_zone_id"
rdsLabelInstanceEndpointPort = rdsLabelInstance + "endpoint_port"
rdsLabelInstanceEngine = rdsLabelInstance + "engine"
rdsLabelInstanceEngineLifecycleSupport = rdsLabelInstance + "engine_lifecycle_support"
rdsLabelInstanceEngineVersion = rdsLabelInstance + "engine_version"
rdsLabelInstanceEnhancedMonitoringResourceArn = rdsLabelInstance + "enhanced_monitoring_resource_arn"
rdsLabelInstanceIAMDatabaseAuthenticationEnabled = rdsLabelInstance + "iam_database_authentication_enabled"
rdsLabelInstanceInstanceCreateTime = rdsLabelInstance + "instance_create_time"
rdsLabelInstanceIops = rdsLabelInstance + "iops"
rdsLabelInstanceIsStorageConfigUpgradeAvailable = rdsLabelInstance + "is_storage_config_upgrade_available"
rdsLabelInstanceKMSKeyID = rdsLabelInstance + "kms_key_id"
rdsLabelInstanceLatestRestorableTime = rdsLabelInstance + "latest_restorable_time"
rdsLabelInstanceLicenseModel = rdsLabelInstance + "license_model"
rdsLabelInstanceListenerEndpointAddress = rdsLabelInstance + "listener_endpoint_address"
rdsLabelInstanceListenerEndpointHostedZoneID = rdsLabelInstance + "listener_endpoint_hosted_zone_id"
rdsLabelInstanceListenerEndpointPort = rdsLabelInstance + "listener_endpoint_port"
rdsLabelInstanceMasterUsername = rdsLabelInstance + "master_username"
rdsLabelInstanceMaxAllocatedStorage = rdsLabelInstance + "max_allocated_storage"
rdsLabelInstanceMonitoringInterval = rdsLabelInstance + "monitoring_interval"
rdsLabelInstanceMonitoringRoleArn = rdsLabelInstance + "monitoring_role_arn"
rdsLabelInstanceMultiAZ = rdsLabelInstance + "multi_az"
rdsLabelInstanceMultiTenant = rdsLabelInstance + "multi_tenant"
rdsLabelInstanceNcharCharacterSetName = rdsLabelInstance + "nchar_character_set_name"
rdsLabelInstanceNetworkType = rdsLabelInstance + "network_type"
rdsLabelInstancePercentProgress = rdsLabelInstance + "percent_progress"
rdsLabelInstancePerformanceInsightsEnabled = rdsLabelInstance + "performance_insights_enabled"
rdsLabelInstancePerformanceInsightsKMSKeyID = rdsLabelInstance + "performance_insights_kms_key_id"
rdsLabelInstancePerformanceInsightsRetentionPeriod = rdsLabelInstance + "performance_insights_retention_period"
rdsLabelInstancePreferredBackupWindow = rdsLabelInstance + "preferred_backup_window"
rdsLabelInstancePreferredMaintenanceWindow = rdsLabelInstance + "preferred_maintenance_window"
rdsLabelInstancePromotionTier = rdsLabelInstance + "promotion_tier"
rdsLabelInstancePubliclyAccessible = rdsLabelInstance + "publicly_accessible"
rdsLabelInstanceReadReplicaSourceDBClusterIdentifier = rdsLabelInstance + "read_replica_source_db_cluster_identifier"
rdsLabelInstanceReadReplicaSourceDBInstanceIdentifier = rdsLabelInstance + "read_replica_source_db_instance_identifier"
rdsLabelInstanceReplicaMode = rdsLabelInstance + "replica_mode"
rdsLabelInstanceResumeFullAutomationModeTime = rdsLabelInstance + "resume_full_automation_mode_time"
rdsLabelInstanceSecondaryAvailabilityZone = rdsLabelInstance + "secondary_availability_zone"
rdsLabelInstanceStorageEncrypted = rdsLabelInstance + "storage_encrypted"
rdsLabelInstanceStorageEncryptionType = rdsLabelInstance + "storage_encryption_type"
rdsLabelInstanceStorageThroughput = rdsLabelInstance + "storage_throughput"
rdsLabelInstanceStorageType = rdsLabelInstance + "storage_type"
rdsLabelInstanceStorageVolumeStatus = rdsLabelInstance + "storage_volume_status"
rdsLabelInstanceTdeCredentialArn = rdsLabelInstance + "tde_credential_arn"
rdsLabelInstanceTimezone = rdsLabelInstance + "timezone"
rdsLabelInstanceUpgradeRolloutOrder = rdsLabelInstance + "upgrade_rollout_order"
// DB instance tags - create one label per tag key, with the format: rds_instance_tag_<tagkey>.
rdsLabelInstanceTag = rdsLabelInstance + "tag_"
)
// DefaultRDSSDConfig is the default RDS SD configuration.
var DefaultRDSSDConfig = RDSSDConfig{
Port: 80,
RefreshInterval: model.Duration(60 * time.Second),
RequestConcurrency: 10,
HTTPClientConfig: config.DefaultHTTPClientConfig,
}
func init() {
discovery.RegisterConfig(&RDSSDConfig{})
}
// RDSSDConfig is the configuration for RDS based service discovery.
type RDSSDConfig struct {
Region string `yaml:"region"`
Endpoint string `yaml:"endpoint"`
AccessKey string `yaml:"access_key,omitempty"`
SecretKey config.Secret `yaml:"secret_key,omitempty"`
Profile string `yaml:"profile,omitempty"`
RoleARN string `yaml:"role_arn,omitempty"`
Clusters []string `yaml:"clusters,omitempty"`
Port int `yaml:"port"`
RefreshInterval model.Duration `yaml:"refresh_interval,omitempty"`
RequestConcurrency int `yaml:"request_concurrency,omitempty"`
HTTPClientConfig config.HTTPClientConfig `yaml:",inline"`
}
// NewDiscovererMetrics implements discovery.Config.
func (*RDSSDConfig) NewDiscovererMetrics(_ prometheus.Registerer, rmi discovery.RefreshMetricsInstantiator) discovery.DiscovererMetrics {
return &rdsMetrics{
refreshMetrics: rmi,
}
}
// Name returns the name of the RDS Config.
func (*RDSSDConfig) Name() string { return "rds" }
// NewDiscoverer returns a Discoverer for the RDS Config.
func (c *RDSSDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) {
return NewRDSDiscovery(c, opts)
}
// UnmarshalYAML implements the yaml.Unmarshaler interface for the RDS Config.
func (c *RDSSDConfig) UnmarshalYAML(unmarshal func(any) error) error {
*c = DefaultRDSSDConfig
type plain RDSSDConfig
err := unmarshal((*plain)(c))
if err != nil {
return err
}
c.Region, err = loadRegion(context.Background(), c.Region)
if err != nil {
return fmt.Errorf("could not determine AWS region: %w", err)
}
return c.HTTPClientConfig.Validate()
}
type rdsClient interface {
DescribeDBClusters(context.Context, *rds.DescribeDBClustersInput, ...func(*rds.Options)) (*rds.DescribeDBClustersOutput, error)
DescribeDBInstances(context.Context, *rds.DescribeDBInstancesInput, ...func(*rds.Options)) (*rds.DescribeDBInstancesOutput, error)
}
// RDSDiscovery periodically performs RDS-SD requests. It implements
// the Discoverer interface.
type RDSDiscovery struct {
*refresh.Discovery
logger *slog.Logger
cfg *RDSSDConfig
rds rdsClient
}
// NewRDSDiscovery returns a new RDSDiscovery which periodically refreshes its targets.
func NewRDSDiscovery(conf *RDSSDConfig, opts discovery.DiscovererOptions) (*RDSDiscovery, error) {
m, ok := opts.Metrics.(*rdsMetrics)
if !ok {
return nil, errors.New("invalid discovery metrics type")
}
if opts.Logger == nil {
opts.Logger = promslog.NewNopLogger()
}
d := &RDSDiscovery{
logger: opts.Logger,
cfg: conf,
}
d.Discovery = refresh.NewDiscovery(
refresh.Options{
Logger: opts.Logger,
Mech: "rds",
Interval: time.Duration(d.cfg.RefreshInterval),
RefreshF: d.refresh,
MetricsInstantiator: m.refreshMetrics,
},
)
return d, nil
}
func (d *RDSDiscovery) initRdsClient(ctx context.Context) error {
if d.rds != nil {
return nil
}
if d.cfg.Region == "" {
return errors.New("region must be set for RDS service discovery")
}
// Build the HTTP client from the provided HTTPClientConfig.
client, err := config.NewClientFromConfig(d.cfg.HTTPClientConfig, "rds_sd")
if err != nil {
return err
}
// Build the AWS config with the provided region.
var configOptions []func(*awsConfig.LoadOptions) error
configOptions = append(configOptions, awsConfig.WithRegion(d.cfg.Region))
configOptions = append(configOptions, awsConfig.WithHTTPClient(client))
// Only set static credentials if both access key and secret key are provided
// Otherwise, let AWS SDK use its default credential chain
if d.cfg.AccessKey != "" && d.cfg.SecretKey != "" {
credProvider := credentials.NewStaticCredentialsProvider(d.cfg.AccessKey, string(d.cfg.SecretKey), "")
configOptions = append(configOptions, awsConfig.WithCredentialsProvider(credProvider))
}
if d.cfg.Profile != "" {
configOptions = append(configOptions, awsConfig.WithSharedConfigProfile(d.cfg.Profile))
}
cfg, err := awsConfig.LoadDefaultConfig(ctx, configOptions...)
if err != nil {
d.logger.Error("Failed to create AWS config", "error", err)
return fmt.Errorf("could not create aws config: %w", err)
}
// If the role ARN is set, assume the role to get credentials and set the credentials provider in the config.
if d.cfg.RoleARN != "" {
assumeProvider := stscreds.NewAssumeRoleProvider(sts.NewFromConfig(cfg), d.cfg.RoleARN)
cfg.Credentials = aws.NewCredentialsCache(assumeProvider)
}
d.rds = rds.NewFromConfig(cfg, func(options *rds.Options) {
if d.cfg.Endpoint != "" {
options.BaseEndpoint = &d.cfg.Endpoint
}
options.HTTPClient = client
})
// Test credentials by making a simple API call
testCtx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
_, err = d.rds.DescribeDBClusters(testCtx, &rds.DescribeDBClustersInput{})
if err != nil {
d.logger.Error("Failed to test RDS credentials", "error", err)
return fmt.Errorf("RDS credential test failed: %w", err)
}
return nil
}
func (d *RDSDiscovery) describeAllDBClusters(ctx context.Context) (map[string]types.DBCluster, error) {
dbClustersByARN := make(map[string]types.DBCluster)
var nextToken *string
for {
output, err := d.rds.DescribeDBClusters(ctx, &rds.DescribeDBClustersInput{
Marker: nextToken,
MaxRecords: aws.Int32(100),
})
if err != nil {
return nil, fmt.Errorf("failed to describe DB clusters: %w", err)
}
for _, dbCluster := range output.DBClusters {
if dbCluster.DBClusterArn != nil {
dbClustersByARN[*dbCluster.DBClusterArn] = dbCluster
}
}
if output.Marker == nil {
break
}
nextToken = output.Marker
}
return dbClustersByARN, nil
}
func (d *RDSDiscovery) describeDBClusters(ctx context.Context, dbClusterARNS []string) (map[string]types.DBCluster, error) {
mu := &sync.Mutex{}
errg, ectx := errgroup.WithContext(ctx)
errg.SetLimit(d.cfg.RequestConcurrency)
dbClustersByARN := make(map[string]types.DBCluster)
var nextToken *string
for _, arn := range dbClusterARNS {
errg.Go(func() error {
for {
output, err := d.rds.DescribeDBClusters(ectx, &rds.DescribeDBClustersInput{
DBClusterIdentifier: aws.String(arn),
Marker: nextToken,
MaxRecords: aws.Int32(100),
})
if err != nil {
return fmt.Errorf("failed to describe DB cluster %s: %w", arn, err)
}
if len(output.DBClusters) == 0 {
return fmt.Errorf("no DB cluster found for ARN %s", arn)
}
for _, dbCluster := range output.DBClusters {
mu.Lock()
dbClustersByARN[arn] = dbCluster
mu.Unlock()
}
if output.Marker == nil {
break
}
nextToken = output.Marker
}
return nil
})
}
return dbClustersByARN, errg.Wait()
}
func (d *RDSDiscovery) describeDBInstances(ctx context.Context, dbClusterARN string) ([]types.DBInstance, error) {
mu := &sync.Mutex{}
errg, ectx := errgroup.WithContext(ctx)
errg.SetLimit(d.cfg.RequestConcurrency)
dbInstances := []types.DBInstance{}
var nextToken *string
for {
output, err := d.rds.DescribeDBInstances(ectx, &rds.DescribeDBInstancesInput{
Filters: []types.Filter{
{
Name: aws.String("db-cluster-id"),
Values: []string{dbClusterARN},
},
},
Marker: nextToken,
MaxRecords: aws.Int32(100),
})
if err != nil {
return nil, fmt.Errorf("failed to describe DB instances for cluster ARN %s: %w", dbClusterARN, err)
}
if len(output.DBInstances) == 0 {
return nil, fmt.Errorf("no DB instances found for cluster ARN %s", dbClusterARN)
}
for _, dbInstance := range output.DBInstances {
mu.Lock()
dbInstances = append(dbInstances, dbInstance)
mu.Unlock()
}
if output.Marker == nil {
break
}
nextToken = output.Marker
}
return dbInstances, errg.Wait()
}
func (d *RDSDiscovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) {
err := d.initRdsClient(ctx)
if err != nil {
return nil, err
}
tg := &targetgroup.Group{
Source: d.cfg.Region,
}
var clusters map[string]types.DBCluster
if len(d.cfg.Clusters) == 0 {
clusters, err = d.describeAllDBClusters(ctx)
if err != nil {
return nil, fmt.Errorf("error describing all DB clusters: %w", err)
}
} else {
clusters, err = d.describeDBClusters(ctx, d.cfg.Clusters)
if err != nil {
return nil, fmt.Errorf("error describing DB clusters: %w", err)
}
}
var (
mu sync.Mutex
wg sync.WaitGroup
)
for _, cluster := range clusters {
wg.Add(1)
instances, err := d.describeDBInstances(ctx, *cluster.DBClusterArn)
if err != nil {
return nil, fmt.Errorf("error describing DB instances: %w", err)
}
go func(cluster types.DBCluster, instances []types.DBInstance) {
defer wg.Done()
// Build a map of instance identifiers to their IsClusterWriter status
writerMap := make(map[string]bool)
for _, member := range cluster.DBClusterMembers {
if member.DBInstanceIdentifier != nil && member.IsClusterWriter != nil {
writerMap[*member.DBInstanceIdentifier] = *member.IsClusterWriter
}
}
for _, instance := range instances {
labels := model.LabelSet{}
// Cluster labels
if cluster.DBClusterArn != nil {
labels[rdsLabelClusterDBClusterArn] = model.LabelValue(*cluster.DBClusterArn)
}
if cluster.DBClusterIdentifier != nil {
labels[rdsLabelClusterDBClusterIdentifier] = model.LabelValue(*cluster.DBClusterIdentifier)
}
if cluster.ActivityStreamKinesisStreamName != nil {
labels[rdsLabelClusterActivityStreamKinesisStreamName] = model.LabelValue(*cluster.ActivityStreamKinesisStreamName)
}
if cluster.ActivityStreamKmsKeyId != nil {
labels[rdsLabelClusterActivityStreamKMSKeyID] = model.LabelValue(*cluster.ActivityStreamKmsKeyId)
}
if cluster.ActivityStreamMode != "" {
labels[rdsLabelClusterActivityStreamMode] = model.LabelValue(cluster.ActivityStreamMode)
}
if cluster.ActivityStreamStatus != "" {
labels[rdsLabelClusterActivityStreamStatus] = model.LabelValue(cluster.ActivityStreamStatus)
}
if cluster.AllocatedStorage != nil {
labels[rdsLabelClusterAllocatedStorage] = model.LabelValue(strconv.Itoa(int(*cluster.AllocatedStorage)))
}
if cluster.AutoMinorVersionUpgrade != nil {
labels[rdsLabelClusterAutoMinorVersionUpgrade] = model.LabelValue(strconv.FormatBool(*cluster.AutoMinorVersionUpgrade))
}
if cluster.AutomaticRestartTime != nil {
labels[rdsLabelClusterAutomaticRestartTime] = model.LabelValue(cluster.AutomaticRestartTime.Format(time.RFC3339))
}
if cluster.AwsBackupRecoveryPointArn != nil {
labels[rdsLabelClusterAwsBackupRecoveryPointArn] = model.LabelValue(*cluster.AwsBackupRecoveryPointArn)
}
if cluster.BacktrackConsumedChangeRecords != nil {
labels[rdsLabelClusterBacktrackConsumedChangeRecords] = model.LabelValue(strconv.FormatInt(*cluster.BacktrackConsumedChangeRecords, 10))
}
if cluster.BacktrackWindow != nil {
labels[rdsLabelClusterBacktrackWindow] = model.LabelValue(strconv.FormatInt(*cluster.BacktrackWindow, 10))
}
if cluster.BackupRetentionPeriod != nil {
labels[rdsLabelClusterBackupRetentionPeriod] = model.LabelValue(strconv.Itoa(int(*cluster.BackupRetentionPeriod)))
}
if cluster.Capacity != nil {
labels[rdsLabelClusterCapacity] = model.LabelValue(strconv.Itoa(int(*cluster.Capacity)))
}
if cluster.CharacterSetName != nil {
labels[rdsLabelClusterCharacterSetName] = model.LabelValue(*cluster.CharacterSetName)
}
if cluster.CloneGroupId != nil {
labels[rdsLabelClusterCloneGroupID] = model.LabelValue(*cluster.CloneGroupId)
}
if cluster.ClusterCreateTime != nil {
labels[rdsLabelClusterClusterCreateTime] = model.LabelValue(cluster.ClusterCreateTime.Format(time.RFC3339))
}
if cluster.ClusterScalabilityType != "" {
labels[rdsLabelClusterClusterScalabilityType] = model.LabelValue(cluster.ClusterScalabilityType)
}
if cluster.CopyTagsToSnapshot != nil {
labels[rdsLabelClusterCopyTagsToSnapshot] = model.LabelValue(strconv.FormatBool(*cluster.CopyTagsToSnapshot))
}
if cluster.CrossAccountClone != nil {
labels[rdsLabelClusterCrossAccountClone] = model.LabelValue(strconv.FormatBool(*cluster.CrossAccountClone))
}
if cluster.DBClusterInstanceClass != nil {
labels[rdsLabelClusterDBClusterInstanceClass] = model.LabelValue(*cluster.DBClusterInstanceClass)
}
if cluster.DBClusterParameterGroup != nil {
labels[rdsLabelClusterDBClusterParameterGroup] = model.LabelValue(*cluster.DBClusterParameterGroup)
}
if cluster.DBSubnetGroup != nil {
labels[rdsLabelClusterDBSubnetGroup] = model.LabelValue(*cluster.DBSubnetGroup)
}
if cluster.DBSystemId != nil {
labels[rdsLabelClusterDBSystemID] = model.LabelValue(*cluster.DBSystemId)
}
if cluster.DatabaseInsightsMode != "" {
labels[rdsLabelClusterDatabaseInsightsMode] = model.LabelValue(cluster.DatabaseInsightsMode)
}
if cluster.DatabaseName != nil {
labels[rdsLabelClusterDatabaseName] = model.LabelValue(*cluster.DatabaseName)
}
if cluster.DbClusterResourceId != nil {
labels[rdsLabelClusterDBClusterResourceID] = model.LabelValue(*cluster.DbClusterResourceId)
}
if cluster.DeletionProtection != nil {
labels[rdsLabelClusterDeletionProtection] = model.LabelValue(strconv.FormatBool(*cluster.DeletionProtection))
}
if cluster.EarliestBacktrackTime != nil {
labels[rdsLabelClusterEarliestBacktrackTime] = model.LabelValue(cluster.EarliestBacktrackTime.Format(time.RFC3339))
}
if cluster.EarliestRestorableTime != nil {
labels[rdsLabelClusterEarliestRestorableTime] = model.LabelValue(cluster.EarliestRestorableTime.Format(time.RFC3339))
}
if cluster.Endpoint != nil {
labels[rdsLabelClusterEndpoint] = model.LabelValue(*cluster.Endpoint)
}
if cluster.Engine != nil {
labels[rdsLabelClusterEngine] = model.LabelValue(*cluster.Engine)
}
if cluster.EngineLifecycleSupport != nil {
labels[rdsLabelClusterEngineLifecycleSupport] = model.LabelValue(*cluster.EngineLifecycleSupport)
}
if cluster.EngineMode != nil {
labels[rdsLabelClusterEngineMode] = model.LabelValue(*cluster.EngineMode)
}
if cluster.EngineVersion != nil {
labels[rdsLabelClusterEngineVersion] = model.LabelValue(*cluster.EngineVersion)
}
if cluster.GlobalClusterIdentifier != nil {
labels[rdsLabelClusterGlobalClusterIdentifier] = model.LabelValue(*cluster.GlobalClusterIdentifier)
}
if cluster.GlobalWriteForwardingRequested != nil {
labels[rdsLabelClusterGlobalWriteForwardingRequested] = model.LabelValue(strconv.FormatBool(*cluster.GlobalWriteForwardingRequested))
}
if cluster.GlobalWriteForwardingStatus != "" {
labels[rdsLabelClusterGlobalWriteForwardingStatus] = model.LabelValue(cluster.GlobalWriteForwardingStatus)
}
if cluster.HostedZoneId != nil {
labels[rdsLabelClusterHostedZoneID] = model.LabelValue(*cluster.HostedZoneId)
}
if cluster.HttpEndpointEnabled != nil {
labels[rdsLabelClusterHTTPEndpointEnabled] = model.LabelValue(strconv.FormatBool(*cluster.HttpEndpointEnabled))
}
if cluster.IAMDatabaseAuthenticationEnabled != nil {
labels[rdsLabelClusterIAMDatabaseAuthenticationEnabled] = model.LabelValue(strconv.FormatBool(*cluster.IAMDatabaseAuthenticationEnabled))
}
if cluster.IOOptimizedNextAllowedModificationTime != nil {
labels[rdsLabelClusterIOOptimizedNextAllowedModificationTime] = model.LabelValue(cluster.IOOptimizedNextAllowedModificationTime.Format(time.RFC3339))
}
if cluster.Iops != nil {
labels[rdsLabelClusterIops] = model.LabelValue(strconv.Itoa(int(*cluster.Iops)))
}
if cluster.KmsKeyId != nil {
labels[rdsLabelClusterKMSKeyID] = model.LabelValue(*cluster.KmsKeyId)
}
if cluster.LatestRestorableTime != nil {
labels[rdsLabelClusterLatestRestorableTime] = model.LabelValue(cluster.LatestRestorableTime.Format(time.RFC3339))
}
if cluster.LocalWriteForwardingStatus != "" {
labels[rdsLabelClusterLocalWriteForwardingStatus] = model.LabelValue(cluster.LocalWriteForwardingStatus)
}
if cluster.MasterUsername != nil {
labels[rdsLabelClusterMasterUsername] = model.LabelValue(*cluster.MasterUsername)
}
if cluster.MonitoringInterval != nil {
labels[rdsLabelClusterMonitoringInterval] = model.LabelValue(strconv.Itoa(int(*cluster.MonitoringInterval)))
}
if cluster.MonitoringRoleArn != nil {
labels[rdsLabelClusterMonitoringRoleArn] = model.LabelValue(*cluster.MonitoringRoleArn)
}
if cluster.MultiAZ != nil {
labels[rdsLabelClusterMultiAZ] = model.LabelValue(strconv.FormatBool(*cluster.MultiAZ))
}
if cluster.NetworkType != nil {
labels[rdsLabelClusterNetworkType] = model.LabelValue(*cluster.NetworkType)
}
if cluster.PercentProgress != nil {
labels[rdsLabelClusterPercentProgress] = model.LabelValue(*cluster.PercentProgress)
}
if cluster.PerformanceInsightsEnabled != nil {
labels[rdsLabelClusterPerformanceInsightsEnabled] = model.LabelValue(strconv.FormatBool(*cluster.PerformanceInsightsEnabled))
}
if cluster.PerformanceInsightsKMSKeyId != nil {
labels[rdsLabelClusterPerformanceInsightsKMSKeyID] = model.LabelValue(*cluster.PerformanceInsightsKMSKeyId)
}
if cluster.PerformanceInsightsRetentionPeriod != nil {
labels[rdsLabelClusterPerformanceInsightsRetentionPeriod] = model.LabelValue(strconv.Itoa(int(*cluster.PerformanceInsightsRetentionPeriod)))
}
if cluster.Port != nil {
labels[rdsLabelClusterPort] = model.LabelValue(strconv.Itoa(int(*cluster.Port)))
}
if cluster.PreferredBackupWindow != nil {
labels[rdsLabelClusterPreferredBackupWindow] = model.LabelValue(*cluster.PreferredBackupWindow)
}
if cluster.PreferredMaintenanceWindow != nil {
labels[rdsLabelClusterPreferredMaintenanceWindow] = model.LabelValue(*cluster.PreferredMaintenanceWindow)
}
if cluster.PubliclyAccessible != nil {
labels[rdsLabelClusterPubliclyAccessible] = model.LabelValue(strconv.FormatBool(*cluster.PubliclyAccessible))
}
if cluster.ReaderEndpoint != nil {
labels[rdsLabelClusterReaderEndpoint] = model.LabelValue(*cluster.ReaderEndpoint)
}
if cluster.ReplicationSourceIdentifier != nil {
labels[rdsLabelClusterReplicationSourceIdentifier] = model.LabelValue(*cluster.ReplicationSourceIdentifier)
}
if cluster.ServerlessV2PlatformVersion != nil {
labels[rdsLabelClusterServerlessV2PlatformVersion] = model.LabelValue(*cluster.ServerlessV2PlatformVersion)
}
if cluster.Status != nil {
labels[rdsLabelClusterStatus] = model.LabelValue(*cluster.Status)
}
if cluster.StorageEncrypted != nil {
labels[rdsLabelClusterStorageEncrypted] = model.LabelValue(strconv.FormatBool(*cluster.StorageEncrypted))
}
if cluster.StorageEncryptionType != "" {
labels[rdsLabelClusterStorageEncryptionType] = model.LabelValue(cluster.StorageEncryptionType)
}
if cluster.StorageThroughput != nil {
labels[rdsLabelClusterStorageThroughput] = model.LabelValue(strconv.Itoa(int(*cluster.StorageThroughput)))
}
if cluster.StorageType != nil {
labels[rdsLabelClusterStorageType] = model.LabelValue(*cluster.StorageType)
}
if cluster.UpgradeRolloutOrder != "" {
labels[rdsLabelClusterUpgradeRolloutOrder] = model.LabelValue(cluster.UpgradeRolloutOrder)
}
// Cluster tags
for _, tag := range cluster.TagList {
if tag.Key != nil && tag.Value != nil {
labels[model.LabelName(rdsLabelClusterTag+strutil.SanitizeLabelName(*tag.Key))] = model.LabelValue(*tag.Value)
}
}
// Instance labels
if instance.DBInstanceArn != nil {
labels[rdsLabelInstanceDBInstanceArn] = model.LabelValue(*instance.DBInstanceArn)
}
if instance.DBInstanceIdentifier != nil {
labels[rdsLabelInstanceDBInstanceIdentifier] = model.LabelValue(*instance.DBInstanceIdentifier)
// Set IsClusterWriter based on cluster membership information
if isWriter, found := writerMap[*instance.DBInstanceIdentifier]; found {
labels[rdsLabelInstanceIsClusterWriter] = model.LabelValue(strconv.FormatBool(isWriter))
}
}
if instance.ActivityStreamEngineNativeAuditFieldsIncluded != nil {
labels[rdsLabelInstanceActivityStreamEngineNativeAuditFieldsIncluded] = model.LabelValue(strconv.FormatBool(*instance.ActivityStreamEngineNativeAuditFieldsIncluded))
}
if instance.ActivityStreamKinesisStreamName != nil {
labels[rdsLabelInstanceActivityStreamKinesisStreamName] = model.LabelValue(*instance.ActivityStreamKinesisStreamName)
}
if instance.ActivityStreamKmsKeyId != nil {
labels[rdsLabelInstanceActivityStreamKmsKeyID] = model.LabelValue(*instance.ActivityStreamKmsKeyId)
}
if instance.ActivityStreamMode != "" {
labels[rdsLabelInstanceActivityStreamMode] = model.LabelValue(instance.ActivityStreamMode)
}
if instance.ActivityStreamPolicyStatus != "" {
labels[rdsLabelInstanceActivityStreamPolicyStatus] = model.LabelValue(instance.ActivityStreamPolicyStatus)
}
if instance.ActivityStreamStatus != "" {
labels[rdsLabelInstanceActivityStreamStatus] = model.LabelValue(instance.ActivityStreamStatus)
}
if instance.AllocatedStorage != nil {
labels[rdsLabelInstanceAllocatedStorage] = model.LabelValue(strconv.Itoa(int(*instance.AllocatedStorage)))
}
if instance.AutoMinorVersionUpgrade != nil {
labels[rdsLabelInstanceAutoMinorVersionUpgrade] = model.LabelValue(strconv.FormatBool(*instance.AutoMinorVersionUpgrade))
}
if instance.AutomaticRestartTime != nil {
labels[rdsLabelInstanceAutomaticRestartTime] = model.LabelValue(instance.AutomaticRestartTime.Format(time.RFC3339))
}
if instance.AutomationMode != "" {
labels[rdsLabelInstanceAutomationMode] = model.LabelValue(instance.AutomationMode)
}
if instance.AvailabilityZone != nil {
labels[rdsLabelInstanceAvailabilityZone] = model.LabelValue(*instance.AvailabilityZone)
}
if instance.AwsBackupRecoveryPointArn != nil {
labels[rdsLabelInstanceAwsBackupRecoveryPointArn] = model.LabelValue(*instance.AwsBackupRecoveryPointArn)
}
if instance.BackupRetentionPeriod != nil {
labels[rdsLabelInstanceBackupRetentionPeriod] = model.LabelValue(strconv.Itoa(int(*instance.BackupRetentionPeriod)))
}
if instance.BackupTarget != nil {
labels[rdsLabelInstanceBackupTarget] = model.LabelValue(*instance.BackupTarget)
}
if instance.CACertificateIdentifier != nil {
labels[rdsLabelInstanceCACertificateIdentifier] = model.LabelValue(*instance.CACertificateIdentifier)
}
if instance.CharacterSetName != nil {
labels[rdsLabelInstanceCharacterSetName] = model.LabelValue(*instance.CharacterSetName)
}
if instance.CopyTagsToSnapshot != nil {
labels[rdsLabelInstanceCopyTagsToSnapshot] = model.LabelValue(strconv.FormatBool(*instance.CopyTagsToSnapshot))
}
if instance.CustomIamInstanceProfile != nil {
labels[rdsLabelInstanceCustomIamInstanceProfile] = model.LabelValue(*instance.CustomIamInstanceProfile)
}
if instance.CustomerOwnedIpEnabled != nil {
labels[rdsLabelInstanceCustomerOwnedIPEnabled] = model.LabelValue(strconv.FormatBool(*instance.CustomerOwnedIpEnabled))
}
if instance.DBClusterIdentifier != nil {
labels[rdsLabelInstanceDBClusterIdentifier] = model.LabelValue(*instance.DBClusterIdentifier)
}
if instance.DBInstanceClass != nil {
labels[rdsLabelInstanceDBInstanceClass] = model.LabelValue(*instance.DBInstanceClass)
}
if instance.DBInstanceStatus != nil {
labels[rdsLabelInstanceDBInstanceStatus] = model.LabelValue(*instance.DBInstanceStatus)
}
if instance.DBName != nil {
labels[rdsLabelInstanceDBName] = model.LabelValue(*instance.DBName)
}
if instance.DbInstancePort != nil {
labels[rdsLabelInstanceDBInstancePort] = model.LabelValue(strconv.Itoa(int(*instance.DbInstancePort)))
}
if instance.DbiResourceId != nil {
labels[rdsLabelInstanceDBResourceID] = model.LabelValue(*instance.DbiResourceId)
}
if instance.DedicatedLogVolume != nil {
labels[rdsLabelInstanceDedicatedLogVolume] = model.LabelValue(strconv.FormatBool(*instance.DedicatedLogVolume))
}
if instance.DeletionProtection != nil {
labels[rdsLabelInstanceDeletionProtection] = model.LabelValue(strconv.FormatBool(*instance.DeletionProtection))
}
if instance.Endpoint != nil {
if instance.Endpoint.Address != nil {
labels[rdsLabelInstanceEndpointAddress] = model.LabelValue(*instance.Endpoint.Address)
}
if instance.Endpoint.HostedZoneId != nil {
labels[rdsLabelInstanceEndpointHostedZoneID] = model.LabelValue(*instance.Endpoint.HostedZoneId)
}
if instance.Endpoint.Port != nil {
labels[rdsLabelInstanceEndpointPort] = model.LabelValue(strconv.Itoa(int(*instance.Endpoint.Port)))
}
}
if instance.Engine != nil {
labels[rdsLabelInstanceEngine] = model.LabelValue(*instance.Engine)
}
if instance.EngineLifecycleSupport != nil {
labels[rdsLabelInstanceEngineLifecycleSupport] = model.LabelValue(*instance.EngineLifecycleSupport)
}
if instance.EngineVersion != nil {
labels[rdsLabelInstanceEngineVersion] = model.LabelValue(*instance.EngineVersion)
}
if instance.EnhancedMonitoringResourceArn != nil {
labels[rdsLabelInstanceEnhancedMonitoringResourceArn] = model.LabelValue(*instance.EnhancedMonitoringResourceArn)
}
if instance.IAMDatabaseAuthenticationEnabled != nil {
labels[rdsLabelInstanceIAMDatabaseAuthenticationEnabled] = model.LabelValue(strconv.FormatBool(*instance.IAMDatabaseAuthenticationEnabled))
}
if instance.InstanceCreateTime != nil {
labels[rdsLabelInstanceInstanceCreateTime] = model.LabelValue(instance.InstanceCreateTime.Format(time.RFC3339))
}
if instance.Iops != nil {
labels[rdsLabelInstanceIops] = model.LabelValue(strconv.Itoa(int(*instance.Iops)))
}
if instance.IsStorageConfigUpgradeAvailable != nil {
labels[rdsLabelInstanceIsStorageConfigUpgradeAvailable] = model.LabelValue(strconv.FormatBool(*instance.IsStorageConfigUpgradeAvailable))
}
if instance.KmsKeyId != nil {
labels[rdsLabelInstanceKMSKeyID] = model.LabelValue(*instance.KmsKeyId)
}
if instance.LatestRestorableTime != nil {
labels[rdsLabelInstanceLatestRestorableTime] = model.LabelValue(instance.LatestRestorableTime.Format(time.RFC3339))
}
if instance.LicenseModel != nil {
labels[rdsLabelInstanceLicenseModel] = model.LabelValue(*instance.LicenseModel)
}
if instance.ListenerEndpoint != nil {
if instance.ListenerEndpoint.Address != nil {
labels[rdsLabelInstanceListenerEndpointAddress] = model.LabelValue(*instance.ListenerEndpoint.Address)
}
if instance.ListenerEndpoint.HostedZoneId != nil {
labels[rdsLabelInstanceListenerEndpointHostedZoneID] = model.LabelValue(*instance.ListenerEndpoint.HostedZoneId)
}
if instance.ListenerEndpoint.Port != nil {
labels[rdsLabelInstanceListenerEndpointPort] = model.LabelValue(strconv.Itoa(int(*instance.ListenerEndpoint.Port)))
}
}
if instance.MasterUsername != nil {
labels[rdsLabelInstanceMasterUsername] = model.LabelValue(*instance.MasterUsername)
}
if instance.MaxAllocatedStorage != nil {
labels[rdsLabelInstanceMaxAllocatedStorage] = model.LabelValue(strconv.Itoa(int(*instance.MaxAllocatedStorage)))
}
if instance.MonitoringInterval != nil {
labels[rdsLabelInstanceMonitoringInterval] = model.LabelValue(strconv.Itoa(int(*instance.MonitoringInterval)))
}
if instance.MonitoringRoleArn != nil {
labels[rdsLabelInstanceMonitoringRoleArn] = model.LabelValue(*instance.MonitoringRoleArn)
}
if instance.MultiAZ != nil {
labels[rdsLabelInstanceMultiAZ] = model.LabelValue(strconv.FormatBool(*instance.MultiAZ))
}
if instance.MultiTenant != nil {
labels[rdsLabelInstanceMultiTenant] = model.LabelValue(strconv.FormatBool(*instance.MultiTenant))
}
if instance.NcharCharacterSetName != nil {
labels[rdsLabelInstanceNcharCharacterSetName] = model.LabelValue(*instance.NcharCharacterSetName)
}
if instance.NetworkType != nil {
labels[rdsLabelInstanceNetworkType] = model.LabelValue(*instance.NetworkType)
}
if instance.PercentProgress != nil {
labels[rdsLabelInstancePercentProgress] = model.LabelValue(*instance.PercentProgress)
}
if instance.PerformanceInsightsEnabled != nil {
labels[rdsLabelInstancePerformanceInsightsEnabled] = model.LabelValue(strconv.FormatBool(*instance.PerformanceInsightsEnabled))
}
if instance.PerformanceInsightsKMSKeyId != nil {
labels[rdsLabelInstancePerformanceInsightsKMSKeyID] = model.LabelValue(*instance.PerformanceInsightsKMSKeyId)
}
if instance.PerformanceInsightsRetentionPeriod != nil {
labels[rdsLabelInstancePerformanceInsightsRetentionPeriod] = model.LabelValue(strconv.Itoa(int(*instance.PerformanceInsightsRetentionPeriod)))
}
if instance.PreferredBackupWindow != nil {
labels[rdsLabelInstancePreferredBackupWindow] = model.LabelValue(*instance.PreferredBackupWindow)
}
if instance.PreferredMaintenanceWindow != nil {
labels[rdsLabelInstancePreferredMaintenanceWindow] = model.LabelValue(*instance.PreferredMaintenanceWindow)
}
if instance.PromotionTier != nil {
labels[rdsLabelInstancePromotionTier] = model.LabelValue(strconv.Itoa(int(*instance.PromotionTier)))
}
if instance.PubliclyAccessible != nil {
labels[rdsLabelInstancePubliclyAccessible] = model.LabelValue(strconv.FormatBool(*instance.PubliclyAccessible))
}
if instance.ReadReplicaSourceDBClusterIdentifier != nil {
labels[rdsLabelInstanceReadReplicaSourceDBClusterIdentifier] = model.LabelValue(*instance.ReadReplicaSourceDBClusterIdentifier)
}
if instance.ReadReplicaSourceDBInstanceIdentifier != nil {
labels[rdsLabelInstanceReadReplicaSourceDBInstanceIdentifier] = model.LabelValue(*instance.ReadReplicaSourceDBInstanceIdentifier)
}
if instance.ReplicaMode != "" {
labels[rdsLabelInstanceReplicaMode] = model.LabelValue(instance.ReplicaMode)
}
if instance.ResumeFullAutomationModeTime != nil {
labels[rdsLabelInstanceResumeFullAutomationModeTime] = model.LabelValue(instance.ResumeFullAutomationModeTime.Format(time.RFC3339))
}
if instance.SecondaryAvailabilityZone != nil {
labels[rdsLabelInstanceSecondaryAvailabilityZone] = model.LabelValue(*instance.SecondaryAvailabilityZone)
}
if instance.StorageEncrypted != nil {
labels[rdsLabelInstanceStorageEncrypted] = model.LabelValue(strconv.FormatBool(*instance.StorageEncrypted))
}
if instance.StorageEncryptionType != "" {
labels[rdsLabelInstanceStorageEncryptionType] = model.LabelValue(instance.StorageEncryptionType)
}
if instance.StorageThroughput != nil {
labels[rdsLabelInstanceStorageThroughput] = model.LabelValue(strconv.Itoa(int(*instance.StorageThroughput)))
}
if instance.StorageType != nil {
labels[rdsLabelInstanceStorageType] = model.LabelValue(*instance.StorageType)
}
if instance.StorageVolumeStatus != nil {
labels[rdsLabelInstanceStorageVolumeStatus] = model.LabelValue(*instance.StorageVolumeStatus)
}
if instance.TdeCredentialArn != nil {
labels[rdsLabelInstanceTdeCredentialArn] = model.LabelValue(*instance.TdeCredentialArn)
}
if instance.Timezone != nil {
labels[rdsLabelInstanceTimezone] = model.LabelValue(*instance.Timezone)
}
if instance.UpgradeRolloutOrder != "" {
labels[rdsLabelInstanceUpgradeRolloutOrder] = model.LabelValue(instance.UpgradeRolloutOrder)
}
if instance.DBSubnetGroup != nil && instance.DBSubnetGroup.DBSubnetGroupName != nil {
labels[rdsLabelInstanceDBSubnetGroup] = model.LabelValue(*instance.DBSubnetGroup.DBSubnetGroupName)
}
if instance.DBSystemId != nil {
labels[rdsLabelInstanceDBSystemID] = model.LabelValue(*instance.DBSystemId)
}
if instance.DatabaseInsightsMode != "" {
labels[rdsLabelInstanceDatabaseInsightsMode] = model.LabelValue(instance.DatabaseInsightsMode)
}
// Instance tags
for _, tag := range instance.TagList {
if tag.Key != nil && tag.Value != nil {
labels[model.LabelName(rdsLabelInstanceTag+strutil.SanitizeLabelName(*tag.Key))] = model.LabelValue(*tag.Value)
}
}
// Set the address label
if instance.Endpoint != nil && instance.Endpoint.Address != nil && instance.Endpoint.Port != nil {
labels[model.AddressLabel] = model.LabelValue(net.JoinHostPort(*instance.Endpoint.Address, strconv.Itoa(d.cfg.Port)))
}
mu.Lock()
tg.Targets = append(tg.Targets, labels)
mu.Unlock()
}
}(cluster, instances)
}
wg.Wait()
return []*targetgroup.Group{tg}, nil
}

450
discovery/aws/rds_test.go Normal file
View File

@ -0,0 +1,450 @@
// Copyright 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 aws
import (
"context"
"net"
"strconv"
"testing"
"time"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/rds"
"github.com/aws/aws-sdk-go-v2/service/rds/types"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
"github.com/prometheus/prometheus/discovery/targetgroup"
)
// Mock RDS client for testing.
type mockRDSClient struct {
clusters map[string]types.DBCluster
instances map[string][]types.DBInstance
}
func (m *mockRDSClient) DescribeDBClusters(_ context.Context, input *rds.DescribeDBClustersInput, _ ...func(*rds.Options)) (*rds.DescribeDBClustersOutput, error) {
var clusters []types.DBCluster
if input.DBClusterIdentifier != nil {
// Specific cluster requested
if cluster, ok := m.clusters[*input.DBClusterIdentifier]; ok {
clusters = append(clusters, cluster)
}
} else {
// All clusters
for _, cluster := range m.clusters {
clusters = append(clusters, cluster)
}
}
return &rds.DescribeDBClustersOutput{
DBClusters: clusters,
}, nil
}
func (m *mockRDSClient) DescribeDBInstances(_ context.Context, input *rds.DescribeDBInstancesInput, _ ...func(*rds.Options)) (*rds.DescribeDBInstancesOutput, error) {
var instances []types.DBInstance
// Check if filtering by cluster
if input.Filters != nil {
for _, filter := range input.Filters {
if filter.Name != nil && *filter.Name == "db-cluster-id" {
for _, clusterID := range filter.Values {
if clusterInstances, ok := m.instances[clusterID]; ok {
instances = append(instances, clusterInstances...)
}
}
}
}
} else {
// All instances
for _, clusterInstances := range m.instances {
instances = append(instances, clusterInstances...)
}
}
return &rds.DescribeDBInstancesOutput{
DBInstances: instances,
}, nil
}
func TestRDSDiscoveryRefresh(t *testing.T) {
testTime := time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC)
tests := []struct {
name string
clusters map[string]types.DBCluster
instances map[string][]types.DBInstance
expectedLabels []model.LabelSet
}{
{
name: "SingleClusterWithInstance",
clusters: map[string]types.DBCluster{
"arn:aws:rds:us-east-1:123456789012:cluster:test-cluster": {
DBClusterArn: aws.String("arn:aws:rds:us-east-1:123456789012:cluster:test-cluster"),
DBClusterIdentifier: aws.String("test-cluster"),
Engine: aws.String("aurora-postgresql"),
EngineVersion: aws.String("15.4"),
Status: aws.String("available"),
Endpoint: aws.String("test-cluster.cluster-xyz.us-east-1.rds.amazonaws.com"),
Port: aws.Int32(5432),
MasterUsername: aws.String("admin"),
MultiAZ: aws.Bool(true),
ClusterCreateTime: aws.Time(testTime),
DBClusterMembers: []types.DBClusterMember{
{
DBInstanceIdentifier: aws.String("test-instance-1"),
IsClusterWriter: aws.Bool(true),
},
},
TagList: []types.Tag{
{Key: aws.String("Environment"), Value: aws.String("test")},
},
},
},
instances: map[string][]types.DBInstance{
"arn:aws:rds:us-east-1:123456789012:cluster:test-cluster": {
{
DBInstanceArn: aws.String("arn:aws:rds:us-east-1:123456789012:db:test-instance-1"),
DBInstanceIdentifier: aws.String("test-instance-1"),
DBInstanceClass: aws.String("db.r5.large"),
DBInstanceStatus: aws.String("available"),
Engine: aws.String("aurora-postgresql"),
EngineVersion: aws.String("15.4"),
AvailabilityZone: aws.String("us-east-1a"),
DBClusterIdentifier: aws.String("test-cluster"),
PubliclyAccessible: aws.Bool(false),
InstanceCreateTime: aws.Time(testTime),
Endpoint: &types.Endpoint{
Address: aws.String("test-instance-1.xyz.us-east-1.rds.amazonaws.com"),
Port: aws.Int32(5432),
HostedZoneId: aws.String("Z2R2ITUGPM61AM"),
},
TagList: []types.Tag{
{Key: aws.String("Name"), Value: aws.String("test-instance")},
},
},
},
},
expectedLabels: []model.LabelSet{
{
model.AddressLabel: model.LabelValue("test-instance-1.xyz.us-east-1.rds.amazonaws.com:5432"),
rdsLabelClusterDBClusterArn: model.LabelValue("arn:aws:rds:us-east-1:123456789012:cluster:test-cluster"),
rdsLabelClusterDBClusterIdentifier: model.LabelValue("test-cluster"),
rdsLabelClusterEngine: model.LabelValue("aurora-postgresql"),
rdsLabelClusterEngineVersion: model.LabelValue("15.4"),
rdsLabelClusterStatus: model.LabelValue("available"),
rdsLabelClusterEndpoint: model.LabelValue("test-cluster.cluster-xyz.us-east-1.rds.amazonaws.com"),
rdsLabelClusterPort: model.LabelValue("5432"),
rdsLabelClusterMasterUsername: model.LabelValue("admin"),
rdsLabelClusterMultiAZ: model.LabelValue("true"),
rdsLabelClusterClusterCreateTime: model.LabelValue(testTime.Format(time.RFC3339)),
model.LabelName(rdsLabelClusterTag + "Environment"): model.LabelValue("test"),
rdsLabelInstanceDBInstanceArn: model.LabelValue("arn:aws:rds:us-east-1:123456789012:db:test-instance-1"),
rdsLabelInstanceDBInstanceIdentifier: model.LabelValue("test-instance-1"),
rdsLabelInstanceIsClusterWriter: model.LabelValue("true"),
rdsLabelInstanceDBInstanceClass: model.LabelValue("db.r5.large"),
rdsLabelInstanceDBInstanceStatus: model.LabelValue("available"),
rdsLabelInstanceEngine: model.LabelValue("aurora-postgresql"),
rdsLabelInstanceEngineVersion: model.LabelValue("15.4"),
rdsLabelInstanceAvailabilityZone: model.LabelValue("us-east-1a"),
rdsLabelInstanceDBClusterIdentifier: model.LabelValue("test-cluster"),
rdsLabelInstancePubliclyAccessible: model.LabelValue("false"),
rdsLabelInstanceInstanceCreateTime: model.LabelValue(testTime.Format(time.RFC3339)),
rdsLabelInstanceEndpointAddress: model.LabelValue("test-instance-1.xyz.us-east-1.rds.amazonaws.com"),
rdsLabelInstanceEndpointPort: model.LabelValue("5432"),
rdsLabelInstanceEndpointHostedZoneID: model.LabelValue("Z2R2ITUGPM61AM"),
model.LabelName(rdsLabelInstanceTag + "Name"): model.LabelValue("test-instance"),
},
},
},
{
name: "MultipleInstancesInCluster",
clusters: map[string]types.DBCluster{
"arn:aws:rds:us-west-2:123456789012:cluster:prod-cluster": {
DBClusterArn: aws.String("arn:aws:rds:us-west-2:123456789012:cluster:prod-cluster"),
DBClusterIdentifier: aws.String("prod-cluster"),
Engine: aws.String("aurora-mysql"),
EngineVersion: aws.String("8.0.mysql_aurora.3.04.0"),
Status: aws.String("available"),
DBClusterMembers: []types.DBClusterMember{
{
DBInstanceIdentifier: aws.String("prod-instance-1"),
IsClusterWriter: aws.Bool(true),
},
{
DBInstanceIdentifier: aws.String("prod-instance-2"),
IsClusterWriter: aws.Bool(false),
},
},
},
},
instances: map[string][]types.DBInstance{
"arn:aws:rds:us-west-2:123456789012:cluster:prod-cluster": {
{
DBInstanceArn: aws.String("arn:aws:rds:us-west-2:123456789012:db:prod-instance-1"),
DBInstanceIdentifier: aws.String("prod-instance-1"),
DBInstanceClass: aws.String("db.r6g.xlarge"),
DBInstanceStatus: aws.String("available"),
Endpoint: &types.Endpoint{
Address: aws.String("prod-instance-1.xyz.us-west-2.rds.amazonaws.com"),
Port: aws.Int32(3306),
},
},
{
DBInstanceArn: aws.String("arn:aws:rds:us-west-2:123456789012:db:prod-instance-2"),
DBInstanceIdentifier: aws.String("prod-instance-2"),
DBInstanceClass: aws.String("db.r6g.xlarge"),
DBInstanceStatus: aws.String("available"),
Endpoint: &types.Endpoint{
Address: aws.String("prod-instance-2.xyz.us-west-2.rds.amazonaws.com"),
Port: aws.Int32(3306),
},
},
},
},
expectedLabels: []model.LabelSet{
{
model.AddressLabel: model.LabelValue("prod-instance-1.xyz.us-west-2.rds.amazonaws.com:3306"),
rdsLabelClusterDBClusterArn: model.LabelValue("arn:aws:rds:us-west-2:123456789012:cluster:prod-cluster"),
rdsLabelClusterDBClusterIdentifier: model.LabelValue("prod-cluster"),
rdsLabelClusterEngine: model.LabelValue("aurora-mysql"),
rdsLabelClusterEngineVersion: model.LabelValue("8.0.mysql_aurora.3.04.0"),
rdsLabelClusterStatus: model.LabelValue("available"),
rdsLabelInstanceDBInstanceArn: model.LabelValue("arn:aws:rds:us-west-2:123456789012:db:prod-instance-1"),
rdsLabelInstanceDBInstanceIdentifier: model.LabelValue("prod-instance-1"),
rdsLabelInstanceIsClusterWriter: model.LabelValue("true"),
rdsLabelInstanceDBInstanceClass: model.LabelValue("db.r6g.xlarge"),
rdsLabelInstanceDBInstanceStatus: model.LabelValue("available"),
rdsLabelInstanceEndpointAddress: model.LabelValue("prod-instance-1.xyz.us-west-2.rds.amazonaws.com"),
rdsLabelInstanceEndpointPort: model.LabelValue("3306"),
},
{
model.AddressLabel: model.LabelValue("prod-instance-2.xyz.us-west-2.rds.amazonaws.com:3306"),
rdsLabelClusterDBClusterArn: model.LabelValue("arn:aws:rds:us-west-2:123456789012:cluster:prod-cluster"),
rdsLabelClusterDBClusterIdentifier: model.LabelValue("prod-cluster"),
rdsLabelClusterEngine: model.LabelValue("aurora-mysql"),
rdsLabelClusterEngineVersion: model.LabelValue("8.0.mysql_aurora.3.04.0"),
rdsLabelClusterStatus: model.LabelValue("available"),
rdsLabelInstanceDBInstanceArn: model.LabelValue("arn:aws:rds:us-west-2:123456789012:db:prod-instance-2"),
rdsLabelInstanceDBInstanceIdentifier: model.LabelValue("prod-instance-2"),
rdsLabelInstanceIsClusterWriter: model.LabelValue("false"),
rdsLabelInstanceDBInstanceClass: model.LabelValue("db.r6g.xlarge"),
rdsLabelInstanceDBInstanceStatus: model.LabelValue("available"),
rdsLabelInstanceEndpointAddress: model.LabelValue("prod-instance-2.xyz.us-west-2.rds.amazonaws.com"),
rdsLabelInstanceEndpointPort: model.LabelValue("3306"),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mockClient := &mockRDSClient{
clusters: tt.clusters,
instances: tt.instances,
}
d := &RDSDiscovery{
rds: mockClient,
cfg: &RDSSDConfig{
Region: "us-east-1",
RequestConcurrency: 10,
},
}
tg := &targetgroup.Group{}
// Get all cluster ARNs
var clusterARNs []string
for arn := range tt.clusters {
clusterARNs = append(clusterARNs, arn)
}
clusters, err := d.describeAllDBClusters(context.Background())
require.NoError(t, err)
require.Len(t, clusters, len(tt.clusters))
instances := make(map[string][]types.DBInstance)
for _, arn := range clusterARNs {
clusterInstances, err := d.describeDBInstances(context.Background(), arn)
require.NoError(t, err)
instances[arn] = clusterInstances
}
// Build targets like the refresh function does
for _, cluster := range clusters {
writerMap := make(map[string]bool)
for _, member := range cluster.DBClusterMembers {
if member.DBInstanceIdentifier != nil && member.IsClusterWriter != nil {
writerMap[*member.DBInstanceIdentifier] = *member.IsClusterWriter
}
}
clusterInstances := instances[*cluster.DBClusterArn]
for _, instance := range clusterInstances {
labels := model.LabelSet{}
// Add basic cluster labels
if cluster.DBClusterArn != nil {
labels[rdsLabelClusterDBClusterArn] = model.LabelValue(*cluster.DBClusterArn)
}
if cluster.DBClusterIdentifier != nil {
labels[rdsLabelClusterDBClusterIdentifier] = model.LabelValue(*cluster.DBClusterIdentifier)
}
if cluster.Engine != nil {
labels[rdsLabelClusterEngine] = model.LabelValue(*cluster.Engine)
}
if cluster.EngineVersion != nil {
labels[rdsLabelClusterEngineVersion] = model.LabelValue(*cluster.EngineVersion)
}
if cluster.Status != nil {
labels[rdsLabelClusterStatus] = model.LabelValue(*cluster.Status)
}
if cluster.Endpoint != nil {
labels[rdsLabelClusterEndpoint] = model.LabelValue(*cluster.Endpoint)
}
if cluster.Port != nil {
labels[rdsLabelClusterPort] = model.LabelValue(strconv.Itoa(int(*cluster.Port)))
}
if cluster.MasterUsername != nil {
labels[rdsLabelClusterMasterUsername] = model.LabelValue(*cluster.MasterUsername)
}
if cluster.MultiAZ != nil {
labels[rdsLabelClusterMultiAZ] = model.LabelValue(strconv.FormatBool(*cluster.MultiAZ))
}
if cluster.ClusterCreateTime != nil {
labels[rdsLabelClusterClusterCreateTime] = model.LabelValue(cluster.ClusterCreateTime.Format(time.RFC3339))
}
// Cluster tags
for _, tag := range cluster.TagList {
if tag.Key != nil && tag.Value != nil {
labels[model.LabelName(rdsLabelClusterTag+*tag.Key)] = model.LabelValue(*tag.Value)
}
}
// Add basic instance labels
if instance.DBInstanceArn != nil {
labels[rdsLabelInstanceDBInstanceArn] = model.LabelValue(*instance.DBInstanceArn)
}
if instance.DBInstanceIdentifier != nil {
labels[rdsLabelInstanceDBInstanceIdentifier] = model.LabelValue(*instance.DBInstanceIdentifier)
if isWriter, found := writerMap[*instance.DBInstanceIdentifier]; found {
labels[rdsLabelInstanceIsClusterWriter] = model.LabelValue(strconv.FormatBool(isWriter))
}
}
if instance.DBInstanceClass != nil {
labels[rdsLabelInstanceDBInstanceClass] = model.LabelValue(*instance.DBInstanceClass)
}
if instance.DBInstanceStatus != nil {
labels[rdsLabelInstanceDBInstanceStatus] = model.LabelValue(*instance.DBInstanceStatus)
}
if instance.Engine != nil {
labels[rdsLabelInstanceEngine] = model.LabelValue(*instance.Engine)
}
if instance.EngineVersion != nil {
labels[rdsLabelInstanceEngineVersion] = model.LabelValue(*instance.EngineVersion)
}
if instance.AvailabilityZone != nil {
labels[rdsLabelInstanceAvailabilityZone] = model.LabelValue(*instance.AvailabilityZone)
}
if instance.DBClusterIdentifier != nil {
labels[rdsLabelInstanceDBClusterIdentifier] = model.LabelValue(*instance.DBClusterIdentifier)
}
if instance.PubliclyAccessible != nil {
labels[rdsLabelInstancePubliclyAccessible] = model.LabelValue(strconv.FormatBool(*instance.PubliclyAccessible))
}
if instance.InstanceCreateTime != nil {
labels[rdsLabelInstanceInstanceCreateTime] = model.LabelValue(instance.InstanceCreateTime.Format(time.RFC3339))
}
if instance.Endpoint != nil {
if instance.Endpoint.Address != nil {
labels[rdsLabelInstanceEndpointAddress] = model.LabelValue(*instance.Endpoint.Address)
}
if instance.Endpoint.Port != nil {
labels[rdsLabelInstanceEndpointPort] = model.LabelValue(strconv.Itoa(int(*instance.Endpoint.Port)))
}
if instance.Endpoint.HostedZoneId != nil {
labels[rdsLabelInstanceEndpointHostedZoneID] = model.LabelValue(*instance.Endpoint.HostedZoneId)
}
}
// Instance tags
for _, tag := range instance.TagList {
if tag.Key != nil && tag.Value != nil {
labels[model.LabelName(rdsLabelInstanceTag+*tag.Key)] = model.LabelValue(*tag.Value)
}
}
// Set address
if instance.Endpoint != nil && instance.Endpoint.Address != nil && instance.Endpoint.Port != nil {
labels[model.AddressLabel] = model.LabelValue(net.JoinHostPort(*instance.Endpoint.Address, strconv.Itoa(int(*instance.Endpoint.Port))))
}
tg.Targets = append(tg.Targets, labels)
}
}
require.Len(t, tg.Targets, len(tt.expectedLabels))
// Verify each expected label set is present
for _, expectedLabels := range tt.expectedLabels {
found := false
for _, target := range tg.Targets {
if target[model.AddressLabel] == expectedLabels[model.AddressLabel] {
found = true
// Check all expected labels are present with correct values
for key, expectedValue := range expectedLabels {
require.Equal(t, expectedValue, target[key], "Label %s mismatch", key)
}
break
}
}
require.True(t, found, "Expected target with address %s not found", expectedLabels[model.AddressLabel])
}
})
}
}
func TestDescribeAllDBClusters(t *testing.T) {
mockClient := &mockRDSClient{
clusters: map[string]types.DBCluster{
"arn:aws:rds:us-east-1:123456789012:cluster:cluster-1": {
DBClusterArn: aws.String("arn:aws:rds:us-east-1:123456789012:cluster:cluster-1"),
DBClusterIdentifier: aws.String("cluster-1"),
},
"arn:aws:rds:us-east-1:123456789012:cluster:cluster-2": {
DBClusterArn: aws.String("arn:aws:rds:us-east-1:123456789012:cluster:cluster-2"),
DBClusterIdentifier: aws.String("cluster-2"),
},
},
instances: map[string][]types.DBInstance{},
}
d := &RDSDiscovery{
rds: mockClient,
cfg: &RDSSDConfig{
RequestConcurrency: 10,
},
}
clusters, err := d.describeAllDBClusters(context.Background())
require.NoError(t, err)
require.Len(t, clusters, 2)
require.Contains(t, clusters, "arn:aws:rds:us-east-1:123456789012:cluster:cluster-1")
require.Contains(t, clusters, "arn:aws:rds:us-east-1:123456789012:cluster:cluster-2")
}

View File

@ -1135,11 +1135,177 @@ The following meta labels are available on targets during [relabeling](#relabel_
* `__meta_elasticache_cache_cluster_node_endpoint_port`: cache node endpoint port
* `__meta_elasticache_cache_cluster_tag_<tagkey>`: each cache cluster tag value, keyed by tag name
#### `rds`
The `rds` role discovers targets from [AWS RDS](https://aws.amazon.com/rds/)
database instances within clusters. One target is created for each DB instance
within the specified clusters. The endpoint address and port of each instance is used by default.
The IAM credentials used must have the `rds:DescribeDBClusters` and `rds:DescribeDBInstances`
permissions to discover scrape targets.
The following meta labels are available on targets during [relabeling](#relabel_config):
**Cluster labels:**
* `__meta_rds_cluster_activity_stream_kinesis_stream_name`: the name of the Amazon Kinesis data stream used for database activity stream
* `__meta_rds_cluster_activity_stream_kms_key_id`: the AWS KMS key identifier used for encrypting the database activity stream
* `__meta_rds_cluster_activity_stream_mode`: the mode of the database activity stream (sync or async)
* `__meta_rds_cluster_activity_stream_status`: the status of the database activity stream
* `__meta_rds_cluster_allocated_storage`: the allocated storage size in gibibytes (GiB)
* `__meta_rds_cluster_arn`: the Amazon Resource Name (ARN) of the DB cluster
* `__meta_rds_cluster_auto_minor_version_upgrade`: whether automatic minor version upgrades are enabled
* `__meta_rds_cluster_automatic_restart_time`: the time when a stopped cluster will be automatically restarted
* `__meta_rds_cluster_aws_backup_recovery_point_arn`: the ARN of the recovery point in AWS Backup
* `__meta_rds_cluster_backtrack_consumed_change_records`: the number of change records stored for backtrack
* `__meta_rds_cluster_backtrack_window`: the target backtrack window in hours
* `__meta_rds_cluster_backup_retention_period`: the number of days for which automated backups are retained
* `__meta_rds_cluster_capacity`: the current capacity of an Aurora Serverless DB cluster
* `__meta_rds_cluster_character_set_name`: the name of the character set
* `__meta_rds_cluster_clone_group_id`: the ID of the clone group
* `__meta_rds_cluster_cluster_create_time`: the time when the DB cluster was created
* `__meta_rds_cluster_cluster_scalability_type`: the scalability type of the cluster
* `__meta_rds_cluster_copy_tags_to_snapshot`: whether tags are copied from the cluster to snapshots
* `__meta_rds_cluster_cross_account_clone`: whether the DB cluster is a cross-account clone
* `__meta_rds_cluster_database_insights_mode`: the mode of Database Insights
* `__meta_rds_cluster_database_name`: the database name
* `__meta_rds_cluster_db_system_id`: the Oracle system ID (Oracle SID)
* `__meta_rds_cluster_deletion_protection`: whether deletion protection is enabled
* `__meta_rds_cluster_earliest_backtrack_time`: the earliest time to which a database can be restored with backtrack
* `__meta_rds_cluster_earliest_restorable_time`: the earliest time to which a database can be restored
* `__meta_rds_cluster_endpoint`: the endpoint of the DB cluster
* `__meta_rds_cluster_engine_lifecycle_support`: the engine lifecycle support value
* `__meta_rds_cluster_engine_mode`: the engine mode of the cluster (provisioned, serverless, etc.)
* `__meta_rds_cluster_engine_version`: the version of the database engine
* `__meta_rds_cluster_engine`: the database engine of the DB cluster
* `__meta_rds_cluster_global_cluster_identifier`: the identifier of the global cluster
* `__meta_rds_cluster_global_write_forwarding_requested`: whether global write forwarding is requested
* `__meta_rds_cluster_global_write_forwarding_status`: the status of global write forwarding
* `__meta_rds_cluster_hosted_zone_id`: the Route 53 hosted zone ID
* `__meta_rds_cluster_http_endpoint_enabled`: whether the HTTP endpoint is enabled
* `__meta_rds_cluster_iam_database_authentication_enabled`: whether the DB cluster has IAM database authentication enabled
* `__meta_rds_cluster_identifier`: the identifier of the DB cluster
* `__meta_rds_cluster_instance_class`: the compute and memory capacity class of the DB cluster
* `__meta_rds_cluster_io_optimized_next_allowed_modification_time`: the time when the next IO optimization configuration change is allowed
* `__meta_rds_cluster_iops`: the provisioned IOPS (I/O operations per second) value
* `__meta_rds_cluster_kms_key_id`: the AWS KMS key identifier for the encrypted cluster
* `__meta_rds_cluster_latest_restorable_time`: the latest time to which a database can be restored
* `__meta_rds_cluster_local_write_forwarding_status`: the status of local write forwarding
* `__meta_rds_cluster_master_username`: the master username
* `__meta_rds_cluster_monitoring_interval`: the interval in seconds between enhanced monitoring metrics collection
* `__meta_rds_cluster_monitoring_role_arn`: the ARN for the IAM role that permits RDS to send enhanced monitoring metrics to CloudWatch
* `__meta_rds_cluster_multi_az`: whether the DB cluster is multi-AZ
* `__meta_rds_cluster_network_type`: the network type (IPV4 or DUAL)
* `__meta_rds_cluster_parameter_group`: the name of the DB cluster parameter group
* `__meta_rds_cluster_percent_progress`: the progress percentage of the DB cluster operation
* `__meta_rds_cluster_performance_insights_enabled`: whether Performance Insights is enabled
* `__meta_rds_cluster_performance_insights_kms_key_id`: the AWS KMS key identifier for encrypting Performance Insights data
* `__meta_rds_cluster_performance_insights_retention_period`: the retention period for Performance Insights data
* `__meta_rds_cluster_port`: the port the DB cluster is listening on
* `__meta_rds_cluster_preferred_backup_window`: the daily time range during which automated backups are created
* `__meta_rds_cluster_preferred_maintenance_window`: the weekly time range during which system maintenance can occur
* `__meta_rds_cluster_publicly_accessible`: whether the DB cluster is publicly accessible
* `__meta_rds_cluster_reader_endpoint`: the reader endpoint of the DB cluster
* `__meta_rds_cluster_replication_source_identifier`: the identifier of the source DB cluster if this is a read replica
* `__meta_rds_cluster_resource_id`: the AWS Region-unique immutable identifier for the DB cluster
* `__meta_rds_cluster_serverless_v2_platform_version`: the platform version of the Aurora Serverless v2 DB cluster
* `__meta_rds_cluster_status`: the status of the DB cluster
* `__meta_rds_cluster_storage_encrypted`: whether the DB cluster is storage encrypted
* `__meta_rds_cluster_storage_encryption_type`: the storage encryption type
* `__meta_rds_cluster_storage_throughput`: the storage throughput in MiBps
* `__meta_rds_cluster_storage_type`: the storage type
* `__meta_rds_cluster_subnet_group`: the name of the subnet group associated with the DB cluster
* `__meta_rds_cluster_tag_<tagkey>`: each tag value of the DB cluster
* `__meta_rds_cluster_upgrade_rollout_order`: the upgrade rollout order
**Instance labels:**
* `__meta_rds_instance_activity_stream_engine_native_audit_fields_included`: whether engine-native audit fields are included in the database activity stream
* `__meta_rds_instance_activity_stream_kinesis_stream_name`: the name of the Amazon Kinesis data stream used for the database activity stream
* `__meta_rds_instance_activity_stream_kms_key_id`: the AWS KMS key identifier used for encrypting the database activity stream
* `__meta_rds_instance_activity_stream_mode`: the mode of the database activity stream (sync or async)
* `__meta_rds_instance_activity_stream_policy_status`: the policy status of the database activity stream
* `__meta_rds_instance_activity_stream_status`: the status of the database activity stream
* `__meta_rds_instance_allocated_storage`: the allocated storage size in gibibytes (GiB)
* `__meta_rds_instance_arn`: the Amazon Resource Name (ARN) of the DB instance
* `__meta_rds_instance_auto_minor_version_upgrade`: whether automatic minor version upgrades are enabled
* `__meta_rds_instance_automatic_restart_time`: the time when a stopped instance will be automatically restarted
* `__meta_rds_instance_automation_mode`: the automation mode of the instance
* `__meta_rds_instance_availability_zone`: the availability zone of the DB instance
* `__meta_rds_instance_aws_backup_recovery_point_arn`: the ARN of the recovery point in AWS Backup
* `__meta_rds_instance_backup_retention_period`: the number of days for which automated backups are retained
* `__meta_rds_instance_backup_target`: the backup target (region or outposts)
* `__meta_rds_instance_ca_certificate_identifier`: the identifier of the CA certificate for the DB instance
* `__meta_rds_instance_character_set_name`: the name of the character set
* `__meta_rds_instance_class`: the compute and memory capacity class of the DB instance
* `__meta_rds_instance_copy_tags_to_snapshot`: whether tags are copied from the instance to snapshots
* `__meta_rds_instance_custom_iam_instance_profile`: the instance profile associated with the underlying Amazon EC2 instance
* `__meta_rds_instance_customer_owned_ip_enabled`: whether a customer-owned IP address (CoIP) is enabled
* `__meta_rds_instance_database_insights_mode`: the mode of Database Insights
* `__meta_rds_instance_db_cluster_identifier`: the identifier of the DB cluster this instance is a member of
* `__meta_rds_instance_db_name`: the database name
* `__meta_rds_instance_db_system_id`: the Oracle system ID (Oracle SID)
* `__meta_rds_instance_dedicated_log_volume`: whether the DB instance has a dedicated log volume
* `__meta_rds_instance_deletion_protection`: whether deletion protection is enabled
* `__meta_rds_instance_endpoint_address`: the DNS address of the DB instance
* `__meta_rds_instance_endpoint_hosted_zone_id`: the Route 53 hosted zone ID of the endpoint
* `__meta_rds_instance_endpoint_port`: the port that the DB instance listens on
* `__meta_rds_instance_engine_lifecycle_support`: the engine lifecycle support value
* `__meta_rds_instance_engine_version`: the version of the database engine
* `__meta_rds_instance_engine`: the database engine that the DB instance uses
* `__meta_rds_instance_enhanced_monitoring_resource_arn`: the ARN of the Amazon CloudWatch Logs log stream for enhanced monitoring
* `__meta_rds_instance_iam_database_authentication_enabled`: whether IAM database authentication is enabled
* `__meta_rds_instance_identifier`: the identifier of the DB instance
* `__meta_rds_instance_instance_create_time`: the time when the DB instance was created
* `__meta_rds_instance_iops`: the provisioned IOPS (I/O operations per second) value
* `__meta_rds_instance_is_cluster_writer`: whether the instance is the cluster writer (true/false)
* `__meta_rds_instance_is_storage_config_upgrade_available`: whether a storage configuration upgrade is available
* `__meta_rds_instance_kms_key_id`: the AWS KMS key identifier for the encrypted instance
* `__meta_rds_instance_latest_restorable_time`: the latest time to which a database can be restored
* `__meta_rds_instance_license_model`: the license model information
* `__meta_rds_instance_listener_endpoint_address`: the DNS address of the listener endpoint
* `__meta_rds_instance_listener_endpoint_hosted_zone_id`: the Route 53 hosted zone ID of the listener endpoint
* `__meta_rds_instance_listener_endpoint_port`: the port that the listener endpoint listens on
* `__meta_rds_instance_master_username`: the master username
* `__meta_rds_instance_max_allocated_storage`: the upper limit in gibibytes to which storage can be scaled automatically
* `__meta_rds_instance_monitoring_interval`: the interval in seconds between enhanced monitoring metrics collection
* `__meta_rds_instance_monitoring_role_arn`: the ARN for the IAM role that permits RDS to send enhanced monitoring metrics to CloudWatch
* `__meta_rds_instance_multi_az`: whether the DB instance is a Multi-AZ deployment
* `__meta_rds_instance_multi_tenant`: whether the instance is in a multi-tenant configuration
* `__meta_rds_instance_nchar_character_set_name`: the national character set name
* `__meta_rds_instance_network_type`: the network type (IPV4 or DUAL)
* `__meta_rds_instance_percent_progress`: the progress percentage of the DB instance operation
* `__meta_rds_instance_performance_insights_enabled`: whether Performance Insights is enabled
* `__meta_rds_instance_performance_insights_kms_key_id`: the AWS KMS key identifier for encrypting Performance Insights data
* `__meta_rds_instance_performance_insights_retention_period`: the retention period for Performance Insights data
* `__meta_rds_instance_port`: the port that the DB instance listens on
* `__meta_rds_instance_preferred_backup_window`: the daily time range during which automated backups are created
* `__meta_rds_instance_preferred_maintenance_window`: the weekly time range during which system maintenance can occur
* `__meta_rds_instance_promotion_tier`: the order in which an Aurora replica is promoted to primary instance after a failure
* `__meta_rds_instance_publicly_accessible`: whether the DB instance is publicly accessible
* `__meta_rds_instance_read_replica_source_db_cluster_identifier`: the identifier of the source DB cluster if this instance is a read replica
* `__meta_rds_instance_read_replica_source_db_instance_identifier`: the identifier of the source DB instance if this instance is a read replica
* `__meta_rds_instance_replica_mode`: the replica mode (open-read-only or mounted)
* `__meta_rds_instance_resource_id`: the AWS Region-unique immutable identifier for the DB instance
* `__meta_rds_instance_resume_full_automation_mode_time`: the time when the DB instance will resume full automation
* `__meta_rds_instance_secondary_availability_zone`: the secondary availability zone for Multi-AZ instances
* `__meta_rds_instance_status`: the status of the DB instance
* `__meta_rds_instance_storage_encrypted`: whether the DB instance is storage encrypted
* `__meta_rds_instance_storage_encryption_type`: the storage encryption type
* `__meta_rds_instance_storage_throughput`: the storage throughput in MiBps
* `__meta_rds_instance_storage_type`: the storage type
* `__meta_rds_instance_storage_volume_status`: the status of the storage volume
* `__meta_rds_instance_subnet_group`: the name of the subnet group associated with the DB instance
* `__meta_rds_instance_tag_<tagkey>`: each tag value of the DB instance
* `__meta_rds_instance_tde_credential_arn`: the ARN for the TDE encryption key
* `__meta_rds_instance_timezone`: the time zone of the DB instance
* `__meta_rds_instance_upgrade_rollout_order`: the upgrade rollout order
See below for the configuration options for AWS discovery:
```yaml
# The AWS role to use for service discovery.
# Must be one of: ec2, lightsail, ecs, msk, or elasticache.
# Must be one of: ec2, lightsail, ecs, msk, elasticache, or rds.
role: <string>
# The AWS region. If blank, the region from the instance metadata is used.
@ -1175,7 +1341,7 @@ filters:
[ - name: <string>
values: <string>, [...] ]
# List of ECS, ElastiCache, or MSK cluster identifiers (ecs, elasticache, and msk roles only) to discover.
# List of ECS, ElastiCache, MSK, or RDS cluster identifiers (ecs, elasticache, msk, and rds roles only) to discover.
# A List of ARNs of clusters to discover. If empty, all clusters in the region are discovered.
# This can significantly improve performance when you only need to monitor specific clusters/caches.
[ clusters: [<string>, ...] ]

11
go.mod
View File

@ -11,7 +11,7 @@ require (
github.com/KimMachineGun/automemlimit v0.7.5
github.com/alecthomas/kingpin/v2 v2.4.0
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b
github.com/aws/aws-sdk-go-v2 v1.41.1
github.com/aws/aws-sdk-go-v2 v1.41.2
github.com/aws/aws-sdk-go-v2/config v1.32.9
github.com/aws/aws-sdk-go-v2/credentials v1.19.9
github.com/aws/aws-sdk-go-v2/service/ec2 v1.290.0
@ -19,6 +19,7 @@ require (
github.com/aws/aws-sdk-go-v2/service/elasticache v1.51.9
github.com/aws/aws-sdk-go-v2/service/kafka v1.48.0
github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.11
github.com/aws/aws-sdk-go-v2/service/rds v1.116.1
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6
github.com/aws/smithy-go v1.24.1
github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3
@ -143,11 +144,11 @@ require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17
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.18 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 // 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/service/internal/presigned-url v1.13.17 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.10 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.14 // indirect
github.com/beorn7/perks v1.0.1 // indirect

22
go.sum
View File

@ -49,18 +49,18 @@ github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJ
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
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 v1.41.2 h1:LuT2rzqNQsauaGkPK/7813XxcZ3o3yePY0Iy891T2ls=
github.com/aws/aws-sdk-go-v2 v1.41.2/go.mod h1:IvvlAZQXvTXznUPfRVfryiG1fbzE2NGK6m9u39YQ+S4=
github.com/aws/aws-sdk-go-v2/config v1.32.9 h1:ktda/mtAydeObvJXlHzyGpK1xcsLaP16zfUPDGoW90A=
github.com/aws/aws-sdk-go-v2/config v1.32.9/go.mod h1:U+fCQ+9QKsLW786BCfEjYRj34VVTbPdsLP3CHSYXMOI=
github.com/aws/aws-sdk-go-v2/credentials v1.19.9 h1:sWvTKsyrMlJGEuj/WgrwilpoJ6Xa1+KhIpGdzw7mMU8=
github.com/aws/aws-sdk-go-v2/credentials v1.19.9/go.mod h1:+J44MBhmfVY/lETFiKI+klz0Vym2aCmIjqgClMmW82w=
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.18 h1:F43zk1vemYIqPAwhjTjYIz0irU2EY7sOb/F5eJ3HuyM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.18/go.mod h1:w1jdlZXrGKaJcNoL+Nnrj+k5wlpGXqnNrKoP22HvAug=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18 h1:xCeWVjj0ki0l3nruoyP2slHsGArMxeiiaoPN5QZH6YQ=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.18/go.mod h1:r/eLGuGCBw6l36ZRWiw6PaZwPXb6YOj+i/7MizNl5/k=
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/service/ec2 v1.290.0 h1:Ub4CvLWf8wEQ7/pEiqXM9tTsHXf2BokPLwbqEvrmAq0=
@ -69,14 +69,16 @@ github.com/aws/aws-sdk-go-v2/service/ecs v1.72.0 h1:hggRKpv26DpYMOik3wWo1Ty5MkAN
github.com/aws/aws-sdk-go-v2/service/ecs v1.72.0/go.mod h1:pMlGFDpHoLTJOIZHGdJOAWmi+xeIlQXuFTuQxs1epYE=
github.com/aws/aws-sdk-go-v2/service/elasticache v1.51.9 h1:hTgZLyNoDWphZUtTtcvQh0LP6TZO0mtdSfZK/GObDLk=
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/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/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/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk=
github.com/aws/aws-sdk-go-v2/service/kafka v1.48.0 h1:CKRWqysU9INeoi0nTI9gDzDAJk+GatzFduVYxT/wkrw=
github.com/aws/aws-sdk-go-v2/service/kafka v1.48.0/go.mod h1:tWnHS64fg5ydLHivFlCAtEh/1iMNzr56QsH3F+UTwD4=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.11 h1:VM5e5M39zRSs+aT0O9SoxHjUXqXxhbw3Yi0FdMQWPIc=
github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.11/go.mod h1:0jvzYPIQGCpnY/dmdaotTk2JH4QuBlnW0oeyrcGLWJ4=
github.com/aws/aws-sdk-go-v2/service/rds v1.116.1 h1:a5PMhM3lOcu2DKgvYGjhCDToKQnz9VEUo9iSc5+DsyA=
github.com/aws/aws-sdk-go-v2/service/rds v1.116.1/go.mod h1:bMaMwbVQ96bx42kDw/Ko+YiDyT/UCotPO+1RDp6lq7E=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y=
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M=
github.com/aws/aws-sdk-go-v2/service/sso v1.30.10 h1:+VTRawC4iVY58pS/lzpo0lnoa/SYNGF4/B/3/U5ro8Y=