-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Adding ability for clients, cache and watcher to work with unstructured #101
Conversation
There was a problem hiding this 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.
pkg/cache/internal/cache_reader.go
Outdated
@@ -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 { |
There was a problem hiding this comment.
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...
pkg/client/apiutil/apimachinery.go
Outdated
} | ||
jsonInfo.Serializer = unstructured.UnstructuredJSONScheme | ||
|
||
cfg.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(jsonInfo) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
pkg/client/client_cache.go
Outdated
@@ -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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: "metadata"
There was a problem hiding this comment.
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
pkg/client/client_cache.go
Outdated
// 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) { |
There was a problem hiding this comment.
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
07b9138
to
6d5cca3
Compare
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... |
@DirectXMan12 and I discussed offline the problems that were identified. I will be re-working this based on the suggestions and help. |
pkg/client/client_test.go
Outdated
By("creating the object a second time") | ||
err = cl.Create(context.TODO(), u) | ||
Expect(err).To(HaveOccurred()) | ||
Expect(err.Error()).To(ContainSubstring("already exists")) |
There was a problem hiding this comment.
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.
@DirectXMan12 @droot @pwittrock PTAL I think this is ready for review |
There was a problem hiding this 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
pkg/cache/internal/informers_map.go
Outdated
@@ -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 |
There was a problem hiding this comment.
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
pkg/cache/internal/informers_map.go
Outdated
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. |
There was a problem hiding this comment.
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
pkg/client/apiutil/apimachinery.go
Outdated
//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 |
There was a problem hiding this comment.
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...
@shawn-hurley will take a look today. Thanks |
934f564
to
4a4996f
Compare
There was a problem hiding this 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) |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
pkg/cache/internal/deleg_map.go
Outdated
return nil | ||
} | ||
|
||
// WaitForCacheSync waits until all the caches have been synced |
There was a problem hiding this comment.
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 :)
pkg/cache/internal/informers_map.go
Outdated
@@ -228,3 +237,38 @@ func (ip *InformersMap) newListWatch(gvk schema.GroupVersionKind) (*cache.ListWa | |||
}, | |||
}, nil | |||
} | |||
|
|||
// createUnstructuredClient is a ClientCreatorFunc for use with structured |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/createUnstructuredClient/createStructuredClient
pkg/client/apiutil/apimachinery.go
Outdated
break | ||
} | ||
} | ||
jsonInfo.Serializer = dynamicCodec{} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
pkg/client/apiutil/apimachinery.go
Outdated
return nil, nil, err | ||
} | ||
|
||
if _, ok := obj.(*metav1.Status); !ok && strings.ToLower(gvk.Kind) == "status" { |
There was a problem hiding this comment.
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 ?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
pkg/client/client_cache.go
Outdated
// resourceByGVK caches type metadata for unstructured | ||
unstructuredResourceByGVK map[schema.GroupVersionKind]*resourceMeta | ||
muByType sync.RWMutex | ||
muByGVK sync.RWMutex |
There was a problem hiding this comment.
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.
@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? |
There was a problem hiding this 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 ?
pkg/client/apiutil/apimachinery.go
Outdated
return nil, nil, err | ||
} | ||
|
||
if _, ok := obj.(*metav1.Status); !ok && strings.ToLower(gvk.Kind) == "status" { |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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.
pkg/client/apiutil/apimachinery.go
Outdated
break | ||
} | ||
} | ||
jsonInfo.Serializer = dynamicCodec{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
@shawn-hurley ignore my comment about the tests :) I completely missed them (they were probably collapsed) |
3ce1536
to
0d3d74b
Compare
Updated to the new dynamic client for the unstructured clients. PTAL @DirectXMan12 @droot |
27a3c52
to
9f8892f
Compare
@DirectXMan12 @droot PTAL I think this should be ready to go now |
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. |
9f8892f
to
44e39f9
Compare
@DirectXMan12 I tried to make it more clear for each commit. PTAL |
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. |
1f04ff6
to
15e1a2e
Compare
Want to give @droot one more chance to look this over, but I think it looks good from my end. |
@droot will be back Monday to TAL |
15e1a2e
to
b6b3cfe
Compare
* remove cache reader marshal/unmarshal * adding seperation between types and unstructured types in the cache
* By default the client should not create informers for get and list requsts of unstructured types.
b6b3cfe
to
00c5c79
Compare
There was a problem hiding this 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.
pkg/client/client.go
Outdated
@@ -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) |
There was a problem hiding this comment.
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".
@droot PTAL when you have a second |
[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 |
Adding ability for clients, cache and watcher to work with unstructured
Comment fixes for code generation
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?