From b4e42970823428330558c4afdceba35aad39e10b Mon Sep 17 00:00:00 2001 From: Eric Becker Date: Sat, 2 May 2026 16:09:38 +0000 Subject: [PATCH 1/2] feat: Helm chart for Kubernetes deployment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds charts/floodgate/ — Deployment, Service, ConfigMap, ServiceAccount, optional PodDisruptionBudget, NOTES.txt — with values.yaml that mirrors config.yaml so every floodgate config option is overridable via --set or a values file. The Deployment carries a checksum/config annotation so ConfigMap changes drive a rolling restart automatically. Container ports, Service port, and probe ports all derive from .Values.config.{grpc_port,health_port} so there is one source of truth — no risk of the chart and the rendered config disagreeing. Security context (nonroot, read-only fs, drop ALL caps, runAsUser 65534) matches the upstream Dockerfile defaults. CI: extends the existing manifests job with helm lint and two helm template | kubeconform passes (defaults plus a non-trivial override set covering PDB, drop_enabled, json logs). Closes #20. --- .github/workflows/ci.yml | 28 ++++- charts/floodgate/.helmignore | 12 +++ charts/floodgate/Chart.yaml | 17 +++ charts/floodgate/README.md | 74 +++++++++++++ charts/floodgate/templates/NOTES.txt | 29 +++++ charts/floodgate/templates/_helpers.tpl | 70 ++++++++++++ charts/floodgate/templates/configmap.yaml | 9 ++ charts/floodgate/templates/deployment.yaml | 89 ++++++++++++++++ .../templates/poddisruptionbudget.yaml | 17 +++ charts/floodgate/templates/service.yaml | 19 ++++ .../floodgate/templates/serviceaccount.yaml | 12 +++ charts/floodgate/values.yaml | 100 ++++++++++++++++++ 12 files changed, 474 insertions(+), 2 deletions(-) create mode 100644 charts/floodgate/.helmignore create mode 100644 charts/floodgate/Chart.yaml create mode 100644 charts/floodgate/README.md create mode 100644 charts/floodgate/templates/NOTES.txt create mode 100644 charts/floodgate/templates/_helpers.tpl create mode 100644 charts/floodgate/templates/configmap.yaml create mode 100644 charts/floodgate/templates/deployment.yaml create mode 100644 charts/floodgate/templates/poddisruptionbudget.yaml create mode 100644 charts/floodgate/templates/service.yaml create mode 100644 charts/floodgate/templates/serviceaccount.yaml create mode 100644 charts/floodgate/values.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99551ee..70b895c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,7 +118,8 @@ jobs: run: docker compose -f docker-compose.test.yaml down -v --remove-orphans || true # --------------------------------------------------------------------------- - # Manifest validation — verify k8s YAML is valid against the k8s schema + # Manifest validation — verify static k8s/ YAML and the rendered Helm chart + # are valid against the k8s schema # --------------------------------------------------------------------------- manifests: name: Validate k8s manifests @@ -132,6 +133,29 @@ jobs: curl -sSL https://github.com/yannh/kubeconform/releases/download/v0.7.0/kubeconform-linux-amd64.tar.gz \ | tar xz -C /usr/local/bin kubeconform - - name: Validate k8s manifests + - name: Set up Helm + uses: azure/setup-helm@v4 + with: + version: v3.16.2 + + - name: Validate static k8s manifests run: | kubeconform -strict -summary k8s/*.yaml + + - name: Lint Helm chart + run: | + helm lint charts/floodgate + + - name: Validate rendered chart (defaults) + run: | + helm template floodgate charts/floodgate \ + | kubeconform -strict -summary + + - name: Validate rendered chart (PDB enabled, drop enabled, json logs) + run: | + helm template floodgate charts/floodgate \ + --set podDisruptionBudget.enabled=true \ + --set config.drop_enabled=true \ + --set 'config.drop_portnums={RANGE_TEST_APP}' \ + --set config.log_format=json \ + | kubeconform -strict -summary diff --git a/charts/floodgate/.helmignore b/charts/floodgate/.helmignore new file mode 100644 index 0000000..0fa72d4 --- /dev/null +++ b/charts/floodgate/.helmignore @@ -0,0 +1,12 @@ +# Patterns to ignore when packaging the chart. +.DS_Store +.git/ +.gitignore +.idea/ +.vscode/ +*.swp +*.bak +*.tmp +*.orig +*~ +README.md.tpl diff --git a/charts/floodgate/Chart.yaml b/charts/floodgate/Chart.yaml new file mode 100644 index 0000000..c3a508d --- /dev/null +++ b/charts/floodgate/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +name: floodgate +description: MQTT anti-flood service for Meshtastic — zero-hop modifier and portnum drop filter for EMQX via the ExHook gRPC interface. +type: application +version: 0.1.0 +appVersion: "0.1.0" +home: https://github.com/eric-becker/floodgate +sources: + - https://github.com/eric-becker/floodgate +keywords: + - meshtastic + - mqtt + - emqx + - exhook +maintainers: + - name: Eric Becker + url: https://github.com/eric-becker diff --git a/charts/floodgate/README.md b/charts/floodgate/README.md new file mode 100644 index 0000000..a43d120 --- /dev/null +++ b/charts/floodgate/README.md @@ -0,0 +1,74 @@ +# floodgate + +Helm chart for [floodgate](https://github.com/eric-becker/floodgate), an MQTT anti-flood service for Meshtastic. Floodgate plugs into EMQX via the ExHook gRPC interface and either zeros the `hop_limit` of in-flight `MeshPacket`s or drops them outright by portnum. + +## Install + +```bash +# Default: standard public-channel zerohop, drop disabled, text logs +helm install floodgate ./charts/floodgate -n floodgate --create-namespace + +# Common: enable RANGE_TEST_APP drop, JSON logs for Loki +helm install floodgate ./charts/floodgate -n floodgate --create-namespace \ + --set config.drop_enabled=true \ + --set 'config.drop_portnums={RANGE_TEST_APP}' \ + --set config.log_format=json +``` + +After install, register the ExHook on EMQX: + +```bash +curl -u admin:public -X POST http://:18083/api/v5/exhooks \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "floodgate", + "url": "http://floodgate.floodgate.svc:9000", + "enable": true + }' +``` + +## Upgrade + +```bash +helm upgrade floodgate ./charts/floodgate -n floodgate -f my-values.yaml +``` + +ConfigMap changes trigger a rolling restart automatically — the Deployment carries a `checksum/config` annotation that the chart recomputes from the rendered ConfigMap on every render. + +## Uninstall + +```bash +helm uninstall floodgate -n floodgate +``` + +## Values + +The full default set lives in [values.yaml](values.yaml). Highlights: + +| Key | Default | Notes | +|-----|---------|-------| +| `image.repository` | `ghcr.io/eric-becker/floodgate` | Image registry/repo | +| `image.tag` | `""` (Chart `appVersion`) | Override to pin | +| `replicaCount` | `1` | Each replica registers as its own ExHook target if you front them with a Service per pod; the default cluster-internal Service round-robins | +| `config.zerohop_enabled` | `true` | Master switch for the zero-hop modifier | +| `config.zerohop_channels` | 8 standard presets | Channels whose packets get `hop_limit` zeroed | +| `config.drop_enabled` | `false` | Master switch for the drop filter | +| `config.drop_channels` | `"zerohop_channels"` | Inherits from zerohop, or a list, or `null` for all channels | +| `config.drop_portnums` | `[]` | E.g. `[RANGE_TEST_APP]` | +| `config.log_format` | `"text"` | Set `"json"` for Loki/Grafana | +| `service.type` | `ClusterIP` | gRPC port only; health is internal | +| `podDisruptionBudget.enabled` | `false` | Set `true` and tune `minAvailable` for HA topologies | +| `resources.requests` / `limits` | 50m/200m CPU, 64Mi/128Mi memory | Tuned for ~1 broker; raise for high-throughput EMQX clusters | +| `containerSecurityContext` | nonroot, read-only fs, drop ALL caps | Matches the upstream Dockerfile | + +The `config.*` block is rendered verbatim into the ConfigMap and read by floodgate via `FLOODGATE_CONFIG=/etc/floodgate/config.yaml`. Any key floodgate accepts can be set there; see [config.yaml](../../config.yaml) and the project [README](../../README.md) for the full schema. + +## Health probes + +The container exposes `/health` on `config.health_port` (default `8080`). The Service does **not** expose this port — health is for cluster-internal probes only. To inspect manually, port-forward the Pod: + +```bash +POD=$(kubectl get pod -n floodgate -l app.kubernetes.io/name=floodgate -o name | head -1) +kubectl port-forward -n floodgate $POD 8080:8080 +curl http://localhost:8080/health +``` diff --git a/charts/floodgate/templates/NOTES.txt b/charts/floodgate/templates/NOTES.txt new file mode 100644 index 0000000..e372336 --- /dev/null +++ b/charts/floodgate/templates/NOTES.txt @@ -0,0 +1,29 @@ +Floodgate is installed. + + Release: {{ .Release.Name }} + Namespace: {{ .Release.Namespace }} + Image: {{ include "floodgate.image" . }} + +The gRPC ExHook endpoint is available cluster-internally at: + + http://{{ include "floodgate.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.config.grpc_port }} + +Register it on your EMQX broker: + + curl -u admin:public -X POST http://:18083/api/v5/exhooks \ + -H 'Content-Type: application/json' \ + -d '{ + "name": "floodgate", + "url": "http://{{ include "floodgate.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.config.grpc_port }}", + "enable": true + }' + +Tail floodgate logs: + + kubectl logs -n {{ .Release.Namespace }} deploy/{{ include "floodgate.fullname" . }} -f + +Inspect /health (the Service exposes only gRPC, so port-forward the Pod): + + POD=$(kubectl get pod -n {{ .Release.Namespace }} -l app.kubernetes.io/name={{ include "floodgate.name" . }} -o name | head -1) + kubectl port-forward -n {{ .Release.Namespace }} $POD {{ .Values.config.health_port }}:{{ .Values.config.health_port }} + curl http://localhost:{{ .Values.config.health_port }}/health diff --git a/charts/floodgate/templates/_helpers.tpl b/charts/floodgate/templates/_helpers.tpl new file mode 100644 index 0000000..5d81fc7 --- /dev/null +++ b/charts/floodgate/templates/_helpers.tpl @@ -0,0 +1,70 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "floodgate.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Fully qualified app name. Truncated to 63 chars for label compatibility. +*/}} +{{- define "floodgate.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Chart name and version, used as a label. +*/}} +{{- define "floodgate.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels applied to every object the chart owns. +*/}} +{{- define "floodgate.labels" -}} +helm.sh/chart: {{ include "floodgate.chart" . }} +{{ include "floodgate.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels — used for matchLabels in the Deployment and the Service +selector. Must be stable across upgrades. +*/}} +{{- define "floodgate.selectorLabels" -}} +app.kubernetes.io/name: {{ include "floodgate.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Resolve the ServiceAccount name to use. +*/}} +{{- define "floodgate.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "floodgate.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Resolve the container image reference. Falls back to .Chart.appVersion when +.Values.image.tag is empty. +*/}} +{{- define "floodgate.image" -}} +{{- $tag := default .Chart.AppVersion .Values.image.tag -}} +{{- printf "%s:%s" .Values.image.repository $tag -}} +{{- end }} diff --git a/charts/floodgate/templates/configmap.yaml b/charts/floodgate/templates/configmap.yaml new file mode 100644 index 0000000..114d182 --- /dev/null +++ b/charts/floodgate/templates/configmap.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "floodgate.fullname" . }}-config + labels: + {{- include "floodgate.labels" . | nindent 4 }} +data: + config.yaml: | +{{ toYaml .Values.config | indent 4 }} diff --git a/charts/floodgate/templates/deployment.yaml b/charts/floodgate/templates/deployment.yaml new file mode 100644 index 0000000..e82d180 --- /dev/null +++ b/charts/floodgate/templates/deployment.yaml @@ -0,0 +1,89 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "floodgate.fullname" . }} + labels: + {{- include "floodgate.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + {{- toYaml .Values.updateStrategy | nindent 4 }} + selector: + matchLabels: + {{- include "floodgate.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "floodgate.selectorLabels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + annotations: + # Roll the pods when the rendered config changes. + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "floodgate.serviceAccountName" . }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: floodgate + image: {{ include "floodgate.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: grpc + containerPort: {{ .Values.config.grpc_port }} + protocol: TCP + - name: health + containerPort: {{ .Values.config.health_port }} + protocol: TCP + env: + - name: FLOODGATE_CONFIG + value: /etc/floodgate/config.yaml + {{- with .Values.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + - name: config + mountPath: /etc/floodgate + readOnly: true + livenessProbe: + httpGet: + path: /health + port: health + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + readinessProbe: + httpGet: + path: /health + port: health + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + securityContext: + {{- toYaml .Values.containerSecurityContext | nindent 12 }} + volumes: + - name: config + configMap: + name: {{ include "floodgate.fullname" . }}-config + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/floodgate/templates/poddisruptionbudget.yaml b/charts/floodgate/templates/poddisruptionbudget.yaml new file mode 100644 index 0000000..654ed29 --- /dev/null +++ b/charts/floodgate/templates/poddisruptionbudget.yaml @@ -0,0 +1,17 @@ +{{- if .Values.podDisruptionBudget.enabled -}} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "floodgate.fullname" . }} + labels: + {{- include "floodgate.labels" . | nindent 4 }} +spec: + {{- if hasKey .Values.podDisruptionBudget "minAvailable" }} + minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} + {{- else if hasKey .Values.podDisruptionBudget "maxUnavailable" }} + maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} + {{- end }} + selector: + matchLabels: + {{- include "floodgate.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/floodgate/templates/service.yaml b/charts/floodgate/templates/service.yaml new file mode 100644 index 0000000..93a05cf --- /dev/null +++ b/charts/floodgate/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "floodgate.fullname" . }} + labels: + {{- include "floodgate.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + selector: + {{- include "floodgate.selectorLabels" . | nindent 4 }} + ports: + - name: grpc + port: {{ .Values.config.grpc_port }} + targetPort: grpc + protocol: TCP diff --git a/charts/floodgate/templates/serviceaccount.yaml b/charts/floodgate/templates/serviceaccount.yaml new file mode 100644 index 0000000..748c6ef --- /dev/null +++ b/charts/floodgate/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "floodgate.serviceAccountName" . }} + labels: + {{- include "floodgate.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/floodgate/values.yaml b/charts/floodgate/values.yaml new file mode 100644 index 0000000..8f62ced --- /dev/null +++ b/charts/floodgate/values.yaml @@ -0,0 +1,100 @@ +# Default values for the floodgate chart. +# Override with `--set key=value` or a custom values file (`-f my-values.yaml`). + +image: + repository: ghcr.io/eric-becker/floodgate + # Defaults to .Chart.appVersion when empty. + tag: "" + pullPolicy: IfNotPresent + +imagePullSecrets: [] + +nameOverride: "" +fullnameOverride: "" + +replicaCount: 1 + +# Mirrors src/floodgate/config.py defaults and the project root config.yaml. +# Rendered verbatim into the ConfigMap; floodgate reads it via FLOODGATE_CONFIG. +config: + zerohop_enabled: true + zerohop_channels: + - LongTurbo + - LongFast + - LongModerate + - MediumFast + - MediumSlow + - ShortFast + - ShortSlow + - ShortTurbo + drop_enabled: false + drop_channels: "zerohop_channels" + drop_portnums: [] + grpc_port: 9000 + health_port: 8080 + topic_filter: "msh/#" + stats_interval_s: 60 + log_level: "INFO" + log_format: "text" + stats_log: true + +service: + type: ClusterIP + annotations: {} + +serviceAccount: + create: true + name: "" + annotations: {} + +podAnnotations: {} +podLabels: {} + +resources: + requests: + cpu: 50m + memory: 64Mi + limits: + cpu: 200m + memory: 128Mi + +# Pod-level security context. +podSecurityContext: {} + +# Container-level security context. The floodgate image runs as nobody (65534). +containerSecurityContext: + runAsNonRoot: true + runAsUser: 65534 + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + +probes: + liveness: + initialDelaySeconds: 15 + periodSeconds: 30 + readiness: + initialDelaySeconds: 5 + periodSeconds: 10 + +updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 0 + maxSurge: 1 + +podDisruptionBudget: + enabled: false + minAvailable: 1 + # maxUnavailable: 1 # mutually exclusive with minAvailable + +nodeSelector: {} +tolerations: [] +affinity: {} + +# Extra environment variables for the container. +extraEnv: [] +# - name: FLOODGATE_LOG_FORMAT +# value: json From 62f7406914d6368b123f778a5a1750b32c924968 Mon Sep 17 00:00:00 2001 From: Eric Becker Date: Sat, 2 May 2026 16:09:41 +0000 Subject: [PATCH 2/2] chore: deprecate static k8s/ manifests in favour of Helm chart Adds k8s/README.md flagging the static manifests as deprecated and pointing readers at charts/floodgate/. Updates the root README and CLAUDE.md file table to match. The static manifests are intentionally left in place for one release to give kubectl-only deployers a deprecation window; they will be removed in a follow-up. --- CLAUDE.md | 2 ++ README.md | 12 +++++++++--- k8s/README.md | 11 +++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 k8s/README.md diff --git a/CLAUDE.md b/CLAUDE.md index 501edd7..07f82ec 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,6 +27,8 @@ Gateway → EMQX → [ExHook gRPC] → floodgate → drop / modify / passthru | `docker-compose.test.yaml` | Integration test stack (emqx, floodgate, exhook-init, test-driver) on an isolated bridge network. | | `scripts/run-integration.sh` | Integration harness orchestrator — `--keep` leaves the stack up, `--teardown` removes it. | | `tests/integration/` | Integration test assets: floodgate config, ExHook init container, test-driver image + cases. | +| `charts/floodgate/` | Helm chart (supported install path). Values mirror `config.yaml` 1:1; rendered chart is validated in CI. | +| `k8s/` | Static `kubectl apply -f` manifests, deprecated in favour of the chart. | ## Dev Setup diff --git a/README.md b/README.md index 179d99b..6244d7d 100644 --- a/README.md +++ b/README.md @@ -103,9 +103,15 @@ docker compose up --build -d After startup, register the ExHook per the [Deployment](#deployment) instructions above. -### Kubernetes +### Kubernetes (Helm) + +```bash +helm install floodgate ./charts/floodgate -n floodgate --create-namespace +``` + +See [charts/floodgate/](charts/floodgate/) for the full values reference. The chart renders a Deployment with rolling updates, a ClusterIP Service, ConfigMap, ServiceAccount, and an optional PodDisruptionBudget; ConfigMap changes trigger automatic rolling restarts via a checksum annotation. Register the ExHook at `http://floodgate.floodgate.svc:9000` after install. -See [k8s/](k8s/) — Deployment, Service, and ConfigMap. The Deployment uses a rolling update strategy for zero-downtime upgrades. Register the ExHook at `http://floodgate:9000` after applying. +The flat manifests in [k8s/](k8s/) are still present for `kubectl apply -f` users but are deprecated in favour of the chart. ### Source install @@ -211,7 +217,7 @@ The Kubernetes manifests in [k8s/](k8s/) use `type: ClusterIP` so neither port i ### Container hardening -The production container image runs as `nobody` (UID 65534) with a read-only filesystem, no Linux capabilities, and no privilege escalation. See [Dockerfile](Dockerfile) and [k8s/deployment.yaml](k8s/deployment.yaml). +The production container image runs as `nobody` (UID 65534) with a read-only filesystem, no Linux capabilities, and no privilege escalation. See [Dockerfile](Dockerfile) and the chart's [containerSecurityContext defaults](charts/floodgate/values.yaml). ## Verifying operation diff --git a/k8s/README.md b/k8s/README.md new file mode 100644 index 0000000..904799e --- /dev/null +++ b/k8s/README.md @@ -0,0 +1,11 @@ +# Static manifests (deprecated) + +These flat manifests are kept for users who deploy with `kubectl apply -f k8s/` and don't want to introduce Helm. They are scheduled to be removed in a future release in favour of the [Helm chart](../charts/floodgate/), which is now the supported install path. + +If you're starting fresh, use the chart instead: + +```bash +helm install floodgate ./charts/floodgate -n floodgate --create-namespace +``` + +The chart renders a superset of what's here (Deployment, Service, ConfigMap, plus a ServiceAccount, optional PodDisruptionBudget, and config-checksum-driven rolling restarts) and exposes every option in [config.yaml](../config.yaml) via `values.yaml`.