From 26e2dcf992a1d60860aab97c725f52f0cf2c3d59 Mon Sep 17 00:00:00 2001
From: Sheogorath <sheogorath@shivering-isles.com>
Date: Fri, 15 Sep 2023 20:30:32 +0200
Subject: [PATCH] feat(mastodon): Rework redis integration and add
 PodDisruptionBudgets

This patch adds PDBs for all main deployments with a hardcoded
maxUnavailable 1. This should help to make sure that updates go smooth
and without major downtimes, as soon as at least 2 replicas are running,
while still not blocking cluster updates.

Furhter it makes the redis dependency optional, helping to replace the
integrated redis with a separate or external redis service.
---
 charts/mastodon/Chart.yaml                    |  3 +-
 charts/mastodon/templates/configmap-env.yaml  | 12 ++++
 .../templates/cronjob-media-remove.yaml       |  2 +-
 .../templates/deployment-sidekiq.yaml         |  2 +-
 .../templates/deployment-streaming.yaml       |  3 +-
 charts/mastodon/templates/deployment-web.yaml |  2 +-
 .../templates/job-assets-precompile.yaml      |  2 +-
 .../mastodon/templates/job-chewy-upgrade.yaml |  2 +-
 .../mastodon/templates/job-create-admin.yaml  |  2 +-
 charts/mastodon/templates/job-db-migrate.yaml |  2 +-
 .../templates/job-db-pre-migrate.yaml         |  2 +-
 charts/mastodon/templates/pdb-sidekiq.yaml    | 19 +++++
 charts/mastodon/templates/pdb-streaming.yaml  | 13 ++++
 charts/mastodon/templates/pdb-web.yaml        | 15 ++++
 charts/mastodon/tests/50_sidekiq_test.yaml    |  1 +
 charts/mastodon/tests/50_web_test.yaml        |  1 +
 charts/mastodon/tests/98_snapshot_test.yaml   |  3 +
 .../__snapshot__/50_sidekiq_test.yaml.snap    | 21 ++++++
 .../tests/__snapshot__/50_web_test.yaml.snap  | 23 +++++-
 .../__snapshot__/98_snapshot_test.yaml.snap   | 72 +++++++++++++++++--
 charts/mastodon/values.yaml                   | 12 +++-
 21 files changed, 197 insertions(+), 17 deletions(-)
 create mode 100644 charts/mastodon/templates/pdb-sidekiq.yaml
 create mode 100644 charts/mastodon/templates/pdb-streaming.yaml
 create mode 100644 charts/mastodon/templates/pdb-web.yaml

diff --git a/charts/mastodon/Chart.yaml b/charts/mastodon/Chart.yaml
index 5f8102f57..542f565e8 100644
--- a/charts/mastodon/Chart.yaml
+++ b/charts/mastodon/Chart.yaml
@@ -11,7 +11,7 @@ sources:
     - https://git.shivering-isles.com/shivering-isles/infrastructure-gitops/-/tree/main/charts/mastodon
 
 type: application
-version: 6.1.0
+version: 6.2.0
 # renovate: image=ghcr.io/mastodon/mastodon
 appVersion: "v4.1.7"
 
@@ -27,3 +27,4 @@ dependencies:
   - name: redis
     version: 18.0.2
     repository: https://raw.githubusercontent.com/bitnami/charts/archive-full-index/bitnami
+    condition: redis.enabled
diff --git a/charts/mastodon/templates/configmap-env.yaml b/charts/mastodon/templates/configmap-env.yaml
index 36750daae..dd1eedd04 100644
--- a/charts/mastodon/templates/configmap-env.yaml
+++ b/charts/mastodon/templates/configmap-env.yaml
@@ -39,8 +39,20 @@ data:
   MALLOC_ARENA_MAX: "2"
   NODE_ENV: "production"
   RAILS_ENV: "production"
+  {{- if .Values.redis.enabled }}
   REDIS_HOST: {{ template "mastodon.redis.fullname" . }}-master
   REDIS_PORT: "6379"
