diff --git a/Cargo.lock b/Cargo.lock index 23e2f1d..747f6d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -198,7 +198,7 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "agent" -version = "0.2.3" +version = "0.2.6" dependencies = [ "anyhow", "chrono", @@ -805,7 +805,7 @@ dependencies = [ [[package]] name = "dist" -version = "0.2.2" +version = "0.2.6" dependencies = [ "anyhow", "clap", @@ -1779,7 +1779,7 @@ dependencies = [ [[package]] name = "operator" -version = "0.2.2" +version = "0.2.6" dependencies = [ "actix-web", "async-trait", @@ -1822,7 +1822,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "package" -version = "0.2.2" +version = "0.2.6" dependencies = [ "anyhow", "handlebars", @@ -3080,7 +3080,7 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "vynil" -version = "0.2.1" +version = "0.2.6" [[package]] name = "want" diff --git a/Cargo.toml b/Cargo.toml index 8eb6fde..00b9b74 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "vynil" -version = "0.2.1" +version = "0.2.6" authors = ["Sébastien Huss "] edition = "2021" license = " BSD-3-Clause" diff --git a/agent/Cargo.toml b/agent/Cargo.toml index 8883ab9..4b87f61 100644 --- a/agent/Cargo.toml +++ b/agent/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "agent" -version = "0.2.3" +version = "0.2.6" authors = ["Sébastien Huss "] edition = "2021" license = " BSD-3-Clause" diff --git a/agent/Dockerfile b/agent/Dockerfile index 7402149..eff719a 100644 --- a/agent/Dockerfile +++ b/agent/Dockerfile @@ -50,7 +50,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update \ && chmod 755 /usr/local/bin/kubectl /usr/local/bin/helm \ && mkdir -p /var/lib/vynil/keys /nonexistent \ && chown -R nobody:nogroup /var/lib/vynil /nonexistent \ - && chmod 750 /var/lib/vynil /nonexistent \ + && chmod 770 /var/lib/vynil /nonexistent \ && chmod 700 /var/lib/vynil/keys # Use the intermediary image to download the most used providers for runtime caching purpose FROM middle as downloader diff --git a/agent/providers.tf b/agent/providers.tf index 907c31d..8bf60d5 100644 --- a/agent/providers.tf +++ b/agent/providers.tf @@ -5,28 +5,32 @@ terraform { version = "~> 0.9.2" } kubernetes = { - source = "hashicorp/kubernetes" + source = "hashicorp/kubernetes" version = "~> 2.20.0" } kubectl = { - source = "gavinbunney/kubectl" + source = "gavinbunney/kubectl" version = "~> 1.14.0" } authentik = { - source = "goauthentik/authentik" + source = "goauthentik/authentik" version = "~> 2023.5.0" } postgresql = { - source = "cyrilgdn/postgresql" + source = "cyrilgdn/postgresql" version = "~> 1.19.0" } - http = { - source = "hashicorp/http" + mysql = { + source = "petoju/mysql" + version = "~> 3.0.43" + } + http = { + source = "hashicorp/http" version = "~> 3.3.0" } - restapi = { - source = "Mastercard/restapi" + restapi = { + source = "Mastercard/restapi" version = "~> 1.18.0" - } + } } } diff --git a/agent/src/clone.rs b/agent/src/clone.rs index a5a6b6c..8f196bf 100644 --- a/agent/src/clone.rs +++ b/agent/src/clone.rs @@ -107,14 +107,14 @@ pub async fn clone(target: &PathBuf, client: kube::Client, dist: &client::Distri let mut branch_manage: String = "".to_owned(); if dist.branch() != "" { branch_manage.push_str(&format!( - "git checkout {branch}; git reset --hard origin/{branch}; git pull", + "git switch {branch}; git reset --hard origin/{branch}", branch = dist.branch() )) } else { - branch_manage.push_str("git pull") + branch_manage.push_str("RBRANCH=$(git symbolic-ref refs/remotes/origin/HEAD | sed 's@^refs/remotes/origin/@@'); git switch ${RBRANCH}; git reset --hard origin/${RBRANCH}") } shell::run_log(&format!( - "set -e ; cd {target} ; git remote set-url origin {url} ; {command}", + "set -e ; cd {target} ; git remote set-url origin {url} ; git fetch ; {command}", target = target.display(), url = url, command = branch_manage diff --git a/dist/Cargo.toml b/dist/Cargo.toml index 0569548..8eea0f4 100644 --- a/dist/Cargo.toml +++ b/dist/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dist" -version = "0.2.2" +version = "0.2.6" authors = ["Sébastien Huss "] edition = "2021" license = " BSD-3-Clause" diff --git a/dist/src/files.rs b/dist/src/files.rs index 5287ee8..c937fac 100644 --- a/dist/src/files.rs +++ b/dist/src/files.rs @@ -18,11 +18,26 @@ category: metadata: name: description: +dependencies: +# - category: core +# component: secret-generator +# - category: share +# component: authentik +# - category: dbo +# component: pg +# - category: dbo +# component: redis providers: - authentik: true kubernetes: true kubectl: true + authentik: true + http: true + restapi: true options: + timezone: + default: Europe/Paris + language: + default: fr_FR sub-domain: default: to-be-set domain-name: @@ -35,11 +50,51 @@ options: default: traefik app-group: default: infra + replicas: + default: 1 + hpa: + default: + min-replicas: 1 + max-replicas: 5 + avg-cpu: 50 + backups: + default: + enable: false + use-barman: false + endpoint: \"\" + secret-name: backup-settings + key-id-key: s3-id + secret-key: s3-secret + restic-key: bck-password + schedule: + db: \"10 3 * * *\" + backup: \"10 3 * * *\" + check: \"10 5 * * 1\" + prune: \"10 1 * * 0\" + retention: + db: \"30d\" + keepDaily: 14 + keepMonthly: 12 + keepWeekly: 6 + keepYearly: 12 + postgres: + default: + replicas: 1 + redis: + default: + exporter: + enabled: true storage: + description: Configure this app storage default: - size: 1Gi - accessMode: ReadWriteOnce - type: Filesystem + volume: + size: 1Gi + accessMode: ReadWriteOnce + type: Filesystem + redis: + size: '2Gi' + postgres: + size: '10Gi' properties: type: enum: @@ -52,13 +107,41 @@ options: - ReadWriteMany images: default: - operator: + postgresql: + registry: ghcr.io + repository: cloudnative-pg/postgresql + tag: 15.3 + redis: + registry: quay.io + repository: opstree/redis + tag: v7.0.12 + pullPolicy: IfNotPresent + redis_exporter: + registry: quay.io + repository: opstree/redis-exporter + tag: v1.44.0 + pullPolicy: IfNotPresent + app: registry: docker.io repository: to-be/defined tag: v1.0.0 pullPolicy: IfNotPresent properties: - operator: + app: + properties: + pullPolicy: + enum: + - Always + - Never + - IfNotPresent + redis: + properties: + pullPolicy: + enum: + - Always + - Never + - IfNotPresent + redis_exporter: properties: pullPolicy: enum: @@ -68,213 +151,3 @@ options: ".to_string(), false) } -pub fn gen_index_rhai(dest_dir: &PathBuf) -> Result<()> { - if ! Path::new(dest_dir).is_dir() { - bail!("{:?} is not a directory", dest_dir); - } - let mut file = PathBuf::new(); - file.push(dest_dir); - file.push("index.rhai"); - gen_file(&file, &" -const VERSION=config.release; -const SRC=src; -const DEST=dest; -fn pre_pack() { - shell(\"helm repo add goauthentik https://charts.goauthentik.io/\"); - shell(`helm template authentik goauthentik/authentik --namespace=vynil-auth --values values.yml >${global::SRC}/chart.yaml`); - shell(`kubectl kustomize https://github.com/rabbitmq/cluster-operator//config/manager/?ref=${global::VERSION} >${global::SRC}/manager.yaml`); -} -fn post_pack() { - shell(`rm -f ${global::DEST}/v1_Secret_authentik.yaml`); -} -fn pre_install() { - shell(`kubectl apply -k https://github.com/rabbitmq/cluster-operator//config/crd/?ref=v${global::VERSION}`); -} -".to_string(), false) -} - -pub fn gen_presentation(dest_dir: &PathBuf) -> Result<()> { - if ! Path::new(dest_dir).is_dir() { - bail!("{:?} is not a directory", dest_dir); - } - let mut file = PathBuf::new(); - file.push(dest_dir); - file.push("presentation.tf"); - gen_file(&file, &" -locals { - dns-name = \"${var.sub-domain}.${var.domain-name}\" - dns-names = [local.dns-name] - app-name = var.component == var.instance ? var.instance : format(\"%s-%s\", var.component, var.instance) - icon = \"pics/logo.svg\" - request_headers = { - \"Content-Type\" = \"application/json\" - Authorization = \"Bearer ${data.kubernetes_secret_v1.authentik.data[\"AUTHENTIK_BOOTSTRAP_TOKEN\"]}\" - } - service = { - \"name\" = \"${var.component}-${var.instance}\" - \"port\" = { - \"number\" = 80 - } - } -} - -module \"service\" { - source = \"/dist/modules/service\" - component = var.component - instance = var.instance - namespace = var.namespace - labels = local.common-labels - target = \"http\" - port = local.service.port.number - providers = { - kubectl = kubectl - } -} - -module \"ingress\" { - source = \"/dist/modules/ingress\" - component = \"\" - instance = var.instance - namespace = var.namespace - issuer = var.issuer - ingress-class = var.ingress-class - labels = local.common-labels - dns-names = local.dns-names - middlewares = [\"forward-${local.app-name}\"] - service = local.service - providers = { - kubectl = kubectl - } -} - -module \"application\" { - source = \"/dist/modules/application\" - component = var.component - instance = var.instance - app-group = var.app-group - dns-name = local.dns-name - icon = local.icon - protocol_provider = module.forward.provider-id - providers = { - authentik = authentik - } -} - -provider \"restapi\" { - uri = \"http://authentik.${var.domain}-auth.svc/api/v3/\" - headers = local.request_headers - create_method = \"PATCH\" - update_method = \"PATCH\" - destroy_method = \"PATCH\" - write_returns_object = true - id_attribute = \"name\" -} - -module \"forward\" { - source = \"/dist/modules/forward\" - component = var.component - instance = var.instance - domain = var.domain - namespace = var.namespace - ingress-class = var.ingress-class - labels = local.common-labels - dns-names = local.dns-names - service = local.service - icon = local.icon - request_headers = local.request_headers - providers = { - restapi = restapi - http = http - kubectl = kubectl - authentik = authentik - } -} - EOF -} -".to_string(), false) -} - -pub fn gen_postgresql(dest_dir: &PathBuf) -> Result<()> { - if ! Path::new(dest_dir).is_dir() { - bail!("{:?} is not a directory", dest_dir); - } - let mut file = PathBuf::new(); - file.push(dest_dir); - file.push("postgresql.tf"); - gen_file(&file, &" -locals { - pg-labels = merge(local.common-labels, { - \"app.kubernetes.io/component\" = \"pg\" - }) - pool-labels = merge(local.common-labels, { - \"app.kubernetes.io/component\" = \"pg-pool\" - }) -} -resource \"kubectl_manifest\" \"prj_pg\" { - yaml_body = <<-EOF - apiVersion: postgresql.cnpg.io/v1 - kind: Cluster - metadata: - name: \"${var.instance}-${var.component}-pg\" - namespace: \"${var.namespace}\" - labels: ${jsonencode(local.pg-labels)} - spec: - instances: ${var.postgres.replicas} - storage: - size: \"${var.postgres.storage}\" - EOF -} -resource \"kubectl_manifest\" \"prj_pg_pool\" { - depends_on = [kubectl_manifest.prj_pg] - yaml_body = <<-EOF - apiVersion: postgresql.cnpg.io/v1 - kind: Pooler - metadata: - name: \"${var.instance}-${var.component}-pool\" - namespace: \"${var.namespace}\" - labels: ${jsonencode(local.pool-labels)} - spec: - cluster: - name: \"${var.instance}-${var.component}-pg\" - instances: ${var.postgres.replicas} - type: rw - pgbouncer: - poolMode: session - parameters: - max_client_conn: \"1000\" - default_pool_size: \"10\" - EOF -} -".to_string(), false) -} - -pub fn gen_secret(dest_dir: &PathBuf) -> Result<()> { - if ! Path::new(dest_dir).is_dir() { - bail!("{:?} is not a directory", dest_dir); - } - let mut file = PathBuf::new(); - file.push(dest_dir); - file.push("secret.tf"); - gen_file(&file, &" -resource \"kubectl_manifest\" \"prj_secret\" { - ignore_fields = [\"metadata.annotations\"] - yaml_body = <<-EOF - apiVersion: \"secretgenerator.mittwald.de/v1alpha1\" - kind: \"StringSecret\" - metadata: - name: \"${var.component}\" - namespace: \"${var.namespace}\" - labels: ${jsonencode(local.common-labels)} - spec: - forceRegenerate: false - data: - username: \"${var.component}\" - fields: - - fieldName: \"password\" - length: \"32\" - - fieldName: \"jwt-secret\" - length: \"128\" - EOF -} -".to_string(), false) -} diff --git a/dist/src/gen.rs b/dist/src/gen.rs index 96830ab..815e143 100644 --- a/dist/src/gen.rs +++ b/dist/src/gen.rs @@ -28,14 +28,6 @@ pub enum Commands { Datas(ParametersDest), /// Generate index.yaml (when creating a new project) Index(ParametersDest), - /// Generate index.rhai - Rhai(ParametersDest), - /// Generate secret.tf - Secret(ParametersDest), - /// Generate postgresql.tf - Postgresql(ParametersDest), - /// Generate presentation.tf - Presentation(ParametersDest), /// Generate index.yaml options based on the default values Options(ParametersDest), } @@ -100,30 +92,6 @@ pub fn run(args:&Parameters) { process::exit(1) } }} - Commands::Rhai(args) => {match files::gen_index_rhai(&args.project) { - Ok(d) => d, Err(e) => { - log::error!("Generating the index.rhai file failed with: {e:}"); - process::exit(1) - } - }} - Commands::Presentation(args) => {match files::gen_presentation(&args.project) { - Ok(d) => d, Err(e) => { - log::error!("Generating the presentation.tf file failed with: {e:}"); - process::exit(1) - } - }} - Commands::Postgresql(args) => {match files::gen_postgresql(&args.project) { - Ok(d) => d, Err(e) => { - log::error!("Generating the postgresql.tf file failed with: {e:}"); - process::exit(1) - } - }} - Commands::Secret(args) => {match files::gen_secret(&args.project) { - Ok(d) => d, Err(e) => { - log::error!("Generating the secret.tf file failed with: {e:}"); - process::exit(1) - } - }} Commands::Options(args) => {match options(args) { Ok(d) => d, Err(e) => { log::error!("Generation the options in the index.yaml failed with: {e:}"); diff --git a/operator/Cargo.toml b/operator/Cargo.toml index 816cd39..a1b04e9 100644 --- a/operator/Cargo.toml +++ b/operator/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "operator" -version = "0.2.2" +version = "0.2.6" authors = ["Sébastien Huss "] edition = "2021" default-run = "operator" diff --git a/operator/src/cronjobs.rs b/operator/src/cronjobs.rs index 6e7bb00..52965c7 100644 --- a/operator/src/cronjobs.rs +++ b/operator/src/cronjobs.rs @@ -33,11 +33,17 @@ impl CronJobHandler { self.api.get(name).await } - pub async fn create(&mut self, name: &str, spec: &serde_json::Value) -> Result { + pub async fn create(&mut self, name: &str, spec: &serde_json::Value, distname: &str, action: &str) -> Result { let data = serde_json::from_value(serde_json::json!({ "apiVersion": "batch/v1", "kind": "CronJob", "metadata": { + "labels": { + "app": "vynil", + "component": "agent", + "action": action, + "distrib.name": distname, + }, "name": name, }, "spec": spec @@ -45,20 +51,26 @@ impl CronJobHandler { self.api.create(&PostParams::default(), &data).await } - pub async fn apply(&mut self, name: &str, spec: &serde_json::Value) -> Result { + pub async fn apply(&mut self, name: &str, spec: &serde_json::Value, distname: &str, action: &str) -> Result { if self.have(name).await { let params = PatchParams::apply(OPERATOR); let patch = Patch::Apply(serde_json::json!({ "apiVersion": "batch/v1", "kind": "CronJob", "metadata": { + "labels": { + "app": "vynil", + "component": "agent", + "action": action, + "distrib.name": distname, + }, "name": name, }, "spec": spec })); self.api.patch(name, ¶ms, &patch).await } else { - self.create(name, spec).await + self.create(name, spec, distname, action).await } } diff --git a/operator/src/distrib.rs b/operator/src/distrib.rs index 065d44e..37a1647 100644 --- a/operator/src/distrib.rs +++ b/operator/src/distrib.rs @@ -71,13 +71,13 @@ impl Reconciler for Distrib { let template = jobs.get_clone(name.as_str(), self.spec.login.clone()); if jobs.have(clone_name.as_str()).await { info!("Patching {clone_name} Job"); - let _job = match jobs.apply(clone_name.as_str(), &template).await {Ok(j)=>j,Err(_e)=>{ + let _job = match jobs.apply_distrib(clone_name.as_str(), &template, "clone", name.as_str()).await {Ok(j)=>j,Err(_e)=>{ let job = jobs.get(clone_name.as_str()).await.unwrap(); recorder.publish( events::from_delete("plan", &name, "Job", &job.name_any(), Some(job.object_ref(&()))) ).await.map_err(Error::KubeError)?; jobs.delete(clone_name.as_str()).await.unwrap(); - jobs.create(clone_name.as_str(), &template).await.unwrap() + jobs.create_distrib(clone_name.as_str(), &template, "clone", name.as_str()).await.unwrap() }}; // TODO: Detect if the job changed after the patch (or event better would change prior) // TODO: Send a patched event if changed @@ -90,7 +90,7 @@ impl Reconciler for Distrib { debug!("Waited {clone_name} OK"); } else { info!("Creating {clone_name} Job"); - let job = jobs.create(clone_name.as_str(), &template).await.unwrap(); + let job = jobs.create_distrib(clone_name.as_str(), &template, "clone", name.as_str()).await.unwrap(); recorder.publish( events::from_create("Distrib", &name, "Job", &job.name_any(), Some(job.object_ref(&()))) ).await.map_err(Error::KubeError)?; @@ -108,17 +108,17 @@ impl Reconciler for Distrib { let mut crons = CronJobHandler::new(ctx.client.clone(), ns); if crons.have(clone_name.as_str()).await { info!("Patching {clone_name} CronJob"); - let _cjob = match crons.apply(clone_name.as_str(), &cronjob_tmpl).await {Ok(j)=>j,Err(_e)=>{ + let _cjob = match crons.apply(clone_name.as_str(), &cronjob_tmpl, "clone", name.as_str()).await {Ok(j)=>j,Err(_e)=>{ let job = crons.get(clone_name.as_str()).await.unwrap(); recorder.publish( events::from_delete("plan", &name, "Job", &job.name_any(), Some(job.object_ref(&()))) ).await.map_err(Error::KubeError)?; crons.delete(clone_name.as_str()).await.unwrap(); - crons.create(clone_name.as_str(), &template).await.unwrap() + crons.create(clone_name.as_str(), &template, "clone", name.as_str()).await.unwrap() }}; } else { info!("Creating {clone_name} CronJob"); - let cron = crons.create(clone_name.as_str(), &cronjob_tmpl).await.unwrap(); + let cron = crons.create(clone_name.as_str(), &cronjob_tmpl, "clone", name.as_str()).await.unwrap(); recorder.publish( events::from_create("Distrib", &name, "CronJob", &cron.name_any(), Some(cron.object_ref(&()))) ).await.map_err(Error::KubeError)?; diff --git a/operator/src/install.rs b/operator/src/install.rs index 6ea5d6d..fbe8864 100644 --- a/operator/src/install.rs +++ b/operator/src/install.rs @@ -157,11 +157,16 @@ impl Reconciler for Install { } else { jobs.get_installs_install(&hashedself, self.spec.category.as_str(), self.spec.component.as_str()) }; + let action = if self.should_plan() { + "plan" + } else { + "install" + }; if !jobs.have(agent_name.as_str()).await { info!("Creating {agent_name} Job"); self.update_status_agent_started(client, OPERATOR).await.map_err(Error::KubeError)?; - let job = jobs.create(agent_name.as_str(), &agent_job).await.unwrap(); + let job = jobs.create_install(agent_name.as_str(), &agent_job, action, name.as_str(), ns.as_str()).await.unwrap(); debug!("Sending event {agent_name} Job"); recorder.publish( events::from_create("Install", &name, "Job", &job.name_any(), Some(job.object_ref(&()))) @@ -171,13 +176,13 @@ impl Reconciler for Install { debug!("Waited {agent_name} OK"); } else { info!("Patching {agent_name} Job"); - let _job = match jobs.apply(agent_name.as_str(), &agent_job).await {Ok(j)=>j,Err(_e)=>{ + let _job = match jobs.apply_install(agent_name.as_str(), &agent_job, action, name.as_str(), ns.as_str()).await {Ok(j)=>j,Err(_e)=>{ let job = jobs.get(agent_name.as_str()).await.unwrap(); recorder.publish( events::from_delete("plan", &name, "Job", &job.name_any(), Some(job.object_ref(&()))) ).await.map_err(Error::KubeError)?; jobs.delete(agent_name.as_str()).await.unwrap(); - jobs.create(agent_name.as_str(), &agent_job).await.unwrap() + jobs.create_install(agent_name.as_str(), &agent_job, action, name.as_str(), ns.as_str()).await.unwrap() }}; // TODO: Detect if the job changed after the patch (or event better would change prior) // TODO: Send a patched event if changed @@ -213,13 +218,13 @@ impl Reconciler for Install { let destroyer_job = jobs.get_installs_destroy(&hashedself, self.spec.category.as_str(), self.spec.component.as_str()); info!("Creating {agent_name} Job"); - let job = match jobs.apply_short(agent_name.as_str(), &destroyer_job).await {Ok(j)=>j,Err(_e)=>{ + let job = match jobs.apply_short_install(agent_name.as_str(), &destroyer_job, "destroy", name.as_str(), ns.as_str()).await {Ok(j)=>j,Err(_e)=>{ let job = jobs.get(agent_name.as_str()).await.unwrap(); recorder.publish( events::from_delete("Install", &name, "Job", &job.name_any(), Some(job.object_ref(&()))) ).await.map_err(Error::KubeError)?; jobs.delete(agent_name.as_str()).await.unwrap(); - jobs.create_short(agent_name.as_str(), &destroyer_job).await.unwrap() + jobs.create_short_install(agent_name.as_str(), &destroyer_job, "destroy", name.as_str(), ns.as_str()).await.unwrap() }}; recorder.publish( events::from_create("Install", &name, "Job", &job.name_any(), Some(job.object_ref(&()))) diff --git a/operator/src/jobs.rs b/operator/src/jobs.rs index fe8608c..6817055 100644 --- a/operator/src/jobs.rs +++ b/operator/src/jobs.rs @@ -89,7 +89,8 @@ fn clone_container(name: &str, auth: Option) -> serde_json::Valu if auth.git_credentials.is_some() { mounts.push(serde_json::json!({ "name": "creds", - "mountPath": "/var/lib/vynil", + "mountPath": "/var/lib/vynil/git-credentials", + "subPath": "git-credentials", })); } } @@ -273,12 +274,40 @@ impl JobHandler { self.api.get(name).await } - pub async fn create(&mut self, name: &str, template: &serde_json::Value) -> Result { + pub async fn create_distrib(&mut self, name: &str, template: &serde_json::Value, action: &str, distrib_name: &str) -> Result { let data = serde_json::from_value(serde_json::json!({ "apiVersion": "batch/v1", "kind": "Job", "metadata": { "name": name, + "labels": { + "app": "vynil", + "component": "agent", + "action": action, + "distrib.name": distrib_name, + }, + }, + "spec": { + "backoffLimit": 3, + "parallelism": 1, + "template": template + } + })).unwrap(); + self.api.create(&PostParams::default(), &data).await + } + pub async fn create_install(&mut self, name: &str, template: &serde_json::Value, action: &str, install_name: &str, install_namespace: &str) -> Result { + let data = serde_json::from_value(serde_json::json!({ + "apiVersion": "batch/v1", + "kind": "Job", + "metadata": { + "name": name, + "labels": { + "app": "vynil", + "component": "agent", + "action": action, + "install.name": install_name, + "install.namespace": install_namespace, + }, }, "spec": { "backoffLimit": 3, @@ -289,7 +318,7 @@ impl JobHandler { self.api.create(&PostParams::default(), &data).await } - pub async fn create_short(&mut self, name: &str, template: &serde_json::Value) -> Result { + pub async fn create_short_install(&mut self, name: &str, template: &serde_json::Value, action: &str, install_name: &str, install_namespace: &str) -> Result { let data = serde_json::from_value(serde_json::json!({ "apiVersion": "batch/v1", "kind": "Job", @@ -298,6 +327,13 @@ impl JobHandler { "annotations": { "mayfly.cloud.namecheap.com/expire": "1h" }, + "labels": { + "app": "vynil", + "component": "agent", + "action": action, + "install.name": install_name, + "install.namespace": install_namespace, + }, }, "spec": { "backoffLimit": 3, @@ -308,7 +344,32 @@ impl JobHandler { self.api.create(&PostParams::default(), &data).await } - pub async fn apply(&mut self, name: &str, template: &serde_json::Value) -> Result { + pub async fn apply_install(&mut self, name: &str, template: &serde_json::Value, action: &str, install_name: &str, install_namespace: &str) -> Result { + if self.have(name).await { + let params = PatchParams::apply(OPERATOR); + let patch = Patch::Apply(serde_json::json!({ + "apiVersion": "batch/v1", + "kind": "Job", + "metadata": { + "name": name, + "labels": { + "app": "vynil", + "component": "agent", + "action": action, + "install.name": install_name, + "install.namespace": install_namespace, + }, + }, + "spec": { + "template": template + } + })); + self.api.patch(name, ¶ms, &patch).await + } else { + self.create_install(name, template, action, install_name, install_namespace).await + } + } + pub async fn apply_distrib(&mut self, name: &str, template: &serde_json::Value, action: &str, distrib_name: &str) -> Result { if self.have(name).await { let params = PatchParams::apply(OPERATOR); let patch = Patch::Apply(serde_json::json!({ @@ -316,6 +377,12 @@ impl JobHandler { "kind": "Job", "metadata": { "name": name, + "labels": { + "app": "vynil", + "component": "agent", + "action": action, + "distrib.name": distrib_name, + }, }, "spec": { "template": template @@ -323,11 +390,11 @@ impl JobHandler { })); self.api.patch(name, ¶ms, &patch).await } else { - self.create(name, template).await + self.create_distrib(name, template, action, distrib_name).await } } - pub async fn apply_short(&mut self, name: &str, template: &serde_json::Value) -> Result { + pub async fn apply_short_install(&mut self, name: &str, template: &serde_json::Value, action: &str, install_name: &str, install_namespace: &str) -> Result { if self.have(name).await { let params = PatchParams::apply(OPERATOR); let patch = Patch::Apply(serde_json::json!({ @@ -335,6 +402,13 @@ impl JobHandler { "kind": "Job", "metadata": { "name": name, + "labels": { + "app": "vynil", + "component": "agent", + "action": action, + "install.name": install_name, + "install.namespace": install_namespace, + }, "annotations": { "mayfly.cloud.namecheap.com/expire": "1h" }, @@ -345,7 +419,7 @@ impl JobHandler { })); self.api.patch(name, ¶ms, &patch).await } else { - self.create_short(name, template).await + self.create_short_install(name, template, action, install_name, install_namespace).await } } diff --git a/package/Cargo.toml b/package/Cargo.toml index b8b9124..33d9a61 100644 --- a/package/Cargo.toml +++ b/package/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "package" -version = "0.2.2" +version = "0.2.6" authors = ["Sébastien Huss "] edition = "2021" license = " BSD-3-Clause" diff --git a/package/src/template.rs b/package/src/template.rs index 6464324..1c116f5 100644 --- a/package/src/template.rs +++ b/package/src/template.rs @@ -1,5 +1,5 @@ use handlebars::Handlebars; -use anyhow::{Result}; +use anyhow::Result; pub fn template(template:&str ,values:&serde_json::Map) -> Result { diff --git a/package/src/terraform.rs b/package/src/terraform.rs index 6352b10..e7e03b5 100644 --- a/package/src/terraform.rs +++ b/package/src/terraform.rs @@ -164,8 +164,8 @@ provider \"kubectl\" { }"; content += " provider \"authentik\" { - url = \"http://authentik.${var.domain}-auth.svc\" - token = data.kubernetes_secret_v1.authentik.data[\"AUTHENTIK_BOOTSTRAP_TOKEN\"] + url = local.authentik_url + token = local.authentik_token }"; } else { requiered += " @@ -175,8 +175,8 @@ provider \"authentik\" { # }"; content += " #provider \"authentik\" { -# url = \"http://authentik.${var.domain}-auth.svc\" -# token = data.kubernetes_secret_v1.authentik.data[\"AUTHENTIK_BOOTSTRAP_TOKEN\"] +# url = local.authentik_url +# token = local.authentik_token #}"; } let mut have_postgresql = false; @@ -193,9 +193,9 @@ provider \"authentik\" { }"; content += " provider \"postgresql\" { - host = local.pg-host - username = local.pg-username - password = local.pg-password + host = local.pg_host + username = local.pg_username + password = local.pg_password }"; } else { requiered += " @@ -205,9 +205,40 @@ provider \"postgresql\" { # }"; content += " #provider \"postgresql\" { -# host = local.pg-host -# username = local.pg-username -# password = local.pg-password +# host = local.pg_host +# username = local.pg_username +# password = local.pg_password +#}"; + } + let mut have_mysql = false; + if let Some(providers) = providers.clone() { + if let Some(mysql) = providers.mysql { + have_mysql = mysql; + } + } + if have_mysql { + requiered += " + mysql = { + source = \"petoju/mysql\" + version = \"~> 3.0.43\" + }"; + content += " +provider \"mysql\" { + host = local.mysql_host + username = local.mysql_username + password = local.mysql_password +}"; + } else { + requiered += " +# mysql = { +# source = \"petoju/mysql\" +# version = \"~> 3.0.43\" +# }"; + content += " +#provider \"mysql\" { +# host = local.mysql_host +# username = local.mysql_username +# password = local.mysql_password #}"; } let mut have_http = false; @@ -247,9 +278,9 @@ provider \"http\" {}"; }"; content += " provider \"gitea\" { - base_url = \"http://gitea-http.${var.domain}-ci.svc:3000/\" - username = data.kubernetes_secret_v1.gitea.data[\"username\"] - password = data.kubernetes_secret_v1.gitea.data[\"password\"] + base_url = local.gitea_host + username = local.gitea_username + password = local.gitea_password }"; } else { requiered += " @@ -259,9 +290,9 @@ provider \"gitea\" { # }"; content += " #provider \"gitea\" { -# base_url = \"http://gitea-http.${var.domain}-ci.svc:3000/\" -# username = data.kubernetes_secret_v1.gitea.data[\"username\"] -# password = data.kubernetes_secret_v1.gitea.data[\"password\"] +# base_url = local.gitea_host +# username = local.gitea_username +# password = local.gitea_password #}"; } let mut have_restapi = false; @@ -348,6 +379,11 @@ pub fn gen_datas(dest_dir: &PathBuf) -> Result<()> { file.push("datas.tf"); gen_file(&file, &" locals { +# authentik_url = \"http://authentik.${var.domain}-auth.svc\" +# authentik_token = data.kubernetes_secret_v1.authentik.data[\"AUTHENTIK_BOOTSTRAP_TOKEN\"] +# gitea_host = \"http://gitea-http.${var.domain}-ci.svc:3000/\" +# gitea_username = data.kubernetes_secret_v1.gitea.data[\"username\"] +# gitea_password = data.kubernetes_secret_v1.gitea.data[\"password\"] common-labels = { \"vynil.solidite.fr/owner-name\" = var.instance \"vynil.solidite.fr/owner-namespace\" = var.namespace @@ -367,6 +403,15 @@ locals { # } # } +# data \"kubernetes_secret_v1\" \"prj_mysql_secret\" { +# depends_on = [kubectl_manifest.prj_mysql_secret] +# metadata { +# name = \"${local.app_slug}-mysql\" +# namespace = var.namespace +# labels = local.mysql_labels +# } +# } + # data \"kubernetes_secret_v1\" \"authentik\" { # metadata { # name = \"authentik\" diff --git a/package/src/yaml.rs b/package/src/yaml.rs index aa2a531..57d12d5 100644 --- a/package/src/yaml.rs +++ b/package/src/yaml.rs @@ -25,6 +25,7 @@ pub struct Providers { pub authentik: Option, pub kubectl: Option, pub postgresql: Option, + pub mysql: Option, pub restapi: Option, pub http: Option, pub gitea: Option,