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

Code before:
	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 && len(c.PromoteResourceAttributes) > 0 {
		return errors.New("'promote_all_resource_attributes' and 'promote_resource_attributes' cannot be configured simultaneously")
	}

	if err := validateAttributes(c.IgnoreResourceAttributes); err != nil {
		return fmt.Errorf("invalid 'ignore_resource_attributes': %w", err)
	}
	if err := validateAttributes(c.PromoteResourceAttributes); err != nil {
		return fmt.Errorf("invalid 'promote_resource_attributes': %w", err)
	}
	return nil
}

func validateAttributes(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
	}
	return err
}


Review comment (security):
It confused me that `validateAttributes` would modify the input. `sanitizeAttributes` is more descriptive of what the function actually does, and thus more readable:
```suggestion
func sanitizeAttributes(attributes []string) error {
```

Code after:
	// `seconds` and a separate series `foo.bar` with unit `milliseconds`.
	//
	// As a result, this setting is experimental and currently, should not be used in
	// production systems.
	//
	// TODO(ArthurSens): Mention `type-and-unit-labels` feature (https://github.com/prometheus/proposals/pull/39) once released, as potential mitigation of the above risks.
	NoTranslation translationStrategyOption = "NoTranslation"
)

// OTLPConfig is the configuration for writing to the OTLP endpoint.
type OTLPConfig struct {
	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)