Skip to content

[receiver/k8sobjectsreceiver] Properly deep copy the K8sObjectsConfig struct #39644

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

Merged
merged 12 commits into from
Jun 4, 2025

Conversation

petern48
Copy link
Contributor

Description

In newReceiver() , we perform a shallow copy of the K8sObjectsConfig objects instead of a deep one.

Link to tracking issue

Fixes #39553

Testing

Added TestDeepCopy inside of config_test.go

@petern48
Copy link
Contributor Author

The original issue advised to refactor the config struct to separate mutable fields into a new struct. I tried that, but I felt it was much cleaner to simply keep it as is. I'm not sure if there was reasoning other than code readability, but I'm happy to change this if reviewers still want the refactor.

@@ -129,3 +129,40 @@ func (c *Config) getValidObjects() (map[string][]*schema.GroupVersionResource, e
}
return validObjects, nil
}

func (k *K8sObjectsConfig) DeepCopy() *K8sObjectsConfig {
copied := &K8sObjectsConfig{
Copy link
Member

Choose a reason for hiding this comment

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

Re-using the same struct might not be the best option here. I would define a new one called for example K8sObjectTarget. That'd probably be with non-public fields unless there is need for having them public.

Apart from making it clear in the name that it's not about "Config" anymore will ensure that we won't have issues in the future in case we want to diverge the 2 different structs. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it. Sounds good. The one thing is that we have to keep the fields public for mapstructure to work properly. (I tried unexporting to namespaces and excludeWatchType and things broke)

Copy link
Member

@ChrsMark ChrsMark left a comment

Choose a reason for hiding this comment

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

Thank's @petern48 for working on this! I left few suggestions.

@atoulme atoulme marked this pull request as draft April 29, 2025 17:49
@atoulme
Copy link
Contributor

atoulme commented Apr 29, 2025

Moving to draft while you review suggestions, please mark ready to review once done.

@petern48 petern48 marked this pull request as ready for review April 29, 2025 19:15
@petern48 petern48 requested a review from ChrsMark April 29, 2025 19:16
@petern48
Copy link
Contributor Author

@ChrsMark I incorporated your feedback. Let me know if this is what you had in mind.

FieldSelector string `mapstructure:"field_selector"`
Interval time.Duration `mapstructure:"interval"`
ResourceVersion string `mapstructure:"resource_version"`
K8sObjectTarget `mapstructure:",squash"`
Copy link
Member

Choose a reason for hiding this comment

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

Why this needs to be part of the config? I was thinking that those 2 should be independent. Then you can do the following to instantiate the targets:

targetObject := newTargetObject(configObject)

After this point we don't need the K8sObjectsConfig object anymore and we only work with the targetObject which is of type K8sObjectTarget.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I see. It makes a lot more sense that you were considering separating the objects altogether. However, I'm not quite seeing where you imagine this being stored. Don't we still need to maintain one K8sObjectTarget per corresponding object?

The only way I can see doing that while keeping it outside of K8ObjectsConfig is by adding a new corresponding slice inside of Config, like below. Is that what you had in mind?

type Config struct {
	k8sconfig.APIConfig `mapstructure:",squash"`

	Objects []*K8sObjectsConfig `mapstructure:"objects"`
+	Targets []*K8sObjectTarget `mapstructure:"targets"`

Or do you mean to somehow separate it from the Config object as well? Although that doesn't add up since the example line you gave indicates the config object would have that info to create the new copy.
targetObject := newTargetObject(configObject)

Copy link
Member

Choose a reason for hiding this comment

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

I think we already store the "copies" at

as part of the k8sobjectsreceiver instance. I would just use this field and have the full/deep copy to happen at newReceiver.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think we already store the "copies" at

as part of the k8sobjectsreceiver instance. I would just use this field and have the full/deep copy to happen at newReceiver.

Sorry, I'm still confused. This sounds like what the current implementation is doing. Maybe I'm missing something, but I don't see how we can accomplish this while fulfilling your earlier request of moving K8sObjectTarget outside of the config struct (K8sObjectsConfig). Wouldn't K8sObjectTarget have to be inside of K8sObjectsConfig if we want to re-use this field?

Copy link
Member

Choose a reason for hiding this comment

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

My suggestion is to make objects field of type K8sObjectTarget. Then objects will be deep copies of the initially provided K8sObjectsConfig objects of the Config.

After this point we only interact and handle with the objects field (type K8sObjectTarget) and we don't need the K8sObjectsConfig any more.

Otherwise we should only interact with K8sObjectsConfig in read-only mode to unpack the configurations and create mutable K8sObjectTarget objects that will be the ones we will be using during the execution of the receiver.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've looked into it, and I don't think it's quite possible to never touch the kr.config object after newReceiver() since many of the struct methods use fields that are inside of immutable fields in k8sObjectConfig, that aren't in K8sObjectTarget. We need some way to access those fields in the struct methods, so using the k8sObjectConfigs through kr.config looks like the easiest way. Let me know if you think otherwise. I'll need a little more time to figure out and propose a clean way to implement this.

Copy link
Member

Choose a reason for hiding this comment

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

use fields that are inside of immutable fields in k8sObjectConfig, that aren't in K8sObjectTarget

Why then we don't make them part of the K8sObjectTarget as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well then, at that point, we've gone full circle and have a struct named K8sObjectTarget that's the same as the k8sObjectConfig struct as we originally did, so effectively all we would be doing is renaming the struct... I'm honestly not seeing tremendous value from trying to refactor the code anymore. If we remove the K8sObjectTarget struct and simply deep copy the original k8sObjectConfig, then we can fix the deep copy issue without adding additional complexity. WDYT?

Copy link
Member

@ChrsMark ChrsMark Jun 2, 2025

Choose a reason for hiding this comment

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

I see. The reason for introducing a 2nd struct was described at #39644 (comment).

However, let's solve the deep copy issue for now using the same struct (K8sObjectConfig) as you suggest (removing the use of K8sObjectTarget since it's essentially the same) and maybe we can revisit in a follow-up PR.

Thank's!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, yeah. I just reverted to the deep copy-only code.

@atoulme atoulme marked this pull request as draft May 9, 2025 18:24
@atoulme
Copy link
Contributor

atoulme commented May 9, 2025

Please address the feedback of the codeowner and mark ready for review once done.

Copy link
Contributor

This PR was marked stale due to lack of activity. It will be closed in 14 days.

@github-actions github-actions bot added Stale and removed Stale labels May 28, 2025
@petern48 petern48 force-pushed the refactor_k8_config branch from f1c78f9 to 35c3fb7 Compare June 2, 2025 18:43
@petern48 petern48 marked this pull request as ready for review June 2, 2025 19:04
@petern48 petern48 requested a review from ChrsMark June 2, 2025 19:05
Copy link
Member

@ChrsMark ChrsMark left a comment

Choose a reason for hiding this comment

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

LGTM

component: k8sobjectsreceiver

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Properly deep copy the K8sObjectsConfig struct in the k8sobjectsreceiver and separate mutable fields
Copy link
Member

Choose a reason for hiding this comment

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

This would need an update? Probably we don't need a change-log at all since it's a code refactoring.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got it, just removed it. You'll need to add the Skip changelog label for CI to pass, as mentioned here.

@TylerHelmuth TylerHelmuth added the Skip Changelog PRs that do not require a CHANGELOG.md entry label Jun 4, 2025
@TylerHelmuth TylerHelmuth merged commit 32aec72 into open-telemetry:main Jun 4, 2025
186 of 187 checks passed
@github-actions github-actions bot added this to the next release milestone Jun 4, 2025
rockdaboot pushed a commit to rockdaboot/opentelemetry-collector-contrib that referenced this pull request Jun 10, 2025
… struct (open-telemetry#39644)

<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description
In `newReceiver()` , we perform a shallow copy of the K8sObjectsConfig
objects instead of a deep one.

<!-- Issue number (e.g. open-telemetry#1234) or full URL to issue, if applicable. -->
#### Link to tracking issue
Fixes open-telemetry#39553

<!--Describe what testing was performed and which tests were added.-->
#### Testing
Added `TestDeepCopy` inside of `config_test.go`

<!--Describe the documentation added.
#### Documentation
-->

<!--Please delete paragraphs that you did not use before submitting.-->
dd-jasminesun pushed a commit to DataDog/opentelemetry-collector-contrib that referenced this pull request Jun 23, 2025
… struct (open-telemetry#39644)

<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue.
Ex. Adding a feature - Explain what this achieves.-->
#### Description
In `newReceiver()` , we perform a shallow copy of the K8sObjectsConfig
objects instead of a deep one.

<!-- Issue number (e.g. #1234) or full URL to issue, if applicable. -->
#### Link to tracking issue
Fixes open-telemetry#39553

<!--Describe what testing was performed and which tests were added.-->
#### Testing
Added `TestDeepCopy` inside of `config_test.go`

<!--Describe the documentation added.
#### Documentation
-->

<!--Please delete paragraphs that you did not use before submitting.-->
@petern48 petern48 deleted the refactor_k8_config branch June 30, 2025 00:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
receiver/k8sobjects Skip Changelog PRs that do not require a CHANGELOG.md entry waiting-for-code-owners
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[receiver/k8sobjectsreceiver] Refactor Config Struct to Separate Mutable Fields
6 participants