Skip to content

feat(helm): tonin.toml [env] tables and per-dependency egress ports#108

Merged
Rushit merged 2 commits into
mainfrom
feat/tonin-env-vars
Jul 4, 2026
Merged

feat(helm): tonin.toml [env] tables and per-dependency egress ports#108
Rushit merged 2 commits into
mainfrom
feat/tonin-env-vars

Conversation

@Rushit

@Rushit Rushit commented Jul 4, 2026

Copy link
Copy Markdown
Owner

What

Two tonin helm generate gaps, both found while auditing a real tonin consumer (the ZVectorLabs monorepo, where every service's chart is rendered from tonin.toml):

1. [env] tables → chart env: map

There was no way to declare plain (non-secret) runtime env vars in tonin.toml. The generated values.yaml ships extraEnv: [] / env: {} and regeneration clobbers hand-edits, so a service that needs e.g. IDENTITY_GRPC_URL simply crashes when chart-deployed. The existing mechanisms are narrow: [database]/[cache]statefulEnv literals, [secrets]secretKeyRef env.

New optional [env] table, with per-env overlays and {env} substitution (consistent with namespace = "zvectorlabs-{env}"):

[env]
IDENTITY_GRPC_URL = "http://identity.zvectorlabs-{env}.svc.cluster.local:50051"
LOG_FORMAT        = "json"

[env.dev]
LOG_FORMAT = "pretty"      # merged over the base for dev only

Resolution: base string entries + the [env.<env>] overlay for the rendered env (overlay wins), {env} substituted in values, sorted for deterministic output. Rendered into values.yaml and each values-<env>.yaml as the env: map — the deployment template already injects .Values.env into the server container, so no chart-template changes were needed.

Guards (hard errors, matching the planner's no-silent-fallback philosophy):

  • non-string values (including in inactive env overlays, so typos fail in every env)
  • keys colliding with tonin-emitted env (DATABASE_URL/REDIS_URL stateful literals, secret-sourced keys) — duplicate container env names are last-one-wins in Kubernetes, which would silently override the resolved URLs

2. Per-dependency egress port in the generated network policy

chart/templates/networkpolicy.yaml hardcoded the dependsOn egress port: - { port: "50051", protocol: TCP } — correct only when the dependency is a gRPC service on 50051. Real dependency graphs (ui→api:7001, api→zradar:4317/8081) get their traffic blocked under Cilium.

The [depends_on] table form now accepts port (validated 1–65535), alongside the existing namespace / per-env override / envs keys:

[depends_on]
identity        = "zvectorlabs-{env}"                          # bare string → port 50051 (unchanged)
zradar-platform = { namespace = "zradar-{env}", port = 4317 }  # structured → declared port

values*.yaml networkPolicy.dependsOn entries now carry port:, and the egress rule renders {{ .port | default 50051 }} — so even hand-supplied values without port keep today's behavior.

Also fixed while there (few lines, same template): the generated CiliumNetworkPolicy had no kube-dns egress. Any egress rule makes the policy default-deny for the pod, so DNS — and with it all service discovery — was silently blocked for every cilium-mesh service. Added a kube-dns 53/UDP+TCP egress allow.

Backward compatibility

  • Both tonin.toml additions are optional with defaults preserving today's output: no [env]env: {} exactly as before; bare-string depends_onport: 50051, same egress rule as the old hardcode.
  • Schema stays v1 (additive only). RECOMMENDED_CLI_MIN bumped to 0.14.0 per its doc comment, since older CLIs silently ignore [env].
  • tonin-plugin API: Plan.depends_on is now Vec<DependencyRef> (name, namespace, port) instead of Vec<ServiceRef> — a dependency is a service ref plus a port, and ServiceRef keeps meaning "service in a namespace" for callers. Pre-1.0 minor-version API change; DependencyRef deserializes port with a 50051 serde default.
  • No VERSION / CHANGELOG edits — release automation owns those.

Known follow-ups (not in scope)

  • The otel-collector egress (4317) and the new kube-dns egress are still fixed values; making the observability endpoint configurable per environment could follow the same pattern.
  • [env] values render into values files in plain text by design (non-secret); anything sensitive belongs in [secrets].

Test plan

  • just ci (fmt-check + clippy -D warnings + nextest + doc + version-sync) green.
  • New tonin-plugin plan tests: [env] base + overlay merge, {env} substitution, brace passthrough, non-string errors (active and inactive overlays), reserved-key collisions (stateful + secret); depends_on bare-string default port, table port (with env overrides), missing port default, non-integer / out-of-range port errors.
  • New tonin helm generate tests: [env] rendered into values-dev/prod.yaml with overlay + {env} resolution and into the base values.yaml; env: {} preserved when [env] absent; dependsOn entries carry port: for both forms; networkpolicy template uses {{ .port | default 50051 }}, no hardcoded dependency port, kube-dns egress present.
  • just gen-example (greeter chart re-render + build).

🤖 Generated with Claude Code

Rushit added 2 commits July 4, 2026 00:44
…ports

Two gaps found auditing a real tonin consumer (ZVectorLabs monorepo):

- [env] / [env.<env>] tables declare plain (non-secret) runtime env vars in
  tonin.toml, rendered into the generated values files' env: map (which the
  deployment template already injects). Base entries merge under per-env
  overlays, {env} substitutes in values, and keys colliding with tonin-emitted
  stateful/secret env names fail generation.

- [depends_on] table form accepts port = <p>; the generated
  CiliumNetworkPolicy egress rule uses the per-dependency port instead of a
  hardcoded 50051. Bare-string entries keep defaulting to 50051, so existing
  charts render identically. The policy also allows kube-dns egress, which
  default-deny was silently blocking.

RECOMMENDED_CLI_MIN bumps to 0.14.0 since older CLIs silently ignore [env].
@Rushit Rushit marked this pull request as ready for review July 4, 2026 04:48
@Rushit Rushit merged commit 285f3f0 into main Jul 4, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant