diff --git a/.github/configs/helm-render-assertions/midaz.yaml b/.github/configs/helm-render-assertions/midaz.yaml new file mode 100644 index 000000000..749a40128 --- /dev/null +++ b/.github/configs/helm-render-assertions/midaz.yaml @@ -0,0 +1,241 @@ +# Render assertions for midaz — verified by `validate-helm-charts --render-gate`. +# Each scenario renders the chart with the base fixture values +# (.github/configs/helm-render-values/midaz.yaml) plus its `set` overrides, then +# asserts every `expect` substring is present (and every `absent` one is missing) in +# the rendered manifest. Overrides go through `helm --set`, so bare `false` / `0` +# reach the template as YAML boolean/number — exactly the presence-vs-truthiness case +# the productized helpers must preserve. +scenarios: + # RED 1 — the full legacy Service Discovery contract must survive, including the + # external endpoint WITHOUT an Ingress (on-prem) and the advanced tuning knobs. + - name: sd-legacy-full-contract-on-prem-no-ingress + set: + ledger.ingress.enabled: "false" + ledger.configmap.SD_ENABLED: "true" + ledger.configmap.SD_ADDRESS: "consul.onprem:8500" + ledger.configmap.SD_TLS: "true" + ledger.configmap.SD_WORKLOAD: "midaz-stg" + ledger.configmap.SD_EXTERNAL_ADDRESS: "https://ledger.onprem.local" + ledger.configmap.SD_EXTERNAL_PORT: "8443" + ledger.configmap.SD_DIAL_TIMEOUT: "5s" + ledger.configmap.SD_WATCH_WAIT_TIME: "10s" + ledger.configmap.SD_ALLOW_STALE: "false" + expect: + - 'SD_ADDRESS: "consul.onprem:8500"' + - 'SD_EXTERNAL_ADDRESS: "https://ledger.onprem.local"' + - 'SD_EXTERNAL_PORT: "8443"' + - 'SD_DIAL_TIMEOUT: "5s"' + - 'SD_WATCH_WAIT_TIME: "10s"' + - 'SD_ALLOW_STALE: "false"' + - 'SD_WORKLOAD: "midaz-stg"' + + # RED 2 — the documented per-component dedicated datastore mask must resolve + # (previously ignored because the helper only read .context.Values.datastores). + - name: datastore-dedicated-mask-ledger + set: + ledger.datastores.postgres.host: "ledger-dedicated-db" + expect: + - 'DB_ONBOARDING_HOST: "ledger-dedicated-db"' + - 'DB_TRANSACTION_HOST: "ledger-dedicated-db"' + + # RED 3 — streaming activates from legacy configmap.STREAMING_BROKERS (no global + # broker), and an explicit configmap boolean `false` wins over a global `true` + # (presence-based precedence, not sprig-default truthiness). + - name: streaming-configmap-activation-and-false-precedence + set: + global.streaming.tlsEnabled: "true" + ledger.configmap.STREAMING_ENABLED: "true" + ledger.configmap.STREAMING_BROKERS: "kafka.onprem:9092" + ledger.configmap.STREAMING_TLS_ENABLED: "false" + expect: + - 'STREAMING_BROKERS: "kafka.onprem:9092"' + - 'STREAMING_TLS_ENABLED: "false"' + + # Same presence-based precedence for multi-tenant: an explicit configmap boolean + # `false` for REDIS_TLS must survive a global `true`. + - name: multitenant-configmap-false-precedence + set: + global.multiTenant.url: "http://mt-svc:8080" + global.multiTenant.redisHost: "redis" + global.multiTenant.redisTls: "true" + ledger.multiTenant.enabled: "true" + ledger.configmap.MULTI_TENANT_REDIS_TLS: "false" + ledger.secrets.MULTI_TENANT_SERVICE_API_KEY: "sample" + expect: + - 'MULTI_TENANT_REDIS_TLS: "false"' + - 'MULTI_TENANT_ENABLED: "true"' + + # RED (SASL) — a SASL mechanism without a password is a boot-crashing config; + # the render must FAIL fast (lib-streaming requires username+password for SASL) + # rather than emit a Secret with no STREAMING_SASL_PASSWORD. + - name: streaming-sasl-without-password-fails + set: + ledger.configmap.STREAMING_ENABLED: "true" + ledger.configmap.STREAMING_BROKERS: "kafka:9092" + global.streaming.saslMechanism: "SCRAM-SHA-512" + global.streaming.saslUsername: "user" + global.streaming.tlsEnabled: "true" + expectRenderError: "STREAMING_SASL_PASSWORD" + + # ...and with the password supplied, the Ledger Secret renders it. + - name: streaming-sasl-with-password-renders-secret + set: + ledger.configmap.STREAMING_ENABLED: "true" + ledger.configmap.STREAMING_BROKERS: "kafka:9092" + global.streaming.saslMechanism: "SCRAM-SHA-512" + global.streaming.saslUsername: "user" + global.streaming.tlsEnabled: "true" + ledger.secrets.STREAMING_SASL_PASSWORD: "s3cr3t" + expect: + - 'STREAMING_SASL_PASSWORD:' + + # A SASL mechanism needs BOTH credentials — a mechanism + password but no username is + # a boot-crashing config; the render must FAIL fast (Ledger). + - name: streaming-sasl-without-username-fails-ledger + set: + ledger.configmap.STREAMING_ENABLED: "true" + ledger.configmap.STREAMING_BROKERS: "kafka:9092" + global.streaming.saslMechanism: "SCRAM-SHA-512" + ledger.secrets.STREAMING_SASL_PASSWORD: "s3cr3t" + expectRenderError: "STREAMING_SASL_USERNAME" + + # ...same for CRM. + - name: streaming-sasl-without-username-fails-crm + set: + crm.configmap.STREAMING_ENABLED: "true" + crm.configmap.STREAMING_BROKERS: "kafka:9092" + global.streaming.saslMechanism: "SCRAM-SHA-512" + crm.secrets.STREAMING_SASL_PASSWORD: "s3cr3t" + expectRenderError: "STREAMING_SASL_USERNAME" + + # CRM DEPLOYMENT_MODE knob (was ledger-only) — CRM can now go byoc/saas. + - name: crm-deployment-mode-knob + set: + crm.deploymentMode: "byoc" + expect: + - 'DEPLOYMENT_MODE: "byoc"' + + # CRM KMS AppRole — a Vault approle posture without the secret id is a boot-crashing + # config (app fails closed); the render must FAIL fast. + - name: crm-kms-approle-without-secret-id-fails + set: + crm.kms.vaultAuthMethod: "approle" + crm.kms.vaultRoleId: "role-abc" + expectRenderError: "KMS_VAULT_SECRET_ID" + + # ...and a missing role id also fails. + - name: crm-kms-approle-without-role-id-fails + set: + crm.kms.vaultAuthMethod: "approle" + crm.secrets.KMS_VAULT_SECRET_ID: "secret-xyz" + expectRenderError: "KMS_VAULT_ROLE_ID is required" + + # ...with both, the role id (ConfigMap) and secret id (Secret) render. + - name: crm-kms-approle-renders + set: + crm.kms.vaultAuthMethod: "approle" + crm.kms.vaultRoleId: "role-abc" + crm.secrets.KMS_VAULT_SECRET_ID: "secret-xyz" + expect: + - 'KMS_VAULT_ROLE_ID: "role-abc"' + - 'KMS_VAULT_SECRET_ID:' + + # PDB accepts an explicit 0 (maxUnavailable: 0 = block all voluntary disruptions). + - name: pdb-maxunavailable-zero + set: + ledger.pdb.enabled: "true" + ledger.pdb.maxUnavailable: "0" + expect: + - 'maxUnavailable: 0' + + # revisionHistoryLimit accepts an explicit 0. + - name: revision-history-limit-zero + set: + ledger.revisionHistoryLimit: "0" + expect: + - 'revisionHistoryLimit: 0' + + # SD_ENABLED alone (no address, no global.serviceDiscovery) must render the full SD + # contract with SD_ADDRESS defaulting to localhost:8500 (pre-productization parity). + - name: sd-enabled-alone-emits-full-contract + set: + ledger.configmap.SD_ENABLED: "true" + expect: + - 'SD_ADDRESS: "localhost:8500"' + - 'SD_TLS: "false"' + - 'SD_INTERNAL_SCHEME: "http"' + + # Multi-tenant circuit-breaker threshold accepts an explicit 0 (presence-based, not + # sprig-default) — proves the remaining MT tuning-key footgun is closed. + - name: multitenant-circuit-breaker-threshold-zero + set: + ledger.multiTenant.enabled: "true" + global.multiTenant.url: "http://mt-svc:8080" + global.multiTenant.redisHost: "redis" + ledger.secrets.MULTI_TENANT_SERVICE_API_KEY: "sample" + ledger.configmap.MULTI_TENANT_CIRCUIT_BREAKER_THRESHOLD: "0" + expect: + - 'MULTI_TENANT_CIRCUIT_BREAKER_THRESHOLD: "0"' + + # =========================================================================== + # SASL contract — lib-streaming fails closed; the render must fail fast. + # Three scenarios × two components (Ledger + CRM). + # =========================================================================== + # 1) Unsupported mechanism (only PLAIN / SCRAM-SHA-256 / SCRAM-SHA-512). + - name: streaming-sasl-invalid-mechanism-fails-ledger + set: + ledger.configmap.STREAMING_ENABLED: "true" + ledger.configmap.STREAMING_BROKERS: "kafka:9092" + ledger.configmap.STREAMING_SASL_MECHANISM: "GSSAPI" + ledger.configmap.STREAMING_SASL_USERNAME: "u" + ledger.configmap.STREAMING_TLS_ENABLED: "true" + ledger.secrets.STREAMING_SASL_PASSWORD: "s3cr3t" + expectRenderError: "Unsupported STREAMING_SASL_MECHANISM" + - name: streaming-sasl-invalid-mechanism-fails-crm + set: + crm.configmap.STREAMING_ENABLED: "true" + crm.configmap.STREAMING_BROKERS: "kafka:9092" + crm.configmap.STREAMING_SASL_MECHANISM: "GSSAPI" + crm.configmap.STREAMING_SASL_USERNAME: "u" + crm.configmap.STREAMING_TLS_ENABLED: "true" + crm.secrets.STREAMING_SASL_PASSWORD: "s3cr3t" + expectRenderError: "Unsupported STREAMING_SASL_MECHANISM" + + # 2) SASL without TLS and without the explicit plaintext opt-in. + - name: streaming-sasl-without-tls-fails-ledger + set: + ledger.configmap.STREAMING_ENABLED: "true" + ledger.configmap.STREAMING_BROKERS: "kafka:9092" + ledger.configmap.STREAMING_SASL_MECHANISM: "SCRAM-SHA-512" + ledger.configmap.STREAMING_SASL_USERNAME: "u" + ledger.configmap.STREAMING_TLS_ENABLED: "false" + ledger.secrets.STREAMING_SASL_PASSWORD: "s3cr3t" + expectRenderError: "SASL requires TLS" + - name: streaming-sasl-without-tls-fails-crm + set: + crm.configmap.STREAMING_ENABLED: "true" + crm.configmap.STREAMING_BROKERS: "kafka:9092" + crm.configmap.STREAMING_SASL_MECHANISM: "SCRAM-SHA-512" + crm.configmap.STREAMING_SASL_USERNAME: "u" + crm.configmap.STREAMING_TLS_ENABLED: "false" + crm.secrets.STREAMING_SASL_PASSWORD: "s3cr3t" + expectRenderError: "SASL requires TLS" + + # 3) useExistingSecret bypasses the Secret, but the username (a ConfigMap value) + # is still validated — a mechanism without a username must fail. + - name: streaming-sasl-existingsecret-without-username-fails-ledger + set: + ledger.useExistingSecret: "true" + ledger.configmap.STREAMING_ENABLED: "true" + ledger.configmap.STREAMING_BROKERS: "kafka:9092" + ledger.configmap.STREAMING_SASL_MECHANISM: "SCRAM-SHA-512" + ledger.configmap.STREAMING_TLS_ENABLED: "true" + expectRenderError: "STREAMING_SASL_USERNAME" + - name: streaming-sasl-existingsecret-without-username-fails-crm + set: + crm.useExistingSecret: "true" + crm.configmap.STREAMING_ENABLED: "true" + crm.configmap.STREAMING_BROKERS: "kafka:9092" + crm.configmap.STREAMING_SASL_MECHANISM: "SCRAM-SHA-512" + crm.configmap.STREAMING_TLS_ENABLED: "true" + expectRenderError: "STREAMING_SASL_USERNAME" diff --git a/.github/configs/helm-render-values/midaz.yaml b/.github/configs/helm-render-values/midaz.yaml index ada4f9033..547c6e687 100644 --- a/.github/configs/helm-render-values/midaz.yaml +++ b/.github/configs/helm-render-values/midaz.yaml @@ -1,8 +1,14 @@ crm: + # Render both components in the gate (CRM is off by default) so the CRM manifests + # are exercised too. + enabled: true secrets: # App crypto material — operator-provided (external boundary). LCRYPTO_HASH_SECRET_KEY: "sample" LCRYPTO_ENCRYPT_SECRET_KEY: "sample" + # RabbitMQ — operator-provided (same as ledger). + RABBITMQ_DEFAULT_PASS: "sample" + RABBITMQ_CONSUMER_PASS: "sample" ledger: secrets: # RabbitMQ — operator-provided; not yet single-sourced (see chart README diff --git a/.github/scripts/validate-helm-charts/main.go b/.github/scripts/validate-helm-charts/main.go index f4fc3987a..9607a83e4 100644 --- a/.github/scripts/validate-helm-charts/main.go +++ b/.github/scripts/validate-helm-charts/main.go @@ -62,6 +62,13 @@ var allowlistedCredentialDefaults = map[string]bool{ // plugin-access-manager ships a default initUser.adminPassword so existing releases keep // their admin login across upgrades; operators are expected to override it in production. "plugin-access-manager:auth.initUser.adminPassword": true, + // midaz bundles an optional dev-mode Vault (vault.enabled, default off / opt-in) so an + // operator can spin up a self-contained standalone stack like the compose infra. When + // enabled, its dev root token must be "root" to match the app's built-in DefaultVaultDevToken + // (KMS_VAULT_AUTH_METHOD=token, DEPLOYMENT_MODE=local); it is a throwaway in-memory dev token, + // not a real credential. Production leaves vault.enabled=false and points KMS_VAULT_ADDR at an + // external Vault. + "midaz:vault.server.dev.devRootToken": true, } type chartYAML struct { @@ -1196,12 +1203,31 @@ func buildRenderRows(root string, chartSelection map[string]bool, sampleValuesDi continue } + // Render assertions: when .github/configs/helm-render-assertions/.yaml + // exists, render each scenario (base template args + --set overrides) in the + // same isolated env and check expected/absent substrings. This turns the render + // gate from "does it template" into "does the documented contract actually + // render" — e.g. the full legacy SD contract, the dedicated datastore mask, and + // presence-based streaming/multi-tenant precedence (explicit false/0 survives). + assertDetail, assertFailed := runRenderAssertions(root, chartName, tmpRoot, tmpChart, helmEnv, templateArgs) + if assertFailed { + row.Status = "fail" + row.Class = "render-assertion" + row.Detail = appendDetail(row.Detail, assertDetail) + rows = append(rows, row) + _ = os.RemoveAll(tmpRoot) + continue + } + row.Status = "ok" row.Class = "render-ok" successDetail := "helm dependency build and helm template succeeded" if collapseDetail != "" { successDetail += "; " + collapseDetail } + if assertDetail != "" { + successDetail += "; " + assertDetail + } row.Detail = appendDetail(row.Detail, successDetail) rows = append(rows, row) _ = os.RemoveAll(tmpRoot) @@ -1219,6 +1245,106 @@ func appendDetail(existing, detail string) string { return existing + "; " + detail } +// renderAssertionScenario is one render-and-check case: render the chart with the +// base fixture values plus the Set overrides, then assert every Expect substring is +// present and every Absent substring is missing from the rendered manifest. +type renderAssertionScenario struct { + Name string `yaml:"name"` + Set map[string]string `yaml:"set"` + Expect []string `yaml:"expect"` + Absent []string `yaml:"absent"` + // ExpectRenderError, when set, inverts the scenario: the render MUST fail and its + // output must contain this substring (a fail-fast contract, e.g. SASL mechanism + // configured without a password). expect/absent are ignored in that case. + ExpectRenderError string `yaml:"expectRenderError"` +} + +type renderAssertionsFile struct { + Scenarios []renderAssertionScenario `yaml:"scenarios"` +} + +// loadRenderAssertions reads .github/configs/helm-render-assertions/.yaml. +// Returns (nil, nil) when the chart has no assertions file. +func loadRenderAssertions(root, chartName string) (*renderAssertionsFile, error) { + path := filepath.Join(root, ".github", "configs", "helm-render-assertions", chartName+".yaml") + if !fileExists(path) { + return nil, nil + } + data, err := os.ReadFile(path) + if err != nil { + return nil, err + } + var f renderAssertionsFile + dec := yaml.NewDecoder(strings.NewReader(string(data))) + dec.KnownFields(true) // reject unknown keys (e.g. a typo'd `expext:`) instead of silently ignoring them + if err := dec.Decode(&f); err != nil { + return nil, fmt.Errorf("parse %s: %w", path, err) + } + // Guard against a silently vacuous config: every scenario must have a name and at + // least one expect/absent check, so a scenario can never count as passed without + // actually asserting anything. + for i, sc := range f.Scenarios { + if sc.Name == "" { + return nil, fmt.Errorf("%s: scenario %d has no name", path, i) + } + if len(sc.Expect) == 0 && len(sc.Absent) == 0 && sc.ExpectRenderError == "" { + return nil, fmt.Errorf("%s: scenario %q has no expect/absent/expectRenderError checks", path, sc.Name) + } + } + return &f, nil +} + +// runRenderAssertions renders each scenario for a chart (base template args plus the +// scenario's --set overrides) in the already-materialized isolated workspace and +// checks its expect/absent substrings. Returns a detail string and whether any +// assertion failed. Values are passed via --set (not --set-string) so a scenario can +// exercise YAML boolean/number overrides (e.g. STREAMING_TLS_ENABLED=false), which is +// exactly the presence-vs-truthiness case these assertions guard. +func runRenderAssertions(root, chartName, workDir, chartPath string, env []string, baseArgs []string) (string, bool) { + af, err := loadRenderAssertions(root, chartName) + if err != nil { + return err.Error(), true + } + if af == nil || len(af.Scenarios) == 0 { + return "", false + } + for _, sc := range af.Scenarios { + args := append([]string{}, baseArgs...) + setKeys := make([]string, 0, len(sc.Set)) + for k := range sc.Set { + setKeys = append(setKeys, k) + } + sort.Strings(setKeys) + for _, k := range setKeys { + args = append(args, "--set", k+"="+sc.Set[k]) + } + out, err := runHelmWithEnv(workDir, env, args...) + if sc.ExpectRenderError != "" { + if err == nil { + return fmt.Sprintf("render assertion %q: expected render to fail with %q, but it succeeded", sc.Name, sc.ExpectRenderError), true + } + if !strings.Contains(out, sc.ExpectRenderError) { + return fmt.Sprintf("render assertion %q: expected render error to contain %q, got: %s", sc.Name, sc.ExpectRenderError, oneLine(out)), true + } + continue + } + if err != nil { + return fmt.Sprintf("render assertion %q: helm template failed: %s", sc.Name, oneLine(out)), true + } + for _, exp := range sc.Expect { + if !strings.Contains(out, exp) { + return fmt.Sprintf("render assertion %q: expected %q in render, not found", sc.Name, exp), true + } + } + for _, abs := range sc.Absent { + if strings.Contains(out, abs) { + return fmt.Sprintf("render assertion %q: expected %q to be absent, but it was rendered", sc.Name, abs), true + } + } + } + return fmt.Sprintf("%d render assertion(s) passed", len(af.Scenarios)), false +} + // bitnamiReleaseNames returns the release names that trigger the Bitnami // release-name collapse for a chart: one per bundled Bitnami subchart, using the // dependency alias when set (the alias is what common.names.dependency.fullname diff --git a/.github/workflows/helm-chart-standard.yml b/.github/workflows/helm-chart-standard.yml index 73dcf631c..74ccba4db 100644 --- a/.github/workflows/helm-chart-standard.yml +++ b/.github/workflows/helm-chart-standard.yml @@ -7,6 +7,7 @@ on: - 'docs/helm-chart-standard.md' - '.github/configs/helm-chart-standard-baseline.yaml' - '.github/configs/helm-render-values/**' + - '.github/configs/helm-render-assertions/**' - '.github/scripts/validate-helm-charts/**' - '.github/workflows/helm-chart-standard.yml' workflow_dispatch: @@ -57,7 +58,7 @@ jobs: changed_files="$(git diff --name-only "$BASE_SHA" "$HEAD_SHA")" - if grep -Eq '^(\.github/scripts/validate-helm-charts/|\.github/configs/helm-render-values/|\.github/workflows/helm-chart-standard\.yml|docs/helm-chart-standard\.md)' <<< "$changed_files"; then + if grep -Eq '^(\.github/scripts/validate-helm-charts/|\.github/configs/helm-render-values/|\.github/configs/helm-render-assertions/|\.github/workflows/helm-chart-standard\.yml|docs/helm-chart-standard\.md)' <<< "$changed_files"; then echo "args=--all" >> "$GITHUB_OUTPUT" exit 0 fi diff --git a/charts/midaz/Chart.lock b/charts/midaz/Chart.lock index 85b0e500c..acdcff0da 100644 --- a/charts/midaz/Chart.lock +++ b/charts/midaz/Chart.lock @@ -1,4 +1,7 @@ dependencies: +- name: lerian-common-helm + repository: file://../lerian-common + version: 1.3.4 - name: valkey repository: oci://registry-1.docker.io/bitnamicharts version: 2.4.7 @@ -11,5 +14,8 @@ dependencies: - name: rabbitmq repository: https://groundhog2k.github.io/helm-charts version: 2.1.11 -digest: sha256:2a2522d76f48d28c4bc86bd4c23226346e921eeeba11a217b89b5a71589a7f92 -generated: "2026-06-19T15:26:51.872268447-03:00" +- name: vault + repository: https://helm.releases.hashicorp.com + version: 0.28.1 +digest: sha256:fe18e24d4738faddbcb9242ccd235223f5806a2cda251a2582e4a3e000e016cc +generated: "2026-08-02T15:22:25.096489-03:00" diff --git a/charts/midaz/Chart.yaml b/charts/midaz/Chart.yaml index 623270eaf..e465261ff 100644 --- a/charts/midaz/Chart.yaml +++ b/charts/midaz/Chart.yaml @@ -28,6 +28,9 @@ icon: https://avatars.githubusercontent.com/u/148895005?s=200&v=4 # To understand the purpose and configuration of each dependency, visit: # https://docs.lerian.studio/docs/midaz-components dependencies: + - name: lerian-common-helm + version: "1.3.4" + repository: "file://../lerian-common" - name: valkey version: "2.4.7" repository: "oci://registry-1.docker.io/bitnamicharts" @@ -44,3 +47,7 @@ dependencies: version: "2.1.11" repository: "https://groundhog2k.github.io/helm-charts" condition: rabbitmq.enabled + - name: vault + version: "0.28.1" + repository: "https://helm.releases.hashicorp.com" + condition: vault.enabled diff --git a/charts/midaz/README.md b/charts/midaz/README.md index 3ac74cf09..ea7a3b6a7 100644 --- a/charts/midaz/README.md +++ b/charts/midaz/README.md @@ -1,5 +1,9 @@ # Midaz Helm Chart +## About Midaz + +Midaz is Lerian's high-performance, source-available double-entry accounting ledger — the financial engine the rest of the platform is built on. Its domain model is a strict hierarchy: **Organization → Ledger → Account → Transaction → Operation**. This chart is deployment- and configuration-focused; for the product concepts, APIs, and data model see the official documentation at [docs.lerian.studio/midaz](https://docs.lerian.studio/midaz). + ## Chart Contract - Chart type: `multi-component` @@ -16,7 +20,7 @@ Source code can be found here: * https://github.com/LerianStudio/helm/tree/main/charts/midaz * https://github.com/LerianStudio/midaz -This helm chart installs [Midaz](https://lerian.studio/midaz#about), a high-performance and open-source ledger. +This helm chart installs [Midaz](https://docs.lerian.studio/midaz), a high-performance and open-source ledger. The default installation is similar to the one provided in the [Midaz repo](https://github.com/LerianStudio/midaz?tab=readme-ov-file#quick-installation-guide-localhost). @@ -114,6 +118,27 @@ ingress: ``` +## Productized Configuration Model + +This chart exposes a **grouped, productized values API** for the knobs clients tune most, layered over a fully backward-compatible raw configmap. Three concepts drive it: + +1. **Env-wide contracts under `global.*`.** Cross-cutting integration config is declared once and shared by every component. The chart ships: + - `global.serviceDiscovery` — Consul address/TLS/workload/`preferView` for lib-service-discovery (`SD_*`). + - `global.streaming` — broker/SASL/TLS for lib-streaming (`STREAMING_*`). + - `global.multiTenant` — tenant-manager URL and its Redis (`MULTI_TENANT_*`). + + These, plus the datastore/auth/observability contracts consumed by the bundled `lerian-common-helm` library (`global.datastores`, `global.auth`, `global.observability`), are turned into environment variables by the `lerian-common.*` derivation helpers (`_service_discovery.tpl`, `_streaming.tpl`, `_multi_tenant.tpl`, `_datastore.tpl`, `_auth.tpl`, `_otel.tpl`). See the `lerian-common` chart for those contracts. + +2. **Per-component opt-in toggles.** Each env-wide feature is inert until the component enables it: `ledger.serviceDiscovery.enabled`, `ledger.streaming.enabled`, `ledger.multiTenant.enabled` (and the same under `crm.*`). This keeps configmaps clean and preserves backward compatibility — nothing is emitted for a feature that is off. + +3. **Grouped params per block.** Instead of raw env vars, tune clean nested params — e.g. `ledger.broker.*` (RabbitMQ client + routing + circuit breaker), `ledger.database.*` and `ledger.mongo.*` (per-module DB identity/pool), `ledger.redis.*`, `ledger.server.*`, `ledger.rateLimit.*`, `ledger.swagger.*`, and for the CRM `crm.kms.*` (Vault KMS), `crm.mongo.*`, and `crm.swagger.*`. Every block is documented inline in `values.yaml` with `# --` comments; consult it for the full key list and defaults. + +**Backward-compatibility (precedence).** Every `cfgValue`/`datastore`-derived key honors the legacy flat override: it resolves as **`.configmap.` → grouped param → default**, so a value set under `configmap.` always **wins** over the grouped param and the mask, and those installs render unchanged. This covers the datastore keys, the app/server/swagger/rate-limit/telemetry keys, the feature **toggles** (`MULTI_TENANT_ENABLED`, `SD_ENABLED`, `STREAMING_ENABLED`), and — as of this release — the whole **multi-tenant block** (`MULTI_TENANT_URL`, `MULTI_TENANT_REDIS_*`, circuit-breaker, etc.), which the `multiTenant.env` helper now reads from `.configmap`. + +> **Service Discovery / Streaming server keys follow the same precedence.** Their env is produced by the `serviceDiscovery.env` / `streaming.env` helpers, which now accept a `configmap` passthrough (lerian-common ≥ 1.2.2). The SD/streaming **server** configuration (Consul endpoint/TLS/workload, broker/SASL/TLS/compression/acks) resolves as **`.configmap.SD_*` / `configmap.STREAMING_*` → `global.serviceDiscovery` / `global.streaming` → default**, mirroring the multi-tenant block: a per-key `configmap.SD_*` / `configmap.STREAMING_*` value **wins** over the `global.*` contract. `global.*` (or `extraEnvVars`) remains the recommended way to set env-wide server config; the `configmap.*` passthrough preserves byte-identical backward-compat for installs that still pin these keys flatly. + +`configmap: {}` (empty by default) remains the legacy escape hatch for the `cfgValue`-backed surface — put any raw env var there to override it (e.g. `DB_ONBOARDING_HOST`, `REDIS_HOST`, `RABBITMQ_HOST`, `PLUGIN_AUTH_HOST`, `ENABLE_TELEMETRY`). + ## Midaz Components Midaz deploys the following core services: @@ -122,12 +147,12 @@ Midaz deploys the following core services: | Parameter | Description | Default Value | |---|---|---| -| `ledger.enabled` | Enable or disable the ledger service. | `false` | +| `ledger.enabled` | Enable or disable the ledger service. | `true` | | `ledger.name` | Service name. | `"ledger"` | | `ledger.replicaCount` | Number of replicas for the ledger service. | `1` | | `ledger.image.repository` | Repository for the ledger service container image. | `"lerianstudio/midaz-ledger"` | | `ledger.image.pullPolicy` | Image pull policy. | `"IfNotPresent"` | -| `ledger.image.tag` | Image tag used for deployment. | `"3.7.2"` | +| `ledger.image.tag` | Image tag used for deployment. | `"3.8.0"` | | `ledger.imagePullSecrets` | Secrets for pulling images from a private registry. | `[]` | | `ledger.nameOverride` | Overrides the default generated name by Helm. | `""` | | `ledger.fullnameOverride` | Overrides the full name generated by Helm. | `""` | @@ -140,7 +165,7 @@ Midaz deploys the following core services: | `ledger.pdb.annotations` | Annotations for the PodDisruptionBudget. | `{}` | | `ledger.deploymentUpdate.*` | Deployment update strategy. | See `values.yaml` | | `ledger.service.type` | Kubernetes service type. | `"ClusterIP"` | -| `ledger.service.port` | Port for the HTTP API. | `3000` | +| `ledger.service.port` | Port for the HTTP API. | `3002` | | `ledger.service.annotations` | Annotations for the service. | `{}` | | `ledger.ingress.enabled` | Specifies whether Ingress is enabled. | `false` | | `ledger.ingress.className` | Ingress class name. | `""` | @@ -218,7 +243,7 @@ For more details, refer to the official documentation: [CRM Documentation](https | `crm.replicaCount` | Number of replicas for the CRM service. | `1` | | `crm.image.repository` | Repository for the CRM service container image. | `"lerianstudio/midaz-crm"` | | `crm.image.pullPolicy` | Image pull policy. | `"Always"` | -| `crm.image.tag` | Image tag used for deployment. | `"3.7.2"` | +| `crm.image.tag` | Image tag used for deployment. | `"3.8.0"` | | `crm.imagePullSecrets` | Secrets for pulling images from a private registry. | `[]` | | `crm.nameOverride` | Overrides the default generated name by Helm. | `""` | | `crm.fullnameOverride` | Overrides the full name generated by Helm. | `""` | @@ -344,6 +369,13 @@ grafana: This chart includes the following dependencies for the default installation. All dependencies are enabled by default. +### lerian-common (library) + +- **Version:** pinned exactly to the `lerian-common` chart version the pipeline publishes (currently `1.3.4`) +- **Repository:** `file://../lerian-common` +- **How to disable:** Not disableable — it is a Helm **library** chart that renders no resources of its own. +- **Note:** Provides the derivation helpers that turn the `global.*` contracts (service discovery, streaming, multi-tenant, datastores, auth, observability) into component env vars. See the [Productized Configuration Model](#productized-configuration-model) section. + ### Valkey - **Version:** 2.4.7 @@ -554,6 +586,26 @@ ledger: RABBITMQ_PROTOCOL: "https" # was "http" ``` +### Vault (HashiCorp) + +- **Version:** 0.28.1 +- **Repository:** https://helm.releases.hashicorp.com +- **How to enable:** Set `vault.enabled` to `true` in the values file (default is `false`, opt-in). Enable it together with `crm.enabled=true` for the self-contained/dev stack. +- **Note:** OPT-IN and **off by default**. When enabled it provisions a bundled **dev-mode** Vault (in-memory, auto-unsealed, single node, preset root token `root`, listening on `:8200`), plus a `system:auth-delegator` ClusterRoleBinding and the transit-init Job — intended ONLY for the self-contained/dev stack, never for production. It is pinned to the Service name `midaz-hc-vault` via `fullnameOverride` and mirrors the `midaz-hc-vault` + `midaz-hc-vault-init` services in the midaz `docker-compose`, so the free/self-contained stack needs no external Vault. The Agent Injector, CSI provider, and UI are disabled. Because the default is `false`, existing environments that never mention `vault` render **zero** vault objects. +- A one-shot Job (`templates/vault/transit-init-job.yaml`, gated on `vault.enabled`) enables the **Transit** secrets engine used for CRM envelope encryption (KMS), at the mode-derived mount `transit-st` (single-tenant) or `transit-mt` (when multi-tenancy is on). It is idempotent. +- The CRM KMS default `crm.kms.vaultAddr` (`KMS_VAULT_ADDR`) is `http://midaz-hc-vault:8200`, resolving against the bundled Vault with no extra config. +- **Production:** leave `vault.enabled=false` (the default) and point `crm.kms.vaultAddr` (`KMS_VAULT_ADDR`) at an external Vault, switching `crm.kms.vaultAuthMethod` away from `token` (e.g. `approle`) as appropriate. + + ```yaml + vault: + enabled: false # disable the bundled dev Vault to use an external one + + crm: + kms: + vaultAddr: "https://vault.example.com:8200" # → KMS_VAULT_ADDR + vaultAuthMethod: "approle" # → KMS_VAULT_AUTH_METHOD + ``` + ### OpenTelemetry Collector wiring Since `8.4.0` the `otel-collector-lerian` subchart is no longer installed as a dependency of this chart (the dependency was removed; see [`UPGRADE-8.4.md`](docs/UPGRADE-8.4.md) for the migration story). The `otel-collector-lerian.enabled` flag now only controls whether `HOST_IP`, `POD_IP`, `OTEL_EXPORTER_OTLP_ENDPOINT=$(HOST_IP):4317` and `OTEL_RESOURCE_ATTRIBUTES=k8s.pod.ip=$(POD_IP)` are injected into the `ledger` and `crm` deployments. diff --git a/charts/midaz/templates/crm/configmap.yaml b/charts/midaz/templates/crm/configmap.yaml index 50d8ba5dd..9e1b58e9b 100644 --- a/charts/midaz/templates/crm/configmap.yaml +++ b/charts/midaz/templates/crm/configmap.yaml @@ -1,4 +1,10 @@ {{- if .Values.crm.enabled }} +{{- /* Resolve the environment name ONCE and reuse it for every environment-dependent + key (ENV_NAME, OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT) so they can never drift apart. + Cascade (highest precedence first): configmap.ENV_NAME (legacy) -> crm.env (param) + -> global.environment (env-wide) -> "development" (component default). global.environment + slots just above the hardcoded default via cfgValue's `default` argument. */ -}} +{{- $env := include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "ENV_NAME" "params" .Values.crm "field" "env" "default" (default "development" (.Values.global | default dict).environment)) }} apiVersion: v1 kind: ConfigMap metadata: @@ -7,75 +13,97 @@ metadata: {{- include "midaz-crm.labels" (dict "context" . "name" .Values.crm.name ) | nindent 4 }} data: # -- Default Environment variables for CRM - ALLOW_INSECURE_TLS: {{ .Values.crm.configmap.ALLOW_INSECURE_TLS | default "true" | quote }} - ENV_NAME: {{ .Values.crm.configmap.ENV_NAME | default "development" | quote }} + ALLOW_INSECURE_TLS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "ALLOW_INSECURE_TLS" "params" .Values.crm "field" "allowInsecureTls" "default" "true") | quote }} + ENV_NAME: {{ $env | quote }} # APP - APPLICATION_NAME: {{ .Values.crm.configmap.APPLICATION_NAME | default "crm" | quote }} - SERVER_PORT: {{ .Values.crm.configmap.SERVER_PORT | default "4003" | quote }} - SERVER_ADDRESS: {{ .Values.crm.configmap.SERVER_ADDRESS | default ":4003" | quote }} + APPLICATION_NAME: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "APPLICATION_NAME" "params" .Values.crm "field" "applicationName" "default" "crm") | quote }} + SERVER_PORT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "SERVER_PORT" "params" .Values.crm.server "field" "port" "default" "4003") | quote }} + SERVER_ADDRESS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "SERVER_ADDRESS" "params" .Values.crm.server "field" "address" "default" ":4003") | quote }} VERSION: {{ .Values.crm.image.tag | default .Chart.AppVersion | quote }} # Mongo DB - MONGO_URI: {{ .Values.crm.configmap.MONGO_URI | default "mongodb" | quote }} - MONGO_HOST: {{ .Values.crm.configmap.MONGO_HOST | default "midaz-mongodb" | quote }} - MONGO_PORT: {{ .Values.crm.configmap.MONGO_PORT | default "27017" | quote }} - MONGO_NAME: {{ .Values.crm.configmap.MONGO_NAME | default "crm" | quote }} - MONGO_USER: {{ .Values.crm.configmap.MONGO_USER | default "midaz" | quote }} - MONGO_MAX_POOL_SIZE: {{ .Values.crm.configmap.MONGO_MAX_POOL_SIZE | default "1000" | quote }} - MONGO_PARAMETERS: {{ .Values.crm.configmap.MONGO_PARAMETERS | default "" | quote }} - MONGO_TLS_CA_CERT: {{ .Values.crm.configmap.MONGO_TLS_CA_CERT | default "" | quote }} + MONGO_URI: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "MONGO_URI" "params" .Values.crm.mongo "field" "uri" "default" "mongodb") | quote }} + MONGO_HOST: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.crm.datastores "configmap" .Values.crm.configmap "type" "mongo" "field" "host" "nativeKey" "MONGO_HOST" "default" "midaz-mongodb") | quote }} + MONGO_PORT: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.crm.datastores "configmap" .Values.crm.configmap "type" "mongo" "field" "port" "nativeKey" "MONGO_PORT" "default" "27017") | quote }} + MONGO_NAME: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "MONGO_NAME" "params" .Values.crm.mongo "field" "name" "default" "crm") | quote }} + MONGO_USER: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.crm.datastores "configmap" .Values.crm.configmap "type" "mongo" "field" "user" "nativeKey" "MONGO_USER" "default" "midaz") | quote }} + MONGO_MAX_POOL_SIZE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "MONGO_MAX_POOL_SIZE" "params" .Values.crm.mongo "field" "maxPoolSize" "default" "1000") | quote }} + MONGO_PARAMETERS: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.crm.datastores "configmap" .Values.crm.configmap "type" "mongo" "field" "params" "nativeKey" "MONGO_PARAMETERS" "default" "") | quote }} + MONGO_TLS_CA_CERT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "MONGO_TLS_CA_CERT" "params" .Values.crm.mongo "field" "tlsCaCert" "default" "") | quote }} # SWAGGER - SWAGGER_TITLE: {{ .Values.crm.configmap.SWAGGER_TITLE | default "CRM" | quote }} - SWAGGER_DESCRIPTION: {{ .Values.crm.configmap.SWAGGER_DESCRIPTION | default "The CRM API provides a set of endpoints for managing holder data, including information related to their ledger accounts." | quote }} + SWAGGER_TITLE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "SWAGGER_TITLE" "params" .Values.crm.swagger "field" "title" "default" "CRM") | quote }} + SWAGGER_DESCRIPTION: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "SWAGGER_DESCRIPTION" "params" .Values.crm.swagger "field" "description" "default" "The CRM API provides a set of endpoints for managing holder data, including information related to their ledger accounts.") | quote }} SWAGGER_VERSION: {{ .Values.crm.image.tag | default .Chart.AppVersion | quote }} - SWAGGER_HOST: {{ .Values.crm.configmap.SWAGGER_HOST | default ":4003" | quote }} - SWAGGER_BASE_PATH: {{ .Values.crm.configmap.SWAGGER_BASE_PATH | default "/" | quote }} - SWAGGER_SCHEMES: {{ .Values.crm.configmap.SWAGGER_SCHEMES | default "http" | quote }} - SWAGGER_LEFT_DELIMITER: {{ .Values.crm.configmap.SWAGGER_LEFT_DELIMITER | default "{{" | quote }} - SWAGGER_RIGHT_DELIMITER: {{ .Values.crm.configmap.SWAGGER_RIGHT_DELIMITER | default "}}" | quote }} + SWAGGER_HOST: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "SWAGGER_HOST" "params" .Values.crm.swagger "field" "host" "default" ":4003") | quote }} + SWAGGER_BASE_PATH: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "SWAGGER_BASE_PATH" "params" .Values.crm.swagger "field" "basePath" "default" "/") | quote }} + SWAGGER_SCHEMES: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "SWAGGER_SCHEMES" "params" .Values.crm.swagger "field" "schemes" "default" "http") | quote }} + SWAGGER_LEFT_DELIMITER: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "SWAGGER_LEFT_DELIMITER" "params" .Values.crm.swagger "field" "leftDelim" "default" "{{") | quote }} + SWAGGER_RIGHT_DELIMITER: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "SWAGGER_RIGHT_DELIMITER" "params" .Values.crm.swagger "field" "rightDelim" "default" "}}") | quote }} # LOG LEVEL - LOG_LEVEL: {{ .Values.crm.configmap.LOG_LEVEL | default "debug" | quote }} + LOG_LEVEL: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "LOG_LEVEL" "params" .Values.crm "field" "logLevel" "default" "debug") | quote }} # OPEN TELEMETRY - OTEL_RESOURCE_SERVICE_NAME: {{ .Values.crm.configmap.OTEL_RESOURCE_SERVICE_NAME | default "midaz-crm" | quote }} - OTEL_LIBRARY_NAME: {{ .Values.crm.configmap.OTEL_LIBRARY_NAME | default "github.com/LerianStudio/plugin-crm" | quote }} - OTEL_RESOURCE_SERVICE_VERSION: {{ .Values.crm.image.tag | default .Chart.AppVersion | quote }} - OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT: {{ .Values.crm.configmap.ENV_NAME | default "development" | quote }} - OTEL_EXPORTER_OTLP_ENDPOINT_PORT: {{ .Values.crm.configmap.OTEL_EXPORTER_OTLP_ENDPOINT_PORT | default "4317" | quote }} - OTEL_EXPORTER_OTLP_ENDPOINT: {{ .Values.crm.configmap.OTEL_EXPORTER_OTLP_ENDPOINT | default "midaz-grafana:4317" | quote }} - ENABLE_TELEMETRY: {{ .Values.crm.configmap.ENABLE_TELEMETRY | default "true" | quote }} + {{- /* Static/local OTEL identity keys stay flat (configmap override or default). */ -}} + {{- include "lerian-common.otel.envFlat" (dict + "configmap" .Values.crm.configmap + "keys" (list "OTEL_RESOURCE_SERVICE_NAME" "OTEL_LIBRARY_NAME" "OTEL_RESOURCE_SERVICE_VERSION" "OTEL_EXPORTER_OTLP_ENDPOINT_PORT") + "defaults" (dict + "OTEL_RESOURCE_SERVICE_NAME" "midaz-crm" + "OTEL_LIBRARY_NAME" "github.com/LerianStudio/plugin-crm" + "OTEL_RESOURCE_SERVICE_VERSION" (.Values.crm.image.tag | default .Chart.AppVersion))) | nindent 2 }} + {{/* Env-wide observability contract: endpoint, deployment env and enable derive + from global.observability with configmap. winning. The deployment-env + default is $env (global.environment), so it stays single-sourced with ENV_NAME + unless global.observability.deploymentEnvironment (or configmap) overrides it. */ -}} + OTEL_EXPORTER_OTLP_ENDPOINT: {{ include "lerian-common.globalValue" (dict "context" $ "configmap" .Values.crm.configmap "block" "observability" "field" "otlpEndpoint" "nativeKey" "OTEL_EXPORTER_OTLP_ENDPOINT" "default" "midaz-grafana:4317") | quote }} + OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT: {{ include "lerian-common.globalValue" (dict "context" $ "configmap" .Values.crm.configmap "block" "observability" "field" "deploymentEnvironment" "nativeKey" "OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT" "default" $env) | quote }} + ENABLE_TELEMETRY: {{ include "lerian-common.globalValue" (dict "context" $ "configmap" .Values.crm.configmap "block" "observability" "field" "enabled" "nativeKey" "ENABLE_TELEMETRY" "default" (include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "ENABLE_TELEMETRY" "params" .Values.crm.telemetry "field" "enabled" "default" "true"))) | quote }} # AUTH - PLUGIN_AUTH_ADDRESS: {{ .Values.crm.configmap.PLUGIN_AUTH_ADDRESS | default "plugin-auth" | quote }} - PLUGIN_AUTH_ENABLED: {{ .Values.crm.configmap.PLUGIN_AUTH_ENABLED | default "false" | quote }} + PLUGIN_AUTH_ADDRESS: {{ include "lerian-common.globalValue" (dict "context" $ "configmap" .Values.crm.configmap "block" "auth" "field" "host" "nativeKey" "PLUGIN_AUTH_ADDRESS" "default" "http://plugin-access-manager-auth:4000") | quote }} + PLUGIN_AUTH_ENABLED: {{ include "lerian-common.globalValue" (dict "context" $ "configmap" .Values.crm.configmap "block" "auth" "field" "enabled" "nativeKey" "PLUGIN_AUTH_ENABLED" "default" "false") | quote }} - # MULTI-TENANT - MULTI_TENANT_ENABLED: {{ .Values.crm.configmap.MULTI_TENANT_ENABLED | default "false" | quote }} + # MULTI-TENANT — crm exposes only the toggle (no MT client config on this component) + # Legacy configmap.MULTI_TENANT_ENABLED wins over the grouped param (presence-based). + MULTI_TENANT_ENABLED: {{ eq (include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "MULTI_TENANT_ENABLED" "params" .Values.crm.multiTenant "field" "enabled" "default" "false")) "true" | quote }} - KMS_VAULT_ADDR: {{ .Values.crm.configmap.KMS_VAULT_ADDR | default "http://midaz-hc-vault:8200" | quote }} - KMS_VAULT_AUTH_METHOD: {{ .Values.crm.configmap.KMS_VAULT_AUTH_METHOD | default "token" | quote }} - KMS_VENDOR: {{ .Values.crm.configmap.KMS_VENDOR | default "hashicorp-vault" | quote }} - SD_ADDRESS: {{ .Values.crm.configmap.SD_ADDRESS | default "localhost:8500" | quote }} - SD_ALLOW_STALE: {{ .Values.crm.configmap.SD_ALLOW_STALE | default "" | quote }} - SD_DIAL_TIMEOUT: {{ .Values.crm.configmap.SD_DIAL_TIMEOUT | default "" | quote }} - SD_ENABLED: {{ .Values.crm.configmap.SD_ENABLED | default "false" | quote }} - SD_EXTERNAL_ADDRESS: {{ .Values.crm.configmap.SD_EXTERNAL_ADDRESS | default "" | quote }} - SD_EXTERNAL_PORT: {{ .Values.crm.configmap.SD_EXTERNAL_PORT | default "" | quote }} - SD_INTERNAL_ADDRESS: {{ .Values.crm.configmap.SD_INTERNAL_ADDRESS | default "" | quote }} - SD_INTERNAL_PORT: {{ .Values.crm.configmap.SD_INTERNAL_PORT | default "" | quote }} - SD_INTERNAL_SCHEME: {{ .Values.crm.configmap.SD_INTERNAL_SCHEME | default "" | quote }} - SD_PREFER_VIEW: {{ .Values.crm.configmap.SD_PREFER_VIEW | default "" | quote }} - SD_RESPONSE_HEADER_TIMEOUT: {{ .Values.crm.configmap.SD_RESPONSE_HEADER_TIMEOUT | default "" | quote }} - SD_SEED_TIMEOUT: {{ .Values.crm.configmap.SD_SEED_TIMEOUT | default "" | quote }} - SD_TLS: {{ .Values.crm.configmap.SD_TLS | default "false" | quote }} - SD_TLS_HANDSHAKE_TIMEOUT: {{ .Values.crm.configmap.SD_TLS_HANDSHAKE_TIMEOUT | default "" | quote }} - SD_TLS_SKIP_VERIFY: {{ .Values.crm.configmap.SD_TLS_SKIP_VERIFY | default "false" | quote }} - SD_WATCH_WAIT_TIME: {{ .Values.crm.configmap.SD_WATCH_WAIT_TIME | default "" | quote }} - SD_WORKLOAD: {{ .Values.crm.configmap.SD_WORKLOAD | default "" | quote }} - STREAMING_ENABLED: {{ .Values.crm.configmap.STREAMING_ENABLED | default "false" | quote }} + DEPLOYMENT_MODE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "DEPLOYMENT_MODE" "params" .Values.crm "field" "deploymentMode" "default" "local") | quote }} + KMS_VAULT_ADDR: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "KMS_VAULT_ADDR" "params" .Values.crm.kms "field" "vaultAddr" "default" "http://midaz-hc-vault:8200") | quote }} + KMS_VAULT_AUTH_METHOD: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "KMS_VAULT_AUTH_METHOD" "params" .Values.crm.kms "field" "vaultAuthMethod" "default" "token") | quote }} + KMS_VENDOR: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "KMS_VENDOR" "params" .Values.crm.kms "field" "vendor" "default" "hashicorp-vault") | quote }} + {{- /* AppRole role id is a non-secret identifier (the secret id lives in the Secret). + Emitted only when set so token-auth deploys stay clean. */ -}} + {{- $kmsRoleId := include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "KMS_VAULT_ROLE_ID" "params" .Values.crm.kms "field" "vaultRoleId" "default" "") }} + {{- if $kmsRoleId }} + KMS_VAULT_ROLE_ID: {{ $kmsRoleId | quote }} + {{- end }} + {{- /* Legacy configmap.SD_ENABLED wins over the grouped param (presence-based); + compare to "true" so a legacy "false" string does not evaluate truthy. Same + resolved bool gates both the env var and the helper. */ -}} + {{- $sdEnabled := eq (include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "SD_ENABLED" "params" .Values.crm.serviceDiscovery "field" "enabled" "default" "false")) "true" }} + SD_ENABLED: {{ $sdEnabled | quote }} + {{- include "lerian-common.serviceDiscovery.env" (dict + "context" $ + "enabled" $sdEnabled + "configmap" .Values.crm.configmap + "name" (include "midaz-crm.fullname" .) + "port" .Values.crm.service.port + "namespace" (include "global.namespace" .) + "ingressHost" (include "lerian-common.firstIngressHost" (dict "ingress" .Values.crm.ingress))) | nindent 2 }} + {{- /* Legacy configmap.STREAMING_ENABLED wins over the grouped param + (presence-based); compare to "true" so a legacy "false" string does not + evaluate truthy. Same resolved bool gates both the env var and the helper. */ -}} + {{- $streamingEnabled := eq (include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "STREAMING_ENABLED" "params" .Values.crm.streaming "field" "enabled" "default" "false")) "true" }} + STREAMING_ENABLED: {{ $streamingEnabled | quote }} + {{- include "lerian-common.streaming.env" (dict + "context" $ + "enabled" $streamingEnabled + "configmap" .Values.crm.configmap + "clientId" (include "midaz-crm.fullname" .) + "cloudeventsSource" "lerian.midaz.crm") | nindent 2 }} # Extra Env Vars {{- with .Values.crm.extraEnvVars }} {{- toYaml . | nindent 2 }} diff --git a/charts/midaz/templates/crm/deployment.yaml b/charts/midaz/templates/crm/deployment.yaml index 6e8a82b00..1c7e0ae05 100644 --- a/charts/midaz/templates/crm/deployment.yaml +++ b/charts/midaz/templates/crm/deployment.yaml @@ -6,7 +6,7 @@ metadata: labels: {{- include "midaz-crm.labels" (dict "context" . "name" .Values.crm.name ) | nindent 4 }} spec: - revisionHistoryLimit: {{ .Values.crm.revisionHistoryLimit | default 10 }} + revisionHistoryLimit: {{ dig "revisionHistoryLimit" 10 .Values.crm }} strategy: type: {{ .Values.crm.deploymentUpdate.type }} {{- if eq .Values.crm.deploymentUpdate.type "RollingUpdate" }} diff --git a/charts/midaz/templates/crm/ingress.yaml b/charts/midaz/templates/crm/ingress.yaml index 7113b2efa..ce519410d 100644 --- a/charts/midaz/templates/crm/ingress.yaml +++ b/charts/midaz/templates/crm/ingress.yaml @@ -1,61 +1,12 @@ +{{- $gIng := (.Values.global | default dict).ingress | default dict -}} {{- if and .Values.crm.enabled .Values.crm.ingress.enabled -}} -{{- $fullName := include "midaz-crm.fullname" . -}} -{{- $svcPort := .Values.crm.service.port -}} -{{- if and .Values.crm.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.crm.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.crm.ingress.annotations "kubernetes.io/ingress.class" .Values.crm.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "midaz-crm.labels" (dict "context" . "name" .Values.crm.name ) | nindent 4 }} - {{- with .Values.crm.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.crm.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.crm.ingress.className }} - {{- end }} - {{- if .Values.crm.ingress.tls }} - tls: - {{- range .Values.crm.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.crm.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} +{{- include "lerian-common.ingress" (dict + "context" . + "ingress" .Values.crm.ingress + "global" $gIng + "subdomain" "crm" + "name" (include "midaz-crm.fullname" .) + "labels" (include "midaz-crm.labels" (dict "context" . "name" .Values.crm.name)) + "svcPort" .Values.crm.service.port + ) }} {{- end }} diff --git a/charts/midaz/templates/crm/pdb.yaml b/charts/midaz/templates/crm/pdb.yaml index 404862d55..30d8d01f3 100644 --- a/charts/midaz/templates/crm/pdb.yaml +++ b/charts/midaz/templates/crm/pdb.yaml @@ -12,10 +12,14 @@ metadata: {{- end }} {{- end }} spec: - {{- with .Values.crm.pdb.maxUnavailable }} - maxUnavailable: {{ . }} + {{- /* Presence-based so an explicit 0 (maxUnavailable: 0 = block all disruptions, + minAvailable: 0 = allow all) is honored; a PDB must carry exactly one of the two. */ -}} + {{- if hasKey .Values.crm.pdb "maxUnavailable" }} + maxUnavailable: {{ .Values.crm.pdb.maxUnavailable }} + {{- else if hasKey .Values.crm.pdb "minAvailable" }} + minAvailable: {{ .Values.crm.pdb.minAvailable }} {{- else }} - minAvailable: {{ .Values.crm.pdb.minAvailable | default 1 }} + minAvailable: 1 {{- end }} selector: matchLabels: diff --git a/charts/midaz/templates/crm/secrets.yaml b/charts/midaz/templates/crm/secrets.yaml index c9c158295..27f845909 100644 --- a/charts/midaz/templates/crm/secrets.yaml +++ b/charts/midaz/templates/crm/secrets.yaml @@ -33,10 +33,32 @@ data: # ============================================================================= # STREAMING SECRETS (lib-streaming — SASL password + TLS CA, operator-provided) # ============================================================================= - {{- if .Values.crm.secrets.STREAMING_SASL_PASSWORD }} - STREAMING_SASL_PASSWORD: {{ .Values.crm.secrets.STREAMING_SASL_PASSWORD | b64enc | quote }} + {{- /* SASL password is REQUIRED when a SASL mechanism is configured; the helper + fails the render otherwise (lib-streaming needs username+password for SASL), so a + boot-crashing config is caught at deploy time. Mechanism precedence mirrors the + ConfigMap: configmap.STREAMING_SASL_MECHANISM -> global.streaming.saslMechanism. */ -}} + {{- $streamingEnabled := eq (include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "STREAMING_ENABLED" "params" .Values.crm.streaming "field" "enabled" "default" "false")) "true" }} + {{- $saslMech := (((.Values.global | default dict).streaming | default dict).saslMechanism | default "") }} + {{- if hasKey .Values.crm.configmap "STREAMING_SASL_MECHANISM" }}{{- $saslMech = index .Values.crm.configmap "STREAMING_SASL_MECHANISM" }}{{- end }} + {{- $saslUser := (((.Values.global | default dict).streaming | default dict).saslUsername | default "") }} + {{- if hasKey .Values.crm.configmap "STREAMING_SASL_USERNAME" }}{{- $saslUser = index .Values.crm.configmap "STREAMING_SASL_USERNAME" }}{{- end }} + {{- with (include "lerian-common.streaming.secret" (dict "context" $ "enabled" $streamingEnabled "useExistingSecret" .Values.crm.useExistingSecret "secrets" .Values.crm.secrets "mode" "data" "saslMechanism" $saslMech "saslUsername" $saslUser "valuesPrefix" "crm.secrets." "secretName" (include "midaz-crm.fullname" .))) }} + {{- . | nindent 2 }} {{- end }} - {{- if .Values.crm.secrets.STREAMING_TLS_CA_CERT }} - STREAMING_TLS_CA_CERT: {{ .Values.crm.secrets.STREAMING_TLS_CA_CERT | b64enc | quote }} + + # ============================================================================= + # KMS — HashiCorp Vault AppRole secret id (operator-provided) + # ============================================================================= + {{- /* AppRole auth needs both KMS_VAULT_ROLE_ID (ConfigMap) and KMS_VAULT_SECRET_ID + (Secret). The app fails closed without them, and token auth is rejected in + saas/byoc — so fail the render fast when vendor=hashicorp-vault + approle and + either is missing, instead of shipping a boot-crashing config. */ -}} + {{- $kmsVendor := include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "KMS_VENDOR" "params" .Values.crm.kms "field" "vendor" "default" "hashicorp-vault") }} + {{- $kmsAuth := include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "KMS_VAULT_AUTH_METHOD" "params" .Values.crm.kms "field" "vaultAuthMethod" "default" "token") }} + {{- if and (eq $kmsVendor "hashicorp-vault") (eq $kmsAuth "approle") }} + {{- $kmsRoleId := include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "KMS_VAULT_ROLE_ID" "params" .Values.crm.kms "field" "vaultRoleId" "default" "") }} + {{- if not $kmsRoleId }}{{ fail "\n[midaz-crm] KMS_VAULT_ROLE_ID is required when KMS_VENDOR=hashicorp-vault and KMS_VAULT_AUTH_METHOD=approle (set crm.kms.vaultRoleId or crm.configmap.KMS_VAULT_ROLE_ID)" }}{{- end }} + {{- if not .Values.crm.secrets.KMS_VAULT_SECRET_ID }}{{ fail "\n[midaz-crm] Secret value required but empty: KMS_VAULT_SECRET_ID (KMS AppRole auth) — set crm.secrets.KMS_VAULT_SECRET_ID or configure an existingSecret" }}{{- end }} + KMS_VAULT_SECRET_ID: {{ .Values.crm.secrets.KMS_VAULT_SECRET_ID | b64enc | quote }} {{- end }} {{- end }} diff --git a/charts/midaz/templates/ledger/configmap.yaml b/charts/midaz/templates/ledger/configmap.yaml index 547708f7d..787ec0cb4 100644 --- a/charts/midaz/templates/ledger/configmap.yaml +++ b/charts/midaz/templates/ledger/configmap.yaml @@ -1,7 +1,7 @@ {{- if .Values.ledger.enabled }} -{{- $rabbitHost := .Values.ledger.configmap.RABBITMQ_HOST | default "midaz-rabbitmq" }} -{{- $rabbitProtocol := .Values.ledger.configmap.RABBITMQ_PROTOCOL | default "http" }} -{{- $rabbitAmqpPort := .Values.ledger.configmap.RABBITMQ_PORT_AMQP | default "15672" }} +{{- $rabbitHost := include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "broker" "field" "host" "nativeKey" "RABBITMQ_HOST" "default" "midaz-rabbitmq") }} +{{- $rabbitProtocol := include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_PROTOCOL" "params" .Values.ledger.broker "field" "protocol" "default" "http") }} +{{- $rabbitAmqpPort := include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_PORT_AMQP" "params" .Values.ledger.broker "field" "portAmqp" "default" "15672") }} {{- $rabbitHealthCheckURL := "" }} @@ -11,6 +11,25 @@ {{- $rabbitHealthCheckURL = printf "%s://%s" $rabbitProtocol $rabbitHost }} {{- end }} +{{- /* Guard nested database/mongo sections so a nil parent (operator omitting + the whole `database`/`mongo` block) yields the default instead of a hard + template error — same presence-safe style cfgValue applies to its `params`. */ -}} +{{- $ledgerDb := .Values.ledger.database | default dict }} +{{- $ledgerMongo := .Values.ledger.mongo | default dict }} +{{- $dbOnboarding := $ledgerDb.onboarding | default dict }} +{{- $dbTransaction := $ledgerDb.transaction | default dict }} +{{- $mongoOnboarding := $ledgerMongo.onboarding | default dict }} +{{- $mongoTransaction := $ledgerMongo.transaction | default dict }} +{{- $ledgerBroker := .Values.ledger.broker | default dict }} +{{- $brokerCircuitBreaker := $ledgerBroker.circuitBreaker | default dict }} + +{{- /* Resolve the environment name ONCE and reuse it for every environment-dependent + key (ENV_NAME, OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT) so they can never drift apart. + Cascade (highest precedence first): configmap.ENV_NAME (legacy) -> ledger.env (param) + -> global.environment (env-wide) -> "production" (component default). global.environment + slots just above the hardcoded default via cfgValue's `default` argument. */ -}} +{{- $env := include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "ENV_NAME" "params" .Values.ledger "field" "env" "default" (default "production" (.Values.global | default dict).environment)) }} + kind: ConfigMap apiVersion: v1 metadata: @@ -19,218 +38,230 @@ data: # ============================================================================= # APP CONFIGURATION # ============================================================================= - ALLOW_INSECURE_TLS: {{ .Values.ledger.configmap.ALLOW_INSECURE_TLS | default "true" | quote }} - ENV_NAME: {{ .Values.ledger.configmap.ENV_NAME | default "production" | quote }} + ALLOW_INSECURE_TLS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "ALLOW_INSECURE_TLS" "params" .Values.ledger "field" "allowInsecureTls" "default" "true") | quote }} + ENV_NAME: {{ $env | quote }} VERSION: {{ .Values.ledger.image.tag | default .Chart.AppVersion | quote }} - DEPLOYMENT_MODE: {{ .Values.ledger.configmap.DEPLOYMENT_MODE | default "local" | quote }} - LOG_LEVEL: {{ .Values.ledger.configmap.LOG_LEVEL | default "debug" | quote }} + DEPLOYMENT_MODE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "DEPLOYMENT_MODE" "params" .Values.ledger "field" "deploymentMode" "default" "local") | quote }} + LOG_LEVEL: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "LOG_LEVEL" "params" .Values.ledger "field" "logLevel" "default" "debug") | quote }} - SERVER_PORT: {{ .Values.ledger.configmap.SERVER_PORT | default "3002" | quote }} - SERVER_ADDRESS: {{ .Values.ledger.configmap.SERVER_ADDRESS | default ":3002" | quote }} + SERVER_PORT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "SERVER_PORT" "params" .Values.ledger.server "field" "port" "default" "3002") | quote }} + SERVER_ADDRESS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "SERVER_ADDRESS" "params" .Values.ledger.server "field" "address" "default" ":3002") | quote }} # ============================================================================= # SWAGGER # ============================================================================= - SWAGGER_TITLE: {{ .Values.ledger.configmap.SWAGGER_TITLE | default "" | quote }} - SWAGGER_DESCRIPTION: {{ .Values.ledger.configmap.SWAGGER_DESCRIPTION | default "" | quote }} + SWAGGER_TITLE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "SWAGGER_TITLE" "params" .Values.ledger.swagger "field" "title" "default" "") | quote }} + SWAGGER_DESCRIPTION: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "SWAGGER_DESCRIPTION" "params" .Values.ledger.swagger "field" "description" "default" "") | quote }} SWAGGER_VERSION: {{ .Values.ledger.image.tag | default .Chart.AppVersion | quote }} - SWAGGER_HOST: {{ .Values.ledger.configmap.SWAGGER_HOST | default "" | quote }} - SWAGGER_BASE_PATH: {{ .Values.ledger.configmap.SWAGGER_BASE_PATH | default "" | quote }} - SWAGGER_SCHEMES: {{ .Values.ledger.configmap.SWAGGER_SCHEMES | default "" | quote }} - SWAGGER_LEFT_DELIM: {{ .Values.ledger.configmap.SWAGGER_LEFT_DELIM | default "" | quote }} - SWAGGER_RIGHT_DELIM: {{ .Values.ledger.configmap.SWAGGER_RIGHT_DELIM | default "" | quote }} + SWAGGER_HOST: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "SWAGGER_HOST" "params" .Values.ledger.swagger "field" "host" "default" "") | quote }} + SWAGGER_BASE_PATH: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "SWAGGER_BASE_PATH" "params" .Values.ledger.swagger "field" "basePath" "default" "") | quote }} + SWAGGER_SCHEMES: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "SWAGGER_SCHEMES" "params" .Values.ledger.swagger "field" "schemes" "default" "") | quote }} + SWAGGER_LEFT_DELIM: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "SWAGGER_LEFT_DELIM" "params" .Values.ledger.swagger "field" "leftDelim" "default" "") | quote }} + SWAGGER_RIGHT_DELIM: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "SWAGGER_RIGHT_DELIM" "params" .Values.ledger.swagger "field" "rightDelim" "default" "") | quote }} # ============================================================================= # AUTH CONFIGS (shared) # ============================================================================= - PLUGIN_AUTH_ENABLED: {{ .Values.ledger.configmap.PLUGIN_AUTH_ENABLED | default "false" | quote }} - PLUGIN_AUTH_HOST: {{ .Values.ledger.configmap.PLUGIN_AUTH_HOST | default "" | quote }} + {{- include "lerian-common.auth.env" (dict "context" $ "configmap" .Values.ledger.configmap "hostKey" "PLUGIN_AUTH_HOST") | nindent 2 }} # ============================================================================= # MULTI-TENANT # ============================================================================= - MULTI_TENANT_ENABLED: {{ .Values.ledger.configmap.MULTI_TENANT_ENABLED | default "false" | quote }} - {{- if eq (.Values.ledger.configmap.MULTI_TENANT_ENABLED | default "false" | toString) "true" }} - MULTI_TENANT_URL: {{ required "ledger.configmap.MULTI_TENANT_URL is required when MULTI_TENANT_ENABLED=true" .Values.ledger.configmap.MULTI_TENANT_URL | quote }} - MULTI_TENANT_SERVICE_NAME: {{ .Values.ledger.configmap.MULTI_TENANT_SERVICE_NAME | default "ledger" | quote }} - MULTI_TENANT_CIRCUIT_BREAKER_THRESHOLD: {{ .Values.ledger.configmap.MULTI_TENANT_CIRCUIT_BREAKER_THRESHOLD | default "5" | quote }} - MULTI_TENANT_CIRCUIT_BREAKER_TIMEOUT_SEC: {{ .Values.ledger.configmap.MULTI_TENANT_CIRCUIT_BREAKER_TIMEOUT_SEC | default "30" | quote }} - MULTI_TENANT_REDIS_HOST: {{ required "ledger.configmap.MULTI_TENANT_REDIS_HOST is required when MULTI_TENANT_ENABLED=true" .Values.ledger.configmap.MULTI_TENANT_REDIS_HOST | quote }} - MULTI_TENANT_REDIS_PORT: {{ .Values.ledger.configmap.MULTI_TENANT_REDIS_PORT | default "6379" | quote }} - MULTI_TENANT_REDIS_TLS: {{ .Values.ledger.configmap.MULTI_TENANT_REDIS_TLS | default "false" | quote }} - {{- end }} + # Enable via ledger.multiTenant.enabled; URL + redis come from global.multiTenant + # (env-wide). Emitted only when enabled → configmap stays clean when off. + {{- /* Legacy configmap.MULTI_TENANT_ENABLED wins over the grouped param + (presence-based); compare to "true" so a legacy "false" string does not + evaluate truthy. Same resolved bool gates both the env var and the helper. */ -}} + {{- $mtEnabled := eq (include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MULTI_TENANT_ENABLED" "params" .Values.ledger.multiTenant "field" "enabled" "default" "false")) "true" }} + MULTI_TENANT_ENABLED: {{ $mtEnabled | quote }} + {{- include "lerian-common.multiTenant.env" (dict + "context" $ + "configmap" .Values.ledger.configmap + "enabled" $mtEnabled + "serviceName" "ledger" + "emitRedis" true + "requiredUrl" true + "requiredRedisHost" true) | nindent 2 }} # ============================================================================= # ACCOUNTING CONFIG # ============================================================================= - ACCOUNT_TYPE_VALIDATION: {{ .Values.ledger.configmap.ACCOUNT_TYPE_VALIDATION | default "" | quote }} - TRANSACTION_ROUTE_VALIDATION: {{ .Values.ledger.configmap.TRANSACTION_ROUTE_VALIDATION | default "" | quote }} + ACCOUNT_TYPE_VALIDATION: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "ACCOUNT_TYPE_VALIDATION" "params" .Values.ledger.accounting "field" "accountTypeValidation" "default" "") | quote }} + TRANSACTION_ROUTE_VALIDATION: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "TRANSACTION_ROUTE_VALIDATION" "params" .Values.ledger.accounting "field" "transactionRouteValidation" "default" "") | quote }} # ============================================================================= # DB POSTGRESQL - ONBOARDING MODULE # ============================================================================= # Primary - DB_ONBOARDING_HOST: {{ .Values.ledger.configmap.DB_ONBOARDING_HOST | default "midaz-postgresql-primary" | quote }} - DB_ONBOARDING_USER: {{ .Values.ledger.configmap.DB_ONBOARDING_USER | default "midaz" | quote }} - DB_ONBOARDING_NAME: {{ .Values.ledger.configmap.DB_ONBOARDING_NAME | default "onboarding" | quote }} - DB_ONBOARDING_PORT: {{ .Values.ledger.configmap.DB_ONBOARDING_PORT | default "5432" | quote }} - DB_ONBOARDING_SSLMODE: {{ .Values.ledger.configmap.DB_ONBOARDING_SSLMODE | default "disable" | quote }} + DB_ONBOARDING_HOST: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "host" "nativeKey" "DB_ONBOARDING_HOST" "default" "midaz-postgresql-primary") | quote }} + DB_ONBOARDING_USER: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "user" "nativeKey" "DB_ONBOARDING_USER" "default" "midaz") | quote }} + DB_ONBOARDING_NAME: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "DB_ONBOARDING_NAME" "params" $dbOnboarding "field" "name" "default" "onboarding") | quote }} + DB_ONBOARDING_PORT: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "port" "nativeKey" "DB_ONBOARDING_PORT" "default" "5432") | quote }} + DB_ONBOARDING_SSLMODE: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "ssl" "nativeKey" "DB_ONBOARDING_SSLMODE" "default" "disable") | quote }} # Replica - DB_ONBOARDING_REPLICA_HOST: {{ .Values.ledger.configmap.DB_ONBOARDING_REPLICA_HOST | default "midaz-postgresql-read" | quote }} - DB_ONBOARDING_REPLICA_USER: {{ .Values.ledger.configmap.DB_ONBOARDING_REPLICA_USER | default "midaz" | quote }} - DB_ONBOARDING_REPLICA_NAME: {{ .Values.ledger.configmap.DB_ONBOARDING_REPLICA_NAME | default "onboarding" | quote }} - DB_ONBOARDING_REPLICA_PORT: {{ .Values.ledger.configmap.DB_ONBOARDING_REPLICA_PORT | default "5432" | quote }} - DB_ONBOARDING_REPLICA_SSLMODE: {{ .Values.ledger.configmap.DB_ONBOARDING_REPLICA_SSLMODE | default "disable" | quote }} + DB_ONBOARDING_REPLICA_HOST: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "replicaHost" "nativeKey" "DB_ONBOARDING_REPLICA_HOST" "default" "midaz-postgresql-replication") | quote }} + DB_ONBOARDING_REPLICA_USER: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "user" "nativeKey" "DB_ONBOARDING_REPLICA_USER" "default" "midaz") | quote }} + DB_ONBOARDING_REPLICA_NAME: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "DB_ONBOARDING_REPLICA_NAME" "params" $dbOnboarding "field" "replicaName" "default" "onboarding") | quote }} + DB_ONBOARDING_REPLICA_PORT: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "port" "nativeKey" "DB_ONBOARDING_REPLICA_PORT" "default" "5432") | quote }} + DB_ONBOARDING_REPLICA_SSLMODE: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "ssl" "nativeKey" "DB_ONBOARDING_REPLICA_SSLMODE" "default" "disable") | quote }} # Connection pool - DB_ONBOARDING_MAX_OPEN_CONNS: {{ .Values.ledger.configmap.DB_ONBOARDING_MAX_OPEN_CONNS | default "3000" | quote }} - DB_ONBOARDING_MAX_IDLE_CONNS: {{ .Values.ledger.configmap.DB_ONBOARDING_MAX_IDLE_CONNS | default "3000" | quote }} + DB_ONBOARDING_MAX_OPEN_CONNS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "DB_ONBOARDING_MAX_OPEN_CONNS" "params" $dbOnboarding "field" "maxOpenConns" "default" "3000") | quote }} + DB_ONBOARDING_MAX_IDLE_CONNS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "DB_ONBOARDING_MAX_IDLE_CONNS" "params" $dbOnboarding "field" "maxIdleConns" "default" "3000") | quote }} # ============================================================================= # MONGO DB - ONBOARDING MODULE # ============================================================================= - MONGO_ONBOARDING_URI: {{ .Values.ledger.configmap.MONGO_ONBOARDING_URI | default "mongodb" | quote }} - MONGO_ONBOARDING_HOST: {{ .Values.ledger.configmap.MONGO_ONBOARDING_HOST | default "midaz-mongodb" | quote }} - MONGO_ONBOARDING_NAME: {{ .Values.ledger.configmap.MONGO_ONBOARDING_NAME | default "onboarding" | quote }} - MONGO_ONBOARDING_USER: {{ .Values.ledger.configmap.MONGO_ONBOARDING_USER | default "midaz" | quote }} - MONGO_ONBOARDING_PORT: {{ .Values.ledger.configmap.MONGO_ONBOARDING_PORT | default "27017" | quote }} - MONGO_ONBOARDING_MAX_POOL_SIZE: {{ .Values.ledger.configmap.MONGO_ONBOARDING_MAX_POOL_SIZE | default "1000" | quote }} - MONGO_ONBOARDING_PARAMETERS: {{ .Values.ledger.configmap.MONGO_ONBOARDING_PARAMETERS | default "" | quote }} - MONGO_ONBOARDING_TLS_CA_CERT: {{ .Values.ledger.configmap.MONGO_ONBOARDING_TLS_CA_CERT | default "" | quote }} + MONGO_ONBOARDING_URI: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MONGO_ONBOARDING_URI" "params" $mongoOnboarding "field" "uri" "default" "mongodb") | quote }} + MONGO_ONBOARDING_HOST: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "mongo" "field" "host" "nativeKey" "MONGO_ONBOARDING_HOST" "default" "midaz-mongodb") | quote }} + MONGO_ONBOARDING_NAME: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MONGO_ONBOARDING_NAME" "params" $mongoOnboarding "field" "name" "default" "onboarding") | quote }} + MONGO_ONBOARDING_USER: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "mongo" "field" "user" "nativeKey" "MONGO_ONBOARDING_USER" "default" "midaz") | quote }} + MONGO_ONBOARDING_PORT: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "mongo" "field" "port" "nativeKey" "MONGO_ONBOARDING_PORT" "default" "27017") | quote }} + MONGO_ONBOARDING_MAX_POOL_SIZE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MONGO_ONBOARDING_MAX_POOL_SIZE" "params" $mongoOnboarding "field" "maxPoolSize" "default" "1000") | quote }} + MONGO_ONBOARDING_PARAMETERS: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "mongo" "field" "params" "nativeKey" "MONGO_ONBOARDING_PARAMETERS" "default" "") | quote }} + MONGO_ONBOARDING_TLS_CA_CERT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MONGO_ONBOARDING_TLS_CA_CERT" "params" $mongoOnboarding "field" "tlsCaCert" "default" "") | quote }} # ============================================================================= # DB POSTGRESQL - TRANSACTION MODULE # ============================================================================= # Primary - DB_TRANSACTION_HOST: {{ .Values.ledger.configmap.DB_TRANSACTION_HOST | default "midaz-postgresql-primary" | quote }} - DB_TRANSACTION_USER: {{ .Values.ledger.configmap.DB_TRANSACTION_USER | default "midaz" | quote }} - DB_TRANSACTION_NAME: {{ .Values.ledger.configmap.DB_TRANSACTION_NAME | default "transaction" | quote }} - DB_TRANSACTION_PORT: {{ .Values.ledger.configmap.DB_TRANSACTION_PORT | default "5432" | quote }} - DB_TRANSACTION_SSLMODE: {{ .Values.ledger.configmap.DB_TRANSACTION_SSLMODE | default "disable" | quote }} + DB_TRANSACTION_HOST: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "host" "nativeKey" "DB_TRANSACTION_HOST" "default" "midaz-postgresql-primary") | quote }} + DB_TRANSACTION_USER: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "user" "nativeKey" "DB_TRANSACTION_USER" "default" "midaz") | quote }} + DB_TRANSACTION_NAME: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "DB_TRANSACTION_NAME" "params" $dbTransaction "field" "name" "default" "transaction") | quote }} + DB_TRANSACTION_PORT: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "port" "nativeKey" "DB_TRANSACTION_PORT" "default" "5432") | quote }} + DB_TRANSACTION_SSLMODE: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "ssl" "nativeKey" "DB_TRANSACTION_SSLMODE" "default" "disable") | quote }} # Replica - DB_TRANSACTION_REPLICA_HOST: {{ .Values.ledger.configmap.DB_TRANSACTION_REPLICA_HOST | default "midaz-postgresql-read" | quote }} - DB_TRANSACTION_REPLICA_USER: {{ .Values.ledger.configmap.DB_TRANSACTION_REPLICA_USER | default "midaz" | quote }} - DB_TRANSACTION_REPLICA_NAME: {{ .Values.ledger.configmap.DB_TRANSACTION_REPLICA_NAME | default "transaction" | quote }} - DB_TRANSACTION_REPLICA_PORT: {{ .Values.ledger.configmap.DB_TRANSACTION_REPLICA_PORT | default "5432" | quote }} - DB_TRANSACTION_REPLICA_SSLMODE: {{ .Values.ledger.configmap.DB_TRANSACTION_REPLICA_SSLMODE | default "disable" | quote }} + DB_TRANSACTION_REPLICA_HOST: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "replicaHost" "nativeKey" "DB_TRANSACTION_REPLICA_HOST" "default" "midaz-postgresql-replication") | quote }} + DB_TRANSACTION_REPLICA_USER: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "user" "nativeKey" "DB_TRANSACTION_REPLICA_USER" "default" "midaz") | quote }} + DB_TRANSACTION_REPLICA_NAME: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "DB_TRANSACTION_REPLICA_NAME" "params" $dbTransaction "field" "replicaName" "default" "transaction") | quote }} + DB_TRANSACTION_REPLICA_PORT: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "port" "nativeKey" "DB_TRANSACTION_REPLICA_PORT" "default" "5432") | quote }} + DB_TRANSACTION_REPLICA_SSLMODE: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "postgres" "field" "ssl" "nativeKey" "DB_TRANSACTION_REPLICA_SSLMODE" "default" "disable") | quote }} # Connection pool - DB_TRANSACTION_MAX_OPEN_CONNS: {{ .Values.ledger.configmap.DB_TRANSACTION_MAX_OPEN_CONNS | default "3000" | quote }} - DB_TRANSACTION_MAX_IDLE_CONNS: {{ .Values.ledger.configmap.DB_TRANSACTION_MAX_IDLE_CONNS | default "3000" | quote }} + DB_TRANSACTION_MAX_OPEN_CONNS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "DB_TRANSACTION_MAX_OPEN_CONNS" "params" $dbTransaction "field" "maxOpenConns" "default" "3000") | quote }} + DB_TRANSACTION_MAX_IDLE_CONNS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "DB_TRANSACTION_MAX_IDLE_CONNS" "params" $dbTransaction "field" "maxIdleConns" "default" "3000") | quote }} # ============================================================================= # MONGO DB - TRANSACTION MODULE # ============================================================================= - MONGO_TRANSACTION_URI: {{ .Values.ledger.configmap.MONGO_TRANSACTION_URI | default "mongodb" | quote }} - MONGO_TRANSACTION_HOST: {{ .Values.ledger.configmap.MONGO_TRANSACTION_HOST | default "midaz-mongodb" | quote }} - MONGO_TRANSACTION_NAME: {{ .Values.ledger.configmap.MONGO_TRANSACTION_NAME | default "transaction" | quote }} - MONGO_TRANSACTION_USER: {{ .Values.ledger.configmap.MONGO_TRANSACTION_USER | default "midaz" | quote }} - MONGO_TRANSACTION_PORT: {{ .Values.ledger.configmap.MONGO_TRANSACTION_PORT | default "27017" | quote }} - MONGO_TRANSACTION_MAX_POOL_SIZE: {{ .Values.ledger.configmap.MONGO_TRANSACTION_MAX_POOL_SIZE | default "1000" | quote }} - MONGO_TRANSACTION_PARAMETERS: {{ .Values.ledger.configmap.MONGO_TRANSACTION_PARAMETERS | default "" | quote }} - MONGO_TRANSACTION_TLS_CA_CERT: {{ .Values.ledger.configmap.MONGO_TRANSACTION_TLS_CA_CERT | default "" | quote }} + MONGO_TRANSACTION_URI: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MONGO_TRANSACTION_URI" "params" $mongoTransaction "field" "uri" "default" "mongodb") | quote }} + MONGO_TRANSACTION_HOST: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "mongo" "field" "host" "nativeKey" "MONGO_TRANSACTION_HOST" "default" "midaz-mongodb") | quote }} + MONGO_TRANSACTION_NAME: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MONGO_TRANSACTION_NAME" "params" $mongoTransaction "field" "name" "default" "transaction") | quote }} + MONGO_TRANSACTION_USER: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "mongo" "field" "user" "nativeKey" "MONGO_TRANSACTION_USER" "default" "midaz") | quote }} + MONGO_TRANSACTION_PORT: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "mongo" "field" "port" "nativeKey" "MONGO_TRANSACTION_PORT" "default" "27017") | quote }} + MONGO_TRANSACTION_MAX_POOL_SIZE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MONGO_TRANSACTION_MAX_POOL_SIZE" "params" $mongoTransaction "field" "maxPoolSize" "default" "1000") | quote }} + MONGO_TRANSACTION_PARAMETERS: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "mongo" "field" "params" "nativeKey" "MONGO_TRANSACTION_PARAMETERS" "default" "") | quote }} + MONGO_TRANSACTION_TLS_CA_CERT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MONGO_TRANSACTION_TLS_CA_CERT" "params" $mongoTransaction "field" "tlsCaCert" "default" "") | quote }} # ============================================================================= # REDIS (shared between modules) # ============================================================================= - REDIS_HOST: {{ .Values.ledger.configmap.REDIS_HOST | default "midaz-valkey-primary:6379" | quote }} - REDIS_MASTER_NAME: {{ .Values.ledger.configmap.REDIS_MASTER_NAME | default "" | quote }} - REDIS_TLS: {{ .Values.ledger.configmap.REDIS_TLS | default "false" | quote }} - REDIS_CA_CERT: {{ .Values.ledger.configmap.REDIS_CA_CERT | default "" | quote }} - REDIS_USE_GCP_IAM: {{ .Values.ledger.configmap.REDIS_USE_GCP_IAM | default "false" | quote }} - REDIS_SERVICE_ACCOUNT: {{ .Values.ledger.configmap.REDIS_SERVICE_ACCOUNT | default "" | quote }} - GOOGLE_APPLICATION_CREDENTIALS: {{ .Values.ledger.configmap.GOOGLE_APPLICATION_CREDENTIALS | default "" | quote }} - REDIS_TOKEN_LIFETIME: {{ .Values.ledger.configmap.REDIS_TOKEN_LIFETIME | default "60" | quote }} - REDIS_TOKEN_REFRESH_DURATION: {{ .Values.ledger.configmap.REDIS_TOKEN_REFRESH_DURATION | default "45" | quote }} - REDIS_DB: {{ .Values.ledger.configmap.REDIS_DB | default "0" | quote }} - REDIS_PROTOCOL: {{ .Values.ledger.configmap.REDIS_PROTOCOL | default "3" | quote }} - REDIS_POOL_SIZE: {{ .Values.ledger.configmap.REDIS_POOL_SIZE | default "10" | quote }} - REDIS_MIN_IDLE_CONNS: {{ .Values.ledger.configmap.REDIS_MIN_IDLE_CONNS | default "0" | quote }} - REDIS_READ_TIMEOUT: {{ .Values.ledger.configmap.REDIS_READ_TIMEOUT | default "3" | quote }} - REDIS_WRITE_TIMEOUT: {{ .Values.ledger.configmap.REDIS_WRITE_TIMEOUT | default "3" | quote }} - REDIS_DIAL_TIMEOUT: {{ .Values.ledger.configmap.REDIS_DIAL_TIMEOUT | default "5" | quote }} - REDIS_POOL_TIMEOUT: {{ .Values.ledger.configmap.REDIS_POOL_TIMEOUT | default "2" | quote }} - REDIS_MAX_RETRIES: {{ .Values.ledger.configmap.REDIS_MAX_RETRIES | default "3" | quote }} - REDIS_MIN_RETRY_BACKOFF: {{ .Values.ledger.configmap.REDIS_MIN_RETRY_BACKOFF | default "8" | quote }} - REDIS_MAX_RETRY_BACKOFF: {{ .Values.ledger.configmap.REDIS_MAX_RETRY_BACKOFF | default "1" | quote }} + REDIS_HOST: {{ include "lerian-common.datastore.value" (dict "context" $ "dedicated" .Values.ledger.datastores "configmap" .Values.ledger.configmap "type" "redis" "field" "host" "nativeKey" "REDIS_HOST" "default" "midaz-valkey-primary:6379") | quote }} + REDIS_MASTER_NAME: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_MASTER_NAME" "params" .Values.ledger.redis "field" "masterName" "default" "") | quote }} + REDIS_TLS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_TLS" "params" .Values.ledger.redis "field" "tls" "default" "false") | quote }} + REDIS_CA_CERT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_CA_CERT" "params" .Values.ledger.redis "field" "caCert" "default" "") | quote }} + REDIS_USE_GCP_IAM: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_USE_GCP_IAM" "params" .Values.ledger.redis "field" "useGcpIam" "default" "false") | quote }} + REDIS_SERVICE_ACCOUNT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_SERVICE_ACCOUNT" "params" .Values.ledger.redis "field" "serviceAccount" "default" "") | quote }} + GOOGLE_APPLICATION_CREDENTIALS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "GOOGLE_APPLICATION_CREDENTIALS" "params" .Values.ledger.redis "field" "googleAppCredentials" "default" "") | quote }} + REDIS_TOKEN_LIFETIME: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_TOKEN_LIFETIME" "params" .Values.ledger.redis "field" "tokenLifetime" "default" "60") | quote }} + REDIS_TOKEN_REFRESH_DURATION: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_TOKEN_REFRESH_DURATION" "params" .Values.ledger.redis "field" "tokenRefreshDuration" "default" "45") | quote }} + REDIS_DB: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_DB" "params" .Values.ledger.redis "field" "db" "default" "0") | quote }} + REDIS_PROTOCOL: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_PROTOCOL" "params" .Values.ledger.redis "field" "protocol" "default" "3") | quote }} + REDIS_POOL_SIZE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_POOL_SIZE" "params" .Values.ledger.redis "field" "poolSize" "default" "10") | quote }} + REDIS_MIN_IDLE_CONNS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_MIN_IDLE_CONNS" "params" .Values.ledger.redis "field" "minIdleConns" "default" "0") | quote }} + REDIS_READ_TIMEOUT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_READ_TIMEOUT" "params" .Values.ledger.redis "field" "readTimeout" "default" "3") | quote }} + REDIS_WRITE_TIMEOUT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_WRITE_TIMEOUT" "params" .Values.ledger.redis "field" "writeTimeout" "default" "3") | quote }} + REDIS_DIAL_TIMEOUT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_DIAL_TIMEOUT" "params" .Values.ledger.redis "field" "dialTimeout" "default" "5") | quote }} + REDIS_POOL_TIMEOUT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_POOL_TIMEOUT" "params" .Values.ledger.redis "field" "poolTimeout" "default" "2") | quote }} + REDIS_MAX_RETRIES: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_MAX_RETRIES" "params" .Values.ledger.redis "field" "maxRetries" "default" "3") | quote }} + REDIS_MIN_RETRY_BACKOFF: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_MIN_RETRY_BACKOFF" "params" .Values.ledger.redis "field" "minRetryBackoff" "default" "8") | quote }} + REDIS_MAX_RETRY_BACKOFF: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "REDIS_MAX_RETRY_BACKOFF" "params" .Values.ledger.redis "field" "maxRetryBackoff" "default" "1") | quote }} # ============================================================================= # OPEN TELEMETRY # ============================================================================= - OTEL_RESOURCE_SERVICE_NAME: {{ .Values.ledger.configmap.OTEL_RESOURCE_SERVICE_NAME | default "ledger" | quote }} - OTEL_LIBRARY_NAME: {{ .Values.ledger.configmap.OTEL_LIBRARY_NAME | default "github.com/LerianStudio/midaz/v3/components/ledger" | quote }} - OTEL_RESOURCE_SERVICE_VERSION: {{ .Values.ledger.image.tag | default .Chart.AppVersion | quote }} - OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT: {{ .Values.ledger.configmap.OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT | default "production" | quote }} - OTEL_EXPORTER_OTLP_ENDPOINT_PORT: {{ .Values.ledger.configmap.OTEL_EXPORTER_OTLP_ENDPOINT_PORT | default "4317" | quote }} - OTEL_EXPORTER_OTLP_ENDPOINT: {{ .Values.ledger.configmap.OTEL_EXPORTER_OTLP_ENDPOINT | default "midaz-grafana:4317" | quote }} - ENABLE_TELEMETRY: {{ .Values.ledger.configmap.ENABLE_TELEMETRY | default "false" | quote }} + {{- /* Static/local OTEL identity keys stay flat (configmap override or default). */ -}} + {{- include "lerian-common.otel.envFlat" (dict + "configmap" .Values.ledger.configmap + "keys" (list "OTEL_RESOURCE_SERVICE_NAME" "OTEL_LIBRARY_NAME" "OTEL_RESOURCE_SERVICE_VERSION" "OTEL_EXPORTER_OTLP_ENDPOINT_PORT") + "defaults" (dict + "OTEL_RESOURCE_SERVICE_NAME" "ledger" + "OTEL_LIBRARY_NAME" "github.com/LerianStudio/midaz/v3/components/ledger" + "OTEL_RESOURCE_SERVICE_VERSION" (.Values.ledger.image.tag | default .Chart.AppVersion))) | nindent 2 }} + {{/* Env-wide observability contract: endpoint, deployment env and enable derive + from global.observability with configmap. winning. The deployment-env + default is $env (global.environment), so it stays single-sourced with ENV_NAME + unless global.observability.deploymentEnvironment (or configmap) overrides it. */ -}} + OTEL_EXPORTER_OTLP_ENDPOINT: {{ include "lerian-common.globalValue" (dict "context" $ "configmap" .Values.ledger.configmap "block" "observability" "field" "otlpEndpoint" "nativeKey" "OTEL_EXPORTER_OTLP_ENDPOINT" "default" "midaz-grafana:4317") | quote }} + OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT: {{ include "lerian-common.globalValue" (dict "context" $ "configmap" .Values.ledger.configmap "block" "observability" "field" "deploymentEnvironment" "nativeKey" "OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT" "default" $env) | quote }} + ENABLE_TELEMETRY: {{ include "lerian-common.globalValue" (dict "context" $ "configmap" .Values.ledger.configmap "block" "observability" "field" "enabled" "nativeKey" "ENABLE_TELEMETRY" "default" (include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "ENABLE_TELEMETRY" "params" .Values.ledger.telemetry "field" "enabled" "default" "true"))) | quote }} # ============================================================================= # RABBITMQ (used by transaction module) # ============================================================================= - RABBITMQ_URI: {{ .Values.ledger.configmap.RABBITMQ_URI | default "amqp" | quote }} - RABBITMQ_PROTOCOL: {{ .Values.ledger.configmap.RABBITMQ_PROTOCOL | default "http" | quote }} - RABBITMQ_HOST: {{ .Values.ledger.configmap.RABBITMQ_HOST | default "midaz-rabbitmq" | quote }} - RABBITMQ_PORT_HOST: {{ .Values.ledger.configmap.RABBITMQ_PORT_HOST | default "5672" | quote }} - RABBITMQ_PORT_AMQP: {{ .Values.ledger.configmap.RABBITMQ_PORT_AMQP | default "15672" | quote }} - RABBITMQ_DEFAULT_USER: {{ .Values.ledger.configmap.RABBITMQ_DEFAULT_USER | default "transaction" | quote }} - RABBITMQ_CONSUMER_USER: {{ .Values.ledger.configmap.RABBITMQ_CONSUMER_USER | default "consumer" | quote }} - RABBITMQ_NUMBERS_OF_WORKERS: {{ .Values.ledger.configmap.RABBITMQ_NUMBERS_OF_WORKERS | default "5" | quote }} - RABBITMQ_NUMBERS_OF_PREFETCH: {{ .Values.ledger.configmap.RABBITMQ_NUMBERS_OF_PREFETCH | default "10" | quote }} - RABBITMQ_HEALTH_CHECK_URL: {{ .Values.ledger.configmap.RABBITMQ_HEALTH_CHECK_URL | default $rabbitHealthCheckURL | quote }} - RABBITMQ_BALANCE_CREATE_QUEUE: {{ .Values.ledger.configmap.RABBITMQ_BALANCE_CREATE_QUEUE | default "transaction.balance_create.queue" | quote }} - RABBITMQ_TRANSACTION_BALANCE_OPERATION_EXCHANGE: {{ .Values.ledger.configmap.RABBITMQ_TRANSACTION_BALANCE_OPERATION_EXCHANGE | default "transaction.transaction_balance_operation.exchange" | quote }} - RABBITMQ_TRANSACTION_BALANCE_OPERATION_KEY: {{ .Values.ledger.configmap.RABBITMQ_TRANSACTION_BALANCE_OPERATION_KEY | default "transaction.transaction_balance_operation.key" | quote }} - RABBITMQ_TRANSACTION_BALANCE_OPERATION_QUEUE: {{ .Values.ledger.configmap.RABBITMQ_TRANSACTION_BALANCE_OPERATION_QUEUE | default "transaction.transaction_balance_operation.queue" | quote }} - RABBITMQ_TRANSACTION_ASYNC: {{ .Values.ledger.configmap.RABBITMQ_TRANSACTION_ASYNC | default "false" | quote }} - RABBITMQ_VHOST: {{ .Values.ledger.configmap.RABBITMQ_VHOST | default "" | quote }} + RABBITMQ_URI: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_URI" "params" .Values.ledger.broker "field" "uri" "default" "amqp") | quote }} + RABBITMQ_PROTOCOL: {{ $rabbitProtocol | quote }} + RABBITMQ_HOST: {{ $rabbitHost | quote }} + RABBITMQ_PORT_HOST: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_PORT_HOST" "params" .Values.ledger.broker "field" "portHost" "default" "5672") | quote }} + RABBITMQ_PORT_AMQP: {{ $rabbitAmqpPort | quote }} + RABBITMQ_DEFAULT_USER: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_DEFAULT_USER" "params" .Values.ledger.broker "field" "defaultUser" "default" "transaction") | quote }} + RABBITMQ_CONSUMER_USER: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_CONSUMER_USER" "params" .Values.ledger.broker "field" "consumerUser" "default" "consumer") | quote }} + RABBITMQ_NUMBERS_OF_WORKERS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_NUMBERS_OF_WORKERS" "params" .Values.ledger.broker "field" "workers" "default" "5") | quote }} + RABBITMQ_NUMBERS_OF_PREFETCH: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_NUMBERS_OF_PREFETCH" "params" .Values.ledger.broker "field" "prefetch" "default" "10") | quote }} + RABBITMQ_HEALTH_CHECK_URL: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_HEALTH_CHECK_URL" "params" .Values.ledger.broker "field" "healthCheckUrl" "default" $rabbitHealthCheckURL) | quote }} + RABBITMQ_BALANCE_CREATE_QUEUE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_BALANCE_CREATE_QUEUE" "params" .Values.ledger.broker "field" "balanceCreateQueue" "default" "transaction.balance_create.queue") | quote }} + RABBITMQ_TRANSACTION_BALANCE_OPERATION_EXCHANGE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_TRANSACTION_BALANCE_OPERATION_EXCHANGE" "params" .Values.ledger.broker "field" "balanceOperationExchange" "default" "transaction.transaction_balance_operation.exchange") | quote }} + RABBITMQ_TRANSACTION_BALANCE_OPERATION_KEY: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_TRANSACTION_BALANCE_OPERATION_KEY" "params" .Values.ledger.broker "field" "balanceOperationKey" "default" "transaction.transaction_balance_operation.key") | quote }} + RABBITMQ_TRANSACTION_BALANCE_OPERATION_QUEUE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_TRANSACTION_BALANCE_OPERATION_QUEUE" "params" .Values.ledger.broker "field" "balanceOperationQueue" "default" "transaction.transaction_balance_operation.queue") | quote }} + RABBITMQ_TRANSACTION_ASYNC: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_TRANSACTION_ASYNC" "params" .Values.ledger.broker "field" "transactionAsync" "default" "false") | quote }} + RABBITMQ_VHOST: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_VHOST" "params" .Values.ledger.broker "field" "vhost" "default" "") | quote }} # ============================================================================= # RABBITMQ CIRCUIT BREAKER # ============================================================================= - RABBITMQ_CIRCUIT_BREAKER_CONSECUTIVE_FAILURES: {{ .Values.ledger.configmap.RABBITMQ_CIRCUIT_BREAKER_CONSECUTIVE_FAILURES | default "15" | quote }} - RABBITMQ_CIRCUIT_BREAKER_FAILURE_RATIO: {{ .Values.ledger.configmap.RABBITMQ_CIRCUIT_BREAKER_FAILURE_RATIO | default "50" | quote }} - RABBITMQ_CIRCUIT_BREAKER_INTERVAL: {{ .Values.ledger.configmap.RABBITMQ_CIRCUIT_BREAKER_INTERVAL | default "120" | quote }} - RABBITMQ_CIRCUIT_BREAKER_MAX_REQUESTS: {{ .Values.ledger.configmap.RABBITMQ_CIRCUIT_BREAKER_MAX_REQUESTS | default "3" | quote }} - RABBITMQ_CIRCUIT_BREAKER_MIN_REQUESTS: {{ .Values.ledger.configmap.RABBITMQ_CIRCUIT_BREAKER_MIN_REQUESTS | default "10" | quote }} - RABBITMQ_CIRCUIT_BREAKER_TIMEOUT: {{ .Values.ledger.configmap.RABBITMQ_CIRCUIT_BREAKER_TIMEOUT | default "30" | quote }} - RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_INTERVAL: {{ .Values.ledger.configmap.RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_INTERVAL | default "30" | quote }} - RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_TIMEOUT: {{ .Values.ledger.configmap.RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_TIMEOUT | default "10" | quote }} - RABBITMQ_OPERATION_TIMEOUT: {{ .Values.ledger.configmap.RABBITMQ_OPERATION_TIMEOUT | default "30s" | quote }} + RABBITMQ_CIRCUIT_BREAKER_CONSECUTIVE_FAILURES: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_CIRCUIT_BREAKER_CONSECUTIVE_FAILURES" "params" $brokerCircuitBreaker "field" "consecutiveFailures" "default" "15") | quote }} + RABBITMQ_CIRCUIT_BREAKER_FAILURE_RATIO: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_CIRCUIT_BREAKER_FAILURE_RATIO" "params" $brokerCircuitBreaker "field" "failureRatio" "default" "50") | quote }} + RABBITMQ_CIRCUIT_BREAKER_INTERVAL: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_CIRCUIT_BREAKER_INTERVAL" "params" $brokerCircuitBreaker "field" "interval" "default" "120") | quote }} + RABBITMQ_CIRCUIT_BREAKER_MAX_REQUESTS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_CIRCUIT_BREAKER_MAX_REQUESTS" "params" $brokerCircuitBreaker "field" "maxRequests" "default" "3") | quote }} + RABBITMQ_CIRCUIT_BREAKER_MIN_REQUESTS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_CIRCUIT_BREAKER_MIN_REQUESTS" "params" $brokerCircuitBreaker "field" "minRequests" "default" "10") | quote }} + RABBITMQ_CIRCUIT_BREAKER_TIMEOUT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_CIRCUIT_BREAKER_TIMEOUT" "params" $brokerCircuitBreaker "field" "timeout" "default" "30") | quote }} + RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_INTERVAL: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_INTERVAL" "params" $brokerCircuitBreaker "field" "healthCheckInterval" "default" "30") | quote }} + RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_TIMEOUT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_TIMEOUT" "params" $brokerCircuitBreaker "field" "healthCheckTimeout" "default" "10") | quote }} + RABBITMQ_OPERATION_TIMEOUT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_OPERATION_TIMEOUT" "params" .Values.ledger.broker "field" "operationTimeout" "default" "30s") | quote }} # ============================================================================= # BULK RECORDER # ============================================================================= - BULK_RECORDER_ENABLED: {{ .Values.ledger.configmap.BULK_RECORDER_ENABLED | default "true" | quote }} - BULK_RECORDER_SIZE: {{ .Values.ledger.configmap.BULK_RECORDER_SIZE | default "0" | quote }} - BULK_RECORDER_FLUSH_TIMEOUT_MS: {{ .Values.ledger.configmap.BULK_RECORDER_FLUSH_TIMEOUT_MS | default "100" | quote }} - BULK_RECORDER_MAX_ROWS_PER_INSERT: {{ .Values.ledger.configmap.BULK_RECORDER_MAX_ROWS_PER_INSERT | default "1000" | quote }} + BULK_RECORDER_ENABLED: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "BULK_RECORDER_ENABLED" "params" .Values.ledger.bulkRecorder "field" "enabled" "default" "true") | quote }} + BULK_RECORDER_SIZE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "BULK_RECORDER_SIZE" "params" .Values.ledger.bulkRecorder "field" "size" "default" "0") | quote }} + BULK_RECORDER_FLUSH_TIMEOUT_MS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "BULK_RECORDER_FLUSH_TIMEOUT_MS" "params" .Values.ledger.bulkRecorder "field" "flushTimeoutMs" "default" "100") | quote }} + BULK_RECORDER_MAX_ROWS_PER_INSERT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "BULK_RECORDER_MAX_ROWS_PER_INSERT" "params" .Values.ledger.bulkRecorder "field" "maxRowsPerInsert" "default" "1000") | quote }} # TRANSACTION EXCHANGE EVENTS - RABBITMQ_TRANSACTION_EVENTS_ENABLED: {{ .Values.ledger.configmap.RABBITMQ_TRANSACTION_EVENTS_ENABLED | default "false" | quote }} - RABBITMQ_TRANSACTION_EVENTS_EXCHANGE: {{ .Values.ledger.configmap.RABBITMQ_TRANSACTION_EVENTS_EXCHANGE | default "transaction.transaction_events.exchange" | quote }} + RABBITMQ_TRANSACTION_EVENTS_ENABLED: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_TRANSACTION_EVENTS_ENABLED" "params" .Values.ledger.broker "field" "transactionEventsEnabled" "default" "false") | quote }} + RABBITMQ_TRANSACTION_EVENTS_EXCHANGE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_TRANSACTION_EVENTS_EXCHANGE" "params" .Values.ledger.broker "field" "transactionEventsExchange" "default" "transaction.transaction_events.exchange") | quote }} # OVERDRAFT EVENTS - RABBITMQ_OVERDRAFT_EVENTS_ENABLED: {{ .Values.ledger.configmap.RABBITMQ_OVERDRAFT_EVENTS_ENABLED | default "false" | quote }} - RABBITMQ_OVERDRAFT_EVENTS_EXCHANGE: {{ .Values.ledger.configmap.RABBITMQ_OVERDRAFT_EVENTS_EXCHANGE | default "transaction.overdraft_events.exchange" | quote }} + RABBITMQ_OVERDRAFT_EVENTS_ENABLED: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_OVERDRAFT_EVENTS_ENABLED" "params" .Values.ledger.broker "field" "overdraftEventsEnabled" "default" "false") | quote }} + RABBITMQ_OVERDRAFT_EVENTS_EXCHANGE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_OVERDRAFT_EVENTS_EXCHANGE" "params" .Values.ledger.broker "field" "overdraftEventsExchange" "default" "transaction.overdraft_events.exchange") | quote }} # AUDIT - AUDIT_LOG_ENABLED: {{ .Values.ledger.configmap.AUDIT_LOG_ENABLED | default "false" | quote }} - RABBITMQ_AUDIT_EXCHANGE: {{ .Values.ledger.configmap.RABBITMQ_AUDIT_EXCHANGE | default "audit.append_log.exchange" | quote }} - RABBITMQ_AUDIT_KEY: {{ .Values.ledger.configmap.RABBITMQ_AUDIT_KEY | default "audit.append_log.key" | quote }} + AUDIT_LOG_ENABLED: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "AUDIT_LOG_ENABLED" "params" .Values.ledger.audit "field" "enabled" "default" "false") | quote }} + RABBITMQ_AUDIT_EXCHANGE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_AUDIT_EXCHANGE" "params" .Values.ledger.broker "field" "auditExchange" "default" "audit.append_log.exchange") | quote }} + RABBITMQ_AUDIT_KEY: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RABBITMQ_AUDIT_KEY" "params" .Values.ledger.broker "field" "auditKey" "default" "audit.append_log.key") | quote }} # ============================================================================= # PAGINATION # ============================================================================= - MAX_PAGINATION_LIMIT: {{ .Values.ledger.configmap.MAX_PAGINATION_LIMIT | default "100" | quote }} - MAX_PAGINATION_MONTH_DATE_RANGE: {{ .Values.ledger.configmap.MAX_PAGINATION_MONTH_DATE_RANGE | default "3" | quote }} + MAX_PAGINATION_LIMIT: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MAX_PAGINATION_LIMIT" "params" .Values.ledger.pagination "field" "maxLimit" "default" "100") | quote }} + MAX_PAGINATION_MONTH_DATE_RANGE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MAX_PAGINATION_MONTH_DATE_RANGE" "params" .Values.ledger.pagination "field" "maxMonthRange" "default" "3") | quote }} # ============================================================================= # BALANCE SYNC WORKER # ============================================================================= - BALANCE_SYNC_WORKER_ENABLED: {{ .Values.ledger.configmap.BALANCE_SYNC_WORKER_ENABLED | default "false" | quote }} - BALANCE_SYNC_MAX_WORKERS: {{ .Values.ledger.configmap.BALANCE_SYNC_MAX_WORKERS | default "5" | quote }} - BALANCE_SYNC_BATCH_SIZE: {{ .Values.ledger.configmap.BALANCE_SYNC_BATCH_SIZE | default "50" | quote }} - BALANCE_SYNC_FLUSH_TIMEOUT_MS: {{ .Values.ledger.configmap.BALANCE_SYNC_FLUSH_TIMEOUT_MS | default "500" | quote }} - BALANCE_SYNC_POLL_INTERVAL_MS: {{ .Values.ledger.configmap.BALANCE_SYNC_POLL_INTERVAL_MS | default "50" | quote }} + BALANCE_SYNC_WORKER_ENABLED: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "BALANCE_SYNC_WORKER_ENABLED" "params" .Values.ledger.balanceSync "field" "enabled" "default" "false") | quote }} + BALANCE_SYNC_MAX_WORKERS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "BALANCE_SYNC_MAX_WORKERS" "params" .Values.ledger.balanceSync "field" "maxWorkers" "default" "5") | quote }} + BALANCE_SYNC_BATCH_SIZE: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "BALANCE_SYNC_BATCH_SIZE" "params" .Values.ledger.balanceSync "field" "batchSize" "default" "50") | quote }} + BALANCE_SYNC_FLUSH_TIMEOUT_MS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "BALANCE_SYNC_FLUSH_TIMEOUT_MS" "params" .Values.ledger.balanceSync "field" "flushTimeoutMs" "default" "500") | quote }} + BALANCE_SYNC_POLL_INTERVAL_MS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "BALANCE_SYNC_POLL_INTERVAL_MS" "params" .Values.ledger.balanceSync "field" "pollIntervalMs" "default" "50") | quote }} # ============================================================================= # RATE LIMITING @@ -239,35 +270,45 @@ data: # Set RATE_LIMIT_ENABLED=false to disable (requires ALLOW_RATELIMIT_DISABLED in strict tier) # ALLOW_RATELIMIT_DISABLED: Set a detailed reason message explaining why rate limiting is disabled (e.g., "Service processes batch jobs only, no user-facing API endpoints") # ALLOW_RATELIMIT_FAIL_OPEN: Set a detailed reason message to allow fail-open mode in production - RATE_LIMIT_ENABLED: {{ .Values.ledger.configmap.RATE_LIMIT_ENABLED | default "true" | quote }} - RATE_LIMIT_MAX: {{ .Values.ledger.configmap.RATE_LIMIT_MAX | default "500" | quote }} - RATE_LIMIT_WINDOW_SEC: {{ .Values.ledger.configmap.RATE_LIMIT_WINDOW_SEC | default "60" | quote }} - AGGRESSIVE_RATE_LIMIT_MAX: {{ .Values.ledger.configmap.AGGRESSIVE_RATE_LIMIT_MAX | default "100" | quote }} - AGGRESSIVE_RATE_LIMIT_WINDOW_SEC: {{ .Values.ledger.configmap.AGGRESSIVE_RATE_LIMIT_WINDOW_SEC | default "60" | quote }} - RELAXED_RATE_LIMIT_MAX: {{ .Values.ledger.configmap.RELAXED_RATE_LIMIT_MAX | default "1000" | quote }} - RELAXED_RATE_LIMIT_WINDOW_SEC: {{ .Values.ledger.configmap.RELAXED_RATE_LIMIT_WINDOW_SEC | default "60" | quote }} - RATE_LIMIT_REDIS_TIMEOUT_MS: {{ .Values.ledger.configmap.RATE_LIMIT_REDIS_TIMEOUT_MS | default "500" | quote }} - ALLOW_RATELIMIT_DISABLED: {{ .Values.ledger.configmap.ALLOW_RATELIMIT_DISABLED | default "" | quote }} - ALLOW_RATELIMIT_FAIL_OPEN: {{ .Values.ledger.configmap.ALLOW_RATELIMIT_FAIL_OPEN | default "" | quote }} - - SD_ADDRESS: {{ .Values.ledger.configmap.SD_ADDRESS | default "localhost:8500" | quote }} - SD_ALLOW_STALE: {{ .Values.ledger.configmap.SD_ALLOW_STALE | default "" | quote }} - SD_DIAL_TIMEOUT: {{ .Values.ledger.configmap.SD_DIAL_TIMEOUT | default "" | quote }} - SD_ENABLED: {{ .Values.ledger.configmap.SD_ENABLED | default "false" | quote }} - SD_EXTERNAL_ADDRESS: {{ .Values.ledger.configmap.SD_EXTERNAL_ADDRESS | default "" | quote }} - SD_EXTERNAL_PORT: {{ .Values.ledger.configmap.SD_EXTERNAL_PORT | default "" | quote }} - SD_INTERNAL_ADDRESS: {{ .Values.ledger.configmap.SD_INTERNAL_ADDRESS | default "" | quote }} - SD_INTERNAL_PORT: {{ .Values.ledger.configmap.SD_INTERNAL_PORT | default "" | quote }} - SD_INTERNAL_SCHEME: {{ .Values.ledger.configmap.SD_INTERNAL_SCHEME | default "" | quote }} - SD_PREFER_VIEW: {{ .Values.ledger.configmap.SD_PREFER_VIEW | default "" | quote }} - SD_RESPONSE_HEADER_TIMEOUT: {{ .Values.ledger.configmap.SD_RESPONSE_HEADER_TIMEOUT | default "" | quote }} - SD_SEED_TIMEOUT: {{ .Values.ledger.configmap.SD_SEED_TIMEOUT | default "" | quote }} - SD_TLS: {{ .Values.ledger.configmap.SD_TLS | default "false" | quote }} - SD_TLS_HANDSHAKE_TIMEOUT: {{ .Values.ledger.configmap.SD_TLS_HANDSHAKE_TIMEOUT | default "" | quote }} - SD_TLS_SKIP_VERIFY: {{ .Values.ledger.configmap.SD_TLS_SKIP_VERIFY | default "false" | quote }} - SD_WATCH_WAIT_TIME: {{ .Values.ledger.configmap.SD_WATCH_WAIT_TIME | default "" | quote }} - SD_WORKLOAD: {{ .Values.ledger.configmap.SD_WORKLOAD | default "" | quote }} - STREAMING_ENABLED: {{ .Values.ledger.configmap.STREAMING_ENABLED | default "false" | quote }} + RATE_LIMIT_ENABLED: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RATE_LIMIT_ENABLED" "params" .Values.ledger.rateLimit "field" "enabled" "default" "true") | quote }} + RATE_LIMIT_MAX: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RATE_LIMIT_MAX" "params" .Values.ledger.rateLimit "field" "max" "default" "500") | quote }} + RATE_LIMIT_WINDOW_SEC: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RATE_LIMIT_WINDOW_SEC" "params" .Values.ledger.rateLimit "field" "windowSec" "default" "60") | quote }} + AGGRESSIVE_RATE_LIMIT_MAX: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "AGGRESSIVE_RATE_LIMIT_MAX" "params" .Values.ledger.rateLimit "field" "aggressiveMax" "default" "100") | quote }} + AGGRESSIVE_RATE_LIMIT_WINDOW_SEC: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "AGGRESSIVE_RATE_LIMIT_WINDOW_SEC" "params" .Values.ledger.rateLimit "field" "aggressiveWindowSec" "default" "60") | quote }} + RELAXED_RATE_LIMIT_MAX: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RELAXED_RATE_LIMIT_MAX" "params" .Values.ledger.rateLimit "field" "relaxedMax" "default" "1000") | quote }} + RELAXED_RATE_LIMIT_WINDOW_SEC: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RELAXED_RATE_LIMIT_WINDOW_SEC" "params" .Values.ledger.rateLimit "field" "relaxedWindowSec" "default" "60") | quote }} + RATE_LIMIT_REDIS_TIMEOUT_MS: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "RATE_LIMIT_REDIS_TIMEOUT_MS" "params" .Values.ledger.rateLimit "field" "redisTimeoutMs" "default" "500") | quote }} + ALLOW_RATELIMIT_DISABLED: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "ALLOW_RATELIMIT_DISABLED" "params" .Values.ledger.rateLimit "field" "allowDisabled" "default" "") | quote }} + ALLOW_RATELIMIT_FAIL_OPEN: {{ include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "ALLOW_RATELIMIT_FAIL_OPEN" "params" .Values.ledger.rateLimit "field" "allowFailOpen" "default" "") | quote }} + # SERVICE DISCOVERY — enable via ledger.serviceDiscovery.enabled; env-wide server + # config derived from global.serviceDiscovery; endpoints derived from this chart's + # own service/ingress. Emitted before extraEnvVars so an operator can still override. + {{- /* Legacy configmap.SD_ENABLED wins over the grouped param (presence-based); + compare to "true" so a legacy "false" string does not evaluate truthy. Same + resolved bool gates both the env var and the helper. */ -}} + {{- $sdEnabled := eq (include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "SD_ENABLED" "params" .Values.ledger.serviceDiscovery "field" "enabled" "default" "false")) "true" }} + SD_ENABLED: {{ $sdEnabled | quote }} + {{- include "lerian-common.serviceDiscovery.env" (dict + "context" $ + "enabled" $sdEnabled + "configmap" .Values.ledger.configmap + "name" (include "midaz-ledger.fullname" .) + "port" .Values.ledger.service.port + "namespace" (include "global.namespace" .) + "ingressHost" (include "lerian-common.firstIngressHost" (dict "ingress" .Values.ledger.ingress))) | nindent 2 }} + # Enable via ledger.streaming.enabled; brokers/SASL/TLS come from global.streaming + # (env-wide). Emitted only when enabled → configmap stays clean when off. + {{- /* Legacy configmap.STREAMING_ENABLED wins over the grouped param + (presence-based); compare to "true" so a legacy "false" string does not + evaluate truthy. Same resolved bool gates both the env var and the helper. */ -}} + {{- $streamingEnabled := eq (include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "STREAMING_ENABLED" "params" .Values.ledger.streaming "field" "enabled" "default" "false")) "true" }} + STREAMING_ENABLED: {{ $streamingEnabled | quote }} + {{- include "lerian-common.streaming.env" (dict + "context" $ + "enabled" $streamingEnabled + "configmap" .Values.ledger.configmap + "clientId" (include "midaz-ledger.fullname" .) + "cloudeventsSource" "lerian.midaz.ledger") | nindent 2 }} SWAGGER_VERSION: {{ .Values.ledger.configmap.SWAGGER_VERSION | default "${VERSION}" | quote }} # Extra Env Vars {{- with .Values.ledger.extraEnvVars }} diff --git a/charts/midaz/templates/ledger/deployment.yaml b/charts/midaz/templates/ledger/deployment.yaml index d794b928c..26edddd5e 100644 --- a/charts/midaz/templates/ledger/deployment.yaml +++ b/charts/midaz/templates/ledger/deployment.yaml @@ -6,7 +6,7 @@ metadata: labels: {{- include "midaz.labels" (dict "context" . "component" .Values.ledger.name "name" .Values.ledger.name ) | nindent 4 }} spec: - revisionHistoryLimit: {{ .Values.ledger.revisionHistoryLimit | default 10 }} + revisionHistoryLimit: {{ dig "revisionHistoryLimit" 10 .Values.ledger }} strategy: type: {{ .Values.ledger.deploymentUpdate.type }} {{- if eq .Values.ledger.deploymentUpdate.type "RollingUpdate" }} diff --git a/charts/midaz/templates/ledger/ingress.yaml b/charts/midaz/templates/ledger/ingress.yaml index ee946c7fc..0b4e146fb 100644 --- a/charts/midaz/templates/ledger/ingress.yaml +++ b/charts/midaz/templates/ledger/ingress.yaml @@ -1,61 +1,12 @@ +{{- $gIng := (.Values.global | default dict).ingress | default dict -}} {{- if and .Values.ledger.enabled .Values.ledger.ingress.enabled -}} -{{- $fullName := include "midaz-ledger.fullname" . -}} -{{- $svcPort := .Values.ledger.service.port -}} -{{- if and .Values.ledger.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} - {{- if not (hasKey .Values.ledger.ingress.annotations "kubernetes.io/ingress.class") }} - {{- $_ := set .Values.ledger.ingress.annotations "kubernetes.io/ingress.class" .Values.ledger.ingress.className}} - {{- end }} -{{- end }} -{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1 -{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: networking.k8s.io/v1beta1 -{{- else -}} -apiVersion: extensions/v1beta1 -{{- end }} -kind: Ingress -metadata: - name: {{ $fullName }} - labels: - {{- include "midaz.labels" (dict "context" . "component" .Values.ledger.name "name" .Values.ledger.name ) | nindent 4 }} - {{- with .Values.ledger.ingress.annotations }} - annotations: - {{- toYaml . | nindent 4 }} - {{- end }} -spec: - {{- if and .Values.ledger.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} - ingressClassName: {{ .Values.ledger.ingress.className }} - {{- end }} - {{- if .Values.ledger.ingress.tls }} - tls: - {{- range .Values.ledger.ingress.tls }} - - hosts: - {{- range .hosts }} - - {{ . | quote }} - {{- end }} - secretName: {{ .secretName }} - {{- end }} - {{- end }} - rules: - {{- range .Values.ledger.ingress.hosts }} - - host: {{ .host | quote }} - http: - paths: - {{- range .paths }} - - path: {{ .path }} - {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} - pathType: {{ .pathType }} - {{- end }} - backend: - {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} - service: - name: {{ $fullName }} - port: - number: {{ $svcPort }} - {{- else }} - serviceName: {{ $fullName }} - servicePort: {{ $svcPort }} - {{- end }} - {{- end }} - {{- end }} +{{- include "lerian-common.ingress" (dict + "context" . + "ingress" .Values.ledger.ingress + "global" $gIng + "subdomain" "ledger" + "name" (include "midaz-ledger.fullname" .) + "labels" (include "midaz.labels" (dict "context" . "component" .Values.ledger.name "name" .Values.ledger.name)) + "svcPort" .Values.ledger.service.port + ) }} {{- end }} diff --git a/charts/midaz/templates/ledger/pdb.yaml b/charts/midaz/templates/ledger/pdb.yaml index 9454b1779..57d1a2b67 100644 --- a/charts/midaz/templates/ledger/pdb.yaml +++ b/charts/midaz/templates/ledger/pdb.yaml @@ -12,10 +12,14 @@ metadata: {{- end }} {{- end }} spec: - {{- with .Values.ledger.pdb.maxUnavailable }} - maxUnavailable: {{ . }} + {{- /* Presence-based so an explicit 0 (maxUnavailable: 0 = block all disruptions, + minAvailable: 0 = allow all) is honored; a PDB must carry exactly one of the two. */ -}} + {{- if hasKey .Values.ledger.pdb "maxUnavailable" }} + maxUnavailable: {{ .Values.ledger.pdb.maxUnavailable }} + {{- else if hasKey .Values.ledger.pdb "minAvailable" }} + minAvailable: {{ .Values.ledger.pdb.minAvailable }} {{- else }} - minAvailable: {{ .Values.ledger.pdb.minAvailable | default 1 }} + minAvailable: 1 {{- end }} selector: matchLabels: diff --git a/charts/midaz/templates/ledger/secrets.yaml b/charts/midaz/templates/ledger/secrets.yaml index d872ef3bc..3761da899 100644 --- a/charts/midaz/templates/ledger/secrets.yaml +++ b/charts/midaz/templates/ledger/secrets.yaml @@ -56,11 +56,13 @@ data: # ============================================================================= # MULTI-TENANT SECRETS # ============================================================================= - {{- if eq (.Values.ledger.configmap.MULTI_TENANT_ENABLED | default "false" | toString) "true" }} - MULTI_TENANT_SERVICE_API_KEY: {{ required "ledger.secrets.MULTI_TENANT_SERVICE_API_KEY is required when MULTI_TENANT_ENABLED=true" .Values.ledger.secrets.MULTI_TENANT_SERVICE_API_KEY | b64enc | quote }} - {{- if .Values.ledger.secrets.MULTI_TENANT_REDIS_PASSWORD }} - MULTI_TENANT_REDIS_PASSWORD: {{ .Values.ledger.secrets.MULTI_TENANT_REDIS_PASSWORD | b64enc | quote }} - {{- end }} + {{- with (include "lerian-common.multiTenant.secret" (dict + "context" . "secrets" .Values.ledger.secrets + "secretName" (include "midaz-ledger.fullname" .) + "valuesPrefix" "ledger.secrets." "mode" "data" + "enabled" (eq (include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "MULTI_TENANT_ENABLED" "params" .Values.ledger.multiTenant "field" "enabled" "default" "false")) "true") + "useExistingSecret" .Values.ledger.useExistingSecret)) }} + {{- . | nindent 2 }} {{- end }} # ============================================================================= @@ -71,10 +73,16 @@ data: # ============================================================================= # STREAMING SECRETS (lib-streaming — SASL password + TLS CA, operator-provided) # ============================================================================= - {{- if .Values.ledger.secrets.STREAMING_SASL_PASSWORD }} - STREAMING_SASL_PASSWORD: {{ .Values.ledger.secrets.STREAMING_SASL_PASSWORD | b64enc | quote }} - {{- end }} - {{- if .Values.ledger.secrets.STREAMING_TLS_CA_CERT }} - STREAMING_TLS_CA_CERT: {{ .Values.ledger.secrets.STREAMING_TLS_CA_CERT | b64enc | quote }} + {{- /* SASL password is REQUIRED when a SASL mechanism is configured; the helper + fails the render otherwise (lib-streaming needs username+password for SASL), so a + boot-crashing config is caught at deploy time. Mechanism precedence mirrors the + ConfigMap: configmap.STREAMING_SASL_MECHANISM -> global.streaming.saslMechanism. */ -}} + {{- $streamingEnabled := eq (include "lerian-common.cfgValue" (dict "configmap" .Values.ledger.configmap "nativeKey" "STREAMING_ENABLED" "params" .Values.ledger.streaming "field" "enabled" "default" "false")) "true" }} + {{- $saslMech := (((.Values.global | default dict).streaming | default dict).saslMechanism | default "") }} + {{- if hasKey .Values.ledger.configmap "STREAMING_SASL_MECHANISM" }}{{- $saslMech = index .Values.ledger.configmap "STREAMING_SASL_MECHANISM" }}{{- end }} + {{- $saslUser := (((.Values.global | default dict).streaming | default dict).saslUsername | default "") }} + {{- if hasKey .Values.ledger.configmap "STREAMING_SASL_USERNAME" }}{{- $saslUser = index .Values.ledger.configmap "STREAMING_SASL_USERNAME" }}{{- end }} + {{- with (include "lerian-common.streaming.secret" (dict "context" $ "enabled" $streamingEnabled "useExistingSecret" .Values.ledger.useExistingSecret "secrets" .Values.ledger.secrets "mode" "data" "saslMechanism" $saslMech "saslUsername" $saslUser "valuesPrefix" "ledger.secrets." "secretName" (include "midaz-ledger.fullname" .))) }} + {{- . | nindent 2 }} {{- end }} {{- end }} diff --git a/charts/midaz/templates/vault/transit-init-job.yaml b/charts/midaz/templates/vault/transit-init-job.yaml new file mode 100644 index 000000000..8cc6917e8 --- /dev/null +++ b/charts/midaz/templates/vault/transit-init-job.yaml @@ -0,0 +1,111 @@ +{{- if .Values.vault.enabled }} +{{- /* + Vault Transit init — mirrors the `midaz-hc-vault-init` service in + midaz components/infra/docker-compose.yml. Runs as a post-install/post-upgrade + Helm hook (after the bundled Vault pod is up) and enables the Transit secrets + engine at the mode-derived mount: `transit-mt` when multi-tenancy is on, else + `transit-st`. Idempotent: skips if the engine is already enabled. + + Multi-tenant toggle uses the SAME cfgValue precedence as the CRM ConfigMap + (legacy configmap.MULTI_TENANT_ENABLED wins over the grouped crm.multiTenant + param) — CRM is the KMS consumer and docker-compose sources this from crm/.env. +*/}} +{{- $vaultAddr := printf "http://%s:8200" (.Values.vault.fullnameOverride | default "midaz-hc-vault") }} +{{- $devToken := .Values.vault.server.dev.devRootToken | default "root" }} +{{- $mtEnabled := eq (include "lerian-common.cfgValue" (dict "configmap" .Values.crm.configmap "nativeKey" "MULTI_TENANT_ENABLED" "params" .Values.crm.multiTenant "field" "enabled" "default" "false")) "true" }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "midaz.name" . }}-vault-transit-init + namespace: {{ .Release.Namespace }} + labels: + {{- include "midaz.labels" (dict "context" . "component" "vault-transit-init" "name" "vault-transit-init") | nindent 4 }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "5" + # Keep the completed Job for inspection; the next install/upgrade deletes the + # prior one before re-creating (so success and failure logs both survive). + "helm.sh/hook-delete-policy": before-hook-creation +spec: + completions: 1 + parallelism: 1 + backoffLimit: 6 + template: + metadata: + labels: + {{- include "midaz.selectorLabels" (dict "context" . "component" "vault-transit-init" "name" "vault-transit-init") | nindent 8 }} + spec: + restartPolicy: Never + securityContext: + runAsNonRoot: true + runAsUser: 100 + runAsGroup: 1000 + fsGroup: 1000 + seccompProfile: + type: RuntimeDefault + containers: + - name: vault-transit-init + image: hashicorp/vault:1.15 + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + env: + - name: VAULT_ADDR + value: {{ $vaultAddr | quote }} + - name: VAULT_TOKEN + value: {{ $devToken | quote }} + # Resolved at render time from the CRM multi-tenant toggle; the shell then + # selects the shared Transit mount for the mode (transit-mt vs transit-st). + - name: MULTI_TENANT_ENABLED + value: {{ $mtEnabled | quote }} + - name: HOME + value: /tmp + command: + - /bin/sh + - -c + - | + set -e + if [ "${MULTI_TENANT_ENABLED}" = "true" ]; then + MOUNT="transit-mt" + else + MOUNT="transit-st" + fi + + echo "Waiting for Vault at ${VAULT_ADDR} to be ready..." + ELAPSED=0 + TIMEOUT=300 + until vault status >/dev/null 2>&1; do + if [ "${ELAPSED}" -ge "${TIMEOUT}" ]; then + echo "Timeout waiting for Vault after ${TIMEOUT}s" >&2 + exit 1 + fi + echo "Vault not ready yet, waiting... (${ELAPSED}s/${TIMEOUT}s)" + sleep 3 + ELAPSED=$((ELAPSED + 3)) + done + echo "Vault is ready." + + echo "Checking if Transit is already enabled at ${MOUNT}..." + EXISTING_TYPE=$(vault secrets list | awk -v m="${MOUNT}/" '$1 == m { print $2 }') + if [ -n "${EXISTING_TYPE}" ]; then + if [ "${EXISTING_TYPE}" != "transit" ]; then + echo "ERROR: ${MOUNT}/ exists but is type '${EXISTING_TYPE}', not 'transit'" >&2 + exit 1 + fi + echo "Transit already enabled at ${MOUNT}" + else + echo "Enabling Transit secrets engine at ${MOUNT}..." + vault secrets enable -path="${MOUNT}" transit + echo "Transit enabled successfully" + fi + echo "Vault initialization complete" + volumeMounts: + - name: tmp + mountPath: /tmp + volumes: + - name: tmp + emptyDir: {} +{{- end }} diff --git a/charts/midaz/values.yaml b/charts/midaz/values.yaml index 2120d15c0..e022e98e8 100644 --- a/charts/midaz/values.yaml +++ b/charts/midaz/values.yaml @@ -6,6 +6,11 @@ nameOverride: "midaz" fullnameOverride: "" namespaceOverride: "midaz" global: + # -- Env-wide environment name (e.g. `production`/`staging`); single source consumed by + # every environment-dependent key (ENV_NAME, OTEL_RESOURCE_DEPLOYMENT_ENVIRONMENT) across + # components. Per-component `.env` and legacy `configmap.ENV_NAME` override it. Empty + # by default so component defaults still apply (ledger `production`, crm `development`). + environment: "" # -- Bootstrap job for external RabbitMQ: creates users, vhosts, and permissions externalRabbitmqDefinitions: # -- Enable or disable the RabbitMQ bootstrap job @@ -100,6 +105,27 @@ global: db: "transaction" - role: "readWrite" db: "crm" + + # -- Service discovery (lib-service-discovery) — env-wide, shared by every + # component; each app opts in via .serviceDiscovery.enabled. + serviceDiscovery: + address: "" # Consul host:port → SD_ADDRESS + tls: false # → SD_TLS + tlsSkipVerify: false # → SD_TLS_SKIP_VERIFY (set true only to opt out of TLS verification) + workload: "" # per-env isolation key → SD_WORKLOAD + preferView: "internal" # internal|external → SD_PREFER_VIEW + # -- Streaming (lib-streaming) — env-wide broker/SASL/TLS config. + streaming: + brokers: "" # host:port → STREAMING_BROKERS + tlsEnabled: false # → STREAMING_TLS_ENABLED + saslMechanism: "" # e.g. SCRAM-SHA-256 → STREAMING_SASL_MECHANISM + saslUsername: "" # → STREAMING_SASL_USERNAME + # -- Multi-tenant — env-wide tenant-manager endpoint + its redis. + multiTenant: + url: "" # → MULTI_TENANT_URL + redisHost: "" # → MULTI_TENANT_REDIS_HOST + redisPort: "6379" # → MULTI_TENANT_REDIS_PORT + redisTls: false # → MULTI_TENANT_REDIS_TLS ledger: # -- Readiness probe configuration. All fields override chart defaults. readinessProbe: {} @@ -224,77 +250,210 @@ ledger: # values: # - midaz - # -- ConfigMap for environment variables and configurations + # =========================================================================== + # PRODUCTIZED CONFIG — clean, grouped params for the knobs clients tune most. + # Backward-compatible: the raw `configmap.` below still works and OVERRIDES + # these (precedence: configmap. > these params > default). Defaults here + # match the legacy template defaults, so existing installs render unchanged. + # Connection host/user/port/ssl/params for datastores come from the + # global.datastores mask (shared) or ledger.datastores (dedicated); auth & + # telemetry are the cross-product contracts global.auth / global.observability. + # =========================================================================== + # --- App ------------------------------------------------------------------- + # -- ENV_NAME override for this component (optional). When unset it falls back to + # `global.environment`, then the chart default (`production`). Uncomment to pin. + # env: "production" + # -- LOG_LEVEL (legacy default; set "info" for production) + logLevel: "debug" + # -- ALLOW_INSECURE_TLS (set "false" for strict TLS in prod) + allowInsecureTls: "true" + # -- DEPLOYMENT_MODE (app run mode; "local" | ...) + deploymentMode: "local" # → DEPLOYMENT_MODE + + # --- Server (bind host/port the app listens on) ---------------------------- + server: + port: "3002" # → SERVER_PORT + address: ":3002" # → SERVER_ADDRESS + + # --- Telemetry (OTEL export toggle; endpoints → global.observability) ------- + telemetry: + enabled: "true" # → ENABLE_TELEMETRY (matches main default) + + # --- Swagger / OpenAPI metadata (cosmetic; empty = derived by the app) ------ + swagger: + title: "" # → SWAGGER_TITLE + description: "" # → SWAGGER_DESCRIPTION + host: "" # → SWAGGER_HOST + basePath: "" # → SWAGGER_BASE_PATH + schemes: "" # → SWAGGER_SCHEMES + leftDelim: "" # → SWAGGER_LEFT_DELIM + rightDelim: "" # → SWAGGER_RIGHT_DELIM + + # --- Accounting ------------------------------------------------------------ + accounting: + # -- ACCOUNT_TYPE_VALIDATION (":") + accountTypeValidation: "" + # -- TRANSACTION_ROUTE_VALIDATION + transactionRouteValidation: "" + + # --- Rate limiting (three-tier, Redis-backed) ------------------------------ + rateLimit: + enabled: true # → RATE_LIMIT_ENABLED + max: "500" # → RATE_LIMIT_MAX + windowSec: "60" # → RATE_LIMIT_WINDOW_SEC + aggressiveMax: "100" # → AGGRESSIVE_RATE_LIMIT_MAX + aggressiveWindowSec: "60" # → AGGRESSIVE_RATE_LIMIT_WINDOW_SEC + relaxedMax: "1000" # → RELAXED_RATE_LIMIT_MAX + relaxedWindowSec: "60" # → RELAXED_RATE_LIMIT_WINDOW_SEC + redisTimeoutMs: "500" # → RATE_LIMIT_REDIS_TIMEOUT_MS + allowDisabled: "" # → ALLOW_RATELIMIT_DISABLED (reason required when disabled in strict tier) + allowFailOpen: "" # → ALLOW_RATELIMIT_FAIL_OPEN (reason to allow fail-open in prod) + + # --- Audit ----------------------------------------------------------------- + audit: + enabled: false # → AUDIT_LOG_ENABLED + + # --- Bulk recorder --------------------------------------------------------- + bulkRecorder: + enabled: true # → BULK_RECORDER_ENABLED + size: "0" # → BULK_RECORDER_SIZE + flushTimeoutMs: "100" # → BULK_RECORDER_FLUSH_TIMEOUT_MS + maxRowsPerInsert: "1000" # → BULK_RECORDER_MAX_ROWS_PER_INSERT + + # --- Balance sync worker --------------------------------------------------- + balanceSync: + enabled: false # → BALANCE_SYNC_WORKER_ENABLED + maxWorkers: "5" # → BALANCE_SYNC_MAX_WORKERS + batchSize: "50" # → BALANCE_SYNC_BATCH_SIZE + flushTimeoutMs: "500" # → BALANCE_SYNC_FLUSH_TIMEOUT_MS + pollIntervalMs: "50" # → BALANCE_SYNC_POLL_INTERVAL_MS + + # --- Pagination ------------------------------------------------------------ + pagination: + maxLimit: "100" # → MAX_PAGINATION_LIMIT + maxMonthRange: "3" # → MAX_PAGINATION_MONTH_DATE_RANGE + + # --- Datastores (per module) ----------------------------------------------- + # DB identity + connection pool. Host/user/port/ssl come from the + # global.datastores.postgres mask (shared) or ledger.datastores.postgres (dedicated). + database: # PostgreSQL, per module + onboarding: + name: "onboarding" # → DB_ONBOARDING_NAME + replicaName: "onboarding" # → DB_ONBOARDING_REPLICA_NAME + maxOpenConns: "3000" # → DB_ONBOARDING_MAX_OPEN_CONNS + maxIdleConns: "3000" # → DB_ONBOARDING_MAX_IDLE_CONNS + transaction: + name: "transaction" # → DB_TRANSACTION_NAME + replicaName: "transaction" # → DB_TRANSACTION_REPLICA_NAME + maxOpenConns: "3000" # → DB_TRANSACTION_MAX_OPEN_CONNS + maxIdleConns: "3000" # → DB_TRANSACTION_MAX_IDLE_CONNS + mongo: # MongoDB, per module (host/user/port/params → global.datastores.mongo) + onboarding: + uri: "mongodb" # → MONGO_ONBOARDING_URI + name: "onboarding" # → MONGO_ONBOARDING_NAME + maxPoolSize: "1000" # → MONGO_ONBOARDING_MAX_POOL_SIZE + tlsCaCert: "" # → MONGO_ONBOARDING_TLS_CA_CERT (PEM CA for TLS) + transaction: + uri: "mongodb" # → MONGO_TRANSACTION_URI + name: "transaction" # → MONGO_TRANSACTION_NAME + maxPoolSize: "1000" # → MONGO_TRANSACTION_MAX_POOL_SIZE + tlsCaCert: "" # → MONGO_TRANSACTION_TLS_CA_CERT (PEM CA for TLS) + + # --- Redis / Valkey (client tuning) ---------------------------------------- + # Connection host → global.datastores.redis; password → ledger.secrets.REDIS_PASSWORD. + redis: + masterName: "" # → REDIS_MASTER_NAME (sentinel) + tls: "false" # → REDIS_TLS + caCert: "" # → REDIS_CA_CERT + useGcpIam: "false" # → REDIS_USE_GCP_IAM + serviceAccount: "" # → REDIS_SERVICE_ACCOUNT + googleAppCredentials: "" # → GOOGLE_APPLICATION_CREDENTIALS + tokenLifetime: "60" # → REDIS_TOKEN_LIFETIME + tokenRefreshDuration: "45" # → REDIS_TOKEN_REFRESH_DURATION + db: "0" # → REDIS_DB + protocol: "3" # → REDIS_PROTOCOL + poolSize: "10" # → REDIS_POOL_SIZE + minIdleConns: "0" # → REDIS_MIN_IDLE_CONNS + readTimeout: "3" # → REDIS_READ_TIMEOUT + writeTimeout: "3" # → REDIS_WRITE_TIMEOUT + dialTimeout: "5" # → REDIS_DIAL_TIMEOUT + poolTimeout: "2" # → REDIS_POOL_TIMEOUT + maxRetries: "3" # → REDIS_MAX_RETRIES + minRetryBackoff: "8" # → REDIS_MIN_RETRY_BACKOFF + maxRetryBackoff: "1" # → REDIS_MAX_RETRY_BACKOFF + + # --- Broker (RabbitMQ) ----------------------------------------------------- + # Host → global.datastores.broker; passwords → ledger.secrets. + broker: + uri: "amqp" # → RABBITMQ_URI + protocol: "http" # → RABBITMQ_PROTOCOL (management API scheme; also drives healthCheckUrl) + vhost: "" # → RABBITMQ_VHOST + portHost: "5672" # → RABBITMQ_PORT_HOST + portAmqp: "15672" # → RABBITMQ_PORT_AMQP + defaultUser: "transaction" # → RABBITMQ_DEFAULT_USER + consumerUser: "consumer" # → RABBITMQ_CONSUMER_USER + transactionAsync: "false" # → RABBITMQ_TRANSACTION_ASYNC + workers: "5" # → RABBITMQ_NUMBERS_OF_WORKERS + prefetch: "10" # → RABBITMQ_NUMBERS_OF_PREFETCH + # routing (queue/exchange/key names) + balanceCreateQueue: "transaction.balance_create.queue" # → RABBITMQ_BALANCE_CREATE_QUEUE + balanceOperationExchange: "transaction.transaction_balance_operation.exchange" # → RABBITMQ_TRANSACTION_BALANCE_OPERATION_EXCHANGE + balanceOperationKey: "transaction.transaction_balance_operation.key" # → RABBITMQ_TRANSACTION_BALANCE_OPERATION_KEY + balanceOperationQueue: "transaction.transaction_balance_operation.queue" # → RABBITMQ_TRANSACTION_BALANCE_OPERATION_QUEUE + # events + audit routing + transactionEventsEnabled: "false" # → RABBITMQ_TRANSACTION_EVENTS_ENABLED + transactionEventsExchange: "transaction.transaction_events.exchange" # → RABBITMQ_TRANSACTION_EVENTS_EXCHANGE + overdraftEventsEnabled: "false" # → RABBITMQ_OVERDRAFT_EVENTS_ENABLED + overdraftEventsExchange: "transaction.overdraft_events.exchange" # → RABBITMQ_OVERDRAFT_EVENTS_EXCHANGE + auditExchange: "audit.append_log.exchange" # → RABBITMQ_AUDIT_EXCHANGE + auditKey: "audit.append_log.key" # → RABBITMQ_AUDIT_KEY + # reliability / health + # -- RABBITMQ_HEALTH_CHECK_URL — leave UNSET to keep the computed default + # (protocol://host:portAmqp). Uncomment to pin an explicit URL. + # healthCheckUrl: "" # → RABBITMQ_HEALTH_CHECK_URL + operationTimeout: "30s" # → RABBITMQ_OPERATION_TIMEOUT + # circuit breaker (RABBITMQ_CIRCUIT_BREAKER_*) + circuitBreaker: + consecutiveFailures: "15" # → RABBITMQ_CIRCUIT_BREAKER_CONSECUTIVE_FAILURES + failureRatio: "50" # → RABBITMQ_CIRCUIT_BREAKER_FAILURE_RATIO + interval: "120" # → RABBITMQ_CIRCUIT_BREAKER_INTERVAL + maxRequests: "3" # → RABBITMQ_CIRCUIT_BREAKER_MAX_REQUESTS + minRequests: "10" # → RABBITMQ_CIRCUIT_BREAKER_MIN_REQUESTS + timeout: "30" # → RABBITMQ_CIRCUIT_BREAKER_TIMEOUT + healthCheckInterval: "30" # → RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_INTERVAL + healthCheckTimeout: "10" # → RABBITMQ_CIRCUIT_BREAKER_HEALTH_CHECK_TIMEOUT + + # -- ConfigMap for environment variables and configurations (raw/legacy format; + # still fully supported and takes PRECEDENCE over the productized params above). + # Empty by default — every knob a client tunes is a clean param above, and + # connection host/user/port/ssl/params come from the global.datastores mask. + # Any key set here WINS over the productized params + mask (full backward- + # compatibility) — e.g. ENABLE_TELEMETRY, PLUGIN_AUTH_HOST, DB_ONBOARDING_HOST, + # REDIS_HOST, RABBITMQ_HOST, MONGO_ONBOARDING_PARAMETERS, ALLOW_RATELIMIT_DISABLED. + # --- Service discovery (lib-service-discovery) -------------------------------- + # Enable per-app here; env-wide server config (Consul address/TLS/workload/ + # preferView) comes from global.serviceDiscovery, and the internal/external + # endpoints are derived from this chart's own service + ingress. INERT until + # enabled AND global.serviceDiscovery is configured (backward-compatible). + # A legacy flat configmap.SD_* key wins over global.serviceDiscovery. + serviceDiscovery: + enabled: false # → SD_ENABLED + + # --- Multi-tenant (lib multi-tenant) ------------------------------------------ + # Enable per-app; the tenant-manager URL + its redis come from global.multiTenant. + # Emitted only when enabled → configmap stays clean when off. + multiTenant: + enabled: false # → MULTI_TENANT_ENABLED + + # --- Streaming (lib-streaming) ------------------------------------------------ + # Enable per-app; brokers/SASL/TLS come from global.streaming. Client identity + # (clientId/cloudeventsSource) is derived per component. Emitted only when enabled. + # A legacy flat configmap.STREAMING_* key wins over global.streaming. + streaming: + enabled: false # → STREAMING_ENABLED + # @default -- templates/ledger/configmap.yaml - configmap: - ALLOW_INSECURE_TLS: "true" - # -- Otel default configuration - ENABLE_TELEMETRY: "true" - # -- Auth Plugin configuration - PLUGIN_AUTH_ENABLED: "false" - PLUGIN_AUTH_HOST: "" - # -- Accounting config - ACCOUNT_TYPE_VALIDATION: "" - TRANSACTION_ROUTE_VALIDATION: "" - # -- PostgreSQL Onboarding module - DB_ONBOARDING_HOST: "midaz-postgresql-primary" - DB_ONBOARDING_USER: "midaz" - DB_ONBOARDING_NAME: "onboarding" - DB_ONBOARDING_PORT: "5432" - DB_ONBOARDING_REPLICA_HOST: "midaz-postgresql-replication" - DB_ONBOARDING_REPLICA_USER: "midaz" - DB_ONBOARDING_REPLICA_NAME: "onboarding" - DB_ONBOARDING_REPLICA_PORT: "5432" - # -- MongoDB Onboarding module - MONGO_ONBOARDING_URI: "mongodb" - MONGO_ONBOARDING_HOST: "midaz-mongodb" - MONGO_ONBOARDING_NAME: "onboarding" - MONGO_ONBOARDING_USER: "midaz" - MONGO_ONBOARDING_PORT: "27017" - # -- PostgreSQL Transaction module - DB_TRANSACTION_HOST: "midaz-postgresql-primary" - DB_TRANSACTION_USER: "midaz" - DB_TRANSACTION_NAME: "transaction" - DB_TRANSACTION_PORT: "5432" - DB_TRANSACTION_REPLICA_HOST: "midaz-postgresql-replication" - DB_TRANSACTION_REPLICA_USER: "midaz" - DB_TRANSACTION_REPLICA_NAME: "transaction" - DB_TRANSACTION_REPLICA_PORT: "5432" - # -- MongoDB Transaction module - MONGO_TRANSACTION_URI: "mongodb" - MONGO_TRANSACTION_HOST: "midaz-mongodb" - MONGO_TRANSACTION_NAME: "transaction" - MONGO_TRANSACTION_USER: "midaz" - MONGO_TRANSACTION_PORT: "27017" - # -- Redis (shared) - REDIS_HOST: "midaz-valkey-primary:6379" - # -- RabbitMQ configuration - RABBITMQ_URI: "amqp" - RABBITMQ_HOST: "midaz-rabbitmq" - RABBITMQ_PORT_HOST: "5672" - RABBITMQ_PORT_AMQP: "15672" - RABBITMQ_DEFAULT_USER: "transaction" - RABBITMQ_CONSUMER_USER: "consumer" - RABBITMQ_TRANSACTION_ASYNC: "false" - # -- Audit - AUDIT_LOG_ENABLED: "false" - # -- Balance Sync Worker - BALANCE_SYNC_WORKER_ENABLED: "false" - BALANCE_SYNC_MAX_WORKERS: "5" - # -- Rate Limiting - Three-tier system with Redis backend - # -- Set RATE_LIMIT_ENABLED=false to disable (requires ALLOW_RATELIMIT_DISABLED in strict tier) - # -- ALLOW_RATELIMIT_DISABLED: Set a detailed reason message explaining why rate limiting is disabled - # -- ALLOW_RATELIMIT_FAIL_OPEN: Set a detailed reason message to allow fail-open mode in production - RATE_LIMIT_ENABLED: "true" - RATE_LIMIT_MAX: "500" - RATE_LIMIT_WINDOW_SEC: "60" - AGGRESSIVE_RATE_LIMIT_MAX: "100" - AGGRESSIVE_RATE_LIMIT_WINDOW_SEC: "60" - RELAXED_RATE_LIMIT_MAX: "1000" - RELAXED_RATE_LIMIT_WINDOW_SEC: "60" - RATE_LIMIT_REDIS_TIMEOUT_MS: "500" - ALLOW_RATELIMIT_DISABLED: "" - ALLOW_RATELIMIT_FAIL_OPEN: "" + configmap: {} # -- Existing secrets name useExistingSecret: false existingSecretName: "" @@ -461,34 +620,84 @@ crm: # values: # - midaz - # -- ConfigMap for environment variables and configurations + # =========================================================================== + # PRODUCTIZED CONFIG — clean, grouped params (see ledger notes above for the + # precedence contract: configmap. > these params > default; connection + # host/user/port/params → global.datastores.mongo; auth → global.auth). + # =========================================================================== + # --- App ------------------------------------------------------------------- + # -- ENV_NAME override for this component (optional). When unset it falls back to + # `global.environment`, then the chart default (`development`). Uncomment to pin. + # env: "development" + # -- LOG_LEVEL + logLevel: "debug" + # -- ALLOW_INSECURE_TLS (set "false" for strict TLS in prod) + allowInsecureTls: "true" + # -- APPLICATION_NAME (service identifier) + applicationName: "crm" # → APPLICATION_NAME + + # --- Server (bind host/port the app listens on) ---------------------------- + server: + port: "4003" # → SERVER_PORT + address: ":4003" # → SERVER_ADDRESS + + # --- Telemetry (OTEL export toggle; endpoints → global.observability) ------- + telemetry: + enabled: "true" # → ENABLE_TELEMETRY + + # -- Deployment posture; `saas`/`byoc` enforce TLS and reject token KMS auth + deploymentMode: "local" # → DEPLOYMENT_MODE + + # --- KMS (secrets/crypto vault) -------------------------------------------- + kms: + vaultAddr: "http://midaz-hc-vault:8200" # → KMS_VAULT_ADDR + vaultAuthMethod: "token" # → KMS_VAULT_AUTH_METHOD (approle for saas/byoc) + vendor: "hashicorp-vault" # → KMS_VENDOR + vaultRoleId: "" # → KMS_VAULT_ROLE_ID (required with approle; non-secret) + + # --- Swagger / OpenAPI metadata (cosmetic) --------------------------------- + swagger: + title: "CRM" # → SWAGGER_TITLE + description: "The CRM API provides a set of endpoints for managing holder data, including information related to their ledger accounts." # → SWAGGER_DESCRIPTION + host: ":4003" # → SWAGGER_HOST + basePath: "/" # → SWAGGER_BASE_PATH + schemes: "http" # → SWAGGER_SCHEMES + leftDelim: "{{" # → SWAGGER_LEFT_DELIMITER + rightDelim: "}}" # → SWAGGER_RIGHT_DELIMITER + + # --- Datastore · MongoDB (host/user/port/params → global.datastores.mongo) -- + mongo: + uri: "mongodb" # → MONGO_URI + name: "crm" # → MONGO_NAME + maxPoolSize: "1000" # → MONGO_MAX_POOL_SIZE + tlsCaCert: "" # → MONGO_TLS_CA_CERT (PEM CA for TLS) + + # -- ConfigMap for environment variables and configurations (raw/legacy format; + # still fully supported and takes PRECEDENCE over the productized params above). + # Empty by default — connection host/user/port/params come from the + # global.datastores.mongo mask; auth from global.auth. Any key set here WINS. + # NOTE: RATE_LIMIT_* were dead keys (CRM has no rate-limit config surface) and + # were removed; APPLICATION_NAME defaults to "crm" in the template. + # --- Service discovery (lib-service-discovery) -------------------------------- + # Enable per-app; env-wide config from global.serviceDiscovery; endpoints derived + # from this chart's own service + ingress. INERT until enabled + global set. + # A legacy flat configmap.SD_* key wins over global.serviceDiscovery. + serviceDiscovery: + enabled: false # → SD_ENABLED + + # --- Multi-tenant (lib multi-tenant) ------------------------------------------ + # crm exposes only the toggle (no MT client config on this component). + multiTenant: + enabled: false # → MULTI_TENANT_ENABLED + + # --- Streaming (lib-streaming) ------------------------------------------------ + # Enable per-app; brokers/SASL/TLS come from global.streaming. + # A legacy flat configmap.STREAMING_* key wins over global.streaming. + streaming: + enabled: false # → STREAMING_ENABLED + # @default -- templates/crm/configmap.yaml - configmap: - ALLOW_INSECURE_TLS: "true" - # -- Default Environment - ENV_NAME: "development" - # -- Auth Plugin configuration - PLUGIN_AUTH_ENABLED: "false" - PLUGIN_AUTH_ADDRESS: "http://plugin-access-manager-auth:4000" - # -- MongoDB configuration - MONGO_HOST: "midaz-mongodb" - MONGO_NAME: "crm" - MONGO_PORT: "27017" - MONGO_USER: "midaz" - # -- Rate Limiting - Three-tier system with Redis backend - # -- Set RATE_LIMIT_ENABLED=false to disable (requires ALLOW_RATELIMIT_DISABLED in strict tier) - # -- ALLOW_RATELIMIT_DISABLED: Set a detailed reason message explaining why rate limiting is disabled - # -- ALLOW_RATELIMIT_FAIL_OPEN: Set a detailed reason message to allow fail-open mode in production - RATE_LIMIT_ENABLED: "true" - RATE_LIMIT_MAX: "500" - RATE_LIMIT_WINDOW_SEC: "60" - AGGRESSIVE_RATE_LIMIT_MAX: "100" - AGGRESSIVE_RATE_LIMIT_WINDOW_SEC: "60" - RELAXED_RATE_LIMIT_MAX: "1000" - RELAXED_RATE_LIMIT_WINDOW_SEC: "60" - RATE_LIMIT_REDIS_TIMEOUT_MS: "500" - ALLOW_RATELIMIT_DISABLED: "" - ALLOW_RATELIMIT_FAIL_OPEN: "" + configmap: {} # -- Extra environment variables extraEnvVars: {} # -- Secrets for storing sensitive data @@ -509,6 +718,10 @@ crm: # they are rendered into the CRM Secret (never the ConfigMap). STREAMING_SASL_PASSWORD: "" STREAMING_TLS_CA_CERT: "" + # -- KMS HashiCorp Vault AppRole secret id. Operator-provided; REQUIRED (with + # crm.kms.vaultRoleId) when kms.vendor=hashicorp-vault and kms.vaultAuthMethod=approle. + # Rendered into the CRM Secret (never the ConfigMap). + KMS_VAULT_SECRET_ID: "" # -- Existing secrets name useExistingSecret: false existingSecretName: "" @@ -694,6 +907,54 @@ rabbitmq: mountPath: /etc/rabbitmq/definitions customConfig: | management.load_definitions = /etc/rabbitmq/definitions/load_definition.json +# -- HashiCorp Vault (bundled, dev-mode) — KMS Transit backend for CRM envelope +# encryption. Mirrors midaz components/infra/docker-compose.yml (midaz-hc-vault + +# midaz-hc-vault-init). OPT-IN (enabled: false by default): the bundled Vault is a +# dev-mode server (root token, :8200, a system:auth-delegator ClusterRoleBinding +# and the transit-init Job) intended ONLY for the self-contained/dev stack. Enable +# it together with crm (vault.enabled=true + crm.enabled=true) so the free stack +# needs no external Vault. Production leaves this off and points KMS at an external +# Vault via crm.configmap.KMS_VAULT_ADDR (and switches to approle auth). +vault: + # -- Toggle the bundled Vault subchart. Default false (opt-in). true = self-contained dev Vault. + enabled: false + # -- Pin the Vault Service name to `midaz-hc-vault` so the crm/ledger KMS default + # KMS_VAULT_ADDR (http://midaz-hc-vault:8200) resolves with no extra config. + fullnameOverride: "midaz-hc-vault" + # -- No Agent Injector sidecar in the self-contained stack (app uses token auth). + injector: + enabled: false + # -- No CSI Secrets Store provider. + csi: + enabled: false + # -- No Vault UI. + ui: + enabled: false + server: + # -- Dev mode: in-memory, auto-unsealed, single node, root token preset. + # Matches the docker-compose `hashicorp/vault ... -dev` service. + dev: + enabled: true + # -- Dev root token — matches the midaz KMS token-auth default ("root"). + # Kept in sync with the transit-init Job's VAULT_TOKEN. + devRootToken: "root" + # -- Dev mode is in-memory: disable HA, standalone and persistent storage. + ha: + enabled: false + standalone: + enabled: false + dataStorage: + enabled: false + auditStorage: + enabled: false + # -- Small footprint for minikube (shared with pg/mongo/rabbit/valkey). + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 250m + memory: 256Mi # -- OTEL exporter configuration for midaz services. # When enabled, HOST_IP, POD_IP, OTEL_EXPORTER_OTLP_ENDPOINT and OTEL_RESOURCE_ATTRIBUTES # are injected into the `ledger` and `crm` deployments only. Port is fixed at 4317.