Skip to content

feat(stackable-versioned)!: Integrate with ConversionReviews #1050

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Jun 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
17ff823
feat(stackable-versioned): Add flux-converter
sbernauer May 27, 2025
eb141e0
test: Add roundtrip generation via macro
sbernauer May 28, 2025
5a1a62f
fix some tests
sbernauer May 28, 2025
6a88cf4
Fix remaining tests
sbernauer May 28, 2025
3de96a7
fix rest of tests
sbernauer May 30, 2025
91e440d
Merge branch 'main' into feat/flux-converter-2
sbernauer Jun 2, 2025
27b89b4
chore: Merge branch 'main' into feat/flux-converter-2
Techassi Jun 3, 2025
95d2c83
chore: Merge branch 'main' into feat/flux-converter-2
Techassi Jun 3, 2025
3a10e4d
refactor(k8s-version): Move darling code into module
Techassi Jun 10, 2025
86a1c66
refactor(stackable-versioned): Split utils into separate files
Techassi Jun 10, 2025
bc7da2b
chore(stackable-versioned): Cleanup, move and improve conversion code
Techassi Jun 10, 2025
de8db62
chore(stackable-versioned): Move K8s related error enums
Techassi Jun 10, 2025
57011ab
chore(stackable-versioned): Move code, add error handling
Techassi Jun 11, 2025
51162b9
chore(stackable-versioned): Remove roundtrip tests
Techassi Jun 11, 2025
a6e1273
chore(crd-preview): Fix macro, add re-exports
Techassi Jun 11, 2025
297f75b
chore!(stackable-versioned): Remove unused merged_crd skip flag
Techassi Jun 11, 2025
15bb2a4
fix(stackable-versioned): Emit status field during conversion
Techassi Jun 11, 2025
cfa83ca
chore: Fix clippy lints
Techassi Jun 11, 2025
510a32e
test(stackable-versioned): Adjust snapshots
Techassi Jun 11, 2025
ae2954a
chore: Add rust-analyzer setting
Techassi Jun 11, 2025
83fec7e
chore: Allow RUSTSEC-2024-0436 advisory
Techassi Jun 11, 2025
70c5367
chore(stackable-versioned): Remove unused fixtures
Techassi Jun 11, 2025
37f476d
chore(stackable-versioned): Remove unused feature
Techassi Jun 11, 2025
0e544dc
chore(stackable-versioned): Fix rustdoc
Techassi Jun 11, 2025
b004805
chore: Remove unused dependencies
Techassi Jun 12, 2025
ffd4db8
chore(stackable-versioned): Move Otel attributes into constants
Techassi Jun 12, 2025
914b466
docs(stackable-versioned): Adjust K8s doc comments
Techassi Jun 12, 2025
619a26c
test(stackable-versioned): Improve conversion_paths unit test
Techassi Jun 12, 2025
a432a18
chore(stackable-versioned): Update changelog
Techassi Jun 12, 2025
54f4185
chore: Apply suggestions
Techassi Jun 13, 2025
195b7b7
docs(stackable-versioned): Add doc comments
Techassi Jun 13, 2025
e1bf934
chore: Apply suggestion
Techassi Jun 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"rust-analyzer.cargo.features": "all",
"rust-analyzer.imports.granularity.group": "crate",
Copy link
Member

Choose a reason for hiding this comment

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