+  {{- else }}
+  {{ with .Values.redis.host }}
+  REDIS_HOST: {{ . }}
+  {{- end }}
+  {{ with .Values.redis.port }}
+  REDIS_PORT: {{ . | quote }}
+  {{- end }}
+  {{ with .Values.redis.redisUrl }}
+  REDIS_URL: {{ . | quote }}
+  {{- end }}
+  {{- end }}
   {{- if .Values.mastodon.s3.enabled }}
   S3_BUCKET: {{ .Values.mastodon.s3.bucket }}
   S3_ENABLED: "true"
diff --git a/charts/mastodon/templates/cronjob-media-remove.yaml b/charts/mastodon/templates/cronjob-media-remove.yaml
index 41f1feb82..f0cf4b8c7 100644
--- a/charts/mastodon/templates/cronjob-media-remove.yaml
+++ b/charts/mastodon/templates/cronjob-media-remove.yaml
@@ -64,7 +64,7 @@ spec:
                   valueFrom:
                     secretKeyRef:
                       name: {{ template "mastodon.redis.secretName" . }}
-                      key: redis-password
+                      key: {{ .Values.redis.auth.existingSecretPasswordKey | default "redis-password" }}
                 - name: "PORT"
                   value: {{ .Values.mastodon.web.port | quote }}
               {{- if (not .Values.mastodon.s3.enabled) }}
diff --git a/charts/mastodon/templates/deployment-sidekiq.yaml b/charts/mastodon/templates/deployment-sidekiq.yaml
index 47905321b..14abed897 100644
--- a/charts/mastodon/templates/deployment-sidekiq.yaml
+++ b/charts/mastodon/templates/deployment-sidekiq.yaml
@@ -86,7 +86,7 @@ spec:
               valueFrom:
                 secretKeyRef:
                   name: {{ template "mastodon.redis.secretName" $context }}
-                  key: redis-password
+                  key: {{ $context.Values.redis.auth.existingSecretPasswordKey | default "redis-password" }}
             {{- if (and $context.Values.mastodon.s3.enabled $context.Values.mastodon.s3.existingSecret) }}
             - name: "AWS_SECRET_ACCESS_KEY"
               valueFrom:
diff --git a/charts/mastodon/templates/deployment-streaming.yaml b/charts/mastodon/templates/deployment-streaming.yaml
index bec303cf9..8e90ac815 100644
--- a/charts/mastodon/templates/deployment-streaming.yaml
+++ b/charts/mastodon/templates/deployment-streaming.yaml
@@ -4,6 +4,7 @@ metadata:
   name: {{ include "mastodon.fullname" . }}-streaming
   labels:
     {{- include "mastodon.labels" . | nindent 4 }}
+    app.kubernetes.io/component: streaming
 spec:
   replicas: {{ .Values.mastodon.streaming.replicas }}
   selector:
@@ -57,7 +58,7 @@ spec:
               valueFrom:
                 secretKeyRef:
                   name: {{ template "mastodon.redis.secretName" . }}
-                  key: redis-password
+                  key: {{ .Values.redis.auth.existingSecretPasswordKey | default "redis-password" }}
             - name: "PORT"
               value: {{ .Values.mastodon.streaming.port | quote }}
           ports:
diff --git a/charts/mastodon/templates/deployment-web.yaml b/charts/mastodon/templates/deployment-web.yaml
index c1ec2327e..abfec6871 100644
--- a/charts/mastodon/templates/deployment-web.yaml
+++ b/charts/mastodon/templates/deployment-web.yaml
@@ -71,7 +71,7 @@ spec:
               valueFrom:
                 secretKeyRef:
                   name: {{ template "mastodon.redis.secretName" . }}
-                  key: redis-password
+                  key: {{ .Values.redis.auth.existingSecretPasswordKey | default "redis-password" }}
             - name: "PORT"
               value: {{ .Values.mastodon.web.port | quote }}
             {{- if (and .Values.mastodon.s3.enabled .Values.mastodon.s3.existingSecret) }}
diff --git a/charts/mastodon/templates/job-assets-precompile.yaml b/charts/mastodon/templates/job-assets-precompile.yaml
index 95019629c..8f946685a 100644
--- a/charts/mastodon/templates/job-assets-precompile.yaml
+++ b/charts/mastodon/templates/job-assets-precompile.yaml
@@ -55,7 +55,7 @@ spec:
               valueFrom:
                 secretKeyRef:
                   name: {{ template "mastodon.redis.secretName" . }}
