Skip to content

Commit

Permalink
Add relaxed mode for partially invalid CRDs
Browse files Browse the repository at this point in the history
Fixes #108

Argo workflows have 2 CRDs, minimal and full.
The full CRD is supposed to be used in the editor and can't be
installed in kubernetes, but provide full validation.
The minimal version has no validation and should be installed.

In `--relaxed` mode, entries like `inline: {}` are mapped to
object like hashmaps.

Signed-off-by: Daniel Poelzleithner <[email protected]>
  • Loading branch information
poelzi committed Mar 21, 2024
1 parent 8e0d7ee commit e26da69
Show file tree
Hide file tree
Showing 4 changed files with 19,181 additions and 2 deletions.
5 changes: 5 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ test-argo:
kubectl apply -f tests/app.yaml
cargo test --test runner -- --nocapture

test-argo-wf:
# argo workflows are hard to test since the full crd are not supposed to be installed in k8s
! cargo run --bin kopium -- --filename tests/argoproj.io_clusterworkflowtemplates.yaml > /dev/null
cargo run --bin kopium -- --relaxed --filename tests/argoproj.io_clusterworkflowtemplates.yaml > /dev/null

test-certmanager:
kubectl apply --force-conflicts --server-side -f https://github.com/jetstack/cert-manager/releases/download/v1.7.1/cert-manager.crds.yaml
cargo run --bin kopium -- -d certificates.cert-manager.io > tests/gen.rs
Expand Down
18 changes: 16 additions & 2 deletions src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const IGNORED_KEYS: [&str; 3] = ["metadata", "apiVersion", "kind"];
#[derive(Default)]
pub struct Config {
pub no_condition: bool,
pub relaxed: bool,
}

/// Scan a schema for structs and members, and recurse to find all structs
Expand Down Expand Up @@ -270,7 +271,7 @@ fn extract_container(
"integer" => extract_integer_type(value)?,
"array" => {
// recurse through repeated arrays until we find a concrete type (keep track of how deep we went)
let (mut array_type, recurse_level) = array_recurse_for_type(value, stack, key, 1)?;
let (mut array_type, recurse_level) = array_recurse_for_type(value, stack, key, 1, cfg)?;
trace!("got array {} for {} in level {}", array_type, key, recurse_level);
if !cfg.no_condition && key == "conditions" && is_conditions(value) {
array_type = "Vec<Condition>".into();
Expand All @@ -284,6 +285,9 @@ fn extract_container(
"IntOrString".into()
} else if value.x_kubernetes_preserve_unknown_fields == Some(true) {
"HashMap<String, serde_json::Value>".into()
} else if cfg.relaxed {
debug!("found empty object at {} key: {}", stack, key);
"HashMap<String, serde_json::Value>".into()
} else {
bail!("unknown empty dict type for {}", key)
}
Expand Down Expand Up @@ -405,6 +409,7 @@ fn array_recurse_for_type(
stack: &str,
key: &str,
level: u8,
cfg: &Config,
) -> Result<(String, u8)> {
if let Some(items) = &value.items {
match items {
Expand Down Expand Up @@ -435,7 +440,16 @@ fn array_recurse_for_type(
"date" => Ok((format!("Vec<{}>", extract_date_type(value)?), level)),
"number" => Ok((format!("Vec<{}>", extract_number_type(value)?), level)),
"integer" => Ok((format!("Vec<{}>", extract_integer_type(value)?), level)),
"array" => Ok(array_recurse_for_type(s, stack, key, level + 1)?),
"array" => {
if s.items.is_some() {
Ok(array_recurse_for_type(s, stack, key, level + 1, cfg)?)
} else if cfg.relaxed {
warn!("Empty inner array in: {} key: {}", stack, key);
Ok(("BTreeMap<String, serde_json::Value>".into(), level))
} else {
bail!("Empty inner array in: {} key: {}", stack, key);
}
},
unknown => {
bail!("unsupported recursive array type \"{unknown}\" for {key}")
}
Expand Down
8 changes: 8 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@ struct Kopium {
#[arg(long, short = 'e')]
elide: Vec<String>,

/// Relaxed interpretation
///
/// This allows certain invalid openapi specs to be interpreted as arbriray objects as used by argo workflows for example.
/// the output first.
#[arg(long)]
relaxed: bool,

/// Enable generation of custom Condition APIs.
///
/// If false, it detects if a particular path is an array of Condition objects and uses a standard
Expand Down Expand Up @@ -192,6 +199,7 @@ impl Kopium {
log::debug!("schema: {}", serde_json::to_string_pretty(&schema)?);
let cfg = Config {
no_condition: self.no_condition,
relaxed: self.relaxed,
};
let structs = analyze(schema, kind, cfg)?
.rename()
Expand Down
Loading

0 comments on commit e26da69

Please sign in to comment.