From 2411f4796ad808902dbc0d6abd5d1dbed93f4ee7 Mon Sep 17 00:00:00 2001 From: Andy Goldstein Date: Tue, 25 Jul 2023 11:48:07 -0400 Subject: [PATCH] Add initial Tilt support Signed-off-by: Andy Goldstein --- .gitignore | 2 ++ Tiltfile | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++ tilt.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 Tiltfile create mode 100644 tilt.md diff --git a/.gitignore b/.gitignore index ba2909a2..8be23869 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ dist/ # Release artifacts rukpak.yaml + +.tiltbuild/ diff --git a/Tiltfile b/Tiltfile new file mode 100644 index 00000000..e5b284ea --- /dev/null +++ b/Tiltfile @@ -0,0 +1,75 @@ +# This loads a helper function that isn't part of core Tilt that simplifies restarting the process in the container +# when files changes. +load('ext://restart_process', 'docker_build_with_restart') + +# These are the 4 binaries that make up the 4 deployments for rukpak. +binaries = ["helm", "core", "webhooks", "crdvalidator"] + +# This is how we build each binary +build_cmd = ''' +mkdir -p .tiltbuild/bin +CGO_ENABLED=0 GOOS=linux go build -o .tiltbuild/bin/{binary} ./cmd/{binary} +''' + +# All of our binaries and images are built the same way, so we can iterate and substitute the binary name where needed. +for binary in binaries: + # Treat the main binary as a local resource, so we can automatically rebuild it when any of the deps change. This + # builds it locally, targeting linux, so it can run in a linux container. + local_resource( + '{}_binary'.format(binary), + cmd = build_cmd.format(binary=binary), + deps = ['api', 'cmd/{}'.format(binary), 'internal', 'pkg', 'go.mod', 'go.sum'] + ) + + # Configure our image build. If the file in live_update.sync (.tiltbuild/bin/$binary) changes, Tilt + # copies it to the running container and restarts it. + docker_build_with_restart( + # This has to match an image in the k8s_yaml we call below, so Tilt knows to use this image for our Deployment, + # instead of the actual image specified in the yaml. + ref = 'quay.io/operator-framework/rukpak:{}'.format(binary), + # This is the `docker build` context, and because we're only copying in the binary we've already had Tilt build + # locally, we set the context to the directory containing the binary. + context = '.tiltbuild/bin', + # We use a slimmed-down Dockerfile that only has $binary in it. + dockerfile_contents = ''' +FROM gcr.io/distroless/static:debug +EXPOSE 8080 +WORKDIR / +COPY {} /. + '''.format(binary), + # The set of files Tilt should include in the build. In this case, it's just the binary we built above. + only = binary, + # If .tiltbuild/bin/$binary changes, Tilt will copy it into the running container and restart the process. + live_update = [ + sync('.tiltbuild/bin/{}'.format(binary), '/{}'.format(binary)), + ], + # The command to run in the container. + entrypoint = "/{}".format(binary), + ) + +# Tell Tilt what to deploy by running kustomize and then doing some manipulation to make things work for Tilt. +objects = decode_yaml_stream(kustomize('manifests/overlays/cert-manager')) +for o in objects: + if o['kind'] != 'Deployment': + # We only need to modify Deployments, so we can skip this + continue + + # For Tilt's live_update functionality to work, we have to run the container as root. Otherwise, Tilt won't + # be able to untar on top of /$binary in the container's file system (this is how live update + # works). If the container definition says runAsNonRoot=true, we flip it to false. + if 'securityContext' in o['spec']['template']['spec']: + o['spec']['template']['spec']['securityContext']['runAsNonRoot'] = False + + # The rukpak Deployment manifests all use the same image, quay.io/operator-framework/rukpak:devel. Tilt needs each + # Deployment's image to be unique. We replace the :devel tag with what is effectively :$binary, e.g. :helm. + for c in o['spec']['template']['spec']['containers']: + if c['name'] == 'kube-rbac-proxy': + continue + # The container's command is the same as the binary name with a leading / in front. Replace the / with a : to + # turn it into a valid image tag. + command = c['command'][0].replace('/',':') + # Update the image so instead of :devel it's :$binary + c['image'] = c['image'].replace(':devel', command) + +# Now apply all the yaml +k8s_yaml(encode_yaml_stream(objects)) diff --git a/tilt.md b/tilt.md new file mode 100644 index 00000000..27a0b45d --- /dev/null +++ b/tilt.md @@ -0,0 +1,80 @@ +# Rapid iterative development with Tilt + +[Tilt](https://tilt.dev) is a tool that enables rapid iterative development of containerized workloads. + +Here is an example workflow without Tilt for modifying some source code and testing those changes in a cluster: + +1. Modify the source code. +2. Build the container image. +3. Either push the image to a registry or load it into your kind cluster. +4. Deploy all the appropriate Kubernetes manifests for your application. + 1. Or, if this is an update, you'd instead scale the Deployment to 0 replicas, scale back to 1, and wait for the + new pod to be running. + +This process can take minutes, depending on how long each step takes. + +Here is the same workflow with Tilt: + +1. Run `tilt up` +2. Modify the source code +3. Wait for Tilt to update the container with your changes + +This ends up taking a fraction of the time, sometimes on the order of a few seconds! + +## Installing Tilt + +Follow Tilt's [instructions](https://docs.tilt.dev/install.html) for installation. + +## Installing cert-manager + +[cert-manager](https://cert-manager.io) is a prerequisite for running rukpak. Please follow the +[cert-manager installation documentation](https://cert-manager.io/docs/installation/). + +## Starting Tilt + +This is typically as short as: + +```shell +tilt up +``` + +**NOTE:** if you are using Podman, at least as of v4.5.1, you need to do this: + +```shell +DOCKER_BUILDKIT=0 tilt up +``` + +Otherwise, you'll see an error when Tilt tries to build your image that looks similar to: + +```text +Build Failed: ImageBuild: stat /var/tmp/libpod_builder2384046170/build/Dockerfile: no such file or directory +``` + +When Tilt starts, you'll see something like this in your terminal: + +```text +Tilt started on http://localhost:10350/ +v0.33.1, built 2023-06-28 + +(space) to open the browser +(s) to stream logs (--stream=true) +(t) to open legacy terminal mode (--legacy=true) +(ctrl-c) to exit +``` + +Typically, you'll want to press the space bar to have it open the UI in your web browser. + +Shortly after starting, Tilt processes the `Tiltfile`, resulting in: + +- Building the go binaries +- Building the images +- Loading the images into kind +- Running kustomize and applying everything except the Deployments that reference the images above +- Modifying the Deployments to use the just-built images +- Creating the Deployments + +## Making code changes + +Any time you change any of the files listed in the `deps` section in the `_binary` `local_resource`, +Tilt automatically rebuilds the go binary. As soon as the binary is rebuilt, Tilt pushes it (and only it) into the +appropriate running container, and then restarts the process.