-                  key: redis-password
+                  key: {{ .Values.redis.auth.existingSecretPasswordKey | default "redis-password" }}
             - name: "PORT"
               value: {{ .Values.mastodon.web.port | quote }}
           {{- if (not .Values.mastodon.s3.enabled) }}
diff --git a/charts/mastodon/templates/job-chewy-upgrade.yaml b/charts/mastodon/templates/job-chewy-upgrade.yaml
index 1ecb00518..27b8fc29f 100644
--- a/charts/mastodon/templates/job-chewy-upgrade.yaml
+++ b/charts/mastodon/templates/job-chewy-upgrade.yaml
@@ -56,7 +56,7 @@ spec:
               valueFrom:
                 secretKeyRef:
                   name: {{ template "mastodon.redis.secretName" . }}
-                  key: redis-password
+                  key: {{ .Values.redis.auth.existingSecretPasswordKey | default "redis-password" }}
             - name: "PORT"
               value: {{ .Values.mastodon.web.port | quote }}
           {{- if (not .Values.mastodon.s3.enabled) }}
diff --git a/charts/mastodon/templates/job-create-admin.yaml b/charts/mastodon/templates/job-create-admin.yaml
index 2338e3738..86512c7e1 100644
--- a/charts/mastodon/templates/job-create-admin.yaml
+++ b/charts/mastodon/templates/job-create-admin.yaml
@@ -61,7 +61,7 @@ spec:
               valueFrom:
                 secretKeyRef:
                   name: {{ template "mastodon.redis.secretName" . }}
-                  key: redis-password
+                  key: {{ .Values.redis.auth.existingSecretPasswordKey | default "redis-password" }}
             - name: "PORT"
               value: {{ .Values.mastodon.web.port | quote }}
           {{- if (not .Values.mastodon.s3.enabled) }}
diff --git a/charts/mastodon/templates/job-db-migrate.yaml b/charts/mastodon/templates/job-db-migrate.yaml
index 1f4c60a0d..51949f973 100644
--- a/charts/mastodon/templates/job-db-migrate.yaml
+++ b/charts/mastodon/templates/job-db-migrate.yaml
@@ -55,7 +55,7 @@ spec:
               valueFrom:
                 secretKeyRef:
                   name: {{ template "mastodon.redis.secretName" . }}
-                  key: redis-password
+                  key: {{ .Values.redis.auth.existingSecretPasswordKey | default "redis-password" }}
             - name: "PORT"
               value: {{ .Values.mastodon.web.port | quote }}
           {{- if (not .Values.mastodon.s3.enabled) }}
diff --git a/charts/mastodon/templates/job-db-pre-migrate.yaml b/charts/mastodon/templates/job-db-pre-migrate.yaml
index f5dfa1c76..0129091f8 100644
--- a/charts/mastodon/templates/job-db-pre-migrate.yaml
+++ b/charts/mastodon/templates/job-db-pre-migrate.yaml
@@ -55,7 +55,7 @@ spec:
               valueFrom:
                 secretKeyRef:
                   name: {{ template "mastodon.redis.secretName" . }}
-                  key: redis-password
+                  key: {{ .Values.redis.auth.existingSecretPasswordKey | default "redis-password" }}
             - name: "PORT"
               value: {{ .Values.mastodon.web.port | quote }}
             - name: SKIP_POST_DEPLOYMENT_MIGRATIONS
