Skip to content

Replace docs URL placeholder with versioned docs link in CRD output #689

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 8 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ All notable changes to this project will be documented in this file.

## [Unreleased]

### Changed

- BREAKING: The `CustomResourceExt` functions now take the Operator version as an argument.
It replaces `DOCS_BASE_URL_PLACEHOLDER` in doc strings with a link to URL base, so
`DOCS_BASE_URL_PLACEHOLDER/druid/` turns into `https://docs.stackable.tech/home/nightly/druid/`
in the nightly operator ([#689]).

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

## [0.56.2] - 2023-11-23

### Added
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ product-config = { git = "https://github.com/stackabletech/product-config.git",
rand = "0.8.5"
regex = "1.9.3"
schemars = "0.8.12"
semver = "1.0"
serde = { version = "1.0.184", features = ["derive"] }
serde_json = "1.0.104"
serde_yaml = "0.9.25"
Expand Down
6 changes: 4 additions & 2 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
//! use stackable_operator::{CustomResourceExt, cli};
//! use stackable_operator::error::OperatorResult;
//!
//! const OPERATOR_VERSION: &str = "23.1.1";
//!
//! #[derive(Clone, CustomResource, Debug, JsonSchema, Serialize, Deserialize)]
//! #[kube(
//! group = "foo.stackable.tech",
Expand Down Expand Up @@ -56,8 +58,8 @@
//!
//! match opts.command {
//! cli::Command::Crd => {
//! FooCluster::print_yaml_schema()?;
//! BarCluster::print_yaml_schema()?;
//! FooCluster::print_yaml_schema(OPERATOR_VERSION)?;
//! BarCluster::print_yaml_schema(OPERATOR_VERSION)?;
//! },
//! cli::Command::Run { .. } => {
//! // Run the operator
Expand Down
2 changes: 1 addition & 1 deletion src/commons/affinity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
pub const TOPOLOGY_KEY_HOSTNAME: &str = "kubernetes.io/hostname";

/// These configuration settings control
/// [Pod placement](https://docs.stackable.tech/home/nightly/concepts/operations/pod_placement).
/// [Pod placement](DOCS_BASE_URL_PLACEHOLDER/concepts/operations/pod_placement).
#[derive(Clone, Debug, Default, Deserialize, Fragment, JsonSchema, PartialEq, Serialize)]
#[fragment(path_overrides(fragment = "crate::config::fragment"))]
#[fragment_attrs(
Expand Down
2 changes: 1 addition & 1 deletion src/commons/cluster_operation.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// [Cluster operations](https://docs.stackable.tech/home/nightly/concepts/operations/cluster_operations)
/// [Cluster operations](DOCS_BASE_URL_PLACEHOLDER/concepts/operations/cluster_operations)
/// properties, allow stopping the product instance as well as pausing reconciliation.
#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
Expand Down
6 changes: 3 additions & 3 deletions src/commons/opa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@ impl OpaApiVersion {
}
}

/// Configure the OPA stacklet [discovery ConfigMap](https://docs.stackable.tech/home/nightly/concepts/service_discovery)
/// Configure the OPA stacklet [discovery ConfigMap](DOCS_BASE_URL_PLACEHOLDER/concepts/service_discovery)
/// and the name of the Rego package containing your authorization rules.
/// Consult the [OPA authorization documentation](https://docs.stackable.tech/home/nightly/concepts/opa)
/// Consult the [OPA authorization documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/opa)
/// to learn how to deploy Rego authorization rules with OPA.
#[derive(Clone, Debug, Default, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct OpaConfig {
/// The [discovery ConfigMap](https://docs.stackable.tech/home/nightly/concepts/service_discovery)
/// The [discovery ConfigMap](DOCS_BASE_URL_PLACEHOLDER/concepts/service_discovery)
/// for the OPA stacklet that should be used for authorization requests.
pub config_map_name: String,
/// The name of the Rego package containing the Rego rules for the product.
Expand Down
2 changes: 1 addition & 1 deletion src/commons/pdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
/// 2. The allowed number of Pods to be unavailable (`maxUnavailable`)
///
/// Learn more in the
/// [allowed Pod disruptions documentation](https://docs.stackable.tech/home/nightly/concepts/operations/pod_disruptions).
/// [allowed Pod disruptions documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/operations/pod_disruptions).
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PdbConfig {
Expand Down
2 changes: 1 addition & 1 deletion src/commons/product_image_selection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub const STACKABLE_DOCKER_REPO: &str = "docker.stackable.tech/stackable";
/// You can also configure a custom image registry to pull from, as well as completely custom
/// images.
///
/// Consult the [Product image selection documentation](https://docs.stackable.tech/home/nightly/concepts/product_image_selection)
/// Consult the [Product image selection documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/product_image_selection)
/// for details.
#[derive(Clone, Debug, Deserialize, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
Expand Down
2 changes: 1 addition & 1 deletion src/commons/s3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl InlinedS3BucketSpec {

/// An S3 bucket definition, it can either be a reference to an explicit S3Bucket object,
/// or it can be an inline defintion of a bucket. Read the
/// [S3 resources concept documentation](https://docs.stackable.tech/home/nightly/concepts/s3)
/// [S3 resources concept documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/s3)
/// to learn more.
#[derive(Clone, Debug, Deserialize, Eq, JsonSchema, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
Expand Down
54 changes: 46 additions & 8 deletions src/crd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::marker::PhantomData;

use derivative::Derivative;
use schemars::JsonSchema;
use semver::Version;
use serde::{Deserialize, Serialize};

use crate::error::{Error, OperatorResult};
Expand All @@ -10,6 +11,9 @@ use std::fs::File;
use std::io::Write;
use std::path::Path;

const DOCS_HOME_URL_PLACEHOLDER: &str = "DOCS_BASE_URL_PLACEHOLDER";
const DOCS_HOME_BASE_URL: &str = "https://docs.stackable.tech/home";

/// A reference to a product cluster (for example, a `ZookeeperCluster`)
///
/// `namespace`'s defaulting only applies when retrieved via [`ClusterRef::namespace_relative_from`]
Expand Down Expand Up @@ -69,41 +73,75 @@ pub trait HasApplication {
fn get_application_name() -> &'static str;
}

/// Takes an operator version and returns a docs version
fn docs_version(operator_version: &str) -> OperatorResult<String> {
if operator_version == "0.0.0-dev" {
Ok("nightly".to_owned())
} else {
let v = Version::parse(operator_version).map_err(|err| Error::InvalidSemverVersion {
source: err,
version: operator_version.to_owned(),
})?;
Ok(format!("{}.{}", v.major, v.minor))
}
}

/// Given an operator version like 0.0.0-dev or 23.1.1, generate a docs home
/// component base URL like `https://docs.stackable.tech/home/nightly/` or
/// `https://docs.stackable.tech/home/23.1/`.
fn docs_home_versioned_base_url(operator_version: &str) -> OperatorResult<String> {
Ok(format!(
"{}/{}",
DOCS_HOME_BASE_URL,
docs_version(operator_version)?
))
}

/// This trait can be implemented to allow automatic handling
/// (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<()>
fn generate_yaml_schema<W>(mut writer: W, operator_version: &str) -> OperatorResult<()>
where
W: Write,
{
yaml::serialize_to_explicit_document(&mut writer, &Self::crd())
let mut buffer = Vec::new();
yaml::serialize_to_explicit_document(&mut buffer, &Self::crd())?;

let yaml_schema = String::from_utf8(buffer)
.map_err(Error::CrdFromUtf8Error)?
.replace(
DOCS_HOME_URL_PLACEHOLDER,
&docs_home_versioned_base_url(operator_version)?,
);

Ok(writer.write_all(yaml_schema.as_bytes())?)
}

/// 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<()> {
fn write_yaml_schema<P: AsRef<Path>>(path: P, operator_version: &str) -> OperatorResult<()> {
let writer = File::create(path)?;
Self::generate_yaml_schema(writer)
Self::generate_yaml_schema(writer, operator_version)
}

/// 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<()> {
fn print_yaml_schema(operator_version: &str) -> OperatorResult<()> {
let writer = std::io::stdout();
Self::generate_yaml_schema(writer)
Self::generate_yaml_schema(writer, operator_version)
}

/// 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> {
fn yaml_schema(operator_version: &str) -> OperatorResult<String> {
let mut writer = Vec::new();
Self::generate_yaml_schema(&mut writer)?;
Self::generate_yaml_schema(&mut writer, operator_version)?;
String::from_utf8(writer).map_err(Error::CrdFromUtf8Error)
}
}
Expand Down
6 changes: 6 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ pub enum Error {
container_name: String,
violation: String,
},

#[error("Cannot parse version [{version}] as a semantic version.")]
InvalidSemverVersion {
source: semver::Error,
version: String,
},
}

pub type OperatorResult<T> = std::result::Result<T, Error>;
2 changes: 1 addition & 1 deletion src/product_logging/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ use serde::{Deserialize, Serialize};
rename_all = "camelCase",
),
// We don't want Rust code in public API descriptions
schemars(description = "Logging configuration, learn more in the [logging concept documentation](https://docs.stackable.tech/home/nightly/concepts/logging).")
schemars(description = "Logging configuration, learn more in the [logging concept documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/logging).")
)]
pub struct Logging<T>
where
Expand Down
16 changes: 4 additions & 12 deletions src/role_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,24 +114,16 @@ pub struct CommonConfiguration<T> {
#[schemars(default = "config_schema_default")]
pub config: T,
/// The `configOverrides` can be used to configure properties in product config files
/// that are not exposed in the CRD. For example, for a HdfsCluster:
///
/// ```yaml
/// configOverrides: # on role level
/// core-site.xml: # the name of the configuration file
/// fs.trash.interval: "5" # the name of the property and the value to set
/// ```
///
/// Read the
/// [config overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#config-overrides)
/// that are not exposed in the CRD. Read the
/// [config overrides documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/overrides#config-overrides)
/// and consult the operator specific usage guide documentation for details on the
/// available config files and settings for the specific product.
#[serde(default)]
pub config_overrides: HashMap<String, HashMap<String, String>>,
/// `envOverrides` configure environment variables to be set in the Pods.
/// It is a map from strings to strings - environment variables and the value to set.
/// Read the
/// [environment variable overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#env-overrides)
/// [environment variable overrides documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/overrides#env-overrides)
/// for more information and consult the operator specific usage guide to find out about
/// the product specific environment variables that are available.
#[serde(default)]
Expand All @@ -144,7 +136,7 @@ pub struct CommonConfiguration<T> {
/// [PodTemplateSpec](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#podtemplatespec-v1-core)
/// to override any property that can be set on a Kubernetes Pod.
/// Read the
/// [Pod overrides documentation](https://docs.stackable.tech/home/nightly/concepts/overrides#pod-overrides)
/// [Pod overrides documentation](DOCS_BASE_URL_PLACEHOLDER/concepts/overrides#pod-overrides)
/// for more information.
#[serde(default)]
#[schemars(schema_with = "pod_overrides_schema")]
Expand Down