genpolicy: add cli integration tests

Add a new type of integration test to genpolicy. Now we can test flag handling
and how the CLI behaves with certain yaml inputs.

The first tests cover the case when a Pod references a Kubernetes secret of
config map in another file. Those need to be explicitly added via the
--config-files flag.

In the future we can easily add test suites that cover that all yaml fields
of all resources are understood by genpolicy.

Signed-off-by: Leonard Cohnen <leonard.cohnen@gmail.com>
This commit is contained in:
Leonard Cohnen 2025-03-06 05:16:54 +01:00 committed by Markus Rudy
parent 61ee330029
commit bad0cd0003
7 changed files with 460 additions and 2 deletions

View File

@ -41,6 +41,12 @@ dependencies = [
"libc",
]
[[package]]
name = "anstyle"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
[[package]]
name = "anyhow"
version = "1.0.69"
@ -53,6 +59,22 @@ version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
[[package]]
name = "assert_cmd"
version = "2.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bd389a4b2970a01282ee455294913c0a43724daedcd1a24c3eb0ec1c1320b66"
dependencies = [
"anstyle",
"bstr",
"doc-comment",
"libc",
"predicates",
"predicates-core",
"predicates-tree",
"wait-timeout",
]
[[package]]
name = "async-trait"
version = "0.1.68"
@ -169,6 +191,17 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bstr"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
dependencies = [
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
@ -451,6 +484,12 @@ dependencies = [
"syn 2.0.58",
]
[[package]]
name = "difflib"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8"
[[package]]
name = "digest"
version = "0.10.6"
@ -462,6 +501,12 @@ dependencies = [
"subtle",
]
[[package]]
name = "doc-comment"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10"
[[package]]
name = "docker_credential"
version = "1.3.1"
@ -699,6 +744,7 @@ name = "genpolicy"
version = "0.1.0"
dependencies = [
"anyhow",
"assert_cmd",
"async-trait",
"base64 0.21.7",
"clap",
@ -1580,6 +1626,33 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "predicates"
version = "3.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573"
dependencies = [
"anstyle",
"difflib",
"predicates-core",
]
[[package]]
name = "predicates-core"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa"
[[package]]
name = "predicates-tree"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c"
dependencies = [
"predicates-core",
"termtree",
]
[[package]]
name = "prettyplease"
version = "0.1.25"
@ -2351,6 +2424,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "termtree"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683"
[[package]]
name = "thiserror"
version = "1.0.40"
@ -2695,6 +2774,15 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wait-timeout"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
dependencies = [
"libc",
]
[[package]]
name = "want"
version = "0.3.0"

View File

@ -40,14 +40,16 @@ serde_yaml = "0.8"
anyhow = "1.0.32"
async-trait = "0.1.68"
docker_credential = "1.3.1"
flate2 = { version = "1.0.26", features = ["zlib-ng"], default-features = false }
flate2 = { version = "1.0.26", features = [
"zlib-ng",
], default-features = false }
libz-ng-sys = "1.1.15" # force newer version that compiles on ppc64le
oci-client = { version = "0.12.0" }
openssl = { version = "0.10.72", features = ["vendored"] }
serde_ignored = "0.1.7"
serde_json = "1.0.39"
serde-transcode = "1.1.1"
tokio = {version = "1.38.0", features = ["rt-multi-thread"]}
tokio = { version = "1.38.0", features = ["rt-multi-thread"] }
# OCI container specs.
oci-spec = { version = "0.6.8", features = ["runtime"] }
@ -76,3 +78,4 @@ tar = "0.4.41"
[dev-dependencies]
kata-agent-policy = { path = "../../agent/policy" }
slog = "2.5.2"
assert_cmd = "2.0.14"

View File

@ -0,0 +1,99 @@
// Copyright (c) 2025 Edgeless Systems GmbH
//
// SPDX-License-Identifier: Apache-2.0
//
use assert_cmd::prelude::*;
use std::fs::{self};
use std::path;
use std::process::Command;
#[test]
fn config_map_in_separate_file_config_map_flag() -> Result<(), Box<dyn std::error::Error>> {
// Prepare temp dir for running genpolicy.
let test_case_dir = "config_map_separate_file_config_map_flag";
let pod_yaml_name = "pod_with_config_map_ref.yaml";
let config_file = "config_map.yaml";
let workdir = prepare_workdir(test_case_dir, &[pod_yaml_name, config_file]);
let mut cmd = Command::cargo_bin("genpolicy")?;
cmd.arg("--yaml-file").arg(workdir.join(pod_yaml_name));
cmd.assert().failure();
let mut cmd = Command::cargo_bin("genpolicy")?;
cmd.arg("--yaml-file").arg(workdir.join(pod_yaml_name));
cmd.arg("--config-map-file").arg(workdir.join(config_file));
cmd.assert().success();
Ok(())
}
#[test]
fn config_map_in_separate_file_workdir_flag() -> Result<(), Box<dyn std::error::Error>> {
// Prepare temp dir for running genpolicy.
let test_case_dir = "config_map_separate_file_workdir_flag";
let pod_yaml_name = "pod_with_config_map_ref.yaml";
let config_file = "config_map.yaml";
let workdir = prepare_workdir(test_case_dir, &[pod_yaml_name, config_file]);
let mut cmd = Command::cargo_bin("genpolicy")?;
cmd.arg("--yaml-file").arg(workdir.join(pod_yaml_name));
cmd.assert().failure();
let mut cmd = Command::cargo_bin("genpolicy")?;
cmd.arg("--yaml-file").arg(workdir.join(pod_yaml_name));
cmd.arg("--config-file").arg(workdir.join(config_file));
cmd.assert().success();
Ok(())
}
#[test]
fn secret_in_separate_file() -> Result<(), Box<dyn std::error::Error>> {
// Prepare temp dir for running genpolicy.
let test_case_dir = "secret_separate_file";
let pod_yaml_name = "pod_with_secret_ref.yaml";
let config_file = "secret.yaml";
let workdir = prepare_workdir(test_case_dir, &[pod_yaml_name, config_file]);
let mut cmd = Command::cargo_bin("genpolicy")?;
cmd.arg("--yaml-file").arg(workdir.join(pod_yaml_name));
cmd.assert().failure();
let mut cmd = Command::cargo_bin("genpolicy")?;
cmd.arg("--yaml-file").arg(workdir.join(pod_yaml_name));
cmd.arg("--config-file").arg(workdir.join(config_file));
cmd.assert().success();
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);
fs::create_dir_all(&workdir)
.expect("should be able to create directories under CARGO_TARGET_TMPDIR");
let testdata_dir =
path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/generate/testdata");
// Make sure that workdir is empty.
for entry in fs::read_dir(&workdir).expect("should be able to read directories") {
let entry = entry.expect("should be able to read directory entries");
fs::remove_file(entry.path()).expect("should be able to remove files");
}
for file in files_to_copy {
fs::copy(testdata_dir.join(file), workdir.join(file))
.expect("copying files around should not fail");
}
let genpolicy_dir = path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
for base in ["rules.rego", "genpolicy-settings.json"] {
fs::copy(genpolicy_dir.join(base), workdir.join(base))
.expect("copying files around should not fail");
}
workdir
}

View File

@ -0,0 +1,75 @@
apiVersion: v1
data:
key: YmFyCg==
kind: ConfigMap
metadata:
name: configmap-sample
---
apiVersion: v1
data:
key: YmFyCg==
kind: ConfigMap
metadata:
name: configmap-sample2
---
# Add some other yaml document to make sure that we can parse out
# ConfigMaps and Secrets from yaml files with multiple documents.
apiVersion: v1
kind: Pod
metadata:
name: one-container-config-map
labels:
run: busybox
spec:
restartPolicy: Never
runtimeClassName: kata-cc
containers:
- name: busybox
image: "quay.io/prometheus/busybox:latest"
stdin: true
env:
- name: ENV_FROM_CONFIGMAP
valueFrom:
configMapKeyRef:
key: key
name: configmap-sample
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.serviceAccountName
- name: PROXY_CONFIG
value: "{}\n"
- name: ISTIO_META_POD_PORTS
value: "[\n]"
- name: ISTIO_META_APP_CONTAINERS
value: serviceaclient
- name: ISTIO_META_CLUSTER_ID
value: Kubernetes
- name: ISTIO_META_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
securityContext:
privileged: true
command:
- /bin/sh
args:
- "-c"
- while true; do echo $(POD_NAME); sleep 10; done

View File

@ -0,0 +1,60 @@
---
apiVersion: v1
kind: Pod
metadata:
name: one-container
labels:
run: busybox
spec:
restartPolicy: Never
runtimeClassName: kata-cc
containers:
- name: busybox
image: "quay.io/prometheus/busybox:latest"
stdin: true
env:
- name: ENV_FROM_CONFIGMAP
valueFrom:
configMapKeyRef:
key: key
name: configmap-sample
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.serviceAccountName
- name: PROXY_CONFIG
value: "{}\n"
- name: ISTIO_META_POD_PORTS
value: "[\n]"
- name: ISTIO_META_APP_CONTAINERS
value: serviceaclient
- name: ISTIO_META_CLUSTER_ID
value: Kubernetes
- name: ISTIO_META_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
securityContext:
privileged: true
command:
- /bin/sh
args:
- "-c"
- while true; do echo $(POD_NAME); sleep 10; done

View File

@ -0,0 +1,60 @@
---
apiVersion: v1
kind: Pod
metadata:
name: one-container
labels:
run: busybox
spec:
restartPolicy: Never
runtimeClassName: kata-cc
containers:
- name: busybox
image: "quay.io/prometheus/busybox:latest"
stdin: true
env:
- name: ENV_FROM_SECRET
valueFrom:
secretKeyRef:
key: key
name: secret-sample
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.serviceAccountName
- name: PROXY_CONFIG
value: "{}\n"
- name: ISTIO_META_POD_PORTS
value: "[\n]"
- name: ISTIO_META_APP_CONTAINERS
value: serviceaclient
- name: ISTIO_META_CLUSTER_ID
value: Kubernetes
- name: ISTIO_META_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
securityContext:
privileged: true
command:
- /bin/sh
args:
- "-c"
- while true; do echo $(POD_NAME); sleep 10; done

View File

@ -0,0 +1,73 @@
apiVersion: v1
data:
key: YmFyCg==
kind: Secret
metadata:
name: secret-sample
---
apiVersion: v1
data:
key: YmFyCg==
kind: ConfigMap
metadata:
name: configmap-sample3
---
apiVersion: v1
kind: Pod
metadata:
name: one-container-secret
labels:
run: busybox
spec:
restartPolicy: Never
runtimeClassName: kata-cc
containers:
- name: busybox
image: "quay.io/prometheus/busybox:latest"
stdin: true
env:
- name: ENV_FROM_CONFIGMAP
valueFrom:
configMapKeyRef:
key: key
name: configmap-sample
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.serviceAccountName
- name: PROXY_CONFIG
value: "{}\n"
- name: ISTIO_META_POD_PORTS
value: "[\n]"
- name: ISTIO_META_APP_CONTAINERS
value: serviceaclient
- name: ISTIO_META_CLUSTER_ID
value: Kubernetes
- name: ISTIO_META_NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
securityContext:
privileged: true
command:
- /bin/sh
args:
- "-c"
- while true; do echo $(POD_NAME); sleep 10; done