Repository: prometheus/prometheus
File: config/config.go (Go)

Code before:
	PromoteAllResourceAttributes      bool                      `yaml:"promote_all_resource_attributes,omitempty"`
	PromoteResourceAttributes         []string                  `yaml:"promote_resource_attributes,omitempty"`
	IgnoreResourceAttributes          []string                  `yaml:"ignore_resource_attributes,omitempty"`
	TranslationStrategy               translationStrategyOption `yaml:"translation_strategy,omitempty"`
	KeepIdentifyingResourceAttributes bool                      `yaml:"keep_identifying_resource_attributes,omitempty"`
	ConvertHistogramsToNHCB           bool                      `yaml:"convert_histograms_to_nhcb,omitempty"`
}

// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (c *OTLPConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
	*c = DefaultOTLPConfig
	type plain OTLPConfig
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}

	if c.PromoteAllResourceAttributes {
		if len(c.PromoteResourceAttributes) > 0 {
			return errors.New("'promote_all_resource_attributes' and 'promote_resource_attributes' cannot be configured simultaneously")
		}
		if err := sanitizeAttributes(c.IgnoreResourceAttributes); err != nil {
			return fmt.Errorf("invalid 'ignore_resource_attributes': %w", err)
		}
	} else {
		if len(c.IgnoreResourceAttributes) > 0 {
			return errors.New("'promote_resource_attributes' and 'ignore_resource_attributes' cannot be configured simultaneously")
		}
		if err := sanitizeAttributes(c.PromoteResourceAttributes); err != nil {
			return fmt.Errorf("invalid 'promote_resource_attributes': %w", err)
		}
	}

	return nil
}

func sanitizeAttributes(attributes []string) error {
	seen := map[string]struct{}{}
	var err error
	for i, attr := range attributes {
		attr = strings.TrimSpace(attr)
		if attr == "" {
			err = errors.Join(err, errors.New("empty OTel resource attribute"))
			continue
		}
		if _, exists := seen[attr]; exists {
			err = errors.Join(err, fmt.Errorf("duplicated OTel resource attribute %q", attr))
			continue
		}

		seen[attr] = struct{}{}
		attributes[i] = attr

Review comment (suggestion):
Sorry for missing this before, but this parameter only makes sense if `promote_all_resource_attributes'` is true.
```suggestion
			return errors.New("'ignore_resource_attributes' cannot be configured unless 'promote_all_resource_attributes' is true")
```

Code after:
	PromoteAllResourceAttributes      bool                      `yaml:"promote_all_resource_attributes,omitempty"`
	PromoteResourceAttributes         []string                  `yaml:"promote_resource_attributes,omitempty"`
	IgnoreResourceAttributes          []string                  `yaml:"ignore_resource_attributes,omitempty"`
	TranslationStrategy               translationStrategyOption `yaml:"translation_strategy,omitempty"`
	KeepIdentifyingResourceAttributes bool                      `yaml:"keep_identifying_resource_attributes,omitempty"`
	ConvertHistogramsToNHCB           bool                      `yaml:"convert_histograms_to_nhcb,omitempty"`
}

// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (c *OTLPConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
	*c = DefaultOTLPConfig
	type plain OTLPConfig
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}

	if c.PromoteAllResourceAttributes {
		if len(c.PromoteResourceAttributes) > 0 {
			return errors.New("'promote_all_resource_attributes' and 'promote_resource_attributes' cannot be configured simultaneously")
		}
		if err := sanitizeAttributes(c.IgnoreResourceAttributes, "ignored"); err != nil {
			return fmt.Errorf("invalid 'ignore_resource_attributes': %w", err)
		}
	} else {
		if len(c.IgnoreResourceAttributes) > 0 {
			return errors.New("'ignore_resource_attributes' cannot be configured unless 'promote_all_resource_attributes' is true")
		}
		if err := sanitizeAttributes(c.PromoteResourceAttributes, "promoted"); err != nil {
			return fmt.Errorf("invalid 'promote_resource_attributes': %w", err)
		}
	}

	return nil
}

func sanitizeAttributes(attributes []string, adjective string) error {
	seen := map[string]struct{}{}
	var err error
	for i, attr := range attributes {
		attr = strings.TrimSpace(attr)
		if attr == "" {
			err = errors.Join(err, fmt.Errorf("empty %s OTel resource attribute", adjective))
			continue
		}
		if _, exists := seen[attr]; exists {
			err = errors.Join(err, fmt.Errorf("duplicated %s OTel resource attribute %q", adjective, attr))
			continue
		}

		seen[attr] = struct{}{}
		attributes[i] = attr