diff --git a/crates/cuddle-clusters/src/catalog.rs b/crates/cuddle-clusters/src/catalog.rs index d49afed..004c560 100644 --- a/crates/cuddle-clusters/src/catalog.rs +++ b/crates/cuddle-clusters/src/catalog.rs @@ -2,4 +2,5 @@ pub mod cluster_vars; pub mod crdb_database; pub mod cuddle_vars; pub mod ingress; +pub mod postgres_database; pub mod vault_secret; diff --git a/crates/cuddle-clusters/src/catalog/cluster_vars.rs b/crates/cuddle-clusters/src/catalog/cluster_vars.rs index 1ca9676..43eba2d 100644 --- a/crates/cuddle-clusters/src/catalog/cluster_vars.rs +++ b/crates/cuddle-clusters/src/catalog/cluster_vars.rs @@ -81,10 +81,6 @@ impl Component for ClusterVars { } } } - // vars.raw = match value.clone().try_into() { - // Ok(o) => o, - // Err(e) => panic!("{}", e), - // }; vars.raw = value.into(); vars.name = environment.into(); diff --git a/crates/cuddle-clusters/src/catalog/postgres_database.rs b/crates/cuddle-clusters/src/catalog/postgres_database.rs new file mode 100644 index 0000000..8e8dc41 --- /dev/null +++ b/crates/cuddle-clusters/src/catalog/postgres_database.rs @@ -0,0 +1,145 @@ +use std::path::Path; + +use minijinja::{value::Object, Value}; + +use crate::Component; + +use super::cuddle_vars::{load_cuddle_file, CuddleVariable, CuddleVariables}; + +pub struct PostgresDatabase { + variables: CuddleVariables, +} + +impl PostgresDatabase { + pub async fn new(path: &Path) -> anyhow::Result { + let variables = load_cuddle_file(path).await?; + + Ok(Self { variables }) + } +} + +impl Component for PostgresDatabase { + fn name(&self) -> String { + "cuddle/postgres".into() + } + + fn render_value( + &self, + environment: &str, + _value: &serde_yaml::Value, + ) -> Option> { + if let Some(true) = self + .variables + .0 + .get("database") + .and_then(|v| match v { + CuddleVariable::Object(o) => Some(o), + _ => None, + }) + .and_then(|o| o.0.get("postgres")) + .and_then(|o| match o { + CuddleVariable::String(o) => { + if o == "true" { + Some(true) + } else { + None + } + } + _ => None, + }) + { + return Some(Ok(minijinja::Value::from_object(PostgresDatabaseValues { + name: self.name(), + enabled: true, + }))); + } + + Some(Ok(minijinja::Value::from_object(PostgresDatabaseValues { + name: self.name(), + enabled: false, + }))) + } + + fn render( + &self, + environment: &str, + _value: &serde_yaml::Value, + ) -> Option> { + if let Some(true) = self + .variables + .0 + .get("database") + .and_then(|v| match v { + CuddleVariable::Object(o) => Some(o), + _ => None, + }) + .and_then(|o| o.0.get("postgres")) + .and_then(|o| match o { + CuddleVariable::String(o) => { + if o == "true" { + Some(true) + } else { + None + } + } + _ => None, + }) + { + return Some(Ok(( + format!("{}.yaml", self.name().replace("/", "-")), + r#" +{%- if environment == "dev" %} +{% set port = 5433 %} +{% else %} +{% set port = 5432 %} +{%- endif%} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ vars.cuddle_crdb.file_name(vars.cuddle_vars.service) }} + namespace: {{ vars.cluster_vars.namespace }} +data: + DATABASE_TYPE: postgresql + DATABASE_HOST: {{ environment }}.postgresql.kjuulh.app + DATABASE_PORT: {{ port }} + DATABASE_USER: {{ vars.cuddle_vars.service | replace("_", "-") }} + DATABASE_DB: {{ vars.cuddle_vars.service | replace("_", "-") }} + "# + .into(), + ))); + } + + None + } +} + +#[derive(Debug)] +struct PostgresDatabaseValues { + name: String, + enabled: bool, +} + +impl Object for PostgresDatabaseValues { + fn get_value(self: &std::sync::Arc, key: &minijinja::Value) -> Option { + let name = self.name.clone(); + match key.as_str()? { + "has_values" => { + if self.enabled { + Some(minijinja::Value::from_serialize(true)) + } else { + Some(minijinja::Value::from_serialize(false)) + } + } + "file_name" => Some(Value::from_function(move |file_name: String| { + format!("{}-{}", file_name, name.replace("/", "-")) + })), + "env" => Some(Value::from_serialize(vec![ + "DATABASE_HOST", + "DATABASE_PORT", + "DATABASE_USER", + "DATABASE_DB", + ])), + _ => None, + } + } +} diff --git a/crates/cuddle-clusters/tests/tests.rs b/crates/cuddle-clusters/tests/tests.rs index 5f4208f..4a34df1 100644 --- a/crates/cuddle-clusters/tests/tests.rs +++ b/crates/cuddle-clusters/tests/tests.rs @@ -6,7 +6,7 @@ mod cuddle_vars; use cuddle_clusters::{ catalog::{ cluster_vars::ClusterVars, crdb_database::CockroachDB, cuddle_vars::CuddleVars, - ingress::Ingress, vault_secret::VaultSecret, + ingress::Ingress, postgres_database::PostgresDatabase, vault_secret::VaultSecret, }, IntoComponent, }; @@ -125,3 +125,21 @@ async fn with_ingress() -> anyhow::Result<()> { Ok(()) } + +#[tokio::test] +async fn with_postgres_databse() -> anyhow::Result<()> { + let current_dir = std::env::current_dir()?.join("tests/with_postgres_database"); + + run_test_with_components( + "with_postgres_database", + vec![ + CuddleVars::new(¤t_dir).await?.into_component(), + ClusterVars::default().into_component(), + VaultSecret::default().into_component(), + PostgresDatabase::new(¤t_dir).await?.into_component(), + ], + ) + .await?; + + Ok(()) +} diff --git a/crates/cuddle-clusters/tests/with_postgres_database/cuddle.yaml b/crates/cuddle-clusters/tests/with_postgres_database/cuddle.yaml new file mode 100644 index 0000000..40ddb91 --- /dev/null +++ b/crates/cuddle-clusters/tests/with_postgres_database/cuddle.yaml @@ -0,0 +1,7 @@ +vars: + service: service + database: + postgres: "true" + +cuddle/clusters: + dev: diff --git a/crates/cuddle-clusters/tests/with_postgres_database/expected/dev/cuddle-postgresql.yaml b/crates/cuddle-clusters/tests/with_postgres_database/expected/dev/cuddle-postgresql.yaml new file mode 100644 index 0000000..8a380ff --- /dev/null +++ b/crates/cuddle-clusters/tests/with_postgres_database/expected/dev/cuddle-postgresql.yaml @@ -0,0 +1,9 @@ + +apiVersion: v1 +kind: ConfigMap +metadata: + name: service-cuddle-crdb + namespace: dev +data: + DATABASE_URL: postgresql://root@dev-cluster:26257/service + \ No newline at end of file diff --git a/crates/cuddle-clusters/tests/with_postgres_database/expected/dev/some.yaml b/crates/cuddle-clusters/tests/with_postgres_database/expected/dev/some.yaml new file mode 100644 index 0000000..6286958 --- /dev/null +++ b/crates/cuddle-clusters/tests/with_postgres_database/expected/dev/some.yaml @@ -0,0 +1,39 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: service + name: service +spec: + replicas: 3 + selector: + matchLabels: + app: service + template: + metadata: + labels: + app: service + spec: + containers: + - args: + - serve + command: + - service + image: kasperhermansen/service:main-1715336504 + name: service + envFrom: + - configMapRef: + name: service-config + env: + - name: DATABASE_URL + valueFrom: + secretKeyRef: + name: service-cuddle-crdb + key: DATABASE_URL + ports: + - containerPort: 3000 + name: external-http + - containerPort: 3001 + name: internal-http + - containerPort: 3002 + name: internal-grpc \ No newline at end of file diff --git a/crates/cuddle-clusters/tests/with_postgres_database/templates/clusters/some.yaml.jinja2 b/crates/cuddle-clusters/tests/with_postgres_database/templates/clusters/some.yaml.jinja2 new file mode 100644 index 0000000..4ce643a --- /dev/null +++ b/crates/cuddle-clusters/tests/with_postgres_database/templates/clusters/some.yaml.jinja2 @@ -0,0 +1,56 @@ +{%- set service_name = vars.cuddle_vars.service -%} + +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: {{ service_name }} + name: {{ service_name }} +spec: + replicas: 3 + selector: + matchLabels: + app: {{ service_name }} + template: + metadata: + labels: + app: {{ service_name }} + spec: + containers: + - args: + - serve + command: + - {{ service_name }} + image: kasperhermansen/{{ service_name }}:main-1715336504 + name: {{ service_name }} + envFrom: + - configMapRef: + name: {{service_name}}-config + {%- if vars.vault_secret.has_values or vars.cuddle_crdb.has_values %} + env: + {%- if vars.vault_secret.has_values %} + {%- for secret in vars.vault_secret.secrets %} + - name: {{secret | upper | replace(".", "_") | replace("-", "_") }} + valueFrom: + secretKeyRef: + name: {{ vars.vault_secret.file_name(service_name) }} + key: {{ secret }} + {%- endfor %} + {%- endif %} + {%- if vars.cuddle_postgres.has_values %} + {%- for env in vars.cuddle_postgres.env %} + - name: {{ env }} + valueFrom: + secretKeyRef: + name: {{ vars.cuddle_postgres.file_name(service_name) }} + key: {{ env }} + {%- endfor %} + {%- endif %} + {%- endif %} + ports: + - containerPort: 3000 + name: external-http + - containerPort: 3001 + name: internal-http + - containerPort: 3002 + name: internal-grpc