Rebrand: Sentry -> Cairn OBS
Full rebrand across cosmetic branding, code identifiers, and infrastructure/data-plane naming, using the supplied Cairn OBS logo package. Cosmetic: favicon/logo swap (also closes a stale license-audit finding -- the old favicon was SvelteKit's unreplaced scaffold logo), new centered welcome landing page, larger/legible sidebar logo, page titles, CLAUDE.md/README/docs prose. Code identifiers: Go module path github.com/sentry/sentry -> github.com/cairnobs/cairnobs across all 13 modules and ~91 files (protoc regenerated); Rust crates sentry-agent/sentry-parser/sentry-search -> cairnobs-*; CLI sentryctl -> cairnobsctl; Terraform provider fully renamed (sentry_dashboard etc. -> cairnobs_dashboard, provider type, env vars); every session/auth cookie name; agent config paths and Windows service identity. Deliberately preserved: the gRPC wire protocol's protobuf packages (sentry.logs.v1, sentry.agent.v1) and their Go import directory (proto/sentry/...) -- renaming the wire-level package would break every currently-deployed agent binary (confirmed two real hosts, including mail.inbuxa.com, are actively streaming through this exact contract) until rebuilt and redeployed in lockstep with an ingest cutover. Only the Go module path wrapping the generated code changes. Infrastructure: every docker-compose container name (root and three component-level compose files); the Helm chart (directory, Chart.yaml, named-template helpers, all templates, values.yaml image repos); Kubernetes Operator (CRD group sentry.io -> cairnobs.io, both CRD YAML files, Go identifiers, RBAC markers); the coupled enterprise/tenantcrd package. Caught and fixed real path-coupling bugs along the way: the Helm chart's search/ingest volume mounts and the dev-only-credential detection constant vs. docker-compose.yml's literal values had to move together or a security warning would have silently stopped firing. Data plane: Postgres database sentry_metadata -> cairnobs_metadata and role sentry -> cairnobs; ClickHouse database sentry -> cairnobs; Kafka topic sentry.logs.raw -> cairnobs.logs.raw and its consumer groups. Source-level defaults, docker-compose.yml, and every migrate.sh/ provision script default updated together; already-applied migration files left untouched per this repo's immutable-migration convention. Verified at every layer: all 13 Go modules build/vet/test clean, both Rust workspaces (agent, search) build/clippy/test clean, npm run check/ build clean, docker compose config validates on all four compose files. Live-verified against a real docker stack multiple times through this work, including a final fresh-volume run confirming the actual renamed Postgres database/role, ClickHouse database, and Kafka topic all work end to end with a real login and query, zero console errors.
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
apiVersion: v2
|
||||
name: cairnobs
|
||||
description: >-
|
||||
Cairn OBS: Kubernetes-native distributed log aggregation. Phase 4 adds
|
||||
multi-tenant-aware deployment (per-tenant ClickHouse credential Secrets
|
||||
via the tenant-operator, optional enterprise-auth) on top of the same
|
||||
single-cluster topology Phases 0-3 ran under docker-compose -- see
|
||||
deploy/README.md for what "multi-tenant-aware" does and doesn't mean
|
||||
here.
|
||||
type: application
|
||||
version: 0.4.0
|
||||
appVersion: "phase-4"
|
||||
@@ -0,0 +1,142 @@
|
||||
# deploy/helm/cairnobs
|
||||
|
||||
A Helm chart covering every `docker-compose.yml` service (Redpanda,
|
||||
ClickHouse, Postgres, ingest, search, alerting, web) plus, when
|
||||
`enterprise.enabled: true`: enterprise-auth, the `deploy/operator`
|
||||
tenant-operator, and `Tenant` CRs from `values.tenants`. See
|
||||
`/deploy/README.md` for what "multi-tenant-aware" does and doesn't mean
|
||||
at this layer, and its verification-status section before trusting this
|
||||
against a real cluster.
|
||||
|
||||
This chart never builds images -- push every image its `values.yaml`
|
||||
references to a registry the cluster can pull from first, same division
|
||||
of labor as `docker compose build` vs. `docker compose up`.
|
||||
|
||||
## `api` vs `enterprise-api`: one Deployment, chosen by `enterprise.enabled`
|
||||
|
||||
`templates/api.yaml` and `templates/enterprise-api.yaml` are mutually
|
||||
exclusive, gated on opposite sides of the same `enterprise.enabled` flag
|
||||
-- exactly one of them ever renders, both under the same
|
||||
`{{ .Release.Name }}-api` Service name and port 8080. This is the fix
|
||||
for what `/docs/security/threat-model.md` named as Phase 4's single
|
||||
largest remaining gap once both storage engines' isolation mechanisms
|
||||
were built: previously nothing forced or even flagged whether a
|
||||
deployment ran the tenant-isolated binary. Now it's not a second knob to
|
||||
remember -- the same flag that turns on RBAC/audit/SSO also swaps which
|
||||
query binary actually serves `/query` and `/dashboards` traffic. Every
|
||||
consumer (`alerting`'s `API_QUERY_URL`, `web`'s build args) needs zero
|
||||
conditional logic of its own, since both variants answer on the same
|
||||
name/port.
|
||||
|
||||
`enterprise-api` starts with an empty tenant set until
|
||||
`-provision-tenant` has been run for at least one tenant (see
|
||||
`/enterprise/README.md`) -- until then it's up and healthy, but every
|
||||
`/query` request correctly fails closed with no tenant to route to.
|
||||
|
||||
## Startup ordering
|
||||
|
||||
`docker-compose.yml` uses `depends_on: condition: service_healthy` /
|
||||
`service_completed_successfully` to sequence startup (e.g. `api` waits
|
||||
for `clickhouse-migrate` to actually finish, not just for `clickhouse` to
|
||||
be reachable). This chart approximates that more loosely:
|
||||
|
||||
- Migration Jobs (`clickhouse-migrate`, `metadata-migrate`,
|
||||
`redpanda-provision`) are plain `Job` resources (not Helm hooks --
|
||||
making the StatefulSets they depend on into hooks too, to get
|
||||
ordering, would break `helm upgrade`/`helm uninstall`'s normal
|
||||
ownership tracking of stateful resources, a worse tradeoff), with
|
||||
`backoffLimit: 6` so they retry a few times if their dependency isn't
|
||||
up yet.
|
||||
- App Deployments get an `initContainer` that busy-waits for their
|
||||
dependency's **TCP port**, not for a specific Job's completion (see
|
||||
`templates/_helpers.tpl`'s `cairnobs.waitForTCP`) -- this covers "is
|
||||
ClickHouse/Postgres/Redpanda up" but not "has the migration Job
|
||||
actually finished."
|
||||
- The gap that leaves (a pod starts before its migration has completed)
|
||||
is covered by every Go service here already calling `os.Exit(1)` on a
|
||||
failed startup DB ping (see e.g. `api/cmd/api/main.go`) --
|
||||
Kubernetes' pod restart policy retries with backoff until the schema
|
||||
is ready. This is a real, working, but *looser* guarantee than
|
||||
docker-compose's explicit ordering -- documented here rather than
|
||||
implied to be equivalent.
|
||||
|
||||
## Trying the two-tenant example
|
||||
|
||||
```sh
|
||||
# Quote each --set value -- zsh globs an unquoted tenants[0] as a
|
||||
# pattern and fails with "no matches found." Also note: no --include-crds
|
||||
# here -- that's a helm template-only flag (install always installs
|
||||
# crds/ by default); confirmed the hard way running this against a real
|
||||
# kind cluster, see /docs/phase-4-runbook.md §7.
|
||||
helm install cairnobs . \
|
||||
--set enterprise.enabled=true \
|
||||
--set tenantOperator.enabled=true \
|
||||
--set 'tenants[0].name=acme' --set 'tenants[0].displayName=Acme Corp' \
|
||||
--set 'tenants[1].name=globex' --set 'tenants[1].displayName=Globex Corporation'
|
||||
|
||||
kubectl get tenants
|
||||
# expect: both Provisioning -- the Tenant CRs above are just a
|
||||
# declarative request; nothing has actually provisioned ClickHouse for
|
||||
# either yet (see below).
|
||||
|
||||
kubectl exec -it deploy/cairnobs-api -- /enterprise-api -provision-tenant=acme -display-name="Acme Corp"
|
||||
kubectl exec -it deploy/cairnobs-api -- /enterprise-api -provision-tenant=globex -display-name="Globex Corporation"
|
||||
|
||||
kubectl get tenants
|
||||
# expect: both Active now.
|
||||
kubectl get secret cairnobs-tenant-acme-clickhouse cairnobs-tenant-globex-clickhouse
|
||||
```
|
||||
|
||||
Before any of this: `ingest` needs a real mTLS cert Secret
|
||||
(`--set ingest.tlsSecretName=...`, see `values.yaml`'s comment on it and
|
||||
`/docs/phase-4-runbook.md` §7 for the exact `kubectl create secret`
|
||||
invocation using `hack/dev-certs`) or it crash-loops on startup --
|
||||
unconditional by design, no disable switch.
|
||||
|
||||
**Genuinely run against a real `kind` cluster, not just described**: see
|
||||
`/docs/phase-4-runbook.md` §7 for the exact steps (image loading into
|
||||
`kind`, the two chart bugs it found and fixed) and confirmation that
|
||||
both tenants reached `status.phase: Active` with real credentials.
|
||||
|
||||
This proves Phase 4's "two tenants... with their own users, roles,
|
||||
dashboards" exit criteria (`/CLAUDE.md`) end to end at the deployment-
|
||||
topology layer: `-provision-tenant` (`enterprise/internal/
|
||||
tenantprovision`) is what actually creates each tenant's ClickHouse
|
||||
database/user/grant and marks it active in `rbacstore`; running inside
|
||||
the `enterprise-api` Deployment's Pod means it automatically syncs that
|
||||
real result into the `Tenant` CRD too (`enterprise/internal/tenantcrd`,
|
||||
via the ServiceAccount/Role `tenantOperator.enabled` also grants that
|
||||
Deployment) -- the credential Secret you see above has *real*
|
||||
credentials, not a placeholder, and `Tenant.status.phase: Active` means
|
||||
the same thing `rbacstore.tenants.status='active'` does, not two
|
||||
different claims about two different systems. The `Tenant` CRD and
|
||||
`-provision-tenant` used to be genuinely disconnected (a Secret existed
|
||||
the moment the CR was created, with a password that authenticated
|
||||
against nothing) -- see `/deploy/README.md`'s "lightweight unification"
|
||||
section for the full history. OIDC/SAML login still needs a manual
|
||||
`tenant_memberships` grant (`enterprise-auth -grant-membership-*` --
|
||||
see `/docs/phase-4-runbook.md` §3a/§3b) before a human can actually
|
||||
query as either tenant.
|
||||
|
||||
## `web`'s image needs rebuilding per environment
|
||||
|
||||
`web` is a static SvelteKit build (`adapter-static`) -- its three API
|
||||
base URLs (`VITE_API_BASE_URL`/`VITE_ALERTING_API_BASE_URL`/
|
||||
`VITE_ENTERPRISE_AUTH_BASE_URL`) are baked in at **image build time**
|
||||
(`web/Dockerfile`'s build args), not read from the container's
|
||||
environment at runtime. `values.yaml`'s `web.builtWithApiBaseURL` etc.
|
||||
document what the image you point `web.image` at needs to have been
|
||||
built with (an Ingress hostname, a LoadBalancer IP, etc.) -- this chart
|
||||
has no Ingress resources and can't itself act on those values; rebuild
|
||||
`web`'s image with the right build args for wherever this release is
|
||||
actually reachable from a browser before pointing real users at it.
|
||||
|
||||
## Validating without a cluster
|
||||
|
||||
```sh
|
||||
helm lint .
|
||||
helm template cairnobs . --include-crds > /tmp/rendered.yaml
|
||||
```
|
||||
|
||||
See `/deploy/README.md`'s verification section for what was checked
|
||||
this way versus against a real cluster (now done -- see above).
|
||||
@@ -0,0 +1,93 @@
|
||||
# Hand-written, not `controller-gen crd` output -- see
|
||||
# api/v1alpha1/groupversion_info.go's doc comment. Kept in sync with
|
||||
# api/v1alpha1/tenant_types.go by hand; api/v1alpha1/api_test.go's
|
||||
# round-trip tests catch a Go/YAML drift in the *shape* of the types,
|
||||
# but not a drift in this file's field descriptions/validation rules --
|
||||
# review both together when either changes.
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
name: tenants.cairnobs.io
|
||||
spec:
|
||||
group: cairnobs.io
|
||||
names:
|
||||
kind: Tenant
|
||||
listKind: TenantList
|
||||
plural: tenants
|
||||
singular: tenant
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- name: v1alpha1
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
additionalPrinterColumns:
|
||||
- name: Phase
|
||||
type: string
|
||||
jsonPath: .status.phase
|
||||
- name: Age
|
||||
type: date
|
||||
jsonPath: .metadata.creationTimestamp
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
type: object
|
||||
description: >-
|
||||
Tenant is the K8s-native representation of one Sentry tenant's
|
||||
deployment-topology state -- see
|
||||
deploy/operator/internal/controller/tenant_controller.go's doc
|
||||
comment for what the controller does and does not manage.
|
||||
properties:
|
||||
apiVersion:
|
||||
type: string
|
||||
kind:
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
type: object
|
||||
required: [displayName]
|
||||
properties:
|
||||
displayName:
|
||||
type: string
|
||||
description: Human-readable only -- the object's own metadata.name is the stable identifier.
|
||||
suspended:
|
||||
type: boolean
|
||||
description: Admin-facing lever for the Suspended phase.
|
||||
default: false
|
||||
status:
|
||||
type: object
|
||||
properties:
|
||||
phase:
|
||||
type: string
|
||||
enum: [Provisioning, Active, Suspended, Deprovisioning]
|
||||
clickHouseDatabaseName:
|
||||
type: string
|
||||
clickHouseSecretRef:
|
||||
type: string
|
||||
tantivyIndexPath:
|
||||
type: string
|
||||
observedGeneration:
|
||||
type: integer
|
||||
format: int64
|
||||
conditions:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
required: [type, status]
|
||||
properties:
|
||||
type:
|
||||
type: string
|
||||
status:
|
||||
type: string
|
||||
enum: ["True", "False", "Unknown"]
|
||||
reason:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
observedGeneration:
|
||||
type: integer
|
||||
format: int64
|
||||
lastTransitionTime:
|
||||
type: string
|
||||
format: date-time
|
||||
@@ -0,0 +1,55 @@
|
||||
{{/*
|
||||
Standard labels applied to every resource this chart renders.
|
||||
*/}}
|
||||
{{- define "cairnobs.labels" -}}
|
||||
app.kubernetes.io/part-of: cairnobs
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
Per-component selector labels -- usage:
|
||||
{{ include "cairnobs.selectorLabels" (list $ "api") }}
|
||||
A plain string arg (the old shape this started with) can't reach
|
||||
$.Release from inside the defined template -- `include`'s argument
|
||||
becomes the template's entire root context, so a bare "api" string
|
||||
leaves no way to get back to the chart root. A two-element list carries
|
||||
both.
|
||||
*/}}
|
||||
{{- define "cairnobs.selectorLabels" -}}
|
||||
{{- $root := index . 0 -}}
|
||||
{{- $name := index . 1 -}}
|
||||
app.kubernetes.io/name: cairnobs-{{ $name }}
|
||||
app.kubernetes.io/instance: {{ $root.Release.Name }}
|
||||
{{- end -}}
|
||||
|
||||
{{/*
|
||||
An initContainer that busy-waits for a TCP host:port to accept
|
||||
connections -- usage: {{ include "cairnobs.waitForTCP" (list "name-suffix" "host" "port") }}
|
||||
|
||||
This approximates docker-compose.yml's `depends_on: condition:
|
||||
service_healthy` (waits for the dependency's process to be reachable),
|
||||
but NOT `condition: service_completed_successfully` (waits for a
|
||||
one-shot Job, like clickhouse-migrate, to have actually finished). That
|
||||
second guarantee doesn't have a lightweight equivalent here without
|
||||
giving every app pod's ServiceAccount RBAC to read Job status, which is
|
||||
a lot of privilege for a startup-ordering nicety -- see
|
||||
deploy/helm/cairnobs/README.md's "Startup ordering" section. The gap it
|
||||
leaves (a pod starts before its migration Job has finished) is covered
|
||||
by the app's own crash-and-restart-on-connect/schema failure: every Go
|
||||
service here already os.Exit(1)s on a failed Postgres/ClickHouse ping at
|
||||
startup (see e.g. api/cmd/api/main.go), so Kubernetes' restart policy
|
||||
naturally retries until the schema is ready. Documented as a real,
|
||||
accepted tradeoff, not implied to be a hard ordering guarantee.
|
||||
*/}}
|
||||
{{- define "cairnobs.waitForTCP" -}}
|
||||
{{- $name := index . 0 -}}
|
||||
{{- $host := index . 1 -}}
|
||||
{{- $port := index . 2 -}}
|
||||
- name: wait-for-{{ $name }}
|
||||
image: busybox:1.36
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- until nc -z -w2 {{ $host }} {{ $port }}; do echo "waiting for {{ $host }}:{{ $port }}"; sleep 2; done
|
||||
{{- end -}}
|
||||
@@ -0,0 +1,81 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-alerting
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "alerting") | nindent 4 }}
|
||||
spec:
|
||||
# See values.yaml's comment: replicas is not a real knob here yet.
|
||||
replicas: {{ .Values.alerting.replicas }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "alerting") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "alerting") | nindent 8 }}
|
||||
spec:
|
||||
initContainers:
|
||||
{{- include "cairnobs.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }}
|
||||
{{- include "cairnobs.waitForTCP" (list "api" (printf "%s-api" .Release.Name) "8080") | nindent 8 }}
|
||||
containers:
|
||||
- name: alerting
|
||||
image: "{{ .Values.alerting.image.repository }}:{{ .Values.alerting.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
env:
|
||||
- name: POSTGRES_ADDR
|
||||
value: "{{ .Release.Name }}-postgres:5432"
|
||||
- name: POSTGRES_DATABASE
|
||||
value: sentry_metadata
|
||||
- name: POSTGRES_USERNAME
|
||||
value: sentry
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
key: password
|
||||
- name: API_QUERY_URL
|
||||
value: "http://{{ .Release.Name }}-api:8080"
|
||||
{{- if .Values.enterprise.enabled }}
|
||||
# RoleService credential for POST /query, once api's
|
||||
# ENTERPRISE_AUTH_URL enforcement is on -- see
|
||||
# /docs/phase-4-isolation-design.md's alerting<->api gap and
|
||||
# alerting/internal/queryclient's doc comment. NOT generated
|
||||
# by this chart: mint one with
|
||||
# `enterprise-auth -mint-service-token=alerting` (see
|
||||
# enterprise/README.md) and supply it via
|
||||
# --set-string alerting.apiServiceToken=... or a values
|
||||
# override backed by a Secret you manage -- a chart
|
||||
# generating its own long-lived service credential and
|
||||
# storing it in the same release's values would defeat the
|
||||
# point of it being a distinct, revocable credential.
|
||||
{{- if .Values.alerting.apiServiceToken }}
|
||||
- name: API_SERVICE_TOKEN
|
||||
value: {{ .Values.alerting.apiServiceToken | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8081
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["/alerting", "-healthcheck"]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.alerting.resources | nindent 12 }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-alerting
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "alerting") | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "alerting") | nindent 4 }}
|
||||
ports:
|
||||
- name: http
|
||||
port: 8081
|
||||
@@ -0,0 +1,86 @@
|
||||
{{/*
|
||||
Mutually exclusive with enterprise-api.yaml's Deployment+Service, gated
|
||||
the opposite way -- see that file's doc comment for why: "does a
|
||||
deployment run the tenant-isolated binary or not" should be a single
|
||||
values.yaml decision (enterprise.enabled), not two independently
|
||||
driftable ones. Both render a Service named {{ .Release.Name }}-api on
|
||||
port 8080, so every consumer (alerting's API_QUERY_URL, web's build
|
||||
args) needs zero conditional logic of its own.
|
||||
*/}}
|
||||
{{- if not .Values.enterprise.enabled }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-api
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.api.replicas }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "api") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "api") | nindent 8 }}
|
||||
spec:
|
||||
initContainers:
|
||||
{{- include "cairnobs.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }}
|
||||
{{- include "cairnobs.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }}
|
||||
{{- include "cairnobs.waitForTCP" (list "search" (printf "%s-search" .Release.Name) "50052") | nindent 8 }}
|
||||
containers:
|
||||
- name: api
|
||||
image: "{{ .Values.api.image.repository }}:{{ .Values.api.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
env:
|
||||
- name: CLICKHOUSE_ADDR
|
||||
value: "{{ .Release.Name }}-clickhouse:9000"
|
||||
- name: CLICKHOUSE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-clickhouse
|
||||
key: password
|
||||
- name: SEARCH_GRPC_ADDR
|
||||
value: "{{ .Release.Name }}-search:50052"
|
||||
- name: POSTGRES_ADDR
|
||||
value: "{{ .Release.Name }}-postgres:5432"
|
||||
- name: POSTGRES_DATABASE
|
||||
value: sentry_metadata
|
||||
- name: POSTGRES_USERNAME
|
||||
value: sentry
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
key: password
|
||||
# No ENTERPRISE_AUTH_URL here -- this file only renders when
|
||||
# enterprise.enabled is false (see the top of this file), so
|
||||
# authz.RequireRole*/RequireRoleOrService stay a permanent
|
||||
# no-op for this Deployment. enterprise-api.yaml is where
|
||||
# that enforcement actually turns on.
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["/api", "-healthcheck"]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.api.resources | nindent 12 }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-api
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }}
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
{{- end }}
|
||||
@@ -0,0 +1,113 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-clickhouse
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "clickhouse") | nindent 4 }}
|
||||
spec:
|
||||
serviceName: {{ .Release.Name }}-clickhouse
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "clickhouse") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "clickhouse") | nindent 8 }}
|
||||
spec:
|
||||
containers:
|
||||
- name: clickhouse
|
||||
image: "{{ .Values.clickhouse.image.repository }}:{{ .Values.clickhouse.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
env:
|
||||
# Required to avoid the official image's network lockdown of
|
||||
# the implicit `default` user -- see values.yaml's comment on
|
||||
# this password and docker-compose.yml's original.
|
||||
- name: CLICKHOUSE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-clickhouse
|
||||
key: password
|
||||
# Same reasoning as docker-compose.yml's identical setting --
|
||||
# enterprise/internal/tenantprovision needs CREATE USER/GRANT
|
||||
# on this admin connection, which the official image's
|
||||
# default user doesn't have without this. Confirmed the hard
|
||||
# way: -provision-tenant failed with "Not enough
|
||||
# privileges... grant CREATE USER ON *.*" against a real kind
|
||||
# cluster before this was added -- this chart had never
|
||||
# actually been exercised against a live cluster before that.
|
||||
- name: CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT
|
||||
value: "1"
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8123
|
||||
- name: native
|
||||
containerPort: 9000
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/clickhouse
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /ping
|
||||
port: http
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.clickhouse.resources | nindent 12 }}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.clickhouse.persistence.size }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-clickhouse
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "clickhouse") | nindent 4 }}
|
||||
spec:
|
||||
clusterIP: None
|
||||
selector:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "clickhouse") | nindent 4 }}
|
||||
ports:
|
||||
- name: http
|
||||
port: 8123
|
||||
- name: native
|
||||
port: 9000
|
||||
---
|
||||
# One-shot: applies /storage/migrations/*.sql -- same image
|
||||
# storage/Dockerfile builds for docker-compose.yml's clickhouse-migrate
|
||||
# service. Plain Job, not a Helm hook -- see redpanda.yaml's comment and
|
||||
# deploy/helm/cairnobs/README.md's "Startup ordering" section.
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-clickhouse-migrate
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
spec:
|
||||
backoffLimit: 6
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "clickhouse-migrate") | nindent 8 }}
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: clickhouse-migrate
|
||||
image: "{{ .Values.clickhouse.migrateImage.repository }}:{{ .Values.clickhouse.migrateImage.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
env:
|
||||
- name: CLICKHOUSE_HTTP
|
||||
value: "http://{{ .Release.Name }}-clickhouse:8123"
|
||||
- name: CLICKHOUSE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-clickhouse
|
||||
key: password
|
||||
@@ -0,0 +1,172 @@
|
||||
{{/*
|
||||
Mutually exclusive with api.yaml's Deployment+Service -- see that file's
|
||||
doc comment. This is the concrete fix for the deployment-topology gap
|
||||
/docs/security/threat-model.md names as the single largest remaining
|
||||
Phase 4 issue once both storage engines' isolation mechanisms were
|
||||
built: "nothing forces or flags whether a deployment runs the isolated
|
||||
binary." With this file, it's not a separate knob to forget -- the same
|
||||
enterprise.enabled that turns on RBAC/audit/SSO also swaps which query
|
||||
binary actually serves traffic. Uses the "api" selector label (not
|
||||
"enterprise-api") deliberately, so the shared Service name+port below
|
||||
routes to whichever Deployment is actually rendered, with zero
|
||||
conditional logic needed in any consumer (alerting, web).
|
||||
*/}}
|
||||
{{- if .Values.enterprise.enabled }}
|
||||
{{- if .Values.tenantOperator.enabled }}
|
||||
# Grants enterprise-api's -provision-tenant (enterprise/internal/
|
||||
# tenantcrd) permission to sync real provisioning results into the
|
||||
# Tenant CRD -- a Role, not a ClusterRole (unlike tenant-operator's:
|
||||
# this binary only ever provisions tenants that live in its own release
|
||||
# namespace, no reason to widen it), scoped to exactly the two resource
|
||||
# types tenantcrd.Syncer touches. Only rendered when tenantOperator is
|
||||
# also enabled -- no Tenant CRD installed, nothing to sync into.
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-enterprise-api
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-enterprise-api
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups: ["cairnobs.io"]
|
||||
resources: ["tenants"]
|
||||
verbs: ["get", "list", "create"]
|
||||
- apiGroups: ["cairnobs.io"]
|
||||
resources: ["tenants/status"]
|
||||
verbs: ["get", "update", "patch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["secrets"]
|
||||
verbs: ["get", "create", "update"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-enterprise-api
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: {{ .Release.Name }}-enterprise-api
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ .Release.Name }}-enterprise-api
|
||||
namespace: {{ .Release.Namespace }}
|
||||
---
|
||||
{{- end }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-api
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }}
|
||||
app.kubernetes.io/component: enterprise-api
|
||||
spec:
|
||||
replicas: {{ .Values.api.replicas }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "api") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "api") | nindent 8 }}
|
||||
spec:
|
||||
{{- if .Values.tenantOperator.enabled }}
|
||||
serviceAccountName: {{ .Release.Name }}-enterprise-api
|
||||
{{- end }}
|
||||
initContainers:
|
||||
{{- include "cairnobs.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }}
|
||||
{{- include "cairnobs.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }}
|
||||
{{- include "cairnobs.waitForTCP" (list "search" (printf "%s-search" .Release.Name) "50052") | nindent 8 }}
|
||||
{{- include "cairnobs.waitForTCP" (list "enterprise-auth" (printf "%s-enterprise-auth" .Release.Name) "8082") | nindent 8 }}
|
||||
containers:
|
||||
- name: enterprise-api
|
||||
image: "{{ .Values.enterprise.apiImage.repository }}:{{ .Values.enterprise.apiImage.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
env:
|
||||
# :8080, not enterprise-api's own :8083 default -- this
|
||||
# container occupies the same Service/port every consumer
|
||||
# (alerting's API_QUERY_URL, web's build args) already
|
||||
# expects "-api:8080" to mean. See this file's doc comment.
|
||||
- name: HTTP_LISTEN_ADDR
|
||||
value: ":8080"
|
||||
- name: CLICKHOUSE_ADDR
|
||||
value: "{{ .Release.Name }}-clickhouse:9000"
|
||||
# tenantprovision's admin connection -- the same credential
|
||||
# clickhouse-migrate uses, needs access_management, never a
|
||||
# tenant-scoped grant. See enterprise/internal/tenantprovision's
|
||||
# doc comment.
|
||||
- name: CLICKHOUSE_ADMIN_USERNAME
|
||||
value: "default"
|
||||
- name: CLICKHOUSE_ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-clickhouse
|
||||
key: password
|
||||
- name: SEARCH_GRPC_ADDR
|
||||
value: "{{ .Release.Name }}-search:50052"
|
||||
- name: POSTGRES_ADDR
|
||||
value: "{{ .Release.Name }}-postgres:5432"
|
||||
- name: POSTGRES_DATABASE
|
||||
value: sentry_metadata
|
||||
- name: POSTGRES_USERNAME
|
||||
value: sentry
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
key: password
|
||||
# Restricted audit_writer Postgres role (Phase 4 task 4) --
|
||||
# its own pool, never the shared "sentry" credential above.
|
||||
# See enterprise/internal/audit's doc comment.
|
||||
- name: AUDIT_WRITER_USERNAME
|
||||
value: "audit_writer"
|
||||
- name: AUDIT_WRITER_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
key: auditWriterPassword
|
||||
- name: ENTERPRISE_AUTH_URL
|
||||
value: "http://{{ .Release.Name }}-enterprise-auth:8082"
|
||||
{{- if .Values.tenantOperator.enabled }}
|
||||
# Enables enterprise/internal/tenantcrd -- -provision-tenant
|
||||
# (run via `kubectl exec` into this Deployment's Pod, using
|
||||
# its ServiceAccount/Role above) syncs real provisioning
|
||||
# results into the Tenant CRD this namespace's tenants live
|
||||
# in. Unset (the default, when tenantOperator isn't enabled)
|
||||
# is a documented no-op -- see apiconfig.Config.TenantCRDNamespace.
|
||||
- name: TENANT_CRD_NAMESPACE
|
||||
value: {{ .Release.Namespace | quote }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8080
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["/enterprise-api", "-healthcheck"]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.api.resources | nindent 12 }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-api
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "api") | nindent 4 }}
|
||||
ports:
|
||||
- name: http
|
||||
port: 8080
|
||||
{{- end }}
|
||||
@@ -0,0 +1,87 @@
|
||||
{{- if .Values.enterprise.enabled }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-enterprise-auth
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "enterprise-auth") | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.enterprise.replicas }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "enterprise-auth") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "enterprise-auth") | nindent 8 }}
|
||||
spec:
|
||||
initContainers:
|
||||
{{- include "cairnobs.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }}
|
||||
containers:
|
||||
- name: enterprise-auth
|
||||
image: "{{ .Values.enterprise.image.repository }}:{{ .Values.enterprise.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
env:
|
||||
- name: ENTERPRISE_SESSION_SIGNING_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-enterprise-auth
|
||||
key: sessionSigningKey
|
||||
- name: POSTGRES_ADDR
|
||||
value: "{{ .Release.Name }}-postgres:5432"
|
||||
- name: POSTGRES_DATABASE
|
||||
value: sentry_metadata
|
||||
- name: POSTGRES_USERNAME
|
||||
value: sentry
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
key: password
|
||||
{{- if .Values.enterprise.oidc.issuerURL }}
|
||||
- name: OIDC_ISSUER_URL
|
||||
value: {{ .Values.enterprise.oidc.issuerURL | quote }}
|
||||
- name: OIDC_CLIENT_ID
|
||||
value: {{ .Values.enterprise.oidc.clientID | quote }}
|
||||
- name: OIDC_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-enterprise-auth
|
||||
key: oidcClientSecret
|
||||
- name: OIDC_REDIRECT_URL
|
||||
value: {{ .Values.enterprise.oidc.redirectURL | quote }}
|
||||
{{- end }}
|
||||
{{- if .Values.enterprise.saml.idpMetadataURL }}
|
||||
- name: SAML_ENTITY_ID
|
||||
value: {{ .Values.enterprise.saml.entityID | quote }}
|
||||
- name: SAML_ACS_URL
|
||||
value: {{ .Values.enterprise.saml.acsURL | quote }}
|
||||
- name: SAML_IDP_METADATA_URL
|
||||
value: {{ .Values.enterprise.saml.idpMetadataURL | quote }}
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8082
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["/enterprise-auth", "-healthcheck"]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.enterprise.resources | nindent 12 }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-enterprise-auth
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "enterprise-auth") | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "enterprise-auth") | nindent 4 }}
|
||||
ports:
|
||||
- name: http
|
||||
port: 8082
|
||||
{{- end }}
|
||||
@@ -0,0 +1,64 @@
|
||||
{{/*
|
||||
Only rendered once per-tenant write-routing is actually turned on (see
|
||||
ingest.yaml's -mode=server comment) -- this Deployment is what takes
|
||||
over consuming sentry.logs.raw once ingest.yaml's own consumer half
|
||||
stops, routing each record to its own tenant's dedicated ClickHouse
|
||||
database (enterprise/internal/chwriter) instead of the one shared table.
|
||||
No Service: this is a pure background worker, nothing calls it, only
|
||||
kubelet's own probes talk to its /healthz.
|
||||
*/}}
|
||||
{{- if and .Values.enterprise.enabled .Values.ingest.requireTenantCredential }}
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-enterprise-ingest
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "enterprise-ingest") | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.ingest.replicas }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "enterprise-ingest") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "enterprise-ingest") | nindent 8 }}
|
||||
spec:
|
||||
initContainers:
|
||||
{{- include "cairnobs.waitForTCP" (list "redpanda" (printf "%s-redpanda" .Release.Name) "9092") | nindent 8 }}
|
||||
{{- include "cairnobs.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }}
|
||||
{{- include "cairnobs.waitForTCP" (list "postgres" (printf "%s-postgres" .Release.Name) "5432") | nindent 8 }}
|
||||
containers:
|
||||
- name: enterprise-ingest
|
||||
image: "{{ .Values.enterprise.ingestImage.repository }}:{{ .Values.enterprise.ingestImage.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
env:
|
||||
- name: REDPANDA_BROKERS
|
||||
value: "{{ .Release.Name }}-redpanda:9092"
|
||||
- name: CLICKHOUSE_ADDR
|
||||
value: "{{ .Release.Name }}-clickhouse:9000"
|
||||
- name: POSTGRES_ADDR
|
||||
value: "{{ .Release.Name }}-postgres:5432"
|
||||
- name: POSTGRES_DATABASE
|
||||
value: sentry_metadata
|
||||
- name: POSTGRES_USERNAME
|
||||
value: sentry
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
key: password
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["/enterprise-ingest", "-healthcheck"]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
exec:
|
||||
command: ["/enterprise-ingest", "-healthcheck"]
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
{{- toYaml .Values.ingest.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,95 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-ingest
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "ingest") | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.ingest.replicas }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "ingest") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "ingest") | nindent 8 }}
|
||||
spec:
|
||||
initContainers:
|
||||
{{- include "cairnobs.waitForTCP" (list "redpanda" (printf "%s-redpanda" .Release.Name) "9092") | nindent 8 }}
|
||||
{{- include "cairnobs.waitForTCP" (list "clickhouse" (printf "%s-clickhouse" .Release.Name) "9000") | nindent 8 }}
|
||||
containers:
|
||||
- name: ingest
|
||||
image: "{{ .Values.ingest.image.repository }}:{{ .Values.ingest.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
{{- if and .Values.enterprise.enabled .Values.ingest.requireTenantCredential }}
|
||||
# -mode=server only: this Deployment stops running the
|
||||
# ClickHouse-writing consumer half (the default -mode=all)
|
||||
# once per-tenant write-routing is on -- enterprise-ingest.yaml
|
||||
# (below) takes over consuming sentry.logs.raw instead, so it
|
||||
# can write each tenant's records to their own database rather
|
||||
# than the one shared table ingest's own consumer always
|
||||
# writes to. The agent-facing server half (PushBatch, tenant
|
||||
# tagging via TenantResolver) keeps running here unconditionally
|
||||
# either way -- only which process consumes the topic changes.
|
||||
args: ["-mode=server"]
|
||||
{{- end }}
|
||||
env:
|
||||
- name: REDPANDA_BROKERS
|
||||
value: "{{ .Release.Name }}-redpanda:9092"
|
||||
- name: CLICKHOUSE_ADDR
|
||||
value: "{{ .Release.Name }}-clickhouse:9000"
|
||||
- name: CLICKHOUSE_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-clickhouse
|
||||
key: password
|
||||
{{- if and .Values.enterprise.enabled .Values.ingest.requireTenantCredential }}
|
||||
# Enables ingest/internal/grpcserver.TenantResolver.
|
||||
# Deliberately its OWN opt-in, not folded into
|
||||
# enterprise.enabled directly (same reasoning
|
||||
# api.yaml/enterprise-api.yaml's ENTERPRISE_AUTH_URL isn't
|
||||
# set just because enterprise.enabled is true -- see that
|
||||
# env var's own comment there): turning this on requires
|
||||
# every agent to already present a valid `Authorization:
|
||||
# Bearer <ingest token>` (minted via `enterprise-auth
|
||||
# -create-ingest-credential-tenant=<id>`) or be refused
|
||||
# outright, which would silently break ingest for any
|
||||
# not-yet-reconfigured agent if it defaulted on alongside
|
||||
# enterprise.enabled. Off (the default) leaves every record
|
||||
# without a tenant_id header, same as every Phase 0-3
|
||||
# deployment.
|
||||
- name: ENTERPRISE_AUTH_URL
|
||||
value: "http://{{ .Release.Name }}-enterprise-auth:8082"
|
||||
{{- end }}
|
||||
ports:
|
||||
- name: grpc
|
||||
containerPort: 4317
|
||||
{{- if .Values.ingest.tlsSecretName }}
|
||||
volumeMounts:
|
||||
- name: tls
|
||||
mountPath: /etc/cairnobs-ingest
|
||||
readOnly: true
|
||||
{{- end }}
|
||||
resources:
|
||||
{{- toYaml .Values.ingest.resources | nindent 12 }}
|
||||
{{- if .Values.ingest.tlsSecretName }}
|
||||
volumes:
|
||||
- name: tls
|
||||
secret:
|
||||
secretName: {{ .Values.ingest.tlsSecretName }}
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-ingest
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "ingest") | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "ingest") | nindent 4 }}
|
||||
ports:
|
||||
- name: grpc
|
||||
port: 4317
|
||||
@@ -0,0 +1,110 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "postgres") | nindent 4 }}
|
||||
spec:
|
||||
serviceName: {{ .Release.Name }}-postgres
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "postgres") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "postgres") | nindent 8 }}
|
||||
spec:
|
||||
containers:
|
||||
- name: postgres
|
||||
image: "{{ .Values.postgres.image.repository }}:{{ .Values.postgres.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
env:
|
||||
- name: POSTGRES_DB
|
||||
value: sentry_metadata
|
||||
- name: POSTGRES_USER
|
||||
value: sentry
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
key: password
|
||||
ports:
|
||||
- name: postgres
|
||||
containerPort: 5432
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/postgresql/data
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["pg_isready", "-U", "sentry", "-d", "sentry_metadata"]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.postgres.resources | nindent 12 }}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.postgres.persistence.size }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "postgres") | nindent 4 }}
|
||||
spec:
|
||||
clusterIP: None
|
||||
selector:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "postgres") | nindent 4 }}
|
||||
ports:
|
||||
- name: postgres
|
||||
port: 5432
|
||||
---
|
||||
# One-shot: applies /metadata/migrations/*.sql (including Phase 4's
|
||||
# tenants/users/tenant_memberships/audit_log schema) -- same image
|
||||
# metadata/Dockerfile builds for docker-compose.yml's metadata-migrate
|
||||
# service. Plain Job, not a Helm hook -- see redpanda.yaml's comment.
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-metadata-migrate
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
spec:
|
||||
backoffLimit: 6
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "metadata-migrate") | nindent 8 }}
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: metadata-migrate
|
||||
image: "{{ .Values.postgres.migrateImage.repository }}:{{ .Values.postgres.migrateImage.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
env:
|
||||
- name: POSTGRES_HOST
|
||||
value: "{{ .Release.Name }}-postgres"
|
||||
- name: POSTGRES_PORT
|
||||
value: "5432"
|
||||
- name: POSTGRES_USER
|
||||
value: sentry
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
key: password
|
||||
- name: POSTGRES_DATABASE
|
||||
value: sentry_metadata
|
||||
- name: AUDIT_WRITER_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
key: auditWriterPassword
|
||||
@@ -0,0 +1,108 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-redpanda
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "redpanda") | nindent 4 }}
|
||||
spec:
|
||||
serviceName: {{ .Release.Name }}-redpanda
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "redpanda") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "redpanda") | nindent 8 }}
|
||||
spec:
|
||||
containers:
|
||||
- name: redpanda
|
||||
image: "{{ .Values.redpanda.image.repository }}:{{ .Values.redpanda.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
args:
|
||||
- redpanda
|
||||
- start
|
||||
- --smp=1
|
||||
- --memory=1G
|
||||
- --reserve-memory=0M
|
||||
- --overprovisioned
|
||||
- --node-id=0
|
||||
- --check=false
|
||||
- --kafka-addr=PLAINTEXT://0.0.0.0:9092
|
||||
- --advertise-kafka-addr=PLAINTEXT://{{ .Release.Name }}-redpanda:9092
|
||||
ports:
|
||||
- name: kafka
|
||||
containerPort: 9092
|
||||
- name: admin
|
||||
containerPort: 9644
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/redpanda/data
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["rpk", "cluster", "health", "--exit-when-healthy"]
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
resources:
|
||||
{{- toYaml .Values.redpanda.resources | nindent 12 }}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.redpanda.persistence.size }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-redpanda
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "redpanda") | nindent 4 }}
|
||||
spec:
|
||||
clusterIP: None
|
||||
selector:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "redpanda") | nindent 4 }}
|
||||
ports:
|
||||
- name: kafka
|
||||
port: 9092
|
||||
- name: admin
|
||||
port: 9644
|
||||
---
|
||||
# One-shot: creates the sentry.logs.raw topic. Same image
|
||||
# transport/Dockerfile builds for docker-compose.yml's redpanda-provision
|
||||
# service. Deliberately a plain Job, not a Helm hook -- see
|
||||
# deploy/helm/cairnobs/README.md's "Startup ordering" section for why
|
||||
# (StatefulSets-as-hooks breaks helm upgrade/uninstall's ownership
|
||||
# tracking of stateful resources). backoffLimit gives it room to retry
|
||||
# until redpanda's StatefulSet is actually ready; ingest/search's own
|
||||
# crash-and-restart-on-connect-failure covers the rest of the ordering,
|
||||
# same as every dependency in this chart.
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-redpanda-provision
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
spec:
|
||||
backoffLimit: 6
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "redpanda-provision") | nindent 8 }}
|
||||
spec:
|
||||
restartPolicy: OnFailure
|
||||
containers:
|
||||
- name: redpanda-provision
|
||||
image: "{{ .Values.redpanda.provisionImage.repository }}:{{ .Values.redpanda.provisionImage.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
env:
|
||||
- name: REDPANDA_BROKERS
|
||||
value: "{{ .Release.Name }}-redpanda:9092"
|
||||
- name: REDPANDA_ADMIN_HOSTS
|
||||
value: "{{ .Release.Name }}-redpanda:9644"
|
||||
- name: REDPANDA_TOPIC_PARTITIONS
|
||||
value: "6"
|
||||
@@ -0,0 +1,71 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-search
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "search") | nindent 4 }}
|
||||
spec:
|
||||
# See values.yaml's comment: replicas is not a real knob here yet.
|
||||
replicas: {{ .Values.search.replicas }}
|
||||
strategy:
|
||||
type: Recreate # single PVC below (ReadWriteOnce) -- avoid two pods racing to mount it during a rollout
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "search") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "search") | nindent 8 }}
|
||||
spec:
|
||||
initContainers:
|
||||
{{- include "cairnobs.waitForTCP" (list "redpanda" (printf "%s-redpanda" .Release.Name) "9092") | nindent 8 }}
|
||||
containers:
|
||||
- name: search
|
||||
image: "{{ .Values.search.image.repository }}:{{ .Values.search.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
env:
|
||||
- name: REDPANDA_BROKERS
|
||||
value: "{{ .Release.Name }}-redpanda:9092"
|
||||
- name: REDPANDA_TOPIC_PARTITIONS
|
||||
value: "6"
|
||||
- name: RUST_LOG
|
||||
value: "info"
|
||||
ports:
|
||||
- name: grpc
|
||||
containerPort: 50052
|
||||
volumeMounts:
|
||||
- name: index-data
|
||||
mountPath: /var/lib/cairnobs-search
|
||||
resources:
|
||||
{{- toYaml .Values.search.resources | nindent 12 }}
|
||||
volumes:
|
||||
- name: index-data
|
||||
persistentVolumeClaim:
|
||||
claimName: {{ .Release.Name }}-search-index
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-search-index
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: {{ .Values.search.persistence.size }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-search
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "search") | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "search") | nindent 4 }}
|
||||
ports:
|
||||
- name: grpc
|
||||
port: 50052
|
||||
@@ -0,0 +1,81 @@
|
||||
{{/*
|
||||
Shared control-plane secrets -- the cluster-wide passwords
|
||||
docker-compose.yml hardcodes as "cairnobs-dev-only"/etc (see its
|
||||
clickhouse/metadata-postgres/metadata-migrate comments) become real
|
||||
generated-or-supplied Secrets here. Each follows the same pattern: a
|
||||
values override wins if set, otherwise a value is generated once and
|
||||
kept stable across `helm upgrade` via `lookup` (so upgrades don't
|
||||
silently rotate a live credential out from under a running Deployment --
|
||||
same "never rotate a live credential without coordinating the
|
||||
consumer-side change" reasoning as
|
||||
deploy/operator/internal/controller/tenant_controller.go's
|
||||
reconcileSecret). `lookup` returns nothing under `helm template`
|
||||
(no live cluster) -- expected; see deploy/README.md's verification
|
||||
section for what that means for this file specifically.
|
||||
*/}}
|
||||
{{- define "cairnobs.stableSecretValue" -}}
|
||||
{{- $ns := index . 0 -}}
|
||||
{{- $name := index . 1 -}}
|
||||
{{- $key := index . 2 -}}
|
||||
{{- $override := index . 3 -}}
|
||||
{{- $existing := lookup "v1" "Secret" $ns $name -}}
|
||||
{{- if $override -}}
|
||||
{{ $override }}
|
||||
{{- else if $existing -}}
|
||||
{{ index $existing.data $key | b64dec }}
|
||||
{{- else -}}
|
||||
{{ randAlphaNum 40 }}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-clickhouse
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
# The official clickhouse-server image locks down *network* access
|
||||
# entirely for the implicit `default` user unless this is genuinely
|
||||
# non-empty -- see docker-compose.yml's clickhouse service comment.
|
||||
# Not a substitute for task 2's per-tenant credentials (still unbuilt
|
||||
# -- see deploy/operator's Tenant controller); this is the shared
|
||||
# admin/migration credential only.
|
||||
password: {{ include "cairnobs.stableSecretValue" (list .Release.Namespace (printf "%s-clickhouse" .Release.Name) "password" .Values.clickhouse.password) }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-postgres
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
password: {{ include "cairnobs.stableSecretValue" (list .Release.Namespace (printf "%s-postgres" .Release.Name) "password" .Values.postgres.password) }}
|
||||
# Restricted audit_writer Postgres role (Phase 4 task 4) -- INSERT+SELECT
|
||||
# only, via its own pool, never the shared role above. See
|
||||
# /docs/phase-4-isolation-design.md's audit-logging section and
|
||||
# metadata/README.md.
|
||||
auditWriterPassword: {{ include "cairnobs.stableSecretValue" (list .Release.Namespace (printf "%s-postgres" .Release.Name) "auditWriterPassword" .Values.postgres.auditWriterPassword) }}
|
||||
{{- if .Values.enterprise.enabled }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-enterprise-auth
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
type: Opaque
|
||||
stringData:
|
||||
# Must be >= 32 bytes -- see enterprise/internal/config.Load and
|
||||
# enterprise/internal/session.MinSigningKeyBytes. Rotating this
|
||||
# invalidates every outstanding session/service token -- same
|
||||
# "don't rotate a live credential silently" reasoning as above,
|
||||
# which is why it's kept stable via the lookup above rather than
|
||||
# regenerated on every `helm upgrade`.
|
||||
sessionSigningKey: {{ include "cairnobs.stableSecretValue" (list .Release.Namespace (printf "%s-enterprise-auth" .Release.Name) "sessionSigningKey" .Values.enterprise.sessionSigningKey) }}
|
||||
{{- if .Values.enterprise.oidc.clientSecret }}
|
||||
oidcClientSecret: {{ .Values.enterprise.oidc.clientSecret | quote }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,93 @@
|
||||
{{- if and .Values.enterprise.enabled .Values.tenantOperator.enabled }}
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-tenant-operator
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
---
|
||||
# ClusterRole, not Role: Tenant is cluster-scoped-CRD-but-namespaced-object
|
||||
# (see crds/cairnobs.io_tenants.yaml's scope: Namespaced), and this chart
|
||||
# doesn't assume it's the only namespace the operator might one day watch
|
||||
# -- narrowed to exactly the two resource types
|
||||
# deploy/operator/internal/controller/tenant_controller.go's
|
||||
# +kubebuilder:rbac markers name (tenants, tenants/status), not a
|
||||
# wildcard grant. No `secrets` permission -- this controller stopped
|
||||
# managing the ClickHouse credential Secret once enterprise-api
|
||||
# -provision-tenant took over creating it with real credentials (see
|
||||
# that controller's doc comment); see enterprise-api.yaml's own
|
||||
# ServiceAccount/Role for the `secrets` grant that binary needs instead.
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-tenant-operator
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups: ["cairnobs.io"]
|
||||
resources: ["tenants"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
|
||||
- apiGroups: ["cairnobs.io"]
|
||||
resources: ["tenants/status"]
|
||||
verbs: ["get", "update", "patch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-tenant-operator
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: {{ .Release.Name }}-tenant-operator
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ .Release.Name }}-tenant-operator
|
||||
namespace: {{ .Release.Namespace }}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-tenant-operator
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "tenant-operator") | nindent 4 }}
|
||||
spec:
|
||||
# One replica -- see deploy/operator/cmd/tenant-operator/main.go's
|
||||
# comment: no leader election yet, a second replica could
|
||||
# double-generate a Secret.
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "tenant-operator") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "tenant-operator") | nindent 8 }}
|
||||
spec:
|
||||
serviceAccountName: {{ .Release.Name }}-tenant-operator
|
||||
containers:
|
||||
- name: tenant-operator
|
||||
image: "{{ .Values.tenantOperator.image.repository }}:{{ .Values.tenantOperator.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
ports:
|
||||
- name: metrics
|
||||
containerPort: 8080
|
||||
- name: probes
|
||||
containerPort: 8081
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: probes
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: probes
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
{{- toYaml .Values.tenantOperator.resources | nindent 12 }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,14 @@
|
||||
{{- if .Values.enterprise.enabled }}
|
||||
{{- range .Values.tenants }}
|
||||
---
|
||||
apiVersion: cairnobs.io/v1alpha1
|
||||
kind: Tenant
|
||||
metadata:
|
||||
name: {{ .name }}
|
||||
labels:
|
||||
{{- include "cairnobs.labels" $ | nindent 4 }}
|
||||
spec:
|
||||
displayName: {{ .displayName | default .name | quote }}
|
||||
suspended: {{ .suspended | default false }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,43 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-web
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "web") | nindent 4 }}
|
||||
spec:
|
||||
replicas: {{ .Values.web.replicas }}
|
||||
selector:
|
||||
matchLabels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "web") | nindent 6 }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "web") | nindent 8 }}
|
||||
spec:
|
||||
containers:
|
||||
- name: web
|
||||
# No env vars here -- see values.yaml's web.builtWith* comment:
|
||||
# this is a static build, its API base URLs are baked into the
|
||||
# image, not configurable at the Deployment level.
|
||||
image: "{{ .Values.web.image.repository }}:{{ .Values.web.image.tag }}"
|
||||
imagePullPolicy: {{ .Values.global.imagePullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 3000
|
||||
resources:
|
||||
{{- toYaml .Values.web.resources | nindent 12 }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-web
|
||||
labels:
|
||||
{{- include "cairnobs.labels" . | nindent 4 }}
|
||||
{{- include "cairnobs.selectorLabels" (list $ "web") | nindent 4 }}
|
||||
spec:
|
||||
selector:
|
||||
{{- include "cairnobs.selectorLabels" (list $ "web") | nindent 4 }}
|
||||
ports:
|
||||
- name: http
|
||||
port: 3000
|
||||
@@ -0,0 +1,218 @@
|
||||
# Default values for the cairnobs chart. See deploy/helm/cairnobs/README.md
|
||||
# for the multi-tenant-specific values (enterprise.*, tenants) and what
|
||||
# "multi-tenant-aware" does and doesn't mean at this layer.
|
||||
#
|
||||
# Image repositories default to locally-built tags matching each
|
||||
# service's docker-compose.yml container_name, minus the "cairnobs-"
|
||||
# container_name prefix duplication -- push these to a registry this
|
||||
# cluster can actually pull from before installing; this chart never
|
||||
# builds images itself (same division of labor as docker-compose.yml:
|
||||
# `docker compose build` vs. `docker compose up`).
|
||||
|
||||
global:
|
||||
imagePullPolicy: IfNotPresent
|
||||
|
||||
redpanda:
|
||||
image:
|
||||
repository: docker.redpanda.com/redpandadata/redpanda
|
||||
tag: v24.2.7
|
||||
persistence:
|
||||
size: 10Gi
|
||||
resources: {}
|
||||
# Built from ./transport (docker-compose.yml's redpanda-provision
|
||||
# service) -- the one-shot topic-creation Job below.
|
||||
provisionImage:
|
||||
repository: cairnobs-redpanda-provision
|
||||
tag: latest
|
||||
|
||||
clickhouse:
|
||||
image:
|
||||
repository: clickhouse/clickhouse-server
|
||||
tag: "24.8"
|
||||
persistence:
|
||||
size: 20Gi
|
||||
resources: {}
|
||||
# Leave empty to auto-generate and persist across upgrades -- see
|
||||
# templates/secrets.yaml's stableSecretValue helper.
|
||||
password: ""
|
||||
# Built from ./storage (docker-compose.yml's clickhouse-migrate
|
||||
# service) -- the one-shot schema-migration Job.
|
||||
migrateImage:
|
||||
repository: cairnobs-clickhouse-migrate
|
||||
tag: latest
|
||||
|
||||
postgres:
|
||||
image:
|
||||
repository: postgres
|
||||
tag: 16-alpine
|
||||
persistence:
|
||||
size: 10Gi
|
||||
resources: {}
|
||||
password: ""
|
||||
auditWriterPassword: ""
|
||||
# Built from ./metadata (docker-compose.yml's metadata-migrate
|
||||
# service) -- the one-shot schema-migration Job.
|
||||
migrateImage:
|
||||
repository: cairnobs-metadata-migrate
|
||||
tag: latest
|
||||
|
||||
ingest:
|
||||
image:
|
||||
repository: cairnobs-ingest
|
||||
tag: latest
|
||||
replicas: 1
|
||||
resources: {}
|
||||
# mTLS server cert/key/CA -- see hack/dev-certs/generate.sh for the
|
||||
# dev equivalent of what this Secret must contain
|
||||
# (server.pem/server-key.pem/ca.pem) in a real deployment. Unlike
|
||||
# docker-compose.yml's bind-mounted ./hack/dev-certs/out, a cluster
|
||||
# deployment supplies this as a real Secret -- named here, not
|
||||
# generated by this chart (cert issuance is out of scope, same
|
||||
# "boring, well-understood" preference as everywhere else in this
|
||||
# repo -- use cert-manager or an equivalent, don't hand-roll it here).
|
||||
tlsSecretName: ""
|
||||
# Only meaningful when enterprise.enabled is also true -- see
|
||||
# templates/ingest.yaml's ENTERPRISE_AUTH_URL comment for why this is
|
||||
# its own deliberate opt-in, not folded into enterprise.enabled
|
||||
# directly: turning it on requires every agent to already present a
|
||||
# valid ingest credential (`enterprise-auth
|
||||
# -create-ingest-credential-tenant=<id>`) or be refused outright. Also
|
||||
# controls per-tenant write-routing: true switches ingest.yaml's
|
||||
# Deployment to -mode=server only and renders enterprise-ingest.yaml
|
||||
# to take over consuming sentry.logs.raw, writing each tenant's
|
||||
# records into their own ClickHouse database instead of the one
|
||||
# shared table -- both flags gate together since write-routing is only
|
||||
# meaningful once records actually carry a tenant_id to route on.
|
||||
requireTenantCredential: false
|
||||
|
||||
search:
|
||||
image:
|
||||
repository: cairnobs-search
|
||||
tag: latest
|
||||
# Pinned to 1: search consumes the same Redpanda topic ingest's
|
||||
# consumer does with its own offset tracking (see search/README.md).
|
||||
# A second replica would form a second, independent consumer instance
|
||||
# against the same partitions with no coordination -- correctness,
|
||||
# not just resource waste, is the reason this isn't a `replicas` knob
|
||||
# yet. Matches CLAUDE.md's Phase 4 non-goal: "no general multi-cluster
|
||||
# orchestration."
|
||||
replicas: 1
|
||||
resources: {}
|
||||
persistence:
|
||||
size: 20Gi
|
||||
|
||||
api:
|
||||
image:
|
||||
repository: cairnobs-api
|
||||
tag: latest
|
||||
replicas: 2
|
||||
resources: {}
|
||||
|
||||
alerting:
|
||||
image:
|
||||
repository: cairnobs-alerting
|
||||
tag: latest
|
||||
# Pinned to 1 for the same reason as search: rulestore.ClaimDueRules
|
||||
# has no leader-election/partitioning story for multiple evaluator
|
||||
# replicas yet -- two would both try to claim and evaluate the same
|
||||
# due rules. Named explicitly rather than silently defaulted, since
|
||||
# it's the kind of knob someone reasonably expects to just work.
|
||||
replicas: 1
|
||||
resources: {}
|
||||
# See templates/alerting.yaml's comment -- only meaningful when
|
||||
# enterprise.enabled is true. Empty by default.
|
||||
apiServiceToken: ""
|
||||
|
||||
web:
|
||||
image:
|
||||
repository: cairnobs-web
|
||||
tag: latest
|
||||
replicas: 2
|
||||
resources: {}
|
||||
# NOT wired to any Deployment env var -- web is a static SvelteKit
|
||||
# build (adapter-static, see web/package.json), and VITE_API_BASE_URL/
|
||||
# VITE_ALERTING_API_BASE_URL/VITE_ENTERPRISE_AUTH_BASE_URL are baked in
|
||||
# at *image build time* (docker-compose.yml's web.build.args), not
|
||||
# read at container runtime. Deploying this chart into a real cluster
|
||||
# means rebuilding the web image with these three build args pointed
|
||||
# at wherever api/alerting/enterprise-auth are actually reachable from
|
||||
# a browser (an Ingress host, a LoadBalancer IP, etc.) -- this section
|
||||
# exists to document that requirement, not because the chart can act
|
||||
# on it.
|
||||
builtWithApiBaseURL: "http://localhost:8080"
|
||||
builtWithAlertingApiBaseURL: "http://localhost:8081"
|
||||
builtWithEnterpriseAuthBaseURL: "http://localhost:8082"
|
||||
|
||||
# enterprise-auth (commercial license) + the tenant-operator that
|
||||
# reconciles the Tenant CRD -- both off by default, matching
|
||||
# docker-compose.yml's own "included, not wired into enforcement by
|
||||
# default" stance (see its enterprise-auth service comment) and
|
||||
# enterprise/README.md's "Status" section on what's built vs. deferred.
|
||||
enterprise:
|
||||
enabled: false
|
||||
image:
|
||||
repository: cairnobs-enterprise-auth
|
||||
tag: latest
|
||||
# enterprise-api (templates/enterprise-api.yaml) -- swaps in for
|
||||
# api.yaml's plain api Deployment when enterprise.enabled is true, on
|
||||
# the same Service/port every consumer already expects. Built from the
|
||||
# repo root (needs api/ and proto/, not just enterprise/), unlike
|
||||
# enterprise-auth's image above -- see
|
||||
# enterprise/cmd/enterprise-api/Dockerfile.
|
||||
apiImage:
|
||||
repository: cairnobs-enterprise-api
|
||||
tag: latest
|
||||
# enterprise-ingest (templates/enterprise-ingest.yaml) -- only
|
||||
# rendered when ingest.requireTenantCredential is also true (see that
|
||||
# value's comment); takes over consuming sentry.logs.raw from
|
||||
# ingest.yaml's own consumer once per-tenant write-routing is on. Same
|
||||
# "repo root build context" reasoning as apiImage above -- see
|
||||
# enterprise/cmd/enterprise-ingest/Dockerfile.
|
||||
ingestImage:
|
||||
repository: cairnobs-enterprise-ingest
|
||||
tag: latest
|
||||
replicas: 1
|
||||
resources: {}
|
||||
# Leave empty to auto-generate (>= 32 bytes) and persist across
|
||||
# upgrades -- see templates/secrets.yaml.
|
||||
sessionSigningKey: ""
|
||||
oidc:
|
||||
issuerURL: ""
|
||||
clientID: ""
|
||||
clientSecret: ""
|
||||
redirectURL: ""
|
||||
saml:
|
||||
entityID: ""
|
||||
acsURL: ""
|
||||
idpMetadataURL: ""
|
||||
|
||||
# Installs deploy/operator (the Tenant CRD controller) alongside this
|
||||
# chart. Only meaningful when enterprise.enabled is also true -- gated
|
||||
# on that, not a separate flag, since a Tenant CR with nothing to
|
||||
# reflect (see below) has nothing to do. Also turns on enterprise-api's
|
||||
# own Tenant-CRD-syncing permissions (a ServiceAccount/Role, and the
|
||||
# TENANT_CRD_NAMESPACE env var) -- see templates/enterprise-api.yaml.
|
||||
tenantOperator:
|
||||
enabled: false
|
||||
image:
|
||||
repository: cairnobs-tenant-operator
|
||||
tag: latest
|
||||
resources: {}
|
||||
|
||||
# One entry per tenant to provision -- rendered as Tenant CRs
|
||||
# (templates/tenants.yaml), a declarative request an admin/GitOps
|
||||
# process makes. The operator (tenant-operator, above) only ever
|
||||
# *reflects* real state onto these objects (Phase/Conditions, derived
|
||||
# from what enterprise-api's `-provision-tenant` has reported) -- it's
|
||||
# `-provision-tenant` (run via `kubectl exec` into the enterprise-api
|
||||
# Pod), not the operator, that actually calls ClickHouse and writes the
|
||||
# credential Secret. See deploy/operator/internal/controller/
|
||||
# tenant_controller.go's and enterprise/internal/tenantcrd's doc
|
||||
# comments for the full split. Empty by default; a real two-tenant
|
||||
# deployment (Phase 4's exit criteria) sets e.g.:
|
||||
# tenants:
|
||||
# - name: acme
|
||||
# displayName: "Acme Corp"
|
||||
# - name: globex
|
||||
# displayName: "Globex Corporation"
|
||||
tenants: []
|
||||
Reference in New Issue
Block a user