Open sehz opened 4 years ago
@qrilka Interested in working on this?
I need to take a better look to understand the details but could it help with #92 and #112 ? Is the idea to generate code with a macro or maybe somehow else?
@vijaylaxmid did you have any progress with this? Maybe some ideas?
Yes, using procedure macro to generate would make sense. Something like:
#[kubeapi(group = "core",version ="1", status = ServiceStatus)]
pub struct ServiceSpec {
#[serde(rename = "clusterIP")]
pub cluster_ip: String,
#[serde(rename = "externalIPs")]
pub external_ips: Vec<String>,
#[serde(rename = "loadBalancerIP")]
pub load_balancer_ip: Option<String>,
pub r#type: Option<LoadBalancerType>,
pub external_name: Option<String>,
pub external_traffic_policy: Option<ExternalTrafficPolicy>,
pub ports: Vec<ServicePort>,
pub selector: Option<HashMap<String, String>>,
}
in the kube-rs, similar derive: https://docs.rs/kube-derive/0.63.2/kube_derive/derive.CustomResource.html
I don't have much experience with macros yes but the task looks interesting and looks helpful for https://github.com/infinyon/fluvio/issues/1131 that I was originally looking into when starting with k8-api/fluvio Not sure how much time this will take though :)
@sehz how could proc macro approach use Open API spec? The only sensible option I see is to list derived specs and test their conformance to the spec (but that could require some extra Spec details to be available at least when tests are enabled)
procedure macro just generates boiler plate once trait is defined. You can take look at how to parse as in here: https://github.com/infinyon/fluvio/tree/master/crates/fluvio-protocol-derive
Sure but I guess the mapping from OpenAPI spec to Spec in Rust code base is a manual thing, right?
@sehz I was looking a bit into other things and read a bit more about proc macros and as for this ticket - what actually should a macro derive here? When I look into https://github.com/infinyon/k8-api/blob/master/src/k8-types/src/core/service.rs then the only things like that seem to be:
impl Spec for ServiceSpec
(with a bit unclear logic in make_same
to empty cluster_ip
)default_store_spec!(ServiceSpec, ServiceStatus, "Service")
which is already a macroDo I miss something here?
default_store_spec
implements StoreSpec
which not part of scope. This is just about reducing boiling plate:
impl Spec for ServiceSpec {
type Status = ServiceStatus;
type Header = DefaultHeader;
fn metadata() -> &'static Crd {
&SERVICE_API
}
fn make_same(&mut self, other: &Self) {
if other.cluster_ip.is_empty() {
self.cluster_ip = "".to_owned();
}
}
}
This doesn't seem to be a lot boilerplate but OK.
The only remaining question then is about this special handling for cluster_ip
- is it needed only for string fields or maybe there's some more complicated logic?
Can implement field attributes like #[serde(default = "path")], so this can be something like:
#[kubeapi(group = "core",version ="1", status = ServiceStatus,makesame="same_service")]
...
And what does "same_service" mean your example?
As for field attributes, those work OK as (parameterized) markers but it doesn't look like it could allow generic handling, e.g. in StatefulSetSpec
I see a different definition:
// statefulset doesnt' like to change volume claim template
fn make_same(&mut self, other: &Self) {
self.volume_claim_templates = other.volume_claim_templates.clone();
}
So it looks like make_same
isn't something that needs to be derived
impl Spec for ServiceSpec {
type Status = ServiceStatus;
type Header = DefaultHeader;
fn metadata() -> &'static Crd {
&SERVICE_API
}
fn make_same(&mut self, other: &Self) {
make_same2(self,other)
}
}
fn make_same2(sv1: &mut ServiceSpec,sv2: &mut ServiceSpec2) {
if sv2.cluster_ip.is_empty() {
sv1.cluster_ip = "".to_owned();
}
}
I see but what about changing the types a bit? I think it would be more elegant to split make_same
into a separate trait MakeSame
, require it only for apply
(the only place it seems to be used) and derive the default no-op implementation if needed or allow it to be declared if custom implementation is needed.
Make sense
Hey. could we use https://github.com/kube-rs/kube-rs client?
I am happy to port fluvio/#1131 to it and see how well it works?
I have love / hate relationship with the current crop of OpenAPI rust generators :/
@pinkforest I asked about kube-rs previously - https://github.com/infinyon/k8-api/issues/92#issuecomment-943541525 as for https://github.com/infinyon/fluvio/issues/1131 - I've done work on that but didn't have much free time lately
Kubernetes uses OpenAPI spec: https://github.com/kubernetes/kubernetes/tree/master/api/openapi-spec to define their api.
Generate rust k8-api bindings from the spec which implements Spec trait as in the example: