genpolicy: suppress YAML output when --{base64/raw}-out are used

this will suppress yaml output only if the input is passed via
stdin. If {base64/raw}-out is passed in alongside a yaml file, the
encoded annotation or the policy data respectively will be printed
to stdout as before.

Fixes #12438

Signed-off-by: Spyros Seimenis <sse@edgeless.systems>
This commit is contained in:
Spyros Seimenis
2026-02-02 15:29:28 +01:00
parent c84e37f6ac
commit 282bfc9f14
3 changed files with 151 additions and 8 deletions

View File

@@ -599,9 +599,6 @@ impl AgentPolicy {
let mut yaml_string = String::new();
for i in 0..self.resources.len() {
let annotation = self.resources[i].generate_initdata_anno(self);
if self.config.base64_out {
println!("{annotation}");
}
yaml_string += &self.resources[i].serialize(&annotation);
}
@@ -614,8 +611,7 @@ impl AgentPolicy {
.unwrap()
.write_all(yaml_string.as_bytes())
.unwrap();
} else {
// When input YAML came through stdin, print the output YAML to stdout.
} else if !self.config.base64_out && !self.config.raw_out {
std::io::stdout().write_all(yaml_string.as_bytes()).unwrap();
}
}
@@ -639,13 +635,18 @@ impl AgentPolicy {
let json_data = serde_json::to_string_pretty(&policy_data).unwrap();
let policy = format!("{}\npolicy_data := {json_data}", &self.rules);
let mut initdata = self.config.initdata.clone();
initdata.insert_data("policy.rego", policy.clone());
let encoded = kata_types::initdata::encode_initdata(&initdata);
if self.config.raw_out {
std::io::stdout().write_all(policy.as_bytes()).unwrap();
}
let mut initdata = self.config.initdata.clone();
initdata.insert_data("policy.rego", policy);
if self.config.base64_out {
std::io::stdout().write_all(encoded.as_bytes()).unwrap();
}
kata_types::initdata::encode_initdata(&initdata)
encoded
}
pub fn get_container_policy(

View File

@@ -5,6 +5,7 @@
use assert_cmd::prelude::*;
use std::fs::{self};
use std::io::Write;
use std::path;
use std::process::Command;
@@ -68,6 +69,131 @@ fn secret_in_separate_file() -> Result<(), Box<dyn std::error::Error>> {
Ok(())
}
#[test]
fn output_behavior() -> Result<(), Box<dyn std::error::Error>> {
struct TestCase {
name: &'static str,
flag: Option<&'static str>,
use_yaml_file: bool,
expect_yaml_in_stdout: bool,
expect_base64_in_stdout: bool,
expect_raw_in_stdout: bool,
}
let test_cases = &[
// --yaml-file alone: file modified, no stdout
TestCase {
name: "yaml_file_only",
flag: None,
use_yaml_file: true,
expect_yaml_in_stdout: false,
expect_base64_in_stdout: false,
expect_raw_in_stdout: false,
},
// --yaml-file --base64-out: file modified, base64 to stdout
TestCase {
name: "yaml_file_with_base64_out",
flag: Some("--base64-out"),
use_yaml_file: true,
expect_yaml_in_stdout: false,
expect_base64_in_stdout: true,
expect_raw_in_stdout: false,
},
// --yaml-file --raw-out: file modified, raw to stdout
TestCase {
name: "yaml_file_with_raw_out",
flag: Some("--raw-out"),
use_yaml_file: true,
expect_yaml_in_stdout: false,
expect_base64_in_stdout: false,
expect_raw_in_stdout: true,
},
// stdin alone: annotated YAML to stdout
TestCase {
name: "stdin_only",
flag: None,
use_yaml_file: false,
expect_yaml_in_stdout: true,
expect_base64_in_stdout: false,
expect_raw_in_stdout: false,
},
// stdin --base64-out: only base64 to stdout (suppress YAML)
TestCase {
name: "stdin_with_base64_out",
flag: Some("--base64-out"),
use_yaml_file: false,
expect_yaml_in_stdout: false,
expect_base64_in_stdout: true,
expect_raw_in_stdout: false,
},
// stdin --raw-out: only raw to stdout (suppress YAML)
TestCase {
name: "stdin_with_raw_out",
flag: Some("--raw-out"),
use_yaml_file: false,
expect_yaml_in_stdout: false,
expect_base64_in_stdout: false,
expect_raw_in_stdout: true,
},
];
for tc in test_cases.iter() {
let workdir = prepare_workdir(tc.name, &["simple_pod.yaml"]);
let pod_yaml_path = workdir.join("simple_pod.yaml");
let output = if tc.use_yaml_file {
let mut cmd = Command::cargo_bin("genpolicy")?;
cmd.arg("--yaml-file").arg(&pod_yaml_path);
if let Some(flag) = tc.flag {
cmd.arg(flag);
}
cmd.output()?
} else {
let pod_yaml_content = fs::read_to_string(&pod_yaml_path)?;
let mut cmd = Command::cargo_bin("genpolicy")?;
cmd.current_dir(&workdir);
if let Some(flag) = tc.flag {
cmd.arg(flag);
}
let mut child = cmd
.stdin(std::process::Stdio::piped())
.stdout(std::process::Stdio::piped())
.spawn()?;
child
.stdin
.take()
.unwrap()
.write_all(pod_yaml_content.as_bytes())?;
child.wait_with_output()?
};
assert!(output.status.success(), "{}: command failed", tc.name);
let stdout = String::from_utf8_lossy(&output.stdout);
let has_yaml = stdout.contains("apiVersion:");
let has_raw = stdout.contains("policy_data");
let has_base64 = !stdout.trim().is_empty() && !has_yaml && !has_raw;
assert_eq!(
has_yaml, tc.expect_yaml_in_stdout,
"{}: yaml in stdout",
tc.name
);
assert_eq!(
has_raw, tc.expect_raw_in_stdout,
"{}: raw policy in stdout",
tc.name
);
assert_eq!(
has_base64, tc.expect_base64_in_stdout,
"{}: base64 policy in stdout",
tc.name
);
}
Ok(())
}
fn prepare_workdir(test_case_dir: &str, files_to_copy: &[&str]) -> path::PathBuf {
// Prepare temp dir for running genpolicy.
let workdir = path::PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join(test_case_dir);

View File

@@ -0,0 +1,16 @@
---
apiVersion: v1
kind: Pod
metadata:
name: simple-pod
spec:
restartPolicy: Never
runtimeClassName: kata-cc
containers:
- name: busybox
image: "quay.io/prometheus/busybox:latest"
command:
- /bin/sh
args:
- "-c"
- echo hello