-
-
Notifications
You must be signed in to change notification settings - Fork 313
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
rewrite api module to use provided traits from k8s_openapi #157
Conversation
Tried to make some compatibility layer thing with: #[cfg(feature = "openapi")]
impl<P, U> k8s_openapi::Metadata for Object<P, U>
where
P: Clone,
U: Clone,
{
type Ty = ObjectMeta;
fn metadata(&self) -> Option<&Self::Ty> {
Some(&self.metadata)
}
} but that won't work because the It's also a bit awkward with the Metadata trait returning |
No. The reason that |
Yeah, that makes sense. Having investigated more, it looks like the kubernetes api is just super-conservative on optionality. Just like PodSpec on Interestingly, trying to create a At any rate, it's a shame because this causes so many guaranteed unwraps in controllers, and I really would just like to fail to even deserialize an object if it doesn't satisfy my assumptions about it. |
(What I meant is that types with |
The golang code defines |
Have updated all examples, and fixed all tests to work with the current setup. Removed Have tried out the k8s-openapi-derive setup first. This gets most of the way there, and actually lets us have a custom thing that implements However, it's not super customizable atm and merging as it stands would (afaikt) make us drop support for manipulating status objects. Have raised Arnavion/k8s-openapi#62 upstream. But might explore an additional approach. @teozkr how do you feel about this pr? |
Big fan of this in concept, but I'm a bit out of order right now so I haven't done a proper review yet (pizzas are bloody dangerous, as it turns out..). That said, I don't think this resolves (my interpretation of) #150, although it did end up getting a bit derailed in this direction (sorry!). In particular, |
And for the record, 👍 from me on requiring |
Yeah, I realised that this only addresses the "derailed part" of this issue, and didn't want to change that world in one go, as this PR is already quite big. But I agree that this could be quite nice. That said, I am not sure I understand how connection pooling would be improved given that the underlying connection is already shared. |
Right, forgot about the distinction between |
/// | ||
/// This is the smallest amount of info we need to run the API against a CR | ||
/// The version, and group must be set by the user. | ||
pub struct CustomResource { |
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.
This looks like a leftover from the days of feature-gated k8s-openapi
.
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.
This is a temporary new thing until I have a fully reliable derive crate. This will be removed at some point. (All the other autogenerated Api::v1Pod disappeared, but the manual Api::customResource had to go somewhere temporaily).
} | ||
|
||
/// Make Resource useable on CRDs without k8s_openapi | ||
impl From<CustomResource> for Resource { |
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.
They seem to match 1:1, so.. why not just have CrBuilder
build a Resource
directly? (Assuming that this is still worthwhile to keep.)
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 could've called it a ResourceBuilder
perhaps - might be more appropriate - but it's only really useful for custom resources, that was why. It's almost the same, it infers the api_version
from version
and kind
.
src/api/metadata.rs
Outdated
fn resource_ver(&self) -> Option<String>; | ||
fn name(&self) -> String; | ||
fn namespace(&self) -> Option<String>; |
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.
Very minor nitpick, but.. I think all of these could borrow the String
instead?
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, can do. Was flipping between when implementing it. Switching now. Want to rename the trait to something shorter anyway. Maybe just straight Meta
.
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.
Actually, I might still clone in the non-expensive getters like Meta::name
, because it's really conflicting in all the examples where we need to burrow multiple parts from the main object:
let name = Meta::name(&pod);
let phase = pod.status.as_ref().unwrap().phase.as_ref().unwrap();
let containers = pod.spec.as_ref().unwrap()
.containers
.into_iter()
.map(|c| c.name)
.collect::<Vec<_>>();
info!("Found initial pod {} ({}) with {:?}", name, phase, containers);
If we all burrowed from pod in Meta
, then the least significant burrow for the name would prevent us from burrowing pod.spec (cannot move out of shared reference).
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 client code already knows what Metadata::Ty
will be, so it should just be able to access the field directly (let meta = &pod.metadata.name;
). Then again, I can see that it would be a pain to change all the examples again.. :/
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.
Once you do that you quickly hit move out of burrowed context
since metadata is optional. I was trying a bunch of different configurations, and have gone for the most ergonomic one so far, maybe there is a better one, but also kind of just want to get this done now :D
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.
Fair enough!
Added(K), | ||
Modified(K), | ||
Deleted(K), | ||
Error(ErrorResponse), |
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.
This wasn't really added here, but.. maybe the error conditions should be moved out into a Result<WatchEvent<K>>
instead? This should probably be a separate issue rather than blocking this PR.
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, that might be something worth raising. Have also considered picking up the upstream WatchEvent from k8s_openapi which in its latest version is very similar to the one we have (although with even more error separation).
src/api/object.rs
Outdated
/// Note that this is only used internally within reflectors and informers, | ||
/// and is generally produced from list/watch/delete collection queries on an `Resource`. | ||
/// | ||
/// This is now equivalent to k8s_openapi::List<T>, but with extra convenience impls |
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.
Is this machinery worth it to be able to say for x in list.iter() {}
rather than for x in list.items.iter() {}
? Personally, I almost prefer the latter..
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.
if feels nicer to beginners who can do node like things like:
for pod in api.list() {
println!("{:?}", pod)
}
and it's common pattern to implement for wrapper types like this. I don't think there's a real cost other than the couple of extra lines in here?
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.
For one, k8s_openapi::List
implements a bunch of traits that kube::api::List
doesn't (Debug
, PartialEq
, Serialize
, ...).
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. that is true. Probably worth moving this over as well 😪
this reduces a bunch of unwrap mechanics on metadata in examples.
This thing is too big to fail. |
This has grown to be a semi-rewrite of Api...
Fixes #150, #158, #25, #96
RawApi
renamed toResource
(much better tests now)Api
revampedopenapi
feature removed (now always depend on non-default-features version)runtime
now useMetaContent
trait for types withObjectMeta
examples
+ readme updated to showcase new apiso glad I'm doing this before kubecon... this is such a huge change.
Notably, this makes our Api work with ANY kubernetes type exported by
k8s_openapi
, removes a bunch of duplicated structs fromapimachinery
so we can focus on what's unique to this library; theruntime
.A few cons:
Option
heavy, leading to less ergonomic manipulation of their values (we can impl helpers later)Object<P, U>
is currently broken - it needs to implementk8s_openapi::Resource
via codegen now