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

Retrieve every k8s resource generated by a Carvel package installation (alt) #4068

Merged
merged 9 commits into from
Jan 20, 2022

Conversation

antgamdia
Copy link
Contributor

@antgamdia antgamdia commented Jan 12, 2022

(Alternative solution for the same purpose of #4062)

Description of the change

This PR removes the workaround we implemented when we released it last time and implements a generic way to retrieve all the kubernetes resources matching a certain label. This way we can fetch every k8s resource created as a result of a Carvel PackageInstall.

Benefits

The retrieved resources for a carvel package will now include every possible one, not a fixed list of pods, svc and deployments.

New: it uses the Kapp code directly, instead of manually iterating over the k8s resources manually.

Possible drawbacks

Hitting the k8s APIs for querying every possible available resource is expensive in time if the QPS is low. Using a reasonable value (as OpenShift does) of 50 QPS, the waiting time decreases.

Applicable issues

Additional information

** Testing pending, I'm not able to get it to work so far **

PR in action:

carvelResourcesKapp2.mp4

Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>
Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>
Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>
Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>
Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>
Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>
Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>
@antgamdia antgamdia marked this pull request as ready for review January 13, 2022 18:52
Comment on lines +1665 to +1668
qps: "50.0"
## @param kubeappsapis.burst KubeappsAPIs Kubernetes API client Burst limit
##
burst: "15"
burst: "100"
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Limits increased with the same OpenShift defaults; the waiting time is now reasonable, though increasing the QPS will of course benefit the response time when querying the k8s API.

Comment on lines +90 to +92
// Pass the REST client to the (custom) kapp factory
configFactory := NewConfigurableConfigFactoryImpl()
configFactory.ConfigureRESTConfig(config)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the part currently unsupported by the Carvel team. They recommended we just implement our custom struct satisfying their interface, don't know if they will be willing to accept a PR for that, though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like a simple and useful change, so not sure why not.

Comment on lines +111 to +121
// Getting namespaced clients (e.g., for fetching an App)
supportingNsObjs, err := kappcmdapp.FactoryClients(depsFactory, kappcmdcore.NamespaceFlags{Name: namespace}, resourceTypesFlags, logger.NewNoopLogger())
if err != nil {
return ctlapp.Apps{}, ctlres.IdentifiedResources{}, nil, ctlres.ResourceFilter{}, status.Errorf(codes.FailedPrecondition, "unable to get config due to: %v", err)
}

// Getting non-namespaced clients (e.g., for fetching every k8s object in the cluster)
supportingObjs, err := kappcmdapp.FactoryClients(depsFactory, kappcmdcore.NamespaceFlags{Name: ""}, resourceTypesFlags, logger.NewNoopLogger())
if err != nil {
return ctlapp.Apps{}, ctlres.IdentifiedResources{}, nil, ctlres.ResourceFilter{}, status.Errorf(codes.FailedPrecondition, "unable to get config due to: %v", err)
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the only way I've seen to able to query the resources that have been created in a different namespace (like Harbor). I can't find any reference to this freshly created namespace in any of the kapp-related CRs.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, I'm missing some info here? Does the harbor package create resources in the target namespace (of the package install) as well as another namespace? And why can't we see the client that the Kapp CLI creates to do the same command? (which you've mentioned below, just not sure why we can't also see the same for this issue here).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, sorry, let me explain myself better.

When we install a package in Kubeapps in a certain namespace (e.g., Harbor in default ns), we are creating: a PackageInstall in default and a ConfigMap in default.
Additionally, kapp, under the scenes, is gonna create an App in default. Neither the PackageInstall nor the App has references to anything different from the namespace default.

However, when reconciling, this App starts creating the resources, which happen to be a namespace harbor and a set of several resources that are also created in this harbor namespace.

Perhaps there's something I missed in the Kapp CLI code. In fact, they have a --dangerous-scope-to-fallback-allowed-namespaces flag, so I guess, for the name it has, they are not allowing querying everything.
If only I had a reference to this harbor namespace somewhere, I'd be able to query the current default ns as well as harbor as this kind of "fallback allowed namespace".

Comment on lines -263 to -266
if app.Status.Inspect != nil {
inspectStdout = app.Status.Inspect.Stdout
inspectStderr = app.Status.Inspect.Stderr
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removing the text-plain output of the resources, as it becomes redundant after this PR.

@@ -399,3 +401,55 @@ func (s *Server) updatePkgInstall(ctx context.Context, cluster, namespace string
}
return &pkgInstall, nil
}

// inspectKappK8sResources returns the list of k8s resources matching the given listOptions
func (s *Server) inspectKappK8sResources(ctx context.Context, cluster, namespace, packageId string) ([]*corev1.ResourceRef, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is mostly the code that kapp CLI itself uses for inspecting an app, but adapted to our needs.

Comment on lines 3166 to 3170
kappClientsGetter: func(ctx context.Context, cluster, namespace string) (ctlapp.Apps, ctlres.IdentifiedResources, *kappcmdapp.FailingAPIServicesPolicy, ctlres.ResourceFilter, error) {
// Create a fake DepsFactory and pass it the fake k8s clients
depsFactory := NewFakeDepsFactoryImpl()
depsFactory.SetCoreClient(typedClient)
depsFactory.SetDynamicClient(dynClient)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the approach I've been able to come up with for faking the kapp internal clients. Even if it kinda works, it returns an empty list of resources found :(
If you have any suggestions or can give it a try, I'd really appreciate it!

Copy link
Contributor

Choose a reason for hiding this comment

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

So I setup the vscode debugger (which was pretty simple - hadn't used it much before) and traced this down from your call to resources, err := resourcesClient.List(labelSelector, nil) in server_data_resources.go. From there:

Looking at the implementation, the core issue is the function which collects the resource types is using the discovery api, so in your test it's not returning any APIs. Might be worth re-checking the kapp test code to see how they setup the discovery API in tests or otherwise ensure certain resource types (pods, in your case) are returned.

HTH!

Copy link
Contributor

@absoludity absoludity left a comment

Choose a reason for hiding this comment

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

+1ing in case I don't get back to it today, as it looks great. Hopefully I'll get to look at the issue for the tests later too.

})
}
return refs, nil
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Excellent, thanks for the very clear code in this part :)

Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>
Comment on lines +3140 to +3144
typedClient := typfake.NewSimpleClientset(tc.existingTypedObjects...)

// We cast the dynamic client to a fake client, so we can set the response
fakeDiscovery, _ := typedClient.Discovery().(*disfake.FakeDiscovery)
fakeDiscovery.Fake.Resources = apiResources
Copy link
Contributor Author

Choose a reason for hiding this comment

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

After creating a bunch of custom types... it just came down to this approach.
The crux was just in casting the Discovery() result so that we can manually leverage from the builtin .Fake.Resources

Thanks for the pointer, Michael. Best solution when you're stuck is have some rest and start over the next day with a fresh mind :P

Signed-off-by: Antonio Gamez Diaz <agamez@vmware.com>

Conflicts:
	go.mod
	go.sum
@antgamdia antgamdia merged commit 106d417 into vmware-tanzu:master Jan 20, 2022
@antgamdia antgamdia deleted the 3854-getResources-alt branch January 20, 2022 16:01
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.

Add a proper implementation for GetInstalledPackageResourceRefs
2 participants