Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support camelcase matchLabels and matchExpressions in TA config #3418

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

davidhaja
Copy link
Contributor

Description:

Currently TA cannot parse camel case matchLabels and matchExpressions fields due to the limitation of gopkg.in/yaml.v2 package and due to the lack of the yaml tag in metav1.LabelSelector struct definition.
The github.com/goccy/go-yaml parses the camel case labelselector fields by default and provides the possibility to parse a specific route in a yaml string. Relying on this feature the TA can unmarshal the only lowercase version of these fields, and then merge together with the originally unmarshalled config.
Therefore both the camel case and the only lowercase fields of the LabelSelector will be handled.

Link to tracking Issue(s): 3350

Testing:
Provided test configs with camelcase matchLabels and matchExpressions

Documentation:
N/A

@davidhaja davidhaja marked this pull request as ready for review November 4, 2024 08:39
@davidhaja davidhaja requested a review from a team as a code owner November 4, 2024 08:39
@jaronoff97
Copy link
Contributor

@davidhaja did you try to use mapstructure? I wonder if that would work more effectively than trying to implement some custom support here. Also, because LabelSelector has json tags, we may be able to use this unmarshaller which does a conversion from yaml -> json and then uses json decoders

@davidhaja
Copy link
Contributor Author

@davidhaja did you try to use mapstructure? I wonder if that would work more effectively than trying to implement some custom support here. Also, because LabelSelector has json tags, we may be able to use this unmarshaller which does a conversion from yaml -> json and then uses json decoders

@jaronoff97 I've tried this package.
I wrote a comment in the original issue about what I've tried before I implemented the change: #3350 (comment)

Copying it here:

The way I see is either we try to support the flexible/case insensitive unmarshalling in TA, or we change both the marshalling and unmarshalling to follow (and require) the k8s camel case convention. The latter one is a breaking change, which I believe requires more effort. (Although probably results in cleaner logic, code as well) In the submitted PR I've made some changes that implement the former option.

I've tried multiple yaml packages, but none of them supports case insensitive parsing of YAML files.

I ended up with github.com/goccy/go-yaml because of its feature set. I've described the feature that my change is using for providing case insensitive parsing.

@jaronoff97
Copy link
Contributor

Ah thank you, and did mapstructure not work at all?

@davidhaja
Copy link
Contributor Author

Ah thank you, and did mapstructure not work at all?

I haven't used mapstructure before your comment. :)
I tried it and could achieve the same functionality with less (and hopefully cleaner) code.
Thanks for the tip.

Comment on lines +153 to +182
func StringToModelDurationHookFunc() mapstructure.DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{},
) (interface{}, error) {
if f.Kind() != reflect.String {
return data, nil
}
if t != reflect.TypeOf(model.Duration(5)) {
return data, nil
}

// Convert it by parsing
return time.ParseDuration(data.(string))
}
}

func decodeSubConfig(t interface{}, dc mapstructure.DecoderConfig) error {
dec, decError := mapstructure.NewDecoder(&dc)
if decError != nil {
return decError
}
if err := dec.Decode(t); err != nil {
return err
}
return nil
}

func flexibleUnmarshal(yamlFile []byte, cfg *Config) error {
Copy link
Contributor

@swiatekm swiatekm Nov 5, 2024

Choose a reason for hiding this comment

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

We should add docstrings to these functions, as well as a longer comment explaining why they exist.

Comment on lines 210 to +217
if err = yaml.Unmarshal(yamlFile, cfg); err != nil {
return fmt.Errorf("error unmarshaling YAML: %w", err)
}

if err := flexibleUnmarshal(yamlFile, cfg); err != nil {
return err
}

Copy link
Contributor

Choose a reason for hiding this comment

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

We should add a comment here explaining why both these statements are necessary. Incidentally, what we're doing here also means that if someone were to use both versions, the camel case version wins.

@nicolastakashi
Copy link

Hey @davidhaja
Thanks for working on that.
I'm a bit concerned about "Two Unmarshal", I'm wondering if in this use case would be simpler just implement a custom Unmarshal function to the entire object.

wdyt?

@nicolastakashi
Copy link

@davidhaja
Copy link
Contributor Author

@davidhaja check how we're doing that on the prometheus receiver: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/770befbc7a5e4f436b67e7bb9ecf42bf3c5e7053/receiver/prometheusreceiver/targetallocator/config.go#L31C6-L31C22

I think we can have a similar approach, wdyt?

I will check this out.
Thank you! :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Collector Instances Not Discovered Due to Case Sensitivity in matchLabels
4 participants