Update helm chart to detach integrations pod (#3204)

Depends on https://github.com/grafana/oncall/pull/3203

Related to https://github.com/grafana/oncall/issues/3162
This commit is contained in:
Matias Bordese 2023-11-03 09:34:22 -03:00 committed by GitHub
parent 68c1b2efba
commit 24357f5ff0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 548 additions and 29 deletions

View file

@ -0,0 +1,41 @@
import sys
from importlib import import_module, reload
from unittest.mock import patch
import pytest
from django.conf import settings
from django.urls import clear_url_caches
from rest_framework import status
from rest_framework.test import APIClient
def reload_urlconf():
clear_url_caches()
if settings.ROOT_URLCONF in sys.modules:
reload(sys.modules[settings.ROOT_URLCONF])
return import_module(settings.ROOT_URLCONF)
@pytest.mark.parametrize(
"detached_integrations,urlconf,is_cache_updated",
[
(False, None, True),
(True, None, False),
(True, "engine.integrations_urls", True),
],
)
def test_startupprobe_populates_integrations_cache(settings, detached_integrations, urlconf, is_cache_updated):
settings.DETACHED_INTEGRATIONS_SERVER = detached_integrations
if urlconf:
settings.ROOT_URLCONF = urlconf
reload_urlconf()
client = APIClient()
with patch(
"apps.integrations.mixins.AlertChannelDefiningMixin.update_alert_receive_channel_cache"
) as mock_update_cache:
response = client.get("/startupprobe/")
assert response.status_code == status.HTTP_200_OK
assert mock_update_cache.called == is_cache_updated

View file

@ -1,3 +1,4 @@
from django import urls
from django.conf import settings
from django.core.cache import cache
from django.http import HttpResponse, JsonResponse
@ -43,7 +44,13 @@ class StartupProbeView(View):
dangerously_bypass_middlewares = True
def get(self, request):
if cache.get(AlertChannelDefiningMixin.CACHE_KEY_DB_FALLBACK) is None:
# enable integrations cache if current engine instance is serving them
integrations_enabled = True
if settings.DETACHED_INTEGRATIONS_SERVER:
url_resolver = urls.get_resolver(urls.get_urlconf())
integrations_enabled = url_resolver.namespace_dict.get("integrations")
if integrations_enabled and cache.get(AlertChannelDefiningMixin.CACHE_KEY_DB_FALLBACK) is None:
AlertChannelDefiningMixin().update_alert_receive_channel_cache()
cache.set("healthcheck", "healthcheck", 30) # Checking cache connectivity

View file

@ -2,7 +2,8 @@
1. Create the cluster with [kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation)
> Make sure ports 30001 and 30002 are free on your machine
> Make sure ports 30001, 30002 (Grafana, optional) and
> 30003 (detached integrations server, optional) are free on your machine
```bash
kind create cluster --image kindest/node:v1.24.7 --config kind.yml
@ -10,12 +11,25 @@
2. (Optional) Build oncall image locally and load it to kind cluster
3. ```bash
docker build ../engine -t oncall/engine:latest --target dev
kind load docker-image oncall/engine:latest
```bash
docker build ../engine -t oncall/engine:latest --target dev
kind load docker-image oncall/engine:latest
```
4. Install the helm chart
Also make sure to add the following lines to your `simple.yml` (you may also need to enable `devMode`):
```yaml
image:
repository: oncall/engine
tag: latest
pullPolicy: IfNotPresent
oncall:
devMode: true
```
Alternatively you can also pass an extra `--values ./local_image.yml` in the command below.
3. Install the helm chart
```bash
helm install helm-testing \
@ -24,14 +38,14 @@
./oncall
```
5. Get credentials
4. Get credentials
```bash
echo "\n\nOpen Grafana on localhost:30002 with credentials - user: admin, password: $(kubectl get secret --namespace default helm-testing-grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo)"
echo "Open Plugins -> Grafana OnCall -> fill form: backend url: http://host.docker.internal:30001"
```
6. Clean up
5. Clean up
If you happen to `helm uninstall helm-testing` be sure to delete all the Persistent Volume Claims, as Postgres stores
the auto-generated password on disk, and the next `helm install` will fail.

View file

@ -8,6 +8,8 @@ nodes:
hostPort: 30001
- containerPort: 30002
hostPort: 30002
- containerPort: 30003
hostPort: 30003
# https://stackoverflow.com/a/62695918
extraMounts:
# this basically mounts our local ./grafana-plugin (frontend) directory into the kind node

View file

@ -19,6 +19,8 @@
value: "admin"
- name: OSS
value: "True"
- name: DETACHED_INTEGRATIONS_SERVER
value: {{ .Values.detached_integrations.enabled | toString | title | quote }}
{{- include "snippet.oncall.uwsgi" . }}
- name: BROKER_TYPE
value: {{ .Values.broker.type | default "rabbitmq" }}
@ -640,3 +642,15 @@ when broker.type != rabbitmq, we do not need to include rabbitmq environment var
value: {{ .Values.oncall.exporter.enabled | toString | title | quote }}
{{- end }}
{{- end }}
{{- define "snippet.oncall.engine.env" -}}
{{ include "snippet.oncall.env" . }}
{{ include "snippet.oncall.slack.env" . }}
{{ include "snippet.oncall.telegram.env" . }}
{{ include "snippet.oncall.smtp.env" . }}
{{ include "snippet.oncall.twilio.env" . }}
{{ include "snippet.oncall.exporter.env" . }}
{{ include "snippet.db.env" . }}
{{ include "snippet.broker.env" . }}
{{ include "oncall.extraEnvs" . }}
{{- end }}

View file

@ -85,6 +85,11 @@ Create the name of the service account to use
{{- printf "%s-%s" .Release.Name "redis" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/* Generate engine image name */}}
{{- define "oncall.engine.image" -}}
{{- printf "%s:%s" .Values.image.repository (.Values.image.tag | default .Chart.AppVersion) }}
{{- end }}
{{- define "oncall.initContainer" }}
- name: wait-for-db
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"

View file

@ -51,7 +51,7 @@ spec:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
image: {{ include "oncall.engine.image" . }}
{{- if .Values.oncall.devMode }}
command: ["python", "manage.py", "start_celery"]
{{- else }}
@ -60,14 +60,7 @@ spec:
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
{{- include "snippet.celery.env" . | nindent 12 }}
{{- include "snippet.oncall.env" . | nindent 12 }}
{{- include "snippet.oncall.slack.env" . | nindent 12 }}
{{- include "snippet.oncall.telegram.env" . | nindent 12 }}
{{- include "snippet.oncall.smtp.env" . | nindent 12 }}
{{- include "snippet.oncall.exporter.env" . | nindent 12 }}
{{- include "snippet.db.env" . | nindent 12 }}
{{- include "snippet.broker.env" . | nindent 12 }}
{{- include "oncall.extraEnvs" . | nindent 12 }}
{{- include "snippet.oncall.engine.env" . | nindent 12 }}
{{- if .Values.celery.livenessProbe.enabled }}
livenessProbe:
exec:

View file

@ -34,7 +34,7 @@ spec:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
image: {{ include "oncall.engine.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- if .Values.oncall.devMode }}
command: ["sh", "-c", "uwsgi --disable-logging --py-autoreload 3 --ini uwsgi.ini"]
@ -44,15 +44,7 @@ spec:
containerPort: 8080
protocol: TCP
env:
{{- include "snippet.oncall.env" . | nindent 12 }}
{{- include "snippet.oncall.slack.env" . | nindent 12 }}
{{- include "snippet.oncall.telegram.env" . | nindent 12 }}
{{- include "snippet.oncall.smtp.env" . | nindent 12 }}
{{- include "snippet.oncall.twilio.env" . | nindent 12 }}
{{- include "snippet.oncall.exporter.env" . | nindent 12 }}
{{- include "snippet.db.env" . | nindent 12 }}
{{- include "snippet.broker.env" . | nindent 12 }}
{{- include "oncall.extraEnvs" . | nindent 12 }}
{{- include "snippet.oncall.engine.env" . | nindent 12 }}
livenessProbe:
httpGet:
path: /health/

View file

@ -58,7 +58,7 @@ spec:
- name: {{ .Chart.Name }}-migrate
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
image: {{ include "oncall.engine.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command:
- /bin/sh

View file

@ -53,4 +53,13 @@ spec:
port:
number: 80
{{- end }}
{{ if .Values.detached_integrations.enabled }}
- path: /integrations
pathType: Prefix
backend:
service:
name: {{ include "oncall.detached_integrations.fullname" . }}
port:
number: 8080
{{- end }}
{{- end }}

View file

@ -0,0 +1,26 @@
{{/*
Maximum of 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
*/}}
{{- define "oncall.detached_integrations.name" -}}
{{ include "oncall.name" . | trunc 55 }}-integrations
{{- end }}
{{- define "oncall.detached_integrations.fullname" -}}
{{ include "oncall.fullname" . | trunc 55 }}-integrations
{{- end }}
{{/*
Integrations common labels
*/}}
{{- define "oncall.detached_integrations.labels" -}}
{{ include "oncall.labels" . }}
app.kubernetes.io/component: integrations
{{- end }}
{{/*
Integrations selector labels
*/}}
{{- define "oncall.detached_integrations.selectorLabels" -}}
{{ include "oncall.selectorLabels" . }}
app.kubernetes.io/component: integrations
{{- end }}

View file

@ -0,0 +1,99 @@
{{- if .Values.detached_integrations.enabled -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "oncall.detached_integrations.fullname" . }}
labels:
{{- include "oncall.detached_integrations.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.detached_integrations.replicaCount }}
selector:
matchLabels:
{{- include "oncall.detached_integrations.selectorLabels" . | nindent 6 }}
strategy:
{{- toYaml .Values.detached_integrations.updateStrategy | nindent 4 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
random-annotation: {{ randAlphaNum 10 | lower }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "oncall.detached_integrations.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "oncall.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
initContainers:
{{- include "oncall.initContainer" . | indent 8 }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: {{ include "oncall.engine.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- if .Values.oncall.devMode }}
command: ["sh", "-c", "uwsgi --disable-logging --py-autoreload 3 --ini uwsgi.ini"]
{{- end }}
ports:
- name: http
containerPort: 8080
protocol: TCP
env:
{{- include "snippet.oncall.engine.env" . | nindent 12 }}
- name: ROOT_URLCONF
value: "engine.integrations_urls"
livenessProbe:
httpGet:
path: /health/
port: http
periodSeconds: 60
timeoutSeconds: 3
readinessProbe:
httpGet:
path: /ready/
port: http
periodSeconds: 60
timeoutSeconds: 3
startupProbe:
httpGet:
path: /startupprobe/
port: http
periodSeconds: 10
timeoutSeconds: 3
resources:
{{- toYaml .Values.detached_integrations.resources | nindent 12 }}
{{- with .Values.detached_integrations.extraVolumeMounts }}
volumeMounts: {{- . | toYaml | nindent 12 }}
{{- end }}
{{- with .Values.detached_integrations.extraContainers }}
{{- tpl . $ | nindent 8 }}
{{- end }}
{{- with .Values.detached_integrations.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.detached_integrations.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.detached_integrations.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.detached_integrations.topologySpreadConstraints }}
topologySpreadConstraints:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.detached_integrations.priorityClassName }}
priorityClassName: {{ . }}
{{- end }}
{{- with .Values.detached_integrations.extraVolumes }}
volumes: {{- . | toYaml | nindent 8 }}
{{- end }}
{{- end -}}

View file

@ -0,0 +1,24 @@
{{- if .Values.detached_integrations_service.enabled }}
apiVersion: v1
kind: Service
metadata:
name: {{ include "oncall.detached_integrations.fullname" . }}-external
labels:
{{- include "oncall.detached_integrations.labels" . | nindent 4 }}
{{- with .Values.detached_integrations_service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.detached_integrations_service.type }}
ports:
- port: {{ .Values.detached_integrations_service.port }}
targetPort: http
protocol: TCP
name: http
{{- if and (eq .Values.detached_integrations_service.type "NodePort") (.Values.detached_integrations_service.nodePort) }}
nodePort: {{ .Values.detached_integrations_service.nodePort }}
{{- end }}
selector:
{{- include "oncall.detached_integrations.selectorLabels" . | nindent 4 }}
{{- end }}

View file

@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "oncall.detached_integrations.fullname" . }}
labels:
{{- include "oncall.detached_integrations.labels" . | nindent 4 }}
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: http
protocol: TCP
name: http
selector:
{{- include "oncall.detached_integrations.selectorLabels" . | nindent 4 }}

View file

@ -28,7 +28,7 @@ spec:
- name: telegram-polling
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
image: {{ include "oncall.engine.image" . }}
imagePullPolicy: {{ .Values.image.pullPolicy }}
command: ['sh', '-c', 'python manage.py start_telegram_polling']
env:

View file

@ -0,0 +1,129 @@
detached_integrations.enabled=true -> should create integrations deployment:
1: |
- env:
- name: BASE_URL
value: https://example.com
- name: SECRET_KEY
valueFrom:
secretKeyRef:
key: SECRET_KEY
name: oncall
- name: MIRAGE_SECRET_KEY
valueFrom:
secretKeyRef:
key: MIRAGE_SECRET_KEY
name: oncall
- name: MIRAGE_CIPHER_IV
value: 1234567890abcdef
- name: DJANGO_SETTINGS_MODULE
value: settings.helm
- name: AMIXR_DJANGO_ADMIN_PATH
value: admin
- name: OSS
value: "True"
- name: DETACHED_INTEGRATIONS_SERVER
value: "True"
- name: UWSGI_LISTEN
value: "1024"
- name: BROKER_TYPE
value: rabbitmq
- name: GRAFANA_API_URL
value: http://oncall-grafana
- name: FEATURE_SLACK_INTEGRATION_ENABLED
value: "False"
- name: FEATURE_TELEGRAM_INTEGRATION_ENABLED
value: "False"
- name: FEATURE_EMAIL_INTEGRATION_ENABLED
value: "True"
- name: EMAIL_HOST
value: null
- name: EMAIL_PORT
value: "587"
- name: EMAIL_HOST_USER
value: null
- name: EMAIL_HOST_PASSWORD
valueFrom:
secretKeyRef:
key: smtp-password
name: oncall-smtp
optional: true
- name: EMAIL_USE_TLS
value: "True"
- name: EMAIL_FROM_ADDRESS
value: null
- name: EMAIL_NOTIFICATIONS_LIMIT
value: "200"
- name: FEATURE_PROMETHEUS_EXPORTER_ENABLED
value: "False"
- name: MYSQL_HOST
value: oncall-mariadb
- name: MYSQL_PORT
value: "3306"
- name: MYSQL_DB_NAME
value: oncall
- name: MYSQL_USER
value: root
- name: MYSQL_PASSWORD
valueFrom:
secretKeyRef:
key: mariadb-root-password
name: oncall-mariadb
- name: REDIS_PROTOCOL
value: redis
- name: REDIS_HOST
value: oncall-redis-master
- name: REDIS_PORT
value: "6379"
- name: REDIS_DATABASE
value: "0"
- name: REDIS_USERNAME
value: ""
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
key: redis-password
name: oncall-redis
- name: RABBITMQ_USERNAME
value: user
- name: RABBITMQ_PASSWORD
valueFrom:
secretKeyRef:
key: rabbitmq-password
name: oncall-rabbitmq
- name: RABBITMQ_HOST
value: oncall-rabbitmq
- name: RABBITMQ_PORT
value: "5672"
- name: RABBITMQ_PROTOCOL
value: amqp
- name: RABBITMQ_VHOST
value: ""
- name: ROOT_URLCONF
value: engine.integrations_urls
image: grafana/oncall:v1.3.39
imagePullPolicy: Always
livenessProbe:
httpGet:
path: /health/
port: http
periodSeconds: 60
timeoutSeconds: 3
name: oncall
ports:
- containerPort: 8080
name: http
protocol: TCP
readinessProbe:
httpGet:
path: /ready/
port: http
periodSeconds: 60
timeoutSeconds: 3
resources: {}
securityContext: {}
startupProbe:
httpGet:
path: /startupprobe/
port: http
periodSeconds: 10
timeoutSeconds: 3

View file

@ -25,6 +25,8 @@ telegramPolling.enabled=true -> should create telegram polling deployment:
value: admin
- name: OSS
value: "True"
- name: DETACHED_INTEGRATIONS_SERVER
value: "False"
- name: UWSGI_LISTEN
value: "1024"
- name: BROKER_TYPE

View file

@ -25,6 +25,8 @@ database.type=mysql -> should create initContainer for MySQL database (default):
value: admin
- name: OSS
value: "True"
- name: DETACHED_INTEGRATIONS_SERVER
value: "False"
- name: UWSGI_LISTEN
value: "1024"
- name: BROKER_TYPE
@ -111,6 +113,8 @@ database.type=mysql -> should create initContainer for MySQL database (default):
value: admin
- name: OSS
value: "True"
- name: DETACHED_INTEGRATIONS_SERVER
value: "False"
- name: UWSGI_LISTEN
value: "1024"
- name: BROKER_TYPE
@ -197,6 +201,8 @@ database.type=mysql -> should create initContainer for MySQL database (default):
value: admin
- name: OSS
value: "True"
- name: DETACHED_INTEGRATIONS_SERVER
value: "False"
- name: UWSGI_LISTEN
value: "1024"
- name: BROKER_TYPE
@ -284,6 +290,8 @@ database.type=postgresql -> should create initContainer for PostgreSQL database:
value: admin
- name: OSS
value: "True"
- name: DETACHED_INTEGRATIONS_SERVER
value: "False"
- name: UWSGI_LISTEN
value: "1024"
- name: BROKER_TYPE
@ -372,6 +380,8 @@ database.type=postgresql -> should create initContainer for PostgreSQL database:
value: admin
- name: OSS
value: "True"
- name: DETACHED_INTEGRATIONS_SERVER
value: "False"
- name: UWSGI_LISTEN
value: "1024"
- name: BROKER_TYPE
@ -460,6 +470,8 @@ database.type=postgresql -> should create initContainer for PostgreSQL database:
value: admin
- name: OSS
value: "True"
- name: DETACHED_INTEGRATIONS_SERVER
value: "False"
- name: UWSGI_LISTEN
value: "1024"
- name: BROKER_TYPE

View file

@ -0,0 +1,52 @@
suite: test integrations deployment
templates:
- integrations/deployment.yaml
release:
name: oncall
chart:
appVersion: v1.3.39
tests:
- it: detached_integrations.enabled=false -> should not create deployment (default)
asserts:
- hasDocuments:
count: 0
- it: detached_integrations.enabled=true -> should create integrations deployment
set:
detached_integrations.enabled: true
asserts:
- containsDocument:
kind: Deployment
apiVersion: apps/v1
metadata.name: oncall-integrations
- isSubset:
path: metadata.labels
content:
app.kubernetes.io/component: integrations
app.kubernetes.io/instance: oncall
app.kubernetes.io/name: oncall
- isSubset:
path: spec.selector.matchLabels
content:
app.kubernetes.io/component: integrations
app.kubernetes.io/instance: oncall
app.kubernetes.io/name: oncall
- isSubset:
path: spec.template.metadata.labels
content:
app.kubernetes.io/component: integrations
app.kubernetes.io/instance: oncall
app.kubernetes.io/name: oncall
- equal:
path: spec.replicas
value: 1
- equal:
path: spec.template.spec.serviceAccountName
value: oncall
- contains:
path: spec.template.spec.initContainers
content:
name: wait-for-db
any: true
- matchSnapshot:
path: spec.template.spec.containers

View file

@ -95,6 +95,82 @@ engine:
# - mountPath: /mnt/redis-tls
# name: redis-tls
detached_integrations_service:
enabled: false
type: LoadBalancer
port: 8080
annotations: {}
# Integrations pods configuration
detached_integrations:
enabled: false
replicaCount: 1
resources:
{}
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
## Deployment update strategy
## ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy
updateStrategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
type: RollingUpdate
## Affinity for pod assignment
## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
affinity: {}
## Node labels for pod assignment
## ref: https://kubernetes.io/docs/user-guide/node-selection/
nodeSelector: {}
## Tolerations for pod assignment
## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/
tolerations: []
## Topology spread constraints for pod assignment
## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/
topologySpreadConstraints: []
## Priority class for the pods
## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/
priorityClassName: ""
# Extra containers which runs as sidecar
extraContainers: ""
# extraContainers: |
# - name: cloud-sql-proxy
# image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.1.2
# args:
# - --private-ip
# - --port=5432
# - example:europe-west3:grafana-oncall-db
# Extra volume mounts for the container
extraVolumeMounts: []
# - name: postgres-tls
# configMap:
# name: my-postgres-tls
# defaultMode: 0640
# - name: redis-tls
# configMap:
# name: my-redis-tls
# defaultMode: 0640
# Extra volumes for the pod
extraVolumes: []
# - mountPath: /mnt/postgres-tls
# name: postgres-tls
# - mountPath: /mnt/redis-tls
# name: redis-tls
# Celery workers pods configuration
celery:
replicaCount: 1

View file

@ -14,6 +14,13 @@ grafana:
service:
type: NodePort
nodePort: 30002
detached_integrations:
enabled: true
detached_integrations_service:
enabled: true
type: NodePort
port: 8080
nodePort: 30003
database:
# can be either mysql or postgresql
type: postgresql