Added OIDC workflow and Helm Chart
Checks / test (push) Failing after 4m13s

This commit is contained in:
Aleksandr Bogomiakov committed 2026-10-07 14:22:37 +01:00
1 parent 984c7083a1
commit 7afdc4be97
29 files changed
+872 -33

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
.git
.gitignore
ci/
*.swp
*.bak
+7
View File
@@ -0,0 +1,7 @@
apiVersion: v2
name: meguri
description: On-call schedules, replacements and escalation policies as a source of truth
type: application
version: 0.1.0
appVersion: "0.1.0"
kubeVersion: ">=1.29.0-0"
+79
View File
@@ -0,0 +1,79 @@
# Meguri Helm chart
Deploys the web/worker Deployment, Service, optional Ingress and PodDisruptionBudget, and runs a
**pre-install/pre-upgrade migrate Job** that applies Cot migrations and the declarative instance
configuration *before* the Deployment is updated. The chart contains no secrets and no Helm-managed
database. Requires Kubernetes 1.29+ (native sidecars) and Helm 3.
## What you provide
1. An image: `ultradesu/meguri:latest` by default (build your own from the repository `Dockerfile` and set `image.repository` / `image.tag`; pin a tag for reproducible releases).
2. A Secret in the release namespace, named by `secretName`. All of its keys become environment
variables:
```sh
kubectl -n meguri create secret generic meguri-secrets \
--from-literal=DATABASE_URL='postgresql://USER:PERCENT_ENCODED_PASSWORD@HOST:5432/meguri' \
--from-literal=MEGURI_OIDC_CLIENT_SECRET='...'
```
Per-tenant provider credentials (`MEGURI_JSM_<TENANT>_EMAIL` / `_TOKEN`) go in the same Secret. `extraSecretNames` lists further Secrets injected the same way, for example one that holds only `MEGURI_OIDC_CLIENT_SECRET`.
3. Your values. The two required ones are `publicUrl` (the exact browser origin) and `secretName`;
everything about people and access lives under `instanceConfig`.
## Install and upgrade
```sh
helm upgrade --install meguri ./helm/meguri -n meguri --create-namespace -f my-values.yaml
kubectl -n meguri logs job/meguri-migrate-1 # what the sync did; revision number increments
```
`instanceConfig` is rendered verbatim into `/etc/meguri/config.yaml` (field reference:
`deploy/instance-config.example.yaml`, behavior: `docs/deployment.md`). The chart schema rejects
unknown fields and obviously invalid values at `helm lint`/`install`; the application validates it
again, and a rejected configuration fails the migrate Job, so the release stops before any pod
changes. A configuration change rolls the Deployment (checksum annotation) after the Job succeeded.
```yaml
publicUrl: https://meguri.example.internal
secretName: meguri-secrets
image: {repository: ultradesu/meguri, tag: "1.4.0"}
instanceConfig:
auth: {password_login: false}
oidc:
issuer: https://sso.example.internal/realms/corp
client_id: meguri
instance_admin_groups: [/meguri/admins]
workspaces:
- key: payments
name: Payments
admin_groups: [/payments/leads]
member_groups: [/payments/team]
- key: customer-support
name: Customer Support
admin_groups: [/support/leads]
member_groups: [/support/team, /support/contractors]
```
Removing a workspace from `workspaces` **disables** it at the next release (data is kept; an
instance administrator can delete it permanently). Giving a new team access is one more list entry.
## Break-glass administrator
Add `instanceConfig.auth.bootstrap_admin.usernames: [rescue]` and upgrade. The generated password is
printed in the migrate Job log (`kubectl logs job/meguri-migrate-N`) and rotates each UTC hour; anyone who can read
that log can sign in as an instance administrator, so remove the block as soon as SSO works again.
Finished Jobs are deleted after `migrate.ttlSecondsAfterFinished` (a day).
## Notable values
| Value | Purpose |
|---|---|
| `replicaCount`, `resources`, `podDisruptionBudget` | Sizing and availability (publication is serialized by database leases, so replicas are safe). |
| `cloudSqlProxy.*` | Cloud SQL Auth Proxy native sidecar for the app and the Job; point `DATABASE_URL` at `127.0.0.1:5432`. |
| `serviceAccount.create` / `.annotations` | Workload Identity account, created once at install (the pre-install hook needs it). Later annotation changes need `kubectl annotate`. |
| `ingress.*` | Host and TLS follow `publicUrl`. |
| `allowDisableAll` | Permit a release whose `workspaces` list is empty to disable every workspace. |
| `extraEnv`, `nodeSelector`, `tolerations`, `affinity`, `topologySpreadConstraints` | Usual scheduling and environment hooks. |
The demonstration mode is not available in the chart: it only runs on loopback.
+14
View File
@@ -0,0 +1,14 @@
# Values for `helm lint` and `helm template` in CI; not a deployment example.
publicUrl: https://meguri.example.internal
secretName: meguri-secrets
serviceAccount:
create: true
annotations:
iam.gke.io/gcp-service-account: meguri@project.iam.gserviceaccount.com
cloudSqlProxy:
enabled: true
instanceConnectionName: project:region:instance
ingress:
enabled: true
className: nginx
tlsSecretName: meguri-tls
+17
View File
@@ -0,0 +1,17 @@
Meguri {{ .Chart.AppVersion }} is released as {{ .Release.Name }}.
Open {{ .Values.publicUrl }} (register {{ .Values.publicUrl | trimSuffix "/" }}/api/oidc/callback as the redirect URI of the OIDC client).
Workspaces and group mappings come only from `instanceConfig`; the migrate Job applied them
before the Deployment was updated. See what it did:
kubectl -n {{ .Release.Namespace }} logs job/{{ include "meguri.fullname" . }}-migrate-{{ .Release.Revision }}
{{- if (.Values.instanceConfig.auth).bootstrap_admin }}
WARNING: bootstrap_admin is enabled. Its generated password is in that Job log, so anyone who can
read the log can sign in as an instance administrator. Remove the block when SSO works again.
{{- end }}
{{- if not .Values.instanceConfig.workspaces }}
No workspaces are configured.
{{- end }}
+84
View File
@@ -0,0 +1,84 @@
{{- define "meguri.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 40 | trimSuffix "-" -}}
{{- end -}}
{{- define "meguri.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 40 | trimSuffix "-" -}}
{{- else if contains .Chart.Name .Release.Name -}}
{{- .Release.Name | trunc 40 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 40 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- define "meguri.selectorLabels" -}}
app.kubernetes.io/name: {{ include "meguri.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{- define "meguri.labels" -}}
helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{ include "meguri.selectorLabels" . }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{- define "meguri.image" -}}
{{- printf "%s:%s" .Values.image.repository (default .Chart.AppVersion .Values.image.tag) -}}
{{- end -}}
{{- define "meguri.serviceAccountName" -}}
{{- if .Values.serviceAccount.create -}}
{{- default (include "meguri.fullname" .) .Values.serviceAccount.name -}}
{{- else -}}
{{- default "default" .Values.serviceAccount.name -}}
{{- end -}}
{{- end -}}
{{/* The configuration file exactly as the application reads it. */}}
{{- define "meguri.instanceConfig" -}}
{{- toYaml .Values.instanceConfig -}}
{{- end -}}
{{/* Fails the render early with a readable message. Outputs nothing. */}}
{{- define "meguri.requirements" -}}
{{- $_ := required "publicUrl is required: the exact browser origin, for example https://meguri.example.internal" .Values.publicUrl -}}
{{- $_ := required "secretName is required: a Secret with DATABASE_URL (and MEGURI_OIDC_CLIENT_SECRET when oidc is configured)" .Values.secretName -}}
{{- end -}}
{{- define "meguri.cloudSqlProxy" -}}
- name: cloud-sql-proxy
restartPolicy: Always
image: {{ .Values.cloudSqlProxy.image | quote }}
args:
{{- if .Values.cloudSqlProxy.privateIp }}
- --private-ip
{{- end }}
- --structured-logs
- --port={{ .Values.cloudSqlProxy.port }}
- --health-check
- --http-address=0.0.0.0
- --http-port=9090
- {{ required "cloudSqlProxy.instanceConnectionName is required when the proxy is enabled" .Values.cloudSqlProxy.instanceConnectionName | quote }}
securityContext:
allowPrivilegeEscalation: false
capabilities: {drop: [ALL]}
resources:
{{- toYaml .Values.cloudSqlProxy.resources | nindent 4 }}
startupProbe:
httpGet: {path: /startup, port: 9090}
periodSeconds: 2
failureThreshold: 30
{{- end -}}
{{/* Environment shared by the application and the migrate Job. */}}
{{- define "meguri.env" -}}
- {name: MEGURI_BIND, value: "0.0.0.0:3000"}
- {name: MEGURI_PUBLIC_URL, value: {{ .Values.publicUrl | trimSuffix "/" | quote }}}
- {name: MEGURI_CONFIG_FILE, value: /etc/meguri/config.yaml}
- {name: RUST_LOG, value: {{ .Values.logLevel | quote }}}
{{- with .Values.extraEnv }}
{{ toYaml . }}
{{- end }}
{{- end -}}
+25
View File
@@ -0,0 +1,25 @@
{{- include "meguri.requirements" . -}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "meguri.fullname" . }}-instance-config
labels:
{{- include "meguri.labels" . | nindent 4 }}
data:
config.yaml: |
{{- include "meguri.instanceConfig" . | nindent 4 }}
---
{{- /* The migrate Job runs before ordinary resources are updated, so it reads its own copy. */}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "meguri.fullname" . }}-instance-config-migrate
labels:
{{- include "meguri.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "-10"
helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded
data:
config.yaml: |
{{- include "meguri.instanceConfig" . | nindent 4 }}
+89
View File
@@ -0,0 +1,89 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "meguri.fullname" . }}
labels:
{{- include "meguri.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "meguri.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: app
template:
metadata:
labels:
{{- include "meguri.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: app
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
annotations:
checksum/instance-config: {{ include "meguri.instanceConfig" . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
serviceAccountName: {{ include "meguri.serviceAccountName" . }}
terminationGracePeriodSeconds: 60
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
runAsNonRoot: true
seccompProfile: {type: RuntimeDefault}
{{- if .Values.cloudSqlProxy.enabled }}
initContainers:
{{- include "meguri.cloudSqlProxy" . | nindent 8 }}
{{- end }}
volumes:
- name: instance-config
configMap:
name: {{ include "meguri.fullname" . }}-instance-config
containers:
- name: meguri
image: {{ include "meguri.image" . | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports: [{name: http, containerPort: 3000}]
envFrom:
- secretRef: {name: {{ required "secretName is required" .Values.secretName | quote }}}
{{- range .Values.extraSecretNames }}
- secretRef: {name: {{ . | quote }}}
{{- end }}
env:
{{- include "meguri.env" . | nindent 12 }}
- {name: MEGURI_ROLE, value: {{ .Values.role | quote }}}
- {name: MEGURI_SKIP_MIGRATIONS, value: "true"}
volumeMounts:
- {name: instance-config, mountPath: /etc/meguri, readOnly: true}
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities: {drop: [ALL]}
resources:
{{- toYaml .Values.resources | nindent 12 }}
startupProbe:
httpGet: {path: /health/ready, port: http}
periodSeconds: 2
failureThreshold: 60
readinessProbe:
httpGet: {path: /health/ready, port: http}
livenessProbe:
httpGet: {path: /health/live, port: http}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}
+32
View File
@@ -0,0 +1,32 @@
{{- if .Values.ingress.enabled }}
{{- $url := urlParse (required "publicUrl is required" .Values.publicUrl) }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "meguri.fullname" . }}
labels:
{{- include "meguri.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- with .Values.ingress.className }}
ingressClassName: {{ . }}
{{- end }}
{{- if .Values.ingress.tlsSecretName }}
tls:
- hosts: [{{ $url.hostname | quote }}]
secretName: {{ .Values.ingress.tlsSecretName }}
{{- end }}
rules:
- host: {{ $url.hostname | quote }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: {{ include "meguri.fullname" . }}
port: {name: http}
{{- end }}
+62
View File
@@ -0,0 +1,62 @@
apiVersion: batch/v1
kind: Job
metadata:
name: {{ include "meguri.fullname" . }}-migrate-{{ .Release.Revision }}
labels:
{{- include "meguri.labels" . | nindent 4 }}
app.kubernetes.io/component: migrate
annotations:
helm.sh/hook: pre-install,pre-upgrade
helm.sh/hook-weight: "0"
helm.sh/hook-delete-policy: before-hook-creation
spec:
backoffLimit: 0
activeDeadlineSeconds: {{ .Values.migrate.activeDeadlineSeconds }}
ttlSecondsAfterFinished: {{ .Values.migrate.ttlSecondsAfterFinished }}
template:
metadata:
labels:
{{- include "meguri.selectorLabels" . | nindent 8 }}
app.kubernetes.io/component: migrate
spec:
restartPolicy: Never
serviceAccountName: {{ include "meguri.serviceAccountName" . }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
securityContext:
runAsNonRoot: true
seccompProfile: {type: RuntimeDefault}
{{- if .Values.cloudSqlProxy.enabled }}
initContainers:
{{- include "meguri.cloudSqlProxy" . | nindent 8 }}
{{- end }}
volumes:
- name: instance-config
configMap:
name: {{ include "meguri.fullname" . }}-instance-config-migrate
containers:
- name: migrate
image: {{ include "meguri.image" . | quote }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
envFrom:
- secretRef: {name: {{ required "secretName is required" .Values.secretName | quote }}}
{{- range .Values.extraSecretNames }}
- secretRef: {name: {{ . | quote }}}
{{- end }}
env:
{{- include "meguri.env" . | nindent 12 }}
- {name: MEGURI_ROLE, value: migrate}
- {name: MEGURI_SKIP_MIGRATIONS, value: "false"}
{{- if .Values.allowDisableAll }}
- {name: MEGURI_ALLOW_DISABLE_ALL, value: "true"}
{{- end }}
volumeMounts:
- {name: instance-config, mountPath: /etc/meguri, readOnly: true}
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities: {drop: [ALL]}
resources:
{{- toYaml .Values.migrate.resources | nindent 12 }}
+14
View File
@@ -0,0 +1,14 @@
{{- if and .Values.podDisruptionBudget.enabled (gt (int .Values.replicaCount) 1) }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "meguri.fullname" . }}
labels:
{{- include "meguri.labels" . | nindent 4 }}
spec:
minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
selector:
matchLabels:
{{- include "meguri.selectorLabels" . | nindent 6 }}
app.kubernetes.io/component: app
{{- end }}
+13
View File
@@ -0,0 +1,13 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "meguri.fullname" . }}
labels:
{{- include "meguri.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
selector:
{{- include "meguri.selectorLabels" . | nindent 4 }}
app.kubernetes.io/component: app
ports:
- {name: http, port: {{ .Values.service.port }}, targetPort: http}
+16
View File
@@ -0,0 +1,16 @@
{{- if .Values.serviceAccount.create }}
{{- /* A hook, because the pre-install migrate Job needs it before ordinary resources exist. */}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "meguri.serviceAccountName" . }}
labels:
{{- include "meguri.labels" . | nindent 4 }}
annotations:
helm.sh/hook: pre-install
helm.sh/hook-weight: "-20"
helm.sh/hook-delete-policy: before-hook-creation
{{- with .Values.serviceAccount.annotations }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
+88
View File
@@ -0,0 +1,88 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["publicUrl", "secretName", "instanceConfig"],
"properties": {
"image": {
"type": "object",
"required": ["repository"],
"properties": {
"repository": {"type": "string", "minLength": 1},
"tag": {"type": "string"},
"pullPolicy": {"enum": ["Always", "IfNotPresent", "Never"]}
}
},
"publicUrl": {"type": "string", "pattern": "^https?://[^/\\s]+/?$"},
"secretName": {"type": "string", "minLength": 1},
"extraSecretNames": {"type": "array", "items": {"type": "string", "minLength": 1}},
"replicaCount": {"type": "integer", "minimum": 1},
"role": {"enum": ["all", "web", "worker"]},
"logLevel": {"type": "string"},
"allowDisableAll": {"type": "boolean"},
"cloudSqlProxy": {
"type": "object",
"properties": {
"enabled": {"type": "boolean"},
"instanceConnectionName": {"type": "string"},
"port": {"type": "integer", "minimum": 1, "maximum": 65535}
}
},
"instanceConfig": {
"type": "object",
"additionalProperties": false,
"properties": {
"auth": {
"type": "object",
"additionalProperties": false,
"properties": {
"password_login": {"type": "boolean"},
"bootstrap_admin": {
"type": "object",
"additionalProperties": false,
"required": ["usernames"],
"properties": {
"usernames": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {"type": "string", "pattern": "^[a-z0-9._-]{3,64}$"}
}
}
}
}
},
"oidc": {
"type": "object",
"additionalProperties": false,
"required": ["issuer", "client_id"],
"properties": {
"issuer": {"type": "string", "pattern": "^https://[^\\s]+$"},
"client_id": {"type": "string", "minLength": 1},
"scopes": {"type": "array", "items": {"type": "string"}, "contains": {"const": "openid"}},
"groups_claim": {"type": "string", "minLength": 1},
"groups_sources": {
"type": "array",
"minItems": 1,
"items": {"enum": ["id_token", "userinfo"]}
}
}
},
"instance_admin_groups": {"type": "array", "uniqueItems": true, "items": {"type": "string", "minLength": 1}},
"workspaces": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["key", "name", "admin_groups"],
"properties": {
"key": {"type": "string", "pattern": "^[a-z0-9]([a-z0-9-]{1,62})[a-z0-9]$"},
"name": {"type": "string", "minLength": 1, "maxLength": 120},
"admin_groups": {"type": "array", "minItems": 1, "uniqueItems": true, "items": {"type": "string", "minLength": 1}},
"member_groups": {"type": "array", "uniqueItems": true, "items": {"type": "string", "minLength": 1}}
}
}
}
}
}
}
}
+116
View File
@@ -0,0 +1,116 @@
# Meguri Helm chart. Secrets are never part of the chart: create the Secret named by
# `secretName` first (see README.md). Everything under `instanceConfig` is rendered verbatim into
# the instance configuration file; the chart schema and the application validate it strictly.
image:
repository: ultradesu/meguri
# Pin an immutable tag or digest for reproducible releases; `latest` is re-pulled on every restart.
tag: latest
pullPolicy: Always
imagePullSecrets: []
# Exact browser origin (scheme, host, port) of the service. Cookies, CSRF checks and the OIDC
# redirect URI ({publicUrl}/api/oidc/callback) depend on it. Required.
publicUrl: ""
# Name of a Secret holding at least DATABASE_URL (postgresql://USER:PERCENT_ENCODED_PASSWORD@HOST:5432/DB)
# and, when instanceConfig.oidc is set, MEGURI_OIDC_CLIENT_SECRET. All of its keys become environment
# variables, so per-tenant provider credentials (MEGURI_JSM_<TENANT>_EMAIL / _TOKEN) belong there too.
# Required.
secretName: ""
# Further Secrets whose keys are also injected as environment variables (for example a separate
# MEGURI_OIDC_CLIENT_SECRET kept apart from the database credentials).
extraSecretNames: []
replicaCount: 2
# all = web and publication worker in each replica; web = HTTP only (add a separate release
# with role worker if you split them).
role: all
logLevel: meguri=info,cot=warn
# Disables every existing workspace when instanceConfig.workspaces is empty. Leave false.
allowDisableAll: false
# The instance configuration. Written to /etc/meguri/config.yaml. The migrate hook applies it at
# every install and upgrade: workspaces are created, adopted, renamed, enabled or DISABLED (never
# deleted) so that the database matches this list. Field reference: deploy/instance-config.example.yaml.
instanceConfig:
auth:
# Lets ordinary password accounts (created by an instance administrator) sign in.
password_login: false
# Break-glass instance administrators. Remove in production. While present, their generated
# password is written to the migrate Job log.
# bootstrap_admin:
# usernames: [rescue]
oidc:
issuer: https://sso.example.internal/realms/corp
client_id: meguri
scopes: [openid, profile, email]
groups_claim: groups
groups_sources: [id_token, userinfo]
instance_admin_groups:
- /meguri/admins
workspaces:
- key: payments
name: Payments
admin_groups:
- /payments/leads
member_groups:
- /payments/team
migrate:
# Runs the Cot migrations and the configuration sync before the Deployment is touched.
activeDeadlineSeconds: 300
# Finished Jobs (and the log that may contain bootstrap passwords) are removed after this long.
ttlSecondsAfterFinished: 86400
resources:
requests: {cpu: 250m, memory: 128Mi}
limits: {memory: 256Mi}
resources:
requests: {cpu: 250m, memory: 256Mi}
limits: {memory: 512Mi}
# Optional Cloud SQL Auth Proxy as a native sidecar (Kubernetes 1.29+), for the application and
# the migrate Job. Point DATABASE_URL at 127.0.0.1:<port>.
cloudSqlProxy:
enabled: false
image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.14.1
instanceConnectionName: "" # PROJECT:REGION:INSTANCE
privateIp: true
port: 5432
resources:
requests: {cpu: 100m, memory: 128Mi}
limits: {memory: 256Mi}
serviceAccount:
# Created once, at install, because the migrate hook runs before ordinary resources exist.
# Later annotation changes need `kubectl annotate`. Leave create false to use an existing
# account named `name` (or the namespace default).
create: false
name: ""
annotations: {}
# iam.gke.io/gcp-service-account: meguri@PROJECT.iam.gserviceaccount.com
service:
type: ClusterIP
port: 80
ingress:
enabled: false
className: ""
annotations: {}
# Host and TLS follow publicUrl. Set tlsSecretName for an https publicUrl terminated at the ingress.
tlsSecretName: ""
podDisruptionBudget:
enabled: true
minAvailable: 1
podAnnotations: {}
podLabels: {}
extraEnv: []
nodeSelector: {}
tolerations: []
affinity: {}
topologySpreadConstraints: []