From fb72db42d9a42f874ea4c2ce3a7e377335490400 Mon Sep 17 00:00:00 2001 From: Joe Kratzat Date: Thu, 2 Jun 2022 21:46:28 -0400 Subject: [PATCH] feat: add Tilt support for local development This will allow for more rapid development as tilt supports hot reload on code changes. --- .gitignore | 5 +- Makefile | 59 ++++- Tiltfile | 215 ++++++++++++++++++ config/manager/manager.yaml | 2 - hack/kustomize-sub.sh | 22 ++ local-dev.md | 64 ++++++ scripts/go_install.sh | 45 ++++ .../kind-with-registry.sh | 0 tilt-settings.json | 8 + tilt_modules/cert_manager/README.md | 26 +++ tilt_modules/cert_manager/Tiltfile | 65 ++++++ tilt_modules/cert_manager/test/Tiltfile | 8 + tilt_modules/cert_manager/test/test.sh | 7 + tilt_modules/extensions.json | 9 + 14 files changed, 531 insertions(+), 4 deletions(-) create mode 100644 Tiltfile create mode 100755 hack/kustomize-sub.sh create mode 100644 local-dev.md create mode 100755 scripts/go_install.sh rename kind-with-registry.sh => scripts/kind-with-registry.sh (100%) create mode 100644 tilt-settings.json create mode 100644 tilt_modules/cert_manager/README.md create mode 100644 tilt_modules/cert_manager/Tiltfile create mode 100644 tilt_modules/cert_manager/test/Tiltfile create mode 100755 tilt_modules/cert_manager/test/test.sh create mode 100644 tilt_modules/extensions.json diff --git a/.gitignore b/.gitignore index fc8d91b1..17c93aee 100644 --- a/.gitignore +++ b/.gitignore @@ -48,4 +48,7 @@ test/e2e/data/infrastructure-oci/v1beta1/cluster-template-multiple-node-nsg.yaml test/e2e/data/infrastructure-oci/v1beta1/cluster-template-local-vcn-peering.yaml test/e2e/data/infrastructure-oci/v1beta1/cluster-template-cluster-class.yaml test/e2e/data/infrastructure-oci/v1beta1/cluster-template-remote-vcn-peering.yaml -test/e2e/data/infrastructure-oci/v1beta1/cluster-template-externally-managed-vcn.yaml \ No newline at end of file +test/e2e/data/infrastructure-oci/v1beta1/cluster-template-externally-managed-vcn.yaml + +# tilt +tilt-settings.json \ No newline at end of file diff --git a/Makefile b/Makefile index c49bb76d..1b3e5ae9 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ CONTROLLER_IMG ?= $(REGISTRY)/$(IMAGE_NAME) TAG ?= dev ARCH ?= amd64 ALL_ARCH = amd64 arm64 - +TOOLS_DIR := hack/tools TOOLS_BIN_DIR := $(abspath $(TOOLS_DIR)/bin) GINKGO_VER := v1.16.5 @@ -60,6 +60,21 @@ EXP_MACHINE_POOL ?= false # Set build time variables including version details LDFLAGS := $(shell source ./hack/version.sh; version::ldflags) +# curl retries +CURL_RETRIES=3 + +ENVSUBST_VER := v2.0.0-20210730161058-179042472c46 +ENVSUBST_BIN := envsubst +ENVSUBST := $(TOOLS_BIN_DIR)/$(ENVSUBST_BIN)-$(ENVSUBST_VER) + +KUSTOMIZE_VER := v4.5.2 +KUSTOMIZE_BIN := kustomize +KUSTOMIZE := $(TOOLS_BIN_DIR)/$(KUSTOMIZE_BIN)-$(KUSTOMIZE_VER) + +KUBECTL_VER := v1.22.9 +KUBECTL_BIN := kubectl +KUBECTL := $(TOOLS_BIN_DIR)/$(KUBECTL_BIN)-$(KUBECTL_VER) + # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) GOBIN=$(shell go env GOPATH)/bin @@ -326,3 +341,45 @@ release-metadata: $(RELEASE_DIR) .PHONY: clean-release clean-release: rm -rf $(RELEASE_DIR) + +GO_INSTALL = ./scripts/go_install.sh +GOOS := $(shell go env GOOS) +GOARCH := $(shell go env GOARCH) + +.PHONY: install-tools # populate hack/tools/bin +install-tools: $(ENVSUBST) $(KUSTOMIZE) $(KUBECTL) + +envsubst: $(ENVSUBST) ## Build a local copy of envsubst. +kustomize: $(KUSTOMIZE) ## Build a local copy of kustomize. +kubectl: $(KUBECTL) ## Build a local copy of kubectl. + +$(ENVSUBST): ## Build envsubst from tools folder. + GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) github.com/drone/envsubst/v2/cmd/envsubst $(ENVSUBST_BIN) $(ENVSUBST_VER) + +$(KUSTOMIZE): ## Build kustomize from tools folder. + GOBIN=$(TOOLS_BIN_DIR) $(GO_INSTALL) sigs.k8s.io/kustomize/kustomize/v4 $(KUSTOMIZE_BIN) $(KUSTOMIZE_VER) + +$(KUBECTL): ## Build kubectl from tools folder. + mkdir -p $(TOOLS_BIN_DIR) + rm -f "$(TOOLS_BIN_DIR)/$(KUBECTL_BIN)*" + curl --retry $(CURL_RETRIES) -fsL https://storage.googleapis.com/kubernetes-release/release/$(KUBECTL_VER)/bin/$(GOOS)/$(GOARCH)/kubectl -o $(KUBECTL) + ln -sf $(KUBECTL) $(TOOLS_BIN_DIR)/$(KUBECTL_BIN) + chmod +x $(KUBECTL) $(TOOLS_BIN_DIR)/$(KUBECTL_BIN) + +.PHONY: $(ENVSUBST_BIN) +$(ENVSUBST_BIN): $(ENVSUBST) + +.PHONY: $(KUBECTL_BIN) +$(KUBECTL_BIN): $(KUBECTL) + +## -------------------------------------- +## Tilt / Kind +## -------------------------------------- + +.PHONY: kind-create +kind-create: $(KUBECTL) ## Create kind cluster if needed. + ./scripts/kind-with-registry.sh + +.PHONY: tilt-up +tilt-up: install-tools kind-create ## Start tilt and build kind cluster if needed. + EXP_CLUSTER_RESOURCE_SET=true EXP_MACHINE_POOL=true tilt up \ No newline at end of file diff --git a/Tiltfile b/Tiltfile new file mode 100644 index 00000000..232b0aab --- /dev/null +++ b/Tiltfile @@ -0,0 +1,215 @@ + +envsubst_cmd = "./hack/tools/bin/envsubst" +kubectl_cmd = "./hack/tools/bin/kubectl" +helm_cmd = "./hack/tools/bin/helm" +tools_bin = "./hack/tools/bin" + +update_settings(k8s_upsert_timeout_secs = 60) # on first tilt up, often can take longer than 30 seconds + +#Add tools to path +os.putenv("PATH", os.getenv("PATH") + ":" + tools_bin) + +keys = ["OCI_TENANCY_ID", "OCI_USER_ID", "OCI_CREDENTIALS_FINGERPRINT", "OCI_REGION", "OCI_CREDENTIALS_KEY_PATH"] + +# set defaults +settings = { + "allowed_contexts": [ + "kind-capoci", + ], + "deploy_cert_manager": True, + "preload_images_for_kind": True, + "kind_cluster_name": "capoci", + "capi_version": "v1.1.3", + "cert_manager_version": "v1.1.0", + "kubernetes_version": "v1.22.9", +} + +# global settings +settings.update(read_json( + "tilt-settings.json", + default = {}, +)) + +if settings.get("trigger_mode") == "manual": + trigger_mode(TRIGGER_MODE_MANUAL) + +if "allowed_contexts" in settings: + allow_k8s_contexts(settings.get("allowed_contexts")) + +if "default_registry" in settings: + default_registry(settings.get("default_registry")) + +tilt_helper_dockerfile_header = """ +# Tilt image +FROM golang:1.17 as tilt-helper +# Support live reloading with Tilt +RUN wget --output-document /restart.sh --quiet https://raw.githubusercontent.com/windmilleng/rerun-process-wrapper/master/restart.sh && \ + wget --output-document /start.sh --quiet https://raw.githubusercontent.com/windmilleng/rerun-process-wrapper/master/start.sh && \ + chmod +x /start.sh && chmod +x /restart.sh +""" + +tilt_dockerfile_header = """ +FROM gcr.io/distroless/base:debug as tilt +WORKDIR / +COPY --from=tilt-helper /start.sh . +COPY --from=tilt-helper /restart.sh . +COPY manager . +""" + +def validate_auth(): + substitutions = settings.get("kustomize_substitutions", {}) + os.environ.update(substitutions) + for sub in substitutions: + if sub[-4:] == "_B64": + os.environ[sub[:-4]] = base64_decode(os.environ[sub]) + print("{} was not specified in tilt-settings.json, attempting to load {}".format(base64_decode(os.environ[sub]), sub)) + missing = [k for k in keys if not os.environ.get(k)] + if missing: + fail("missing kustomize_substitutions keys {} in tilt-setting.json".format(missing)) + +# Users may define their own Tilt customizations in tilt.d. This directory is excluded from git and these files will +# not be checked in to version control. +def include_user_tilt_files(): + user_tiltfiles = listdir("tilt.d") + for f in user_tiltfiles: + include(f) + +# deploy CAPI +def deploy_capi(): + version = settings.get("capi_version") + capi_uri = "https://github.com/kubernetes-sigs/cluster-api/releases/download/{}/cluster-api-components.yaml".format(version) + cmd = "curl -sSL {} | {} | {} apply -f -".format(capi_uri, envsubst_cmd, kubectl_cmd) + local(cmd, quiet = False) + if settings.get("extra_args"): + extra_args = settings.get("extra_args") + if extra_args.get("core"): + core_extra_args = extra_args.get("core") + if core_extra_args: + for namespace in ["capi-system", "capi-webhook-system"]: + patch_args_with_extra_args(namespace, "capi-controller-manager", core_extra_args) + if extra_args.get("kubeadm-bootstrap"): + kb_extra_args = extra_args.get("kubeadm-bootstrap") + if kb_extra_args: + patch_args_with_extra_args("capi-kubeadm-bootstrap-system", "capi-kubeadm-bootstrap-controller-manager", kb_extra_args) + +def patch_args_with_extra_args(namespace, name, extra_args): + args_str = str(local("{} get deployments {} -n {} -o jsonpath={{.spec.template.spec.containers[1].args}}".format(kubectl_cmd, name, namespace))) + args_to_add = [arg for arg in extra_args if arg not in args_str] + if args_to_add: + args = args_str[1:-1].split() + args.extend(args_to_add) + patch = [{ + "op": "replace", + "path": "/spec/template/spec/containers/1/args", + "value": args, + }] + local("{} patch deployment {} -n {} --type json -p='{}'".format(kubectl_cmd, name, namespace, str(encode_json(patch)).replace("\n", ""))) + +# Build CAPOCI and add feature gates +def capoci(): + # Apply the kustomized yaml for this provider + yaml = str(kustomizesub("./config/default")) + + # add extra_args if they are defined + if settings.get("extra_args"): + oci_extra_args = settings.get("extra_args").get("oci") + if oci_extra_args: + yaml_dict = decode_yaml_stream(yaml) + append_arg_for_container_in_deployment(yaml_dict, "capoci-controller-manager", "capoci-system", "cluster-api-oci-controller", oci_extra_args) + yaml = str(encode_yaml_stream(yaml_dict)) + yaml = fixup_yaml_empty_arrays(yaml) + + # Set up a local_resource build of the provider's manager binary. + local_resource( + "manager", + cmd = 'mkdir -p .tiltbuild;CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags \'-extldflags "-static"\' -o .tiltbuild/manager', + deps = ["api", "cloud", "config", "controllers", "exp", "feature", "pkg", "go.mod", "go.sum", "main.go", "auth-config.yaml"], + labels = ["cluster-api"], + ) + + dockerfile_contents = "\n".join([ + tilt_helper_dockerfile_header, + tilt_dockerfile_header, + ]) + + entrypoint = ["sh", "/start.sh", "/manager"] + extra_args = settings.get("extra_args") + if extra_args: + entrypoint.extend(extra_args) + + # Set up an image build for the provider. The live update configuration syncs the output from the local_resource + # build into the container. + docker_build( + ref = "ghcr.io/oracle/cluster-api-oci-controller-amd64:dev", + context = "./.tiltbuild/", + dockerfile_contents = dockerfile_contents, + target = "tilt", + entrypoint = entrypoint, + only = "manager", + live_update = [ + sync(".tiltbuild/manager", "/manager"), + run("sh /restart.sh"), + ], + ignore = ["templates"], + network = "host", + ) + + #secret_settings(disable_scrub=True) + k8s_yaml(blob(yaml)) + + +def append_arg_for_container_in_deployment(yaml_stream, name, namespace, contains_image_name, args): + for item in yaml_stream: + if item["kind"] == "Deployment" and item.get("metadata").get("name") == name and item.get("metadata").get("namespace") == namespace: + containers = item.get("spec").get("template").get("spec").get("containers") + for container in containers: + if contains_image_name in container.get("image"): + container.get("args").extend(args) + +def fixup_yaml_empty_arrays(yaml_str): + yaml_str = yaml_str.replace("conditions: null", "conditions: []") + return yaml_str.replace("storedVersions: null", "storedVersions: []") + +def waitforsystem(): + local(kubectl_cmd + " wait --for=condition=ready --timeout=300s pod --all -n capi-kubeadm-bootstrap-system") + local(kubectl_cmd + " wait --for=condition=ready --timeout=300s pod --all -n capi-kubeadm-control-plane-system") + local(kubectl_cmd + " wait --for=condition=ready --timeout=300s pod --all -n capi-system") + +def base64_encode(to_encode): + encode_blob = local("echo '{}' | tr -d '\n' | base64 - | tr -d '\n'".format(to_encode), quiet = True, echo_off = True) + return str(encode_blob) + +def base64_encode_file(path_to_encode): + encode_blob = local("cat {} | tr -d '\n' | base64 - | tr -d '\n'".format(path_to_encode), quiet = True) + return str(encode_blob) + +def read_file_from_path(path_to_read): + str_blob = local("cat {} | tr -d '\n'".format(path_to_read), quiet = True) + return str(str_blob) + +def base64_decode(to_decode): + decode_blob = local("echo '{}' | base64 --decode -".format(to_decode), quiet = True, echo_off = True) + return str(decode_blob) + +def kustomizesub(folder): + yaml = local('hack/kustomize-sub.sh {}'.format(folder), quiet=True) + return yaml + +############################## +# Actual work happens here +############################## + +validate_auth() + +include_user_tilt_files() + +load("ext://cert_manager", "deploy_cert_manager") + +if settings.get("deploy_cert_manager"): + deploy_cert_manager() + +deploy_capi() + +capoci() + +waitforsystem() \ No newline at end of file diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 0e6ee9e9..29dcea98 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -22,8 +22,6 @@ spec: labels: control-plane: controller-manager spec: - securityContext: - runAsNonRoot: true containers: - command: - /manager diff --git a/hack/kustomize-sub.sh b/hack/kustomize-sub.sh new file mode 100755 index 00000000..a4129861 --- /dev/null +++ b/hack/kustomize-sub.sh @@ -0,0 +1,22 @@ + +#!/bin/bash +# Copyright 2021 The Kubernetes Authors. +# +# Licensed 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. + +set -o errexit +set -o nounset +set -o pipefail + +root=$(dirname "${BASH_SOURCE[0]}") +$root/tools/bin/kustomize build $1 | $root/tools/bin/envsubst \ No newline at end of file diff --git a/local-dev.md b/local-dev.md new file mode 100644 index 00000000..f252c0dd --- /dev/null +++ b/local-dev.md @@ -0,0 +1,64 @@ +# Local Development + +## Tilt + +### Tilt Requirements + +Install [Tilt](https://docs.tilt.dev/install.html): + +- `brew install tilt-dev/tap/tilt` on macOS or Linux. +- `scoop bucket add tilt-dev https://github.com/tilt-dev/scoop-bucket` & `scoop install tilt` on Windows +- for alternatives you can follow the installation instruction for [macOS](https://docs.tilt.dev/install.html#macos), [Linux](https://docs.tilt.dev/install.html#linux) or [Windows](https://docs.tilt.dev/install.html#windows) + +After the installation is done, verify that you have installed it correctly with: `tilt version`. + +Install Helm: + +- brew install helm on MacOS +- `choco install kubernetes-helm` on Windows +- [Install Instruction](https://helm.sh/docs/intro/install/#from-source-linux-macos) on Linux + +You would require installation of Helm for successfully setting up Tilt. + +### Using Tilt + +From the root of the CAPOCI repository and after configuring the [environment +variables](https://oracle.github.io/cluster-api-provider-oci/gs/install-cluster-api.html#configure-authentication). +One extra ENV that needs to be set is `OCI_CREDENTIALS_KEY_PATH`. This should point to your OCI private PEM file. +Once set you can run the following to generate your `tilt-settings.json` file: + +``` +cat < tilt-settings.json +{ + "kustomize_substitutions": { + "OCI_TENANCY_ID_B64": "$(echo "${OCI_TENANCY_ID}" | tr -d '\n' | base64 | tr -d '\n')", + "OCI_CREDENTIALS_FINGERPRINT_B64": "$(echo "${OCI_CREDENTIALS_FINGERPRINT}" | tr -d '\n' | base64 | tr -d '\n')", + "OCI_USER_ID_B64": "$(echo "${OCI_USER_ID}" | tr -d '\n' | base64 | tr -d '\n')", + "OCI_REGION_B64": "$(echo "${OCI_REGION}" | tr -d '\n' | base64 | tr -d '\n')", + "OCI_CREDENTIALS_KEY_B64": "$(echo "${OCI_CREDENTIALS_KEY_PATH}" | tr -d '\n' | base64 | tr -d '\n')" + + } +} +EOF +``` + +To build a kind cluster and start Tilt, just run: + +``` +make tilt-up +``` + +Once your kind management cluster is up and running, you can deploy a [workload cluster](https://oracle.github.io/cluster-api-provider-oci/gs/create-workload-cluster.html). + +To tear down the kind cluster built by the command above you will need to +delete your workload cluster: + +``` +kubectl delete cluster +``` + +then delete your local management cluster: + +``` +kind delete cluster +``` \ No newline at end of file diff --git a/scripts/go_install.sh b/scripts/go_install.sh new file mode 100755 index 00000000..1e562ea2 --- /dev/null +++ b/scripts/go_install.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# Copyright 2020 The Kubernetes Authors. +# +# Licensed 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. + +set -o errexit +set -o nounset +set -o pipefail + +if [ -z "${1}" ]; then + echo "must provide module as first parameter" + exit 1 +fi + +if [ -z "${2}" ]; then + echo "must provide binary name as second parameter" + exit 1 +fi + +if [ -z "${3}" ]; then + echo "must provide version as third parameter" + exit 1 +fi + +if [ -z "${GOBIN}" ]; then + echo "GOBIN is not set. Must set GOBIN to install the bin in a specified directory." + exit 1 +fi + +rm "${GOBIN}/${2}"* || true + +# install the golang module specified as the first argument +go install -tags tools "${1}@${3}" +mv "${GOBIN}/${2}" "${GOBIN}/${2}-${3}" +ln -sf "${GOBIN}/${2}-${3}" "${GOBIN}/${2}" diff --git a/kind-with-registry.sh b/scripts/kind-with-registry.sh similarity index 100% rename from kind-with-registry.sh rename to scripts/kind-with-registry.sh diff --git a/tilt-settings.json b/tilt-settings.json new file mode 100644 index 00000000..a9c927a0 --- /dev/null +++ b/tilt-settings.json @@ -0,0 +1,8 @@ +{ + + "extra_args": { + "default_registry": "localhost:5000/cluster-api-oci-controller-amd64:dev", + "provider_repos": ["../cluster-api-provider-oci"], + "enable_providers": ["aws", "docker", "kubeadm-bootstrap", "kubeadm-control-plane"] + } +} diff --git a/tilt_modules/cert_manager/README.md b/tilt_modules/cert_manager/README.md new file mode 100644 index 00000000..1d1feb76 --- /dev/null +++ b/tilt_modules/cert_manager/README.md @@ -0,0 +1,26 @@ +# Cert-manager + +This extension deploys cert-manager. + +## Usage + +Basic usage + +``` +load('ext://cert_manager', 'deploy_cert_manager') + +deploy_cert_manager() +``` + +This will deploy cert-manager to your cluster and check that it actually works. + +If working with Kind, it's possible to pass `load_to_kind=True` to `deploy_cert_manager` so +all the cert-manager images will be pre-pulled to your local environment and then loaded into Kind before installing. +This speeds up your workflow if you're repeatedly destroying and recreating your kind cluster, as it doesn't +have to pull the images over the network each time. + +The full list of parameters accepted by `deploy_cert_manager` includes: +- `registry` from which images should be pulled, defaults to `quay.io/jetstack` +- `version` of cert-manager to install, defaults to `v1.3.1` +- `load_to_kind` (see above), defaults to `False` +- `kind_cluster_name`, defaults to `kind` diff --git a/tilt_modules/cert_manager/Tiltfile b/tilt_modules/cert_manager/Tiltfile new file mode 100644 index 00000000..f5d23e41 --- /dev/null +++ b/tilt_modules/cert_manager/Tiltfile @@ -0,0 +1,65 @@ +cert_manager_test_resources = """ +apiVersion: v1 +kind: Namespace +metadata: + name: cert-manager-test +--- +apiVersion: cert-manager.io/{cert_manager_api_version} +kind: Issuer +metadata: + name: test-selfsigned + namespace: cert-manager-test +spec: + selfSigned: {{}} +--- +apiVersion: cert-manager.io/{cert_manager_api_version} +kind: Certificate +metadata: + name: selfsigned-cert + namespace: cert-manager-test +spec: + dnsNames: + - example.com + secretName: selfsigned-cert-tls + issuerRef: + name: test-selfsigned +""" + +# Deploys cert manager to your environment +def deploy_cert_manager(registry="quay.io/jetstack", version="v1.3.1", load_to_kind=False, kind_cluster_name="kind"): + silent=True + if version.startswith('v0'): + cert_manager_test_resources_versioned = cert_manager_test_resources.format(cert_manager_api_version='v1alpha2') + else: + cert_manager_test_resources_versioned = cert_manager_test_resources.format(cert_manager_api_version='v1') + + if load_to_kind == True: + print("Loading images to kind") + # Prepull all the cert-manager images to your local environment and then load them directly into kind. This speeds up + # setup if you're repeatedly destroying and recreating your kind cluster, as it doesn't have to pull the images over + # the network each time. + images = ["cert-manager-controller", "cert-manager-cainjector", "cert-manager-webhook"] + for image in images: + local("docker pull {}/{}:{}".format(registry, image, version), quiet=silent, echo_off=silent) + local("kind load docker-image --name {} {}/{}:{}".format(kind_cluster_name, registry, image, version), quiet=silent, echo_off=silent) + + # apply the cert-manager manifest + # NOTE! + # Applying the same manifest twice to same cluster kubectl get stuck with older versions of kubernetes/cert-manager. + # https://github.com/jetstack/cert-manager/issues/3121 + print("Installing cert-manager") + local("kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/{}/cert-manager.yaml".format(version), quiet=silent, echo_off=silent) + + # verifies cert-manager is properly working (https://cert-manager.io/docs/installation/kubernetes/#verifying-the-installation) + # 1. wait for the cert-manager to be running + print("Waiting for cert-manager to start") + local("kubectl wait --for=condition=Available --timeout=300s -n cert-manager deployment/cert-manager", quiet=silent, echo_off=silent) + local("kubectl wait --for=condition=Available --timeout=300s -n cert-manager deployment/cert-manager-cainjector", quiet=silent, echo_off=silent) + local("kubectl wait --for=condition=Available --timeout=300s -n cert-manager deployment/cert-manager-webhook", quiet=silent, echo_off=silent) + + # 2. create a test certificate + print("Testing cert-manager") + # The webhook may refuse connections initially (despite the deployment being Available), so try several times. + local("for i in 1 2 3 4 5 6; do (kubectl apply -f - <