-
-
Notifications
You must be signed in to change notification settings - Fork 320
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
Move ApiCapabilities
into ApiResource
and add Scope
trait
#1038
base: main
Are you sure you want to change the base?
Conversation
ApiCapabilities
into ApiResource
ApiCapabilities
into ApiResource
and add Scope
trait
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## main #1038 +/- ##
==========================================
- Coverage 72.29% 71.82% -0.47%
==========================================
Files 65 66 +1
Lines 4681 4699 +18
==========================================
- Hits 3384 3375 -9
- Misses 1297 1324 +27
|
Eyes on this would be appreciated @kube-rs/maintainers . Clarity on how to convert between scope requirement on dynamic and static types is needed for a good way forward on the new client api in #1037, but this should stand well on its own without that (even though it doesn't go all the way with subresources). |
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.
+1 on the overall concept, having to distinguish resource and caps was pretty weird and awkward.
let mut ar = parse::parse_apiresource(res, &list.group_version).map_err( | ||
|ParseGroupVersionError(s)| Error::Discovery(DiscoveryError::InvalidGroupVersion(s)), | ||
)?; | ||
let caps = parse::parse_apicapabilities(&list, &res.name)?; | ||
return Ok((ar, caps)); | ||
ar.subresources = parse::find_subresources(&list, &res.name)?; | ||
return Ok(ar); |
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.
Would it make sense to reorganize this to:
struct ApiResourceMeta; // current ApiResource
struct ApiCapabilities; // current ApiCapabilities
struct ApiResource { // current (ApiResource, ApiCapabilities)
meta: ApiResourceMeta,
caps: ApiCapabilities,
}
That way we could provide wrapper functions as needed on ApiResource
to avoid having the user care about the distinction, but also statically guarantee that caps are actually available when required. We might even want to impl Deref<Target = ApiResourceMeta> for ApiResource
to approximate subtyping...
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.
Sorry, s/caps/subresources/g
.
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 think that sounds like the nicest possible way to split the structs. It is still a bit awkward though:
- the upstream struct does not have a split like that
- we are not really able to split
APIResource
into Caps/Info now (where does short_names go? is scope a capability?), and it might be even harder in the future if upstream adds more info - we probably need to push the distinction stuff into
ApiResourceMeta
instead as a user could want to query the api with just the GVK + plural, so now they need a partialApiResourceMeta
constructed somehow
The distinction here might not be that big of a deal because if we provide clean paths to get fully speced out variants of ApiResource
via kube-derive
or discovery
then that's the happy path for users. If people want to construct them manually, then they probably should care about the distinction.
(As for the subresource parts of ApiResource
, I'm not entirely sure what is the best way to represent that inside ApiResource
. Nesting ApiResource
inside ApiResource
to represent subresources is unnecessary (and leaves many fields unused). Hoping to experiment and clean up that part in the client api pr - for now have left it as it was)
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.
What do you think?
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.
After looking into it a bit more it feels like there was a useful distinction where ApiResource
was "the information you need to start querying the API" and ApiCapabilities
was "auxilary data that might be useful for a UI (like kubectl)". I think it makes sense to keep those types distinct to document whether ApiCapabilities
is also available at some given point.
Where the old API (IMO) fell down was in that having to deal with ApiCapabilities
ended up being a constant annoyance for people who only care about ApiResource
.
APIResource
is a slightly different beast, since it's always generated by the K8s API server, which always knows the canonical information for all of the fields. from_gvk
isn't really an API that makes sense for the K8s type.
we are not really able to split APIResource into Caps/Info now (where does short_names go? is scope a capability?), and it might be even harder in the future if upstream adds more info
short_names
is definitely an info/cap/whatever-we-end-up-calling-it. scope
I could see going both ways, but I'm leaning towards info as well since if you already have the object (or know how to construct it) then you can infer the scope based on that. If you want to guide the user towards how to construct the object then you'll want an ApiResourceInfo
.
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.
Yeah, I don't think Option-wrapping at runtime is great.
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.
Have put a capabilities
member as Option<ApiCapabilities>
directly on ApiResource
now.
Have not included namespaced
in there because namespaced
is currently the one thing we can find through all forms of construction:
- kube-derive/discovery (exists by construction)
- ApiResource::erase (using the new Scope trait through Resource)
- ApiResource manual construction (if setting namespaced manually - although that's up to the user to get right in that case)
In fact, namespaced is now universally accessible so have made a Resource::is_namespaced
method to help determine the namespace (which even works in the dynamic case).
Having namespaced
within ApiCapabilities
then we would need to partially construct it in ApiResource::erase
and that means all fields within ApiCapabilities
need to be option wrapped, which is not great for users of the dynamic_api
and similar (multiple guaranteed unwraps necessary in their code). Currently it's now limited to a single unwrap in dynamic_api to access the caps:
// use discovery to get an AR
let caps = ar.capabilities.as_ref().expect("guaranteed because we used discovery");
caps.supports_operation(...); // can now do a check without option unwrapping from caps
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.
Have lifted the namespaced
into capabilities struct by removing the option, and relying on default instead.
I think this is the least worst option, that still preserves a reasonable split (although the setters have been moved into ApiResource
because we need to set namespaced
from the root (it's the resource that can extract it) - but that's fine, these setters are only set by either kube-derive or hardcore manual users anyway).
So now ApiCapabilities has namespace/verbs/subresources/shortnames, and all of these will be set in the happy path (kube-derive/discovery). It's the happy path we want to be as frictionless as possible, so having options for just the manual path ends up mandating unwraps on the happy path which is not great.
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 think we just keep talking past each other.
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.
Okay, I did a bit more digging.
I tried implementing the ApiResource
/ApiResourceCore
split to see how it'd go, and Deref
/DerefMut
helped hide it from most usage sites (aside from constructors): https://github.com/teozkr/kube-rs/tree/rm-caps-/typed.
However, this also highlit another problem with merging caps into ApiResource
: ApiResource
is currently embedded into ObjectRef<DynamicObject>
, so any fields on it are also considered for ObjectRef
's Eq
/Hash
implementations. This means that an ObjectRef<DynamicObject>
constructed using a discovered type and an ObjectRef
constructed by erasing a k8s_openapi
type would not be considered equal, even if they refer to same version of the exact same Kubernetes object!.
Signed-off-by: clux <[email protected]>
Signed-off-by: clux <[email protected]>
Signed-off-by: clux <[email protected]>
Signed-off-by: clux <[email protected]>
Signed-off-by: clux <[email protected]>
Co-authored-by: Teo Klestrup Röijezon <[email protected]> Signed-off-by: Eirik A <[email protected]>
Signed-off-by: clux <[email protected]>
Signed-off-by: clux <[email protected]>
Signed-off-by: clux <[email protected]>
Signed-off-by: clux <[email protected]>
Signed-off-by: clux <[email protected]>
ApiCapabilities
into ApiResource
and add Scope
traitApiCapabilities
into ApiResource
and add Scope
trait
Going to put this on hold until 0.77. I have some ideas for it, but it's been a while since a release and will get that out the door first. |
Generally simplified code everywhere. The main edge cases worth documenting here is that
ApiResource
can be constructed in multiple ways, and some of these will not yield all the fields on it.MergedMovedApiCapabilities
data intoApiResource
shortnames
toApiResource
for Add shortnames field in kube::discovery::ApiResource #1002ApiCapabilities
returns (always constructed together withApiResource
, so now returned as one)Scope
trait implemented for allk8s-openapi::ResourceScope
+ ourDynamicResourceScope
Resource
trait to require the associated Scope type to implement the newScope
traitResource::is_namespaced
usingScope
trait or dyn type inspectionApiResource::erase
to be able to determinenamespaced
bool from theScope
traitApiResource
ctors (removedApiResource::from_gvk_with_plural
in favour ofApiResource::new
)ApiCapabilities
toApiResource
ApiResource
ApiResource
(todo in WIP: Core methods onClient
#1037)