diff --git a/charts/mastodon/templates/pdb-sidekiq.yaml b/charts/mastodon/templates/pdb-sidekiq.yaml
new file mode 100644
index 000000000..358ff6cac
--- /dev/null
+++ b/charts/mastodon/templates/pdb-sidekiq.yaml
@@ -0,0 +1,19 @@
+{{- $context := . }}
+{{- range .Values.mastodon.sidekiq.workers }}
+---
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+  name: {{ include "mastodon.fullname" $context }}-sidekiq-{{ .name }}
+  labels:
+    {{- include "mastodon.labels" $context | nindent 4 }}
+    app.kubernetes.io/component: sidekiq-{{ .name }}
+    app.kubernetes.io/part-of: rails
+spec:
+  selector:
+    matchLabels:
+      {{- include "mastodon.selectorLabels" $context | nindent 6 }}
+      app.kubernetes.io/component: sidekiq-{{ .name }}
+      app.kubernetes.io/part-of: rails
+  maxUnavailable: 1
+{{- end }}
\ No newline at end of file
diff --git a/charts/mastodon/templates/pdb-streaming.yaml b/charts/mastodon/templates/pdb-streaming.yaml
new file mode 100644
index 000000000..4132d3e95
--- /dev/null
+++ b/charts/mastodon/templates/pdb-streaming.yaml
@@ -0,0 +1,13 @@
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+  name: {{ include "mastodon.fullname" . }}-streaming
+  labels:
+    {{- include "mastodon.labels" . | nindent 4 }}
+    app.kubernetes.io/component: streaming
+spec:
+  selector:
+    matchLabels:
+      {{- include "mastodon.selectorLabels" . | nindent 6 }}
+      app.kubernetes.io/component: streaming
+  maxUnavailable: 1
\ No newline at end of file
diff --git a/charts/mastodon/templates/pdb-web.yaml b/charts/mastodon/templates/pdb-web.yaml
new file mode 100644
index 000000000..5586f503e
--- /dev/null
+++ b/charts/mastodon/templates/pdb-web.yaml
@@ -0,0 +1,15 @@
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+  name: {{ include "mastodon.fullname" . }}-web
+  labels:
+    {{- include "mastodon.labels" . | nindent 4 }}
+    app.kubernetes.io/component: web
+    app.kubernetes.io/part-of: rails
+spec:
+  selector:
+    matchLabels:
+      {{- include "mastodon.selectorLabels" . | nindent 6 }}
+      app.kubernetes.io/component: web
+      app.kubernetes.io/part-of: rails
+  maxUnavailable: 1
\ No newline at end of file
diff --git a/charts/mastodon/tests/50_sidekiq_test.yaml b/charts/mastodon/tests/50_sidekiq_test.yaml
index 37c7a6abb..4c5bbf666 100644
--- a/charts/mastodon/tests/50_sidekiq_test.yaml
+++ b/charts/mastodon/tests/50_sidekiq_test.yaml
@@ -2,6 +2,7 @@ suite: sidekiq
 templates:
   - configmap-env.yaml
   - deployment-sidekiq.yaml
+  - pdb-sidekiq.yaml
   - secrets.yaml
 tests:
   - it: should match basic snapshot
diff --git a/charts/mastodon/tests/50_web_test.yaml b/charts/mastodon/tests/50_web_test.yaml
index 9d17ba46d..9d6ea0244 100644
--- a/charts/mastodon/tests/50_web_test.yaml
+++ b/charts/mastodon/tests/50_web_test.yaml
@@ -3,6 +3,7 @@ templates:
   - configmap-env.yaml
   - deployment-web.yaml
   - ingress.yaml
+  - pdb-web.yaml
   - secrets.yaml
   - service-web.yaml
 tests:
diff --git a/charts/mastodon/tests/98_snapshot_test.yaml b/charts/mastodon/tests/98_snapshot_test.yaml
index 87290ef40..44c3a2fbc 100644
--- a/charts/mastodon/tests/98_snapshot_test.yaml
+++ b/charts/mastodon/tests/98_snapshot_test.yaml
@@ -11,6 +11,9 @@ templates:
   - job-create-admin.yaml
   - job-db-migrate.yaml
   - job-db-pre-migrate.yaml
+  - pdb-sidekiq.yaml
+  - pdb-streaming.yaml
+  - pdb-web.yaml
   - pvc-assets.yaml
   - pvc-system.yaml
   - secrets.yaml
diff --git a/charts/mastodon/tests/__snapshot__/50_sidekiq_test.yaml.snap b/charts/mastodon/tests/__snapshot__/50_sidekiq_test.yaml.snap
index aae912064..f119198bf 100644
--- a/charts/mastodon/tests/__snapshot__/50_sidekiq_test.yaml.snap
+++ b/charts/mastodon/tests/__snapshot__/50_sidekiq_test.yaml.snap
@@ -132,6 +132,27 @@ should match basic snapshot:
               persistentVolumeClaim:
                 claimName: RELEASE-NAME-mastodon-system
   3: |
