#![allow(clippy::module_name_repetitions)]
pub use lava_core::{Architecture, ResourceRef, Value};
pub use lava_eval::{
eval_architecture, eval_architecture_with_schema, interfaces_in_source, parse, Atom,
EvalError, InputBindings, InterfaceParseError, ParseError, Sx,
};
pub use lava_schema::{Field, Interface, SchemaError};
pub use lava_types::Type;
pub const BUNDLED_ARCHITECTURES: &[(&str, usize)] = &[
("aws-vpc-network", 10), ("cloudflare-dns-records", 5), ("akeyless-secrets", 6), ("cloudflare-r2-bucket", 4), ("public-dns", 6), ("akeyless-platform", 8), ("cloudflare-tunnel", 3), ("cloudflare-zone", 2), ("aws-eks-cluster", 4), ("split-horizon-dns", 4), ("dns-record-set", 2), ("cilium-irsa", 3), ("cluster-autoscaler-iam", 2), ("backup-recovery", 3), ("ami-production-iam", 3), ("cloudflare-tunnel-ingress", 3), ("cloudflare-headless-blog", 3), ("akeyless-aws-integration", 3), ("convergence-dashboard", 4), ("drill-network", 3), ("cloudflare-zero-trust-access", 2), ("akeyless-dev-cluster", 2), ("ami-test-iam", 2), ("drill-iam-role", 2), ("continuous-monitoring", 3), ("akeyless-target-attested-infra", 2), ("cloudflare-domain", 2), ("cloudflare-dns-security", 3), ("akeyless-dev-packer", 2), ("akeyless-dev-workspace", 2), ("azure-aks-cluster", 1), ("burst-forge-dashboard", 2), ("dns-record-set-typed", 4), ("backup-recovery-multi-region", 3), ("cilium-irsa-variants", 3), ];
#[must_use]
pub fn interface_for(name: &str) -> Option<Interface> {
let path = bundled_source_path(name);
let src = std::fs::read_to_string(&path).ok()?;
let ifaces = interfaces_in_source(&src).ok()?;
ifaces.into_iter().find(|i| i.name == name)
}
fn bundled_source_path(name: &str) -> std::path::PathBuf {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join(ARCHITECTURE_DIR)
.join(format!("{name}.tlisp"))
}
#[must_use]
pub fn bundled_source(name: &str) -> Option<String> {
std::fs::read_to_string(bundled_source_path(name)).ok()
}
pub const ARCHITECTURE_DIR: &str = "architectures";
pub fn load_bundled(
name: &str,
bindings: &InputBindings,
) -> Result<lava_core::Architecture, EvalError> {
let path = bundled_source_path(name);
let src = std::fs::read_to_string(&path)
.map_err(|e| EvalError::NotArchForm(format!("io: {} — {e}", path.display())))?;
eval_architecture(&src, bindings)
}
#[cfg(test)]
mod integration_tests {
use super::*;
#[test]
fn aws_vpc_network_tlisp_renders_byte_equivalent_to_pangea_spec() {
let bindings = InputBindings::new();
let arch = load_bundled("aws-vpc-network", &bindings).unwrap();
let json = arch.render_terraform_json().unwrap();
assert_eq!(json["resource"]["aws_vpc"]["main-vpc"]["cidr_block"], "10.0.0.0/16");
assert_eq!(json["resource"]["aws_vpc"]["main-vpc"]["enable_dns_support"], true);
assert_eq!(json["resource"]["aws_vpc"]["main-vpc"]["enable_dns_hostnames"], true);
assert_eq!(json["resource"]["aws_vpc"]["main-vpc"]["tags"]["Name"], "main-vpc");
assert_eq!(
json["resource"]["aws_vpc"]["main-vpc"]["tags"]["Environment"],
"production"
);
assert_eq!(
json["resource"]["aws_internet_gateway"]["main-igw"]["vpc_id"],
"${aws_vpc.main-vpc.id}"
);
for i in 0..3 {
let name = format!("main-public-{i}");
assert_eq!(
json["resource"]["aws_subnet"][&name]["vpc_id"],
"${aws_vpc.main-vpc.id}",
"public subnet {i} vpc_id"
);
assert_eq!(
json["resource"]["aws_subnet"][&name]["cidr_block"],
format!("10.0.{i}.0/24"),
"public subnet {i} cidr"
);
assert_eq!(
json["resource"]["aws_subnet"][&name]["map_public_ip_on_launch"],
true,
"public subnet {i} map_public_ip"
);
}
for i in 0..3 {
let name = format!("main-private-{i}");
assert_eq!(
json["resource"]["aws_subnet"][&name]["vpc_id"],
"${aws_vpc.main-vpc.id}",
"private subnet {i} vpc_id"
);
assert_eq!(
json["resource"]["aws_subnet"][&name]["cidr_block"],
format!("10.0.{}.0/24", i + 10),
"private subnet {i} cidr"
);
}
assert_eq!(json["resource"]["aws_eip"]["main-nat-eip"]["domain"], "vpc");
assert_eq!(
json["resource"]["aws_nat_gateway"]["main-nat"]["subnet_id"],
"${aws_subnet.main-public-0.id}"
);
assert_eq!(
json["resource"]["aws_nat_gateway"]["main-nat"]["allocation_id"],
"${aws_eip.main-nat-eip.allocation_id}"
);
assert_eq!(
json["resource"]["aws_security_group"]["main-default-sg"]["vpc_id"],
"${aws_vpc.main-vpc.id}"
);
}
#[test]
fn cloudflare_dns_records_tlisp_renders_five_typed_records() {
let bindings = InputBindings::new();
let arch = load_bundled("cloudflare-dns-records", &bindings).unwrap();
let json = arch.render_terraform_json().unwrap();
let records = &json["resource"]["cloudflare_dns_record"];
assert_eq!(records["root-cname"]["name"], "@");
assert_eq!(records["root-cname"]["type"], "CNAME");
assert_eq!(records["root-cname"]["proxied"], true);
assert_eq!(records["www-cname"]["name"], "www");
assert_eq!(records["api-cname"]["proxied"], true);
assert_eq!(records["wildcard-staging-cname"]["name"], "*.staging");
assert_eq!(records["wildcard-staging-cname"]["proxied"], false);
assert_eq!(records["acme-challenge-txt"]["type"], "TXT");
assert_eq!(records["acme-challenge-txt"]["name"], "_acme-challenge");
}
#[test]
fn akeyless_secrets_tlisp_renders_full_resource_set() {
let bindings = InputBindings::new();
let arch = load_bundled("akeyless-secrets", &bindings).unwrap();
let json = arch.render_terraform_json().unwrap();
assert_eq!(
json["resource"]["akeyless_auth_method_api_key"]["prod-ci-auth"]["name"],
"/prod/ci-auth"
);
assert_eq!(
json["resource"]["akeyless_auth_method_k8s"]["prod-k8s-auth"]["name"],
"/prod/k8s-auth"
);
assert_eq!(
json["resource"]["akeyless_static_secret"]["prod-db-password"]["name"],
"/prod/database/password"
);
assert_eq!(
json["resource"]["akeyless_static_secret"]["prod-api-key"]["name"],
"/prod/service/api-key"
);
assert_eq!(
json["resource"]["akeyless_target_db"]["prod-db-target"]["db_type"],
"postgres"
);
assert_eq!(
json["resource"]["akeyless_role"]["prod-app-role"]["name"],
"/prod/roles/app"
);
}
#[test]
fn cloudflare_interface_rejects_missing_required_zone_id() {
let iface = interface_for("cloudflare-dns-records").unwrap();
let src = std::fs::read_to_string(
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join(ARCHITECTURE_DIR)
.join("cloudflare-dns-records.tlisp"),
)
.unwrap();
let bindings = InputBindings::new();
let err = eval_architecture_with_schema(&src, &bindings, &iface).unwrap_err();
match err {
EvalError::Schema {
interface, errors, ..
} => {
assert_eq!(interface, "cloudflare-dns-records");
assert!(errors.iter().any(|e| matches!(e, SchemaError::MissingRequired { name, .. } if name == "zone-id")));
}
other => panic!("expected EvalError::Schema, got {other:?}"),
}
}
#[test]
fn akeyless_interface_gates_valid_eval() {
let iface = interface_for("akeyless-secrets").unwrap();
let src = std::fs::read_to_string(
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join(ARCHITECTURE_DIR)
.join("akeyless-secrets.tlisp"),
)
.unwrap();
let mut bindings = InputBindings::new();
bindings.set_str("name-prefix", "staging");
let arch = eval_architecture_with_schema(&src, &bindings, &iface).unwrap();
let json = arch.render_terraform_json().unwrap();
assert_eq!(
json["resource"]["akeyless_role"]["staging-app-role"]["name"],
"/staging/roles/app"
);
}
#[test]
fn user_inputs_override_defaults_in_tlisp_eval() {
let mut b = InputBindings::new();
b.set_str("name", "preview");
b.set_list("availability-zones", vec!["us-west-2a".into(), "us-west-2b".into()]);
let arch = load_bundled("aws-vpc-network", &b).unwrap();
let json = arch.render_terraform_json().unwrap();
assert!(json["resource"]["aws_vpc"]["preview-vpc"].is_object());
assert!(json["resource"]["aws_internet_gateway"]["preview-igw"].is_object());
assert!(json["resource"]["aws_subnet"]["preview-public-0"].is_object());
assert!(json["resource"]["aws_subnet"]["preview-public-1"].is_object());
assert!(json["resource"]["aws_subnet"]["preview-public-2"].is_null());
assert_eq!(
json["resource"]["aws_subnet"]["preview-public-0"]["availability_zone"],
"us-west-2a"
);
assert_eq!(
json["resource"]["aws_subnet"]["preview-public-1"]["availability_zone"],
"us-west-2b"
);
}
}