This is an automated email from the ASF dual-hosted git repository.
jedcunningham pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 7a254ff240 Add rpc server to helm chart (#38549)
7a254ff240 is described below
commit 7a254ff240e7f39a8fd08cb3bae18021af29c9c1
Author: Daniel Standish <[email protected]>
AuthorDate: Sat Jun 8 07:54:19 2024 -0700
Add rpc server to helm chart (#38549)
---
chart/templates/_helpers.yaml | 10 +
.../rpc-server/rpc-server-deployment.yaml | 259 ++++++
.../rpc-server/rpc-server-networkpolicy.yaml | 58 ++
.../rpc-server/rpc-server-poddisruptionbudget.yaml | 46 +
chart/templates/rpc-server/rpc-server-service.yaml | 59 ++
.../rpc-server/rpc-server-serviceaccount.yaml | 41 +
chart/values.schema.json | 727 ++++++++++++++++
chart/values.yaml | 104 +++
helm_tests/airflow_aux/test_basic_helm_chart.py | 105 ++-
helm_tests/airflow_core/test_rpc_server.py | 954 +++++++++++++++++++++
helm_tests/security/test_rbac.py | 48 +-
11 files changed, 2341 insertions(+), 70 deletions(-)
diff --git a/chart/templates/_helpers.yaml b/chart/templates/_helpers.yaml
index 59a28fd9b7..aaa86ecbe8 100644
--- a/chart/templates/_helpers.yaml
+++ b/chart/templates/_helpers.yaml
@@ -582,6 +582,16 @@ server_tls_key_file = /etc/pgbouncer/server.key
{{- end }}
{{- end }}
+
+{{/* Create the name of the RPC server service account to use */}}
+{{- define "rpcServer.serviceAccountName" -}}
+ {{- if .Values._rpcServer.serviceAccount.create }}
+ {{- default (printf "%s-rpc-server" (include "airflow.serviceAccountName"
.)) .Values._rpcServer.serviceAccount.name }}
+ {{- else }}
+ {{- default "default" .Values._rpcServer.serviceAccount.name }}
+ {{- end }}
+{{- end }}
+
{{/* Create the name of the redis service account to use */}}
{{- define "redis.serviceAccountName" -}}
{{- if .Values.redis.serviceAccount.create }}
diff --git a/chart/templates/rpc-server/rpc-server-deployment.yaml
b/chart/templates/rpc-server/rpc-server-deployment.yaml
new file mode 100644
index 0000000000..4a3766c585
--- /dev/null
+++ b/chart/templates/rpc-server/rpc-server-deployment.yaml
@@ -0,0 +1,259 @@
+{{/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/}}
+
+################################
+## Airflow rpc-server Deployment
+#################################
+{{- if .Values._rpcServer.enabled }}
+{{- $nodeSelector := or .Values._rpcServer.nodeSelector .Values.nodeSelector }}
+{{- $affinity := or .Values._rpcServer.affinity .Values.affinity }}
+{{- $tolerations := or .Values._rpcServer.tolerations .Values.tolerations }}
+{{- $topologySpreadConstraints := or
.Values._rpcServer.topologySpreadConstraints .Values.topologySpreadConstraints
}}
+{{- $revisionHistoryLimit := or .Values._rpcServer.revisionHistoryLimit
.Values.revisionHistoryLimit }}
+{{- $securityContext := include "airflowPodSecurityContext" (list .
.Values._rpcServer) }}
+{{- $containerSecurityContext := include "containerSecurityContext" (list .
.Values._rpcServer) }}
+{{- $containerSecurityContextWaitForMigrations := include
"containerSecurityContext" (list . .Values._rpcServer.waitForMigrations) }}
+{{- $containerLifecycleHooks := or .Values._rpcServer.containerLifecycleHooks
.Values.containerLifecycleHooks }}
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "airflow.fullname" . }}-rpc-server
+ labels:
+ tier: airflow
+ component: rpc-server
+ release: {{ .Release.Name }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ heritage: {{ .Release.Service }}
+ {{- with .Values.labels }}
+ {{- toYaml . | nindent 4 }}
+ {{- end }}
+ {{- if .Values._rpcServer.annotations }}
+ annotations: {{- toYaml .Values._rpcServer.annotations | nindent 4 }}
+ {{- end }}
+spec:
+ replicas: {{ .Values._rpcServer.replicas }}
+ {{- if $revisionHistoryLimit }}
+ revisionHistoryLimit: {{ $revisionHistoryLimit }}
+ {{- end }}
+ strategy:
+ {{- if .Values._rpcServer.strategy }}
+ {{- toYaml .Values._rpcServer.strategy | nindent 4 }}
+ {{- else }}
+ {{- if semverCompare ">=2.0.0" .Values.airflowVersion }}
+ # Here we define the rolling update strategy
+ # - maxSurge define how many pod we can add at a time
+ # - maxUnavailable define how many pod can be unavailable
+ # during the rolling update
+ # Setting maxUnavailable to 0 would make sure we have the appropriate
+ # capacity during the rolling update.
+ # You can also use percentage based value instead of integer.
+ type: RollingUpdate
+ rollingUpdate:
+ maxSurge: 1
+ maxUnavailable: 0
+ {{- else }}
+ type: Recreate
+ {{- end }}
+ {{- end }}
+ selector:
+ matchLabels:
+ tier: airflow
+ component: rpc-server
+ release: {{ .Release.Name }}
+ template:
+ metadata:
+ labels:
+ tier: airflow
+ component: rpc-server
+ release: {{ .Release.Name }}
+ {{- if or (.Values.labels) (.Values._rpcServer.labels) }}
+ {{- mustMerge .Values._rpcServer.labels .Values.labels | toYaml |
nindent 8 }}
+ {{- end }}
+ annotations:
+ checksum/metadata-secret: {{ include (print $.Template.BasePath
"/secrets/metadata-connection-secret.yaml") . | sha256sum }}
+ checksum/pgbouncer-config-secret: {{ include (print
$.Template.BasePath "/secrets/pgbouncer-config-secret.yaml") . | sha256sum }}
+ checksum/airflow-config: {{ include (print $.Template.BasePath
"/configmaps/configmap.yaml") . | sha256sum }}
+ checksum/extra-configmaps: {{ include (print $.Template.BasePath
"/configmaps/extra-configmaps.yaml") . | sha256sum }}
+ checksum/extra-secrets: {{ include (print $.Template.BasePath
"/secrets/extra-secrets.yaml") . | sha256sum }}
+ {{- if .Values.airflowPodAnnotations }}
+ {{- toYaml .Values.airflowPodAnnotations | nindent 8 }}
+ {{- end }}
+ {{- if .Values._rpcServer.podAnnotations }}
+ {{- toYaml .Values._rpcServer.podAnnotations | nindent 8 }}
+ {{- end }}
+ spec:
+ {{- if .Values._rpcServer.hostAliases }}
+ hostAliases: {{- toYaml .Values._rpcServer.hostAliases | nindent 8 }}
+ {{- end }}
+ serviceAccountName: {{ include "rpcServer.serviceAccountName" . }}
+ {{- if .Values._rpcServer.priorityClassName }}
+ priorityClassName: {{ .Values._rpcServer.priorityClassName }}
+ {{- end }}
+ {{- if .Values.schedulerName }}
+ schedulerName: {{ .Values.schedulerName }}
+ {{- end }}
+ nodeSelector: {{- toYaml $nodeSelector | nindent 8 }}
+ affinity:
+ {{- if $affinity }}
+ {{- toYaml $affinity | nindent 8 }}
+ {{- else }}
+ podAntiAffinity:
+ preferredDuringSchedulingIgnoredDuringExecution:
+ - podAffinityTerm:
+ labelSelector:
+ matchLabels:
+ component: rpc-server
+ topologyKey: kubernetes.io/hostname
+ weight: 100
+ {{- end }}
+ tolerations: {{- toYaml $tolerations | nindent 8 }}
+ topologySpreadConstraints: {{- toYaml $topologySpreadConstraints |
nindent 8 }}
+ restartPolicy: Always
+ securityContext: {{ $securityContext | nindent 8 }}
+ {{- if or .Values.registry.secretName .Values.registry.connection }}
+ imagePullSecrets:
+ - name: {{ template "registry_secret" . }}
+ {{- end }}
+ initContainers:
+ {{- if .Values._rpcServer.waitForMigrations.enabled }}
+ - name: wait-for-airflow-migrations
+ resources: {{- toYaml .Values._rpcServer.resources | nindent 12 }}
+ image: {{ template "airflow_image_for_migrations" . }}
+ imagePullPolicy: {{ .Values.images.airflow.pullPolicy }}
+ securityContext: {{ $containerSecurityContextWaitForMigrations |
nindent 12 }}
+ volumeMounts:
+ {{- include "airflow_config_mount" . | nindent 12 }}
+ {{- if .Values.volumeMounts }}
+ {{- toYaml .Values.volumeMounts | nindent 12 }}
+ {{- end }}
+ {{- if .Values._rpcServer.extraVolumeMounts }}
+ {{- tpl (toYaml .Values._rpcServer.extraVolumeMounts) . |
nindent 12 }}
+ {{- end }}
+ args: {{- include "wait-for-migrations-command" . | indent 10 }}
+ envFrom: {{- include "custom_airflow_environment_from" . | default
"\n []" | indent 10 }}
+ env:
+ {{- include "custom_airflow_environment" . | indent 10 }}
+ {{- include "standard_airflow_environment" . | indent 10 }}
+ {{- if .Values._rpcServer.waitForMigrations.env }}
+ {{- tpl (toYaml .Values._rpcServer.waitForMigrations.env) $ |
nindent 12 }}
+ {{- end }}
+ {{- end }}
+ {{- if .Values._rpcServer.extraInitContainers }}
+ {{- toYaml .Values._rpcServer.extraInitContainers | nindent 8 }}
+ {{- end }}
+ containers:
+ - name: rpc-server
+ image: {{ template "airflow_image" . }}
+ imagePullPolicy: {{ .Values.images.airflow.pullPolicy }}
+ securityContext: {{ $containerSecurityContext | nindent 12 }}
+ {{- if $containerLifecycleHooks }}
+ lifecycle: {{- tpl (toYaml $containerLifecycleHooks) . | nindent 12
}}
+ {{- end }}
+ {{- if .Values._rpcServer.command }}
+ command: {{ tpl (toYaml .Values._rpcServer.command) . | nindent 12 }}
+ {{- end }}
+ {{- if .Values._rpcServer.args }}
+ args: {{- tpl (toYaml .Values._rpcServer.args) . | nindent 12 }}
+ {{- end }}
+ resources: {{- toYaml .Values._rpcServer.resources | nindent 12 }}
+ volumeMounts:
+ {{- include "airflow_config_mount" . | nindent 12 }}
+ {{- if .Values.logs.persistence.enabled }}
+ - name: logs
+ mountPath: {{ template "airflow_logs" . }}
+ {{- end }}
+ {{- if .Values.volumeMounts }}
+ {{- toYaml .Values.volumeMounts | nindent 12 }}
+ {{- end }}
+ {{- if .Values._rpcServer.extraVolumeMounts }}
+ {{- tpl (toYaml .Values._rpcServer.extraVolumeMounts) . |
nindent 12 }}
+ {{- end }}
+ ports:
+ - name: rpc-server
+ containerPort: {{ .Values.ports._rpcServer }}
+ livenessProbe:
+ httpGet:
+ path: {{ if .Values.config.core.internal_api_url }}{{- with
urlParse (tpl .Values.config.core.internal_api_url .) }}{{ .path }}{{ end }}{{
end }}/internal_api/v1/health
+ port: {{ .Values.ports._rpcServer }}
+ {{- if .Values.config.core.internal_api_url}}
+ httpHeaders:
+ - name: Host
+ value: {{ regexReplaceAll ":\\d+$" (urlParse (tpl
.Values.config.core.internal_api_url .)).host "" }}
+ {{- end }}
+ scheme: {{ .Values._rpcServer.livenessProbe.scheme | default
"http" }}
+ initialDelaySeconds: {{
.Values._rpcServer.livenessProbe.initialDelaySeconds }}
+ timeoutSeconds: {{ .Values._rpcServer.livenessProbe.timeoutSeconds
}}
+ failureThreshold: {{
.Values._rpcServer.livenessProbe.failureThreshold }}
+ periodSeconds: {{ .Values._rpcServer.livenessProbe.periodSeconds }}
+ readinessProbe:
+ httpGet:
+ path: {{ if .Values.config.core.internal_api_url }}{{- with
urlParse (tpl .Values.config.core.internal_api_url .) }}{{ .path }}{{ end }}{{
end }}/internal_api/v1/health
+ port: {{ .Values.ports._rpcServer }}
+ {{- if .Values.config.core.internal_api_url }}
+ httpHeaders:
+ - name: Host
+ value: {{ regexReplaceAll ":\\d+$" (urlParse (tpl
.Values.config.core.internal_api_url .)).host "" }}
+ {{- end }}
+ scheme: {{ .Values._rpcServer.readinessProbe.scheme | default
"http" }}
+ initialDelaySeconds: {{
.Values._rpcServer.readinessProbe.initialDelaySeconds }}
+ timeoutSeconds: {{
.Values._rpcServer.readinessProbe.timeoutSeconds }}
+ failureThreshold: {{
.Values._rpcServer.readinessProbe.failureThreshold }}
+ periodSeconds: {{ .Values._rpcServer.readinessProbe.periodSeconds
}}
+ startupProbe:
+ httpGet:
+ path: {{ if .Values.config.core.internal_api_url }}{{- with
urlParse (tpl .Values.config.core.internal_api_url .) }}{{ .path }}{{ end }}{{
end }}/internal_api/v1/health
+ port: {{ .Values.ports._rpcServer }}
+ {{- if .Values.config.core.internal_api_url}}
+ httpHeaders:
+ - name: Host
+ value: {{ regexReplaceAll ":\\d+$" (urlParse (tpl
.Values.config.core.internal_api_url .)).host "" }}
+ {{- end }}
+ scheme: {{ .Values._rpcServer.startupProbe.scheme | default
"http" }}
+ timeoutSeconds: {{ .Values._rpcServer.startupProbe.timeoutSeconds
}}
+ failureThreshold: {{
.Values._rpcServer.startupProbe.failureThreshold }}
+ periodSeconds: {{ .Values._rpcServer.startupProbe.periodSeconds }}
+ envFrom: {{- include "custom_airflow_environment_from" . | default
"\n []" | indent 10 }}
+ env:
+ {{- include "custom_airflow_environment" . | indent 10 }}
+ {{- include "standard_airflow_environment" . | indent 10 }}
+ {{- include "container_extra_envs" (list . .Values._rpcServer.env)
| indent 10 }}
+ {{- if and (.Values.dags.gitSync.enabled) (not
.Values.dags.persistence.enabled) (semverCompare "<2.0.0"
.Values.airflowVersion) }}
+ {{- include "git_sync_container" . | nindent 8 }}
+ {{- end }}
+ {{- if .Values._rpcServer.extraContainers }}
+ {{- tpl (toYaml .Values._rpcServer.extraContainers) . | nindent 8 }}
+ {{- end }}
+ volumes:
+ - name: config
+ configMap:
+ name: {{ template "airflow_config" . }}
+ {{- if (semverCompare "<2.0.0" .Values.airflowVersion) }}
+ {{- end }}
+ {{- if .Values.logs.persistence.enabled }}
+ - name: logs
+ persistentVolumeClaim:
+ claimName: {{ template "airflow_logs_volume_claim" . }}
+ {{- end }}
+ {{- if .Values.volumes }}
+ {{- toYaml .Values.volumes | nindent 8 }}
+ {{- end }}
+ {{- if .Values._rpcServer.extraVolumes }}
+ {{- tpl (toYaml .Values._rpcServer.extraVolumes) . | nindent 8 }}
+ {{- end }}
+{{- end }}
diff --git a/chart/templates/rpc-server/rpc-server-networkpolicy.yaml
b/chart/templates/rpc-server/rpc-server-networkpolicy.yaml
new file mode 100644
index 0000000000..ce34534294
--- /dev/null
+++ b/chart/templates/rpc-server/rpc-server-networkpolicy.yaml
@@ -0,0 +1,58 @@
+{{/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/}}
+
+################################
+## Airflow rpc-server NetworkPolicy
+#################################
+{{- if .Values._rpcServer.enabled }}
+{{- if .Values.networkPolicies.enabled }}
+apiVersion: networking.k8s.io/v1
+kind: NetworkPolicy
+metadata:
+ name: {{ include "airflow.fullname" . }}-rpc-server-policy
+ labels:
+ tier: airflow
+ component: airflow-rpc-server-policy
+ release: {{ .Release.Name }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ heritage: {{ .Release.Service }}
+ {{- if or (.Values.labels) (.Values._rpcServer.labels) }}
+ {{- mustMerge .Values._rpcServer.labels .Values.labels | toYaml |
nindent 4 }}
+ {{- end }}
+spec:
+ podSelector:
+ matchLabels:
+ tier: airflow
+ component: rpc-server
+ release: {{ .Release.Name }}
+ policyTypes:
+ - Ingress
+ {{- if .Values._rpcServer.networkPolicy.ingress.from }}
+ ingress:
+ - from: {{- toYaml .Values._rpcServer.networkPolicy.ingress.from | nindent
6 }}
+ ports:
+ {{ range .Values._rpcServer.networkPolicy.ingress.ports }}
+ -
+ {{- range $key, $val := . }}
+ {{ $key }}: {{ tpl (toString $val) $ }}
+ {{- end }}
+ {{- end }}
+ {{- end }}
+{{- end }}
+{{- end }}
diff --git a/chart/templates/rpc-server/rpc-server-poddisruptionbudget.yaml
b/chart/templates/rpc-server/rpc-server-poddisruptionbudget.yaml
new file mode 100644
index 0000000000..e6d4afebb2
--- /dev/null
+++ b/chart/templates/rpc-server/rpc-server-poddisruptionbudget.yaml
@@ -0,0 +1,46 @@
+{{/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/}}
+
+################################
+## Airflow rpc-server PodDisruptionBudget
+#################################
+{{- if .Values._rpcServer.enabled }}
+{{- if .Values._rpcServer.podDisruptionBudget.enabled }}
+apiVersion: policy/v1
+kind: PodDisruptionBudget
+metadata:
+ name: {{ include "airflow.fullname" . }}-rpc-server-pdb
+ labels:
+ tier: airflow
+ component: rpc-server
+ release: {{ .Release.Name }}
+ chart: {{ .Chart.Name }}
+ heritage: {{ .Release.Service }}
+ {{- if or (.Values.labels) (.Values._rpcServer.labels) }}
+ {{- mustMerge .Values._rpcServer.labels .Values.labels | toYaml |
nindent 4 }}
+ {{- end }}
+spec:
+ selector:
+ matchLabels:
+ tier: airflow
+ component: rpc-server
+ release: {{ .Release.Name }}
+ {{- toYaml .Values._rpcServer.podDisruptionBudget.config | nindent 2 }}
+{{- end }}
+{{- end }}
diff --git a/chart/templates/rpc-server/rpc-server-service.yaml
b/chart/templates/rpc-server/rpc-server-service.yaml
new file mode 100644
index 0000000000..f6b9160d2f
--- /dev/null
+++ b/chart/templates/rpc-server/rpc-server-service.yaml
@@ -0,0 +1,59 @@
+{{/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/}}
+
+################################
+## Airflow rpc-server Service
+#################################
+{{- if .Values._rpcServer.enabled }}
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "airflow.fullname" . }}-rpc-server
+ labels:
+ tier: airflow
+ component: rpc-server
+ release: {{ .Release.Name }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ heritage: {{ .Release.Service }}
+ {{- if or (.Values.labels) (.Values._rpcServer.labels) }}
+ {{- mustMerge .Values._rpcServer.labels .Values.labels | toYaml |
nindent 4 }}
+ {{- end }}
+ {{- with .Values._rpcServer.service.annotations }}
+ annotations: {{- toYaml . | nindent 4 }}
+ {{- end }}
+spec:
+ type: {{ .Values._rpcServer.service.type }}
+ selector:
+ tier: airflow
+ component: rpc-server
+ release: {{ .Release.Name }}
+ ports:
+ {{ range .Values._rpcServer.service.ports }}
+ -
+ {{- range $key, $val := . }}
+ {{ $key }}: {{ tpl (toString $val) $ }}
+ {{- end }}
+ {{- end }}
+ {{- if .Values._rpcServer.service.loadBalancerIP }}
+ loadBalancerIP: {{ .Values._rpcServer.service.loadBalancerIP }}
+ {{- end }}
+ {{- if .Values._rpcServer.service.loadBalancerSourceRanges }}
+ loadBalancerSourceRanges: {{- toYaml
.Values._rpcServer.service.loadBalancerSourceRanges | nindent 4 }}
+ {{- end }}
+{{- end }}
diff --git a/chart/templates/rpc-server/rpc-server-serviceaccount.yaml
b/chart/templates/rpc-server/rpc-server-serviceaccount.yaml
new file mode 100644
index 0000000000..655b346fbe
--- /dev/null
+++ b/chart/templates/rpc-server/rpc-server-serviceaccount.yaml
@@ -0,0 +1,41 @@
+{{/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+*/}}
+
+######################################
+## Airflow rpc-server ServiceAccount
+######################################
+{{- if and .Values._rpcServer.enabled .Values._rpcServer.serviceAccount.create
}}
+apiVersion: v1
+kind: ServiceAccount
+automountServiceAccountToken: {{
.Values._rpcServer.serviceAccount.automountServiceAccountToken }}
+metadata:
+ name: {{ include "rpcServer.serviceAccountName" . }}
+ labels:
+ tier: airflow
+ component: rpc-server
+ release: {{ .Release.Name }}
+ chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
+ heritage: {{ .Release.Service }}
+ {{- if or (.Values.labels) (.Values._rpcServer.labels) }}
+ {{- mustMerge .Values._rpcServer.labels .Values.labels | toYaml |
nindent 4 }}
+ {{- end }}
+ {{- with .Values._rpcServer.serviceAccount.annotations }}
+ annotations: {{- toYaml . | nindent 4 }}
+ {{- end }}
+{{- end }}
diff --git a/chart/values.schema.json b/chart/values.schema.json
index 9e7a819aef..a39901178f 100644
--- a/chart/values.schema.json
+++ b/chart/values.schema.json
@@ -9,6 +9,7 @@
"Ports",
"Database",
"PgBouncer",
+ "RPC Server",
"Scheduler",
"Webserver",
"Workers",
@@ -4237,6 +4238,727 @@
}
}
},
+ "_rpcServer": {
+ "description": "Airflow RPC server settings (AIP-44). Experimental
/ for dev purpose only.",
+ "type": "object",
+ "x-docsSection": "RPC Server",
+ "additionalProperties": false,
+ "properties": {
+ "enabled": {
+ "description": "Enable RPC server",
+ "type": "boolean",
+ "default": false
+ },
+ "configMapAnnotations": {
+ "description": "Extra annotations to apply to the RPC
server configmap.",
+ "type": "object",
+ "default": {},
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "hostAliases": {
+ "description": "HostAliases for the RPC server pod.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.HostAlias"
+ },
+ "type": "array",
+ "default": [],
+ "examples": [
+ {
+ "ip": "127.0.0.1",
+ "hostnames": [
+ "foo.local"
+ ]
+ },
+ {
+ "ip": "10.1.2.3",
+ "hostnames": [
+ "foo.remote"
+ ]
+ }
+ ]
+ },
+ "allowPodLogReading": {
+ "description": "Allow RPC server to read k8s pod logs.
Useful when you don't have an external log store.",
+ "type": "boolean",
+ "default": true
+ },
+ "livenessProbe": {
+ "description": "Liveness probe configuration.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "initialDelaySeconds": {
+ "description": "RPC server Liveness probe initial
delay.",
+ "type": "integer",
+ "default": 15
+ },
+ "timeoutSeconds": {
+ "description": "RPC server Liveness probe timeout
seconds.",
+ "type": "integer",
+ "default": 5
+ },
+ "failureThreshold": {
+ "description": "RPC server Liveness probe failure
threshold.",
+ "type": "integer",
+ "default": 5
+ },
+ "periodSeconds": {
+ "description": "RPC server Liveness probe period
seconds.",
+ "type": "integer",
+ "default": 10
+ },
+ "scheme": {
+ "description": "RPC server Liveness probe scheme.",
+ "type": "string",
+ "default": "HTTP"
+ }
+ }
+ },
+ "readinessProbe": {
+ "description": "Readiness probe configuration.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "initialDelaySeconds": {
+ "description": "RPC server Readiness probe initial
delay.",
+ "type": "integer",
+ "default": 15
+ },
+ "timeoutSeconds": {
+ "description": "RPC server Readiness probe timeout
seconds.",
+ "type": "integer",
+ "default": 5
+ },
+ "failureThreshold": {
+ "description": "RPC server Readiness probe failure
threshold.",
+ "type": "integer",
+ "default": 5
+ },
+ "periodSeconds": {
+ "description": "RPC server Readiness probe period
seconds.",
+ "type": "integer",
+ "default": 10
+ },
+ "scheme": {
+ "description": "RPC server Readiness probe
scheme.",
+ "type": "string",
+ "default": "HTTP"
+ }
+ }
+ },
+ "startupProbe": {
+ "description": "Startup probe configuration.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "timeoutSeconds": {
+ "description": "RPC server Startup probe timeout
seconds.",
+ "type": "integer",
+ "default": 20
+ },
+ "failureThreshold": {
+ "description": "RPC server Startup probe failure
threshold.",
+ "type": "integer",
+ "default": 6
+ },
+ "periodSeconds": {
+ "description": "RPC server Startup probe period
seconds.",
+ "type": "integer",
+ "default": 10
+ },
+ "scheme": {
+ "description": "RPC server Startup probe scheme.",
+ "type": "string",
+ "default": "HTTP"
+ }
+ }
+ },
+ "replicas": {
+ "description": "How many Airflow RPC server replicas
should run.",
+ "type": "integer",
+ "default": 1
+ },
+ "revisionHistoryLimit": {
+ "description": "Number of old replicasets to retain.",
+ "type": [
+ "integer",
+ "null"
+ ],
+ "default": null,
+ "x-docsSection": null
+ },
+ "command": {
+ "description": "Command to use when running the Airflow
RPC server (templated).",
+ "type": [
+ "array",
+ "null"
+ ],
+ "items": {
+ "type": "string"
+ },
+ "default": [
+ "bash"
+ ]
+ },
+ "args": {
+ "description": "Args to use when running the Airflow RPC
server (templated).",
+ "type": [
+ "array",
+ "null"
+ ],
+ "items": {
+ "type": "string"
+ },
+ "default": [
+ "-c",
+ "exec airflow internal-api"
+ ]
+ },
+ "strategy": {
+ "description": "Specifies the strategy used to replace old
Pods by new ones.",
+ "type": [
+ "null",
+ "object"
+ ],
+ "default": null
+ },
+ "serviceAccount": {
+ "description": "Create ServiceAccount.",
+ "type": "object",
+ "properties": {
+ "automountServiceAccountToken": {
+ "description": "Specifies if ServiceAccount's API
credentials should be mounted onto Pods",
+ "type": "boolean",
+ "default": true
+ },
+ "create": {
+ "description": "Specifies whether a ServiceAccount
should be created.",
+ "type": "boolean",
+ "default": true
+ },
+ "name": {
+ "description": "The name of the ServiceAccount to
use. If not set and create is true, a name is generated using the release
name.",
+ "type": [
+ "string",
+ "null"
+ ],
+ "default": null
+ },
+ "annotations": {
+ "description": "Annotations to add to the RPC
server Kubernetes ServiceAccount.",
+ "type": "object",
+ "default": {},
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "podDisruptionBudget": {
+ "description": "RPC server pod disruption budget.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "enabled": {
+ "description": "Enable pod disruption budget.",
+ "type": "boolean",
+ "default": false
+ },
+ "config": {
+ "description": "Disruption budget configuration.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "maxUnavailable": {
+ "description": "Max unavailable pods for
RPC server.",
+ "type": [
+ "integer",
+ "string"
+ ],
+ "default": 1
+ },
+ "minAvailable": {
+ "description": "Min available pods for RPC
server.",
+ "type": [
+ "integer",
+ "string"
+ ],
+ "default": 1
+ }
+ }
+ }
+ }
+ },
+ "extraNetworkPolicies": {
+ "description": "Additional NetworkPolicies as needed
(Deprecated - renamed to `RPC server.networkPolicy.ingress.from`).",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "$ref":
"#/definitions/io.k8s.api.networking.v1.NetworkPolicyPeer"
+ },
+ "default": []
+ },
+ "networkPolicy": {
+ "description": "RPC server NetworkPolicy configuration",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "ingress": {
+ "description": "RPC server NetworkPolicyingress
configuration",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "from": {
+ "description": "Peers for RPC server
NetworkPolicyingress.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "$ref":
"#/definitions/io.k8s.api.networking.v1.NetworkPolicyPeer"
+ }
+ },
+ "ports": {
+ "description": "Ports for RPC server
NetworkPolicyingress (if `from` is set).",
+ "type": "array",
+ "items": {
+ "$ref":
"#/definitions/io.k8s.api.networking.v1.NetworkPolicyPort"
+ },
+ "default": [
+ {
+ "port": "{{
.Values.ports._rpcServer }}"
+ }
+ ],
+ "examples": [
+ {
+ "port": 9080
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "containerLifecycleHooks": {
+ "description": "Container Lifecycle Hooks definition for
the RPC server. If not set, the values from global `containerLifecycleHooks`
will be used.",
+ "type": "object",
+ "$ref": "#/definitions/io.k8s.api.core.v1.Lifecycle",
+ "default": {},
+ "x-docsSection": "Kubernetes",
+ "examples": [
+ {
+ "postStart": {
+ "exec": {
+ "command": [
+ "/bin/sh",
+ "-c",
+ "echo postStart handler >
/usr/share/message"
+ ]
+ }
+ },
+ "preStop": {
+ "exec": {
+ "command": [
+ "/bin/sh",
+ "-c",
+ "echo preStop handler >
/usr/share/message"
+ ]
+ }
+ }
+ }
+ ]
+ },
+ "securityContexts": {
+ "description": "Security context definition for the RPC
server. If not set, the values from global `securityContexts` will be used.",
+ "type": "object",
+ "x-docsSection": "Kubernetes",
+ "properties": {
+ "pod": {
+ "description": "Pod security context definition
for the RPC server.",
+ "type": "object",
+ "$ref":
"#/definitions/io.k8s.api.core.v1.PodSecurityContext",
+ "default": {},
+ "x-docsSection": "Kubernetes",
+ "examples": [
+ {
+ "runAsUser": 50000,
+ "runAsGroup": 0,
+ "fsGroup": 0
+ }
+ ]
+ },
+ "container": {
+ "description": "Container security context
definition for the RPC server.",
+ "type": "object",
+ "$ref":
"#/definitions/io.k8s.api.core.v1.SecurityContext",
+ "default": {},
+ "x-docsSection": "Kubernetes",
+ "examples": [
+ {
+ "allowPrivilegeEscalation": false,
+ "capabilities": {
+ "drop": [
+ "ALL"
+ ]
+ }
+ }
+ ]
+ }
+ }
+ },
+ "resources": {
+ "description": "Resources for RPC server pods.",
+ "type": "object",
+ "default": {},
+ "examples": [
+ {
+ "limits": {
+ "cpu": "100m",
+ "memory": "128Mi"
+ },
+ "requests": {
+ "cpu": "100m",
+ "memory": "128Mi"
+ }
+ }
+ ],
+ "$ref":
"#/definitions/io.k8s.api.core.v1.ResourceRequirements"
+ },
+ "defaultUser": {
+ "description": "Optional default Airflow user information",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "enabled": {
+ "description": "Enable default user creation.",
+ "type": "boolean",
+ "x-docsSection": "Common",
+ "default": true
+ },
+ "role": {
+ "description": "Default user role.",
+ "type": "string",
+ "default": "Admin"
+ },
+ "username": {
+ "description": "Default user username.",
+ "type": "string",
+ "default": "admin"
+ },
+ "email": {
+ "description": "Default user email address.",
+ "type": "string",
+ "default": "[email protected]"
+ },
+ "firstName": {
+ "description": "Default user firstname.",
+ "type": "string",
+ "default": "admin"
+ },
+ "lastName": {
+ "description": "Default user lastname.",
+ "type": "string",
+ "default": "user"
+ },
+ "password": {
+ "description": "Default user password.",
+ "type": "string",
+ "default": "admin"
+ }
+ }
+ },
+ "extraContainers": {
+ "description": "Launch additional containers into RPC
server.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.Container"
+ }
+ },
+ "extraInitContainers": {
+ "description": "Add additional init containers into RPC
server.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.Container"
+ }
+ },
+ "extraVolumes": {
+ "description": "Mount additional volumes into RPC server.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.Volume"
+ }
+ },
+ "extraVolumeMounts": {
+ "description": "Mount additional volumes into RPC server.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.core.v1.VolumeMount"
+ }
+ },
+ "RPC serverConfig": {
+ "description": "This string (can be templated) will be
mounted into the Airflow RPC server as a custom `RPC server_config.py`. You can
bake a `RPC server_config.py` in to your image instead or specify a configmap
containing the RPC server_config.py.",
+ "type": [
+ "string",
+ "null"
+ ],
+ "x-docsSection": "Common",
+ "default": null,
+ "examples": [
+ "from airflow import configuration as conf\n\n# The
SQLAlchemy connection string.\nSQLALCHEMY_DATABASE_URI = conf.get('database',
'SQL_ALCHEMY_CONN')\n\n# Flask-WTF flag for CSRF\nCSRF_ENABLED = True"
+ ]
+ },
+ "RPC serverConfigConfigMapName": {
+ "description": "The configmap name containing the RPC
server_config.py.",
+ "type": [
+ "string",
+ "null"
+ ],
+ "x-docsSection": "Common",
+ "default": null,
+ "examples": [
+ "my-RPC server-configmap"
+ ]
+ },
+ "service": {
+ "description": "RPC server Service configuration.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "type": {
+ "description": "RPC server Service type.",
+ "type": "string",
+ "default": "ClusterIP"
+ },
+ "annotations": {
+ "description": "Annotations for the RPC server
Service.",
+ "type": "object",
+ "default": {},
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "ports": {
+ "description": "Ports for the RPC server Service.",
+ "type": "array",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "port": {
+ "type": [
+ "string",
+ "integer"
+ ]
+ },
+ "targetPort": {
+ "type": [
+ "string",
+ "integer"
+ ]
+ },
+ "nodePort": {
+ "type": [
+ "string",
+ "integer"
+ ]
+ },
+ "protocol": {
+ "type": "string"
+ }
+ }
+ },
+ "default": [
+ {
+ "name": "rpc-server",
+ "port": "{{ .Values.ports._rpcServer }}"
+ }
+ ],
+ "examples": [
+ {
+ "name": "rpc-server",
+ "port": 9080,
+ "targetPort": "rpc-server"
+ },
+ {
+ "name": "only_sidecar",
+ "port": 9080,
+ "targetPort": 8888
+ }
+ ]
+ },
+ "loadBalancerIP": {
+ "description": "RPC server Service
loadBalancerIP.",
+ "type": [
+ "string",
+ "null"
+ ],
+ "default": null
+ },
+ "loadBalancerSourceRanges": {
+ "description": "RPC server Service
``loadBalancerSourceRanges``.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "default": [],
+ "examples": [
+ "10.123.0.0/16"
+ ]
+ }
+ }
+ },
+ "nodeSelector": {
+ "description": "Select certain nodes for RPC server pods.",
+ "type": "object",
+ "default": {},
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "priorityClassName": {
+ "description": "Specify priority for RPC server pods.",
+ "type": [
+ "string",
+ "null"
+ ],
+ "default": null
+ },
+ "affinity": {
+ "description": "Specify scheduling constraints for RPC
server pods.",
+ "type": "object",
+ "default": "See values.yaml",
+ "$ref": "#/definitions/io.k8s.api.core.v1.Affinity"
+ },
+ "tolerations": {
+ "description": "Specify Tolerations for RPC server pods.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "type": "object",
+ "$ref": "#/definitions/io.k8s.api.core.v1.Toleration"
+ }
+ },
+ "topologySpreadConstraints": {
+ "description": "Specify topology spread constraints for
RPC server pods.",
+ "type": "array",
+ "default": [],
+ "x-docsSection": "Kubernetes",
+ "items": {
+ "type": "object",
+ "$ref":
"#/definitions/io.k8s.api.core.v1.TopologySpreadConstraint"
+ }
+ },
+ "annotations": {
+ "description": "Annotations to add to the RPC server
deployment",
+ "type": "object",
+ "default": {},
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "podAnnotations": {
+ "description": "Annotations to add to the RPC server
pods.",
+ "type": "object",
+ "default": {},
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "labels": {
+ "description": "Labels to add to the RPC server objects
and pods.",
+ "type": "object",
+ "default": {},
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "waitForMigrations": {
+ "description": "wait-for-airflow-migrations init
container.",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "enabled": {
+ "description": "Enable wait-for-airflow-migrations
init container.",
+ "type": "boolean",
+ "default": true
+ },
+ "env": {
+ "description": "Add additional env vars to
wait-for-airflow-migrations init container.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "additionalProperties": false
+ }
+ },
+ "securityContexts": {
+ "description": "Security context definition for
the wait for migrations. If not set, the values from global `securityContexts`
will be used.",
+ "type": "object",
+ "x-docsSection": "Kubernetes",
+ "properties": {
+ "container": {
+ "description": "Container security context
definition for the wait for migrations.",
+ "type": "object",
+ "$ref":
"#/definitions/io.k8s.api.core.v1.SecurityContext",
+ "default": {},
+ "x-docsSection": "Kubernetes",
+ "examples": [
+ {
+ "allowPrivilegeEscalation": false,
+ "capabilities": {
+ "drop": [
+ "ALL"
+ ]
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "env": {
+ "description": "Add additional env vars to RPC server.",
+ "type": "array",
+ "default": [],
+ "items": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string"
+ },
+ "value": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "value"
+ ],
+ "additionalProperties": false
+ }
+ }
+ }
+ },
"webserver": {
"description": "Airflow webserver settings.",
"type": "object",
@@ -6761,6 +7483,11 @@
"type": "integer",
"default": 8080
},
+ "_rpcServer": {
+ "description": "RPC server port (AIP-44). Experimental /
dev purpose only.",
+ "type": "integer",
+ "default": 9080
+ },
"workerLogs": {
"description": "Worker logs port.",
"type": "integer",
diff --git a/chart/values.yaml b/chart/values.yaml
index 44eee370cb..45bfeadbc6 100644
--- a/chart/values.yaml
+++ b/chart/values.yaml
@@ -1156,6 +1156,108 @@ migrateDatabaseJob:
useHelmHooks: true
applyCustomEnv: true
+# rpcServer support is experimental / dev purpose only and will later be
renamed
+_rpcServer:
+ enabled: false
+
+ # Labels specific to workers objects and pods
+ labels: {}
+
+ # Command to use when running the Airflow rpc server (templated).
+ command:
+ - "bash"
+ # Args to use when running the Airflow rpc server (templated).
+ args: ["-c", "exec airflow internal-api"]
+ env: []
+ serviceAccount:
+ # default value is true
+ # ref:
https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
+ automountServiceAccountToken: true
+ # Specifies whether a ServiceAccount should be created
+ create: true
+ # The name of the ServiceAccount to use.
+ # If not set and create is true, a name is generated using the release name
+ name: ~
+
+ # Annotations to add to webserver kubernetes service account.
+ annotations: {}
+ service:
+ type: ClusterIP
+ ## service annotations
+ annotations: {}
+ ports:
+ - name: rpc-server
+ port: "{{ .Values.ports._rpcServer }}"
+
+ loadBalancerIP: ~
+ ## Limit load balancer source ips to list of CIDRs
+ # loadBalancerSourceRanges:
+ # - "10.123.0.0/16"
+ loadBalancerSourceRanges: []
+
+ podDisruptionBudget:
+ enabled: false
+
+ # PDB configuration
+ config:
+ # minAvailable and maxUnavailable are mutually exclusive
+ maxUnavailable: 1
+ # minAvailable: 1
+
+ # Detailed default security contexts for webserver deployments for container
and pod level
+ securityContexts:
+ pod: {}
+ container: {}
+
+ waitForMigrations:
+ # Whether to create init container to wait for db migrations
+ enabled: true
+ env: []
+ # Detailed default security context for waitForMigrations for container
level
+ securityContexts:
+ container: {}
+
+ # Launch additional containers into the flower pods.
+ extraContainers: []
+
+ # Additional network policies as needed (Deprecated - renamed to
`webserver.networkPolicy.ingress.from`)
+ extraNetworkPolicies: []
+ networkPolicy:
+ ingress:
+ # Peers for webserver NetworkPolicy ingress
+ from: []
+ # Ports for webserver NetworkPolicy ingress (if `from` is set)
+ ports:
+ - port: "{{ .Values.ports._rpcServer }}"
+
+ resources: {}
+ # limits:
+ # cpu: 100m
+ # memory: 128Mi
+ # requests:
+ # cpu: 100m
+ # memory: 128Mi
+
+ livenessProbe:
+ initialDelaySeconds: 15
+ timeoutSeconds: 5
+ failureThreshold: 5
+ periodSeconds: 10
+ scheme: HTTP
+
+ readinessProbe:
+ initialDelaySeconds: 15
+ timeoutSeconds: 5
+ failureThreshold: 5
+ periodSeconds: 10
+ scheme: HTTP
+
+ startupProbe:
+ timeoutSeconds: 20
+ failureThreshold: 6
+ periodSeconds: 10
+ scheme: HTTP
+
# Airflow webserver settings
webserver:
enabled: true
@@ -2240,6 +2342,8 @@ ports:
statsdScrape: 9102
pgbouncer: 6543
pgbouncerScrape: 9127
+ # rpcServer support is experimental / dev purpose only and will later be
renamed
+ _rpcServer: 9080
# Define any ResourceQuotas for namespace
quotas: {}
diff --git a/helm_tests/airflow_aux/test_basic_helm_chart.py
b/helm_tests/airflow_aux/test_basic_helm_chart.py
index c45326bce4..54ee072579 100644
--- a/helm_tests/airflow_aux/test_basic_helm_chart.py
+++ b/helm_tests/airflow_aux/test_basic_helm_chart.py
@@ -29,6 +29,45 @@ from tests.charts.helm_template_generator import render_chart
OBJECT_COUNT_IN_BASIC_DEPLOYMENT = 35
+DEFAULT_OBJECTS_STD_NAMING = {
+ ("ServiceAccount", "test-basic-airflow-create-user-job"),
+ ("ServiceAccount", "test-basic-airflow-migrate-database-job"),
+ ("ServiceAccount", "test-basic-airflow-redis"),
+ ("ServiceAccount", "test-basic-airflow-scheduler"),
+ ("ServiceAccount", "test-basic-airflow-statsd"),
+ ("ServiceAccount", "test-basic-airflow-triggerer"),
+ ("ServiceAccount", "test-basic-airflow-webserver"),
+ ("ServiceAccount", "test-basic-airflow-worker"),
+ ("Secret", "test-basic-airflow-metadata"),
+ ("Secret", "test-basic-broker-url"),
+ ("Secret", "test-basic-fernet-key"),
+ ("Secret", "test-basic-airflow-webserver-secret-key"),
+ ("Secret", "test-basic-redis-password"),
+ ("Secret", "test-basic-postgresql"),
+ ("ConfigMap", "test-basic-airflow-config"),
+ ("ConfigMap", "test-basic-airflow-statsd"),
+ ("Role", "test-basic-airflow-pod-launcher-role"),
+ ("Role", "test-basic-airflow-pod-log-reader-role"),
+ ("RoleBinding", "test-basic-airflow-pod-launcher-rolebinding"),
+ ("RoleBinding", "test-basic-airflow-pod-log-reader-rolebinding"),
+ ("Service", "test-basic-airflow-redis"),
+ ("Service", "test-basic-airflow-statsd"),
+ ("Service", "test-basic-airflow-triggerer"),
+ ("Service", "test-basic-airflow-webserver"),
+ ("Service", "test-basic-airflow-worker"),
+ ("Service", "test-basic-postgresql"),
+ ("Service", "test-basic-postgresql-hl"),
+ ("Deployment", "test-basic-airflow-scheduler"),
+ ("Deployment", "test-basic-airflow-statsd"),
+ ("Deployment", "test-basic-airflow-webserver"),
+ ("StatefulSet", "test-basic-airflow-redis"),
+ ("StatefulSet", "test-basic-airflow-worker"),
+ ("StatefulSet", "test-basic-airflow-triggerer"),
+ ("StatefulSet", "test-basic-postgresql"),
+ ("Job", "test-basic-airflow-create-user"),
+ ("Job", "test-basic-airflow-run-airflow-migrations"),
+}
+
class TestBaseChartTest:
"""Tests basic helm chart tests."""
@@ -104,7 +143,7 @@ class TestBaseChartTest:
if version == "default":
expected.add(("Service", "test-basic-triggerer"))
assert list_of_kind_names_tuples == expected
- assert expected_object_count_in_basic_deployment == len(k8s_objects)
+ assert len(k8s_objects) == expected_object_count_in_basic_deployment
for k8s_object in k8s_objects:
labels = jmespath.search("metadata.labels", k8s_object) or {}
if "helm.sh/chart" in labels:
@@ -123,48 +162,21 @@ class TestBaseChartTest:
"test-basic",
{"useStandardNaming": True},
)
- list_of_kind_names_tuples = {
- (k8s_object["kind"], k8s_object["metadata"]["name"]) for
k8s_object in k8s_objects
- }
- expected = {
- ("ServiceAccount", "test-basic-airflow-create-user-job"),
- ("ServiceAccount", "test-basic-airflow-migrate-database-job"),
- ("ServiceAccount", "test-basic-airflow-redis"),
- ("ServiceAccount", "test-basic-airflow-scheduler"),
- ("ServiceAccount", "test-basic-airflow-statsd"),
- ("ServiceAccount", "test-basic-airflow-triggerer"),
- ("ServiceAccount", "test-basic-airflow-webserver"),
- ("ServiceAccount", "test-basic-airflow-worker"),
- ("Secret", "test-basic-airflow-metadata"),
- ("Secret", "test-basic-broker-url"),
- ("Secret", "test-basic-fernet-key"),
- ("Secret", "test-basic-airflow-webserver-secret-key"),
- ("Secret", "test-basic-redis-password"),
- ("Secret", "test-basic-postgresql"),
- ("ConfigMap", "test-basic-airflow-config"),
- ("ConfigMap", "test-basic-airflow-statsd"),
- ("Role", "test-basic-airflow-pod-launcher-role"),
- ("Role", "test-basic-airflow-pod-log-reader-role"),
- ("RoleBinding", "test-basic-airflow-pod-launcher-rolebinding"),
- ("RoleBinding", "test-basic-airflow-pod-log-reader-rolebinding"),
- ("Service", "test-basic-airflow-redis"),
- ("Service", "test-basic-airflow-statsd"),
- ("Service", "test-basic-airflow-triggerer"),
- ("Service", "test-basic-airflow-webserver"),
- ("Service", "test-basic-airflow-worker"),
- ("Service", "test-basic-postgresql"),
- ("Service", "test-basic-postgresql-hl"),
- ("Deployment", "test-basic-airflow-scheduler"),
- ("Deployment", "test-basic-airflow-statsd"),
- ("Deployment", "test-basic-airflow-webserver"),
- ("StatefulSet", "test-basic-airflow-redis"),
- ("StatefulSet", "test-basic-airflow-worker"),
- ("StatefulSet", "test-basic-airflow-triggerer"),
- ("StatefulSet", "test-basic-postgresql"),
- ("Job", "test-basic-airflow-create-user"),
- ("Job", "test-basic-airflow-run-airflow-migrations"),
+ actual = {(x["kind"], x["metadata"]["name"]) for x in k8s_objects}
+ assert actual == DEFAULT_OBJECTS_STD_NAMING
+
+ def test_basic_deployment_with_rpc_server(self):
+ extra_objects = {
+ ("Deployment", "test-basic-airflow-rpc-server"),
+ ("Service", "test-basic-airflow-rpc-server"),
+ ("ServiceAccount", "test-basic-airflow-rpc-server"),
}
- assert list_of_kind_names_tuples == expected
+ k8s_objects = render_chart(
+ "test-basic",
+ values={"_rpcServer": {"enabled": True}, "useStandardNaming":
True},
+ )
+ actual = {(x["kind"], x["metadata"]["name"]) for x in k8s_objects}
+ assert actual == (DEFAULT_OBJECTS_STD_NAMING | extra_objects)
@pytest.mark.parametrize("version", ["2.3.2", "2.4.0", "default"])
def test_basic_deployment_with_standalone_dag_processor(self, version):
@@ -259,7 +271,7 @@ class TestBaseChartTest:
(k8s_object["kind"], k8s_object["metadata"]["name"]) for
k8s_object in k8s_objects
]
assert ("Job", "test-basic-create-user") not in
list_of_kind_names_tuples
- assert expected_object_count_in_basic_deployment - 2 ==
len(k8s_objects)
+ assert len(k8s_objects) == expected_object_count_in_basic_deployment -
2
@pytest.mark.parametrize("version", ["2.3.2", "2.4.0", "default"])
def test_basic_deployment_without_statsd(self, version):
@@ -276,7 +288,7 @@ class TestBaseChartTest:
assert ("Service", "test-basic-statsd") not in
list_of_kind_names_tuples
assert ("Deployment", "test-basic-statsd") not in
list_of_kind_names_tuples
- assert expected_object_count_in_basic_deployment - 4 ==
len(k8s_objects)
+ assert len(k8s_objects) == expected_object_count_in_basic_deployment -
4
def test_network_policies_are_valid(self):
k8s_objects = render_chart(
@@ -511,7 +523,10 @@ class TestBaseChartTest:
image: str = obj["image"]
if image.startswith(image_repo):
# Make sure that a command is not specified
- assert "command" not in obj
+ if obj["name"] == "rpc-server":
+ assert obj["command"] == ["bash"]
+ else:
+ assert "command" not in obj
@pytest.mark.parametrize(
"executor",
diff --git a/helm_tests/airflow_core/test_rpc_server.py
b/helm_tests/airflow_core/test_rpc_server.py
new file mode 100644
index 0000000000..106b2d38e8
--- /dev/null
+++ b/helm_tests/airflow_core/test_rpc_server.py
@@ -0,0 +1,954 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+from subprocess import CalledProcessError
+
+import jmespath
+import pytest
+
+from tests.charts.helm_template_generator import render_chart
+
+
+class TestRPCServerDeployment:
+ """Tests rpc-server deployment."""
+
+ def test_is_disabled_by_default(self):
+ """
+ RPC server should be disabled by default.
+ """
+ docs = render_chart(
+ values={"_rpcServer": {}},
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert len(docs) == 0
+
+ def
test_should_add_host_header_to_liveness_and_readiness_and_startup_probes(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {"enabled": True},
+ "config": {"core": {"internal_api_url":
"https://example.com:21222/mypath/path"}},
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert {"name": "Host", "value": "example.com"} in jmespath.search(
+
"spec.template.spec.containers[0].livenessProbe.httpGet.httpHeaders", docs[0]
+ )
+ assert {"name": "Host", "value": "example.com"} in jmespath.search(
+
"spec.template.spec.containers[0].readinessProbe.httpGet.httpHeaders", docs[0]
+ )
+ assert {"name": "Host", "value": "example.com"} in jmespath.search(
+
"spec.template.spec.containers[0].startupProbe.httpGet.httpHeaders", docs[0]
+ )
+
+ def
test_should_add_path_to_liveness_and_readiness_and_startup_probes(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ },
+ "config": {"core": {"internal_api_url":
"https://example.com:21222/mypath/path"}},
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert (
+
jmespath.search("spec.template.spec.containers[0].livenessProbe.httpGet.path",
docs[0])
+ == "/mypath/path/internal_api/v1/health"
+ )
+ assert (
+
jmespath.search("spec.template.spec.containers[0].readinessProbe.httpGet.path",
docs[0])
+ == "/mypath/path/internal_api/v1/health"
+ )
+ assert (
+
jmespath.search("spec.template.spec.containers[0].startupProbe.httpGet.path",
docs[0])
+ == "/mypath/path/internal_api/v1/health"
+ )
+
+ @pytest.mark.parametrize(
+ "revision_history_limit, global_revision_history_limit",
+ [(8, 10), (10, 8), (8, None), (None, 10), (None, None)],
+ )
+ def test_revision_history_limit(self, revision_history_limit,
global_revision_history_limit):
+ values = {
+ "_rpcServer": {
+ "enabled": True,
+ }
+ }
+ if revision_history_limit:
+ values["_rpcServer"]["revisionHistoryLimit"] =
revision_history_limit
+ if global_revision_history_limit:
+ values["revisionHistoryLimit"] = global_revision_history_limit
+ docs = render_chart(
+ values=values,
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+ expected_result = revision_history_limit if revision_history_limit
else global_revision_history_limit
+ assert jmespath.search("spec.revisionHistoryLimit", docs[0]) ==
expected_result
+
+ @pytest.mark.parametrize(
+ "values",
+ [
+ {"_rpcServer": {"enabled": True}},
+ {
+ "_rpcServer": {"enabled": True},
+ "config": {"core": {"internal_api_url": ""}},
+ },
+ ],
+ )
+ def test_should_not_contain_host_header(self, values):
+ docs = render_chart(values=values,
show_only=["templates/rpc-server/rpc-server-deployment.yaml"])
+
+ assert (
+
jmespath.search("spec.template.spec.containers[0].livenessProbe.httpGet.httpHeaders",
docs[0])
+ is None
+ )
+ assert (
+
jmespath.search("spec.template.spec.containers[0].readinessProbe.httpGet.httpHeaders",
docs[0])
+ is None
+ )
+ assert (
+
jmespath.search("spec.template.spec.containers[0].startupProbe.httpGet.httpHeaders",
docs[0])
+ is None
+ )
+
+ def test_should_use_templated_base_url_for_probes(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ },
+ "config": {
+ "core": {
+ "internal_api_url": "https://{{ .Release.Name
}}.com:21222/mypath/{{ .Release.Name }}/path"
+ }
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+ container = jmespath.search("spec.template.spec.containers[0]",
docs[0])
+
+ assert {"name": "Host", "value": "release-name.com"} in
jmespath.search(
+ "livenessProbe.httpGet.httpHeaders", container
+ )
+ assert {"name": "Host", "value": "release-name.com"} in
jmespath.search(
+ "readinessProbe.httpGet.httpHeaders", container
+ )
+ assert {"name": "Host", "value": "release-name.com"} in
jmespath.search(
+ "startupProbe.httpGet.httpHeaders", container
+ )
+ assert "/mypath/release-name/path/internal_api/v1/health" ==
jmespath.search(
+ "livenessProbe.httpGet.path", container
+ )
+ assert "/mypath/release-name/path/internal_api/v1/health" ==
jmespath.search(
+ "readinessProbe.httpGet.path", container
+ )
+ assert "/mypath/release-name/path/internal_api/v1/health" ==
jmespath.search(
+ "startupProbe.httpGet.path", container
+ )
+
+ def
test_should_add_scheme_to_liveness_and_readiness_and_startup_probes(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "livenessProbe": {"scheme": "HTTPS"},
+ "readinessProbe": {"scheme": "HTTPS"},
+ "startupProbe": {"scheme": "HTTPS"},
+ }
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert "HTTPS" in jmespath.search(
+ "spec.template.spec.containers[0].livenessProbe.httpGet.scheme",
docs[0]
+ )
+ assert "HTTPS" in jmespath.search(
+ "spec.template.spec.containers[0].readinessProbe.httpGet.scheme",
docs[0]
+ )
+ assert "HTTPS" in jmespath.search(
+ "spec.template.spec.containers[0].startupProbe.httpGet.scheme",
docs[0]
+ )
+
+ def test_should_add_extra_containers(self):
+ docs = render_chart(
+ values={
+ "executor": "CeleryExecutor",
+ "_rpcServer": {
+ "enabled": True,
+ "extraContainers": [
+ {"name": "{{.Chart.Name}}", "image":
"test-registry/test-repo:test-tag"}
+ ],
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert jmespath.search("spec.template.spec.containers[-1]", docs[0])
== {
+ "name": "airflow",
+ "image": "test-registry/test-repo:test-tag",
+ }
+
+ def test_should_add_extraEnvs(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "env": [{"name": "TEST_ENV_1", "value": "test_env_1"}],
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert {"name": "TEST_ENV_1", "value": "test_env_1"} in
jmespath.search(
+ "spec.template.spec.containers[0].env", docs[0]
+ )
+
+ def test_should_add_extra_volume_and_extra_volume_mount(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "extraVolumes": [{"name": "test-volume-{{ .Chart.Name }}",
"emptyDir": {}}],
+ "extraVolumeMounts": [
+ {"name": "test-volume-{{ .Chart.Name }}", "mountPath":
"/opt/test"}
+ ],
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert "test-volume-airflow" ==
jmespath.search("spec.template.spec.volumes[-1].name", docs[0])
+ assert "test-volume-airflow" == jmespath.search(
+ "spec.template.spec.containers[0].volumeMounts[-1].name", docs[0]
+ )
+ assert "test-volume-airflow" == jmespath.search(
+ "spec.template.spec.initContainers[0].volumeMounts[-1].name",
docs[0]
+ )
+
+ def test_should_add_global_volume_and_global_volume_mount(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {"enabled": True},
+ "volumes": [{"name": "test-volume", "emptyDir": {}}],
+ "volumeMounts": [{"name": "test-volume", "mountPath":
"/opt/test"}],
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert "test-volume" ==
jmespath.search("spec.template.spec.volumes[-1].name", docs[0])
+ assert "test-volume" == jmespath.search(
+ "spec.template.spec.containers[0].volumeMounts[-1].name", docs[0]
+ )
+
+ def test_should_add_extraEnvs_to_wait_for_migration_container(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "waitForMigrations": {
+ "env": [{"name": "TEST_ENV_1", "value": "test_env_1"}],
+ },
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert {"name": "TEST_ENV_1", "value": "test_env_1"} in
jmespath.search(
+ "spec.template.spec.initContainers[0].env", docs[0]
+ )
+
+ @pytest.mark.parametrize(
+ "airflow_version, expected_arg",
+ [
+ ("2.0.0", ["airflow", "db", "check-migrations",
"--migration-wait-timeout=60"]),
+ ("2.1.0", ["airflow", "db", "check-migrations",
"--migration-wait-timeout=60"]),
+ ("1.10.2", ["python", "-c"]),
+ ],
+ )
+ def test_wait_for_migration_airflow_version(self, airflow_version,
expected_arg):
+ docs = render_chart(
+ values={
+ "_rpcServer": {"enabled": True},
+ "airflowVersion": airflow_version,
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+ # Don't test the full string, just the length of the expect matches
+ actual = jmespath.search("spec.template.spec.initContainers[0].args",
docs[0])
+ assert expected_arg == actual[: len(expected_arg)]
+
+ def test_disable_wait_for_migration(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "waitForMigrations": {"enabled": False},
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+ actual = jmespath.search(
+
"spec.template.spec.initContainers[?name=='wait-for-airflow-migrations']",
docs[0]
+ )
+ assert actual is None
+
+ def test_should_add_extra_init_containers(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "extraInitContainers": [
+ {"name": "test-init-container", "image":
"test-registry/test-repo:test-tag"}
+ ],
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert {
+ "name": "test-init-container",
+ "image": "test-registry/test-repo:test-tag",
+ } == jmespath.search("spec.template.spec.initContainers[-1]", docs[0])
+
+ def test_should_add_component_specific_labels(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "labels": {"test_label": "test_label_value"},
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert "test_label" in
jmespath.search("spec.template.metadata.labels", docs[0])
+ assert jmespath.search("spec.template.metadata.labels",
docs[0])["test_label"] == "test_label_value"
+
+ def test_should_create_valid_affinity_tolerations_and_node_selector(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "affinity": {
+ "nodeAffinity": {
+ "requiredDuringSchedulingIgnoredDuringExecution": {
+ "nodeSelectorTerms": [
+ {
+ "matchExpressions": [
+ {"key": "foo", "operator": "In",
"values": ["true"]},
+ ]
+ }
+ ]
+ }
+ }
+ },
+ "tolerations": [
+ {"key": "dynamic-pods", "operator": "Equal", "value":
"true", "effect": "NoSchedule"}
+ ],
+ "nodeSelector": {"diskType": "ssd"},
+ }
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert "Deployment" == jmespath.search("kind", docs[0])
+ assert "foo" == jmespath.search(
+ "spec.template.spec.affinity.nodeAffinity."
+ "requiredDuringSchedulingIgnoredDuringExecution."
+ "nodeSelectorTerms[0]."
+ "matchExpressions[0]."
+ "key",
+ docs[0],
+ )
+ assert "ssd" == jmespath.search(
+ "spec.template.spec.nodeSelector.diskType",
+ docs[0],
+ )
+ assert "dynamic-pods" == jmespath.search(
+ "spec.template.spec.tolerations[0].key",
+ docs[0],
+ )
+
+ def test_should_create_default_affinity(self):
+ docs = render_chart(
+ values={"_rpcServer": {"enabled": True}},
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert {"component": "rpc-server"} == jmespath.search(
+ "spec.template.spec.affinity.podAntiAffinity."
+ "preferredDuringSchedulingIgnoredDuringExecution[0]."
+ "podAffinityTerm.labelSelector.matchLabels",
+ docs[0],
+ )
+
+ def
test_affinity_tolerations_topology_spread_constraints_and_node_selector_precedence(self):
+ """When given both global and rpc-server affinity etc, rpc-server
affinity etc is used."""
+ expected_affinity = {
+ "nodeAffinity": {
+ "requiredDuringSchedulingIgnoredDuringExecution": {
+ "nodeSelectorTerms": [
+ {
+ "matchExpressions": [
+ {"key": "foo", "operator": "In", "values":
["true"]},
+ ]
+ }
+ ]
+ }
+ }
+ }
+ expected_topology_spread_constraints = {
+ "maxSkew": 1,
+ "topologyKey": "foo",
+ "whenUnsatisfiable": "ScheduleAnyway",
+ "labelSelector": {"matchLabels": {"tier": "airflow"}},
+ }
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "affinity": expected_affinity,
+ "tolerations": [
+ {"key": "dynamic-pods", "operator": "Equal", "value":
"true", "effect": "NoSchedule"}
+ ],
+ "topologySpreadConstraints":
[expected_topology_spread_constraints],
+ "nodeSelector": {"type": "ssd"},
+ },
+ "affinity": {
+ "nodeAffinity": {
+ "preferredDuringSchedulingIgnoredDuringExecution": [
+ {
+ "weight": 1,
+ "preference": {
+ "matchExpressions": [
+ {"key": "not-me", "operator": "In",
"values": ["true"]},
+ ]
+ },
+ }
+ ]
+ }
+ },
+ "tolerations": [
+ {"key": "not-me", "operator": "Equal", "value": "true",
"effect": "NoSchedule"}
+ ],
+ "topologySpreadConstraints": [
+ {
+ "maxSkew": 1,
+ "topologyKey": "not-me",
+ "whenUnsatisfiable": "ScheduleAnyway",
+ "labelSelector": {"matchLabels": {"tier": "airflow"}},
+ }
+ ],
+ "nodeSelector": {"type": "not-me"},
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert expected_affinity ==
jmespath.search("spec.template.spec.affinity", docs[0])
+ assert "ssd" == jmespath.search(
+ "spec.template.spec.nodeSelector.type",
+ docs[0],
+ )
+ tolerations = jmespath.search("spec.template.spec.tolerations",
docs[0])
+ assert 1 == len(tolerations)
+ assert "dynamic-pods" == tolerations[0]["key"]
+ assert expected_topology_spread_constraints == jmespath.search(
+ "spec.template.spec.topologySpreadConstraints[0]", docs[0]
+ )
+
+ def test_scheduler_name(self):
+ docs = render_chart(
+ values={"_rpcServer": {"enabled": True}, "schedulerName":
"airflow-scheduler"},
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert "airflow-scheduler" == jmespath.search(
+ "spec.template.spec.schedulerName",
+ docs[0],
+ )
+
+ @pytest.mark.parametrize(
+ "log_persistence_values, expected_claim_name",
+ [
+ ({"enabled": False}, None),
+ ({"enabled": True}, "release-name-logs"),
+ ({"enabled": True, "existingClaim": "test-claim"}, "test-claim"),
+ ],
+ )
+ def test_logs_persistence_adds_volume_and_mount(self,
log_persistence_values, expected_claim_name):
+ docs = render_chart(
+ values={"_rpcServer": {"enabled": True}, "logs": {"persistence":
log_persistence_values}},
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ if expected_claim_name:
+ assert {
+ "name": "logs",
+ "persistentVolumeClaim": {"claimName": expected_claim_name},
+ } in jmespath.search("spec.template.spec.volumes", docs[0])
+ assert {
+ "name": "logs",
+ "mountPath": "/opt/airflow/logs",
+ } in
jmespath.search("spec.template.spec.containers[0].volumeMounts", docs[0])
+ else:
+ assert "logs" not in [v["name"] for v in
jmespath.search("spec.template.spec.volumes", docs[0])]
+ assert "logs" not in [
+ v["name"] for v in
jmespath.search("spec.template.spec.containers[0].volumeMounts", docs[0])
+ ]
+
+ def test_config_volumes(self):
+ docs = render_chart(
+ values={"_rpcServer": {"enabled": True}},
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ # default config
+ assert {
+ "name": "config",
+ "mountPath": "/opt/airflow/airflow.cfg",
+ "readOnly": True,
+ "subPath": "airflow.cfg",
+ } in jmespath.search("spec.template.spec.containers[0].volumeMounts",
docs[0])
+
+ def test_rpc_server_resources_are_configurable(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "resources": {
+ "limits": {"cpu": "200m", "memory": "128Mi"},
+ "requests": {"cpu": "300m", "memory": "169Mi"},
+ },
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+ assert "128Mi" ==
jmespath.search("spec.template.spec.containers[0].resources.limits.memory",
docs[0])
+ assert "200m" ==
jmespath.search("spec.template.spec.containers[0].resources.limits.cpu",
docs[0])
+
+ assert "169Mi" == jmespath.search(
+ "spec.template.spec.containers[0].resources.requests.memory",
docs[0]
+ )
+ assert "300m" ==
jmespath.search("spec.template.spec.containers[0].resources.requests.cpu",
docs[0])
+
+ # initContainer wait-for-airflow-migrations
+ assert "128Mi" == jmespath.search(
+ "spec.template.spec.initContainers[0].resources.limits.memory",
docs[0]
+ )
+ assert "200m" ==
jmespath.search("spec.template.spec.initContainers[0].resources.limits.cpu",
docs[0])
+
+ assert "169Mi" == jmespath.search(
+ "spec.template.spec.initContainers[0].resources.requests.memory",
docs[0]
+ )
+ assert "300m" == jmespath.search(
+ "spec.template.spec.initContainers[0].resources.requests.cpu",
docs[0]
+ )
+
+ def test_rpc_server_security_contexts_are_configurable(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "securityContexts": {
+ "pod": {
+ "fsGroup": 1000,
+ "runAsGroup": 1001,
+ "runAsNonRoot": True,
+ "runAsUser": 2000,
+ },
+ "container": {
+ "allowPrivilegeEscalation": False,
+ "readOnlyRootFilesystem": True,
+ },
+ },
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+ assert {"allowPrivilegeEscalation": False, "readOnlyRootFilesystem":
True} == jmespath.search(
+ "spec.template.spec.containers[0].securityContext", docs[0]
+ )
+
+ assert {
+ "runAsUser": 2000,
+ "runAsGroup": 1001,
+ "fsGroup": 1000,
+ "runAsNonRoot": True,
+ } == jmespath.search("spec.template.spec.securityContext", docs[0])
+
+ def test_rpc_server_security_context_legacy(self):
+ with pytest.raises(CalledProcessError, match="Additional property
securityContext is not allowed"):
+ render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "securityContext": {
+ "fsGroup": 1000,
+ "runAsGroup": 1001,
+ "runAsNonRoot": True,
+ "runAsUser": 2000,
+ },
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ def test_rpc_server_resources_are_not_added_by_default(self):
+ docs = render_chart(
+ values={"_rpcServer": {"enabled": True}},
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+ assert jmespath.search("spec.template.spec.containers[0].resources",
docs[0]) == {}
+ assert
jmespath.search("spec.template.spec.initContainers[0].resources", docs[0]) == {}
+
+ @pytest.mark.parametrize(
+ "airflow_version, expected_strategy",
+ [
+ ("2.0.2", {"type": "RollingUpdate", "rollingUpdate": {"maxSurge":
1, "maxUnavailable": 0}}),
+ ("1.10.14", {"type": "Recreate"}),
+ ("1.9.0", {"type": "Recreate"}),
+ ("2.1.0", {"type": "RollingUpdate", "rollingUpdate": {"maxSurge":
1, "maxUnavailable": 0}}),
+ ],
+ )
+ def test_default_update_strategy(self, airflow_version, expected_strategy):
+ docs = render_chart(
+ values={"_rpcServer": {"enabled": True}, "airflowVersion":
airflow_version},
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert jmespath.search("spec.strategy", docs[0]) == expected_strategy
+
+ def test_update_strategy(self):
+ expected_strategy = {"type": "RollingUpdate", "rollingUpdate":
{"maxUnavailable": 1}}
+ docs = render_chart(
+ values={"_rpcServer": {"enabled": True, "strategy":
expected_strategy}},
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert jmespath.search("spec.strategy", docs[0]) == expected_strategy
+
+ def test_default_command_and_args(self):
+ docs = render_chart(
+ values={"_rpcServer": {"enabled": True}},
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert jmespath.search("spec.template.spec.containers[0].command",
docs[0]) == ["bash"]
+ assert ["-c", "exec airflow internal-api"] == jmespath.search(
+ "spec.template.spec.containers[0].args", docs[0]
+ )
+
+ @pytest.mark.parametrize("command", [None, ["custom", "command"]])
+ @pytest.mark.parametrize("args", [None, ["custom", "args"]])
+ def test_command_and_args_overrides(self, command, args):
+ docs = render_chart(
+ values={"_rpcServer": {"enabled": True, "command": command,
"args": args}},
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert command ==
jmespath.search("spec.template.spec.containers[0].command", docs[0])
+ assert args ==
jmespath.search("spec.template.spec.containers[0].args", docs[0])
+
+ def test_command_and_args_overrides_are_templated(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "command": ["{{ .Release.Name }}"],
+ "args": ["{{ .Release.Service }}"],
+ }
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert ["release-name"] ==
jmespath.search("spec.template.spec.containers[0].command", docs[0])
+ assert ["Helm"] ==
jmespath.search("spec.template.spec.containers[0].args", docs[0])
+
+ def test_should_add_component_specific_annotations(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "annotations": {"test_annotation":
"test_annotation_value"},
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+ assert "annotations" in jmespath.search("metadata", docs[0])
+ assert jmespath.search("metadata.annotations",
docs[0])["test_annotation"] == "test_annotation_value"
+
+ def test_rpc_server_pod_hostaliases(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "hostAliases": [{"ip": "127.0.0.1", "hostnames":
["foo.local"]}],
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-deployment.yaml"],
+ )
+
+ assert "127.0.0.1" ==
jmespath.search("spec.template.spec.hostAliases[0].ip", docs[0])
+ assert "foo.local" ==
jmespath.search("spec.template.spec.hostAliases[0].hostnames[0]", docs[0])
+
+
+class TestRPCServerService:
+ """Tests rpc-server service."""
+
+ def test_default_service(self):
+ docs = render_chart(
+ values={"_rpcServer": {"enabled": True}},
+ show_only=["templates/rpc-server/rpc-server-service.yaml"],
+ )
+
+ assert "release-name-rpc-server" == jmespath.search("metadata.name",
docs[0])
+ assert jmespath.search("metadata.annotations", docs[0]) is None
+ assert {"tier": "airflow", "component": "rpc-server", "release":
"release-name"} == jmespath.search(
+ "spec.selector", docs[0]
+ )
+ assert "ClusterIP" == jmespath.search("spec.type", docs[0])
+ assert {"name": "rpc-server", "port": 9080} in
jmespath.search("spec.ports", docs[0])
+
+ def test_overrides(self):
+ docs = render_chart(
+ values={
+ "ports": {"_rpcServer": 9000},
+ "_rpcServer": {
+ "enabled": True,
+ "service": {
+ "type": "LoadBalancer",
+ "loadBalancerIP": "127.0.0.1",
+ "annotations": {"foo": "bar"},
+ "loadBalancerSourceRanges": ["10.123.0.0/16"],
+ },
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-service.yaml"],
+ )
+
+ assert {"foo": "bar"} == jmespath.search("metadata.annotations",
docs[0])
+ assert "LoadBalancer" == jmespath.search("spec.type", docs[0])
+ assert {"name": "rpc-server", "port": 9000} in
jmespath.search("spec.ports", docs[0])
+ assert "127.0.0.1" == jmespath.search("spec.loadBalancerIP", docs[0])
+ assert ["10.123.0.0/16"] ==
jmespath.search("spec.loadBalancerSourceRanges", docs[0])
+
+ @pytest.mark.parametrize(
+ "ports, expected_ports",
+ [
+ ([{"port": 8888}], [{"port": 8888}]), # name is optional with a
single port
+ (
+ [
+ {
+ "name": "{{ .Release.Name }}",
+ "protocol": "UDP",
+ "port": "{{ .Values.ports._rpcServer }}",
+ }
+ ],
+ [{"name": "release-name", "protocol": "UDP", "port": 9080}],
+ ),
+ ([{"name": "only_sidecar", "port": "{{ int 9000 }}"}], [{"name":
"only_sidecar", "port": 9000}]),
+ (
+ [
+ {"name": "rpc-server", "port": "{{
.Values.ports._rpcServer }}"},
+ {"name": "sidecar", "port": 80, "targetPort": "sidecar"},
+ ],
+ [
+ {"name": "rpc-server", "port": 9080},
+ {"name": "sidecar", "port": 80, "targetPort": "sidecar"},
+ ],
+ ),
+ ],
+ )
+ def test_ports_overrides(self, ports, expected_ports):
+ docs = render_chart(
+ values={
+ "_rpcServer": {"enabled": True, "service": {"ports": ports}},
+ },
+ show_only=["templates/rpc-server/rpc-server-service.yaml"],
+ )
+
+ assert jmespath.search("spec.ports", docs[0]) == expected_ports
+
+ def test_should_add_component_specific_labels(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "labels": {"test_label": "test_label_value"},
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-service.yaml"],
+ )
+ assert "test_label" in jmespath.search("metadata.labels", docs[0])
+ assert jmespath.search("metadata.labels", docs[0])["test_label"] ==
"test_label_value"
+
+ @pytest.mark.parametrize(
+ "ports, expected_ports",
+ [
+ (
+ [{"nodePort": "31000", "port": "8080"}],
+ [{"nodePort": 31000, "port": 8080}],
+ ),
+ (
+ [{"port": "8080"}],
+ [{"port": 8080}],
+ ),
+ ],
+ )
+ def test_nodeport_service(self, ports, expected_ports):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "service": {
+ "type": "NodePort",
+ "ports": ports,
+ },
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-service.yaml"],
+ )
+
+ assert "NodePort" == jmespath.search("spec.type", docs[0])
+ assert expected_ports == jmespath.search("spec.ports", docs[0])
+
+
+class TestRPCServerNetworkPolicy:
+ """Tests rpc-server network policy."""
+
+ def test_off_by_default(self):
+ docs = render_chart(
+ show_only=["templates/rpc-server/rpc-server-networkpolicy.yaml"],
+ )
+ assert 0 == len(docs)
+
+ def test_defaults(self):
+ docs = render_chart(
+ values={
+ "networkPolicies": {"enabled": True},
+ "_rpcServer": {
+ "enabled": True,
+ "networkPolicy": {
+ "ingress": {
+ "from": [{"namespaceSelector": {"matchLabels":
{"release": "myrelease"}}}]
+ }
+ },
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-networkpolicy.yaml"],
+ )
+
+ assert 1 == len(docs)
+ assert "NetworkPolicy" == docs[0]["kind"]
+ assert [{"namespaceSelector": {"matchLabels": {"release":
"myrelease"}}}] == jmespath.search(
+ "spec.ingress[0].from", docs[0]
+ )
+ assert jmespath.search("spec.ingress[0].ports", docs[0]) == [{"port":
9080}]
+
+ @pytest.mark.parametrize(
+ "ports, expected_ports",
+ [
+ ([{"port": "sidecar"}], [{"port": "sidecar"}]),
+ (
+ [
+ {"port": "{{ .Values.ports._rpcServer }}"},
+ {"port": 80},
+ ],
+ [
+ {"port": 9080},
+ {"port": 80},
+ ],
+ ),
+ ],
+ )
+ def test_ports_overrides(self, ports, expected_ports):
+ docs = render_chart(
+ values={
+ "networkPolicies": {"enabled": True},
+ "_rpcServer": {
+ "enabled": True,
+ "networkPolicy": {
+ "ingress": {
+ "from": [{"namespaceSelector": {"matchLabels":
{"release": "myrelease"}}}],
+ "ports": ports,
+ }
+ },
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-networkpolicy.yaml"],
+ )
+
+ assert expected_ports == jmespath.search("spec.ingress[0].ports",
docs[0])
+
+ def test_should_add_component_specific_labels(self):
+ docs = render_chart(
+ values={
+ "networkPolicies": {"enabled": True},
+ "_rpcServer": {
+ "enabled": True,
+ "labels": {"test_label": "test_label_value"},
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-networkpolicy.yaml"],
+ )
+ assert "test_label" in jmespath.search("metadata.labels", docs[0])
+ assert jmespath.search("metadata.labels", docs[0])["test_label"] ==
"test_label_value"
+
+
+class TestRPCServerServiceAccount:
+ """Tests rpc-server service account."""
+
+ def test_should_add_component_specific_labels(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "serviceAccount": {"create": True},
+ "labels": {"test_label": "test_label_value"},
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-serviceaccount.yaml"],
+ )
+ assert "test_label" in jmespath.search("metadata.labels", docs[0])
+ assert jmespath.search("metadata.labels", docs[0])["test_label"] ==
"test_label_value"
+
+ def test_default_automount_service_account_token(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "serviceAccount": {"create": True},
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-serviceaccount.yaml"],
+ )
+ assert jmespath.search("automountServiceAccountToken", docs[0]) is True
+
+ def test_overridden_automount_service_account_token(self):
+ docs = render_chart(
+ values={
+ "_rpcServer": {
+ "enabled": True,
+ "serviceAccount": {"create": True,
"automountServiceAccountToken": False},
+ },
+ },
+ show_only=["templates/rpc-server/rpc-server-serviceaccount.yaml"],
+ )
+ assert jmespath.search("automountServiceAccountToken", docs[0]) is
False
diff --git a/helm_tests/security/test_rbac.py b/helm_tests/security/test_rbac.py
index c305194c9e..75924684cd 100644
--- a/helm_tests/security/test_rbac.py
+++ b/helm_tests/security/test_rbac.py
@@ -34,6 +34,7 @@ DEPLOYMENT_NO_RBAC_NO_SA_KIND_NAME_TUPLES = [
("Service", "test-rbac-postgresql"),
("Service", "test-rbac-statsd"),
("Service", "test-rbac-webserver"),
+ ("Service", "test-rbac-rpc-server"),
("Service", "test-rbac-flower"),
("Service", "test-rbac-pgbouncer"),
("Service", "test-rbac-redis"),
@@ -41,6 +42,7 @@ DEPLOYMENT_NO_RBAC_NO_SA_KIND_NAME_TUPLES = [
("Deployment", "test-rbac-scheduler"),
("Deployment", "test-rbac-statsd"),
("Deployment", "test-rbac-webserver"),
+ ("Deployment", "test-rbac-rpc-server"),
("Deployment", "test-rbac-flower"),
("Deployment", "test-rbac-pgbouncer"),
("StatefulSet", "test-rbac-postgresql"),
@@ -68,6 +70,7 @@ SERVICE_ACCOUNT_NAME_TUPLES = [
("ServiceAccount", "test-rbac-cleanup"),
("ServiceAccount", "test-rbac-scheduler"),
("ServiceAccount", "test-rbac-webserver"),
+ ("ServiceAccount", "test-rbac-rpc-server"),
("ServiceAccount", "test-rbac-worker"),
("ServiceAccount", "test-rbac-triggerer"),
("ServiceAccount", "test-rbac-pgbouncer"),
@@ -79,31 +82,19 @@ SERVICE_ACCOUNT_NAME_TUPLES = [
]
CUSTOM_SERVICE_ACCOUNT_NAMES = (
- CUSTOM_SCHEDULER_NAME,
- CUSTOM_WEBSERVER_NAME,
- CUSTOM_WORKER_NAME,
- CUSTOM_TRIGGERER_NAME,
- CUSTOM_CLEANUP_NAME,
- CUSTOM_FLOWER_NAME,
- CUSTOM_PGBOUNCER_NAME,
- CUSTOM_STATSD_NAME,
- CUSTOM_CREATE_USER_JOBS_NAME,
- CUSTOM_MIGRATE_DATABASE_JOBS_NAME,
- CUSTOM_REDIS_NAME,
- CUSTOM_POSTGRESQL_NAME,
-) = (
- "TestScheduler",
- "TestWebserver",
- "TestWorker",
- "TestTriggerer",
- "TestCleanup",
- "TestFlower",
- "TestPGBouncer",
- "TestStatsd",
- "TestCreateUserJob",
- "TestMigrateDatabaseJob",
- "TestRedis",
- "TestPostgresql",
+ (CUSTOM_SCHEDULER_NAME := "TestScheduler"),
+ (CUSTOM_WEBSERVER_NAME := "TestWebserver"),
+ (CUSTOM_RPC_SERVER_NAME := "TestRPCSserver"),
+ (CUSTOM_WORKER_NAME := "TestWorker"),
+ (CUSTOM_TRIGGERER_NAME := "TestTriggerer"),
+ (CUSTOM_CLEANUP_NAME := "TestCleanup"),
+ (CUSTOM_FLOWER_NAME := "TestFlower"),
+ (CUSTOM_PGBOUNCER_NAME := "TestPGBouncer"),
+ (CUSTOM_STATSD_NAME := "TestStatsd"),
+ (CUSTOM_CREATE_USER_JOBS_NAME := "TestCreateUserJob"),
+ (CUSTOM_MIGRATE_DATABASE_JOBS_NAME := "TestMigrateDatabaseJob"),
+ (CUSTOM_REDIS_NAME := "TestRedis"),
+ (CUSTOM_POSTGRESQL_NAME := "TestPostgresql"),
)
@@ -150,6 +141,7 @@ class TestRBAC:
"redis": {"serviceAccount": {"create": False}},
"scheduler": {"serviceAccount": {"create": False}},
"webserver": {"serviceAccount": {"create": False}},
+ "_rpcServer": {"enabled": True, "serviceAccount":
{"create": False}},
"workers": {"serviceAccount": {"create": False}},
"triggerer": {"serviceAccount": {"create": False}},
"statsd": {"serviceAccount": {"create": False}},
@@ -176,6 +168,7 @@ class TestRBAC:
"cleanup": {"enabled": True},
"flower": {"enabled": True},
"pgbouncer": {"enabled": True},
+ "_rpcServer": {"enabled": True},
},
version=version,
),
@@ -201,6 +194,7 @@ class TestRBAC:
},
"scheduler": {"serviceAccount": {"create": False}},
"webserver": {"serviceAccount": {"create": False}},
+ "_rpcServer": {"enabled": True, "serviceAccount":
{"create": False}},
"workers": {"serviceAccount": {"create": False}},
"triggerer": {"serviceAccount": {"create": False}},
"flower": {"enabled": True, "serviceAccount": {"create":
False}},
@@ -234,6 +228,7 @@ class TestRBAC:
"cleanup": {"enabled": True},
"flower": {"enabled": True},
"pgbouncer": {"enabled": True},
+ "_rpcServer": {"enabled": True},
},
version=version,
),
@@ -259,6 +254,7 @@ class TestRBAC:
},
"scheduler": {"serviceAccount": {"name":
CUSTOM_SCHEDULER_NAME}},
"webserver": {"serviceAccount": {"name":
CUSTOM_WEBSERVER_NAME}},
+ "_rpcServer": {"enabled": True, "serviceAccount": {"name":
CUSTOM_RPC_SERVER_NAME}},
"workers": {"serviceAccount": {"name": CUSTOM_WORKER_NAME}},
"triggerer": {"serviceAccount": {"name":
CUSTOM_TRIGGERER_NAME}},
"flower": {"enabled": True, "serviceAccount": {"name":
CUSTOM_FLOWER_NAME}},
@@ -295,6 +291,7 @@ class TestRBAC:
},
"scheduler": {"serviceAccount": {"name":
CUSTOM_SCHEDULER_NAME}},
"webserver": {"serviceAccount": {"name":
CUSTOM_WEBSERVER_NAME}},
+ "_rpcServer": {"enabled": True, "serviceAccount": {"name":
CUSTOM_RPC_SERVER_NAME}},
"workers": {"serviceAccount": {"name": CUSTOM_WORKER_NAME}},
"triggerer": {"serviceAccount": {"name":
CUSTOM_TRIGGERER_NAME}},
"flower": {"enabled": True, "serviceAccount": {"name":
CUSTOM_FLOWER_NAME}},
@@ -337,6 +334,7 @@ class TestRBAC:
"redis": {"enabled": False},
"flower": {"enabled": False},
"statsd": {"enabled": False},
+ "_rpcServer": {"enabled": False},
"webserver": {"defaultUser": {"enabled": False}},
},
)