Note to self: check if this is the same in our other rust repos:

  • operator-templating
  • stackablectl
  • docker-images (patchable)
  • containerdebug
  • actions (interu)
  • trino-lb
  • product-config (any reason this can't just be a crate inside operator-rs?)
  • config-utils

There are more, but they are less used.

Copy link
Member

Choose a reason for hiding this comment

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

Nothing to action here, this was just me thinking. I will check it out later.

"rust-analyzer.rustfmt.overrideCommand": [
"rustfmt",
"+nightly-2025-05-26",
Expand Down
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ educe = { version = "0.6.0", default-features = false, features = ["Clone", "De
either = "1.13.0"
futures = "0.3.30"
futures-util = "0.3.30"
indexmap = "2.5"
indexmap = "2.5.0"
indoc = "2.0.6"
insta = { version= "1.40", features = ["glob"] }
hyper = { version = "1.4.1", features = ["full"] }
hyper-util = "0.1.8"
Expand All @@ -41,6 +42,7 @@ opentelemetry-appender-tracing = "0.29.1"
opentelemetry-otlp = "0.29.0"
# opentelemetry-semantic-conventions = "0.28.0"
p256 = { version = "0.13.2", features = ["ecdsa"] }
paste = "1.0.15"
pin-project = "1.1.5"
prettyplease = "0.2.22"
proc-macro2 = "1.0.86"
Expand Down
35 changes: 35 additions & 0 deletions crates/k8s-version/src/api_version/darling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
use std::str::FromStr;

use darling::FromMeta;

use crate::ApiVersion;

impl FromMeta for ApiVersion {
fn from_string(value: &str) -> darling::Result<Self> {
Self::from_str(value).map_err(darling::Error::custom)
}
}

#[cfg(test)]
mod test {
use quote::quote;
use rstest::rstest;

use super::*;
use crate::{Level, Version};

fn parse_meta(tokens: proc_macro2::TokenStream) -> ::std::result::Result<syn::Meta, String> {
let attribute: syn::Attribute = syn::parse_quote!(#[#tokens]);
Ok(attribute.meta)
}

#[rstest]
#[case(quote!(ignore = "extensions/v1beta1"), ApiVersion { group: Some("extensions".parse().unwrap()), version: Version { major: 1, level: Some(Level::Beta(1)) } })]
#[case(quote!(ignore = "v1beta1"), ApiVersion { group: None, version: Version { major: 1, level: Some(Level::Beta(1)) } })]
#[case(quote!(ignore = "v1"), ApiVersion { group: None, version: Version { major: 1, level: None } })]
fn from_meta(#[case] input: proc_macro2::TokenStream, #[case] expected: ApiVersion) {
let meta = parse_meta(input).expect("valid attribute tokens");
let api_version = ApiVersion::from_meta(&meta).expect("version must parse from attribute");
assert_eq!(api_version, expected);
}
}
41 changes: 8 additions & 33 deletions crates/k8s-version/src/api_version/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
use std::{cmp::Ordering, fmt::Display, str::FromStr};

#[cfg(feature = "darling")]
use darling::FromMeta;
use snafu::{ResultExt, Snafu};

use crate::{Group, ParseGroupError, ParseVersionError, Version};

#[cfg(feature = "serde")]
mod serde;

#[cfg(feature = "darling")]
mod darling;

/// Error variants which can be encountered when creating a new [`ApiVersion`]
/// from unparsed input.
#[derive(Debug, PartialEq, Snafu)]
Expand Down Expand Up @@ -45,13 +46,13 @@ impl FromStr for ApiVersion {
fn from_str(input: &str) -> Result<Self, Self::Err> {
let (group, version) = if let Some((group, version)) = input.split_once('/') {
let group = Group::from_str(group).context(ParseGroupSnafu)?;
let version = Version::from_str(version).context(ParseVersionSnafu)?;

(
Some(group),
Version::from_str(version).context(ParseVersionSnafu)?,
)
(Some(group), version)
} else {
(None, Version::from_str(input).context(ParseVersionSnafu)?)
let version = Version::from_str(input).context(ParseVersionSnafu)?;

(None, version)
};

Ok(Self { group, version })
Expand All @@ -77,13 +78,6 @@ impl Display for ApiVersion {
}
}

#[cfg(feature = "darling")]
impl FromMeta for ApiVersion {
fn from_string(value: &str) -> darling::Result<Self> {
Self::from_str(value).map_err(darling::Error::custom)
}
}

impl ApiVersion {
/// Create a new Kubernetes API version.
pub fn new(group: Option<Group>, version: Version) -> Self {
Expand All @@ -104,19 +98,11 @@ impl ApiVersion {

#[cfg(test)]
mod test {
#[cfg(feature = "darling")]
use quote::quote;
use rstest::rstest;

use super::*;
use crate::Level;

#[cfg(feature = "darling")]
fn parse_meta(tokens: proc_macro2::TokenStream) -> ::std::result::Result<syn::Meta, String> {
let attribute: syn::Attribute = syn::parse_quote!(#[#tokens]);
Ok(attribute.meta)
}

#[rstest]
#[case("extensions/v1beta1", ApiVersion { group: Some("extensions".parse().unwrap()), version: Version { major: 1, level: Some(Level::Beta(1)) } })]
#[case("v1beta1", ApiVersion { group: None, version: Version { major: 1, level: Some(Level::Beta(1)) } })]
Expand Down Expand Up @@ -145,15 +131,4 @@ mod test {
fn partial_ord(#[case] input: Version, #[case] other: Version, #[case] expected: Ordering) {
assert_eq!(input.partial_cmp(&other), Some(expected));
}

#[cfg(feature = "darling")]
#[rstest]
#[case(quote!(ignore = "extensions/v1beta1"), ApiVersion { group: Some("extensions".parse().unwrap()), version: Version { major: 1, level: Some(Level::Beta(1)) } })]
#[case(quote!(ignore = "v1beta1"), ApiVersion { group: None, version: Version { major: 1, level: Some(Level::Beta(1)) } })]
#[case(quote!(ignore = "v1"), ApiVersion { group: None, version: Version { major: 1, level: None } })]
fn from_meta(#[case] input: proc_macro2::TokenStream, #[case] expected: ApiVersion) {
let meta = parse_meta(input).expect("valid attribute tokens");
let api_version = ApiVersion::from_meta(&meta).expect("version must parse from attribute");
assert_eq!(api_version, expected);
}
}
4 changes: 2 additions & 2 deletions crates/k8s-version/src/group.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ impl FromStr for Group {
ensure!(group.len() <= MAX_GROUP_LENGTH, TooLongSnafu);
ensure!(API_GROUP_REGEX.is_match(group), InvalidFormatSnafu);

Ok(Self(group.to_string()))
Ok(Self(group.to_owned()))
}
}

impl fmt::Display for Group {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
f.write_str(self)
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/stackable-operator/src/commons/resources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
//! crates(
//! kube_core = "stackable_operator::kube::core",
//! k8s_openapi = "stackable_operator::k8s_openapi",
//! schemars = "stackable_operator::schemars"
//! schemars = "stackable_operator::schemars",
//! )
//! )]
//! #[serde(rename_all = "camelCase")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ pub mod versioned {
crates(
kube_core = "kube::core",
k8s_openapi = "k8s_openapi",
schemars = "schemars"
schemars = "schemars",
)
))]
#[derive(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl Default for FieldNames {
#[cfg(test)]
mod tests {
use super::*;
use crate::commons::secret_class::SecretClassVolume;
use crate::{commons::secret_class::SecretClassVolume, utils::yaml_from_str_singleton_map};

#[test]
fn minimal() {
Expand Down Expand Up @@ -213,9 +213,7 @@ mod tests {
caCert:
secretClass: ldap-ca-cert
"#;
let deserializer = serde_yaml::Deserializer::from_str(input);
let ldap: AuthenticationProvider =
serde_yaml::with::singleton_map_recursive::deserialize(deserializer).unwrap();
let ldap: AuthenticationProvider = yaml_from_str_singleton_map(input).unwrap();

assert_eq!(ldap.port(), 42);
assert!(ldap.tls.uses_tls());
Expand Down
6 changes: 2 additions & 4 deletions crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ mod tests {
use super::*;
use crate::{
config::fragment::validate, product_config_utils::env_vars_from,
product_logging::spec::default_container_log_config,
product_logging::spec::default_container_log_config, utils::yaml_from_str_singleton_map,
};

#[test]
Expand Down Expand Up @@ -435,9 +435,7 @@ mod tests {
--git-config: key:value,safe.directory:/safe-dir
"#;

let deserializer = serde_yaml::Deserializer::from_str(git_sync_spec);
let git_syncs: Vec<GitSync> =
serde_yaml::with::singleton_map_recursive::deserialize(deserializer).unwrap();
let git_syncs: Vec<GitSync> = yaml_from_str_singleton_map(git_sync_spec).unwrap();

let resolved_product_image = ResolvedProductImage {
image: "oci.stackable.tech/sdp/product:latest".to_string(),
Expand Down
4 changes: 2 additions & 2 deletions crates/stackable-operator/src/crd/listener/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ mod class;
mod core;
mod listeners;

pub use class::ListenerClass;
pub use listeners::{Listener, PodListeners};
pub use class::{ListenerClass, ListenerClassVersion};
pub use listeners::{Listener, ListenerVersion, PodListeners, PodListenersVersion};

// Group all v1alpha1 items in one module.
pub mod v1alpha1 {
Expand Down
2 changes: 1 addition & 1 deletion crates/stackable-operator/src/crd/s3/bucket/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub mod versioned {
crates(
kube_core = "kube::core",
k8s_openapi = "k8s_openapi",
schemars = "schemars"
schemars = "schemars",
),
namespaced
))]
Expand Down
2 changes: 1 addition & 1 deletion crates/stackable-operator/src/crd/s3/connection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ pub mod versioned {
crates(
kube_core = "kube::core",
k8s_openapi = "k8s_openapi",
schemars = "schemars"
schemars = "schemars",
),
namespaced
))]
Expand Down
4 changes: 2 additions & 2 deletions crates/stackable-operator/src/crd/s3/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
mod bucket;
mod connection;

pub use bucket::S3Bucket;
pub use connection::S3Connection;
pub use bucket::{S3Bucket, S3BucketVersion};
pub use connection::{S3Connection, S3ConnectionVersion};

// Group all v1alpha1 items in one module.
pub mod v1alpha1 {
Expand Down
8 changes: 8 additions & 0 deletions crates/stackable-operator/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,11 @@ pub use self::{option::OptionExt, url::UrlExt};
pub(crate) fn format_full_controller_name(operator: &str, controller: &str) -> String {
format!("{operator}_{controller}")
}

pub fn yaml_from_str_singleton_map<'a, D>(input: &'a str) -> Result<D, serde_yaml::Error>
where
D: serde::Deserialize<'a>,
{
let deserializer = serde_yaml::Deserializer::from_str(input);
serde_yaml::with::singleton_map_recursive::deserialize(deserializer)
}
2 changes: 2 additions & 0 deletions crates/stackable-versioned-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ k8s-version = { path = "../k8s-version", features = ["darling"] }

convert_case.workspace = true
darling.workspace = true
indoc.workspace = true
itertools.workspace = true
k8s-openapi = { workspace = true, optional = true }
kube = { workspace = true, optional = true }
Expand All @@ -53,4 +54,5 @@ serde.workspace = true
serde_json.workspace = true
serde_yaml.workspace = true
snafu.workspace = true
tracing.workspace = true
trybuild.workspace = true
16 changes: 1 addition & 15 deletions crates/stackable-versioned-macros/src/attrs/container/k8s.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,10 @@ pub struct KubernetesArguments {
// doc
// annotation
// label
pub skip: Option<KubernetesSkipArguments>,

#[darling(default)]
pub options: KubernetesConfigOptions,
}

/// This struct contains supported kubernetes skip arguments.
///
/// Supported arguments are:
///
/// - `merged_crd` flag, which skips generating the `crd()` and `merged_crd()` functions are
/// generated.
#[derive(Clone, Debug, FromMeta)]
pub struct KubernetesSkipArguments {
/// Whether the `crd()` and `merged_crd()` generation should be skipped for
/// this container.
pub merged_crd: Flag,
}

/// This struct contains crate overrides to be passed to `#[kube]`.
#[derive(Clone, Debug, FromMeta)]
pub struct KubernetesCrateArguments {
Expand Down Expand Up @@ -176,4 +161,5 @@ impl ToTokens for KubernetesCrateArguments {
#[derive(Clone, Default, Debug, FromMeta)]
pub struct KubernetesConfigOptions {
pub experimental_conversion_tracking: Flag,
pub enable_tracing: Flag,
}
Loading