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

Adding ability for clients, cache and watcher to work with unstructured #101

Merged
merged 8 commits into from
Sep 20, 2018

Conversation

shawn-hurley
Copy link

Attempt to fix #17 and I think it also addresses #33

/cc @DirectXMan12 @pwittrock can you PTAL and let me know if I am even on the right track?

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Jul 31, 2018
Copy link
Contributor

@DirectXMan12 DirectXMan12 left a comment

Choose a reason for hiding this comment

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

a few comments inline, but generally on the right track.
Needs tests around unstructured as well.

@@ -73,6 +75,21 @@ func (c *CacheReader) Get(_ context.Context, key client.ObjectKey, out runtime.O
// TODO(directxman12): revisit the decision to always deepcopy
obj = obj.(runtime.Object).DeepCopyObject()

// If out is supposed to be unstructured handle correctly.
if o, ok := out.(*unstructured.Unstructured); ok {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I'm following the logic behind the marshalling and unmarshalling... shouldn't the object already be an unstructured? We should just be able to do the reflection hack...

}
jsonInfo.Serializer = unstructured.UnstructuredJSONScheme

cfg.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(jsonInfo)
Copy link
Contributor

Choose a reason for hiding this comment

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

the only thing that changes here is the negotiated serializer setup, right? Refactor the common bits out into a private function so we're not copying and pasting code around.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, do you have to care about status, like the old dynamic client did?

@@ -46,7 +47,10 @@ type clientCache struct {

// resourceByType caches type metadata
resourceByType map[reflect.Type]*resourceMeta
mu sync.RWMutex
// resourceByGVK caches type metadat for unstructured
Copy link
Contributor

Choose a reason for hiding this comment

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

typo: "metadata"

Copy link
Contributor

Choose a reason for hiding this comment

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

also, call this unstructuredResourceByGVK, so it's clear why it's separate

// getResource returns the resource meta information for the given type of object.
// If the object is a list, the resource represents the item's type instead.
func (c *clientCache) getResource(obj runtime.Object) (*resourceMeta, error) {
func (c *clientCache) handleResourceByGVK(obj runtime.Object) (*resourceMeta, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

leave these as get -- it's a bit clearer, IMO

@DirectXMan12
Copy link
Contributor

So, I've been thinking about this a bit more, and I want to make sure we're not introducing a foot-gun with (specifically) the unstructured cache part. Client I'm totally fine with.

I'm curious as to the use case for unstructured cache. The normal case for unstructured client is something like "I want to access a bunch of object regardless of the kind". But if you do that with the caching client, you'll get a new watch that you might not want (you might accidentally end up caching the entire cluster, for instance, if you're running something like GC). You might even get a duplicate cache (one for the concrete object, one for the unstructured).

We should careful here...

@shawn-hurley
Copy link
Author

@DirectXMan12 and I discussed offline the problems that were identified. I will be re-working this based on the suggestions and help.

@k8s-ci-robot k8s-ci-robot added size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Aug 9, 2018
By("creating the object a second time")
err = cl.Create(context.TODO(), u)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("already exists"))
Copy link
Contributor

Choose a reason for hiding this comment

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

you should be able to use kapierrors.IsNotFound here.

@shawn-hurley shawn-hurley changed the title [WIP] - Adding ability for clients, cache and watcher to work with unstructured Adding ability for clients, cache and watcher to work with unstructured Aug 10, 2018
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Aug 10, 2018
@shawn-hurley
Copy link
Author

@DirectXMan12 @droot @pwittrock PTAL I think this is ready for review

Copy link
Contributor

@DirectXMan12 DirectXMan12 left a comment

Choose a reason for hiding this comment

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

looks a lot better. couple nits inline. Also want to try something this afternoon around code structure (abstracting over the details of structured vs unstructured to make code flow more straightforward). I'll let you know if it seems reasonable -- have to play with it a bit.b

@@ -73,6 +75,10 @@ type InformersMap struct {
// informersByGVK is the cache of informers keyed by groupVersionKind
informersByGVK map[schema.GroupVersionKind]*MapEntry

// unstructuredInformerByGVK is a cache of informers for unstructured types
// keyed by groupVersionKind
Copy link
Contributor

Choose a reason for hiding this comment

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

add a note as to why they're kept separately -- we don't want to request a structured object but get an unstructured informer and vice versa

return i, ok

}

// Get will create a new Informer and add it to the map of InformersMap if none exists. Returns
// the Informer from the map.
Copy link
Contributor

Choose a reason for hiding this comment

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

we need a note that structured and unstructured are treated distinctly

//Copied from deprecated-dynamic/bad_debt.go
// dynamicCodec is a codec that wraps the standard unstructured codec
// with special handling for Status objects.
// Deprecated only used by test code and its wrong
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 have a TODO to figure out why this is bad...

@droot
Copy link
Contributor

droot commented Aug 14, 2018

@shawn-hurley will take a look today. Thanks

@shawn-hurley shawn-hurley force-pushed the unstructured branch 2 times, most recently from 934f564 to 4a4996f Compare August 15, 2018 13:27
Copy link
Contributor

@droot droot left a comment

Choose a reason for hiding this comment

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

Made a first pass, have a few minor comments. Learnt a few new things :). Will take another look, have a few doubts about unstructured, so want to read a bit more.

if err != nil {
return nil
}

gvk, err := apiutil.GVKForObject(out, ip.Scheme)
Copy link
Contributor

Choose a reason for hiding this comment

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

I am assuming GVKForObject has special handling for unstructured.

Copy link
Author

Choose a reason for hiding this comment

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

I don't think that it does. unstructured does conform to the runtime.Object interface and has the methods to retrieve the GVK from that interface at least that is my understanding.

Is there an edge case that I am not considering?

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it. No, was just curious.

return nil
}

// WaitForCacheSync waits until all the caches have been synced
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: . at the end of comment :)

@@ -228,3 +237,38 @@ func (ip *InformersMap) newListWatch(gvk schema.GroupVersionKind) (*cache.ListWa
},
}, nil
}

// createUnstructuredClient is a ClientCreatorFunc for use with structured
Copy link
Contributor

Choose a reason for hiding this comment

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

s/createUnstructuredClient/createStructuredClient

break
}
}
jsonInfo.Serializer = dynamicCodec{}
Copy link
Contributor

Choose a reason for hiding this comment

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

comment below about dynamicCodec doesn't boost my confidence. I thought initially it will be used in test code or so, but I see it is being used here in real code.

Copy link
Author

Choose a reason for hiding this comment

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

@droot This code has been used for multiple releases of client-go including 7.0. please see here: https://github.com/kubernetes/client-go/blob/release-7.0/dynamic/client.go#L300

I don't know why it is considered wrong, but if I were to use the rest mapper and create a client-pool and get that client, it would be using the same codec.

I am interested in using the new dynamic client when everything moves to 3.11.

Any ideas on what may be a good workaround if this is unacceptable?

Copy link
Contributor

Choose a reason for hiding this comment

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

Ack.

return nil, nil, err
}

if _, ok := obj.(*metav1.Status); !ok && strings.ToLower(gvk.Kind) == "status" {
Copy link
Contributor

Choose a reason for hiding this comment

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

interesting we have Status resource as well ?

Copy link
Author

Choose a reason for hiding this comment

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

https://godoc.org/k8s.io/apimachinery/pkg/apis/meta/v1#Status

I believe this is used for errors from the API servers but I don't know for a fact.

Copy link
Contributor

Choose a reason for hiding this comment

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

correct, the Status Kind is used to convey API errors. It doesn't belong to any particular resource, IIRC.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ack.

// resourceByGVK caches type metadata for unstructured
unstructuredResourceByGVK map[schema.GroupVersionKind]*resourceMeta
muByType sync.RWMutex
muByGVK sync.RWMutex
Copy link
Contributor

Choose a reason for hiding this comment

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

consider moving these above the fields they guard.

@shawn-hurley
Copy link
Author

@DirectXMan12 and I had an offline discussion about the dynamic client.

In the changes, we discussed using the new dynamic client for the cache's list watcher setup. We also talked about a separate client for the unstructured client. This new unstructured client would use the new dynamic client.

@droot Wondering if the above would help with the concerns about the client?

Copy link
Contributor

@droot droot left a comment

Choose a reason for hiding this comment

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

@droot Wondering if the above would help with the concerns about the client?

Sounds good to me.

Can we please add tests ?

return nil, nil, err
}

