This is an automated email from the ASF dual-hosted git repository. potiuk pushed a commit to branch v1-10-test in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 21f792ffb2c7ae041d766853d1aa5fe7191d4fe4 Author: Kamil BreguĊa <[email protected]> AuthorDate: Sat Oct 31 18:51:36 2020 +0100 All k8s object must comply with JSON Schema (#12003) * All k8s resources should have global labels * All k8s object must comply with JSON Schema (cherry picked from commit 3c85c2c16e845fa7eafaadf7a7598428e4022c6f) --- .pre-commit-config.yaml | 4 +- chart/files/pod-template-file.kubernetes-helm-yaml | 9 +--- chart/templates/redis/redis-statefulset.yaml | 4 +- .../scheduler/scheduler-serviceaccount.yaml | 6 +-- chart/templates/secrets/redis-secrets.yaml | 3 ++ chart/templates/webserver/webserver-ingress.yaml | 63 ++++++++++++++++++++++ chart/templates/webserver/webserver-service.yaml | 4 ++ .../webserver/webserver-serviceaccount.yaml | 10 ++-- chart/templates/workers/worker-serviceaccount.yaml | 12 ++--- chart/tests/helm_template_generator.py | 37 +++++++++++++ chart/tests/test_basic_helm_chart.py | 22 +++++++- 11 files changed, 146 insertions(+), 28 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4dbf84c..2e27e50 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -272,14 +272,14 @@ repos: ^\s*def\s*\S*\([^:#)]*:.*| # Matches function param with Python3 type ^\sdef\s*\S*\(.*\):\s*\-\>\s*\S*.* # Matches -> return value syntax from Python3 )$ - exclude: ^dev|^scripts|^docs + exclude: ^dev|^scripts|^docs|^chart pass_filenames: true - id: python2-compile name: Compile code using python2 language: system entry: python2.7 -m py_compile files: \.py$ - exclude: ^dev|^scripts|^docs + exclude: ^dev|^scripts|^docs|^chart pass_filenames: true require_serial: true - id: incorrect-use-of-LoggingMixin diff --git a/chart/files/pod-template-file.kubernetes-helm-yaml b/chart/files/pod-template-file.kubernetes-helm-yaml index ed21d68..8647060 100644 --- a/chart/files/pod-template-file.kubernetes-helm-yaml +++ b/chart/files/pod-template-file.kubernetes-helm-yaml @@ -40,12 +40,7 @@ spec: volumeMounts: - mountPath: {{ template "airflow_logs" . }} name: airflow-logs -{{- if or .Values.dags.persistence.enabled .Values.dags.gitSync.enabled }} - - mountPath: {{ template "airflow_dags_mount_path" . }} - name: airflow-dags - readOnly: false -{{- end }} -{{- if .Values.dags.gitSync.sshKeySecret }} +{{- if .Values.dags.gitSync.knownHosts }} - mountPath: /etc/git-secret/known_hosts name: {{ .Values.dags.gitSync.knownHosts }} subPath: known_hosts @@ -77,7 +72,7 @@ spec: {{ toYaml .Values.affinity | indent 8 }} tolerations: {{ toYaml .Values.tolerations | indent 8 }} - serviceAccountName: '{{ .Release.Name }}-worker-serviceaccount' + serviceAccountName: '{{ .Release.Name }}-worker' volumes: {{- if .Values.dags.persistence.enabled }} - name: dags diff --git a/chart/templates/redis/redis-statefulset.yaml b/chart/templates/redis/redis-statefulset.yaml index 6df78b4..ca47d97 100644 --- a/chart/templates/redis/redis-statefulset.yaml +++ b/chart/templates/redis/redis-statefulset.yaml @@ -48,10 +48,10 @@ spec: {{- with .Values.labels }} {{ toYaml . | indent 8 }} {{- end }} + {{- if .Values.redis.safeToEvict }} annotations: - {{- if .Values.redis.safeToEvict }} cluster-autoscaler.kubernetes.io/safe-to-evict: "true" - {{- end }} + {{- end }} spec: nodeSelector: {{ toYaml .Values.nodeSelector | indent 8 }} diff --git a/chart/templates/scheduler/scheduler-serviceaccount.yaml b/chart/templates/scheduler/scheduler-serviceaccount.yaml index 5dfa1dc..f12991d 100644 --- a/chart/templates/scheduler/scheduler-serviceaccount.yaml +++ b/chart/templates/scheduler/scheduler-serviceaccount.yaml @@ -28,13 +28,13 @@ metadata: release: {{ .Release.Name }} chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" heritage: {{ .Release.Service }} + {{- with .Values.labels }} + {{ toYaml . | nindent 4 }} + {{- end }} {{- with .Values.scheduler.serviceAccountAnnotations }} annotations: {{- range $key, $value := . }} {{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 4 }} {{- end }} {{- end }} -{{- with .Values.labels }} -{{ toYaml . | indent 4 }} -{{- end }} {{- end }} diff --git a/chart/templates/secrets/redis-secrets.yaml b/chart/templates/secrets/redis-secrets.yaml index 7c9fe26..958474d 100644 --- a/chart/templates/secrets/redis-secrets.yaml +++ b/chart/templates/secrets/redis-secrets.yaml @@ -51,6 +51,9 @@ metadata: release: {{ .Release.Name }} chart: {{ .Chart.Name }} heritage: {{ .Release.Service }} + {{- with .Values.labels }} + {{ toYaml . | nindent 4 }} + {{- end }} annotations: "helm.sh/hook": "pre-install" "helm.sh/hook-delete-policy": "before-hook-creation" diff --git a/chart/templates/webserver/webserver-ingress.yaml b/chart/templates/webserver/webserver-ingress.yaml new file mode 100644 index 0000000..c249cb2 --- /dev/null +++ b/chart/templates/webserver/webserver-ingress.yaml @@ -0,0 +1,63 @@ +# 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 Webserver Ingress +################################# +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: {{ .Release.Name }}-airflow-ingress + labels: + tier: airflow + component: airflow-ingress + release: {{ .Release.Name }} + chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + heritage: {{ .Release.Service }} + {{- with .Values.ingress.web.annotations }} + annotations: + {{ toYaml . | indent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.web.tls.enabled }} + tls: + - hosts: + - {{ .Values.ingress.web.host }} + secretName: {{ .Values.ingress.web.tls.secretName }} + {{- end }} + rules: + - http: + paths: + {{- range .Values.ingress.web.precedingPaths }} + - path: {{ .path }} + backend: + serviceName: {{ .serviceName }} + servicePort: {{ .servicePort }} + {{- end }} + - path: {{ .Values.ingress.web.path }} + backend: + serviceName: {{ .Release.Name }}-webserver + servicePort: airflow-ui + {{- range .Values.ingress.web.succeedingPaths }} + - path: {{ .path }} + backend: + serviceName: {{ .serviceName }} + servicePort: {{ .servicePort }} + {{- end }} + host: {{ .Values.ingress.web.host }} +{{- end }} diff --git a/chart/templates/webserver/webserver-service.yaml b/chart/templates/webserver/webserver-service.yaml index 77f5995..feae23d 100644 --- a/chart/templates/webserver/webserver-service.yaml +++ b/chart/templates/webserver/webserver-service.yaml @@ -31,6 +31,10 @@ metadata: {{- with .Values.labels }} {{ toYaml . | indent 4 }} {{- end }} +{{- with .Values.webserver.service.annotations }} + annotations: +{{- toYaml . | nindent 4 }} +{{- end }} spec: type: {{ .Values.webserver.service.type }} selector: diff --git a/chart/templates/webserver/webserver-serviceaccount.yaml b/chart/templates/webserver/webserver-serviceaccount.yaml index ba99cea..e42d767 100644 --- a/chart/templates/webserver/webserver-serviceaccount.yaml +++ b/chart/templates/webserver/webserver-serviceaccount.yaml @@ -27,12 +27,10 @@ metadata: release: {{ .Release.Name }} chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" heritage: {{ .Release.Service }} + {{- with .Values.labels }} + {{ toYaml . | nindent 4 }} + {{- end }} {{- with .Values.webserver.serviceAccountAnnotations }} annotations: - {{- range $key, $value := . }} - {{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 4 }} - {{- end }} + {{ toYaml . | nindent 4 }} {{- end }} -{{- with .Values.labels }} -{{ toYaml . | indent 4 }} -{{- end }} diff --git a/chart/templates/workers/worker-serviceaccount.yaml b/chart/templates/workers/worker-serviceaccount.yaml index 4a7542e..f87751b 100644 --- a/chart/templates/workers/worker-serviceaccount.yaml +++ b/chart/templates/workers/worker-serviceaccount.yaml @@ -28,13 +28,11 @@ metadata: release: {{ .Release.Name }} chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" heritage: {{ .Release.Service }} - {{- with .Values.workers.serviceAccountAnnotations }} + {{- with .Values.labels }} + {{ toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.workers.serviceAccountAnnotations}} annotations: - {{- range $key, $value := . }} - {{- printf "%s: %s" $key (tpl $value $ | quote) | nindent 4 }} - {{- end }} + {{ toYaml . | nindent 4 }} {{- end }} - {{- with .Values.labels }} -{{ toYaml . | indent 4 }} -{{- end }} {{- end }} diff --git a/chart/tests/helm_template_generator.py b/chart/tests/helm_template_generator.py index dccbb1e..ba870ed 100644 --- a/chart/tests/helm_template_generator.py +++ b/chart/tests/helm_template_generator.py @@ -17,13 +17,48 @@ import subprocess import sys +from functools import lru_cache from tempfile import NamedTemporaryFile +import jmespath +import jsonschema +import requests import yaml from kubernetes.client.api_client import ApiClient api_client = ApiClient() +BASE_URL_SPEC = "https://raw.githubusercontent.com/instrumenta/kubernetes-json-schema/master/v1.12.9" + + +@lru_cache(maxsize=None) +def create_validator(api_version, kind): + api_version = api_version.lower() + kind = kind.lower() + + if '/' in api_version: + ext, _, api_version = api_version.partition("/") + ext = ext.split(".")[0] + url = f'{BASE_URL_SPEC}/{kind}-{ext}-{api_version}.json' + else: + url = f'{BASE_URL_SPEC}/{kind}-{api_version}.json' + request = requests.get(url) + request.raise_for_status() + schema = request.json() + jsonschema.Draft7Validator.check_schema(schema) + validator = jsonschema.Draft7Validator(schema) + return validator + + +def validate_k8s_object(instance): + # Skip PostgresSQL chart + chart = jmespath.search("metadata.labels.chart", instance) + if chart and 'postgresql' in chart: + return + + validate = create_validator(instance.get("apiVersion"), instance.get("kind")) + validate.validate(instance) + def render_chart(name="RELEASE-NAME", values=None, show_only=None): """ @@ -41,6 +76,8 @@ def render_chart(name="RELEASE-NAME", values=None, show_only=None): templates = subprocess.check_output(command) k8s_objects = yaml.load_all(templates) k8s_objects = [k8s_object for k8s_object in k8s_objects if k8s_object] # type: ignore + for k8s_object in k8s_objects: + validate_k8s_object(k8s_object) return k8s_objects diff --git a/chart/tests/test_basic_helm_chart.py b/chart/tests/test_basic_helm_chart.py index e535ac9..767a073 100644 --- a/chart/tests/test_basic_helm_chart.py +++ b/chart/tests/test_basic_helm_chart.py @@ -17,6 +17,8 @@ import unittest +import jmespath + from tests.helm_template_generator import render_chart OBJECT_COUNT_IN_BASIC_DEPLOYMENT = 22 @@ -24,7 +26,15 @@ OBJECT_COUNT_IN_BASIC_DEPLOYMENT = 22 class TestBaseChartTest(unittest.TestCase): def test_basic_deployments(self): - k8s_objects = render_chart("TEST-BASIC", {"chart": {'metadata': 'AA'}}) + k8s_objects = render_chart( + "TEST-BASIC", + values={ + "chart": { + 'metadata': 'AA', + }, + 'labels': {"TEST-LABEL": "TEST-VALUE"}, + }, + ) list_of_kind_names_tuples = [ (k8s_object['kind'], k8s_object['metadata']['name']) for k8s_object in k8s_objects ] @@ -56,6 +66,16 @@ class TestBaseChartTest(unittest.TestCase): ], ) self.assertEqual(OBJECT_COUNT_IN_BASIC_DEPLOYMENT, len(k8s_objects)) + for k8s_object in k8s_objects: + labels = jmespath.search('metadata.labels', k8s_object) or {} + if 'postgresql' in labels.get('chart'): + continue + k8s_name = k8s_object['kind'] + ":" + k8s_object['metadata']['name'] + self.assertEqual( + 'TEST-VALUE', + labels.get("TEST-LABEL"), + f"Missing label TEST-LABEL on {k8s_name}. Current labels: {labels}", + ) def test_basic_deployment_without_default_users(self): k8s_objects = render_chart("TEST-BASIC", {"webserver": {'defaultUser': {'enabled': False}}})
