Skip to content

Commit

Permalink
Merge branch 'main' into fix/clashing-volumes-and-mounts-2
Browse files Browse the repository at this point in the history
  • Loading branch information
sbernauer authored Sep 24, 2024
2 parents b7fcdaa + 8fcf506 commit 78bd455
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 35 deletions.
2 changes: 2 additions & 0 deletions crates/stackable-operator/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ All notable changes to this project will be documented in this file.

### Fixed

- Fix the logback configuration for logback versions from 1.3.6/1.4.6 to 1.3.11/1.4.11 ([#874]).
- BREAKING: Avoid clashing volumes and mounts by only adding volumes or mounts if they do not already exist. This makes functions such as `PodBuilder::add_volume` or `ContainerBuilder::add_volume_mount` as well as related ones fallible ([#871]).

### Changed

- BREAKING: Remove the `unique_identifier` argument from `ResolvedS3Connection::add_volumes_and_mounts`, `ResolvedS3Connection::volumes_and_mounts` and `ResolvedS3Connection::credentials_mount_paths` as it is not needed anymore ([#871]).

[#871]: https://github.com/stackabletech/operator-rs/pull/871
[#874]: https://github.com/stackabletech/operator-rs/pull/874

## [0.76.0] - 2024-09-19

Expand Down
14 changes: 13 additions & 1 deletion crates/stackable-operator/src/product_logging/framework.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,19 @@ pub fn create_logback_config(
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<MaxFileSize>{max_log_file_size_in_mib}MB</MaxFileSize>
<checkIncrement>5 seconds</checkIncrement>
<!--
checkIncrement defines how often file sizes are checked, because
checking them is a relatively costly operation.
checkIncrement was introduced in the SizeBasedTriggeringPolicy in
logback 1.3.6/1.4.6 as an Integer representing milliseconds. In logback
1.3.12/1.4.12, it was changed to a Duration, also accepting a unit.
Without a given unit, milliseconds are assumed. The logback manual is
misleading: In logback 1.5.8, checkIncrement is no longer used for the
SizeAndTimeBasedFileNamingAndTriggeringPolicy, but it is still used for
the SizeBasedTriggeringPolicy!
In prior versions of logback, setting this option has no effect.
-->
<checkIncrement>5000</checkIncrement>
</triggeringPolicy>
</appender>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ pub(crate) struct OptionAttributes {
#[derive(Clone, Debug, FromMeta)]
pub(crate) struct KubernetesAttributes {
pub(crate) skip: Option<KubernetesSkipAttributes>,
pub(crate) singular: Option<String>,
pub(crate) plural: Option<String>,
pub(crate) kind: Option<String>,
pub(crate) namespaced: Flag,
pub(crate) group: String,
}

Expand Down
18 changes: 18 additions & 0 deletions crates/stackable-versioned-macros/src/codegen/common/container.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
use std::ops::Deref;

use convert_case::{Case, Casing};
use k8s_version::Version;
use proc_macro2::TokenStream;
use quote::format_ident;
use syn::{Attribute, Ident, Visibility};
Expand Down Expand Up @@ -52,6 +54,16 @@ impl IdentExt for Ident {
}
}

pub(crate) trait VersionExt {
fn as_variant_ident(&self) -> Ident;
}

impl VersionExt for Version {
fn as_variant_ident(&self) -> Ident {
format_ident!("{ident}", ident = self.to_string().to_case(Case::Pascal))
}
}

/// This struct bundles values from [`DeriveInput`][1].
///
/// [`DeriveInput`][1] cannot be used directly when constructing a
Expand Down Expand Up @@ -118,6 +130,9 @@ impl<I> VersionedContainer<I> {

let kubernetes_options = attributes.kubernetes_attrs.map(|a| KubernetesOptions {
skip_merged_crd: a.skip.map_or(false, |s| s.merged_crd.is_present()),
namespaced: a.namespaced.is_present(),
singular: a.singular,
plural: a.plural,
group: a.group,
kind: a.kind,
});
Expand Down Expand Up @@ -166,7 +181,10 @@ pub(crate) struct VersionedContainerOptions {

#[derive(Debug)]
pub(crate) struct KubernetesOptions {
pub(crate) singular: Option<String>,
pub(crate) plural: Option<String>,
pub(crate) skip_merged_crd: bool,
pub(crate) kind: Option<String>,
pub(crate) namespaced: bool,
pub(crate) group: String,
}
135 changes: 103 additions & 32 deletions crates/stackable-versioned-macros/src/codegen/vstruct/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ use syn::{parse_quote, DataStruct, Error, Ident};
use crate::{
attrs::common::ContainerAttributes,
codegen::{
common::{Container, ContainerInput, ContainerVersion, Item, VersionedContainer},
common::{
Container, ContainerInput, ContainerVersion, Item, VersionExt, VersionedContainer,
},
vstruct::field::VersionedField,
},
};

pub(crate) mod field;

type GenerateVersionReturn = (TokenStream, Option<(TokenStream, (Ident, String))>);

/// Stores individual versions of a single struct. Each version tracks field
/// actions, which describe if the field was added, renamed or deprecated in
/// that version. Fields which are not versioned, are included in every
Expand Down Expand Up @@ -85,24 +89,30 @@ impl Container<DataStruct, VersionedField> for VersionedStruct {
}

fn generate_tokens(&self) -> TokenStream {
let mut kubernetes_crd_fn_calls = TokenStream::new();
let mut container_definition = TokenStream::new();
let mut tokens = TokenStream::new();

let mut enum_variants = Vec::new();
let mut crd_fn_calls = Vec::new();

let mut versions = self.versions.iter().peekable();

while let Some(version) = versions.next() {
container_definition.extend(self.generate_version(version, versions.peek().copied()));
kubernetes_crd_fn_calls.extend(self.generate_kubernetes_crd_fn_call(version));
let (container_definition, merged_crd) =
self.generate_version(version, versions.peek().copied());

if let Some((crd_fn_call, enum_variant)) = merged_crd {
enum_variants.push(enum_variant);
crd_fn_calls.push(crd_fn_call);
}

tokens.extend(container_definition);
}

// If tokens for the 'crd()' function calls were generated, also generate
// the 'merge_crds' call.
if !kubernetes_crd_fn_calls.is_empty() {
container_definition
.extend(self.generate_kubernetes_merge_crds(kubernetes_crd_fn_calls));
if !crd_fn_calls.is_empty() {
tokens.extend(self.generate_kubernetes_merge_crds(crd_fn_calls, enum_variants));
}

container_definition
tokens
}
}

Expand All @@ -112,7 +122,7 @@ impl VersionedStruct {
&self,
version: &ContainerVersion,
next_version: Option<&ContainerVersion>,
) -> TokenStream {
) -> GenerateVersionReturn {
let mut token_stream = TokenStream::new();

let original_attributes = &self.original_attributes;
Expand All @@ -137,7 +147,27 @@ impl VersionedStruct {
let version_specific_docs = self.generate_struct_docs(version);

// Generate K8s specific code
let kubernetes_cr_derive = self.generate_kubernetes_cr_derive(version);
let (kubernetes_cr_derive, merged_crd) = match &self.options.kubernetes_options {
Some(options) => {
// Generate the CustomResource derive macro with the appropriate
// attributes supplied using #[kube()].
let cr_derive = self.generate_kubernetes_cr_derive(version);

// Generate merged_crd specific code when not opted out.
let merged_crd = if !options.skip_merged_crd {
let crd_fn_call = self.generate_kubernetes_crd_fn_call(version);
let enum_variant = version.inner.as_variant_ident();
let enum_display = version.inner.to_string();

Some((crd_fn_call, (enum_variant, enum_display)))
} else {
None
};

(Some(cr_derive), merged_crd)
}
None => (None, None),
};

// Generate tokens for the module and the contained struct
token_stream.extend(quote! {
Expand All @@ -160,7 +190,7 @@ impl VersionedStruct {
token_stream.extend(self.generate_from_impl(version, next_version));
}

token_stream
(token_stream, merged_crd)
}

/// Generates version specific doc comments for the struct.
Expand Down Expand Up @@ -253,61 +283,102 @@ impl VersionedStruct {
/// attributes.
fn generate_kubernetes_cr_derive(&self, version: &ContainerVersion) -> Option<TokenStream> {
if let Some(kubernetes_options) = &self.options.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.idents.kubernetes.to_string(), |kind| kind.clone());

// Optional arguments
let namespaced = kubernetes_options
.namespaced
.then_some(quote! { , namespaced });
let singular = kubernetes_options
.singular
.as_ref()
.map(|s| quote! { , singular = #s });
let plural = kubernetes_options
.plural
.as_ref()
.map(|p| quote! { , plural = #p });

return Some(quote! {
#[derive(::kube::CustomResource)]
#[kube(group = #group, version = #version, kind = #kind)]
#[kube(group = #group, version = #version, kind = #kind #singular #plural #namespaced)]
});
}

None
}

/// Generates the `merge_crds` function call.
fn generate_kubernetes_merge_crds(&self, fn_calls: TokenStream) -> TokenStream {
fn generate_kubernetes_merge_crds(
&self,
crd_fn_calls: Vec<TokenStream>,
enum_variants: Vec<(Ident, String)>,
) -> TokenStream {
let ident = &self.idents.kubernetes;

let version_enum_definition = self.generate_kubernetes_version_enum(enum_variants);

quote! {
#[automatically_derived]
pub struct #ident;

#version_enum_definition

#[automatically_derived]
impl #ident {
/// Generates a merged CRD which contains all versions defined using the
/// `#[versioned()]` macro.
pub fn merged_crd(
stored_apiversion: &str
stored_apiversion: Version
) -> ::std::result::Result<::k8s_openapi::apiextensions_apiserver::pkg::apis::apiextensions::v1::CustomResourceDefinition, ::kube::core::crd::MergeError> {
::kube::core::crd::merge_crds(vec![#fn_calls], stored_apiversion)
::kube::core::crd::merge_crds(vec![#(#crd_fn_calls),*], &stored_apiversion.to_string())
}
}
}
}

/// Generates the inner `crd()` functions calls which get used in the
/// `merge_crds` function.
fn generate_kubernetes_crd_fn_call(&self, version: &ContainerVersion) -> Option<TokenStream> {
if self
.options
.kubernetes_options
.as_ref()
.is_some_and(|o| !o.skip_merged_crd)
{
let struct_ident = &self.idents.kubernetes;
let version_ident = &version.ident;

let path: syn::Path = parse_quote!(#version_ident::#struct_ident);
return Some(quote! {
<#path as ::kube::CustomResourceExt>::crd(),
fn generate_kubernetes_crd_fn_call(&self, version: &ContainerVersion) -> TokenStream {
let struct_ident = &self.idents.kubernetes;
let version_ident = &version.ident;
let path: syn::Path = parse_quote!(#version_ident::#struct_ident);

quote! {
<#path as ::kube::CustomResourceExt>::crd()
}
}

fn generate_kubernetes_version_enum(&self, enum_variants: Vec<(Ident, String)>) -> TokenStream {
let mut enum_variant_matches = TokenStream::new();
let mut enum_variant_idents = TokenStream::new();

for (enum_variant_ident, enum_variant_display) in enum_variants {
enum_variant_idents.extend(quote! {#enum_variant_ident,});
enum_variant_matches.extend(quote! {
Version::#enum_variant_ident => f.write_str(#enum_variant_display),
});
}

None
quote! {
#[automatically_derived]
pub enum Version {
#enum_variant_idents
}

#[automatically_derived]
impl ::std::fmt::Display for Version {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::result::Result<(), ::std::fmt::Error> {
match self {
#enum_variant_matches
}
}
}
}
}
}
9 changes: 9 additions & 0 deletions crates/stackable-versioned-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,15 @@ println!("{}", serde_yaml::to_string(&merged_crd).unwrap());
```
"#
)]
/// Currently, the following arguments are supported:
///
/// - `group`: Sets the CRD group, usually the domain of the company.
/// - `kind`: Allows overwriting the kind field of the CRD. This defaults
/// to the struct name (without the 'Spec' suffix).
/// - `singular`: Sets the singular name.
/// - `plural`: Sets the plural name.
/// - `namespaced`: Specifies that this is a namespaced resource rather than
/// a cluster scoped.
#[proc_macro_attribute]
pub fn versioned(attrs: TokenStream, input: TokenStream) -> TokenStream {
let attrs = match NestedMeta::parse_meta_list(attrs.into()) {
Expand Down
9 changes: 7 additions & 2 deletions crates/stackable-versioned-macros/tests/k8s/pass/crd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ fn main() {
version(name = "v1alpha1"),
version(name = "v1beta1"),
version(name = "v1"),
k8s(group = "stackable.tech")
k8s(
group = "stackable.tech",
singular = "foo",
plural = "foos",
namespaced,
)
)]
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
pub struct FooSpec {
Expand All @@ -21,6 +26,6 @@ fn main() {
baz: bool,
}

let merged_crd = Foo::merged_crd("v1").unwrap();
let merged_crd = Foo::merged_crd(Version::V1).unwrap();
println!("{}", serde_yaml::to_string(&merged_crd).unwrap());
}
15 changes: 15 additions & 0 deletions crates/stackable-versioned/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Added

- Add forwarding of `singular`, `plural`, and `namespaced` arguments in `k8s()`
([#873]).
- Generate a `Version` enum containing all declared versions as variants
([#872]).

### Changed

- The `merged_crd` associated function now takes `Version` instead of `&str` as
input ([#872]).

[#872]: https://github.com/stackabletech/operator-rs/pull/872
[#873]: https://github.com/stackabletech/operator-rs/pull/873

## [0.2.0] - 2024-09-19

### Added
Expand Down

0 comments on commit 78bd455

Please sign in to comment.