if _, ok := obj.(*metav1.Status); !ok && strings.ToLower(gvk.Kind) == "status" {
Copy link
Contributor

Choose a reason for hiding this comment

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

Ack.

if err != nil {
return nil
}

gvk, err := apiutil.GVKForObject(out, ip.Scheme)
Copy link
Contributor

Choose a reason for hiding this comment

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

Got it. No, was just curious.

break
}
}
jsonInfo.Serializer = dynamicCodec{}
Copy link
Contributor

Choose a reason for hiding this comment

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

Ack.

@droot
Copy link
Contributor

droot commented Aug 22, 2018

@shawn-hurley ignore my comment about the tests :) I completely missed them (they were probably collapsed)

@shawn-hurley shawn-hurley force-pushed the unstructured branch 2 times, most recently from 3ce1536 to 0d3d74b Compare August 31, 2018 19:28
@shawn-hurley
Copy link
Author

Updated to the new dynamic client for the unstructured clients.

PTAL @DirectXMan12 @droot

@shawn-hurley
Copy link
Author

@DirectXMan12 @droot PTAL I think this should be ready to go now

@DirectXMan12
Copy link
Contributor

For what this does, I think it came out pretty sane and readable. Please squash down the commits into logical chunks, and then I think it looks good from my end. We'll want equivalent docs in the gitbook about unstructured as well.

@shawn-hurley
Copy link
Author

@DirectXMan12 I tried to make it more clear for each commit. PTAL

@DirectXMan12
Copy link
Contributor

better -- generally, commits should not show review or development history -- so for instance, the "refactor structure out of..." commit should probably be squashed into the first commit. We can merge it like this, but I'd prefer to fix that kind of thing up first.

@DirectXMan12
Copy link
Contributor

Want to give @droot one more chance to look this over, but I think it looks good from my end.

@pwittrock
Copy link
Contributor

@droot will be back Monday to TAL

Copy link
Contributor

@droot droot left a comment

Choose a reason for hiding this comment

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

Looks good except one comment.

@@ -173,7 +154,7 @@ var _ StatusWriter = &statusWriter{}

// Update implements client.StatusWriter
func (sw *statusWriter) Update(_ context.Context, obj runtime.Object) error {
o, err := sw.client.cache.getObjMeta(obj)
o, err := sw.client.typedClient.cache.getObjMeta(obj)
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like we forgot to add support for unstructured type here ? Is it intentional ? I think dynamic client support CRUD on "subresources".

@shawn-hurley
Copy link
Author

@droot PTAL when you have a second

@droot droot added lgtm "Looks good to me", indicates that a PR is ready to be merged. approved Indicates a PR has been approved by an approver from all required OWNERS files. labels Sep 20, 2018
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

Approval requirements bypassed by manually added approval.

This pull-request has been approved by: shawn-hurley

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

The pull request process is described 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 merged commit df7c11e into kubernetes-sigs:master Sep 20, 2018
justinsb pushed a commit to justinsb/controller-runtime that referenced this pull request Dec 7, 2018
Adding ability for clients, cache and watcher to work with unstructured
DirectXMan12 pushed a commit that referenced this pull request Jan 31, 2020
Comment fixes for code generation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support source.Kind for unstructured objects
5 participants