From c351c021461b17b2cb2c3f92d7801f54ef459e95 Mon Sep 17 00:00:00 2001 From: Techassi Date: Wed, 14 May 2025 16:09:03 +0200 Subject: [PATCH 01/26] feat(stackable-versioned): Generate downgrade From impls --- .../src/attrs/item/mod.rs | 13 +- .../src/codegen/container/enum.rs | 83 +++++++++---- .../src/codegen/container/mod.rs | 94 ++++++++++----- .../src/codegen/container/struct.rs | 81 +++++++++++-- .../src/codegen/item/field.rs | 68 ++++++++++- .../src/codegen/item/variant.rs | 111 +++++++++++++----- .../src/codegen/mod.rs | 5 +- .../src/codegen/module.rs | 2 +- 8 files changed, 350 insertions(+), 107 deletions(-) diff --git a/crates/stackable-versioned-macros/src/attrs/item/mod.rs b/crates/stackable-versioned-macros/src/attrs/item/mod.rs index 34dbb3986..e82b2d3fe 100644 --- a/crates/stackable-versioned-macros/src/attrs/item/mod.rs +++ b/crates/stackable-versioned-macros/src/attrs/item/mod.rs @@ -240,13 +240,13 @@ impl CommonItemAttributes { // The convert_with argument only makes sense to use when the // type changed - if let Some(convert_func) = change.convert_with.as_ref() { + if let Some(upgrade_func) = change.upgrade_with.as_ref() { if change.from_type.is_none() { errors.push( Error::custom( "the `convert_with` argument must be used in combination with `from_type`", ) - .with_span(&convert_func.span()), + .with_span(&upgrade_func.span()), ); } } @@ -315,7 +315,8 @@ impl CommonItemAttributes { .unwrap_or(ty.clone()); actions.insert(*change.since, ItemStatus::Change { - convert_with: change.convert_with.as_deref().cloned(), + downgrade_with: change.downgrade_with.as_deref().cloned(), + upgrade_with: change.upgrade_with.as_deref().cloned(), from_ident: from_ident.clone(), from_type: from_ty.clone(), to_ident: ident, @@ -359,7 +360,8 @@ impl CommonItemAttributes { .unwrap_or(ty.clone()); actions.insert(*change.since, ItemStatus::Change { - convert_with: change.convert_with.as_deref().cloned(), + downgrade_with: change.downgrade_with.as_deref().cloned(), + upgrade_with: change.upgrade_with.as_deref().cloned(), from_ident: from_ident.clone(), from_type: from_ty.clone(), to_ident: ident, @@ -435,7 +437,8 @@ pub struct ChangedAttributes { pub since: SpannedValue, pub from_name: Option>, pub from_type: Option>, - pub convert_with: Option>, + pub upgrade_with: Option>, + pub downgrade_with: Option>, } /// For the deprecated() action diff --git a/crates/stackable-versioned-macros/src/codegen/container/enum.rs b/crates/stackable-versioned-macros/src/codegen/container/enum.rs index da28cc785..2c2f4d47d 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/enum.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/enum.rs @@ -1,6 +1,6 @@ use std::ops::Not; -use darling::{FromAttributes, Result, util::IdentString}; +use darling::{FromAttributes, Result}; use proc_macro2::TokenStream; use quote::quote; use syn::{Generics, ItemEnum}; @@ -126,11 +126,11 @@ impl Enum { } /// Generates code for the `From for NextVersion` implementation. - pub(crate) fn generate_from_impl( + pub fn generate_upgrade_from_impl( &self, version: &VersionDefinition, next_version: Option<&VersionDefinition>, - is_nested: bool, + add_attributes: bool, ) -> Option { if version.skip_from || self.common.options.skip_from { return None; @@ -145,12 +145,17 @@ impl Enum { // later versions. let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); let enum_ident = &self.common.idents.original; - let from_ident = &self.common.idents.from; + let from_enum_ident = &self.common.idents.from; - let next_version_ident = &next_version.ident; - let version_ident = &version.ident; + let for_module_ident = &next_version.ident; + let from_module_ident = &version.ident; - let variants = self.generate_from_variants(version, next_version, enum_ident); + let variants: TokenStream = self + .variants + .iter() + .map(|v| v.generate_for_upgrade_from_impl(version, next_version, enum_ident)) + .flatten() + .collect(); // Include allow(deprecated) only when this or the next version is // deprecated. Also include it, when a variant in this or the next @@ -163,17 +168,18 @@ impl Enum { // Only add the #[automatically_derived] attribute only if this impl is used // outside of a module (in standalone mode). - let automatically_derived = - is_nested.not().then(|| quote! {#[automatically_derived]}); + let automatically_derived = add_attributes + .not() + .then(|| quote! {#[automatically_derived]}); Some(quote! { #automatically_derived #allow_attribute - impl #impl_generics ::std::convert::From<#version_ident::#enum_ident #type_generics> for #next_version_ident::#enum_ident #type_generics + impl #impl_generics ::std::convert::From<#from_module_ident::#enum_ident #type_generics> for #for_module_ident::#enum_ident #type_generics #where_clause { - fn from(#from_ident: #version_ident::#enum_ident #type_generics) -> Self { - match #from_ident { + fn from(#from_enum_ident: #from_module_ident::#enum_ident #type_generics) -> Self { + match #from_enum_ident { #variants } } @@ -184,20 +190,53 @@ impl Enum { } } - /// Generates code for enum variants used in `From` implementations. - fn generate_from_variants( + pub fn generate_downgrade_from_impl( &self, version: &VersionDefinition, - next_version: &VersionDefinition, - enum_ident: &IdentString, - ) -> TokenStream { - let mut tokens = TokenStream::new(); - - for variant in &self.variants { - tokens.extend(variant.generate_for_from_impl(version, next_version, enum_ident)); + next_version: Option<&VersionDefinition>, + add_attributes: bool, + ) -> Option { + if version.skip_from || self.common.options.skip_from { + return None; } - tokens + match next_version { + Some(next_version) => { + let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); + let enum_ident = &self.common.idents.original; + let from_enum_ident = &self.common.idents.from; + + let for_module_ident = &version.ident; + let from_module_ident = &next_version.ident; + + let variants: TokenStream = self + .variants + .iter() + .map(|v| v.generate_for_downgrade_from_impl(version, next_version, enum_ident)) + .flatten() + .collect(); + + // Only add the #[automatically_derived] attribute only if this impl is used + // outside of a module (in standalone mode). + let automatically_derived = add_attributes + .not() + .then(|| quote! {#[automatically_derived]}); + + Some(quote! { + #automatically_derived + impl #impl_generics ::std::convert::From<#from_module_ident::#enum_ident #type_generics> for #for_module_ident::#enum_ident #type_generics + #where_clause + { + fn from(#from_enum_ident: #from_module_ident::#enum_ident #type_generics) -> Self { + match #from_enum_ident { + #variants + } + } + } + }) + } + None => None, + } } /// Returns whether any variant is deprecated in the provided `version`. diff --git a/crates/stackable-versioned-macros/src/codegen/container/mod.rs b/crates/stackable-versioned-macros/src/codegen/container/mod.rs index fbbff4006..fbc7565ba 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/mod.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/mod.rs @@ -21,7 +21,7 @@ mod r#enum; mod r#struct; /// Contains common container data shared between structs and enums. -pub(crate) struct CommonContainerData { +pub struct CommonContainerData { /// Original attributes placed on the container, like `#[derive()]` or `#[cfg()]`. pub(crate) original_attributes: Vec, @@ -37,7 +37,7 @@ pub(crate) struct CommonContainerData { /// Abstracting away with kind of container is generated makes it possible to create a list of /// containers when the macro is used on modules. This enum provides functions to generate code /// which then internally call the appropriate function based on the variant. -pub(crate) enum Container { +pub enum Container { Struct(Struct), Enum(Enum), } @@ -51,16 +51,37 @@ impl Container { } } - /// Generates the container `From for NextVersion` implementation. - pub(crate) fn generate_from_impl( + /// Generates the `From for NextVersion` implementation for the container. + pub fn generate_upgrade_from_impl( &self, version: &VersionDefinition, next_version: Option<&VersionDefinition>, add_attributes: bool, ) -> Option { match self { - Container::Struct(s) => s.generate_from_impl(version, next_version, add_attributes), - Container::Enum(e) => e.generate_from_impl(version, next_version, add_attributes), + Container::Struct(s) => { + s.generate_upgrade_from_impl(version, next_version, add_attributes) + } + Container::Enum(e) => { + e.generate_upgrade_from_impl(version, next_version, add_attributes) + } + } + } + + /// Generates the `From for Version` implementation for the container. + pub fn generate_downgrade_from_impl( + &self, + version: &VersionDefinition, + next_version: Option<&VersionDefinition>, + add_attributes: bool, + ) -> Option { + match self { + Container::Struct(s) => { + s.generate_downgrade_from_impl(version, next_version, add_attributes) + } + Container::Enum(e) => { + e.generate_downgrade_from_impl(version, next_version, add_attributes) + } } } @@ -180,9 +201,17 @@ impl StandaloneContainer { // NOTE (@Techassi): Using '.copied()' here does not copy or clone the data, but instead // removes one level of indirection of the double reference '&&'. - let from_impl = + let next_version = versions.peek().copied(); + + // Generate the From impl for upgrading the CRD. + let upgrade_from_impl = + self.container + .generate_upgrade_from_impl(version, next_version, false); + + // Generate the From impl for downgrading the CRD. + let downgrade_from_impl = self.container - .generate_from_impl(version, versions.peek().copied(), false); + .generate_downgrade_from_impl(version, next_version, false); // Add the #[deprecated] attribute when the version is marked as deprecated. let deprecated_attribute = version @@ -210,7 +239,8 @@ impl StandaloneContainer { #container_definition } - #from_impl + #upgrade_from_impl + #downgrade_from_impl }); } @@ -231,13 +261,13 @@ impl StandaloneContainer { pub(crate) struct ContainerIdents { /// The ident used in the context of Kubernetes specific code. This ident /// removes the 'Spec' suffix present in the definition container. - pub(crate) kubernetes: IdentString, + pub kubernetes: IdentString, /// The original ident, or name, of the versioned container. - pub(crate) original: IdentString, + pub original: IdentString, /// The ident used in the [`From`] impl. - pub(crate) from: IdentString, + pub from: IdentString, } impl ContainerIdents { @@ -261,32 +291,32 @@ impl ContainerIdents { } #[derive(Debug)] -pub(crate) struct ContainerOptions { - pub(crate) kubernetes_options: Option, - pub(crate) skip_from: bool, +pub struct ContainerOptions { + pub kubernetes_options: Option, + pub skip_from: bool, } #[derive(Debug)] -pub(crate) struct KubernetesOptions { - pub(crate) group: String, - pub(crate) kind: Option, - pub(crate) singular: Option, - pub(crate) plural: Option, - pub(crate) namespaced: bool, +pub struct KubernetesOptions { + pub group: String, + pub kind: Option, + pub singular: Option, + pub plural: Option, + pub namespaced: bool, // root - pub(crate) crates: KubernetesCrateOptions, - pub(crate) status: Option, + pub crates: KubernetesCrateOptions, + pub status: Option, // derive // schema // scale // printcolumn - pub(crate) shortnames: Vec, + pub shortnames: Vec, // category // selectable // doc // annotation // label - pub(crate) skip_merged_crd: bool, + pub skip_merged_crd: bool, } impl From for KubernetesOptions { @@ -308,12 +338,12 @@ impl From for KubernetesOptions { } #[derive(Debug)] -pub(crate) struct KubernetesCrateOptions { - pub(crate) kube_core: Override, - pub(crate) k8s_openapi: Override, - pub(crate) schemars: Override, - pub(crate) serde: Override, - pub(crate) serde_json: Override, +pub struct KubernetesCrateOptions { + pub kube_core: Override, + pub k8s_openapi: Override, + pub schemars: Override, + pub serde: Override, + pub serde_json: Override, } impl Default for KubernetesCrateOptions { @@ -396,7 +426,7 @@ impl ToTokens for KubernetesCrateOptions { /// Wraps a value to indicate whether it is original or has been overridden. #[derive(Debug)] -pub(crate) enum Override { +pub enum Override { Default(T), Overridden(T), } diff --git a/crates/stackable-versioned-macros/src/codegen/container/struct.rs b/crates/stackable-versioned-macros/src/codegen/container/struct.rs index 584a293b1..07dff52d5 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/struct.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/struct.rs @@ -134,7 +134,7 @@ pub(crate) struct Struct { // Common token generation impl Struct { /// Generates code for the struct definition. - pub(crate) fn generate_definition(&self, version: &VersionDefinition) -> TokenStream { + pub fn generate_definition(&self, version: &VersionDefinition) -> TokenStream { let where_clause = self.generics.where_clause.as_ref(); let type_generics = &self.generics; @@ -160,8 +160,13 @@ impl Struct { } } + // TODO (@Techassi): It looks like some of the stuff from the upgrade and downgrade functions + // can be combined into a single piece of code. Figure out a nice way to do that. /// Generates code for the `From for NextVersion` implementation. - pub(crate) fn generate_from_impl( + /// + /// The `add_attributes` parameter declares if attributes (macros) should be added to the + /// generated `From` impl block. + pub fn generate_upgrade_from_impl( &self, version: &VersionDefinition, next_version: Option<&VersionDefinition>, @@ -185,7 +190,13 @@ impl Struct { let for_module_ident = &next_version.ident; let from_module_ident = &version.ident; - let fields = self.generate_from_fields(version, next_version, from_struct_ident); + let fields: TokenStream = self + .fields + .iter() + .map(|f| { + f.generate_for_upgrade_from_impl(version, next_version, from_struct_ident) + }) + .collect(); // Include allow(deprecated) only when this or the next version is // deprecated. Also include it, when a field in this or the next @@ -220,20 +231,64 @@ impl Struct { } } - /// Generates code for struct fields used in `From` implementations. - fn generate_from_fields( + pub fn generate_downgrade_from_impl( &self, version: &VersionDefinition, - next_version: &VersionDefinition, - from_struct_ident: &IdentString, - ) -> TokenStream { - let mut tokens = TokenStream::new(); - - for field in &self.fields { - tokens.extend(field.generate_for_from_impl(version, next_version, from_struct_ident)); + next_version: Option<&VersionDefinition>, + add_attributes: bool, + ) -> Option { + if version.skip_from || self.common.options.skip_from { + return None; } - tokens + match next_version { + Some(next_version) => { + let (impl_generics, type_generics, where_clause) = self.generics.split_for_impl(); + let struct_ident = &self.common.idents.original; + let from_struct_ident = &self.common.idents.from; + + let for_module_ident = &version.ident; + let from_module_ident = &next_version.ident; + + let fields: TokenStream = self + .fields + .iter() + .map(|f| { + f.generate_for_downgrade_from_impl(version, next_version, from_struct_ident) + }) + .collect(); + + // Include allow(deprecated) only when this or the next version is + // deprecated. Also include it, when a field in this or the next + // version is deprecated. + let allow_attribute = (version.deprecated.is_some() + || next_version.deprecated.is_some() + || self.is_any_field_deprecated(version) + || self.is_any_field_deprecated(next_version)) + .then(|| quote! { #[allow(deprecated)] }); + + // Only add the #[automatically_derived] attribute only if this impl is used + // outside of a module (in standalone mode). + let automatically_derived = add_attributes + .not() + .then(|| quote! {#[automatically_derived]}); + + Some(quote! { + #automatically_derived + #allow_attribute + impl #impl_generics ::std::convert::From<#from_module_ident::#struct_ident #type_generics> for #for_module_ident::#struct_ident #type_generics + #where_clause + { + fn from(#from_struct_ident: #from_module_ident::#struct_ident #type_generics) -> Self { + Self { + #fields + } + } + } + }) + } + None => None, + } } /// Returns whether any field is deprecated in the provided `version`. diff --git a/crates/stackable-versioned-macros/src/codegen/item/field.rs b/crates/stackable-versioned-macros/src/codegen/item/field.rs index f55f774fa..c039e9873 100644 --- a/crates/stackable-versioned-macros/src/codegen/item/field.rs +++ b/crates/stackable-versioned-macros/src/codegen/item/field.rs @@ -144,7 +144,7 @@ impl VersionedField { } // TODO (@Techassi): This should probably return an optional token stream. - pub(crate) fn generate_for_from_impl( + pub fn generate_for_upgrade_from_impl( &self, version: &VersionDefinition, next_version: &VersionDefinition, @@ -174,16 +174,16 @@ impl VersionedField { _, ItemStatus::Change { from_ident: old_field_ident, - convert_with, + upgrade_with, to_ident, .. }, - ) => match convert_with { + ) => match upgrade_with { // The user specified a custom conversion function which // will be used here instead of the default .into() call // which utilizes From impls. - Some(convert_fn) => quote! { - #to_ident: #convert_fn(#from_struct_ident.#old_field_ident), + Some(upgrade_fn) => quote! { + #to_ident: #upgrade_fn(#from_struct_ident.#old_field_ident), }, // Default .into() call using From impls. None => quote! { @@ -212,4 +212,62 @@ impl VersionedField { } } } + + pub fn generate_for_downgrade_from_impl( + &self, + version: &VersionDefinition, + next_version: &VersionDefinition, + from_struct_ident: &IdentString, + ) -> TokenStream { + match &self.changes { + Some(changes) => { + let next_change = changes.get_expect(&next_version.inner); + let change = changes.get_expect(&version.inner); + + match (change, next_change) { + // If both this status and the next one is NotPresent, which means + // a field was introduced after a bunch of versions, we don't + // need to generate any code for the From impl. + (ItemStatus::NotPresent, ItemStatus::NotPresent) => { + quote! {} + } + (_, ItemStatus::Addition { .. }) => quote! {}, + ( + _, + ItemStatus::Change { + downgrade_with, + from_ident: old_field_ident, + to_ident, + .. + }, + ) => match downgrade_with { + Some(downgrade_fn) => quote! { + #old_field_ident: #downgrade_fn(#from_struct_ident.#to_ident), + }, + None => quote! { + #old_field_ident: #from_struct_ident.#to_ident.into(), + }, + }, + (old, next) => { + let next_field_ident = next.get_ident(); + let old_field_ident = old.get_ident(); + + // NOTE (@Techassi): Do we really need .into() here. I'm + // currently not sure why it is there and if it is needed + // in some edge cases. + quote! { + #old_field_ident: #from_struct_ident.#next_field_ident.into(), + } + } + } + } + None => { + let field_ident = &*self.ident; + + quote! { + #field_ident: #from_struct_ident.#field_ident.into(), + } + } + } + } } diff --git a/crates/stackable-versioned-macros/src/codegen/item/variant.rs b/crates/stackable-versioned-macros/src/codegen/item/variant.rs index a65168c57..24c62f76b 100644 --- a/crates/stackable-versioned-macros/src/codegen/item/variant.rs +++ b/crates/stackable-versioned-macros/src/codegen/item/variant.rs @@ -133,40 +133,67 @@ impl VersionedVariant { } } - pub(crate) fn generate_for_from_impl( + pub fn generate_for_upgrade_from_impl( &self, version: &VersionDefinition, next_version: &VersionDefinition, enum_ident: &IdentString, ) -> Option { - let variant_fields = match &self.fields { - Fields::Named(fields_named) => { - let fields: Vec<_> = fields_named - .named - .iter() - .map(|field| { - field - .ident - .as_ref() - .expect("named fields always have an ident") - .clone() - }) - .collect(); + let variant_fields = self.fields_as_token_stream(); + + match &self.changes { + Some(changes) => { + let next_change = changes.get_expect(&next_version.inner); + let change = changes.get_expect(&version.inner); + + match (change, next_change) { + (_, ItemStatus::Addition { .. }) => None, + (old, next) => { + let next_version_ident = &next_version.ident; + let old_version_ident = &version.ident; - quote! { { #(#fields),* } } + let next_variant_ident = next.get_ident(); + let old_variant_ident = old.get_ident(); + + let old = quote! { + #old_version_ident::#enum_ident::#old_variant_ident #variant_fields + }; + let next = quote! { + #next_version_ident::#enum_ident::#next_variant_ident #variant_fields + }; + + Some(quote! { + #old => #next, + }) + } + } } - Fields::Unnamed(fields_unnamed) => { - let fields: Vec<_> = fields_unnamed - .unnamed - .iter() - .enumerate() - .map(|(index, _)| format_ident!("__sv_{index}")) - .collect(); + None => { + let next_version_ident = &next_version.ident; + let old_version_ident = &version.ident; + let variant_ident = &*self.ident; + + let old = quote! { + #old_version_ident::#enum_ident::#variant_ident #variant_fields + }; + let next = quote! { + #next_version_ident::#enum_ident::#variant_ident #variant_fields + }; - quote! { ( #(#fields),* ) } + Some(quote! { + #old => #next, + }) } - Fields::Unit => TokenStream::new(), - }; + } + } + + pub fn generate_for_downgrade_from_impl( + &self, + version: &VersionDefinition, + next_version: &VersionDefinition, + enum_ident: &IdentString, + ) -> Option { + let variant_fields = self.fields_as_token_stream(); match &self.changes { Some(changes) => { @@ -190,7 +217,7 @@ impl VersionedVariant { }; Some(quote! { - #old => #next, + #next => #old, }) } } @@ -208,9 +235,39 @@ impl VersionedVariant { }; Some(quote! { - #old => #next, + #next => #old, }) } } } + + fn fields_as_token_stream(&self) -> Option { + match &self.fields { + Fields::Named(fields_named) => { + let fields: Vec<_> = fields_named + .named + .iter() + .map(|field| { + field + .ident + .as_ref() + .expect("named fields always have an ident") + }) + .collect(); + + Some(quote! { { #(#fields),* } }) + } + Fields::Unnamed(fields_unnamed) => { + let fields: Vec<_> = fields_unnamed + .unnamed + .iter() + .enumerate() + .map(|(index, _)| format_ident!("__sv_{index}")) + .collect(); + + Some(quote! { ( #(#fields),* ) }) + } + Fields::Unit => None, + } + } } diff --git a/crates/stackable-versioned-macros/src/codegen/mod.rs b/crates/stackable-versioned-macros/src/codegen/mod.rs index 4f4b2ea3c..88bf340c6 100644 --- a/crates/stackable-versioned-macros/src/codegen/mod.rs +++ b/crates/stackable-versioned-macros/src/codegen/mod.rs @@ -81,7 +81,8 @@ pub enum ItemStatus { ty: Type, }, Change { - convert_with: Option, + downgrade_with: Option, + upgrade_with: Option, from_ident: IdentString, to_ident: IdentString, from_type: Type, @@ -107,7 +108,7 @@ impl ItemStatus { ItemStatus::Change { to_ident, .. } => to_ident, ItemStatus::Deprecation { ident, .. } => ident, ItemStatus::NoChange { ident, .. } => ident, - ItemStatus::NotPresent => unreachable!(), + ItemStatus::NotPresent => unreachable!("ItemStatus::NotPresent does not have an ident"), } } } diff --git a/crates/stackable-versioned-macros/src/codegen/module.rs b/crates/stackable-versioned-macros/src/codegen/module.rs index 217dacced..c940ba059 100644 --- a/crates/stackable-versioned-macros/src/codegen/module.rs +++ b/crates/stackable-versioned-macros/src/codegen/module.rs @@ -160,7 +160,7 @@ impl Module { container_definitions.extend(container.generate_definition(version)); if !self.skip_from { - from_impls.extend(container.generate_from_impl( + from_impls.extend(container.generate_upgrade_from_impl( version, versions.peek().copied(), self.preserve_module, From 711aabe77514c7cafc669fd7bc86b778bbde6628 Mon Sep 17 00:00:00 2001 From: Techassi Date: Wed, 14 May 2025 18:22:42 +0200 Subject: [PATCH 02/26] feat(stackable-versioned): Generate primitive Status struct --- Cargo.lock | 2 + .../src/attrs/k8s.rs | 11 +++-- .../src/codegen/container/mod.rs | 24 ++++++++-- .../src/codegen/container/struct.rs | 48 ++++++++++++++++--- crates/stackable-versioned/Cargo.toml | 4 ++ crates/stackable-versioned/src/lib.rs | 6 +++ 6 files changed, 80 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0b6637a52..acfec9648 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3211,6 +3211,8 @@ dependencies = [ name = "stackable-versioned" version = "0.7.1" dependencies = [ + "schemars", + "serde", "stackable-versioned-macros", ] diff --git a/crates/stackable-versioned-macros/src/attrs/k8s.rs b/crates/stackable-versioned-macros/src/attrs/k8s.rs index c241b17f7..b48def5ef 100644 --- a/crates/stackable-versioned-macros/src/attrs/k8s.rs +++ b/crates/stackable-versioned-macros/src/attrs/k8s.rs @@ -61,9 +61,10 @@ pub(crate) struct KubernetesSkipArguments { /// This struct contains crate overrides to be passed to `#[kube]`. #[derive(Clone, Debug, FromMeta)] pub(crate) struct KubernetesCrateArguments { - pub(crate) kube_core: Option, - pub(crate) k8s_openapi: Option, - pub(crate) schemars: Option, - pub(crate) serde: Option, - pub(crate) serde_json: Option, + pub kube_core: Option, + pub k8s_openapi: Option, + pub schemars: Option, + pub serde: Option, + pub serde_json: Option, + pub versioned: Option, } diff --git a/crates/stackable-versioned-macros/src/codegen/container/mod.rs b/crates/stackable-versioned-macros/src/codegen/container/mod.rs index fbc7565ba..2e49087f8 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/mod.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/mod.rs @@ -95,7 +95,7 @@ impl Container { /// /// This function only returns `Some` if it is a struct. Enums cannot be used to define /// Kubernetes custom resources. - pub(crate) fn generate_kubernetes_item( + pub fn generate_kubernetes_item( &self, version: &VersionDefinition, ) -> Option<(IdentString, String, TokenStream)> { @@ -109,7 +109,7 @@ impl Container { /// /// This function only returns `Some` if it is a struct. Enums cannot be used to define /// Kubernetes custom resources. - pub(crate) fn generate_kubernetes_merge_crds( + pub fn generate_kubernetes_merge_crds( &self, enum_variant_idents: &[IdentString], enum_variant_strings: &[String], @@ -129,7 +129,14 @@ impl Container { } } - pub(crate) fn get_original_ident(&self) -> &Ident { + pub fn generate_kubernetes_status_struct(&self, vis: &Visibility) -> Option { + match self { + Container::Struct(s) => s.generate_kubernetes_status_struct(vis), + Container::Enum(_) => None, + } + } + + pub fn get_original_ident(&self) -> &Ident { match &self { Container::Struct(s) => s.common.idents.original.as_ident(), Container::Enum(e) => e.common.idents.original.as_ident(), @@ -252,6 +259,10 @@ impl StandaloneContainer { false, )); + if self.versions.len() > 1 { + tokens.extend(self.container.generate_kubernetes_status_struct(vis)); + } + tokens } } @@ -344,11 +355,13 @@ pub struct KubernetesCrateOptions { pub schemars: Override, pub serde: Override, pub serde_json: Override, + pub versioned: Override, } impl Default for KubernetesCrateOptions { fn default() -> Self { Self { + versioned: Override::Default(parse_quote! { ::stackable_versioned }), k8s_openapi: Override::Default(parse_quote! { ::k8s_openapi }), serde_json: Override::Default(parse_quote! { ::serde_json }), kube_core: Override::Default(parse_quote! { ::kube::core }), @@ -382,6 +395,10 @@ impl From for KubernetesCrateOptions { crate_options.serde = Override::Overridden(serde); } + if let Some(versioned) = args.versioned { + crate_options.versioned = Override::Overridden(versioned); + } + crate_options } } @@ -396,6 +413,7 @@ impl ToTokens for KubernetesCrateOptions { kube_core, schemars, serde, + .. } = self; if let Override::Overridden(k8s_openapi) = k8s_openapi { diff --git a/crates/stackable-versioned-macros/src/codegen/container/struct.rs b/crates/stackable-versioned-macros/src/codegen/container/struct.rs index 07dff52d5..eb2ea23cc 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/struct.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/struct.rs @@ -2,7 +2,7 @@ use std::ops::Not; use darling::{Error, FromAttributes, Result, util::IdentString}; use proc_macro2::TokenStream; -use quote::{ToTokens, quote}; +use quote::{ToTokens, format_ident, quote}; use syn::{Generics, ItemStruct, Path, Visibility, parse_quote}; use crate::{ @@ -317,10 +317,7 @@ impl Struct { // Kubernetes-specific token generation impl Struct { - pub(crate) fn generate_kube_attribute( - &self, - version: &VersionDefinition, - ) -> Option { + pub fn generate_kube_attribute(&self, version: &VersionDefinition) -> Option { match &self.common.options.kubernetes_options { Some(kubernetes_options) => { // Required arguments @@ -371,7 +368,7 @@ impl Struct { } } - pub(crate) fn generate_kubernetes_item( + pub fn generate_kubernetes_item( &self, version: &VersionDefinition, ) -> Option<(IdentString, String, TokenStream)> { @@ -396,7 +393,7 @@ impl Struct { } } - pub(crate) fn generate_kubernetes_merge_crds( + pub fn generate_kubernetes_merge_crds( &self, enum_variant_idents: &[IdentString], enum_variant_strings: &[String], @@ -446,4 +443,41 @@ impl Struct { _ => None, } } + + pub fn generate_kubernetes_status_struct(&self, vis: &Visibility) -> Option { + match &self.common.options.kubernetes_options { + Some(kubernetes_options) => { + let status_ident = format_ident!( + "{struct_ident}Status", + struct_ident = self.common.idents.kubernetes.as_ident() + ); + + let versioned_crate = &*kubernetes_options.crates.versioned; + let schemars_crate = &*kubernetes_options.crates.schemars; + let serde_crate = &*kubernetes_options.crates.serde; + + // FIXME (@Techassi): This needs to be set in the #[kube] attribute + match &kubernetes_options.status { + Some(status) => Some(quote! { + #[derive(Clone, Debug, #serde_crate::Deserialize, #serde_crate::Serialize, #schemars_crate::JsonSchema)] + #[serde(rename_all = "camelCase")] + #vis struct #status_ident { + pub crd_changes: #versioned_crate::CrdChanges, + + #[serde(flatten)] + pub status: #status, + } + }), + None => Some(quote! { + #[derive(Clone, Debug, #serde_crate::Deserialize, #serde_crate::Serialize, #schemars_crate::JsonSchema)] + #[serde(rename_all = "camelCase")] + #vis struct #status_ident { + pub crd_changes: #versioned_crate::CrdChanges, + } + }), + } + } + None => None, + } + } } diff --git a/crates/stackable-versioned/Cargo.toml b/crates/stackable-versioned/Cargo.toml index 9f4327f31..d12467ff1 100644 --- a/crates/stackable-versioned/Cargo.toml +++ b/crates/stackable-versioned/Cargo.toml @@ -14,7 +14,11 @@ all-features = true full = ["k8s"] k8s = [ "stackable-versioned-macros/k8s", # Forward the k8s feature to the underlying macro crate + "dep:schemars", + "dep:serde", ] [dependencies] stackable-versioned-macros = { path = "../stackable-versioned-macros" } +schemars = { workspace = true, optional = true } +serde = { workspace = true, optional = true } diff --git a/crates/stackable-versioned/src/lib.rs b/crates/stackable-versioned/src/lib.rs index 8c0c399b1..1dc08322b 100644 --- a/crates/stackable-versioned/src/lib.rs +++ b/crates/stackable-versioned/src/lib.rs @@ -24,3 +24,9 @@ pub trait AsVersionStr { Self::VERSION } } + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct CrdChanges { + pub downgrades: Vec, + pub upgrades: Vec, +} From 92ed46c09f538e5680c13fc3d3b76e4879ad7647 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 15 May 2025 18:38:06 +0200 Subject: [PATCH 03/26] feat(stackable-versioned): Emit status struct --- Cargo.lock | 3 + crates/stackable-versioned-macros/Cargo.toml | 3 +- .../src/codegen/container/mod.rs | 21 ++++-- .../src/codegen/container/struct.rs | 69 ++++++++++++++----- .../src/codegen/module.rs | 4 +- crates/stackable-versioned/Cargo.toml | 6 ++ crates/stackable-versioned/src/lib.rs | 42 ++++++++--- 7 files changed, 111 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index acfec9648..c7821988c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3211,8 +3211,11 @@ dependencies = [ name = "stackable-versioned" version = "0.7.1" dependencies = [ + "k8s-version", "schemars", "serde", + "serde_json", + "serde_yaml", "stackable-versioned-macros", ] diff --git a/crates/stackable-versioned-macros/Cargo.toml b/crates/stackable-versioned-macros/Cargo.toml index 123679118..4b4996642 100644 --- a/crates/stackable-versioned-macros/Cargo.toml +++ b/crates/stackable-versioned-macros/Cargo.toml @@ -43,8 +43,9 @@ quote.workspace = true [dev-dependencies] # Only needed for doc tests / examples stackable-versioned = { path = "../stackable-versioned", features = ["k8s"] } -k8s-openapi.workspace = true +k8s-openapi.workspace = true +kube.workspace = true insta.workspace = true prettyplease.workspace = true regex.workspace = true diff --git a/crates/stackable-versioned-macros/src/codegen/container/mod.rs b/crates/stackable-versioned-macros/src/codegen/container/mod.rs index 2e49087f8..903f5f2fc 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/mod.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/mod.rs @@ -44,9 +44,13 @@ pub enum Container { impl Container { /// Generates the container definition for the specified `version`. - pub(crate) fn generate_definition(&self, version: &VersionDefinition) -> TokenStream { + pub(crate) fn generate_definition( + &self, + version: &VersionDefinition, + multiple_versions: bool, + ) -> TokenStream { match self { - Container::Struct(s) => s.generate_definition(version), + Container::Struct(s) => s.generate_definition(version, multiple_versions), Container::Enum(e) => e.generate_definition(version), } } @@ -129,9 +133,9 @@ impl Container { } } - pub fn generate_kubernetes_status_struct(&self, vis: &Visibility) -> Option { + pub fn generate_kubernetes_status_struct(&self) -> Option { match self { - Container::Struct(s) => s.generate_kubernetes_status_struct(vis), + Container::Struct(s) => s.generate_kubernetes_status_struct(), Container::Enum(_) => None, } } @@ -202,9 +206,12 @@ impl StandaloneContainer { let mut kubernetes_enum_variant_strings = Vec::new(); let mut versions = self.versions.iter().peekable(); + let multiple_versions = versions.len() > 1; while let Some(version) = versions.next() { - let container_definition = self.container.generate_definition(version); + let container_definition = self + .container + .generate_definition(version, multiple_versions); // NOTE (@Techassi): Using '.copied()' here does not copy or clone the data, but instead // removes one level of indirection of the double reference '&&'. @@ -259,8 +266,8 @@ impl StandaloneContainer { false, )); - if self.versions.len() > 1 { - tokens.extend(self.container.generate_kubernetes_status_struct(vis)); + if multiple_versions { + tokens.extend(self.container.generate_kubernetes_status_struct()); } tokens diff --git a/crates/stackable-versioned-macros/src/codegen/container/struct.rs b/crates/stackable-versioned-macros/src/codegen/container/struct.rs index eb2ea23cc..dfcb6f6bb 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/struct.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/struct.rs @@ -119,7 +119,7 @@ impl Container { } /// A versioned struct. -pub(crate) struct Struct { +pub struct Struct { /// List of fields defined in the original struct. How, and if, an item /// should generate code, is decided by the currently generated version. pub fields: Vec, @@ -134,7 +134,11 @@ pub(crate) struct Struct { // Common token generation impl Struct { /// Generates code for the struct definition. - pub fn generate_definition(&self, version: &VersionDefinition) -> TokenStream { + pub fn generate_definition( + &self, + version: &VersionDefinition, + multiple_versions: bool, + ) -> TokenStream { let where_clause = self.generics.where_clause.as_ref(); let type_generics = &self.generics; @@ -148,7 +152,7 @@ impl Struct { } // This only returns Some, if K8s features are enabled - let kube_attribute = self.generate_kube_attribute(version); + let kube_attribute = self.generate_kube_attribute(version, multiple_versions); quote! { #(#[doc = #version_docs])* @@ -315,9 +319,15 @@ impl Struct { } } +// TODO (@Techassi): Somehow bundle this into one struct which can emit all K8s related code. This +// makes keeping track of interconnected parts easier. // Kubernetes-specific token generation impl Struct { - pub fn generate_kube_attribute(&self, version: &VersionDefinition) -> Option { + pub fn generate_kube_attribute( + &self, + version: &VersionDefinition, + multiple_versions: bool, + ) -> Option { match &self.common.options.kubernetes_options { Some(kubernetes_options) => { // Required arguments @@ -335,18 +345,30 @@ impl Struct { .singular .as_ref() .map(|s| quote! { , singular = #s }); + let plural = kubernetes_options .plural .as_ref() .map(|p| quote! { , plural = #p }); + let namespaced = kubernetes_options .namespaced .then_some(quote! { , namespaced }); let crates = kubernetes_options.crates.to_token_stream(); - let status = kubernetes_options - .status - .as_ref() - .map(|s| quote! { , status = #s }); + + let status = if multiple_versions { + let status_ident = format_ident!( + "{struct_ident}Status", + struct_ident = self.common.idents.kubernetes.as_ident() + ); + Some(quote! { , status = #status_ident }) + } else { + kubernetes_options + .status + .as_ref() + .map(|s| quote! { , status = #s }) + }; + let shortnames: TokenStream = kubernetes_options .shortnames .iter() @@ -374,7 +396,7 @@ impl Struct { ) -> Option<(IdentString, String, TokenStream)> { match &self.common.options.kubernetes_options { Some(options) if !options.skip_merged_crd => { - let kube_core_path = &*options.crates.kube_core; + let kube_core_crate = &*options.crates.kube_core; let enum_variant_ident = version.inner.as_variant_ident(); let enum_variant_string = version.inner.to_string(); @@ -384,7 +406,7 @@ impl Struct { let qualified_path: Path = parse_quote!(#module_ident::#struct_ident); let merge_crds_fn_call = quote! { - <#qualified_path as #kube_core_path::CustomResourceExt>::crd() + <#qualified_path as #kube_core_crate::CustomResourceExt>::crd() }; Some((enum_variant_ident, enum_variant_string, merge_crds_fn_call)) @@ -444,7 +466,7 @@ impl Struct { } } - pub fn generate_kubernetes_status_struct(&self, vis: &Visibility) -> Option { + pub fn generate_kubernetes_status_struct(&self) -> Option { match &self.common.options.kubernetes_options { Some(kubernetes_options) => { let status_ident = format_ident!( @@ -456,23 +478,34 @@ impl Struct { let schemars_crate = &*kubernetes_options.crates.schemars; let serde_crate = &*kubernetes_options.crates.serde; - // FIXME (@Techassi): This needs to be set in the #[kube] attribute match &kubernetes_options.status { Some(status) => Some(quote! { - #[derive(Clone, Debug, #serde_crate::Deserialize, #serde_crate::Serialize, #schemars_crate::JsonSchema)] + #[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + #serde_crate::Deserialize, + #serde_crate::Serialize, + #schemars_crate::JsonSchema + )] #[serde(rename_all = "camelCase")] - #vis struct #status_ident { - pub crd_changes: #versioned_crate::CrdChanges, + pub struct #status_ident { + pub crd_values: #versioned_crate::CrdValues, #[serde(flatten)] pub status: #status, } }), None => Some(quote! { - #[derive(Clone, Debug, #serde_crate::Deserialize, #serde_crate::Serialize, #schemars_crate::JsonSchema)] + #[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + #serde_crate::Deserialize, + #serde_crate::Serialize, + #schemars_crate::JsonSchema + )] #[serde(rename_all = "camelCase")] - #vis struct #status_ident { - pub crd_changes: #versioned_crate::CrdChanges, + pub struct #status_ident { + pub crd_values: #versioned_crate::CrdValues, } }), } diff --git a/crates/stackable-versioned-macros/src/codegen/module.rs b/crates/stackable-versioned-macros/src/codegen/module.rs index c940ba059..d75e0b08d 100644 --- a/crates/stackable-versioned-macros/src/codegen/module.rs +++ b/crates/stackable-versioned-macros/src/codegen/module.rs @@ -149,6 +149,7 @@ impl Module { let mut kubernetes_container_items: HashMap = HashMap::new(); let mut versions = self.versions.iter().peekable(); + let multiple_versions = self.versions.len() > 1; while let Some(version) = versions.next() { let mut container_definitions = TokenStream::new(); @@ -157,7 +158,8 @@ impl Module { let version_ident = &version.ident; for container in &self.containers { - container_definitions.extend(container.generate_definition(version)); + container_definitions + .extend(container.generate_definition(version, multiple_versions)); if !self.skip_from { from_impls.extend(container.generate_upgrade_from_impl( diff --git a/crates/stackable-versioned/Cargo.toml b/crates/stackable-versioned/Cargo.toml index d12467ff1..b5dc5218b 100644 --- a/crates/stackable-versioned/Cargo.toml +++ b/crates/stackable-versioned/Cargo.toml @@ -14,11 +14,17 @@ all-features = true full = ["k8s"] k8s = [ "stackable-versioned-macros/k8s", # Forward the k8s feature to the underlying macro crate + "dep:k8s-version", + "dep:serde_json", + "dep:serde_yaml", "dep:schemars", "dep:serde", ] [dependencies] +k8s-version = { path = "../k8s-version", features = ["serde"], optional = true } stackable-versioned-macros = { path = "../stackable-versioned-macros" } schemars = { workspace = true, optional = true } serde = { workspace = true, optional = true } +serde_json = { workspace = true, optional = true } +serde_yaml = { workspace = true, optional = true } diff --git a/crates/stackable-versioned/src/lib.rs b/crates/stackable-versioned/src/lib.rs index 1dc08322b..b324dbf5b 100644 --- a/crates/stackable-versioned/src/lib.rs +++ b/crates/stackable-versioned/src/lib.rs @@ -12,21 +12,43 @@ //! See [`versioned`] for an in-depth usage guide and a list of supported //! parameters. +use std::collections::HashMap; + +use k8s_version::Version; +use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec}; // Re-export macro pub use stackable_versioned_macros::*; -// Unused for now, might get picked up again in the future. -#[doc(hidden)] -pub trait AsVersionStr { - const VERSION: &'static str; +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct CrdValues { + /// List of values needed when downgrading to a particular version. + pub downgrades: HashMap>, - fn as_version_str(&self) -> &'static str { - Self::VERSION - } + /// List of values needed when upgrading to a particular version. + pub upgrades: HashMap>, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct CrdChanges { - pub downgrades: Vec, - pub upgrades: Vec, +pub struct CrdValue { + /// The name of the field of the custom resource this value is for. + pub name: String, + + /// The value to be used when upgrading or downgrading the custom resource. + #[schemars(schema_with = "raw_object_schema")] + pub value: serde_yaml::Value, +} + +// TODO (@Techassi): Think about where this should live. Basically this already exists in +// stackable-operator, but we cannot use it without depending on it which I would like to +// avoid. +fn raw_object_schema(_: &mut schemars::r#gen::SchemaGenerator) -> Schema { + Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), + extensions: [( + "x-kubernetes-preserve-unknown-fields".to_owned(), + serde_json::Value::Bool(true), + )] + .into(), + ..Default::default() + }) } From 857710fb608e6f06335c2efaf793cb78925962e9 Mon Sep 17 00:00:00 2001 From: Techassi Date: Fri, 16 May 2025 09:12:58 +0200 Subject: [PATCH 04/26] chore: Fix clippy lints --- crates/k8s-version/src/api_version/serde.rs | 2 +- crates/k8s-version/src/level/serde.rs | 2 +- crates/k8s-version/src/version/serde.rs | 2 +- .../src/codegen/container/enum.rs | 10 ++++++---- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/k8s-version/src/api_version/serde.rs b/crates/k8s-version/src/api_version/serde.rs index f9754c842..2ef14822b 100644 --- a/crates/k8s-version/src/api_version/serde.rs +++ b/crates/k8s-version/src/api_version/serde.rs @@ -11,7 +11,7 @@ impl<'de> Deserialize<'de> for ApiVersion { { struct ApiVersionVisitor; - impl<'de> Visitor<'de> for ApiVersionVisitor { + impl Visitor<'_> for ApiVersionVisitor { type Value = ApiVersion; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/crates/k8s-version/src/level/serde.rs b/crates/k8s-version/src/level/serde.rs index 59bda5702..4d1fd8075 100644 --- a/crates/k8s-version/src/level/serde.rs +++ b/crates/k8s-version/src/level/serde.rs @@ -11,7 +11,7 @@ impl<'de> Deserialize<'de> for Level { { struct LevelVisitor; - impl<'de> Visitor<'de> for LevelVisitor { + impl Visitor<'_> for LevelVisitor { type Value = Level; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/crates/k8s-version/src/version/serde.rs b/crates/k8s-version/src/version/serde.rs index 88c7d98c3..c0f3b3c6c 100644 --- a/crates/k8s-version/src/version/serde.rs +++ b/crates/k8s-version/src/version/serde.rs @@ -11,7 +11,7 @@ impl<'de> Deserialize<'de> for Version { { struct VersionVisitor; - impl<'de> Visitor<'de> for VersionVisitor { + impl Visitor<'_> for VersionVisitor { type Value = Version; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { diff --git a/crates/stackable-versioned-macros/src/codegen/container/enum.rs b/crates/stackable-versioned-macros/src/codegen/container/enum.rs index 2c2f4d47d..8a7b8ce2b 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/enum.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/enum.rs @@ -153,8 +153,9 @@ impl Enum { let variants: TokenStream = self .variants .iter() - .map(|v| v.generate_for_upgrade_from_impl(version, next_version, enum_ident)) - .flatten() + .filter_map(|v| { + v.generate_for_upgrade_from_impl(version, next_version, enum_ident) + }) .collect(); // Include allow(deprecated) only when this or the next version is @@ -212,8 +213,9 @@ impl Enum { let variants: TokenStream = self .variants .iter() - .map(|v| v.generate_for_downgrade_from_impl(version, next_version, enum_ident)) - .flatten() + .filter_map(|v| { + v.generate_for_downgrade_from_impl(version, next_version, enum_ident) + }) .collect(); // Only add the #[automatically_derived] attribute only if this impl is used From 14b09f228cbe77680126dd5e3a4355757671f830 Mon Sep 17 00:00:00 2001 From: Techassi Date: Fri, 16 May 2025 10:01:33 +0200 Subject: [PATCH 05/26] fix(stackable-versioned): Emit correct code for modules --- .../stackable-versioned-macros/src/codegen/module.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/stackable-versioned-macros/src/codegen/module.rs b/crates/stackable-versioned-macros/src/codegen/module.rs index d75e0b08d..68b6eacb3 100644 --- a/crates/stackable-versioned-macros/src/codegen/module.rs +++ b/crates/stackable-versioned-macros/src/codegen/module.rs @@ -152,6 +152,7 @@ impl Module { let multiple_versions = self.versions.len() > 1; while let Some(version) = versions.next() { + let next_version = versions.peek().copied(); let mut container_definitions = TokenStream::new(); let mut from_impls = TokenStream::new(); @@ -164,7 +165,13 @@ impl Module { if !self.skip_from { from_impls.extend(container.generate_upgrade_from_impl( version, - versions.peek().copied(), + next_version, + self.preserve_module, + )); + + from_impls.extend(container.generate_downgrade_from_impl( + version, + next_version, self.preserve_module, )); } @@ -230,6 +237,8 @@ impl Module { version_module_vis, self.preserve_module, )); + + kubernetes_tokens.extend(container.generate_kubernetes_status_struct()); } } From 93570c65262ece65336d1dd5e99fe100999ccc47 Mon Sep 17 00:00:00 2001 From: Techassi Date: Fri, 16 May 2025 11:02:59 +0200 Subject: [PATCH 06/26] test(stackable-versioned): Update snapshot tests --- .../fixtures/inputs/default/convert_with.rs | 4 +- ...st__default_snapshots@basic_struct.rs.snap | 41 +++++++ ...st__default_snapshots@convert_with.rs.snap | 12 ++ ...__default_snapshots@deprecate_enum.rs.snap | 36 ++++++ ...default_snapshots@deprecate_struct.rs.snap | 39 ++++++ ...default_snapshots@enum_data_simple.rs.snap | 9 ++ ...efault_snapshots@generics_defaults.rs.snap | 12 ++ ...t__default_snapshots@generics_enum.rs.snap | 12 ++ ..._default_snapshots@generics_module.rs.snap | 22 ++++ ..._default_snapshots@generics_struct.rs.snap | 12 ++ ...os__test__default_snapshots@module.rs.snap | 32 +++++ ..._default_snapshots@module_preserve.rs.snap | 28 +++++ ...os__test__default_snapshots@rename.rs.snap | 18 +++ ...lt_snapshots@skip_from_for_version.rs.snap | 6 + ...shots@skip_from_module_for_version.rs.snap | 12 ++ ..._test__default_snapshots@submodule.rs.snap | 6 + ..._macros__test__k8s_snapshots@basic.rs.snap | 37 +++++- ...est__k8s_snapshots@crate_overrides.rs.snap | 37 +++++- ...macros__test__k8s_snapshots@module.rs.snap | 111 +++++++++++++++++- ...est__k8s_snapshots@module_preserve.rs.snap | 103 +++++++++++++++- ...__test__k8s_snapshots@renamed_kind.rs.snap | 37 +++++- ...d_macros__test__k8s_snapshots@skip.rs.snap | 37 +++++- 22 files changed, 637 insertions(+), 26 deletions(-) diff --git a/crates/stackable-versioned-macros/fixtures/inputs/default/convert_with.rs b/crates/stackable-versioned-macros/fixtures/inputs/default/convert_with.rs index 8055f961a..e5e494656 100644 --- a/crates/stackable-versioned-macros/fixtures/inputs/default/convert_with.rs +++ b/crates/stackable-versioned-macros/fixtures/inputs/default/convert_with.rs @@ -5,8 +5,8 @@ struct Foo { // This tests two additional things: // - that both unquoted and quoted usage works // - that the renamed name does get picked up correctly by the conversion function - changed(since = "v1", from_type = "u16", from_name = "bar", convert_with = u16_to_u32), - changed(since = "v2", from_type = "u32", convert_with = "u32_to_u64") + changed(since = "v1", from_type = "u16", from_name = "bar", upgrade_with = u16_to_u32), + changed(since = "v2", from_type = "u32", upgrade_with = "u32_to_u64") )] baz: u64, } diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@basic_struct.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@basic_struct.rs.snap index 93dea7303..77df445d0 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@basic_struct.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@basic_struct.rs.snap @@ -24,6 +24,16 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + Self { + jjj: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v1beta1 { use super::*; pub struct Foo { @@ -43,6 +53,15 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v1 { use super::*; pub struct Foo { @@ -64,6 +83,17 @@ impl ::std::convert::From for v2::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2::Foo) -> Self { + Self { + foo: __sv_foo.foo.into(), + bar: __sv_foo.deprecated_bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v2 { use super::*; pub struct Foo { @@ -86,6 +116,17 @@ impl ::std::convert::From for v3::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v2::Foo { + fn from(__sv_foo: v3::Foo) -> Self { + Self { + foo: __sv_foo.foo.into(), + deprecated_bar: __sv_foo.deprecated_bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v3 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@convert_with.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@convert_with.rs.snap index ae5a50e8e..175ca2bae 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@convert_with.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@convert_with.rs.snap @@ -19,6 +19,12 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { bar: __sv_foo.baz.into() } + } +} +#[automatically_derived] mod v1 { use super::*; pub struct Foo { @@ -34,6 +40,12 @@ impl ::std::convert::From for v2::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2::Foo) -> Self { + Self { baz: __sv_foo.baz.into() } + } +} +#[automatically_derived] mod v2 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_enum.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_enum.rs.snap index df0e35530..f65c81d4b 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_enum.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_enum.rs.snap @@ -21,6 +21,15 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + match __sv_foo { + v1beta1::Foo::Bar => v1alpha1::Foo::Bar, + v1beta1::Foo::Baz => v1alpha1::Foo::Baz, + } + } +} +#[automatically_derived] mod v1beta1 { use super::*; pub enum Foo { @@ -39,6 +48,15 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + match __sv_foo { + v1::Foo::DeprecatedBar => v1beta1::Foo::Bar, + v1::Foo::Baz => v1beta1::Foo::Baz, + } + } +} +#[automatically_derived] mod v1 { use super::*; pub enum Foo { @@ -58,6 +76,15 @@ impl ::std::convert::From for v2::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2::Foo) -> Self { + match __sv_foo { + v2::Foo::DeprecatedBar => v1::Foo::DeprecatedBar, + v2::Foo::Baz => v1::Foo::Baz, + } + } +} +#[automatically_derived] mod v2 { use super::*; pub enum Foo { @@ -77,6 +104,15 @@ impl ::std::convert::From for v3::Foo { } } #[automatically_derived] +impl ::std::convert::From for v2::Foo { + fn from(__sv_foo: v3::Foo) -> Self { + match __sv_foo { + v3::Foo::DeprecatedBar => v2::Foo::DeprecatedBar, + v3::Foo::Baz => v2::Foo::Baz, + } + } +} +#[automatically_derived] mod v3 { use super::*; pub enum Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_struct.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_struct.rs.snap index c23badb37..0a49feef6 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_struct.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@deprecate_struct.rs.snap @@ -21,6 +21,15 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v1beta1 { use super::*; pub struct Foo { @@ -39,6 +48,16 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1beta1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.deprecated_bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v1 { use super::*; pub struct Foo { @@ -58,6 +77,16 @@ impl ::std::convert::From for v2::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2::Foo) -> Self { + Self { + deprecated_bar: __sv_foo.deprecated_bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v2 { use super::*; pub struct Foo { @@ -77,6 +106,16 @@ impl ::std::convert::From for v3::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v2::Foo { + fn from(__sv_foo: v3::Foo) -> Self { + Self { + deprecated_bar: __sv_foo.deprecated_bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v3 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@enum_data_simple.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@enum_data_simple.rs.snap index d945ef961..d1dc42f2e 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@enum_data_simple.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@enum_data_simple.rs.snap @@ -21,6 +21,15 @@ impl ::std::convert::From for v1alpha2::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1alpha2::Foo) -> Self { + match __sv_foo { + v1alpha2::Foo::Foo => v1alpha1::Foo::Foo, + v1alpha2::Foo::Bar(__sv_0, __sv_1) => v1alpha1::Foo::Bar(__sv_0, __sv_1), + } + } +} +#[automatically_derived] mod v1alpha2 { use super::*; pub enum Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_defaults.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_defaults.rs.snap index aa4910bda..2bba1d1c8 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_defaults.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_defaults.rs.snap @@ -27,6 +27,18 @@ where } } #[automatically_derived] +impl ::std::convert::From> for v1alpha1::Foo +where + T: Default, +{ + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub mod v1 { use super::*; pub struct Foo diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_enum.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_enum.rs.snap index 8adccda04..e344ae245 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_enum.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_enum.rs.snap @@ -27,6 +27,18 @@ where } } #[automatically_derived] +impl ::std::convert::From> for v1alpha1::Foo +where + T: Default, +{ + fn from(__sv_foo: v1::Foo) -> Self { + match __sv_foo { + v1::Foo::Bar(__sv_0) => v1alpha1::Foo::Bar(__sv_0), + v1::Foo::Baz => v1alpha1::Foo::Baz, + } + } +} +#[automatically_derived] pub mod v1 { use super::*; pub enum Foo diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_module.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_module.rs.snap index 72c56daa2..2f53ac95e 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_module.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_module.rs.snap @@ -33,6 +33,17 @@ pub mod versioned { } } } + impl ::std::convert::From> for v1alpha1::Foo + where + T: Default, + { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } + } impl ::std::convert::From> for v1::Boom where T: Default, @@ -44,6 +55,17 @@ pub mod versioned { } } } + impl ::std::convert::From> for v1alpha1::Boom + where + T: Default, + { + fn from(__sv_boom: v1::Boom) -> Self { + match __sv_boom { + v1::Boom::Big(__sv_0) => v1alpha1::Boom::Big(__sv_0), + v1::Boom::Shaq => v1alpha1::Boom::Shaq, + } + } + } pub mod v1 { use super::*; pub struct Foo diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_struct.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_struct.rs.snap index 76781812f..ae4d5417a 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_struct.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@generics_struct.rs.snap @@ -27,6 +27,18 @@ where } } #[automatically_derived] +impl ::std::convert::From> for v1alpha1::Foo +where + T: Default, +{ + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] pub mod v1 { use super::*; pub struct Foo diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module.rs.snap index 5b91b66f9..aa1e55701 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module.rs.snap @@ -25,12 +25,27 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + foo: __sv_foo.foo.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v1::Bar { fn from(__sv_bar: v1alpha1::Bar) -> Self { Self { baz: __sv_bar.baz.into() } } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Bar { + fn from(__sv_bar: v1::Bar) -> Self { + Self { baz: __sv_bar.baz.into() } + } +} +#[automatically_derived] pub(crate) mod v1 { use super::*; pub struct Foo { @@ -54,12 +69,29 @@ impl ::std::convert::From for v2alpha1::Foo { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2alpha1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + foo: __sv_foo.deprecated_foo.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v2alpha1::Bar { fn from(__sv_bar: v1::Bar) -> Self { Self { baz: __sv_bar.baz.into() } } } #[automatically_derived] +impl ::std::convert::From for v1::Bar { + fn from(__sv_bar: v2alpha1::Bar) -> Self { + Self { baz: __sv_bar.baz.into() } + } +} +#[automatically_derived] pub(crate) mod v2alpha1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module_preserve.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module_preserve.rs.snap index b74af9f05..00eef3c5c 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module_preserve.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@module_preserve.rs.snap @@ -24,11 +24,24 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + foo: __sv_foo.foo.into(), + } + } + } impl ::std::convert::From for v1::Bar { fn from(__sv_bar: v1alpha1::Bar) -> Self { Self { baz: __sv_bar.baz.into() } } } + impl ::std::convert::From for v1alpha1::Bar { + fn from(__sv_bar: v1::Bar) -> Self { + Self { baz: __sv_bar.baz.into() } + } + } pub mod v1 { use super::*; pub struct Foo { @@ -50,11 +63,26 @@ pub(crate) mod versioned { } } } + #[allow(deprecated)] + impl ::std::convert::From for v1::Foo { + fn from(__sv_foo: v2alpha1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + foo: __sv_foo.deprecated_foo.into(), + } + } + } impl ::std::convert::From for v2alpha1::Bar { fn from(__sv_bar: v1::Bar) -> Self { Self { baz: __sv_bar.baz.into() } } } + impl ::std::convert::From for v1::Bar { + fn from(__sv_bar: v2alpha1::Bar) -> Self { + Self { baz: __sv_bar.baz.into() } + } + } pub mod v2alpha1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@rename.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@rename.rs.snap index bf200c9f5..13e8a9d93 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@rename.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@rename.rs.snap @@ -21,6 +21,15 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + Self { + bat: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v1beta1 { use super::*; pub struct Foo { @@ -38,6 +47,15 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + bar: __sv_foo.bar.into(), + baz: __sv_foo.baz.into(), + } + } +} +#[automatically_derived] mod v1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_for_version.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_for_version.rs.snap index 1dd6c5a6a..254d0466b 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_for_version.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_for_version.rs.snap @@ -20,6 +20,12 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + Self { baz: __sv_foo.baz.into() } + } +} +#[automatically_derived] pub mod v1beta1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_module_for_version.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_module_for_version.rs.snap index 8db99d15c..a04ee29a0 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_module_for_version.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@skip_from_module_for_version.rs.snap @@ -23,6 +23,12 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + Self { bar: __sv_foo.bar.into() } + } +} +#[automatically_derived] impl ::std::convert::From for v1beta1::Bar { fn from(__sv_bar: v1alpha1::Bar) -> Self { Self { @@ -32,6 +38,12 @@ impl ::std::convert::From for v1beta1::Bar { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Bar { + fn from(__sv_bar: v1beta1::Bar) -> Self { + Self { foo: __sv_bar.foo.into() } + } +} +#[automatically_derived] pub(crate) mod v1beta1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@submodule.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@submodule.rs.snap index 7b6b9bc3d..f69c2970b 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@submodule.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__default_snapshots@submodule.rs.snap @@ -18,6 +18,12 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { bar: __sv_foo.bar.into() } + } +} +#[automatically_derived] mod v1 { use super::*; pub struct Foo { diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap index 83bab4878..a9b098bb5 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap @@ -20,7 +20,8 @@ pub(crate) mod v1alpha1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub baz: bool, @@ -36,6 +37,14 @@ impl ::std::convert::From for v1beta1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1beta1::FooSpec) -> Self { + Self { + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v1beta1 { use super::*; #[derive( @@ -52,7 +61,8 @@ pub(crate) mod v1beta1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bah: u16, @@ -69,6 +79,15 @@ impl ::std::convert::From for v1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bah: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub(crate) mod v1 { use super::*; #[derive( @@ -85,7 +104,8 @@ pub(crate) mod v1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -130,3 +150,14 @@ impl Foo { ) } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap index 2999586ad..c30e7caa9 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap @@ -21,7 +21,8 @@ pub mod v1alpha1 { singular = "foo", plural = "foos", namespaced, - crates(kube_core = ::kube::core) + crates(kube_core = ::kube::core), + status = FooStatus )] pub struct FooSpec { pub baz: bool, @@ -37,6 +38,14 @@ impl ::std::convert::From for v1beta1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1beta1::FooSpec) -> Self { + Self { + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1beta1 { use super::*; #[derive( @@ -54,7 +63,8 @@ pub mod v1beta1 { singular = "foo", plural = "foos", namespaced, - crates(kube_core = ::kube::core) + crates(kube_core = ::kube::core), + status = FooStatus )] pub struct FooSpec { pub bah: u16, @@ -71,6 +81,15 @@ impl ::std::convert::From for v1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bah: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1 { use super::*; #[derive( @@ -88,7 +107,8 @@ pub mod v1 { singular = "foo", plural = "foos", namespaced, - crates(kube_core = ::kube::core) + crates(kube_core = ::kube::core), + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -133,3 +153,14 @@ impl Foo { ) } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap index d01dbc544..be85bce45 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap @@ -22,7 +22,8 @@ pub(crate) mod v1alpha1 { version = "v1alpha1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -40,7 +41,8 @@ pub(crate) mod v1alpha1 { group = "bar.example.org", version = "v1alpha1", kind = "Bar", - plural = "bars" + plural = "bars", + status = BarStatus )] pub struct BarSpec { pub baz: String, @@ -57,6 +59,12 @@ impl ::std::convert::From for v1::Baz { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Baz { + fn from(__sv_baz: v1::Baz) -> Self { + Self { boom: __sv_baz.boom.into() } + } +} +#[automatically_derived] impl ::std::convert::From for v1::FooSpec { fn from(__sv_foospec: v1alpha1::FooSpec) -> Self { Self { @@ -67,6 +75,15 @@ impl ::std::convert::From for v1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bar: __sv_foospec.bar.into(), + foo: __sv_foospec.foo.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v1::BarSpec { fn from(__sv_barspec: v1alpha1::BarSpec) -> Self { Self { @@ -75,6 +92,14 @@ impl ::std::convert::From for v1::BarSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::BarSpec { + fn from(__sv_barspec: v1::BarSpec) -> Self { + Self { + baz: __sv_barspec.baz.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v1::Boom { fn from(__sv_boom: v1alpha1::Boom) -> Self { match __sv_boom { @@ -84,6 +109,15 @@ impl ::std::convert::From for v1::Boom { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Boom { + fn from(__sv_boom: v1::Boom) -> Self { + match __sv_boom { + v1::Boom::Big => v1alpha1::Boom::Big, + v1::Boom::Shaq => v1alpha1::Boom::Shaq, + } + } +} +#[automatically_derived] pub(crate) mod v1 { use super::*; pub struct Baz { @@ -102,7 +136,8 @@ pub(crate) mod v1 { version = "v1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -117,7 +152,13 @@ pub(crate) mod v1 { schemars::JsonSchema, kube::CustomResource, )] - #[kube(group = "bar.example.org", version = "v1", kind = "Bar", plural = "bars")] + #[kube( + group = "bar.example.org", + version = "v1", + kind = "Bar", + plural = "bars", + status = BarStatus + )] pub struct BarSpec { pub baz: String, } @@ -133,6 +174,12 @@ impl ::std::convert::From for v2alpha1::Baz { } } #[automatically_derived] +impl ::std::convert::From for v1::Baz { + fn from(__sv_baz: v2alpha1::Baz) -> Self { + Self { boom: __sv_baz.boom.into() } + } +} +#[automatically_derived] #[allow(deprecated)] impl ::std::convert::From for v2alpha1::FooSpec { fn from(__sv_foospec: v1::FooSpec) -> Self { @@ -144,6 +191,17 @@ impl ::std::convert::From for v2alpha1::FooSpec { } } #[automatically_derived] +#[allow(deprecated)] +impl ::std::convert::From for v1::FooSpec { + fn from(__sv_foospec: v2alpha1::FooSpec) -> Self { + Self { + bar: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + foo: __sv_foospec.deprecated_foo.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v2alpha1::BarSpec { fn from(__sv_barspec: v1::BarSpec) -> Self { Self { @@ -152,6 +210,14 @@ impl ::std::convert::From for v2alpha1::BarSpec { } } #[automatically_derived] +impl ::std::convert::From for v1::BarSpec { + fn from(__sv_barspec: v2alpha1::BarSpec) -> Self { + Self { + baz: __sv_barspec.baz.into(), + } + } +} +#[automatically_derived] impl ::std::convert::From for v2alpha1::Boom { fn from(__sv_boom: v1::Boom) -> Self { match __sv_boom { @@ -161,6 +227,15 @@ impl ::std::convert::From for v2alpha1::Boom { } } #[automatically_derived] +impl ::std::convert::From for v1::Boom { + fn from(__sv_boom: v2alpha1::Boom) -> Self { + match __sv_boom { + v2alpha1::Boom::Big => v1::Boom::Big, + v2alpha1::Boom::Shaq => v1::Boom::Shaq, + } + } +} +#[automatically_derived] pub(crate) mod v2alpha1 { use super::*; pub struct Baz { @@ -179,7 +254,8 @@ pub(crate) mod v2alpha1 { version = "v2alpha1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -199,7 +275,8 @@ pub(crate) mod v2alpha1 { group = "bar.example.org", version = "v2alpha1", kind = "Bar", - plural = "bars" + plural = "bars", + status = BarStatus )] pub struct BarSpec { pub baz: String, @@ -247,6 +324,17 @@ impl Foo { ) } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} #[automatically_derived] pub(crate) enum Bar { V1Alpha1, @@ -285,3 +373,14 @@ impl Bar { ) } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct BarStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap index 601a8a0a9..6df68fd8c 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap @@ -23,7 +23,8 @@ pub(crate) mod versioned { version = "v1alpha1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -41,7 +42,8 @@ pub(crate) mod versioned { group = "bar.example.org", version = "v1alpha1", kind = "Bar", - plural = "bars" + plural = "bars", + status = BarStatus )] pub struct BarSpec { pub baz: String, @@ -56,6 +58,11 @@ pub(crate) mod versioned { Self { boom: __sv_baz.boom.into() } } } + impl ::std::convert::From for v1alpha1::Baz { + fn from(__sv_baz: v1::Baz) -> Self { + Self { boom: __sv_baz.boom.into() } + } + } impl ::std::convert::From for v1::FooSpec { fn from(__sv_foospec: v1alpha1::FooSpec) -> Self { Self { @@ -65,6 +72,14 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bar: __sv_foospec.bar.into(), + foo: __sv_foospec.foo.into(), + } + } + } impl ::std::convert::From for v1::BarSpec { fn from(__sv_barspec: v1alpha1::BarSpec) -> Self { Self { @@ -72,6 +87,13 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1alpha1::BarSpec { + fn from(__sv_barspec: v1::BarSpec) -> Self { + Self { + baz: __sv_barspec.baz.into(), + } + } + } impl ::std::convert::From for v1::Boom { fn from(__sv_boom: v1alpha1::Boom) -> Self { match __sv_boom { @@ -80,6 +102,14 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1alpha1::Boom { + fn from(__sv_boom: v1::Boom) -> Self { + match __sv_boom { + v1::Boom::Big => v1alpha1::Boom::Big, + v1::Boom::Shaq => v1alpha1::Boom::Shaq, + } + } + } pub mod v1 { use super::*; pub struct Baz { @@ -98,7 +128,8 @@ pub(crate) mod versioned { version = "v1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -113,7 +144,13 @@ pub(crate) mod versioned { schemars::JsonSchema, kube::CustomResource, )] - #[kube(group = "bar.example.org", version = "v1", kind = "Bar", plural = "bars")] + #[kube( + group = "bar.example.org", + version = "v1", + kind = "Bar", + plural = "bars", + status = BarStatus + )] pub struct BarSpec { pub baz: String, } @@ -127,6 +164,11 @@ pub(crate) mod versioned { Self { boom: __sv_baz.boom.into() } } } + impl ::std::convert::From for v1::Baz { + fn from(__sv_baz: v2alpha1::Baz) -> Self { + Self { boom: __sv_baz.boom.into() } + } + } #[allow(deprecated)] impl ::std::convert::From for v2alpha1::FooSpec { fn from(__sv_foospec: v1::FooSpec) -> Self { @@ -137,6 +179,16 @@ pub(crate) mod versioned { } } } + #[allow(deprecated)] + impl ::std::convert::From for v1::FooSpec { + fn from(__sv_foospec: v2alpha1::FooSpec) -> Self { + Self { + bar: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + foo: __sv_foospec.deprecated_foo.into(), + } + } + } impl ::std::convert::From for v2alpha1::BarSpec { fn from(__sv_barspec: v1::BarSpec) -> Self { Self { @@ -144,6 +196,13 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1::BarSpec { + fn from(__sv_barspec: v2alpha1::BarSpec) -> Self { + Self { + baz: __sv_barspec.baz.into(), + } + } + } impl ::std::convert::From for v2alpha1::Boom { fn from(__sv_boom: v1::Boom) -> Self { match __sv_boom { @@ -152,6 +211,14 @@ pub(crate) mod versioned { } } } + impl ::std::convert::From for v1::Boom { + fn from(__sv_boom: v2alpha1::Boom) -> Self { + match __sv_boom { + v2alpha1::Boom::Big => v1::Boom::Big, + v2alpha1::Boom::Shaq => v1::Boom::Shaq, + } + } + } pub mod v2alpha1 { use super::*; pub struct Baz { @@ -170,7 +237,8 @@ pub(crate) mod versioned { version = "v2alpha1", kind = "Foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, @@ -190,7 +258,8 @@ pub(crate) mod versioned { group = "bar.example.org", version = "v2alpha1", kind = "Bar", - plural = "bars" + plural = "bars", + status = BarStatus )] pub struct BarSpec { pub baz: String, @@ -235,6 +304,17 @@ pub(crate) mod versioned { ) } } + #[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema + )] + #[serde(rename_all = "camelCase")] + pub struct FooStatus { + pub crd_values: ::stackable_versioned::CrdValues, + } pub enum Bar { V1Alpha1, V1, @@ -270,4 +350,15 @@ pub(crate) mod versioned { ) } } + #[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema + )] + #[serde(rename_all = "camelCase")] + pub struct BarStatus { + pub crd_values: ::stackable_versioned::CrdValues, + } } diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap index fbda4713a..e96a7decf 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap @@ -20,7 +20,8 @@ pub mod v1alpha1 { kind = "FooBar", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooBarStatus )] pub struct FooSpec { pub baz: bool, @@ -36,6 +37,14 @@ impl ::std::convert::From for v1beta1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1beta1::FooSpec) -> Self { + Self { + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1beta1 { use super::*; #[derive( @@ -52,7 +61,8 @@ pub mod v1beta1 { kind = "FooBar", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooBarStatus )] pub struct FooSpec { pub bah: u16, @@ -69,6 +79,15 @@ impl ::std::convert::From for v1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bah: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1 { use super::*; #[derive( @@ -85,7 +104,8 @@ pub mod v1 { kind = "FooBar", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooBarStatus )] pub struct FooSpec { pub bar: usize, @@ -130,3 +150,14 @@ impl FooBar { ) } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooBarStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap index 39f0b2263..118f1ae44 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap @@ -20,7 +20,8 @@ pub mod v1alpha1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub baz: bool, @@ -36,6 +37,14 @@ impl ::std::convert::From for v1beta1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1beta1::FooSpec) -> Self { + Self { + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1beta1 { use super::*; #[derive( @@ -52,7 +61,8 @@ pub mod v1beta1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bah: u16, @@ -69,6 +79,15 @@ impl ::std::convert::From for v1::FooSpec { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bah: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] pub mod v1 { use super::*; #[derive( @@ -85,10 +104,22 @@ pub mod v1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced + namespaced, + status = FooStatus )] pub struct FooSpec { pub bar: usize, pub baz: bool, } } +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooStatus { + pub crd_values: ::stackable_versioned::CrdValues, +} From 760a7bcceadce858492f8d8b64110e12a69703a3 Mon Sep 17 00:00:00 2001 From: Techassi Date: Fri, 16 May 2025 12:00:13 +0200 Subject: [PATCH 07/26] test(stackable-versioned): Update doc tests --- crates/stackable-versioned-macros/src/lib.rs | 33 +++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/crates/stackable-versioned-macros/src/lib.rs b/crates/stackable-versioned-macros/src/lib.rs index 469ee61b0..a57292f8d 100644 --- a/crates/stackable-versioned-macros/src/lib.rs +++ b/crates/stackable-versioned-macros/src/lib.rs @@ -478,11 +478,16 @@ mod utils; /// #[versioned(changed( /// since = "v1beta1", /// from_name = "prev_bar", -/// from_type = "u16" +/// from_type = "u16", +/// downgrade_with = usize_to_u16 /// ))] /// bar: usize, /// baz: bool, /// } +/// +/// fn usize_to_u16(input: usize) -> u16 { +/// input.try_into().unwrap() +/// } /// ``` /// ///
@@ -591,8 +596,8 @@ mod utils; /// ### Custom conversion function at field level /// /// As stated in the [`changed()`](#changed-action) section, a custom conversion -/// function can be provided using the `convert_with` argument. A simple example -/// looks like this: +/// function can be provided using the `downgrade_with` and `upgrade_with` +/// argument. A simple example looks like this: /// /// ``` /// # use stackable_versioned_macros::versioned; @@ -604,13 +609,13 @@ mod utils; /// #[versioned(changed( /// since = "v1beta1", /// from_type = "u8", -/// convert_with = "u8_to_u16" +/// downgrade_with = "u16_to_u8" /// ))] /// bar: u16, /// } /// -/// fn u8_to_u16(old: u8) -> u16 { -/// old as u16 +/// fn u16_to_u8(input: u16) -> u8 { +/// input.try_into().unwrap() /// } /// ``` /// @@ -628,7 +633,15 @@ mod utils; /// impl ::std::convert::From for v1beta1::Foo { /// fn from(__sv_foo: v1alpha1::Foo) -> Self { /// Self { -/// bar: u8_to_u16(__sv_foo.bar), +/// bar: __sv_foo.bar.into(), +/// } +/// } +/// } +/// +/// impl ::std::convert::From for v1alpha1::Foo { +/// fn from(__sv_foo: v1beta1::Foo) -> Self { +/// Self { +/// bar: u16_to_u8(__sv_foo.bar), /// } /// } /// } @@ -718,12 +731,16 @@ use serde::{Deserialize, Serialize}; pub struct FooSpec { #[versioned( added(since = "v1beta1"), - changed(since = "v1", from_name = "prev_bar", from_type = "u16") + changed(since = "v1", from_name = "prev_bar", from_type = "u16", downgrade_with = usize_to_u16) )] bar: usize, baz: bool, } +fn usize_to_u16(input: usize) -> u16 { + input.try_into().unwrap() +} + # fn main() { let merged_crd = Foo::merged_crd(Foo::V1).unwrap(); println!("{}", serde_yaml::to_string(&merged_crd).unwrap()); From 3fc0b821d8e46a5100211cc2f7eabe86e8bf4698 Mon Sep 17 00:00:00 2001 From: Techassi Date: Fri, 16 May 2025 12:17:11 +0200 Subject: [PATCH 08/26] chore(stackable-versioned): Update changelog --- crates/stackable-versioned/CHANGELOG.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/crates/stackable-versioned/CHANGELOG.md b/crates/stackable-versioned/CHANGELOG.md index 9fee8c70c..0aa3abe84 100644 --- a/crates/stackable-versioned/CHANGELOG.md +++ b/crates/stackable-versioned/CHANGELOG.md @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +### Added + +- Implement basic ground work for downgrading custom resources ([#1033]). + - Emit `From` implementations to downgrade custom resource specs. + - Emit a status struct when multiple versions are defined to be able to track + values required during downgrades and upgrades of custom resources. + +### Changed + +- BREAKING: The `convert_with` parameter of the `changed()` action was renamed and split into two + parts to be able to control the conversion during upgrades and downgrades: `upgrade_with` and + `downgrade_with` ([#1033]). + +[#1033]: https://github.com/stackabletech/operator-rs/pull/1033 + ### Fixed - Correctly handle fields added in later versions ([#1031]). From 6579eb00881b5f9dfcfc9ba7321fa879adea3f85 Mon Sep 17 00:00:00 2001 From: Techassi Date: Wed, 21 May 2025 12:33:34 +0200 Subject: [PATCH 09/26] chore(stackable-versioned): Remove unused dev dependencies --- crates/stackable-versioned-macros/Cargo.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/stackable-versioned-macros/Cargo.toml b/crates/stackable-versioned-macros/Cargo.toml index 4b4996642..9739c4427 100644 --- a/crates/stackable-versioned-macros/Cargo.toml +++ b/crates/stackable-versioned-macros/Cargo.toml @@ -44,8 +44,6 @@ quote.workspace = true # Only needed for doc tests / examples stackable-versioned = { path = "../stackable-versioned", features = ["k8s"] } -k8s-openapi.workspace = true -kube.workspace = true insta.workspace = true prettyplease.workspace = true regex.workspace = true From 4f3a8d8e5589cf235c366e25ea19e64c40dcc633 Mon Sep 17 00:00:00 2001 From: Techassi Date: Wed, 21 May 2025 12:34:51 +0200 Subject: [PATCH 10/26] chore(stackable-versioned): Adjust {upgrade,downgrade}_with validation --- .../src/attrs/item/mod.rs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/crates/stackable-versioned-macros/src/attrs/item/mod.rs b/crates/stackable-versioned-macros/src/attrs/item/mod.rs index e82b2d3fe..bcfb7e512 100644 --- a/crates/stackable-versioned-macros/src/attrs/item/mod.rs +++ b/crates/stackable-versioned-macros/src/attrs/item/mod.rs @@ -238,17 +238,28 @@ impl CommonItemAttributes { } } - // The convert_with argument only makes sense to use when the - // type changed - if let Some(upgrade_func) = change.upgrade_with.as_ref() { - if change.from_type.is_none() { + if change.from_type.is_none() { + // The upgrade_with argument only makes sense to use when the + // type changed + if let Some(upgrade_func) = change.upgrade_with.as_ref() { errors.push( Error::custom( - "the `convert_with` argument must be used in combination with `from_type`", + "the `upgrade_with` argument must be used in combination with `from_type`", ) .with_span(&upgrade_func.span()), ); } + + // The downgrade_with argument only makes sense to use when the + // type changed + if let Some(downgrade_func) = change.downgrade_with.as_ref() { + errors.push( + Error::custom( + "the `downgrade_with` argument must be used in combination with `from_type`", + ) + .with_span(&downgrade_func.span()), + ); + } } } From c448ed270d4c153009129293f962b40b858eceb9 Mon Sep 17 00:00:00 2001 From: Techassi Date: Wed, 21 May 2025 12:36:58 +0200 Subject: [PATCH 11/26] chore(stackable-versioned): Improve K8s code generation --- .../src/codegen/container/struct.rs | 292 +++++++++--------- 1 file changed, 142 insertions(+), 150 deletions(-) diff --git a/crates/stackable-versioned-macros/src/codegen/container/struct.rs b/crates/stackable-versioned-macros/src/codegen/container/struct.rs index dfcb6f6bb..39c4f5a38 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/struct.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/struct.rs @@ -326,92 +326,96 @@ impl Struct { pub fn generate_kube_attribute( &self, version: &VersionDefinition, - multiple_versions: bool, + _multiple_versions: bool, ) -> Option { - match &self.common.options.kubernetes_options { - Some(kubernetes_options) => { - // Required arguments - let group = &kubernetes_options.group; - let version = version.inner.to_string(); - let kind = kubernetes_options - .kind - .as_ref() - .map_or(self.common.idents.kubernetes.to_string(), |kind| { - kind.clone() - }); - - // Optional arguments - let singular = kubernetes_options - .singular - .as_ref() - .map(|s| quote! { , singular = #s }); - - let plural = kubernetes_options - .plural - .as_ref() - .map(|p| quote! { , plural = #p }); - - let namespaced = kubernetes_options - .namespaced - .then_some(quote! { , namespaced }); - let crates = kubernetes_options.crates.to_token_stream(); - - let status = if multiple_versions { - let status_ident = format_ident!( - "{struct_ident}Status", - struct_ident = self.common.idents.kubernetes.as_ident() - ); - Some(quote! { , status = #status_ident }) - } else { - kubernetes_options - .status - .as_ref() - .map(|s| quote! { , status = #s }) - }; - - let shortnames: TokenStream = kubernetes_options - .shortnames - .iter() - .map(|s| quote! { , shortname = #s }) - .collect(); + let kubernetes_options = self.common.options.kubernetes_options.as_ref()?; + + // Required arguments + let group = &kubernetes_options.group; + let version = version.inner.to_string(); + let kind = kubernetes_options + .kind + .as_ref() + .map_or(self.common.idents.kubernetes.to_string(), |kind| { + kind.clone() + }); + + // Optional arguments + let singular = kubernetes_options + .singular + .as_ref() + .map(|s| quote! { , singular = #s }); + + let plural = kubernetes_options + .plural + .as_ref() + .map(|p| quote! { , plural = #p }); + + let namespaced = kubernetes_options + .namespaced + .then_some(quote! { , namespaced }); + let crates = kubernetes_options.crates.to_token_stream(); + + // TODO (@Techassi): Comment back in, once we are happy with the status struct + // let status = if multiple_versions { + // let status_ident = format_ident!( + // "{struct_ident}Status", + // struct_ident = self.common.idents.kubernetes.as_ident() + // ); + // Some(quote! { , status = #status_ident }) + // } else { + // kubernetes_options + // .status + // .as_ref() + // .map(|s| quote! { , status = #s }) + // }; + + let status = kubernetes_options + .status + .as_ref() + .map(|s| quote! { , status = #s }); + + let shortnames: TokenStream = kubernetes_options + .shortnames + .iter() + .map(|s| quote! { , shortname = #s }) + .collect(); - Some(quote! { - // The end-developer needs to derive CustomResource and JsonSchema. - // This is because we don't know if they want to use a re-exported or renamed import. - #[kube( - // These must be comma separated (except the last) as they always exist: - group = #group, version = #version, kind = #kind - // These fields are optional, and therefore the token stream must prefix each with a comma: - #singular #plural #namespaced #crates #status #shortnames - )] - }) - } - None => None, - } + Some(quote! { + // The end-developer needs to derive CustomResource and JsonSchema. + // This is because we don't know if they want to use a re-exported or renamed import. + #[kube( + // These must be comma separated (except the last) as they always exist: + group = #group, version = #version, kind = #kind + // These fields are optional, and therefore the token stream must prefix each with a comma: + #singular #plural #namespaced #crates #status #shortnames + )] + }) } pub fn generate_kubernetes_item( &self, version: &VersionDefinition, ) -> Option<(IdentString, String, TokenStream)> { - match &self.common.options.kubernetes_options { - Some(options) if !options.skip_merged_crd => { - let kube_core_crate = &*options.crates.kube_core; + let kubernetes_options = self.common.options.kubernetes_options.as_ref()?; - let enum_variant_ident = version.inner.as_variant_ident(); - let enum_variant_string = version.inner.to_string(); + if !kubernetes_options.skip_merged_crd { + let kube_core_crate = &*kubernetes_options.crates.kube_core; - let struct_ident = &self.common.idents.kubernetes; - let module_ident = &version.ident; - let qualified_path: Path = parse_quote!(#module_ident::#struct_ident); + let enum_variant_ident = version.inner.as_variant_ident(); + let enum_variant_string = version.inner.to_string(); - let merge_crds_fn_call = quote! { - <#qualified_path as #kube_core_crate::CustomResourceExt>::crd() - }; + let struct_ident = &self.common.idents.kubernetes; + let module_ident = &version.ident; + let qualified_path: Path = parse_quote!(#module_ident::#struct_ident); - Some((enum_variant_ident, enum_variant_string, merge_crds_fn_call)) - } - _ => None, + let merge_crds_fn_call = quote! { + <#qualified_path as #kube_core_crate::CustomResourceExt>::crd() + }; + + Some((enum_variant_ident, enum_variant_string, merge_crds_fn_call)) + } else { + None } } @@ -423,94 +427,82 @@ impl Struct { vis: &Visibility, is_nested: bool, ) -> Option { - match &self.common.options.kubernetes_options { - Some(kubernetes_options) if !kubernetes_options.skip_merged_crd => { - let enum_ident = &self.common.idents.kubernetes; + let kubernetes_options = self.common.options.kubernetes_options.as_ref()?; - // Only add the #[automatically_derived] attribute if this impl is used outside of a - // module (in standalone mode). - let automatically_derived = - is_nested.not().then(|| quote! {#[automatically_derived]}); + if !kubernetes_options.skip_merged_crd { + let enum_ident = &self.common.idents.kubernetes; - // Get the crate paths - let k8s_openapi_path = &*kubernetes_options.crates.k8s_openapi; - let kube_core_path = &*kubernetes_options.crates.kube_core; + // Only add the #[automatically_derived] attribute if this impl is used outside of a + // module (in standalone mode). + let automatically_derived = is_nested.not().then(|| quote! {#[automatically_derived]}); - Some(quote! { - #automatically_derived - #vis enum #enum_ident { - #(#enum_variant_idents),* - } + // Get the crate paths + let k8s_openapi_path = &*kubernetes_options.crates.k8s_openapi; + let kube_core_path = &*kubernetes_options.crates.kube_core; - #automatically_derived - impl ::std::fmt::Display for #enum_ident { - fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<(), ::std::fmt::Error> { - match self { - #(Self::#enum_variant_idents => f.write_str(#enum_variant_strings)),* - } + Some(quote! { + #automatically_derived + #vis enum #enum_ident { + #(#enum_variant_idents),* + } + + #automatically_derived + impl ::std::fmt::Display for #enum_ident { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<(), ::std::fmt::Error> { + match self { + #(Self::#enum_variant_idents => f.write_str(#enum_variant_strings)),* } } + } - #automatically_derived - impl #enum_ident { - /// Generates a merged CRD containing all versions and marking `stored_apiversion` as stored. - pub fn merged_crd( - stored_apiversion: Self - ) -> ::std::result::Result<#k8s_openapi_path::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition, #kube_core_path::crd::MergeError> { - #kube_core_path::crd::merge_crds(vec![#(#fn_calls),*], &stored_apiversion.to_string()) - } + #automatically_derived + impl #enum_ident { + /// Generates a merged CRD containing all versions and marking `stored_apiversion` as stored. + pub fn merged_crd( + stored_apiversion: Self + ) -> ::std::result::Result<#k8s_openapi_path::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition, #kube_core_path::crd::MergeError> { + #kube_core_path::crd::merge_crds(vec![#(#fn_calls),*], &stored_apiversion.to_string()) } - }) - } - _ => None, + } + }) + } else { + None } } pub fn generate_kubernetes_status_struct(&self) -> Option { - match &self.common.options.kubernetes_options { - Some(kubernetes_options) => { - let status_ident = format_ident!( - "{struct_ident}Status", - struct_ident = self.common.idents.kubernetes.as_ident() - ); - - let versioned_crate = &*kubernetes_options.crates.versioned; - let schemars_crate = &*kubernetes_options.crates.schemars; - let serde_crate = &*kubernetes_options.crates.serde; - - match &kubernetes_options.status { - Some(status) => Some(quote! { - #[derive( - ::core::clone::Clone, - ::core::fmt::Debug, - #serde_crate::Deserialize, - #serde_crate::Serialize, - #schemars_crate::JsonSchema - )] - #[serde(rename_all = "camelCase")] - pub struct #status_ident { - pub crd_values: #versioned_crate::CrdValues, - - #[serde(flatten)] - pub status: #status, - } - }), - None => Some(quote! { - #[derive( - ::core::clone::Clone, - ::core::fmt::Debug, - #serde_crate::Deserialize, - #serde_crate::Serialize, - #schemars_crate::JsonSchema - )] - #[serde(rename_all = "camelCase")] - pub struct #status_ident { - pub crd_values: #versioned_crate::CrdValues, - } - }), - } + let kubernetes_options = self.common.options.kubernetes_options.as_ref()?; + + let status_ident = format_ident!( + "{struct_ident}Status", + struct_ident = self.common.idents.kubernetes.as_ident() + ); + + let versioned_crate = &*kubernetes_options.crates.versioned; + let schemars_crate = &*kubernetes_options.crates.schemars; + let serde_crate = &*kubernetes_options.crates.serde; + + let status = kubernetes_options.status.as_ref().map(|status| { + quote! { + #[serde(flatten)] + pub status: #status, } - None => None, - } + }); + + Some(quote! { + #[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + #serde_crate::Deserialize, + #serde_crate::Serialize, + #schemars_crate::JsonSchema + )] + #[serde(rename_all = "camelCase")] + pub struct #status_ident { + pub changed_values: #versioned_crate::ChangedValues, + + #status + } + }) } } From 122e000d275426d4e7fcb04d023aa2531e98ec5d Mon Sep 17 00:00:00 2001 From: Techassi Date: Wed, 21 May 2025 12:45:08 +0200 Subject: [PATCH 12/26] chore(stackable-versioned): Change crd_values to changed_values --- crates/stackable-versioned/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/stackable-versioned/src/lib.rs b/crates/stackable-versioned/src/lib.rs index b324dbf5b..88e73cde7 100644 --- a/crates/stackable-versioned/src/lib.rs +++ b/crates/stackable-versioned/src/lib.rs @@ -20,16 +20,16 @@ use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec}; pub use stackable_versioned_macros::*; #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct CrdValues { +pub struct ChangedValues { /// List of values needed when downgrading to a particular version. - pub downgrades: HashMap>, + pub downgrades: HashMap>, /// List of values needed when upgrading to a particular version. - pub upgrades: HashMap>, + pub upgrades: HashMap>, } #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct CrdValue { +pub struct ChangedValue { /// The name of the field of the custom resource this value is for. pub name: String, From 0f7468b5c5b323e3e175bfc08c3ca43b78f7f5a2 Mon Sep 17 00:00:00 2001 From: Techassi Date: Wed, 21 May 2025 12:45:14 +0200 Subject: [PATCH 13/26] test(stackable-versioned): Update snapshot tests --- ..._macros__test__k8s_snapshots@basic.rs.snap | 11 +++----- ...est__k8s_snapshots@crate_overrides.rs.snap | 11 +++----- ...macros__test__k8s_snapshots@module.rs.snap | 27 ++++++------------- ...est__k8s_snapshots@module_preserve.rs.snap | 27 ++++++------------- ...__test__k8s_snapshots@renamed_kind.rs.snap | 11 +++----- ...d_macros__test__k8s_snapshots@skip.rs.snap | 11 +++----- 6 files changed, 32 insertions(+), 66 deletions(-) diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap index a9b098bb5..c9d43ee66 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@basic.rs.snap @@ -20,8 +20,7 @@ pub(crate) mod v1alpha1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub baz: bool, @@ -61,8 +60,7 @@ pub(crate) mod v1beta1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub bah: u16, @@ -104,8 +102,7 @@ pub(crate) mod v1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub bar: usize, @@ -159,5 +156,5 @@ impl Foo { )] #[serde(rename_all = "camelCase")] pub struct FooStatus { - pub crd_values: ::stackable_versioned::CrdValues, + pub changed_values: ::stackable_versioned::ChangedValues, } diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap index c30e7caa9..4d416b77a 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@crate_overrides.rs.snap @@ -21,8 +21,7 @@ pub mod v1alpha1 { singular = "foo", plural = "foos", namespaced, - crates(kube_core = ::kube::core), - status = FooStatus + crates(kube_core = ::kube::core) )] pub struct FooSpec { pub baz: bool, @@ -63,8 +62,7 @@ pub mod v1beta1 { singular = "foo", plural = "foos", namespaced, - crates(kube_core = ::kube::core), - status = FooStatus + crates(kube_core = ::kube::core) )] pub struct FooSpec { pub bah: u16, @@ -107,8 +105,7 @@ pub mod v1 { singular = "foo", plural = "foos", namespaced, - crates(kube_core = ::kube::core), - status = FooStatus + crates(kube_core = ::kube::core) )] pub struct FooSpec { pub bar: usize, @@ -162,5 +159,5 @@ impl Foo { )] #[serde(rename_all = "camelCase")] pub struct FooStatus { - pub crd_values: ::stackable_versioned::CrdValues, + pub changed_values: ::stackable_versioned::ChangedValues, } diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap index be85bce45..4f7d60f75 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module.rs.snap @@ -22,8 +22,7 @@ pub(crate) mod v1alpha1 { version = "v1alpha1", kind = "Foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub bar: usize, @@ -41,8 +40,7 @@ pub(crate) mod v1alpha1 { group = "bar.example.org", version = "v1alpha1", kind = "Bar", - plural = "bars", - status = BarStatus + plural = "bars" )] pub struct BarSpec { pub baz: String, @@ -136,8 +134,7 @@ pub(crate) mod v1 { version = "v1", kind = "Foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub bar: usize, @@ -152,13 +149,7 @@ pub(crate) mod v1 { schemars::JsonSchema, kube::CustomResource, )] - #[kube( - group = "bar.example.org", - version = "v1", - kind = "Bar", - plural = "bars", - status = BarStatus - )] + #[kube(group = "bar.example.org", version = "v1", kind = "Bar", plural = "bars")] pub struct BarSpec { pub baz: String, } @@ -254,8 +245,7 @@ pub(crate) mod v2alpha1 { version = "v2alpha1", kind = "Foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub bar: usize, @@ -275,8 +265,7 @@ pub(crate) mod v2alpha1 { group = "bar.example.org", version = "v2alpha1", kind = "Bar", - plural = "bars", - status = BarStatus + plural = "bars" )] pub struct BarSpec { pub baz: String, @@ -333,7 +322,7 @@ impl Foo { )] #[serde(rename_all = "camelCase")] pub struct FooStatus { - pub crd_values: ::stackable_versioned::CrdValues, + pub changed_values: ::stackable_versioned::ChangedValues, } #[automatically_derived] pub(crate) enum Bar { @@ -382,5 +371,5 @@ impl Bar { )] #[serde(rename_all = "camelCase")] pub struct BarStatus { - pub crd_values: ::stackable_versioned::CrdValues, + pub changed_values: ::stackable_versioned::ChangedValues, } diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap index 6df68fd8c..6018a160b 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@module_preserve.rs.snap @@ -23,8 +23,7 @@ pub(crate) mod versioned { version = "v1alpha1", kind = "Foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub bar: usize, @@ -42,8 +41,7 @@ pub(crate) mod versioned { group = "bar.example.org", version = "v1alpha1", kind = "Bar", - plural = "bars", - status = BarStatus + plural = "bars" )] pub struct BarSpec { pub baz: String, @@ -128,8 +126,7 @@ pub(crate) mod versioned { version = "v1", kind = "Foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub bar: usize, @@ -144,13 +141,7 @@ pub(crate) mod versioned { schemars::JsonSchema, kube::CustomResource, )] - #[kube( - group = "bar.example.org", - version = "v1", - kind = "Bar", - plural = "bars", - status = BarStatus - )] + #[kube(group = "bar.example.org", version = "v1", kind = "Bar", plural = "bars")] pub struct BarSpec { pub baz: String, } @@ -237,8 +228,7 @@ pub(crate) mod versioned { version = "v2alpha1", kind = "Foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub bar: usize, @@ -258,8 +248,7 @@ pub(crate) mod versioned { group = "bar.example.org", version = "v2alpha1", kind = "Bar", - plural = "bars", - status = BarStatus + plural = "bars" )] pub struct BarSpec { pub baz: String, @@ -313,7 +302,7 @@ pub(crate) mod versioned { )] #[serde(rename_all = "camelCase")] pub struct FooStatus { - pub crd_values: ::stackable_versioned::CrdValues, + pub changed_values: ::stackable_versioned::ChangedValues, } pub enum Bar { V1Alpha1, @@ -359,6 +348,6 @@ pub(crate) mod versioned { )] #[serde(rename_all = "camelCase")] pub struct BarStatus { - pub crd_values: ::stackable_versioned::CrdValues, + pub changed_values: ::stackable_versioned::ChangedValues, } } diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap index e96a7decf..c66ac4eed 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@renamed_kind.rs.snap @@ -20,8 +20,7 @@ pub mod v1alpha1 { kind = "FooBar", singular = "foo", plural = "foos", - namespaced, - status = FooBarStatus + namespaced )] pub struct FooSpec { pub baz: bool, @@ -61,8 +60,7 @@ pub mod v1beta1 { kind = "FooBar", singular = "foo", plural = "foos", - namespaced, - status = FooBarStatus + namespaced )] pub struct FooSpec { pub bah: u16, @@ -104,8 +102,7 @@ pub mod v1 { kind = "FooBar", singular = "foo", plural = "foos", - namespaced, - status = FooBarStatus + namespaced )] pub struct FooSpec { pub bar: usize, @@ -159,5 +156,5 @@ impl FooBar { )] #[serde(rename_all = "camelCase")] pub struct FooBarStatus { - pub crd_values: ::stackable_versioned::CrdValues, + pub changed_values: ::stackable_versioned::ChangedValues, } diff --git a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap index 118f1ae44..114599060 100644 --- a/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap +++ b/crates/stackable-versioned-macros/fixtures/snapshots/stackable_versioned_macros__test__k8s_snapshots@skip.rs.snap @@ -20,8 +20,7 @@ pub mod v1alpha1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub baz: bool, @@ -61,8 +60,7 @@ pub mod v1beta1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub bah: u16, @@ -104,8 +102,7 @@ pub mod v1 { kind = "Foo", singular = "foo", plural = "foos", - namespaced, - status = FooStatus + namespaced )] pub struct FooSpec { pub bar: usize, @@ -121,5 +118,5 @@ pub mod v1 { )] #[serde(rename_all = "camelCase")] pub struct FooStatus { - pub crd_values: ::stackable_versioned::CrdValues, + pub changed_values: ::stackable_versioned::ChangedValues, } From 634b977d69a32ed4947848ec7b61950da36964bc Mon Sep 17 00:00:00 2001 From: Techassi Date: Wed, 21 May 2025 12:57:35 +0200 Subject: [PATCH 14/26] chore(stackable-versioned): Clean up changelog --- crates/stackable-versioned/CHANGELOG.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/crates/stackable-versioned/CHANGELOG.md b/crates/stackable-versioned/CHANGELOG.md index 9d3a898a9..de6dde736 100644 --- a/crates/stackable-versioned/CHANGELOG.md +++ b/crates/stackable-versioned/CHANGELOG.md @@ -8,14 +8,15 @@ All notable changes to this project will be documented in this file. - Implement basic ground work for downgrading custom resources ([#1033]). - Emit `From` implementations to downgrade custom resource specs. - - Emit a status struct when multiple versions are defined to be able to track - values required during downgrades and upgrades of custom resources. - -### Changed + - Emit a status struct when multiple versions are defined to be able to track values required + during downgrades and upgrades of custom resources. - Add `kube_client` crate override to `k8s(crates())` to specify a custom import path. This override will not be passed to the `#[kube()]` attribute, but will only be available to internal `#[versioned]` macro code ([#1038]). + +### Changed + - BREAKING: The `convert_with` parameter of the `changed()` action was renamed and split into two parts to be able to control the conversion during upgrades and downgrades: `upgrade_with` and `downgrade_with` ([#1033]). From 7fd48082aaf748ac59d3e1ce3978586ca41adaa7 Mon Sep 17 00:00:00 2001 From: Techassi Date: Wed, 21 May 2025 12:57:57 +0200 Subject: [PATCH 15/26] test(stackable-versioned): Update 'added' snapshot test --- ...ros__snapshot_tests__default@added.rs.snap | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@added.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@added.rs.snap index 01950c0b4..95a89142a 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@added.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@added.rs.snap @@ -20,6 +20,14 @@ impl ::std::convert::From for v1alpha2::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha1::Foo { + fn from(__sv_foo: v1alpha2::Foo) -> Self { + Self { + username: __sv_foo.username.into(), + } + } +} +#[automatically_derived] mod v1alpha2 { use super::*; pub struct Foo { @@ -38,6 +46,15 @@ impl ::std::convert::From for v1beta1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1alpha2::Foo { + fn from(__sv_foo: v1beta1::Foo) -> Self { + Self { + username: __sv_foo.username.into(), + first_name: __sv_foo.first_name.into(), + } + } +} +#[automatically_derived] mod v1beta1 { use super::*; pub struct Foo { @@ -57,6 +74,16 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +impl ::std::convert::From for v1beta1::Foo { + fn from(__sv_foo: v1::Foo) -> Self { + Self { + username: __sv_foo.username.into(), + first_name: __sv_foo.first_name.into(), + last_name: __sv_foo.last_name.into(), + } + } +} +#[automatically_derived] mod v1 { use super::*; pub struct Foo { From c61340419a793ec9dcbdcb305b47c94e798309ca Mon Sep 17 00:00:00 2001 From: Techassi Date: Wed, 21 May 2025 14:46:45 +0200 Subject: [PATCH 16/26] test(stackable-versioned): Fix and comment previously untested code --- .../src/codegen/container/enum.rs | 10 ++++++++++ .../tests/inputs/default/pass/basic_struct.rs | 12 ++++++++++-- .../tests/inputs/default/pass/convert_with.rs | 12 ++++++------ .../inputs/default/pass/enum_data_simple.rs | 16 ++++++++++------ ..._snapshot_tests__default@basic_struct.rs.snap | 4 ++-- ..._snapshot_tests__default@convert_with.rs.snap | 16 ++++++++-------- ...napshot_tests__default@deprecate_enum.rs.snap | 3 +++ ...pshot_tests__default@enum_data_simple.rs.snap | 1 - 8 files changed, 49 insertions(+), 25 deletions(-) diff --git a/crates/stackable-versioned-macros/src/codegen/container/enum.rs b/crates/stackable-versioned-macros/src/codegen/container/enum.rs index 8a7b8ce2b..d8ee3d668 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/enum.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/enum.rs @@ -218,6 +218,15 @@ impl Enum { }) .collect(); + // Include allow(deprecated) only when this or the next version is + // deprecated. Also include it, when a variant in this or the next + // version is deprecated. + let allow_attribute = (version.deprecated.is_some() + || next_version.deprecated.is_some() + || self.is_any_variant_deprecated(version) + || self.is_any_variant_deprecated(next_version)) + .then_some(quote! { #[allow(deprecated)] }); + // Only add the #[automatically_derived] attribute only if this impl is used // outside of a module (in standalone mode). let automatically_derived = add_attributes @@ -226,6 +235,7 @@ impl Enum { Some(quote! { #automatically_derived + #allow_attribute impl #impl_generics ::std::convert::From<#from_module_ident::#enum_ident #type_generics> for #for_module_ident::#enum_ident #type_generics #where_clause { diff --git a/crates/stackable-versioned-macros/tests/inputs/default/pass/basic_struct.rs b/crates/stackable-versioned-macros/tests/inputs/default/pass/basic_struct.rs index c074e9456..488ea73ad 100644 --- a/crates/stackable-versioned-macros/tests/inputs/default/pass/basic_struct.rs +++ b/crates/stackable-versioned-macros/tests/inputs/default/pass/basic_struct.rs @@ -13,8 +13,8 @@ pub(crate) struct Foo { foo: String, #[versioned( - changed(since = "v1beta1", from_name = "jjj", from_type = "u8"), - changed(since = "v1", from_type = "u16"), + changed(since = "v1beta1", from_name = "jjj", from_type = "u8", downgrade_with = u16_to_u8), + changed(since = "v1", from_type = "u16", downgrade_with = usize_to_u16), deprecated(since = "v2", note = "not empty") )] /// Test @@ -23,3 +23,11 @@ pub(crate) struct Foo { } // --- fn main() {} + +fn usize_to_u16(input: usize) -> u16 { + input.try_into().unwrap() +} + +fn u16_to_u8(input: u16) -> u8 { + input.try_into().unwrap() +} diff --git a/crates/stackable-versioned-macros/tests/inputs/default/pass/convert_with.rs b/crates/stackable-versioned-macros/tests/inputs/default/pass/convert_with.rs index 9312419e5..e589b2d57 100644 --- a/crates/stackable-versioned-macros/tests/inputs/default/pass/convert_with.rs +++ b/crates/stackable-versioned-macros/tests/inputs/default/pass/convert_with.rs @@ -7,18 +7,18 @@ struct Foo { // This tests two additional things: // - that both unquoted and quoted usage works // - that the renamed name does get picked up correctly by the conversion function - changed(since = "v1", from_type = "u16", from_name = "bar", upgrade_with = u16_to_u32), - changed(since = "v2", from_type = "u32", upgrade_with = "u32_to_u64") + changed(since = "v1", from_type = "u16", from_name = "bar", downgrade_with = u32_to_u16), + changed(since = "v2", from_type = "u32", downgrade_with = "u64_to_u32") )] baz: u64, } // --- fn main() {} -fn u16_to_u32(input: u16) -> u32 { - input as u32 +fn u32_to_u16(input: u32) -> u16 { + input.try_into().unwrap() } -fn u32_to_u64(input: u32) -> u64 { - input as u64 +fn u64_to_u32(input: u64) -> u32 { + input.try_into().unwrap() } diff --git a/crates/stackable-versioned-macros/tests/inputs/default/pass/enum_data_simple.rs b/crates/stackable-versioned-macros/tests/inputs/default/pass/enum_data_simple.rs index 99281f414..178c36e9c 100644 --- a/crates/stackable-versioned-macros/tests/inputs/default/pass/enum_data_simple.rs +++ b/crates/stackable-versioned-macros/tests/inputs/default/pass/enum_data_simple.rs @@ -5,12 +5,16 @@ use stackable_versioned::versioned; enum Foo { Foo, Bar(u32, String), - - #[versioned(added(since = "v1alpha2"))] - Baz { - id: u32, - name: String, - }, + // FIXME (@Techassi): How do we handle downgrades of enums? The variant just + // doesn't exist in the earlier version, but we still need to handle the + // variant in the match. I think for this to work, we would need to require + // the user to specify a downgrade_with function. For now, I commented out + // the code to get the test to pass again. + // #[versioned(added(since = "v1alpha2"))] + // Baz { + // id: u32, + // name: String, + // }, } // --- fn main() {} diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@basic_struct.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@basic_struct.rs.snap index 5f434939a..1e8601603 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@basic_struct.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@basic_struct.rs.snap @@ -28,7 +28,7 @@ impl ::std::convert::From for v1beta1::Foo { impl ::std::convert::From for v1alpha1::Foo { fn from(__sv_foo: v1beta1::Foo) -> Self { Self { - jjj: __sv_foo.bar.into(), + jjj: u16_to_u8(__sv_foo.bar), baz: __sv_foo.baz.into(), } } @@ -56,7 +56,7 @@ impl ::std::convert::From for v1::Foo { impl ::std::convert::From for v1beta1::Foo { fn from(__sv_foo: v1::Foo) -> Self { Self { - bar: __sv_foo.bar.into(), + bar: usize_to_u16(__sv_foo.bar), baz: __sv_foo.baz.into(), } } diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@convert_with.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@convert_with.rs.snap index 941e7c036..a9361c1d7 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@convert_with.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@convert_with.rs.snap @@ -13,15 +13,15 @@ mod v1alpha1 { #[automatically_derived] impl ::std::convert::From for v1::Foo { fn from(__sv_foo: v1alpha1::Foo) -> Self { - Self { - baz: u16_to_u32(__sv_foo.bar), - } + Self { baz: __sv_foo.bar.into() } } } #[automatically_derived] impl ::std::convert::From for v1alpha1::Foo { fn from(__sv_foo: v1::Foo) -> Self { - Self { bar: __sv_foo.baz.into() } + Self { + bar: u32_to_u16(__sv_foo.baz), + } } } #[automatically_derived] @@ -34,15 +34,15 @@ mod v1 { #[automatically_derived] impl ::std::convert::From for v2::Foo { fn from(__sv_foo: v1::Foo) -> Self { - Self { - baz: u32_to_u64(__sv_foo.baz), - } + Self { baz: __sv_foo.baz.into() } } } #[automatically_derived] impl ::std::convert::From for v1::Foo { fn from(__sv_foo: v2::Foo) -> Self { - Self { baz: __sv_foo.baz.into() } + Self { + baz: u64_to_u32(__sv_foo.baz), + } } } #[automatically_derived] diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@deprecate_enum.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@deprecate_enum.rs.snap index f9b1fc6dd..274de6173 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@deprecate_enum.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@deprecate_enum.rs.snap @@ -48,6 +48,7 @@ impl ::std::convert::From for v1::Foo { } } #[automatically_derived] +#[allow(deprecated)] impl ::std::convert::From for v1beta1::Foo { fn from(__sv_foo: v1::Foo) -> Self { match __sv_foo { @@ -76,6 +77,7 @@ impl ::std::convert::From for v2::Foo { } } #[automatically_derived] +#[allow(deprecated)] impl ::std::convert::From for v1::Foo { fn from(__sv_foo: v2::Foo) -> Self { match __sv_foo { @@ -104,6 +106,7 @@ impl ::std::convert::From for v3::Foo { } } #[automatically_derived] +#[allow(deprecated)] impl ::std::convert::From for v2::Foo { fn from(__sv_foo: v3::Foo) -> Self { match __sv_foo { diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@enum_data_simple.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@enum_data_simple.rs.snap index c1490006b..572d6639c 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@enum_data_simple.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@enum_data_simple.rs.snap @@ -35,6 +35,5 @@ mod v1alpha2 { pub enum Foo { Foo, Bar(u32, String), - Baz { id: u32, name: String }, } } From 8b095daabc0be621575270fec913b723f28e8b94 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 22 May 2025 09:25:52 +0200 Subject: [PATCH 17/26] docs(stackable-versioned): Update 'convert_with' doc comment --- .../src/attrs/item/mod.rs | 3 ++- crates/stackable-versioned-macros/src/lib.rs | 13 ++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/crates/stackable-versioned-macros/src/attrs/item/mod.rs b/crates/stackable-versioned-macros/src/attrs/item/mod.rs index bcfb7e512..fff4396fb 100644 --- a/crates/stackable-versioned-macros/src/attrs/item/mod.rs +++ b/crates/stackable-versioned-macros/src/attrs/item/mod.rs @@ -442,7 +442,8 @@ fn default_default_fn() -> SpannedValue { /// Example usage: /// - `changed(since = "...", from_name = "...")` /// - `changed(since = "...", from_name = "...", from_type="...")` -/// - `changed(since = "...", from_name = "...", from_type="...", convert_with = "...")` +/// - `changed(since = "...", from_name = "...", from_type="...", upgrade_with = "...")` +/// - `changed(since = "...", from_name = "...", from_type="...", downgrade_with = "...")` #[derive(Clone, Debug, FromMeta)] pub struct ChangedAttributes { pub since: SpannedValue, diff --git a/crates/stackable-versioned-macros/src/lib.rs b/crates/stackable-versioned-macros/src/lib.rs index c6762e54e..9f013a6db 100644 --- a/crates/stackable-versioned-macros/src/lib.rs +++ b/crates/stackable-versioned-macros/src/lib.rs @@ -463,11 +463,14 @@ mod utils; /// - `since` to indicate since which version the item is changed. /// - `from_name` to indicate from which previous name the field is renamed. /// - `from_type` to indicate from which previous type the field is changed. -/// - `convert_with` to provide a custom conversion function instead of using -/// a [`From`] implementation by default. This argument can only be used in -/// combination with the `from_type` argument. The expected function signature -/// is: `fn (OLD_TYPE) -> NEW_TYPE`. This function must not fail. -/// +/// - `upgrade_with` to provide a custom upgrade function. This argument can +/// only be used in combination with the `from_type` argument. The expected +/// function signature is: `fn (OLD_TYPE) -> NEW_TYPE`. This function must +/// not fail. +///- `downgrade_with` to provide a custom downgrade function. This argument can +/// only be used in combination with the `from_type` argument. The expected +/// function signature is: `fn (NEW_TYPE) -> OLD_TYPE`. This function must +/// not fail. /// ``` /// # use stackable_versioned_macros::versioned; /// #[versioned( From 229dcefc274f42ec303b5775395c602c4170b453 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 22 May 2025 09:26:32 +0200 Subject: [PATCH 18/26] test(stackable-versioned): Rename convert_with test to downgrade_with --- .../inputs/default/pass/{convert_with.rs => downgrade_with.rs} | 0 ...oned_macros__snapshot_tests__default@downgrade_with.rs.snap} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename crates/stackable-versioned-macros/tests/inputs/default/pass/{convert_with.rs => downgrade_with.rs} (100%) rename crates/stackable-versioned-macros/tests/snapshots/{stackable_versioned_macros__snapshot_tests__default@convert_with.rs.snap => stackable_versioned_macros__snapshot_tests__default@downgrade_with.rs.snap} (97%) diff --git a/crates/stackable-versioned-macros/tests/inputs/default/pass/convert_with.rs b/crates/stackable-versioned-macros/tests/inputs/default/pass/downgrade_with.rs similarity index 100% rename from crates/stackable-versioned-macros/tests/inputs/default/pass/convert_with.rs rename to crates/stackable-versioned-macros/tests/inputs/default/pass/downgrade_with.rs diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@convert_with.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@downgrade_with.rs.snap similarity index 97% rename from crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@convert_with.rs.snap rename to crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@downgrade_with.rs.snap index a9361c1d7..28915bc31 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@convert_with.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__default@downgrade_with.rs.snap @@ -1,7 +1,7 @@ --- source: crates/stackable-versioned-macros/src/lib.rs expression: formatted -input_file: crates/stackable-versioned-macros/tests/inputs/default/pass/convert_with.rs +input_file: crates/stackable-versioned-macros/tests/inputs/default/pass/downgrade_with.rs --- #[automatically_derived] mod v1alpha1 { From fd4d1ad3048b8b40a4bd55e0924d0b4f2bcc0d9d Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 22 May 2025 09:40:02 +0200 Subject: [PATCH 19/26] chore(stackable-versioned): Add disclaimer comment --- crates/stackable-versioned/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/stackable-versioned/src/lib.rs b/crates/stackable-versioned/src/lib.rs index 88e73cde7..f3dc3778a 100644 --- a/crates/stackable-versioned/src/lib.rs +++ b/crates/stackable-versioned/src/lib.rs @@ -19,6 +19,10 @@ use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec}; // Re-export macro pub use stackable_versioned_macros::*; +// NOTE (@Techassi): This struct represents a rough first draft of how tracking values across +// CRD versions can be achieved. It is currently untested and unproven and might change down the +// line. Currently, this struct is only generated by the macro but not actually used by any other +// code. The tracking itself will be introduced in a follow-up PR. #[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] pub struct ChangedValues { /// List of values needed when downgrading to a particular version. From abf5debc21a2717d21325d2a10ee05508a808327 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 22 May 2025 15:14:43 +0200 Subject: [PATCH 20/26] refactor(stackable-versioned): Make conversion tracking opt in --- .../src/attrs/k8s.rs | 34 +++-- .../src/codegen/container/mod.rs | 36 +++-- .../src/codegen/container/struct.rs | 108 +++++++-------- .../src/codegen/module.rs | 4 +- .../inputs/k8s/pass/conversion_tracking.rs | 33 +++++ ...hot_tests__k8s@conversion_tracking.rs.snap | 130 ++++++++++++++++++ .../tests/trybuild.rs | 1 + 7 files changed, 257 insertions(+), 89 deletions(-) create mode 100644 crates/stackable-versioned-macros/tests/inputs/k8s/pass/conversion_tracking.rs create mode 100644 crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap diff --git a/crates/stackable-versioned-macros/src/attrs/k8s.rs b/crates/stackable-versioned-macros/src/attrs/k8s.rs index 52f3b63ac..06fcbbcb3 100644 --- a/crates/stackable-versioned-macros/src/attrs/k8s.rs +++ b/crates/stackable-versioned-macros/src/attrs/k8s.rs @@ -22,27 +22,30 @@ use syn::Path; /// times. /// - `skip`: Controls skipping parts of the generation. #[derive(Clone, Debug, FromMeta)] -pub(crate) struct KubernetesArguments { - pub(crate) group: String, - pub(crate) kind: Option, - pub(crate) singular: Option, - pub(crate) plural: Option, - pub(crate) namespaced: Flag, +pub struct KubernetesArguments { + pub group: String, + pub kind: Option, + pub singular: Option, + pub plural: Option, + pub namespaced: Flag, // root - pub(crate) crates: Option, - pub(crate) status: Option, + pub crates: Option, + pub status: Option, // derive // schema // scale // printcolumn #[darling(multiple, rename = "shortname")] - pub(crate) shortnames: Vec, + pub shortnames: Vec, // category // selectable // doc // annotation // label - pub(crate) skip: Option, + pub skip: Option, + + #[darling(default)] + pub options: RawKubernetesOptions, } /// This struct contains supported kubernetes skip arguments. @@ -52,15 +55,15 @@ pub(crate) struct KubernetesArguments { /// - `merged_crd` flag, which skips generating the `crd()` and `merged_crd()` functions are /// generated. #[derive(Clone, Debug, FromMeta)] -pub(crate) struct KubernetesSkipArguments { +pub struct KubernetesSkipArguments { /// Whether the `crd()` and `merged_crd()` generation should be skipped for /// this container. - pub(crate) merged_crd: Flag, + pub merged_crd: Flag, } /// This struct contains crate overrides to be passed to `#[kube]`. #[derive(Clone, Debug, FromMeta)] -pub(crate) struct KubernetesCrateArguments { +pub struct KubernetesCrateArguments { pub kube_core: Option, pub kube_client: Option, pub k8s_openapi: Option, @@ -69,3 +72,8 @@ pub(crate) struct KubernetesCrateArguments { pub serde_json: Option, pub versioned: Option, } + +#[derive(Clone, Default, Debug, FromMeta)] +pub struct RawKubernetesOptions { + pub experimental_conversion_tracking: Flag, +} diff --git a/crates/stackable-versioned-macros/src/codegen/container/mod.rs b/crates/stackable-versioned-macros/src/codegen/container/mod.rs index 57fc508a3..7ac654ade 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/mod.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/mod.rs @@ -8,7 +8,7 @@ use syn::{Attribute, Ident, ItemEnum, ItemStruct, Path, Visibility, parse_quote} use crate::{ attrs::{ container::StandaloneContainerAttributes, - k8s::{KubernetesArguments, KubernetesCrateArguments}, + k8s::{KubernetesArguments, KubernetesCrateArguments, RawKubernetesOptions}, }, codegen::{ VersionDefinition, @@ -44,13 +44,9 @@ pub enum Container { impl Container { /// Generates the container definition for the specified `version`. - pub(crate) fn generate_definition( - &self, - version: &VersionDefinition, - multiple_versions: bool, - ) -> TokenStream { + pub(crate) fn generate_definition(&self, version: &VersionDefinition) -> TokenStream { match self { - Container::Struct(s) => s.generate_definition(version, multiple_versions), + Container::Struct(s) => s.generate_definition(version), Container::Enum(e) => e.generate_definition(version), } } @@ -206,12 +202,9 @@ impl StandaloneContainer { let mut kubernetes_enum_variant_strings = Vec::new(); let mut versions = self.versions.iter().peekable(); - let multiple_versions = versions.len() > 1; while let Some(version) = versions.next() { - let container_definition = self - .container - .generate_definition(version, multiple_versions); + let container_definition = self.container.generate_definition(version); // NOTE (@Techassi): Using '.copied()' here does not copy or clone the data, but instead // removes one level of indirection of the double reference '&&'. @@ -266,9 +259,7 @@ impl StandaloneContainer { false, )); - if multiple_versions { - tokens.extend(self.container.generate_kubernetes_status_struct()); - } + tokens.extend(self.container.generate_kubernetes_status_struct()); tokens } @@ -314,6 +305,8 @@ pub struct ContainerOptions { pub skip_from: bool, } +// TODO (@Techassi): Get rid of this whole mess. There should be an elegant way of using the +// attributes directly (with all defaults set and validation done). #[derive(Debug)] pub struct KubernetesOptions { pub group: String, @@ -335,6 +328,7 @@ pub struct KubernetesOptions { // annotation // label pub skip_merged_crd: bool, + pub config_options: KubernetesConfigOptions, } impl From for KubernetesOptions { @@ -351,6 +345,7 @@ impl From for KubernetesOptions { status: args.status, shortnames: args.shortnames, skip_merged_crd: args.skip.is_some_and(|s| s.merged_crd.is_present()), + config_options: args.options.into(), } } } @@ -473,3 +468,16 @@ impl Deref for Override { } } } + +#[derive(Debug)] +pub struct KubernetesConfigOptions { + experimental_conversion_tracking: bool, +} + +impl From for KubernetesConfigOptions { + fn from(options: RawKubernetesOptions) -> Self { + Self { + experimental_conversion_tracking: options.experimental_conversion_tracking.is_present(), + } + } +} diff --git a/crates/stackable-versioned-macros/src/codegen/container/struct.rs b/crates/stackable-versioned-macros/src/codegen/container/struct.rs index 39c4f5a38..d061e1f6e 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/struct.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/struct.rs @@ -134,11 +134,7 @@ pub struct Struct { // Common token generation impl Struct { /// Generates code for the struct definition. - pub fn generate_definition( - &self, - version: &VersionDefinition, - multiple_versions: bool, - ) -> TokenStream { + pub fn generate_definition(&self, version: &VersionDefinition) -> TokenStream { let where_clause = self.generics.where_clause.as_ref(); let type_generics = &self.generics; @@ -152,7 +148,7 @@ impl Struct { } // This only returns Some, if K8s features are enabled - let kube_attribute = self.generate_kube_attribute(version, multiple_versions); + let kube_attribute = self.generate_kube_attribute(version); quote! { #(#[doc = #version_docs])* @@ -323,11 +319,7 @@ impl Struct { // makes keeping track of interconnected parts easier. // Kubernetes-specific token generation impl Struct { - pub fn generate_kube_attribute( - &self, - version: &VersionDefinition, - _multiple_versions: bool, - ) -> Option { + pub fn generate_kube_attribute(&self, version: &VersionDefinition) -> Option { let kubernetes_options = self.common.options.kubernetes_options.as_ref()?; // Required arguments @@ -356,24 +348,16 @@ impl Struct { .then_some(quote! { , namespaced }); let crates = kubernetes_options.crates.to_token_stream(); - // TODO (@Techassi): Comment back in, once we are happy with the status struct - // let status = if multiple_versions { - // let status_ident = format_ident!( - // "{struct_ident}Status", - // struct_ident = self.common.idents.kubernetes.as_ident() - // ); - // Some(quote! { , status = #status_ident }) - // } else { - // kubernetes_options - // .status - // .as_ref() - // .map(|s| quote! { , status = #s }) - // }; - let status = kubernetes_options - .status - .as_ref() - .map(|s| quote! { , status = #s }); + .config_options + .experimental_conversion_tracking + .then(|| { + let status_ident = format_ident!( + "{struct_ident}Status", + struct_ident = self.common.idents.kubernetes.as_ident() + ); + quote! { , status = #status_ident } + }); let shortnames: TokenStream = kubernetes_options .shortnames @@ -473,36 +457,42 @@ impl Struct { pub fn generate_kubernetes_status_struct(&self) -> Option { let kubernetes_options = self.common.options.kubernetes_options.as_ref()?; - let status_ident = format_ident!( - "{struct_ident}Status", - struct_ident = self.common.idents.kubernetes.as_ident() - ); - - let versioned_crate = &*kubernetes_options.crates.versioned; - let schemars_crate = &*kubernetes_options.crates.schemars; - let serde_crate = &*kubernetes_options.crates.serde; - - let status = kubernetes_options.status.as_ref().map(|status| { - quote! { - #[serde(flatten)] - pub status: #status, - } - }); - - Some(quote! { - #[derive( - ::core::clone::Clone, - ::core::fmt::Debug, - #serde_crate::Deserialize, - #serde_crate::Serialize, - #schemars_crate::JsonSchema - )] - #[serde(rename_all = "camelCase")] - pub struct #status_ident { - pub changed_values: #versioned_crate::ChangedValues, - - #status - } - }) + kubernetes_options + .config_options + .experimental_conversion_tracking + .then(|| { + let status_ident = format_ident!( + "{struct_ident}Status", + struct_ident = self.common.idents.kubernetes.as_ident() + ); + + let versioned_crate = &*kubernetes_options.crates.versioned; + let schemars_crate = &*kubernetes_options.crates.schemars; + let serde_crate = &*kubernetes_options.crates.serde; + + // TODO (@Techassi): Validate that users don't specify the status we generate + let status = kubernetes_options.status.as_ref().map(|status| { + quote! { + #[serde(flatten)] + pub status: #status, + } + }); + + quote! { + #[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + #serde_crate::Deserialize, + #serde_crate::Serialize, + #schemars_crate::JsonSchema + )] + #[serde(rename_all = "camelCase")] + pub struct #status_ident { + pub changed_values: #versioned_crate::ChangedValues, + + #status + } + } + }) } } diff --git a/crates/stackable-versioned-macros/src/codegen/module.rs b/crates/stackable-versioned-macros/src/codegen/module.rs index 68b6eacb3..11e84e47c 100644 --- a/crates/stackable-versioned-macros/src/codegen/module.rs +++ b/crates/stackable-versioned-macros/src/codegen/module.rs @@ -149,7 +149,6 @@ impl Module { let mut kubernetes_container_items: HashMap = HashMap::new(); let mut versions = self.versions.iter().peekable(); - let multiple_versions = self.versions.len() > 1; while let Some(version) = versions.next() { let next_version = versions.peek().copied(); @@ -159,8 +158,7 @@ impl Module { let version_ident = &version.ident; for container in &self.containers { - container_definitions - .extend(container.generate_definition(version, multiple_versions)); + container_definitions.extend(container.generate_definition(version)); if !self.skip_from { from_impls.extend(container.generate_upgrade_from_impl( diff --git a/crates/stackable-versioned-macros/tests/inputs/k8s/pass/conversion_tracking.rs b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/conversion_tracking.rs new file mode 100644 index 000000000..09d0d748e --- /dev/null +++ b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/conversion_tracking.rs @@ -0,0 +1,33 @@ +use kube::CustomResource; +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use stackable_versioned::versioned; +// --- +#[versioned( + version(name = "v1alpha1"), + version(name = "v1beta1"), + version(name = "v1"), + k8s( + group = "stackable.tech", + status = MyStatus, + options(experimental_conversion_tracking), + ) +)] +// --- +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)] +pub(crate) struct FooSpec { + #[versioned(added(since = "v1beta1"), changed(since = "v1", from_name = "bah"))] + bar: usize, + baz: bool, +} +// --- +fn main() {} + +#[derive(Clone, Debug, JsonSchema, Deserialize, Serialize)] +pub struct MyStatus { + foo: String, +} + +fn usize_to_u16(input: usize) -> u16 { + input.try_into().unwrap() +} diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap new file mode 100644 index 000000000..039074126 --- /dev/null +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap @@ -0,0 +1,130 @@ +--- +source: crates/stackable-versioned-macros/src/lib.rs +expression: formatted +input_file: crates/stackable-versioned-macros/tests/inputs/k8s/pass/conversion_tracking.rs +--- +#[automatically_derived] +pub(crate) mod v1alpha1 { + use super::*; + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)] + #[kube( + group = "stackable.tech", + version = "v1alpha1", + kind = "Foo", + status = FooStatus + )] + pub struct FooSpec { + pub baz: bool, + } +} +#[automatically_derived] +impl ::std::convert::From for v1beta1::FooSpec { + fn from(__sv_foospec: v1alpha1::FooSpec) -> Self { + Self { + bah: ::std::default::Default::default(), + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] +impl ::std::convert::From for v1alpha1::FooSpec { + fn from(__sv_foospec: v1beta1::FooSpec) -> Self { + Self { + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] +pub(crate) mod v1beta1 { + use super::*; + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)] + #[kube( + group = "stackable.tech", + version = "v1beta1", + kind = "Foo", + status = FooStatus + )] + pub struct FooSpec { + pub bah: usize, + pub baz: bool, + } +} +#[automatically_derived] +impl ::std::convert::From for v1::FooSpec { + fn from(__sv_foospec: v1beta1::FooSpec) -> Self { + Self { + bar: __sv_foospec.bah.into(), + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] +impl ::std::convert::From for v1beta1::FooSpec { + fn from(__sv_foospec: v1::FooSpec) -> Self { + Self { + bah: __sv_foospec.bar.into(), + baz: __sv_foospec.baz.into(), + } + } +} +#[automatically_derived] +pub(crate) mod v1 { + use super::*; + #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)] + #[kube(group = "stackable.tech", version = "v1", kind = "Foo", status = FooStatus)] + pub struct FooSpec { + pub bar: usize, + pub baz: bool, + } +} +#[automatically_derived] +pub(crate) enum Foo { + V1Alpha1, + V1Beta1, + V1, +} +#[automatically_derived] +impl ::std::fmt::Display for Foo { + fn fmt( + &self, + f: &mut ::std::fmt::Formatter<'_>, + ) -> ::std::result::Result<(), ::std::fmt::Error> { + match self { + Self::V1Alpha1 => f.write_str("v1alpha1"), + Self::V1Beta1 => f.write_str("v1beta1"), + Self::V1 => f.write_str("v1"), + } + } +} +#[automatically_derived] +impl Foo { + /// Generates a merged CRD containing all versions and marking `stored_apiversion` as stored. + pub fn merged_crd( + stored_apiversion: Self, + ) -> ::std::result::Result< + ::k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition, + ::kube::core::crd::MergeError, + > { + ::kube::core::crd::merge_crds( + vec![ + < v1alpha1::Foo as ::kube::core::CustomResourceExt > ::crd(), < + v1beta1::Foo as ::kube::core::CustomResourceExt > ::crd(), < v1::Foo as + ::kube::core::CustomResourceExt > ::crd() + ], + &stored_apiversion.to_string(), + ) + } +} +#[derive( + ::core::clone::Clone, + ::core::fmt::Debug, + ::serde::Deserialize, + ::serde::Serialize, + ::schemars::JsonSchema +)] +#[serde(rename_all = "camelCase")] +pub struct FooStatus { + pub changed_values: ::stackable_versioned::ChangedValues, + #[serde(flatten)] + pub status: MyStatus, +} diff --git a/crates/stackable-versioned-macros/tests/trybuild.rs b/crates/stackable-versioned-macros/tests/trybuild.rs index 891cc6023..0a11c993a 100644 --- a/crates/stackable-versioned-macros/tests/trybuild.rs +++ b/crates/stackable-versioned-macros/tests/trybuild.rs @@ -50,6 +50,7 @@ mod inputs { mod k8s { mod pass { // mod basic; + // mod conversion_tracking; // mod crate_overrides; // mod module; // mod module_preserve; From 1a0fcc3609febce35fc5d7d4a0803f75188129c6 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 22 May 2025 15:24:44 +0200 Subject: [PATCH 21/26] refactor(stackable-versioned): Move status struct behind feature flag --- crates/stackable-versioned/src/k8s.rs | 42 ++++++++++++++++++++++++ crates/stackable-versioned/src/lib.rs | 47 +++------------------------ 2 files changed, 47 insertions(+), 42 deletions(-) create mode 100644 crates/stackable-versioned/src/k8s.rs diff --git a/crates/stackable-versioned/src/k8s.rs b/crates/stackable-versioned/src/k8s.rs new file mode 100644 index 000000000..b0cda13dc --- /dev/null +++ b/crates/stackable-versioned/src/k8s.rs @@ -0,0 +1,42 @@ +use std::collections::HashMap; + +use k8s_version::Version; +use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec}; + +// NOTE (@Techassi): This struct represents a rough first draft of how tracking values across +// CRD versions can be achieved. It is currently untested and unproven and might change down the +// line. Currently, this struct is only generated by the macro but not actually used by any other +// code. The tracking itself will be introduced in a follow-up PR. +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct ChangedValues { + /// List of values needed when downgrading to a particular version. + pub downgrades: HashMap>, + + /// List of values needed when upgrading to a particular version. + pub upgrades: HashMap>, +} + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] +pub struct ChangedValue { + /// The name of the field of the custom resource this value is for. + pub name: String, + + /// The value to be used when upgrading or downgrading the custom resource. + #[schemars(schema_with = "raw_object_schema")] + pub value: serde_yaml::Value, +} + +// TODO (@Techassi): Think about where this should live. Basically this already exists in +// stackable-operator, but we cannot use it without depending on it which I would like to +// avoid. +fn raw_object_schema(_: &mut schemars::r#gen::SchemaGenerator) -> Schema { + Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), + extensions: [( + "x-kubernetes-preserve-unknown-fields".to_owned(), + serde_json::Value::Bool(true), + )] + .into(), + ..Default::default() + }) +} diff --git a/crates/stackable-versioned/src/lib.rs b/crates/stackable-versioned/src/lib.rs index f3dc3778a..85edf234e 100644 --- a/crates/stackable-versioned/src/lib.rs +++ b/crates/stackable-versioned/src/lib.rs @@ -12,47 +12,10 @@ //! See [`versioned`] for an in-depth usage guide and a list of supported //! parameters. -use std::collections::HashMap; - -use k8s_version::Version; -use schemars::schema::{InstanceType, Schema, SchemaObject, SingleOrVec}; // Re-export macro -pub use stackable_versioned_macros::*; - -// NOTE (@Techassi): This struct represents a rough first draft of how tracking values across -// CRD versions can be achieved. It is currently untested and unproven and might change down the -// line. Currently, this struct is only generated by the macro but not actually used by any other -// code. The tracking itself will be introduced in a follow-up PR. -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct ChangedValues { - /// List of values needed when downgrading to a particular version. - pub downgrades: HashMap>, - - /// List of values needed when upgrading to a particular version. - pub upgrades: HashMap>, -} - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema)] -pub struct ChangedValue { - /// The name of the field of the custom resource this value is for. - pub name: String, - - /// The value to be used when upgrading or downgrading the custom resource. - #[schemars(schema_with = "raw_object_schema")] - pub value: serde_yaml::Value, -} +#[cfg(feature = "k8s")] +pub use k8s::*; +pub use stackable_versioned_macros::versioned; -// TODO (@Techassi): Think about where this should live. Basically this already exists in -// stackable-operator, but we cannot use it without depending on it which I would like to -// avoid. -fn raw_object_schema(_: &mut schemars::r#gen::SchemaGenerator) -> Schema { - Schema::Object(SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Object))), - extensions: [( - "x-kubernetes-preserve-unknown-fields".to_owned(), - serde_json::Value::Bool(true), - )] - .into(), - ..Default::default() - }) -} +#[cfg(feature = "k8s")] +mod k8s; From 44bb861f139540d90efa7b1d0f0c692cd9d4e274 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 22 May 2025 15:25:41 +0200 Subject: [PATCH 22/26] test(stackable-versioned): Add passing Kubernetes compile tests --- .../tests/inputs/k8s/pass/basic.rs | 6 ++- .../tests/inputs/k8s/pass/crate_overrides.rs | 11 ++--- .../tests/inputs/k8s/pass/renamed_kind.rs | 13 +----- .../tests/inputs/k8s/pass/skip.rs | 13 +----- ..._macros__snapshot_tests__k8s@basic.rs.snap | 13 +----- ...napshot_tests__k8s@crate_overrides.rs.snap | 28 ++----------- ...macros__snapshot_tests__k8s@module.rs.snap | 22 ---------- ...napshot_tests__k8s@module_preserve.rs.snap | 22 ---------- ...__snapshot_tests__k8s@renamed_kind.rs.snap | 40 ++----------------- ...d_macros__snapshot_tests__k8s@skip.rs.snap | 40 ++----------------- .../tests/trybuild.rs | 1 + 11 files changed, 26 insertions(+), 183 deletions(-) diff --git a/crates/stackable-versioned-macros/tests/inputs/k8s/pass/basic.rs b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/basic.rs index 95499f5d8..0b6b9bf5c 100644 --- a/crates/stackable-versioned-macros/tests/inputs/k8s/pass/basic.rs +++ b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/basic.rs @@ -18,10 +18,14 @@ use stackable_versioned::versioned; pub(crate) struct FooSpec { #[versioned( added(since = "v1beta1"), - changed(since = "v1", from_name = "bah", from_type = "u16") + changed(since = "v1", from_name = "bah", from_type = "u16", downgrade_with = usize_to_u16) )] bar: usize, baz: bool, } // --- fn main() {} + +fn usize_to_u16(input: usize) -> u16 { + input.try_into().unwrap() +} diff --git a/crates/stackable-versioned-macros/tests/inputs/k8s/pass/crate_overrides.rs b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/crate_overrides.rs index e6a21dbb2..ff3dacdb9 100644 --- a/crates/stackable-versioned-macros/tests/inputs/k8s/pass/crate_overrides.rs +++ b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/crate_overrides.rs @@ -6,11 +6,9 @@ use stackable_versioned::versioned; version(name = "v1"), k8s( group = "foo.example.org", - singular = "foo", - plural = "foos", - namespaced, crates( - kube_core = ::kube::core + kube_core = ::kube::core, + schemars = ::schemars ) ) )] @@ -19,10 +17,7 @@ use stackable_versioned::versioned; Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema, kube::CustomResource, )] pub struct FooSpec { - #[versioned( - added(since = "v1beta1"), - changed(since = "v1", from_name = "bah", from_type = "u16") - )] + #[versioned(added(since = "v1beta1"), changed(since = "v1", from_name = "bah"))] bar: usize, baz: bool, } diff --git a/crates/stackable-versioned-macros/tests/inputs/k8s/pass/renamed_kind.rs b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/renamed_kind.rs index 29c99d76f..53f479b9a 100644 --- a/crates/stackable-versioned-macros/tests/inputs/k8s/pass/renamed_kind.rs +++ b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/renamed_kind.rs @@ -4,23 +4,14 @@ use stackable_versioned::versioned; version(name = "v1alpha1"), version(name = "v1beta1"), version(name = "v1"), - k8s( - group = "stackable.tech", - kind = "FooBar", - singular = "foo", - plural = "foos", - namespaced, - ) + k8s(group = "stackable.tech", kind = "FooBar", namespaced) )] // --- #[derive( Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema, kube::CustomResource, )] pub struct FooSpec { - #[versioned( - added(since = "v1beta1"), - changed(since = "v1", from_name = "bah", from_type = "u16") - )] + #[versioned(added(since = "v1beta1"), changed(since = "v1", from_name = "bah"))] bar: usize, baz: bool, } diff --git a/crates/stackable-versioned-macros/tests/inputs/k8s/pass/skip.rs b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/skip.rs index 773b0ff16..535091c9e 100644 --- a/crates/stackable-versioned-macros/tests/inputs/k8s/pass/skip.rs +++ b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/skip.rs @@ -4,23 +4,14 @@ use stackable_versioned::versioned; version(name = "v1alpha1"), version(name = "v1beta1"), version(name = "v1"), - k8s( - group = "stackable.tech", - singular = "foo", - plural = "foos", - namespaced, - skip(merged_crd) - ) + k8s(group = "stackable.tech", skip(merged_crd)) )] // --- #[derive( Clone, Debug, serde::Deserialize, serde::Serialize, schemars::JsonSchema, kube::CustomResource, )] pub struct FooSpec { - #[versioned( - added(since = "v1beta1"), - changed(since = "v1", from_name = "bah", from_type = "u16") - )] + #[versioned(added(since = "v1beta1"), changed(since = "v1", from_name = "bah"))] bar: usize, baz: bool, } diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@basic.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@basic.rs.snap index b513551cd..e30c2f3ad 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@basic.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@basic.rs.snap @@ -80,7 +80,7 @@ impl ::std::convert::From for v1::FooSpec { impl ::std::convert::From for v1beta1::FooSpec { fn from(__sv_foospec: v1::FooSpec) -> Self { Self { - bah: __sv_foospec.bar.into(), + bah: usize_to_u16(__sv_foospec.bar), baz: __sv_foospec.baz.into(), } } @@ -147,14 +147,3 @@ impl Foo { ) } } -#[derive( - ::core::clone::Clone, - ::core::fmt::Debug, - ::serde::Deserialize, - ::serde::Serialize, - ::schemars::JsonSchema -)] -#[serde(rename_all = "camelCase")] -pub struct FooStatus { - pub changed_values: ::stackable_versioned::ChangedValues, -} diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@crate_overrides.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@crate_overrides.rs.snap index a63553719..cf7afc27a 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@crate_overrides.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@crate_overrides.rs.snap @@ -18,10 +18,7 @@ pub mod v1alpha1 { group = "foo.example.org", version = "v1alpha1", kind = "Foo", - singular = "foo", - plural = "foos", - namespaced, - crates(kube_core = ::kube::core) + crates(kube_core = ::kube::core, schemars = ::schemars) )] pub struct FooSpec { pub baz: bool, @@ -59,13 +56,10 @@ pub mod v1beta1 { group = "foo.example.org", version = "v1beta1", kind = "Foo", - singular = "foo", - plural = "foos", - namespaced, - crates(kube_core = ::kube::core) + crates(kube_core = ::kube::core, schemars = ::schemars) )] pub struct FooSpec { - pub bah: u16, + pub bah: usize, pub baz: bool, } } @@ -102,10 +96,7 @@ pub mod v1 { group = "foo.example.org", version = "v1", kind = "Foo", - singular = "foo", - plural = "foos", - namespaced, - crates(kube_core = ::kube::core) + crates(kube_core = ::kube::core, schemars = ::schemars) )] pub struct FooSpec { pub bar: usize, @@ -150,14 +141,3 @@ impl Foo { ) } } -#[derive( - ::core::clone::Clone, - ::core::fmt::Debug, - ::serde::Deserialize, - ::serde::Serialize, - ::schemars::JsonSchema -)] -#[serde(rename_all = "camelCase")] -pub struct FooStatus { - pub changed_values: ::stackable_versioned::ChangedValues, -} diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@module.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@module.rs.snap index 406989849..44fa16f86 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@module.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@module.rs.snap @@ -313,17 +313,6 @@ impl Foo { ) } } -#[derive( - ::core::clone::Clone, - ::core::fmt::Debug, - ::serde::Deserialize, - ::serde::Serialize, - ::schemars::JsonSchema -)] -#[serde(rename_all = "camelCase")] -pub struct FooStatus { - pub changed_values: ::stackable_versioned::ChangedValues, -} #[automatically_derived] pub(crate) enum Bar { V1Alpha1, @@ -362,14 +351,3 @@ impl Bar { ) } } -#[derive( - ::core::clone::Clone, - ::core::fmt::Debug, - ::serde::Deserialize, - ::serde::Serialize, - ::schemars::JsonSchema -)] -#[serde(rename_all = "camelCase")] -pub struct BarStatus { - pub changed_values: ::stackable_versioned::ChangedValues, -} diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@module_preserve.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@module_preserve.rs.snap index eeaf08aa8..5fe460b51 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@module_preserve.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@module_preserve.rs.snap @@ -293,17 +293,6 @@ pub(crate) mod versioned { ) } } - #[derive( - ::core::clone::Clone, - ::core::fmt::Debug, - ::serde::Deserialize, - ::serde::Serialize, - ::schemars::JsonSchema - )] - #[serde(rename_all = "camelCase")] - pub struct FooStatus { - pub changed_values: ::stackable_versioned::ChangedValues, - } pub enum Bar { V1Alpha1, V1, @@ -339,15 +328,4 @@ pub(crate) mod versioned { ) } } - #[derive( - ::core::clone::Clone, - ::core::fmt::Debug, - ::serde::Deserialize, - ::serde::Serialize, - ::schemars::JsonSchema - )] - #[serde(rename_all = "camelCase")] - pub struct BarStatus { - pub changed_values: ::stackable_versioned::ChangedValues, - } } diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@renamed_kind.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@renamed_kind.rs.snap index 94d7bf86d..ea38693a9 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@renamed_kind.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@renamed_kind.rs.snap @@ -14,14 +14,7 @@ pub mod v1alpha1 { schemars::JsonSchema, kube::CustomResource, )] - #[kube( - group = "stackable.tech", - version = "v1alpha1", - kind = "FooBar", - singular = "foo", - plural = "foos", - namespaced - )] + #[kube(group = "stackable.tech", version = "v1alpha1", kind = "FooBar", namespaced)] pub struct FooSpec { pub baz: bool, } @@ -54,16 +47,9 @@ pub mod v1beta1 { schemars::JsonSchema, kube::CustomResource, )] - #[kube( - group = "stackable.tech", - version = "v1beta1", - kind = "FooBar", - singular = "foo", - plural = "foos", - namespaced - )] + #[kube(group = "stackable.tech", version = "v1beta1", kind = "FooBar", namespaced)] pub struct FooSpec { - pub bah: u16, + pub bah: usize, pub baz: bool, } } @@ -96,14 +82,7 @@ pub mod v1 { schemars::JsonSchema, kube::CustomResource, )] - #[kube( - group = "stackable.tech", - version = "v1", - kind = "FooBar", - singular = "foo", - plural = "foos", - namespaced - )] + #[kube(group = "stackable.tech", version = "v1", kind = "FooBar", namespaced)] pub struct FooSpec { pub bar: usize, pub baz: bool, @@ -147,14 +126,3 @@ impl FooBar { ) } } -#[derive( - ::core::clone::Clone, - ::core::fmt::Debug, - ::serde::Deserialize, - ::serde::Serialize, - ::schemars::JsonSchema -)] -#[serde(rename_all = "camelCase")] -pub struct FooBarStatus { - pub changed_values: ::stackable_versioned::ChangedValues, -} diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@skip.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@skip.rs.snap index a749a8fb4..84bcd909b 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@skip.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@skip.rs.snap @@ -14,14 +14,7 @@ pub mod v1alpha1 { schemars::JsonSchema, kube::CustomResource, )] - #[kube( - group = "stackable.tech", - version = "v1alpha1", - kind = "Foo", - singular = "foo", - plural = "foos", - namespaced - )] + #[kube(group = "stackable.tech", version = "v1alpha1", kind = "Foo")] pub struct FooSpec { pub baz: bool, } @@ -54,16 +47,9 @@ pub mod v1beta1 { schemars::JsonSchema, kube::CustomResource, )] - #[kube( - group = "stackable.tech", - version = "v1beta1", - kind = "Foo", - singular = "foo", - plural = "foos", - namespaced - )] + #[kube(group = "stackable.tech", version = "v1beta1", kind = "Foo")] pub struct FooSpec { - pub bah: u16, + pub bah: usize, pub baz: bool, } } @@ -96,27 +82,9 @@ pub mod v1 { schemars::JsonSchema, kube::CustomResource, )] - #[kube( - group = "stackable.tech", - version = "v1", - kind = "Foo", - singular = "foo", - plural = "foos", - namespaced - )] + #[kube(group = "stackable.tech", version = "v1", kind = "Foo")] pub struct FooSpec { pub bar: usize, pub baz: bool, } } -#[derive( - ::core::clone::Clone, - ::core::fmt::Debug, - ::serde::Deserialize, - ::serde::Serialize, - ::schemars::JsonSchema -)] -#[serde(rename_all = "camelCase")] -pub struct FooStatus { - pub changed_values: ::stackable_versioned::ChangedValues, -} diff --git a/crates/stackable-versioned-macros/tests/trybuild.rs b/crates/stackable-versioned-macros/tests/trybuild.rs index 0a11c993a..8848928bf 100644 --- a/crates/stackable-versioned-macros/tests/trybuild.rs +++ b/crates/stackable-versioned-macros/tests/trybuild.rs @@ -77,4 +77,5 @@ fn default() { fn k8s() { let t = trybuild::TestCases::new(); t.compile_fail("tests/inputs/k8s/fail/*.rs"); + t.pass("tests/inputs/k8s/pass/*.rs"); } From 69ee88c36a6ae47e6e4a6e321ed2386d758d233c Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 22 May 2025 15:27:07 +0200 Subject: [PATCH 23/26] fix(stackable-versioned): Use correct module name --- crates/stackable-versioned-macros/tests/trybuild.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stackable-versioned-macros/tests/trybuild.rs b/crates/stackable-versioned-macros/tests/trybuild.rs index 8848928bf..ff26900a7 100644 --- a/crates/stackable-versioned-macros/tests/trybuild.rs +++ b/crates/stackable-versioned-macros/tests/trybuild.rs @@ -21,7 +21,7 @@ mod inputs { // mod attribute_enum; // mod attribute_struct; // mod basic_struct; - // mod convert_with; + // mod downgrade_with; // mod deprecate_enum; // mod deprecate_struct; // mod enum_data_simple; From e38161b8aa9dcfcc33d3daa3e74dea89818e9826 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 22 May 2025 15:43:23 +0200 Subject: [PATCH 24/26] chore(stackable-versioned): Update status struct name to avoid collisions --- .../src/codegen/container/struct.rs | 6 ++++-- ..._snapshot_tests__k8s@conversion_tracking.rs.snap | 13 +++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/crates/stackable-versioned-macros/src/codegen/container/struct.rs b/crates/stackable-versioned-macros/src/codegen/container/struct.rs index d061e1f6e..e96f99125 100644 --- a/crates/stackable-versioned-macros/src/codegen/container/struct.rs +++ b/crates/stackable-versioned-macros/src/codegen/container/struct.rs @@ -348,12 +348,14 @@ impl Struct { .then_some(quote! { , namespaced }); let crates = kubernetes_options.crates.to_token_stream(); + // TODO (@Techassi): This struct name should be defined once in a single place instead + // of constructing it in two different places which can lead to de-synchronization. let status = kubernetes_options .config_options .experimental_conversion_tracking .then(|| { let status_ident = format_ident!( - "{struct_ident}Status", + "{struct_ident}StatusWithChangedValues", struct_ident = self.common.idents.kubernetes.as_ident() ); quote! { , status = #status_ident } @@ -462,7 +464,7 @@ impl Struct { .experimental_conversion_tracking .then(|| { let status_ident = format_ident!( - "{struct_ident}Status", + "{struct_ident}StatusWithChangedValues", struct_ident = self.common.idents.kubernetes.as_ident() ); diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap index 039074126..5310a59b6 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap @@ -11,7 +11,7 @@ pub(crate) mod v1alpha1 { group = "stackable.tech", version = "v1alpha1", kind = "Foo", - status = FooStatus + status = FooStatusWithChangedValues )] pub struct FooSpec { pub baz: bool, @@ -42,7 +42,7 @@ pub(crate) mod v1beta1 { group = "stackable.tech", version = "v1beta1", kind = "Foo", - status = FooStatus + status = FooStatusWithChangedValues )] pub struct FooSpec { pub bah: usize, @@ -71,7 +71,12 @@ impl ::std::convert::From for v1beta1::FooSpec { pub(crate) mod v1 { use super::*; #[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, CustomResource)] - #[kube(group = "stackable.tech", version = "v1", kind = "Foo", status = FooStatus)] + #[kube( + group = "stackable.tech", + version = "v1", + kind = "Foo", + status = FooStatusWithChangedValues + )] pub struct FooSpec { pub bar: usize, pub baz: bool, @@ -123,7 +128,7 @@ impl Foo { ::schemars::JsonSchema )] #[serde(rename_all = "camelCase")] -pub struct FooStatus { +pub struct FooStatusWithChangedValues { pub changed_values: ::stackable_versioned::ChangedValues, #[serde(flatten)] pub status: MyStatus, From 1fdc2aa2fb5e8ad312a8d1703d5edd2425db3e37 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 22 May 2025 16:02:56 +0200 Subject: [PATCH 25/26] chore(stackable-versioned): Update changelog --- crates/stackable-versioned/CHANGELOG.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/crates/stackable-versioned/CHANGELOG.md b/crates/stackable-versioned/CHANGELOG.md index de6dde736..47db554de 100644 --- a/crates/stackable-versioned/CHANGELOG.md +++ b/crates/stackable-versioned/CHANGELOG.md @@ -8,9 +8,12 @@ All notable changes to this project will be documented in this file. - Implement basic ground work for downgrading custom resources ([#1033]). - Emit `From` implementations to downgrade custom resource specs. - - Emit a status struct when multiple versions are defined to be able to track values required - during downgrades and upgrades of custom resources. - + - Emit a status struct to be able to track values required during downgrades and upgrades of + custom resources. The generation of code for this feature is opt-in and must be enabled by + adding the `k8s(options(experimental_conversion_tracking))` flag. + - Add `versioned` crate override to `k8s(crates())` to specify a custom import path. This override + will not be passed to the `#[kube()]` attribute, but will only be available to internal + `#[versioned]` macro code. - Add `kube_client` crate override to `k8s(crates())` to specify a custom import path. This override will not be passed to the `#[kube()]` attribute, but will only be available to internal `#[versioned]` macro code ([#1038]). @@ -25,9 +28,19 @@ All notable changes to this project will be documented in this file. - Correctly handle fields added in later versions ([#1031]). +### Removed + +- BREAKING: Remove unused `AsVersionStr` trait ([#1033]). + +### Miscellaneous + +- Fix and add snapshot/compile tests for Kubernetes-specific features ([#1033]). +- Combine snapshot and compile tests ([#1041]). + [#1031]: https://github.com/stackabletech/operator-rs/pull/1031 [#1033]: https://github.com/stackabletech/operator-rs/pull/1033 [#1038]: https://github.com/stackabletech/operator-rs/pull/1038 +[#1041]: https://github.com/stackabletech/operator-rs/pull/1041 ## [0.7.1] - 2025-04-02 From 46da1d69ae2019204dc7525a66a54fb7166d70a5 Mon Sep 17 00:00:00 2001 From: Techassi Date: Thu, 22 May 2025 16:05:26 +0200 Subject: [PATCH 26/26] test(stackable-versioned): Update status struct name --- .../tests/inputs/k8s/pass/conversion_tracking.rs | 4 ++-- ...ed_macros__snapshot_tests__k8s@conversion_tracking.rs.snap | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/stackable-versioned-macros/tests/inputs/k8s/pass/conversion_tracking.rs b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/conversion_tracking.rs index 09d0d748e..325256f4b 100644 --- a/crates/stackable-versioned-macros/tests/inputs/k8s/pass/conversion_tracking.rs +++ b/crates/stackable-versioned-macros/tests/inputs/k8s/pass/conversion_tracking.rs @@ -9,7 +9,7 @@ use stackable_versioned::versioned; version(name = "v1"), k8s( group = "stackable.tech", - status = MyStatus, + status = FooStatus, options(experimental_conversion_tracking), ) )] @@ -24,7 +24,7 @@ pub(crate) struct FooSpec { fn main() {} #[derive(Clone, Debug, JsonSchema, Deserialize, Serialize)] -pub struct MyStatus { +pub struct FooStatus { foo: String, } diff --git a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap index 5310a59b6..9aa32539b 100644 --- a/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap +++ b/crates/stackable-versioned-macros/tests/snapshots/stackable_versioned_macros__snapshot_tests__k8s@conversion_tracking.rs.snap @@ -131,5 +131,5 @@ impl Foo { pub struct FooStatusWithChangedValues { pub changed_values: ::stackable_versioned::ChangedValues, #[serde(flatten)] - pub status: MyStatus, + pub status: FooStatus, }