Repository: prometheus/prometheus
File: discovery/aws/ec2.go (Go)

Code before:

// NewDiscovererMetrics implements discovery.Config.
func (*EC2SDConfig) NewDiscovererMetrics(_ prometheus.Registerer, rmi discovery.RefreshMetricsInstantiator) discovery.DiscovererMetrics {
	return &ec2Metrics{
		refreshMetrics: rmi,
	}
}

// Name returns the name of the EC2 Config.
func (*EC2SDConfig) Name() string { return "ec2" }

// NewDiscoverer returns a Discoverer for the EC2 Config.
func (c *EC2SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) {
	return NewEC2Discovery(c, opts.Logger, opts.Metrics)
}

// UnmarshalYAML implements the yaml.Unmarshaler interface for the EC2 Config.
func (c *EC2SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
	*c = DefaultEC2SDConfig
	type plain EC2SDConfig
	err := unmarshal((*plain)(c))
	if err != nil {
		return err
	}
	if c.Region == "" {
		// TODO(@sysadmind): Should we get a context from somewhere?
		ctx := context.TODO()
		cfg, err := awsConfig.LoadDefaultConfig(ctx)
		if err != nil {
			return err
		}

		if cfg.Region != "" {
			// If the region is already set in the config, use it.
			// This can happen if the user has set the region in the AWS config file or environment variables.
			c.Region = cfg.Region
		} else {
			// Attempt to get the region from the instance metadata service.
			metaClient := imds.NewFromConfig(cfg)
			result, err := metaClient.GetRegion(ctx, &imds.GetRegionInput{})
			if err != nil {
				return errors.New("EC2 SD configuration requires a region")
			}
			c.Region = result.Region
		}
	}

	for _, f := range c.Filters {
		if len(f.Values) == 0 {
			return errors.New("EC2 SD configuration filter values cannot be empty")
		}

Review comment (security):
I tried to check what it is used for, but it's hard every lower level function I checked ignores the context (e.g. https://github.com/aws/aws-sdk-go-v2/blob/main/config/load_options.go#L937, https://github.com/aws/aws-sdk-go-v2/blob/main/config/load_options.go#L269 and hundrends others...)  -- every config resolver and loader can use context.

I [found](https://github.com/search?q=repo%3Aaws%2Faws-sdk-go-v2+%22ctx.%22+path%3Aconfig%2F**&type=code), one passes some credentials internally, so we can ignore it,  one offers [cancels/deadlines on metadata checks](https://github.com/aws/aws-sdk-go-v2/blob/1eea3dbe07dffcef2fdba6f8e2206b32b91d8a83/config/defaultsmode.go#L28)

We can't easily pass any EXIT signal from the process, so I would just pass context.Background here. We could add our own timeout if it becomes a problem here, but SDK has some already https://github.com/aws/aws-sdk-go-v2/blob/1eea3dbe07dffcef2fdba6f8e2206b32b91d8a83/feature/ec2/imds/api_client.go#L275


Code after:
// NewDiscovererMetrics implements discovery.Config.
func (*EC2SDConfig) NewDiscovererMetrics(_ prometheus.Registerer, rmi discovery.RefreshMetricsInstantiator) discovery.DiscovererMetrics {
	return &ec2Metrics{
		refreshMetrics: rmi,
	}
}

// Name returns the name of the EC2 Config.
func (*EC2SDConfig) Name() string { return "ec2" }

// NewDiscoverer returns a Discoverer for the EC2 Config.
func (c *EC2SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) {
	return NewEC2Discovery(c, opts.Logger, opts.Metrics)
}

// UnmarshalYAML implements the yaml.Unmarshaler interface for the EC2 Config.
func (c *EC2SDConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
	*c = DefaultEC2SDConfig
	type plain EC2SDConfig
	err := unmarshal((*plain)(c))
	if err != nil {
		return err
	}
	if c.Region == "" {
		cfg, err := awsConfig.LoadDefaultConfig(context.Background())
		if err != nil {
			return err
		}

		if cfg.Region != "" {
			// If the region is already set in the config, use it.
			// This can happen if the user has set the region in the AWS config file or environment variables.
			c.Region = cfg.Region
		}
	}

	if c.Region == "" {
		return errors.New("EC2 SD configuration requires a region")
	}

	for _, f := range c.Filters {
		if len(f.Values) == 0 {
			return errors.New("EC2 SD configuration filter values cannot be empty")
		}
	}
	return c.HTTPClientConfig.Validate()
}

type ec2Client interface {
	DescribeAvailabilityZones(ctx context.Context, params *ec2.DescribeAvailabilityZonesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeAvailabilityZonesOutput, error)
	DescribeInstances(ctx context.Context, params *ec2.DescribeInstancesInput, optFns ...func(*ec2.Options)) (*ec2.DescribeInstancesOutput, error)