Skip to content
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 by Bors] - Ensure that explicit YAML documents are generated for CRDs #450

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,10 +4,19 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Added

- YAML module added with a function to serialize a data structure as an
explicit YAML document. The YAML documents generated by the functions in
`crd::CustomResourceExt` are now explicit documents and can be safely
concatenated to produce a YAML stream ([#450]).

### Changed

- Objects are now streamed rather than polled when waiting for them to be deleted ([#452]).
- serde\_yaml 0.8.26 -> 0.9.9 ([#450])

[#450]: https://github.com/stackabletech/operator-rs/pull/450
[#452]: https://github.com/stackabletech/operator-rs/pull/452

## [0.24.0] - 2022-08-04
Expand Down
3 changes: 1 addition & 2 deletions Cargo.toml
Expand Up @@ -23,7 +23,7 @@ regex = "1.6.0"
schemars = "0.8.10"
serde = { version = "1.0.140", features = ["derive"] }
serde_json = "1.0.82"
serde_yaml = "0.8.26"
serde_yaml = "0.9.9"
strum = { version = "0.24.1", features = ["derive"] }
thiserror = "1.0.31"
tokio = { version = "1.20.1", features = ["macros", "rt-multi-thread"] }
Expand All @@ -38,7 +38,6 @@ stackable-operator-derive = { path = "stackable-operator-derive" }
[dev-dependencies]
rstest = "0.15.0"
tempfile = "3.3.0"
serde_yaml = "0.8"

[features]
default = ["native-tls"]
Expand Down
13 changes: 6 additions & 7 deletions src/cli.rs
Expand Up @@ -11,10 +11,10 @@
//! ```no_run
//! // Handle CLI arguments
//! use clap::{crate_version, Parser};
//! use kube::{CustomResource, CustomResourceExt};
//! use kube::CustomResource;
//! use schemars::JsonSchema;
//! use serde::{Deserialize, Serialize};
//! use stackable_operator::cli;
//! use stackable_operator::{CustomResourceExt, cli};
//! use stackable_operator::error::OperatorResult;
//!
//! #[derive(Clone, CustomResource, Debug, JsonSchema, Serialize, Deserialize)]
Expand Down Expand Up @@ -55,11 +55,10 @@
//! let opts = Opts::from_args();
//!
//! match opts.command {
//! cli::Command::Crd => println!(
//! "{}{}",
//! serde_yaml::to_string(&FooCluster::crd())?,
//! serde_yaml::to_string(&BarCluster::crd())?,
//! ),
//! cli::Command::Crd => {
//! FooCluster::print_yaml_schema()?;
//! BarCluster::print_yaml_schema()?;
//! },
//! cli::Command::Run { .. } => {
//! // Run the operator
//! }
Expand Down
17 changes: 11 additions & 6 deletions src/commons/s3.rs
Expand Up @@ -222,8 +222,11 @@ impl Default for S3AccessStyle {

#[cfg(test)]
mod test {
use std::str;

use crate::commons::s3::{S3AccessStyle, S3ConnectionDef};
use crate::commons::s3::{S3BucketSpec, S3ConnectionSpec};
use crate::yaml;

#[test]
fn test_ser_inline() {
Expand All @@ -238,17 +241,19 @@ mod test {
})),
};

assert_eq!(
serde_yaml::to_string(&bucket).unwrap(),
"---
let mut buf = Vec::new();
yaml::serialize_to_explicit_document(&mut buf, &bucket).expect("serializable value");
let actual_yaml = str::from_utf8(&buf).expect("UTF-8 encoded document");

let expected_yaml = "---
bucketName: test-bucket-name
connection:
inline:
host: host
port: 8080
accessStyle: VirtualHosted
"
.to_owned()
)
";

assert_eq!(expected_yaml, actual_yaml)
}
}
15 changes: 11 additions & 4 deletions src/crd.rs
Expand Up @@ -5,6 +5,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::error::{Error, OperatorResult};
use crate::yaml;
use std::fs::File;
use std::io::Write;
use std::path::Path;
Expand Down Expand Up @@ -72,28 +73,34 @@ pub trait HasApplication {
/// (e.g. creation) of `CustomResourceDefinition`s in Kubernetes.
pub trait CustomResourceExt: kube::CustomResourceExt {
/// Generates a YAML CustomResourceDefinition and writes it to a `Write`.
///
/// The generated YAML string is an explicit document with leading dashes (`---`).
fn generate_yaml_schema<W>(mut writer: W) -> OperatorResult<()>
where
W: Write,
{
let schema = serde_yaml::to_string(&Self::crd())?;
writer.write_all(schema.as_bytes())?;
Ok(())
yaml::serialize_to_explicit_document(&mut writer, &Self::crd())
}

/// Generates a YAML CustomResourceDefinition and writes it to the specified file.
///
/// The written YAML string is an explicit document with leading dashes (`---`).
fn write_yaml_schema<P: AsRef<Path>>(path: P) -> OperatorResult<()> {
let writer = File::create(path)?;
Self::generate_yaml_schema(writer)
}

/// Generates a YAML CustomResourceDefinition and prints it to stdout.
///
/// The printed YAML string is an explicit document with leading dashes (`---`).
fn print_yaml_schema() -> OperatorResult<()> {
let writer = std::io::stdout();
Self::generate_yaml_schema(writer)
}

// Returns the YAML schema of this CustomResourceDefinition as a string.
/// Returns the YAML schema of this CustomResourceDefinition as a string.
///
/// The written YAML string is an explicit document with leading dashes (`---`).
fn yaml_schema() -> OperatorResult<String> {
let mut writer = Vec::new();
Self::generate_yaml_schema(&mut writer)?;
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Expand Up @@ -16,6 +16,7 @@ pub mod product_config_utils;
pub mod role_utils;
pub mod utils;
pub mod validation;
pub mod yaml;

pub use crate::crd::CustomResourceExt;

Expand Down
59 changes: 59 additions & 0 deletions src/yaml.rs
@@ -0,0 +1,59 @@
//! Utility functions for processing data in the YAML file format
use std::io::Write;

use serde::ser;

use crate::error::OperatorResult;

/// Serializes the given data structure as an explicit YAML document and writes it to a [`Write`].
///
/// Enums are serialized as a YAML map containing one entry in which the key identifies the variant
/// name.
///
/// # Example
///
/// ```
/// use serde::Serialize;
/// use stackable_operator::yaml;
///
/// #[derive(Serialize)]
/// #[serde(rename_all = "camelCase")]
/// enum Connection {
/// Inline(String),
/// Reference(String),
/// }
///
/// #[derive(Serialize)]
/// struct Spec {
/// connection: Connection,
/// }
///
/// let value = Spec {
/// connection: Connection::Inline("http://localhost".into()),
/// };
///
/// let mut buf = Vec::new();
/// yaml::serialize_to_explicit_document(&mut buf, &value).unwrap();
/// let actual_yaml = std::str::from_utf8(&buf).unwrap();
///
/// let expected_yaml = "---
/// connection:
/// inline: http://localhost
/// ";
///
/// assert_eq!(expected_yaml, actual_yaml);
/// ```
///
/// # Errors
///
/// Serialization can fail if `T`'s implementation of `Serialize` decides to return an error.
pub fn serialize_to_explicit_document<T, W>(mut writer: W, value: &T) -> OperatorResult<()>
where
T: ser::Serialize,
W: Write,
{
writer.write_all(b"---\n")?;
let mut serializer = serde_yaml::Serializer::new(writer);
serde_yaml::with::singleton_map_recursive::serialize(value, &mut serializer)?;
Ok(())
}