+    apiVersion: policy/v1
+    kind: PodDisruptionBudget
+    metadata:
+      labels:
+        app.kubernetes.io/component: sidekiq-all-queues
+        app.kubernetes.io/instance: RELEASE-NAME
+        app.kubernetes.io/managed-by: Helm
+        app.kubernetes.io/name: mastodon
+        app.kubernetes.io/part-of: rails
+        app.kubernetes.io/version: 4.5.6
+        helm.sh/chart: mastodon-1.2.3
+      name: RELEASE-NAME-mastodon-sidekiq-all-queues
+    spec:
+      maxUnavailable: 1
+      selector:
+        matchLabels:
+          app.kubernetes.io/component: sidekiq-all-queues
+          app.kubernetes.io/instance: RELEASE-NAME
+          app.kubernetes.io/name: mastodon
+          app.kubernetes.io/part-of: rails
+  4: |
     apiVersion: v1
     data:
       OTP_SECRET: ZHVtbXktb3RwX3NlY3JldA==
diff --git a/charts/mastodon/tests/__snapshot__/50_web_test.yaml.snap b/charts/mastodon/tests/__snapshot__/50_web_test.yaml.snap
index 9cdc3e704..8d0d15233 100644
--- a/charts/mastodon/tests/__snapshot__/50_web_test.yaml.snap
+++ b/charts/mastodon/tests/__snapshot__/50_web_test.yaml.snap
@@ -170,6 +170,27 @@ should match basic snapshot:
             - mastodon.local
           secretName: mastodon-tls
   4: |
+    apiVersion: policy/v1
+    kind: PodDisruptionBudget
+    metadata:
+      labels:
+        app.kubernetes.io/component: web
+        app.kubernetes.io/instance: RELEASE-NAME
+        app.kubernetes.io/managed-by: Helm
+        app.kubernetes.io/name: mastodon
+        app.kubernetes.io/part-of: rails
+        app.kubernetes.io/version: 4.5.6
+        helm.sh/chart: mastodon-1.2.3
+      name: RELEASE-NAME-mastodon-web
+    spec:
+      maxUnavailable: 1
+      selector:
+        matchLabels:
+          app.kubernetes.io/component: web
+          app.kubernetes.io/instance: RELEASE-NAME
+          app.kubernetes.io/name: mastodon
+          app.kubernetes.io/part-of: rails
+  5: |
     apiVersion: v1
     data:
       OTP_SECRET: ZHVtbXktb3RwX3NlY3JldA==
@@ -186,7 +207,7 @@ should match basic snapshot:
         helm.sh/chart: mastodon-1.2.3
       name: RELEASE-NAME-mastodon
     type: Opaque
-  5: |
+  6: |
     apiVersion: v1
     kind: Service
     metadata:
diff --git a/charts/mastodon/tests/__snapshot__/98_snapshot_test.yaml.snap b/charts/mastodon/tests/__snapshot__/98_snapshot_test.yaml.snap
index 185f24a63..6e1337b21 100644
--- a/charts/mastodon/tests/__snapshot__/98_snapshot_test.yaml.snap
+++ b/charts/mastodon/tests/__snapshot__/98_snapshot_test.yaml.snap
@@ -204,6 +204,7 @@ should match basic snapshot:
     kind: Deployment
     metadata:
       labels:
+        app.kubernetes.io/component: streaming
         app.kubernetes.io/instance: RELEASE-NAME
         app.kubernetes.io/managed-by: Helm
         app.kubernetes.io/name: mastodon
@@ -714,6 +715,67 @@ should match basic snapshot:
               persistentVolumeClaim:
                 claimName: RELEASE-NAME-mastodon-system
   12: |
