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

🐛 Avoid creating a new scheme object when combining the options #2237

Closed
wants to merge 3 commits into from

Conversation

ccojocar
Copy link

@ccojocar ccojocar commented Mar 14, 2023

This keeps the inherited scheme object created by the cluster when combining the options instead of creating a new one.
That scheme object can be used later on to register schemes.

The inherited scheme was already configured in the cluster object, by creating a new scheme object
for the cache object when combining the schemes will not allow the schemes registered afterwards into the
cluster scheme object to be visible into the cache, and it will result in an error when the controller is started (see below).

Configure the controller as follows:

	ctrlOpts := ctrl.Options{
		NewCache: cache.BuilderWithOptions(cache.Options{
			SelectorsByObject: cache.SelectorsByObject{
				&corev1.Pod{}: {
					Label: labels.SelectorFromSet(labels.Set{
						bindata.EnableRecordingLabel: "true",
					}),
				},
			},
		}),
	}

	mgr, err := ctrl.NewManager(cfg, ctrlOpts)
	if err != nil {
		return fmt.Errorf("create manager: %w", err)
	}

        if err := spodv1alpha1.AddToScheme(mgr.GetScheme()); err != nil {
		return fmt.Errorf("add SPOD config API to scheme: %w", err)
	}

        if err := mgr.Start(sigHandler); err != nil {
		return fmt.Errorf("SPOd error: %w", err)
	}

This fix will allow the controller to start properly instead of getting this error that the scheme is not registered:

E0314 10:44:24.541365 1302480 source.go:146] controller-runtime/source "msg"="kind must be registered to the Scheme" 
"error"="no kind is registered for the type v1alpha1.SecurityProfilesOperatorDaemon in scheme \"pkg/runtime/scheme.go:100"  

You can see more details in kubernetes-sigs/security-profiles-operator#1543 where we encountered this issue.

The inherited scheme was already configured in the cluster object,
by creating a new scheme object for the cache object when combining
the schemes will not allow the scheme registered afterwards into the
cluster to be visible into the cache.
@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Mar 14, 2023
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: ccojocar
Once this PR has been reviewed and has the lgtm label, please assign joelanford for approval. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the size/S Denotes a PR that changes 10-29 lines, ignoring generated files. label Mar 14, 2023
@ccojocar ccojocar changed the title Preserve the inherited scheme object when combining the options 🐛 Avoid creating a new scheme object when combining the options Mar 14, 2023
@alvaroaleman
Copy link
Member

Please provide a testcase for the underlying bug

@ccojocar
Copy link
Author

Please provide a testcase for the underlying bug

I'll try to add one.

@k8s-ci-robot k8s-ci-robot added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. and removed size/S Denotes a PR that changes 10-29 lines, ignoring generated files. labels Mar 15, 2023
@ccojocar
Copy link
Author

@alvaroaleman I added a test case in manager. I hope that's the right place for this.

@@ -1156,6 +1158,45 @@ var _ = Describe("manger.Manager", func() {
)
})

Context("with custom object selector", func() {
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand what exactly this is supposed to test and how this relates to the problem you described in the PR body? It also passes without your codechanges btw. It might be easier to unittest combineScheme directly

Copy link
Author

Choose a reason for hiding this comment

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

I am trying to test the scenario mentioned above in the description.

The main issue is when a scheme is combined using the BuilderWithOptions function, a new scheme object pointer is created and that pointer is configured into the cache -

out = runtime.NewScheme()
.

The issue is that the original scheme object pointer is configured into the manager/controller and not the newly created one by combineScheme, so this means all the schemes added to mgr.GetScheme(), object are no available in the cache. As a result, the controller will fail to start, returning the error "msg"="kind must be registered to the Scheme".

I think just unit-testing the combineScheme doesn't cover this scenario end to end. Also the combineScheme has unittest which already covers this entirely.

Comment on lines +297 to +300
for gvk, t := range new.AllKnownTypes() {
inherited.AddKnownTypeWithName(gvk, reflect.New(t).Interface().(runtime.Object))
}
return out
return inherited
Copy link
Member

Choose a reason for hiding this comment

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

Won't this leak new scheme types into the inherited scheme? What happens here if (somehow) a conflicting scheme is required by a different cache, and that cache shares the same inherited scheme? Will we change the scheme out from under the first cache?

@joelanford
Copy link
Member

This may be an area of the design where we didn't really consider the possibility of schemes changing dynamically.

Should adding more types to mgr.GetScheme() mean anything that inherited that scheme in the past pick up those changes? What if the inheritee has already added that type in its sub-scheme?

This seems like it could absolutely be an issue in the case of a multi-cluster controller where the schemes for different clusters may need to have conflicting registrations. I can't personally think of a valid case where the same GVK in two different clusters (or maybe even different contexts within the same cluster?) would need a different go type associated with them, but it doesn't seem like we should preclude that possibility.

@vincepri vincepri added this to the v0.15.x milestone Apr 12, 2023
@vincepri
Copy link
Member

vincepri commented May 4, 2023

This seems like it could absolutely be an issue in the case of a multi-cluster controller where the schemes for different clusters may need to have conflicting registrations. I can't personally think of a valid case where the same GVK in two different clusters (or maybe even different contexts within the same cluster?) would need a different go type associated with them, but it doesn't seem like we should preclude that possibility.

It should probably be a hard limitation that the scheme for the time being has to be global.

In terms of this PR, I'm ok allowing the Scheme to be shared, given that's one of those things that's already passed around

@alvaroaleman
Copy link
Member

The corresponding core was removed in #2300, so this isn't needed anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. size/M Denotes a PR that changes 30-99 lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants