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

Change Resource trait #486

Merged
merged 7 commits into from
Apr 6, 2021
Merged

Conversation

MikailBag
Copy link
Contributor

  • add a meta_mut method for mutable access to the metadata
  • move some methods to ResourceExt trait

- add a meta_mut method for mutable access to the metadata
- move some methods to ResourceExt trait
/// when resource was received from the apiserver.
/// Because of `.metadata.generateName` field, in other contexts name
/// may be missing.
fn expect_name(&self) -> String;
Copy link
Member

Choose a reason for hiding this comment

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

Maybe name_unchecked?

Copy link
Member

@clux clux Apr 3, 2021

Choose a reason for hiding this comment

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

I was going to advocate for hiding it like we did before, but after testing it against quick generateName variants, and seeing that it's a common thing (and not just an api wart that could have been an admission controller), we kind of have to deal with it.

  • The response back when you create with generateName does not have metadata.name
  • The first ADDED event from watch with generateName does not have metadata.name (so our runtime machinery can crash pretty quickly, will find a minimal case and open a bug)

EDIT: i might be wrong, unwrap was on something else, actually cannot reproduce the missing name now.

Copy link
Member

@clux clux left a comment

Choose a reason for hiding this comment

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

I think this is a very good way forward. Thank you for this.

I think the trait can cover more of ObjectMeta's foundational types, and I think kazk's naming makes more sense.

Also think we should be using o.name_unchecked() over ResourceExt::name_unchecked(&o) in examples insofar as it's possible.


fn resource_ver(&self) -> Option<String> {
self.meta().resource_version.clone()
}
Copy link
Member

Choose a reason for hiding this comment

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

I think we can have a lot more helpers in here if we are uncoupling the more foundational Resource trait, with a more user-friendly one.

This should be a helper trait for dealing with ObjectMeta, it needs to cover at least the basics: stuff like, labels, annotations, ownerrefs, finalizers, managedfields, where we can unpack optional vectors into empty vectors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will add labels, annotations, ownerrefs, and annotations (getters and setters), and also getter for UID.

I don't think we should add managedfields to the trait - AFAIU one does not need to look at .metadata.managedFields or modify it unless one is writing an alternative apiserver implementation.

Copy link
Member

@clux clux Apr 3, 2021

Choose a reason for hiding this comment

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

ok, fair point on managedFields.

btw, i am not sure about setters, because most of the time people change metadata fields with apply patches, or more awkward json patches, and setters kind of encourage use of api.replace which is awkward, and can run into idempotency issues.

i think maybe some patchbuilder utils for changing metadata fields might be more appropriate.

}

fn labels_mut(&mut self) -> &mut BTreeMap<String, String> {
self.meta_mut().labels.get_or_insert_with(BTreeMap::new)
Copy link
Member

@clux clux Apr 3, 2021

Choose a reason for hiding this comment

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

oh, actually, this is a really nice use of get_or_insert_with!
yeah, I like these mut accessors.

@MikailBag
Copy link
Contributor Author

MikailBag commented Apr 3, 2021

I've reproduced CI failure locally: deployment does not have Completed condition (maybe Available was intended?), so it seems to be a tests issue.

@clux
Copy link
Member

clux commented Apr 5, 2021

I'll fix up the CI later in the week. Thanks for the diagnosis.

The only thing left here is whether or not to call it name or name_unchecked.
I tried to find a case where we can get a null metadata.name, but even when creating an object with only generateName, it still gave a name back on the first event. If we can get away with using .name, i think it's worth keeping the simplicity, but not sure we can.
Can anyone confirm whether or not you can get a None back?

@MikailBag
Copy link
Contributor Author

Can anyone confirm whether or not you can get a None back?
No, AFAIK you always get .metadata.name from the API.

On the other hand, a resource instance can be constructed by the application, and in this case, it can have name missing.

For example, following code will panic:

let p = Pod {spec: make_pod_spec(), ..Default::default()}
println!("{}", p.name());

That's why I have initially decided to guard against such panics. However, I understand this makes API less ergonomic, and this mistake is not too easy to make. Given that this is just a helper function, it really makes sense to allow panicking, so I'm going to rename name_unchecked to name.

Ideally, it should be separated at type level, but it requires serious changes to k8s-openapi (for example, instead of usual Pod one will have InPod (where all fields are optional) and OutPod (where e.g. .metadata.name is required, and .metadata.generateName doesn't exist), and e.g. Api::create will take InPod and return OutPod).

@clux
Copy link
Member

clux commented Apr 6, 2021

Ah, interesting! Yeah, that makes it a bit more ambiguous. It's not like it's even invalid to not have a name, it's just invalid to not have both.

Alternatively, we could sidestep the issue by having ResourceExt::name fallback to {generateName}00000 if .name is missing (and to "unset" if both are missing to never panic), and then say in the docs that it only produces canonical results on valid objects from the apiserver.

@MikailBag
Copy link
Contributor Author

While such a fallback feels natural, I'm afraid it can be too surprising. So now my proposal is to keep current behavior (i.e. panicking name() function) and leave better type safety as a future extension.

@clux clux merged commit a1a50cc into kube-rs:master Apr 6, 2021
@MikailBag MikailBag deleted the break-resource-even-more branch April 6, 2021 14:49
@clux
Copy link
Member

clux commented May 15, 2021

Released in 0.53.0.

@clux clux mentioned this pull request Jun 26, 2021
10 tasks
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.

3 participants