diff --git a/meta/domain-auth/apps.tf b/meta/domain-auth/apps.tf index 9c81e8b..a6dc7e7 100644 --- a/meta/domain-auth/apps.tf +++ b/meta/domain-auth/apps.tf @@ -1,17 +1,17 @@ locals { annotations = { - "vynil.solidite.fr/meta" = var.component - "vynil.solidite.fr/name" = "${var.namespace}-auth" - "vynil.solidite.fr/domain" = var.domain-name - "vynil.solidite.fr/issuer" = var.issuer + "vynil.solidite.fr/meta" = var.component + "vynil.solidite.fr/name" = "${var.namespace}-auth" + "vynil.solidite.fr/domain" = var.domain-name + "vynil.solidite.fr/issuer" = var.issuer "vynil.solidite.fr/ingress" = var.ingress-class } global = { - "domain" = var.namespace - "domain-name" = var.domain-name - "issuer" = var.issuer + "domain" = var.namespace + "domain-name" = var.domain-name + "issuer" = var.issuer "ingress-class" = var.ingress-class - "backups" = var.backups + "backups" = var.backups } authentik = { for k, v in var.authentik : k => v if k!="enable" } authentik-ldap = { for k, v in var.authentik-ldap : k => v if k!="enable" } diff --git a/meta/domain-auth/divisions.tf b/meta/domain-auth/divisions.tf new file mode 100644 index 0000000..db694a5 --- /dev/null +++ b/meta/domain-auth/divisions.tf @@ -0,0 +1,117 @@ +locals { + sorted-div-clients-names = reverse(distinct(sort([ + for div in var.clients.divisions: div.name + ]))) + sorted-div-clients = flatten([ + for name in local.sorted-div-clients-names: [ + for div in var.clients.divisions: + div if div.name == name + ] + ]) + sorted-div-employes-names = reverse(distinct(sort([ + for div in var.employes.divisions: div.name + ]))) + sorted-div-employes = flatten([ + for name in local.sorted-div-employes-names: [ + for div in var.employes.divisions: + div if div.name == name + ] + ]) + sorted-div-fournisseurs-names = reverse(distinct(sort([ + for div in var.fournisseurs.divisions: div.name + ]))) + sorted-div-fournisseurs = flatten([ + for name in local.sorted-div-fournisseurs-names: [ + for div in var.fournisseurs.divisions: + div if div.name == name + ] + ]) +} + +resource "kubectl_manifest" "accounts-management" { + count = var.authentik.enable && var.employes.enable ? 1 : 0 + depends_on = [kubernetes_namespace_v1.auth-ns] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "accounts-management" + namespace: "${var.namespace}-auth" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "${var.distributions.domain}" + category: "share" + component: "accounts-management" + options: ${jsonencode(merge(local.global, { + clients = var.clients + employes = var.employes + fournisseurs = var.fournisseurs + }))} + EOF +} + +resource "kubectl_manifest" "employes-divisions" { + count = var.authentik.enable && var.employes.enable ? length(local.sorted-div-employes) : 0 + depends_on = [kubernetes_namespace_v1.auth-ns,kubectl_manifest.accounts-management] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "employes-${local.sorted-div-employes[count.index].name}" + namespace: "${var.namespace}-auth" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "${var.distributions.domain}" + category: "share" + component: "division" + options: ${jsonencode(merge(local.global, { + parent = "employes" + apps = local.sorted-div-employes[count.index].apps + teams = local.sorted-div-employes[count.index].teams + }))} + EOF +} + +resource "kubectl_manifest" "clients-divisions" { + count = var.authentik.enable && var.employes.enable && var.clients.enable ? length(local.sorted-div-clients) : 0 + depends_on = [kubernetes_namespace_v1.auth-ns,kubectl_manifest.accounts-management] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "clients-${local.sorted-div-clients[count.index].name}" + namespace: "${var.namespace}-auth" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "${var.distributions.domain}" + category: "share" + component: "division" + options: ${jsonencode(merge(local.global, { + parent = "clients" + apps = local.sorted-div-clients[count.index].apps + teams = local.sorted-div-clients[count.index].teams + }))} + EOF +} + +resource "kubectl_manifest" "fournisseurs-divisions" { + count = var.authentik.enable && var.employes.enable && var.fournisseurs.enable ? length(local.sorted-div-fournisseurs) : 0 + depends_on = [kubernetes_namespace_v1.auth-ns,kubectl_manifest.accounts-management] + yaml_body = <<-EOF + apiVersion: "vynil.solidite.fr/v1" + kind: "Install" + metadata: + name: "fournisseurs-${local.sorted-div-fournisseurs[count.index].name}" + namespace: "${var.namespace}-auth" + labels: ${jsonencode(local.common-labels)} + spec: + distrib: "${var.distributions.domain}" + category: "share" + component: "division" + options: ${jsonencode(merge(local.global, { + parent = "fournisseurs" + apps = local.sorted-div-fournisseurs[count.index].apps + teams = local.sorted-div-fournisseurs[count.index].teams + }))} + EOF +} diff --git a/meta/domain-auth/index.yaml b/meta/domain-auth/index.yaml index 653b24d..d8e22bb 100644 --- a/meta/domain-auth/index.yaml +++ b/meta/domain-auth/index.yaml @@ -6,51 +6,6 @@ metadata: name: domain-auth description: null options: - distributions: - default: - core: core - domain: domain - examples: - - core: core - domain: domain - properties: - core: - default: core - type: string - domain: - default: domain - type: string - type: object - authentik-forward: - default: - enable: false - examples: - - enable: false - properties: - enable: - default: false - type: boolean - type: object - issuer: - default: letsencrypt-prod - examples: - - letsencrypt-prod - type: string - authentik: - default: - enable: true - examples: - - enable: true - properties: - enable: - default: true - type: boolean - type: object - domain-name: - default: your_company.com - examples: - - your_company.com - type: string authentik-ldap: default: enable: false @@ -61,6 +16,30 @@ options: default: false type: boolean type: object + employes: + properties: + apps: + items: + type: string + type: array + divisions: + items: + properties: + apps: + items: + type: string + type: array + name: + type: string + teams: + items: + type: object + type: array + type: object + type: array + enable: + default: true + type: boolean backups: default: enable: false @@ -91,11 +70,45 @@ options: default: backup-settings type: string type: object - ingress-class: - default: traefik + distributions: + default: + core: core + domain: domain examples: - - traefik - type: string + - core: core + domain: domain + properties: + core: + default: core + type: string + domain: + default: domain + type: string + type: object + clients: + properties: + apps: + items: + type: string + type: array + divisions: + items: + properties: + apps: + items: + type: string + type: array + name: + type: string + teams: + items: + type: object + type: array + type: object + type: array + enable: + default: false + type: boolean storage-classes: default: BlockReadWriteMany: '' @@ -121,11 +134,70 @@ options: default: '' type: string type: object + domain-name: + default: your_company.com + examples: + - your_company.com + type: string + fournisseurs: + properties: + apps: + items: + type: string + type: array + divisions: + items: + properties: + apps: + items: + type: string + type: array + name: + type: string + teams: + items: + type: object + type: array + type: object + type: array + enable: + default: false + type: boolean + ingress-class: + default: traefik + examples: + - traefik + type: string + authentik: + default: + enable: true + examples: + - enable: true + properties: + enable: + default: true + type: boolean + type: object + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string domain: default: your-company examples: - your-company type: string + authentik-forward: + default: + enable: false + examples: + - enable: false + properties: + enable: + default: false + type: boolean + type: object dependencies: [] providers: kubernetes: true diff --git a/share/accounts-management/datas.tf b/share/accounts-management/datas.tf new file mode 100644 index 0000000..821c848 --- /dev/null +++ b/share/accounts-management/datas.tf @@ -0,0 +1,22 @@ +locals { + common-labels = { + "vynil.solidite.fr/owner-name" = var.instance + "vynil.solidite.fr/owner-namespace" = var.namespace + "vynil.solidite.fr/owner-category" = var.category + "vynil.solidite.fr/owner-component" = var.component + "app.kubernetes.io/managed-by" = "vynil" + "app.kubernetes.io/name" = var.component + "app.kubernetes.io/instance" = var.instance + } +} + +data "kubernetes_secret_v1" "authentik" { + metadata { + name = "authentik" + namespace = var.namespace + } +} + +data "kustomization_overlay" "data" { + resources = [] +} diff --git a/share/accounts-management/groups.tf b/share/accounts-management/groups.tf new file mode 100644 index 0000000..1f04370 --- /dev/null +++ b/share/accounts-management/groups.tf @@ -0,0 +1,16 @@ +resource "authentik_group" "employes" { + name = "employes" + attributes = jsonencode(merge([for app in var.employes.apps: {"${app}" = true}])) +} + +resource "authentik_group" "fournisseurs" { + count = var.fournisseurs.enable ? 1 : 0 + name = "fournisseurs" + attributes = jsonencode(merge([for app in var.fournisseurs.apps: {"${app}" = true}])) +} + +resource "authentik_group" "clients" { + count = var.clients.enable ? 1 : 0 + name = "clients" + attributes = jsonencode(merge([for app in var.clients.apps: {"${app}" = true}])) +} diff --git a/share/accounts-management/index.yaml b/share/accounts-management/index.yaml new file mode 100644 index 0000000..200d59d --- /dev/null +++ b/share/accounts-management/index.yaml @@ -0,0 +1,49 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: share +metadata: + name: accounts-management + description: null +options: + domain: + default: your-company + examples: + - your-company + type: string + employes: + properties: + apps: + items: + type: string + type: array + fournisseurs: + properties: + apps: + items: + type: string + type: array + enable: + default: false + type: boolean + clients: + properties: + apps: + items: + type: string + type: array + enable: + default: false + type: boolean +dependencies: +- dist: null + category: share + component: authentik +providers: + kubernetes: true + authentik: true + kubectl: true + postgresql: null + restapi: null + http: true +tfaddtype: null diff --git a/share/dataset-pg/databases.tf b/share/dataset-pg/databases.tf new file mode 100644 index 0000000..60d0746 --- /dev/null +++ b/share/dataset-pg/databases.tf @@ -0,0 +1,69 @@ +data "kubernetes_secret_v1" "postgresql_password" { + depends_on = [ kubectl_manifest.prj_pg ] + metadata { + name = "${var.instance}-${var.component}-superuser" + namespace = "${var.namespace}" + } +} +locals { + pg-username = data.kubernetes_secret_v1.postgresql_password.data["username"] + pg-password = data.kubernetes_secret_v1.postgresql_password.data["password"] + pg-host = "${var.db-source.name}-pg-rw.${var.db-source.namespace}.svc" + + sorted-db-name = reverse(distinct(sort([ + for db in var.databases: db.name + ]))) + sorted-dbs = flatten([ + for name in local.sorted-db-name: [ + for db in var.databases: + db if db.name == name + ] + ]) +} + + +resource "kubectl_manifest" "db_secret" { + ignore_fields = ["metadata.annotations"] + count = length(local.sorted-dbs) + yaml_body = <<-EOF + apiVersion: "secretgenerator.mittwald.de/v1alpha1" + kind: "StringSecret" + metadata: + name: "${var.instance}-${var.component}-${local.sorted-dbs[count.index].name}" + namespace: "${var.namespace}" + labels: ${jsonencode(merge(local.common-labels, {"app.kubernetes.io/component" = local.sorted-dbs[count.index].name}))} + spec: + forceRegenerate: false + data: + POSGRESQL_USERNAME: "${local.sorted-dbs[count.index].name}" + fields: + - fieldName: "POSGRESQL_PASSWORD" + length: "32" + EOF +} + +data "kubernetes_secret_v1" "password_get" { + depends_on = [ kubectl_manifest.db_secret ] + count = length(local.sorted-dbs) + metadata { + name = "${var.instance}-${var.component}-${local.sorted-dbs[count.index].name}" + namespace = "${var.namespace}" + } +} + +resource "postgresql_role" "owner" { + depends_on = [ kubectl_manifest.prj_pg ] + count = length(local.sorted-dbs) + name = "${local.sorted-dbs[count.index].name}" + login = true + password = data.kubernetes_secret_v1.password_get[count.index].data["POSGRESQL_PASSWORD"] +} + +resource "postgresql_database" "my_db" { + depends_on = [ postgresql_role.owner ] + count = length(local.sorted-dbs) + name = "${local.sorted-dbs[count.index].name}" + owner = "${postgresql_role.owner[count.index].name}" + connection_limit = -1 + allow_connections = true +} diff --git a/share/dataset-pg/index.yaml b/share/dataset-pg/index.yaml new file mode 100644 index 0000000..c4ccb59 --- /dev/null +++ b/share/dataset-pg/index.yaml @@ -0,0 +1,164 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: share +metadata: + name: dataset-pg + description: null +options: + domain: + default: your-company + examples: + - your-company + type: string + replicas: + default: 1 + examples: + - 1 + type: integer + backups: + default: + enable: false + endpoint: '' + key-id-key: s3-id + retention: + db: 30d + schedule: + db: 0 3 * * * + secret-key: s3-secret + secret-name: backup-settings + examples: + - enable: false + endpoint: '' + key-id-key: s3-id + retention: + db: 30d + schedule: + db: 0 3 * * * + secret-key: s3-secret + secret-name: backup-settings + properties: + enable: + default: false + type: boolean + endpoint: + default: '' + type: string + key-id-key: + default: s3-id + type: string + retention: + default: + db: 30d + properties: + db: + default: 30d + type: string + type: object + schedule: + default: + db: 0 3 * * * + properties: + db: + default: 0 3 * * * + type: string + type: object + secret-key: + default: s3-secret + type: string + secret-name: + default: backup-settings + type: string + type: object + storage: + default: 8Gi + examples: + - 8Gi + type: string + images: + default: + operator: + pullPolicy: IfNotPresent + registry: docker.io + repository: to-be/defined + tag: v1.0.0 + examples: + - operator: + pullPolicy: IfNotPresent + registry: docker.io + repository: to-be/defined + tag: v1.0.0 + properties: + operator: + default: + pullPolicy: IfNotPresent + registry: docker.io + repository: to-be/defined + tag: v1.0.0 + properties: + pullPolicy: + default: IfNotPresent + enum: + - Always + - Never + - IfNotPresent + type: string + registry: + default: docker.io + type: string + repository: + default: to-be/defined + type: string + tag: + default: v1.0.0 + type: string + type: object + type: object + roles: + default: [] + items: + type: string + type: array + issuer: + default: letsencrypt-prod + examples: + - letsencrypt-prod + type: string + version: + default: '14' + examples: + - '14' + type: string + sub-domain: + default: to-be-set + examples: + - to-be-set + type: string + ingress-class: + default: traefik + examples: + - traefik + type: string + databases: + default: [] + items: + properties: + name: + default: db + type: string + type: object + type: array + domain-name: + default: your_company.com + examples: + - your_company.com + type: string +dependencies: [] +providers: + kubernetes: true + authentik: null + kubectl: true + postgresql: true + restapi: null + http: null +tfaddtype: null diff --git a/share/dataset-pg/postgresql.tf b/share/dataset-pg/postgresql.tf new file mode 100644 index 0000000..31bd44b --- /dev/null +++ b/share/dataset-pg/postgresql.tf @@ -0,0 +1,79 @@ +locals { + pg-labels = merge(local.common-labels, { + "app.kubernetes.io/component" = "postgresql" + }) + pool-labels = merge(local.common-labels, { + "app.kubernetes.io/component" = "pg-pool" + }) +} + +resource "kubectl_manifest" "prj_pg" { + yaml_body = join("", concat([<<-EOF + apiVersion: postgresql.cnpg.io/v1 + kind: Cluster + metadata: + name: "${var.instance}-${var.component}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.pg-labels)} + spec: + instances: ${var.replicas} + storage: + size: "${var.storage}" + monitoring: + enablePodMonitor: true + EOF + ], var.backups.enable?[<<-EOF + backup: + barmanObjectStore: + destinationPath: "s3://${var.instance}-${var.namespace}/" + endpointURL: "${var.backups.endpoint}/barman" + s3Credentials: + accessKeyId: + name: "${var.backups.secret-name}" + key: "${var.backups.key-id-key}" + secretAccessKey: + name: "${var.backups.secret-name}" + key: "${var.backups.secret-key}" + EOF + ]:[""])) +} + +resource "kubectl_manifest" "prj_pg_backup" { + count = var.backups.enable ? 1:0 + yaml_body = <<-EOF + apiVersion: postgresql.cnpg.io/v1 + kind: ScheduledBackup + metadata: + name: "${var.instance}-${var.component}" + namespace: "${var.namespace}" + labels: ${jsonencode(local.pg-labels)} + spec: + schedule: "${var.backups.schedule.db}" + backupOwnerReference: self + cluster: + name: "${var.instance}-${var.component}" + EOF +} + +resource "kubectl_manifest" "prj_pg_pool" { + count = var.pool.enable ? 1:0 + 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}" + instances: 1 + type: rw + pgbouncer: + poolMode: session + parameters: + max_client_conn: "1000" + default_pool_size: "10" + EOF +} diff --git a/share/dataset-pg/roles.tf b/share/dataset-pg/roles.tf new file mode 100644 index 0000000..5218608 --- /dev/null +++ b/share/dataset-pg/roles.tf @@ -0,0 +1,41 @@ +locals { + sorted-roles = reverse(distinct(sort(var.roles))) +} + + +resource "kubectl_manifest" "db_secret" { + ignore_fields = ["metadata.annotations"] + count = length(local.sorted-roles) + yaml_body = <<-EOF + apiVersion: "secretgenerator.mittwald.de/v1alpha1" + kind: "StringSecret" + metadata: + name: "${var.instance}-${var.component}-role-${local.sorted-roles[count.index]}" + namespace: "${var.namespace}" + labels: ${jsonencode(merge(local.common-labels, {"app.kubernetes.io/component" = local.sorted-roles[count.index]}))} + spec: + forceRegenerate: false + data: + POSGRESQL_USERNAME: "${local.sorted-roles[count.index]}" + fields: + - fieldName: "POSGRESQL_PASSWORD" + length: "32" + EOF +} + +data "kubernetes_secret_v1" "password_get" { + depends_on = [ kubectl_manifest.db_secret ] + count = length(local.sorted-roles) + metadata { + name = "${var.instance}-${var.component}-role-${local.sorted-roles[count.index]}" + namespace = "${var.namespace}" + } +} + +resource "postgresql_role" "role" { + depends_on = [ kubectl_manifest.prj_pg ] + count = length(local.sorted-roles) + name = "${local.sorted-roles[count.index]}" + login = true + password = data.kubernetes_secret_v1.password_get[count.index].data["POSGRESQL_PASSWORD"] +} diff --git a/share/division/datas.tf b/share/division/datas.tf new file mode 100644 index 0000000..821c848 --- /dev/null +++ b/share/division/datas.tf @@ -0,0 +1,22 @@ +locals { + common-labels = { + "vynil.solidite.fr/owner-name" = var.instance + "vynil.solidite.fr/owner-namespace" = var.namespace + "vynil.solidite.fr/owner-category" = var.category + "vynil.solidite.fr/owner-component" = var.component + "app.kubernetes.io/managed-by" = "vynil" + "app.kubernetes.io/name" = var.component + "app.kubernetes.io/instance" = var.instance + } +} + +data "kubernetes_secret_v1" "authentik" { + metadata { + name = "authentik" + namespace = var.namespace + } +} + +data "kustomization_overlay" "data" { + resources = [] +} diff --git a/share/division/groups.tf b/share/division/groups.tf new file mode 100644 index 0000000..cf93d40 --- /dev/null +++ b/share/division/groups.tf @@ -0,0 +1,24 @@ +locals { + sorted-team-names = reverse(distinct(sort([ + for team in var.teams: team.name + ]))) + sorted-teams = flatten([ + for name in local.sorted-team-names: [ + for team in var.teams: + team if team.name == name + ] + ]) +} + +resource "authentik_group" "division" { + name = "div-${var.instance}" + parent = var.parent + attributes = jsonencode(merge([for app in var.apps: {"${app}" = true}])) +} + +resource "authentik_group" "teams" { + count = length(local.sorted-teams) + name = "team-${var.instance}-${local.sorted-teams[count.index].name}" + parent = "div-${var.instance}" + attributes = jsonencode(merge([for app in local.sorted-teams[count.index].apps: {"${app}" = true}])) +} diff --git a/share/division/index.yaml b/share/division/index.yaml new file mode 100644 index 0000000..4533c4d --- /dev/null +++ b/share/division/index.yaml @@ -0,0 +1,53 @@ +--- +apiVersion: vinyl.solidite.fr/v1beta1 +kind: Component +category: share +metadata: + name: division + description: null +options: + parent: + default: employes + enum: + - employes + - clients + - fournisseurs + examples: + - employes + type: string + teams: + default: [] + items: + properties: + apps: + default: [] + items: + type: string + type: array + name: + default: '' + type: string + type: object + type: array + domain: + default: your-company + examples: + - your-company + type: string + apps: + default: [] + items: + type: string + type: array +dependencies: +- dist: null + category: share + component: accounts-management +providers: + kubernetes: true + authentik: true + kubectl: true + postgresql: null + restapi: null + http: true +tfaddtype: null