diff --git a/Cargo.lock b/Cargo.lock index 1ead7822a..01de45c7a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1815,7 +1815,10 @@ dependencies = [ name = "integration-tests" version = "0.1.0" dependencies = [ + "libc", + "tempfile", "tokio", + "toml", "twoliter", ] diff --git a/Cargo.toml b/Cargo.toml index 1c863c0f8..05c303050 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,6 +89,7 @@ home = "0.5" indicatif = "0.17" inotify = "0.10.2" lazy_static = "1" +libc = "0.2" log = "0.4" maplit = "1" nix = "0.28" @@ -99,6 +100,7 @@ pentacle = "1.1" rand = { version = "0.8", default-features = false } regex = "1" reqwest = { version = "0.11", default-features = false } +seccompiler = "0.4" semver = "1" serde = "1" serde_json = "1" diff --git a/tests/integration-tests/Cargo.toml b/tests/integration-tests/Cargo.toml index 7597899f4..4343c3da6 100644 --- a/tests/integration-tests/Cargo.toml +++ b/tests/integration-tests/Cargo.toml @@ -5,5 +5,8 @@ edition = "2021" license = "MIT OR Apache-2.0" [dev-dependencies] +libc.workspace = true +tempfile.workspace = true tokio = { workspace = true, features = ["fs", "process", "rt-multi-thread"] } +toml.workspace = true twoliter = { workspace = true } diff --git a/tests/integration-tests/src/lib.rs b/tests/integration-tests/src/lib.rs index 379a7b0f2..7c4dd372c 100644 --- a/tests/integration-tests/src/lib.rs +++ b/tests/integration-tests/src/lib.rs @@ -2,7 +2,8 @@ use std::ffi::OsStr; use std::path::PathBuf; -use tokio::process::Command; +use std::process::Command; +use tempfile::TempDir; mod twoliter_update; @@ -14,7 +15,7 @@ pub fn test_projects_dir() -> PathBuf { p.join("projects") } -pub async fn run_command(cmd: S, args: I, env: E) -> std::process::Output +pub fn run_command(cmd: S, args: I, env: E) -> std::process::Output where I: IntoIterator, E: IntoIterator, @@ -35,10 +36,88 @@ where .args(args.into_iter()) .envs(env.into_iter()) .output() - .await .expect("failed to execute process"); println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); output } + +struct KitRegistry { + temp_dir: TempDir, + container_id: String, +} + +impl KitRegistry { + fn new() -> Self { + let temp_dir = TempDir::new().expect("failed to create path for oci registry spinup"); + + let cert_dir = temp_dir.path().join("certs"); + let cert_file = cert_dir.join("registry.crt"); + std::fs::create_dir_all(&cert_dir).expect("failed to create nginx dir"); + let output = run_command( + "openssl", + [ + "req", + "-x509", + "-nodes", + "-days", + "365", + "-newkey", + "rsa:2048", + "-keyout", + cert_dir.join("registry.key").to_str().unwrap(), + "-out", + cert_file.to_str().unwrap(), + "-batch", + "-addext", + "subjectAltName=DNS:localhost", + ], + [], + ); + assert!( + output.status.success(), + "generate openssl self-signed certificates" + ); + + let output = run_command( + "docker", + [ + "run", + "-d", + "--rm", + "--volume", + "./certs:/auth/certs", + "-e REGISTRY_HTTP_RELATIVEURLS=true", + "-e REGISTRY_HTTP_ADDR=0.0.0.0:5000", + "-e REGISTRY_HTTP_TLS_CERTIFICATE=/auth/certs/registry.crt", + "-e REGISTRY_HTTP_TLS_KEY=/auth/certs/registry.key", + "-p", + "5000:5000", + "public.ecr.aws/docker/library/registry:2.8.3", + ], + [], + ); + assert!(output.status.success(), "failed to start oci registry"); + let container_id = String::from_utf8(output.stdout).unwrap().trim().to_string(); + + Self { + temp_dir, + container_id, + } + } + + fn cert_file(&self) -> PathBuf { + self.temp_dir + .path() + .join("certs/registry.crt") + .to_path_buf() + } +} + +impl Drop for KitRegistry { + fn drop(&mut self) { + let output = run_command("docker", ["kill", &self.container_id], []); + assert!(output.status.success(), "failed to stop oci registry"); + } +} diff --git a/tests/integration-tests/src/twoliter_update.rs b/tests/integration-tests/src/twoliter_update.rs index 4672f291a..4c7f203c0 100644 --- a/tests/integration-tests/src/twoliter_update.rs +++ b/tests/integration-tests/src/twoliter_update.rs @@ -1,31 +1,32 @@ -use super::{run_command, test_projects_dir, TWOLITER_PATH}; - -const EXPECTED_LOCKFILE: &str = r#"schema-version = 1 - -[sdk] -name = "bottlerocket-sdk" -version = "0.42.0" -vendor = "bottlerocket" -source = "public.ecr.aws/bottlerocket/bottlerocket-sdk:v0.42.0" -digest = "myHHKE41h9qfeyR6V6HB0BfiLPwj3QEFLUFy4TXcR10=" - -[[kit]] -name = "bottlerocket-core-kit" -version = "2.0.0" -vendor = "custom-vendor" -source = "public.ecr.aws/bottlerocket/bottlerocket-core-kit:v2.0.0" -digest = "vlTsAAbSCzXFZofVmw8pLLkRjnG/y8mtb2QsQBSz1zk=" +use super::{run_command, test_projects_dir, KitRegistry, TWOLITER_PATH}; + +const INFRA_TOML: &str = r#" +[vendor.bottlerocket] +registry = "localhost:5000" +"#; + +const TWOLITER_OVERRIDE: &str = r#" +[custom-vendor.core-kit] +registry = "localhost:5000" +name = "core-kit-overridden" "#; -#[tokio::test] +#[test] #[ignore] -/// Generates a Twoliter.lock file for the `external-kit` project using docker -async fn test_twoliter_update_docker() { +/// Generates a Twoliter.lock file for the `external-kit` project using crane +fn test_twoliter_build_and_update() { let external_kit = test_projects_dir().join("external-kit"); - let lockfile = external_kit.join("Twoliter.lock"); - tokio::fs::remove_file(&lockfile).await.ok(); + std::fs::remove_file(&lockfile).ok(); + let override_file = external_kit.join("Twoliter.override"); + std::fs::remove_file(&override_file).ok(); + + // Build & push a local kit to the registry + let registry = KitRegistry::new(); + LocalKit::build(®istry); + // Point twoliter to the local registry as an override + std::fs::write(&override_file, TWOLITER_OVERRIDE).unwrap(); let output = run_command( TWOLITER_PATH, [ @@ -33,42 +34,101 @@ async fn test_twoliter_update_docker() { "--project-path", external_kit.join("Twoliter.toml").to_str().unwrap(), ], - [("TWOLITER_KIT_IMAGE_TOOL", "docker")], - ) - .await; + [ + ("TWOLITER_KIT_IMAGE_TOOL", "crane"), + ("SSL_CERT_FILE", registry.cert_file().to_str().unwrap()), + ], + ); assert!(output.status.success()); - let lock_contents = tokio::fs::read_to_string(&lockfile).await.unwrap(); - assert_eq!(lock_contents, EXPECTED_LOCKFILE); + // Assert that we successfully create a lock + let lock_contents = std::fs::read_to_string(&lockfile).unwrap(); + let parsed: toml::Value = toml::from_str(&lock_contents).unwrap(); + let kits = parsed + .as_table() + .unwrap() + .get("kit") + .unwrap() + .as_array() + .unwrap(); + + assert_eq!(kits.len(), 1); + let core_kit = kits[0].as_table().unwrap(); + assert_eq!(core_kit.get("name").unwrap().as_str().unwrap(), "core-kit"); + assert_eq!(core_kit.get("version").unwrap().as_str().unwrap(), "1.0.0"); + assert_eq!( + core_kit.get("vendor").unwrap().as_str().unwrap(), + "custom-vendor" + ); + assert_eq!( + core_kit.get("source").unwrap().as_str().unwrap(), + "definitely-wont-resolve/core-kit:v1.0.0" + ); - tokio::fs::remove_file(&lockfile).await.ok(); + std::fs::remove_file(&lockfile).ok(); + std::fs::remove_file(&override_file).ok(); } -#[tokio::test] -#[ignore] -/// Generates a Twoliter.lock file for the `external-kit` project using crane -async fn test_twoliter_update_crane() { - let external_kit = test_projects_dir().join("external-kit"); +struct LocalKit; - let lockfile = external_kit.join("Twoliter.lock"); - tokio::fs::remove_file(&lockfile).await.ok(); +impl LocalKit { + fn build(registry: &KitRegistry) { + let local_kit = test_projects_dir().join("local-kit"); - let output = run_command( - TWOLITER_PATH, - [ - "update", - "--project-path", - external_kit.join("Twoliter.toml").to_str().unwrap(), - ], - [("TWOLITER_KIT_IMAGE_TOOL", "crane")], - ) - .await; + run_command( + TWOLITER_PATH, + [ + "update", + "--project-path", + local_kit.join("Twoliter.toml").to_str().unwrap(), + ], + [], + ); - assert!(output.status.success()); + run_command( + TWOLITER_PATH, + [ + "fetch", + "--project-path", + local_kit.join("Twoliter.toml").to_str().unwrap(), + ], + [], + ); - let lock_contents = tokio::fs::read_to_string(&lockfile).await.unwrap(); - assert_eq!(lock_contents, EXPECTED_LOCKFILE); + run_command( + TWOLITER_PATH, + [ + "build", + "kit", + "core-kit", + "--project-path", + local_kit.join("Twoliter.toml").to_str().unwrap(), + ], + [], + ); + + std::fs::write(local_kit.join("Infra.toml"), INFRA_TOML).unwrap(); + run_command( + TWOLITER_PATH, + [ + "publish", + "kit", + "--project-path", + local_kit.join("Twoliter.toml").to_str().unwrap(), + "core-kit", + "bottlerocket", + "core-kit-overridden", + ], + [("SSL_CERT_FILE", registry.cert_file().to_str().unwrap())], + ); + } +} - tokio::fs::remove_file(&lockfile).await.ok(); +impl Drop for LocalKit { + fn drop(&mut self) { + let local_kit = test_projects_dir().join("local-kit"); + std::fs::remove_file(local_kit.join("Twoliter.lock")).ok(); + std::fs::remove_file(local_kit.join("Infra.toml")).ok(); + } } diff --git a/tests/projects/external-kit/.gitignore b/tests/projects/external-kit/.gitignore index 41c75f0c2..fa6f85fe9 100644 --- a/tests/projects/external-kit/.gitignore +++ b/tests/projects/external-kit/.gitignore @@ -8,3 +8,5 @@ Test.toml testsys.kubeconfig Infra.toml +Twoliter.lock +Twoliter.override diff --git a/tests/projects/external-kit/Twoliter.toml b/tests/projects/external-kit/Twoliter.toml index ea47adb8d..fdaf39783 100644 --- a/tests/projects/external-kit/Twoliter.toml +++ b/tests/projects/external-kit/Twoliter.toml @@ -5,9 +5,9 @@ release-version = "1.0.0" registry = "public.ecr.aws/bottlerocket" [vendor.custom-vendor] -registry = "public.ecr.aws/bottlerocket" +registry = "definitely-wont-resolve" [[kit]] -name = "bottlerocket-core-kit" -version = "2.0.0" +name = "core-kit" +version = "1.0.0" vendor = "custom-vendor" diff --git a/tests/projects/local-kit/.gitignore b/tests/projects/local-kit/.gitignore index 41c75f0c2..012a0c13d 100644 --- a/tests/projects/local-kit/.gitignore +++ b/tests/projects/local-kit/.gitignore @@ -8,3 +8,4 @@ Test.toml testsys.kubeconfig Infra.toml +Twoliter.lock diff --git a/tools/unplug/Cargo.toml b/tools/unplug/Cargo.toml index b07cd7d7f..dc0313403 100644 --- a/tools/unplug/Cargo.toml +++ b/tools/unplug/Cargo.toml @@ -10,5 +10,5 @@ publish = false anyhow.workspace = true [target.'cfg(target_os = "linux")'.dependencies] -libc = "0.2.148" -seccompiler = "0.4.0" +libc.workspace = true +seccompiler.workspace = true