+    apiVersion: policy/v1
+    kind: PodDisruptionBudget
+    metadata:
+      labels:
+        app.kubernetes.io/component: sidekiq-all-queues
+        app.kubernetes.io/instance: RELEASE-NAME
+        app.kubernetes.io/managed-by: Helm
+        app.kubernetes.io/name: mastodon
+        app.kubernetes.io/part-of: rails
+        app.kubernetes.io/version: 4.5.6
+        helm.sh/chart: mastodon-1.2.3
+      name: RELEASE-NAME-mastodon-sidekiq-all-queues
+    spec:
+      maxUnavailable: 1
+      selector:
+        matchLabels:
+          app.kubernetes.io/component: sidekiq-all-queues
+          app.kubernetes.io/instance: RELEASE-NAME
+          app.kubernetes.io/name: mastodon
+          app.kubernetes.io/part-of: rails
+  13: |
+    apiVersion: policy/v1
+    kind: PodDisruptionBudget
+    metadata:
+      labels:
+        app.kubernetes.io/component: streaming
+        app.kubernetes.io/instance: RELEASE-NAME
+        app.kubernetes.io/managed-by: Helm
+        app.kubernetes.io/name: mastodon
+        app.kubernetes.io/version: 4.5.6
+        helm.sh/chart: mastodon-1.2.3
+      name: RELEASE-NAME-mastodon-streaming
+    spec:
+      maxUnavailable: 1
+      selector:
+        matchLabels:
+          app.kubernetes.io/component: streaming
+          app.kubernetes.io/instance: RELEASE-NAME
+          app.kubernetes.io/name: mastodon
+  14: |
+    apiVersion: policy/v1
+    kind: PodDisruptionBudget
+    metadata:
+      labels:
+        app.kubernetes.io/component: web
+        app.kubernetes.io/instance: RELEASE-NAME
+        app.kubernetes.io/managed-by: Helm
+        app.kubernetes.io/name: mastodon
+        app.kubernetes.io/part-of: rails
+        app.kubernetes.io/version: 4.5.6
+        helm.sh/chart: mastodon-1.2.3
+      name: RELEASE-NAME-mastodon-web
+    spec:
+      maxUnavailable: 1
+      selector:
+        matchLabels:
+          app.kubernetes.io/component: web
+          app.kubernetes.io/instance: RELEASE-NAME
+          app.kubernetes.io/name: mastodon
+          app.kubernetes.io/part-of: rails
+  15: |
     apiVersion: v1
     kind: PersistentVolumeClaim
     metadata:
@@ -731,7 +793,7 @@ should match basic snapshot:
         requests:
           storage: 10Gi
       storageClassName: null
-  13: |
+  16: |
     apiVersion: v1
     kind: PersistentVolumeClaim
     metadata:
@@ -749,7 +811,7 @@ should match basic snapshot:
         requests:
           storage: 100Gi
       storageClassName: null
-  14: |
+  17: |
     apiVersion: v1
     data:
       OTP_SECRET: ZHVtbXktb3RwX3NlY3JldA==
@@ -766,7 +828,7 @@ should match basic snapshot:
         helm.sh/chart: mastodon-1.2.3
       name: RELEASE-NAME-mastodon
     type: Opaque
-  15: |
+  18: |
     apiVersion: v1
     kind: ServiceAccount
     metadata:
@@ -777,7 +839,7 @@ should match basic snapshot:
         app.kubernetes.io/version: 4.5.6
         helm.sh/chart: mastodon-1.2.3
       name: RELEASE-NAME-mastodon
-  16: |
+  19: |
     apiVersion: v1
     kind: Service
     metadata:
@@ -799,7 +861,7 @@ should match basic snapshot:
         app.kubernetes.io/instance: RELEASE-NAME
         app.kubernetes.io/name: mastodon
       type: ClusterIP
-  17: |
+  20: |
     apiVersion: v1
     kind: Service
     metadata:
diff --git a/charts/mastodon/values.yaml b/charts/mastodon/values.yaml
index cf075d243..2bf3c3104 100644
--- a/charts/mastodon/values.yaml
+++ b/charts/mastodon/values.yaml
@@ -264,13 +264,23 @@ postgresql:
 
 # https://github.com/bitnami/charts/tree/master/bitnami/redis#parameters
 redis:
+  # -- disable if you want to use an existing redis; in which case the values
+  # below must match those of that external redis instance
+  enabled: true
+  # -- hostname, usually service, that provides redis
+  host: null
+  # -- port at which redis is available
+  port: "6379"
+  # -- redisUrl overwrites redis.host and redis.port. It allows to use sentinal redis installations
+  redisUrl: null
   auth:
     # -- you must set a password; the password generated by the redis chart will be
     # rotated on each upgrade:
     password: ""
     # you can also specify the name of an existing Secret
     # with a key of redis-password set to the password you want
-    # existingSecret: ""
+    existingSecret: null
+    existingSecretPasswordKey: null
 
 # @ignored
 service:
-- 
GitLab