diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index d3483bb1..4ffd221d 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -6,6 +6,7 @@ on: pull_request: branches: - main + workflow_dispatch: env: commit_msg: ${{ github.event.head_commit.message }} diff --git a/.gitignore b/.gitignore index 53376089..d6918da7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +values.schema.json docs/build/ .coverage htmlcov/ diff --git a/continuous_integration/kubernetes/helm-install.sh b/continuous_integration/kubernetes/helm-install.sh index 89734d80..4bb77a65 100755 --- a/continuous_integration/kubernetes/helm-install.sh +++ b/continuous_integration/kubernetes/helm-install.sh @@ -22,6 +22,9 @@ echo "DASK_GATEWAY_SERVER_IMAGE: $DASK_GATEWAY_SERVER_IMAGE" k3d image import $DASK_GATEWAY_IMAGE $DASK_GATEWAY_SERVER_IMAGE k3d image import $DASK_GATEWAY_SERVER_IMAGE +echo "Generating Helm Chart's values.schema.json" +"${git_root}/resources/helm/tools/generate-json-schema.py" + echo "Installing Helm Chart" helm install \ test-dask-gateway \ diff --git a/continuous_integration/kubernetes/helm-lint-and-template.sh b/continuous_integration/kubernetes/helm-lint-and-template.sh index 39a59567..9933f5e0 100755 --- a/continuous_integration/kubernetes/helm-lint-and-template.sh +++ b/continuous_integration/kubernetes/helm-lint-and-template.sh @@ -4,6 +4,9 @@ set -e this_dir="$(dirname "${BASH_SOURCE[0]}")" git_root="$(cd "${this_dir}/../.." && pwd)" +echo "Generating Helm Chart's values.schema.json" +"${git_root}/resources/helm/tools/generate-json-schema.py" + echo "Linting Helm Chart" helm lint \ "${git_root}/resources/helm/dask-gateway" \ diff --git a/resources/helm/dask-gateway/templates/traefik/deployment.yaml b/resources/helm/dask-gateway/templates/traefik/deployment.yaml index 4c8c74f6..ac3ddaf5 100644 --- a/resources/helm/dask-gateway/templates/traefik/deployment.yaml +++ b/resources/helm/dask-gateway/templates/traefik/deployment.yaml @@ -31,6 +31,7 @@ spec: containers: - name: traefik image: {{ .Values.traefik.image.name }}:{{ .Values.traefik.image.tag }} + imagePullPolicy: {{ .Values.gateway.image.pullPolicy }} securityContext: runAsUser: 1000 runAsGroup: 1000 diff --git a/resources/helm/dask-gateway/values.schema.yaml b/resources/helm/dask-gateway/values.schema.yaml new file mode 100644 index 00000000..b3f4917e --- /dev/null +++ b/resources/helm/dask-gateway/values.schema.yaml @@ -0,0 +1,572 @@ +# This is a YAML representation of a values.schema.json file that can be +# packaged with a Helm chart so that users' chart config can be validated when +# they use it with `helm template`, `helm install`, or `helm upgrade`. +# +# To generate a values.schema.json from this YAML file, run the +# tools/generate-json-schema.py script. Do this before running tests and +# packaging this Helm chart! +# +$schema": http://json-schema.org/draft-07/schema# +type: object +additionalProperties: false +required: + - gateway + - controller + - traefik + - rbac + - global +properties: + gateway: + type: object + additionalProperties: false + required: + - replicas + - image + - imagePullSecrets + - tolerations + - prefix + - loglevel + - service + - auth + - backend + description: | + `gateway` nested config relates to the `api` Pod and the + `dask-gateway-server` running within it, the k8s Service exposing it, as + well as the schedulers (gateway.backend.scheduler) and workers + (gateway.backend.worker) created by the controller when a DaskCluster k8s + resource is registered. + properties: + replicas: &replicas-spec + type: integer + image: + type: object + additionalProperties: false + required: [name, tag] + description: | + Set custom image name, tag, pullPolicy, or pullSecrets for the + `dask-gateway-server` running in the `api` pod. + properties: &image-properties + name: + type: string + description: | + The name of the image, without the tag. + + ``` + # example name + gcr.io/my-project/my-image + ``` + tag: + type: string + description: | + The tag of the image to pull. This is the value following `:` in + complete image specifications. + + ``` + # example tags + v1.11.1 + zhy270a + ``` + pullPolicy: + enum: [null, "", IfNotPresent, Always, Never] + description: | + Configures the Pod's `spec.imagePullPolicy`. + + See the [Kubernetes + docs](https://kubernetes.io/docs/concepts/containers/images/#updating-images) + for more info. + imagePullSecrets: &imagePullSecrets-spec + type: array + description: | + A list of references to existing k8s Secrets with credentials to pull + the image. + items: + type: object + additionalProperties: false + required: [name] + properties: + name: + type: string + annotations: &labels-and-annotations-spec + type: object + additionalProperties: false + patternProperties: &labels-and-annotations-patternProperties + ".*": + type: string + description: | + Annotations to apply to the associated resource. + + See [the Kubernetes + documentation](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) + for more details about annotations. + resources: &resources-spec + type: object + additionalProperties: true + description: | + A k8s native specification of resources, see [the + documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#resourcerequirements-v1-core). + livenessProbe: &probe-spec + type: object + additionalProperties: true + required: [enabled] + description: | + This config option is exactly like the k8s native specification of a + container probe, except that it also supports an `enabled` boolean + flag. + + See [the k8s + documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#probe-v1-core) + for more details. + properties: + enabled: + type: boolean + readinessProbe: *probe-spec + nodeSelector: &nodeSelector-spec + type: object + additionalProperties: false + patternProperties: *labels-and-annotations-patternProperties + description: | + Node selector labels require a pod to be scheduled on nodes with + matching labels. + + See [the Kubernetes + documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) + for more details. + affinity: &affinity-spec + type: object + additionalProperties: true + description: | + See the [Kubernetes + docs](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) + for more info. + tolerations: &tolerations-spec + type: array + description: | + Tolerations allow a pod to be scheduled on nodes with taints. + + Pass this field an array of + [`Toleration`](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core) + objects. + + See the [Kubernetes + docs](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) + for more info. + extraConfig: + type: [object, string] + additionalProperties: *labels-and-annotations-patternProperties + description: | + Any extra configuration code to append to the generated + `dask_gateway_config.py` file. Can be either a single code-block, or a + map of key -> code-block (code-blocks are run in alphabetical order by + key, the key value itself is meaningless). The map version is useful + as it supports merging multiple `values.yaml` files, but is + unnecessary in other cases. + prefix: + type: string + description: | + Path prefix to serve dask-gateway api requests under. This prefix will + loglevel: &loglevel-spec + enum: [ERROR, WARN, INFO, DEBUG] + + service: + type: object + additionalProperties: false + description: | + Configuration related to the k8s Service exposing the + `dask-gateway-server` running in the `api` pod. + properties: + annotations: *labels-and-annotations-spec + + auth: + type: object + additionalProperties: false + required: + - type + - simple + - kerberos + - jupyterhub + - custom + properties: + type: + enum: [simple, kerberos, jupyterhub, custom] + description: | + The auth type to use. + simple: + type: object + additionalProperties: false + properties: + password: + type: [string, "null"] + description: | + A shared password to use for all users. + kerberos: + type: object + additionalProperties: false + properties: + keytab: + type: [string, "null"] + description: | + Path to the HTTP keytab for this node. + jupyterhub: + type: object + additionalProperties: false + properties: + apiToken: + type: [string, "null"] + description: | + A JupyterHub api token for dask-gateway to use. See + https://gateway.dask.org/install-kube.html#authenticating-with-jupyterhub. + apiUrl: + type: [string, "null"] + description: | + JupyterHub's api url. Inferred from JupyterHub's service name if + running in the same namespace. + custom: + type: object + additionalProperties: false + properties: + class: + type: [string, "null"] + description: | + The full authenticator class name. + config: + type: object + additionalProperties: true + description: | + Configuration fields to set on the authenticator class. + + backend: + type: object + additionalProperties: false + required: + - image + - scheduler + - worker + description: | + `backend` nested configuration relates to the scheduler and worker + resources created for DaskCluster k8s resources by the controller. + properties: + image: + type: object + additionalProperties: false + required: [name, tag] + description: | + Set a custom image name, tag, pullPolicy, or pullSecrets for Dask + Cluster's scheduler and worker pods. + properties: *image-properties + namespace: + type: [string, "null"] + description: | + The namespace to launch dask clusters in. If not specified, + defaults to the same namespace the gateway is running in. + environment: + type: object + additionalProperties: false + description: | + A mapping of environment variables to set for both schedulers and + workers. + patternProperties: + ".*": + type: string + + scheduler: + type: object + additionalProperties: false + required: + - cores + - memory + properties: + extraPodConfig: + type: object + additionalProperties: true + description: | + Any extra configuration for the scheduler pod. Sets + `c.KubeClusterConfig.scheduler_extra_pod_config`. + extraContainerConfig: + type: object + additionalProperties: true + description: | + Any extra configuration for the scheduler container. Sets + `c.KubeClusterConfig.scheduler_extra_container_config`. + cores: + type: object + additionalProperties: false + description: | + Cores request/limit for the scheduler. + properties: + request: + type: [string, number, "null"] + limit: + type: [string, number, "null"] + memory: + type: object + additionalProperties: false + description: | + Memory request/limit for the scheduler. + properties: + request: + type: [string, number, "null"] + limit: + type: [string, number, "null"] + + worker: + type: object + additionalProperties: false + required: + - cores + - memory + - threads + properties: + extraPodConfig: + type: object + additionalProperties: true + description: | + Any extra configuration for the worker pod. Sets + `c.KubeClusterConfig.worker_extra_pod_config`. + extraContainerConfig: + type: object + additionalProperties: true + description: | + Any extra configuration for the worker container. Sets + `c.KubeClusterConfig.worker_extra_container_config`. + cores: + type: object + additionalProperties: false + description: | + Cores request/limit for each worker. + properties: + request: + type: [string, number, "null"] + limit: + type: [string, number, "null"] + memory: + type: object + additionalProperties: false + description: | + Memory request/limit for each worker. + properties: + request: + type: [string, number, "null"] + limit: + type: [string, number, "null"] + threads: + type: integer + description: | + Number of threads available for a worker + + + + controller: + type: object + additionalProperties: false + required: + - enabled + - image + - imagePullSecrets + - tolerations + - loglevel + - completedClusterMaxAge + - completedClusterCleanupPeriod + - backoffBaseDelay + - backoffMaxDelay + - k8sApiRateLimit + - k8sApiRateLimitBurst + description: | + `controller` nested config relates to the `controller` Pod and the + `dask-gateway-server` running within it that makes things happen when + changes to DaskCluster k8s resources are observed. + properties: + enabled: + type: boolean + description: | + Whether the controller should be deployed. Disabling the controller + allows running it locally for development/debugging purposes. + image: + type: object + additionalProperties: false + required: [name, tag] + description: | + Set custom image name, tag, pullPolicy, or pullSecrets for the + `dask-gateway-server` running in the `controller` pod. + properties: *image-properties + imagePullSecrets: *imagePullSecrets-spec + annotations: *labels-and-annotations-spec + resources: *resources-spec + nodeSelector: *nodeSelector-spec + affinity: *affinity-spec + tolerations: *tolerations-spec + loglevel: *loglevel-spec + completedClusterMaxAge: + type: number + description: | + Max time (in seconds) to keep around records of completed clusters. + Default is 24 hours. + completedClusterCleanupPeriod: + type: number + description: | + Time (in seconds) between cleanup tasks removing records of completed + clusters. Default is 5 minutes. + backoffBaseDelay: + type: number + description: | + Base delay (in seconds) for backoff when retrying after failures. + backoffMaxDelay: + type: number + description: | + Max delay (in seconds) for backoff when retrying after failures. + k8sApiRateLimit: + type: number + description: | + Limit on the average number of k8s api calls per second. + k8sApiRateLimitBurst: + type: number + description: | + Limit on the maximum number of k8s api calls per second. + + + + traefik: + type: object + additionalProperties: false + required: + - replicas + - image + - tolerations + - loglevel + - dashboard + - service + description: | + `traefik` nested config relates to the `traefik` Pod and Traefik running + within it that is acting as a proxy for traffic towards the gateway or + user created DaskCluster resources. + properties: + replicas: *replicas-spec + image: + type: object + additionalProperties: false + required: [name, tag] + description: | + Set custom image name, tag, pullPolicy, or pullSecrets for Traefik + running in the `traefik` pod. + properties: *image-properties + annotations: *labels-and-annotations-spec + resources: *resources-spec + nodeSelector: *nodeSelector-spec + affinity: *affinity-spec + tolerations: *tolerations-spec + additionalArguments: + type: array + description: | + Any additional command line arguments to pass to traefik on startup. + loglevel: *loglevel-spec + dashboard: + type: boolean + description: | + Whether to expose the dashboard on port 9000 (enable for debugging only!) + + service: + type: object + additionalProperties: false + required: + - type + - ports + description: | + Configuration related to the k8s Service exposing Traefik running in the + `traefik` pod. + properties: + type: + type: [string, "null"] + annotations: *labels-and-annotations-spec + spec: + type: object + additionalProperties: true + description: | + Additional k8s native configuration to put under the k8s Service's + spec field. + ports: + type: object + additionalProperties: false + required: + - web + - tcp + properties: + web: + type: object + additionalProperties: false + required: + - port + properties: + port: + type: [number] + description: | + The port HTTP(s) requests will be served on + nodePort: + type: [number, "null"] + tcp: + type: object + additionalProperties: false + required: + - port + properties: + port: + type: [string, number] + description: | + The port TCP requests will be served on. Set to `web` to + share the web service port. + nodePort: + type: [number, "null"] + + + + rbac: + type: object + additionalProperties: false + required: + - controller + - gateway + - traefik + description: | + `rbac` nested configuration relates to the choice of creating or replacing + resources like (Cluster)Role, (Cluster)RoleBinding, and ServiceAccount. + properties: + enabled: + type: boolean + description: | + Whether to create RBAC resources. + controller: + type: object + additionalProperties: false + properties: + serviceAccountName: &serviceAccountName-spec + type: [string, "null"] + description: | + Existing k8s ServiceAccount name to reference if ClusterRoles, + ClusterRoleBindings, and ServiceAccounts have already been created + by other means. + + Leave this set to `null` to create all required resources. + gateway: + type: object + additionalProperties: false + properties: + serviceAccountName: *serviceAccountName-spec + traefik: + type: object + additionalProperties: false + properties: + serviceAccountName: *serviceAccountName-spec + + + + global: + type: object + additionalProperties: true + + + + enabled: + type: [boolean, "null"] + description: | + This Helm chart will ignore this setting, but it can be useful to allow + this configuration option for a Helm chart that declares this Helm chart + as a conditional dependency to install. Then, this value can be that + condition. diff --git a/resources/helm/dask-gateway/values.yaml b/resources/helm/dask-gateway/values.yaml index b965e566..687ca0d2 100644 --- a/resources/helm/dask-gateway/values.yaml +++ b/resources/helm/dask-gateway/values.yaml @@ -1,3 +1,7 @@ +# gateway nested config relates to the api Pod and the dask-gateway-server +# running within it, the k8s Service exposing it, as well as the schedulers +# (gateway.backend.scheduler) and workers gateway.backend.worker) created by the +# controller when a DaskCluster k8s resource is registered. gateway: # Number of instances of the gateway-server to run replicas: 1 @@ -55,7 +59,7 @@ gateway: class: null # Configuration fields to set on the authenticator class. - options: {} + config: {} livenessProbe: # Enables the livenessProbe. @@ -74,6 +78,8 @@ gateway: periodSeconds: 10 failureThreshold: 3 + # backend nested configuration relates to the scheduler and worker resources + # created for DaskCluster k8s resources by the controller. backend: # The image to use for both schedulers and workers. image: @@ -86,7 +92,7 @@ gateway: namespace: null # A mapping of environment variables to set for both schedulers and workers. - environment: null + environment: {} scheduler: # Any extra configuration for the scheduler pod. Sets @@ -141,7 +147,11 @@ gateway: # `values.yaml` files, but is unnecessary in other cases. extraConfig: {} -# Configuration for the gateway controller + + +# controller nested config relates to the controller Pod and the +# dask-gateway-server running within it that makes things happen when changes to +# DaskCluster k8s resources are observed. controller: # Whether the controller should be deployed. Disabling the controller allows # running it locally for development/debugging purposes. @@ -190,7 +200,11 @@ controller: affinity: {} tolerations: [] -# Configuration for the traefik proxy + + +# traefik nested config relates to the traefik Pod and Traefik running within it +# that is acting as a proxy for traffic towards the gateway or user created +# DaskCluster resources. traefik: # Number of instances of the proxy to run replicas: 1 @@ -205,6 +219,7 @@ traefik: image: name: traefik tag: 2.1.3 + pullPolicy: IfNotPresent # Any additional arguments to forward to traefik additionalArguments: [] @@ -236,6 +251,10 @@ traefik: affinity: {} tolerations: [] + + +# rbac nested configuration relates to the choice of creating or replacing +# resources like (Cluster)Role, (Cluster)RoleBinding, and ServiceAccount. rbac: # Whether to enable RBAC. enabled: true @@ -251,3 +270,11 @@ rbac: traefik: serviceAccountName: null + + + +# global nested configuration is accessible by all Helm charts that may depend +# on each other, but not used by this Helm chart. An entry is created here to +# validate its use and catch YAML typos via this configurations associated JSON +# schema. +global: {} diff --git a/resources/helm/tools/compare-values-schema-content.py b/resources/helm/tools/compare-values-schema-content.py new file mode 100755 index 00000000..95c4ad3c --- /dev/null +++ b/resources/helm/tools/compare-values-schema-content.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +""" +This script is meant to assist in a manual validation that the content of +values.schema.yaml covers values.yaml, and vice versa. + +FIXME: It would be nice to run this as part of our CI pipeline to report if + values.schema.yaml and values.yaml gets out of sync, but first we need to + address what it means to be out of sync. + + Consider if values.schema.yaml describes extraLabels, and we in this helm chart + have an extra label set in values, how should our comparison realize that + its nothing to bother about? + + That kind of complexity is an issue for labels, resources, + containerSecurityContext, readiness- and livenessProbe's for example. + +This script originated from the jupyterhub/zero-to-jupyterhub-k8s project. It is +not yet extracted to be a standalone package, but may be in the future. +""" + +import os + +from collections.abc import MutableMapping + +import yaml + +here_dir = os.path.abspath(os.path.dirname(__file__)) +schema_yaml = os.path.join(here_dir, os.pardir, "dask-gateway", "values.schema.yaml") +values_yaml = os.path.join(here_dir, os.pardir, "dask-gateway", "values.yaml") +lint_and_validate_values_yaml = os.path.join( + here_dir, os.pardir, "testing", "chart-install-values.yaml" +) + + +def reduce_schema(d): + """ + Takes a jsonschema loaded as a dictionary and return a reduced structure + ignoring everything apart from the structure it describes. + """ + r = {} + CONTAINS_KEYS = "properties" + if CONTAINS_KEYS in d: + for k, v in d[CONTAINS_KEYS].items(): + if isinstance(v, MutableMapping) and v.get(CONTAINS_KEYS): + r[k] = reduce_schema(v) + else: + r[k] = None + return r + + +def flatten(d, parent_key="", sep="."): + """ + Takes a nested dictionary and return all keys flattened using a separator, + so one element returned would for example be "gateway.image.tag". + """ + items = [] + for k, v in d.items(): + new_key = parent_key + sep + k if parent_key else k + if isinstance(v, MutableMapping): + if v: + items.extend(flatten(v, parent_key=new_key, sep=sep)) + else: + items.append(new_key) + else: + items.append(new_key) + if not parent_key: + return set(items) + else: + return items + + +def run(): + # Using these sets, we can validate further manually by printing the results + # of set operations. + with open(schema_yaml) as f: + schema = yaml.safe_load(f) + with open(values_yaml) as f: + values = yaml.safe_load(f) + # with open(lint_and_validate_values_yaml) as f: + # lint_and_validate_values = yaml.safe_load(f) + + schema = flatten(reduce_schema(schema)) + values = flatten(values) + # lint_and_validate_values = flatten(lint_and_validate_values) + + print( + "The keys from values.yaml minus those from values.schema.yaml:\n", + "\n".join(sorted(values - schema)), + "\n\n", + sep="\n", + ) + print( + "The keys from values.schema.yaml minus those from values.yaml:\n", + "\n".join(sorted(schema - values)), + "\n\n", + sep="\n", + ) + + +run() diff --git a/resources/helm/tools/generate-json-schema.py b/resources/helm/tools/generate-json-schema.py new file mode 100755 index 00000000..877b9375 --- /dev/null +++ b/resources/helm/tools/generate-json-schema.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +""" +This script reads values.schema.yaml and generates a values.schema.json that we +can package with the Helm chart. This is allowing the helm CLI to perform +validation of the passed configuration values. + +While we can directly generate a values.schema.json from values.schema.yaml, it +contains a lot of description text we use to generate our configuration +reference that isn't helpful to ship along the validation schema. Due to that, +we trim away everything that isn't needed. + +This script originated from the jupyterhub/zero-to-jupyterhub-k8s project. It is +not yet extracted to be a standalone package, but may be in the future. +""" + +import json +import os + +from collections.abc import MutableMapping + +import yaml + +here_dir = os.path.abspath(os.path.dirname(__file__)) +schema_yaml = os.path.join(here_dir, os.pardir, "dask-gateway", "values.schema.yaml") +values_schema_json = os.path.join( + here_dir, os.pardir, "dask-gateway", "values.schema.json" +) + + +def clean_jsonschema(d, parent_key=""): + """ + Modifies a dictionary representing a jsonschema in place to not contain + jsonschema keys not relevant for a values.schema.json file solely for use by + the helm CLI. + """ + JSONSCHEMA_KEYS_TO_REMOVE = {"description"} + + # start by cleaning up the current level + for k in set.intersection(JSONSCHEMA_KEYS_TO_REMOVE, set(d.keys())): + del d[k] + + # Recursively cleanup nested levels, bypassing one level where there could + # be a valid Helm chart configuration named just like the jsonschema + # specific key to remove. + if "properties" in d: + for k, v in d["properties"].items(): + if isinstance(v, MutableMapping): + clean_jsonschema(v, k) + + +def run(): + # Using these sets, we can validate further manually by printing the results + # of set operations. + with open(schema_yaml) as f: + schema = yaml.safe_load(f) + + # Drop what isn't relevant for a values.schema.json file packaged with the + # Helm chart, such as the description keys only relevant for our + # configuration reference. + clean_jsonschema(schema) + + # dump schema to values.schema.json + with open(values_schema_json, "w") as f: + json.dump(schema, f) + + print("dask-gateway/values.schema.json created") + + +run() diff --git a/resources/helm/tools/validate-against-schema.py b/resources/helm/tools/validate-against-schema.py new file mode 100755 index 00000000..d45798bc --- /dev/null +++ b/resources/helm/tools/validate-against-schema.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +""" +This scripts validates the charts default values against the values.schema.yaml +file, and optionally also another file against the values.schema.yaml. + +This script originated from the jupyterhub/zero-to-jupyterhub-k8s project. It is +not yet extracted to be a standalone package, but may be in the future. +""" + +import jsonschema +import os +import yaml + +here_dir = os.path.abspath(os.path.dirname(__file__)) +schema_yaml = os.path.join(here_dir, os.pardir, "dask-gateway", "values.schema.yaml") +values_yaml = os.path.join(here_dir, os.pardir, "dask-gateway", "values.yaml") +lint_and_validate_values_yaml = os.path.join( + here_dir, os.pardir, "testing", "chart-install-values.yaml" +) + +with open(schema_yaml) as f: + schema = yaml.safe_load(f) +with open(values_yaml) as f: + values = yaml.safe_load(f) +with open(lint_and_validate_values_yaml) as f: + lint_and_validate_values = yaml.safe_load(f) + +# Validate values.yaml against schema +print("Validating values.yaml against values.schema.yaml...") +jsonschema.validate(values, schema) +print("OK!") +print() + +# FIXME: Create a lint-and-validate-values.yaml file that covers all kinds of +# configuration properly and let it be tested to function with the schema +# and successfully render valid k8s templates. +# +# # Validate chart-install-values.yaml against schema +# print("Validating chart-install-values.yaml against values.schema.yaml...") +# jsonschema.validate(lint_and_validate_values, schema) +# print("OK!")