|
| 1 | +# -*- mode: Python -*- |
| 2 | +load("ext://uibutton", "cmd_button", "location", 'text_input') |
| 3 | +load("ext://restart_process", "docker_build_with_restart") |
| 4 | + |
| 5 | +kustomize_cmd = "./hack/tools/bin/kustomize" |
| 6 | +envsubst_cmd = "./hack/tools/bin/envsubst" |
| 7 | +sed_cmd = "sed 's/:=\"\"//g'" |
| 8 | +tools_bin = "./hack/tools/bin" |
| 9 | + |
| 10 | +#Add tools to path |
| 11 | +os.putenv("PATH", os.getenv("PATH") + ":" + tools_bin) |
| 12 | + |
| 13 | +update_settings(k8s_upsert_timeout_secs = 60) # on first tilt up, often can take longer than 30 seconds |
| 14 | + |
| 15 | + |
| 16 | +settings = { |
| 17 | + "allowed_contexts": [ |
| 18 | + "kind-cso", |
| 19 | + ], |
| 20 | + "deploy_cert_manager": True, |
| 21 | + "preload_images_for_kind": True, |
| 22 | + "kind_cluster_name": "cso", |
| 23 | + "capi_version": "v1.5.2", |
| 24 | + "cert_manager_version": "v1.11.0", |
| 25 | + "kustomize_substitutions": { |
| 26 | + }, |
| 27 | +} |
| 28 | + |
| 29 | +# global settings |
| 30 | +settings.update(read_json( |
| 31 | + "tilt-settings.json", |
| 32 | + default = {}, |
| 33 | +)) |
| 34 | + |
| 35 | +if settings.get("trigger_mode") == "manual": |
| 36 | + trigger_mode(TRIGGER_MODE_MANUAL) |
| 37 | + |
| 38 | +if "allowed_contexts" in settings: |
| 39 | + allow_k8s_contexts(settings.get("allowed_contexts")) |
| 40 | + |
| 41 | +if "default_registry" in settings: |
| 42 | + default_registry(settings.get("default_registry")) |
| 43 | + |
| 44 | +# deploy CAPI |
| 45 | +def deploy_capi(): |
| 46 | + version = settings.get("capi_version") |
| 47 | + capi_uri = "https://github.com/kubernetes-sigs/cluster-api/releases/download/{}/cluster-api-components.yaml".format(version) |
| 48 | + cmd = "curl -sSL {} | {} | kubectl apply -f -".format(capi_uri, envsubst_cmd) |
| 49 | + local(cmd, quiet = True) |
| 50 | + if settings.get("extra_args"): |
| 51 | + extra_args = settings.get("extra_args") |
| 52 | + if extra_args.get("core"): |
| 53 | + core_extra_args = extra_args.get("core") |
| 54 | + if core_extra_args: |
| 55 | + for namespace in ["capi-system", "capi-webhook-system"]: |
| 56 | + patch_args_with_extra_args(namespace, "capi-controller-manager", core_extra_args) |
| 57 | + if extra_args.get("kubeadm-bootstrap"): |
| 58 | + kb_extra_args = extra_args.get("kubeadm-bootstrap") |
| 59 | + if kb_extra_args: |
| 60 | + patch_args_with_extra_args("capi-kubeadm-bootstrap-system", "capi-kubeadm-bootstrap-controller-manager", kb_extra_args) |
| 61 | + |
| 62 | +def deploy_capd(): |
| 63 | + version = settings.get("capi_version") |
| 64 | + capd_uri = "https://github.com/kubernetes-sigs/cluster-api/releases/download/{}/infrastructure-components-development.yaml".format(version) |
| 65 | + cmd = "curl -sSL {} | {} | kubectl apply -f -".format(capd_uri, envsubst_cmd) |
| 66 | + local(cmd, quiet = True) |
| 67 | + |
| 68 | + |
| 69 | +def prepare_environment(): |
| 70 | + local("kubectl create namespace cluster --dry-run=client -o yaml | kubectl apply -f -") |
| 71 | + |
| 72 | + # if it's already present then don't copy |
| 73 | + if not os.path.exists('.clusterstack.yaml'): |
| 74 | + local("cp config/cso/clusterstack.yaml .clusterstack.yaml") |
| 75 | + |
| 76 | + k8s_yaml('.clusterstack.yaml') |
| 77 | + |
| 78 | + if not os.path.exists('.cluster.yaml'): |
| 79 | + local("cp config/cso/cluster.yaml .cluster.yaml") |
| 80 | + |
| 81 | +def patch_args_with_extra_args(namespace, name, extra_args): |
| 82 | + args_str = str(local("kubectl get deployments {} -n {} -o jsonpath='{{.spec.template.spec.containers[0].args}}'".format(name, namespace))) |
| 83 | + args_to_add = [arg for arg in extra_args if arg not in args_str] |
| 84 | + if args_to_add: |
| 85 | + args = args_str[1:-1].split() |
| 86 | + args.extend(args_to_add) |
| 87 | + patch = [{ |
| 88 | + "op": "replace", |
| 89 | + "path": "/spec/template/spec/containers/0/args", |
| 90 | + "value": args, |
| 91 | + }] |
| 92 | + local("kubectl patch deployment {} -n {} --type json -p='{}'".format(name, namespace, str(encode_json(patch)).replace("\n", ""))) |
| 93 | + |
| 94 | +# Users may define their own Tilt customizations in tilt.d. This directory is excluded from git and these files will |
| 95 | +# not be checked in to version control. |
| 96 | +def include_user_tilt_files(): |
| 97 | + user_tiltfiles = listdir("tilt.d") |
| 98 | + for f in user_tiltfiles: |
| 99 | + include(f) |
| 100 | + |
| 101 | +def append_arg_for_container_in_deployment(yaml_stream, name, namespace, contains_image_name, args): |
| 102 | + for item in yaml_stream: |
| 103 | + if item["kind"] == "Deployment" and item.get("metadata").get("name") == name and item.get("metadata").get("namespace") == namespace: |
| 104 | + containers = item.get("spec").get("template").get("spec").get("containers") |
| 105 | + for container in containers: |
| 106 | + if contains_image_name in container.get("name"): |
| 107 | + container.get("args").extend(args) |
| 108 | + |
| 109 | +def fixup_yaml_empty_arrays(yaml_str): |
| 110 | + yaml_str = yaml_str.replace("conditions: null", "conditions: []") |
| 111 | + return yaml_str.replace("storedVersions: null", "storedVersions: []") |
| 112 | + |
| 113 | +## This should have the same versions as the Dockerfile |
| 114 | +tilt_dockerfile_header_cso = """ |
| 115 | +FROM docker.io/alpine/helm:3.12.2 as helm |
| 116 | +
|
| 117 | +FROM docker.io/library/alpine:3.18.0 as tilt |
| 118 | +WORKDIR / |
| 119 | +COPY --from=helm --chown=root:root --chmod=755 /usr/bin/helm /usr/local/bin/helm |
| 120 | +COPY manager . |
| 121 | +""" |
| 122 | + |
| 123 | +# Build CSO and add feature gates |
| 124 | +def deploy_cso(): |
| 125 | + # yaml = str(kustomizesub("./hack/observability")) # build an observable kind deployment by default |
| 126 | + yaml = str(kustomizesub("./config/default")) |
| 127 | + local_resource( |
| 128 | + name = "cso-components", |
| 129 | + cmd = ["sh", "-ec", sed_cmd, yaml, "|", envsubst_cmd], |
| 130 | + labels = ["CSO"], |
| 131 | + ) |
| 132 | + |
| 133 | + # Forge the build command |
| 134 | + ldflags = "-extldflags \"-static\" " + str(local("hack/version.sh")).rstrip("\n") |
| 135 | + build_env = "CGO_ENABLED=0 GOOS=linux GOARCH=amd64" |
| 136 | + build_cmd = "{build_env} go build -ldflags '{ldflags}' -o .tiltbuild/manager cmd/main.go".format( |
| 137 | + build_env = build_env, |
| 138 | + ldflags = ldflags, |
| 139 | + ) |
| 140 | + # Set up a local_resource build of the provider's manager binary. |
| 141 | + local_resource( |
| 142 | + "cso-manager", |
| 143 | + cmd = "mkdir -p .tiltbuild; " + build_cmd, |
| 144 | + deps = ["api", "cmd", "config", "internal", "vendor", "pkg", "go.mod", "go.sum"], |
| 145 | + labels = ["CSO"], |
| 146 | + ) |
| 147 | + |
| 148 | + entrypoint = ["/manager"] |
| 149 | + extra_args = settings.get("extra_args") |
| 150 | + if extra_args: |
| 151 | + entrypoint.extend(extra_args) |
| 152 | + |
| 153 | + # Set up an image build for the provider. The live update configuration syncs the output from the local_resource |
| 154 | + # build into the container. |
| 155 | + docker_build_with_restart( |
| 156 | + ref = "ghcr.io/sovereigncloudstack/cso-staging", |
| 157 | + context = "./.tiltbuild/", |
| 158 | + dockerfile_contents = tilt_dockerfile_header_cso, |
| 159 | + target = "tilt", |
| 160 | + entrypoint = entrypoint, |
| 161 | + only = "manager", |
| 162 | + live_update = [ |
| 163 | + sync(".tiltbuild/manager", "/manager"), |
| 164 | + ], |
| 165 | + ignore = ["templates"], |
| 166 | + ) |
| 167 | + k8s_yaml(blob(yaml)) |
| 168 | + k8s_resource(workload = "cso-controller-manager", labels = ["CSO"]) |
| 169 | + k8s_resource( |
| 170 | + objects = [ |
| 171 | + "cso-system:namespace", |
| 172 | + "clusterstackreleases.clusterstack.x-k8s.io:customresourcedefinition", |
| 173 | + "clusterstacks.clusterstack.x-k8s.io:customresourcedefinition", |
| 174 | + "cso-controller-manager:serviceaccount", |
| 175 | + "cso-leader-election-role:role", |
| 176 | + "cso-manager-role:clusterrole", |
| 177 | + "cso-leader-election-rolebinding:rolebinding", |
| 178 | + "cso-manager-rolebinding:clusterrolebinding", |
| 179 | + "cso-serving-cert:certificate", |
| 180 | + "cso-cluster-stack-variables:secret", |
| 181 | + "cso-selfsigned-issuer:issuer", |
| 182 | + #"cso-validating-webhook-configuration:validatingwebhookconfiguration", |
| 183 | + ], |
| 184 | + new_name = "cso-misc", |
| 185 | + labels = ["CSO"], |
| 186 | + ) |
| 187 | + |
| 188 | +def clusterstack(): |
| 189 | + k8s_resource(objects = ["clusterstack:clusterstack"], new_name = "clusterstack", labels = ["CLUSTERSTACK"]) |
| 190 | + |
| 191 | +def base64_encode(to_encode): |
| 192 | + encode_blob = local("echo '{}' | tr -d '\n' | base64 - | tr -d '\n'".format(to_encode), quiet = True) |
| 193 | + return str(encode_blob) |
| 194 | + |
| 195 | +def base64_encode_file(path_to_encode): |
| 196 | + encode_blob = local("cat {} | tr -d '\n' | base64 - | tr -d '\n'".format(path_to_encode), quiet = True) |
| 197 | + return str(encode_blob) |
| 198 | + |
| 199 | +def read_file_from_path(path_to_read): |
| 200 | + str_blob = local("cat {} | tr -d '\n'".format(path_to_read), quiet = True) |
| 201 | + return str(str_blob) |
| 202 | + |
| 203 | +def base64_decode(to_decode): |
| 204 | + decode_blob = local("echo '{}' | base64 --decode -".format(to_decode), quiet = True) |
| 205 | + return str(decode_blob) |
| 206 | + |
| 207 | +def ensure_envsubst(): |
| 208 | + if not os.path.exists(envsubst_cmd): |
| 209 | + local("make {}".format(os.path.abspath(envsubst_cmd))) |
| 210 | + |
| 211 | +def ensure_kustomize(): |
| 212 | + if not os.path.exists(kustomize_cmd): |
| 213 | + local("make {}".format(os.path.abspath(kustomize_cmd))) |
| 214 | + |
| 215 | +def kustomizesub(folder): |
| 216 | + yaml = local("hack/kustomize-sub.sh {}".format(folder), quiet = True) |
| 217 | + return yaml |
| 218 | + |
| 219 | +def waitforsystem(): |
| 220 | + local("kubectl wait --for=condition=ready --timeout=300s pod --all -n capi-kubeadm-bootstrap-system") |
| 221 | + local("kubectl wait --for=condition=ready --timeout=300s pod --all -n capi-kubeadm-control-plane-system") |
| 222 | + local("kubectl wait --for=condition=ready --timeout=300s pod --all -n capi-system") |
| 223 | + |
| 224 | +def deploy_observability(): |
| 225 | + k8s_yaml(blob(str(local("{} build {}".format(kustomize_cmd, "./hack/observability/"), quiet = True)))) |
| 226 | + |
| 227 | + k8s_resource(workload = "promtail", extra_pod_selectors = [{"app": "promtail"}], labels = ["observability"]) |
| 228 | + k8s_resource(workload = "loki", extra_pod_selectors = [{"app": "loki"}], labels = ["observability"]) |
| 229 | + k8s_resource(workload = "grafana", port_forwards = "3000", extra_pod_selectors = [{"app": "grafana"}], labels = ["observability"]) |
| 230 | + |
| 231 | +############################## |
| 232 | +# Actual work happens here |
| 233 | +############################## |
| 234 | +ensure_envsubst() |
| 235 | +ensure_kustomize() |
| 236 | + |
| 237 | +include_user_tilt_files() |
| 238 | + |
| 239 | +load("ext://cert_manager", "deploy_cert_manager") |
| 240 | + |
| 241 | +if settings.get("deploy_cert_manager"): |
| 242 | + deploy_cert_manager() |
| 243 | + |
| 244 | +if settings.get("deploy_observability"): |
| 245 | + deploy_observability() |
| 246 | + |
| 247 | +deploy_capi() |
| 248 | + |
| 249 | +deploy_capd() |
| 250 | + |
| 251 | +deploy_cso() |
| 252 | + |
| 253 | +clusterstack() |
| 254 | + |
| 255 | +waitforsystem() |
| 256 | + |
| 257 | +prepare_environment() |
| 258 | + |
| 259 | +## TODO |
| 260 | +cmd_button( |
| 261 | + "create workload cluster", |
| 262 | + argv=["make", "create-workload-cluster-docker"], |
| 263 | + location=location.NAV, |
| 264 | + icon_name="add_circle", |
| 265 | +) |
0 commit comments