Skip to content

Commit cc7d240

Browse files
sbernauerTechassiNickLarsenNZ
authored
feat(stackable-versioned)!: Integrate with ConversionReviews (#1050)
* feat(stackable-versioned): Add flux-converter * test: Add roundtrip generation via macro * fix some tests * Fix remaining tests * fix rest of tests * refactor(k8s-version): Move darling code into module * refactor(stackable-versioned): Split utils into separate files * chore(stackable-versioned): Cleanup, move and improve conversion code * chore(stackable-versioned): Move K8s related error enums * chore(stackable-versioned): Move code, add error handling * chore(stackable-versioned): Remove roundtrip tests These tests are removed for now, because we are not able to roundtrip CRD conversions without data loss yet. This feature will be re-added in a follow-up PR in an improved form once loss-less roundtripping is supported. * chore(crd-preview): Fix macro, add re-exports * chore!(stackable-versioned): Remove unused merged_crd skip flag * fix(stackable-versioned): Emit status field during conversion * chore: Fix clippy lints * test(stackable-versioned): Adjust snapshots * chore: Add rust-analyzer setting * chore: Allow RUSTSEC-2024-0436 advisory * chore(stackable-versioned): Remove unused fixtures * chore(stackable-versioned): Remove unused feature * chore(stackable-versioned): Fix rustdoc * chore: Remove unused dependencies * chore(stackable-versioned): Move Otel attributes into constants * docs(stackable-versioned): Adjust K8s doc comments * test(stackable-versioned): Improve conversion_paths unit test * chore(stackable-versioned): Update changelog * chore: Apply suggestions Co-authored-by: Nick <[email protected]> * docs(stackable-versioned): Add doc comments * chore: Apply suggestion Co-authored-by: Nick <[email protected]> --------- Co-authored-by: Techassi <[email protected]> Co-authored-by: Nick <[email protected]>
1 parent 1b610a8 commit cc7d240

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+3265
-752
lines changed

.vscode/settings.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"rust-analyzer.cargo.features": "all",
3+
"rust-analyzer.imports.granularity.group": "crate",
34
"rust-analyzer.rustfmt.overrideCommand": [
45
"rustfmt",
56
"+nightly-2025-05-26",

Cargo.lock

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ educe = { version = "0.6.0", default-features = false, features = ["Clone", "De
2525
either = "1.13.0"
2626
futures = "0.3.30"
2727
futures-util = "0.3.30"
28-
indexmap = "2.5"
28+
indexmap = "2.5.0"
29+
indoc = "2.0.6"
2930
insta = { version= "1.40", features = ["glob"] }
3031
hyper = { version = "1.4.1", features = ["full"] }
3132
hyper-util = "0.1.8"
@@ -41,6 +42,7 @@ opentelemetry-appender-tracing = "0.29.1"
4142
opentelemetry-otlp = "0.29.0"
4243
# opentelemetry-semantic-conventions = "0.28.0"
4344
p256 = { version = "0.13.2", features = ["ecdsa"] }
45+
paste = "1.0.15"
4446
pin-project = "1.1.5"
4547
prettyplease = "0.2.22"
4648
proc-macro2 = "1.0.86"
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use std::str::FromStr;
2+
3+
use darling::FromMeta;
4+
5+
use crate::ApiVersion;
6+
7+
impl FromMeta for ApiVersion {
8+
fn from_string(value: &str) -> darling::Result<Self> {
9+
Self::from_str(value).map_err(darling::Error::custom)
10+
}
11+
}
12+
13+
#[cfg(test)]
14+
mod test {
15+
use quote::quote;
16+
use rstest::rstest;
17+
18+
use super::*;
19+
use crate::{Level, Version};
20+
21+
fn parse_meta(tokens: proc_macro2::TokenStream) -> ::std::result::Result<syn::Meta, String> {
22+
let attribute: syn::Attribute = syn::parse_quote!(#[#tokens]);
23+
Ok(attribute.meta)
24+
}
25+
26+
#[rstest]
27+
#[case(quote!(ignore = "extensions/v1beta1"), ApiVersion { group: Some("extensions".parse().unwrap()), version: Version { major: 1, level: Some(Level::Beta(1)) } })]
28+
#[case(quote!(ignore = "v1beta1"), ApiVersion { group: None, version: Version { major: 1, level: Some(Level::Beta(1)) } })]
29+
#[case(quote!(ignore = "v1"), ApiVersion { group: None, version: Version { major: 1, level: None } })]
30+
fn from_meta(#[case] input: proc_macro2::TokenStream, #[case] expected: ApiVersion) {
31+
let meta = parse_meta(input).expect("valid attribute tokens");
32+
let api_version = ApiVersion::from_meta(&meta).expect("version must parse from attribute");
33+
assert_eq!(api_version, expected);
34+
}
35+
}

crates/k8s-version/src/api_version/mod.rs

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
use std::{cmp::Ordering, fmt::Display, str::FromStr};
22

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

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

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

10+
#[cfg(feature = "darling")]
11+
mod darling;
12+
1213
/// Error variants which can be encountered when creating a new [`ApiVersion`]
1314
/// from unparsed input.
1415
#[derive(Debug, PartialEq, Snafu)]
@@ -45,13 +46,13 @@ impl FromStr for ApiVersion {
4546
fn from_str(input: &str) -> Result<Self, Self::Err> {
4647
let (group, version) = if let Some((group, version)) = input.split_once('/') {
4748
let group = Group::from_str(group).context(ParseGroupSnafu)?;
49+
let version = Version::from_str(version).context(ParseVersionSnafu)?;
4850

49-
(
50-
Some(group),
51-
Version::from_str(version).context(ParseVersionSnafu)?,
52-
)
51+
(Some(group), version)
5352
} else {
54-
(None, Version::from_str(input).context(ParseVersionSnafu)?)
53+
let version = Version::from_str(input).context(ParseVersionSnafu)?;
54+
55+
(None, version)
5556
};
5657

5758
Ok(Self { group, version })
@@ -77,13 +78,6 @@ impl Display for ApiVersion {
7778
}
7879
}
7980

80-
#[cfg(feature = "darling")]
81-
impl FromMeta for ApiVersion {
82-
fn from_string(value: &str) -> darling::Result<Self> {
83-
Self::from_str(value).map_err(darling::Error::custom)
84-
}
85-
}
86-
8781
impl ApiVersion {
8882
/// Create a new Kubernetes API version.
8983
pub fn new(group: Option<Group>, version: Version) -> Self {
@@ -104,19 +98,11 @@ impl ApiVersion {
10498

10599
#[cfg(test)]
106100
mod test {
107-
#[cfg(feature = "darling")]
108-
use quote::quote;
109101
use rstest::rstest;
110102

111103
use super::*;
112104
use crate::Level;
113105

114-
#[cfg(feature = "darling")]
115-
fn parse_meta(tokens: proc_macro2::TokenStream) -> ::std::result::Result<syn::Meta, String> {
116-
let attribute: syn::Attribute = syn::parse_quote!(#[#tokens]);
117-
Ok(attribute.meta)
118-
}
119-
120106
#[rstest]
121107
#[case("extensions/v1beta1", ApiVersion { group: Some("extensions".parse().unwrap()), version: Version { major: 1, level: Some(Level::Beta(1)) } })]
122108
#[case("v1beta1", ApiVersion { group: None, version: Version { major: 1, level: Some(Level::Beta(1)) } })]
@@ -145,15 +131,4 @@ mod test {
145131
fn partial_ord(#[case] input: Version, #[case] other: Version, #[case] expected: Ordering) {
146132
assert_eq!(input.partial_cmp(&other), Some(expected));
147133
}
148-
149-
#[cfg(feature = "darling")]
150-
#[rstest]
151-
#[case(quote!(ignore = "extensions/v1beta1"), ApiVersion { group: Some("extensions".parse().unwrap()), version: Version { major: 1, level: Some(Level::Beta(1)) } })]
152-
#[case(quote!(ignore = "v1beta1"), ApiVersion { group: None, version: Version { major: 1, level: Some(Level::Beta(1)) } })]
153-
#[case(quote!(ignore = "v1"), ApiVersion { group: None, version: Version { major: 1, level: None } })]
154-
fn from_meta(#[case] input: proc_macro2::TokenStream, #[case] expected: ApiVersion) {
155-
let meta = parse_meta(input).expect("valid attribute tokens");
156-
let api_version = ApiVersion::from_meta(&meta).expect("version must parse from attribute");
157-
assert_eq!(api_version, expected);
158-
}
159134
}

crates/k8s-version/src/group.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,13 @@ impl FromStr for Group {
4747
ensure!(group.len() <= MAX_GROUP_LENGTH, TooLongSnafu);
4848
ensure!(API_GROUP_REGEX.is_match(group), InvalidFormatSnafu);
4949

50-
Ok(Self(group.to_string()))
50+
Ok(Self(group.to_owned()))
5151
}
5252
}
5353

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

crates/stackable-operator/src/commons/resources.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
//! crates(
3939
//! kube_core = "stackable_operator::kube::core",
4040
//! k8s_openapi = "stackable_operator::k8s_openapi",
41-
//! schemars = "stackable_operator::schemars"
41+
//! schemars = "stackable_operator::schemars",
4242
//! )
4343
//! )]
4444
//! #[serde(rename_all = "camelCase")]

crates/stackable-operator/src/crd/authentication/core/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ pub mod versioned {
3232
crates(
3333
kube_core = "kube::core",
3434
k8s_openapi = "k8s_openapi",
35-
schemars = "schemars"
35+
schemars = "schemars",
3636
)
3737
))]
3838
#[derive(

crates/stackable-operator/src/crd/authentication/ldap/v1alpha1_impl.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ impl Default for FieldNames {
169169
#[cfg(test)]
170170
mod tests {
171171
use super::*;
172-
use crate::commons::secret_class::SecretClassVolume;
172+
use crate::{commons::secret_class::SecretClassVolume, utils::yaml_from_str_singleton_map};
173173

174174
#[test]
175175
fn minimal() {
@@ -213,9 +213,7 @@ mod tests {
213213
caCert:
214214
secretClass: ldap-ca-cert
215215
"#;
216-
let deserializer = serde_yaml::Deserializer::from_str(input);
217-
let ldap: AuthenticationProvider =
218-
serde_yaml::with::singleton_map_recursive::deserialize(deserializer).unwrap();
216+
let ldap: AuthenticationProvider = yaml_from_str_singleton_map(input).unwrap();
219217

220218
assert_eq!(ldap.port(), 42);
221219
assert!(ldap.tls.uses_tls());

crates/stackable-operator/src/crd/git_sync/v1alpha1_impl.rs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ mod tests {
365365
use super::*;
366366
use crate::{
367367
config::fragment::validate, product_config_utils::env_vars_from,
368-
product_logging::spec::default_container_log_config,
368+
product_logging::spec::default_container_log_config, utils::yaml_from_str_singleton_map,
369369
};
370370

371371
#[test]
@@ -435,9 +435,7 @@ mod tests {
435435
--git-config: key:value,safe.directory:/safe-dir
436436
"#;
437437

438-
let deserializer = serde_yaml::Deserializer::from_str(git_sync_spec);
439-
let git_syncs: Vec<GitSync> =
440-
serde_yaml::with::singleton_map_recursive::deserialize(deserializer).unwrap();
438+
let git_syncs: Vec<GitSync> = yaml_from_str_singleton_map(git_sync_spec).unwrap();
441439

442440
let resolved_product_image = ResolvedProductImage {
443441
image: "oci.stackable.tech/sdp/product:latest".to_string(),

crates/stackable-operator/src/crd/listener/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ mod class;
1313
mod core;
1414
mod listeners;
1515

16-
pub use class::ListenerClass;
17-
pub use listeners::{Listener, PodListeners};
16+
pub use class::{ListenerClass, ListenerClassVersion};
17+
pub use listeners::{Listener, ListenerVersion, PodListeners, PodListenersVersion};
1818

1919
// Group all v1alpha1 items in one module.
2020
pub mod v1alpha1 {

crates/stackable-operator/src/crd/s3/bucket/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub mod versioned {
2121
crates(
2222
kube_core = "kube::core",
2323
k8s_openapi = "k8s_openapi",
24-
schemars = "schemars"
24+
schemars = "schemars",
2525
),
2626
namespaced
2727
))]

crates/stackable-operator/src/crd/s3/connection/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ pub mod versioned {
3131
crates(
3232
kube_core = "kube::core",
3333
k8s_openapi = "k8s_openapi",
34-
schemars = "schemars"
34+
schemars = "schemars",
3535
),
3636
namespaced
3737
))]

crates/stackable-operator/src/crd/s3/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
mod bucket;
22
mod connection;
33

4-
pub use bucket::S3Bucket;
5-
pub use connection::S3Connection;
4+
pub use bucket::{S3Bucket, S3BucketVersion};
5+
pub use connection::{S3Connection, S3ConnectionVersion};
66

77
// Group all v1alpha1 items in one module.
88
pub mod v1alpha1 {

crates/stackable-operator/src/utils/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,11 @@ pub use self::{option::OptionExt, url::UrlExt};
2525
pub(crate) fn format_full_controller_name(operator: &str, controller: &str) -> String {
2626
format!("{operator}_{controller}")
2727
}
28+
29+
pub fn yaml_from_str_singleton_map<'a, D>(input: &'a str) -> Result<D, serde_yaml::Error>
30+
where
31+
D: serde::Deserialize<'a>,
32+
{
33+
let deserializer = serde_yaml::Deserializer::from_str(input);
34+
serde_yaml::with::singleton_map_recursive::deserialize(deserializer)
35+
}

crates/stackable-versioned-macros/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ k8s-version = { path = "../k8s-version", features = ["darling"] }
3333

3434
convert_case.workspace = true
3535
darling.workspace = true
36+
indoc.workspace = true
3637
itertools.workspace = true
3738
k8s-openapi = { workspace = true, optional = true }
3839
kube = { workspace = true, optional = true }
@@ -53,4 +54,5 @@ serde.workspace = true
5354
serde_json.workspace = true
5455
serde_yaml.workspace = true
5556
snafu.workspace = true
57+
tracing.workspace = true
5658
trybuild.workspace = true

crates/stackable-versioned-macros/src/attrs/container/k8s.rs

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,10 @@ pub struct KubernetesArguments {
4747
// doc
4848
// annotation
4949
// label
50-
pub skip: Option<KubernetesSkipArguments>,
51-
5250
#[darling(default)]
5351
pub options: KubernetesConfigOptions,
5452
}
5553

56-
/// This struct contains supported kubernetes skip arguments.
57-
///
58-
/// Supported arguments are:
59-
///
60-
/// - `merged_crd` flag, which skips generating the `crd()` and `merged_crd()` functions are
61-
/// generated.
62-
#[derive(Clone, Debug, FromMeta)]
63-
pub struct KubernetesSkipArguments {
64-
/// Whether the `crd()` and `merged_crd()` generation should be skipped for
65-
/// this container.
66-
pub merged_crd: Flag,
67-
}
68-
6954
/// This struct contains crate overrides to be passed to `#[kube]`.
7055
#[derive(Clone, Debug, FromMeta)]
7156
pub struct KubernetesCrateArguments {
@@ -176,4 +161,5 @@ impl ToTokens for KubernetesCrateArguments {
176161
#[derive(Clone, Default, Debug, FromMeta)]
177162
pub struct KubernetesConfigOptions {
178163
pub experimental_conversion_tracking: Flag,
164+
pub enable_tracing: Flag,
179165
}

0 commit comments

Comments
 (0)