diff --git a/.github/workflows/e2e_simple.yml b/.github/workflows/e2e_simple.yml new file mode 100644 index 000000000..f209fa73b --- /dev/null +++ b/.github/workflows/e2e_simple.yml @@ -0,0 +1,65 @@ +name: e2e test simple + +on: + workflow_dispatch: + inputs: + skip-undeploy: + description: "Skip undeploy" + required: false + default: "false" + pull_request: + +env: + container_registry: ghcr.io/edgelesssys + azure_resource_group: nunki-ci + +jobs: + test: + runs-on: ubuntu-22.04 + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + - uses: cachix/install-nix-action@7ac1ec25491415c381d9b62f0657c7a028df52a7 # v24 + with: + github_access_token: ${{ secrets.GITHUB_TOKEN }} + - uses: DeterminateSystems/magic-nix-cache-action@8a218f9e264e9c3803c9a1ee1c30d8e4ab55be63 #v2 + - name: Log in to ghcr.io Container registry + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Login to Azure + uses: azure/login@92a5484dfaf04ca78a94597f4f19fea633851fa2 # v1.4.7 + with: + creds: ${{ secrets.NUNKI_CI_INFRA_AZURE }} + - uses: nicknovitski/nix-develop@a2060d116a50b36dfab02280af558e73ab52427d # v1.1.0 + - name: Generate namespace suffix + id: ns + run: | + uuid=$(cat /proc/sys/kernel/random/uuid) + uid=${uuid##*-} + echo "namespace_suffix=$uid" >> "$GITHUB_OUTPUT" + - name: Create justfile.env + run: | + cat < justfile.env + container_registry=${{ env.container_registry }} + azure_resource_group=${{ env.azure_resource_group }} + namespace_suffix=-${{ steps.ns.outputs.namespace_suffix }} + EOF + - name: Get credentials for CI cluster + run: | + just get-credentials + - name: Build, deploy, nunki generate, nunki set, nunki verify + run: | + just default simple + - name: Summary + run: | + cat ./workspace/just.namespace | tee -a "${GITHUB_STEP_SUMMARY}" + cat ./workspace/just.perf | tee -a "${GITHUB_STEP_SUMMARY}" + - name: Undeploy + if: always() && inputs.skip-undeploy != 'true' + run: | + just undeploy diff --git a/deployments/emojivoto/coordinator.yml b/deployments/emojivoto/coordinator.yml index 1ef5d8a70..a6806e4f2 100644 --- a/deployments/emojivoto/coordinator.yml +++ b/deployments/emojivoto/coordinator.yml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: coordinator - namespace: edg-emojivoto + namespace: edg-default spec: selector: matchLabels: @@ -33,7 +33,7 @@ apiVersion: v1 kind: Service metadata: name: coordinator - namespace: edg-emojivoto + namespace: edg-default spec: ports: - name: intercom diff --git a/deployments/emojivoto/emoji.yml b/deployments/emojivoto/emoji.yml index 59716b8b2..59a82d445 100644 --- a/deployments/emojivoto/emoji.yml +++ b/deployments/emojivoto/emoji.yml @@ -2,13 +2,13 @@ kind: ServiceAccount apiVersion: v1 metadata: name: emoji - namespace: edg-emojivoto + namespace: edg-default --- apiVersion: apps/v1 kind: Deployment metadata: name: emoji - namespace: edg-emojivoto + namespace: edg-default labels: app.kubernetes.io/name: emoji app.kubernetes.io/part-of: emojivoto @@ -32,7 +32,7 @@ spec: imagePullPolicy: Always env: - name: COORDINATOR_HOST - value: coordinator.edg-emojivoto + value: coordinator volumeMounts: - name: tls-certs mountPath: /tls-config @@ -71,7 +71,7 @@ apiVersion: v1 kind: Service metadata: name: emoji-svc - namespace: edg-emojivoto + namespace: edg-default spec: selector: app: emoji-svc diff --git a/deployments/emojivoto/ns.yml b/deployments/emojivoto/ns.yml index 321162eb8..ed2712cc8 100644 --- a/deployments/emojivoto/ns.yml +++ b/deployments/emojivoto/ns.yml @@ -1,4 +1,4 @@ apiVersion: v1 kind: Namespace metadata: - name: edg-emojivoto + name: edg-default diff --git a/deployments/emojivoto/portforwarder.yml b/deployments/emojivoto/portforwarder.yml index 939a1f3e3..624d76f84 100644 --- a/deployments/emojivoto/portforwarder.yml +++ b/deployments/emojivoto/portforwarder.yml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Pod metadata: name: port-forwarder-coordinator - namespace: default + namespace: edg-default spec: containers: - name: port-forwarder @@ -11,7 +11,7 @@ spec: - name: LISTEN_PORT value: "1313" - name: FORWARD_HOST - value: coordinator.edg-emojivoto + value: coordinator - name: FORWARD_PORT value: "1313" command: @@ -24,8 +24,8 @@ spec: apiVersion: v1 kind: Pod metadata: - name: port-forwarder-2 - namespace: default + name: port-forwarder-emojivoto-web + namespace: edg-default spec: containers: - name: port-forwarder @@ -34,7 +34,7 @@ spec: - name: LISTEN_PORT value: "8080" - name: FORWARD_HOST - value: web-svc.edg-emojivoto + value: web-svc - name: FORWARD_PORT value: "443" command: diff --git a/deployments/emojivoto/vote-bot.yml b/deployments/emojivoto/vote-bot.yml index 3bcb35762..63fed2fd8 100644 --- a/deployments/emojivoto/vote-bot.yml +++ b/deployments/emojivoto/vote-bot.yml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: vote-bot - namespace: edg-emojivoto + namespace: edg-default labels: app.kubernetes.io/name: vote-bot app.kubernetes.io/part-of: emojivoto @@ -24,7 +24,7 @@ spec: - emojivoto-vote-bot env: - name: WEB_HOST - value: web-svc.edg-emojivoto:443 + value: web-svc:443 image: ghcr.io/3u13r/emojivoto-web:coco-1 imagePullPolicy: Always name: vote-bot diff --git a/deployments/emojivoto/voting.yml b/deployments/emojivoto/voting.yml index ff04afd0a..48c31f848 100644 --- a/deployments/emojivoto/voting.yml +++ b/deployments/emojivoto/voting.yml @@ -2,13 +2,13 @@ kind: ServiceAccount apiVersion: v1 metadata: name: voting - namespace: edg-emojivoto + namespace: edg-default --- apiVersion: apps/v1 kind: Deployment metadata: name: voting - namespace: edg-emojivoto + namespace: edg-default labels: app.kubernetes.io/name: voting app.kubernetes.io/part-of: emojivoto @@ -32,7 +32,7 @@ spec: imagePullPolicy: Always env: - name: COORDINATOR_HOST - value: coordinator.edg-emojivoto + value: coordinator volumeMounts: - name: tls-certs mountPath: /tls-config @@ -71,7 +71,7 @@ apiVersion: v1 kind: Service metadata: name: voting-svc - namespace: edg-emojivoto + namespace: edg-default spec: selector: app: voting-svc diff --git a/deployments/emojivoto/web.yml b/deployments/emojivoto/web.yml index bc5830ed2..41a159531 100644 --- a/deployments/emojivoto/web.yml +++ b/deployments/emojivoto/web.yml @@ -2,13 +2,13 @@ kind: ServiceAccount apiVersion: v1 metadata: name: web - namespace: edg-emojivoto + namespace: edg-default --- apiVersion: apps/v1 kind: Deployment metadata: name: web - namespace: edg-emojivoto + namespace: edg-default labels: app.kubernetes.io/name: web app.kubernetes.io/part-of: emojivoto @@ -32,7 +32,7 @@ spec: imagePullPolicy: Always env: - name: COORDINATOR_HOST - value: coordinator.edg-emojivoto + value: coordinator volumeMounts: - name: tls-certs mountPath: /tls-config @@ -42,9 +42,9 @@ spec: - name: WEB_PORT value: "8080" - name: EMOJISVC_HOST - value: emoji-svc.edg-emojivoto:8080 + value: emoji-svc:8080 - name: VOTINGSVC_HOST - value: voting-svc.edg-emojivoto:8080 + value: voting-svc:8080 - name: INDEX_BUNDLE value: dist/index_bundle.js - name: EDG_CERT_PATH @@ -75,7 +75,7 @@ apiVersion: v1 kind: Service metadata: name: web-svc - namespace: edg-emojivoto + namespace: edg-default spec: type: ClusterIP selector: diff --git a/deployments/openssl/coordinator.yml b/deployments/openssl/coordinator.yml index b59bc8b4f..a6806e4f2 100644 --- a/deployments/openssl/coordinator.yml +++ b/deployments/openssl/coordinator.yml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: coordinator - namespace: edg-openssl + namespace: edg-default spec: selector: matchLabels: @@ -33,7 +33,7 @@ apiVersion: v1 kind: Service metadata: name: coordinator - namespace: edg-openssl + namespace: edg-default spec: ports: - name: intercom diff --git a/deployments/openssl/initializer.yml b/deployments/openssl/initializer.yml index 4026d2f8e..01b0f26dd 100644 --- a/deployments/openssl/initializer.yml +++ b/deployments/openssl/initializer.yml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: workload - namespace: edg-openssl + namespace: edg-default spec: selector: matchLabels: @@ -20,7 +20,7 @@ spec: imagePullPolicy: Always env: - name: COORDINATOR_HOST - value: coordinator.edg-openssl + value: coordinator volumeMounts: - name: tls-certs mountPath: /tls-config diff --git a/deployments/openssl/ns.yml b/deployments/openssl/ns.yml index 845a2f74c..ed2712cc8 100644 --- a/deployments/openssl/ns.yml +++ b/deployments/openssl/ns.yml @@ -1,4 +1,4 @@ apiVersion: v1 kind: Namespace metadata: - name: edg-openssl + name: edg-default diff --git a/deployments/openssl/openssl-backend.yml b/deployments/openssl/openssl-backend.yml index 9acb0b418..9df86e75c 100644 --- a/deployments/openssl/openssl-backend.yml +++ b/deployments/openssl/openssl-backend.yml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: openssl-backend - namespace: edg-openssl + namespace: edg-default spec: selector: matchLabels: @@ -20,7 +20,7 @@ spec: imagePullPolicy: Always env: - name: COORDINATOR_HOST - value: coordinator.edg-openssl + value: coordinator volumeMounts: - name: tls-certs mountPath: /tls-config @@ -48,7 +48,7 @@ apiVersion: v1 kind: Service metadata: name: openssl-backend - namespace: edg-openssl + namespace: edg-default spec: selector: run: openssl-backend diff --git a/deployments/openssl/openssl-client.yml b/deployments/openssl/openssl-client.yml index 0754c6301..5f561e783 100644 --- a/deployments/openssl/openssl-client.yml +++ b/deployments/openssl/openssl-client.yml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: openssl-client - namespace: edg-openssl + namespace: edg-default spec: selector: matchLabels: @@ -20,7 +20,7 @@ spec: imagePullPolicy: Always env: - name: COORDINATOR_HOST - value: coordinator.edg-openssl + value: coordinator volumeMounts: - name: tls-certs mountPath: /tls-config diff --git a/deployments/openssl/openssl-frontend.yml b/deployments/openssl/openssl-frontend.yml index 2cfdc099b..d020483f7 100644 --- a/deployments/openssl/openssl-frontend.yml +++ b/deployments/openssl/openssl-frontend.yml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: openssl-frontend - namespace: edg-openssl + namespace: edg-default spec: selector: matchLabels: @@ -20,7 +20,7 @@ spec: imagePullPolicy: Always env: - name: COORDINATOR_HOST - value: coordinator.edg-openssl + value: coordinator volumeMounts: - name: tls-certs mountPath: /tls-config @@ -43,7 +43,7 @@ apiVersion: v1 kind: Service metadata: name: openssl-frontend - namespace: edg-openssl + namespace: edg-default spec: selector: run: openssl-frontend diff --git a/deployments/openssl/portforwarder.yml b/deployments/openssl/portforwarder.yml index a76542773..d15748214 100644 --- a/deployments/openssl/portforwarder.yml +++ b/deployments/openssl/portforwarder.yml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Pod metadata: name: port-forwarder-coordinator - namespace: default + namespace: edg-default spec: containers: - name: port-forwarder @@ -11,7 +11,7 @@ spec: - name: LISTEN_PORT value: "1313" - name: FORWARD_HOST - value: coordinator.edg-openssl + value: coordinator - name: FORWARD_PORT value: "1313" command: diff --git a/deployments/simple/coordinator.yml b/deployments/simple/coordinator.yml index cd93dd08b..a6806e4f2 100644 --- a/deployments/simple/coordinator.yml +++ b/deployments/simple/coordinator.yml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: coordinator - namespace: edg-coco + namespace: edg-default spec: selector: matchLabels: @@ -33,7 +33,7 @@ apiVersion: v1 kind: Service metadata: name: coordinator - namespace: edg-coco + namespace: edg-default spec: ports: - name: intercom diff --git a/deployments/simple/initializer.yml b/deployments/simple/initializer.yml index 0016136df..01b0f26dd 100644 --- a/deployments/simple/initializer.yml +++ b/deployments/simple/initializer.yml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: name: workload - namespace: edg-coco + namespace: edg-default spec: selector: matchLabels: @@ -20,7 +20,7 @@ spec: imagePullPolicy: Always env: - name: COORDINATOR_HOST - value: coordinator.edg-coco + value: coordinator volumeMounts: - name: tls-certs mountPath: /tls-config diff --git a/deployments/simple/ns.yml b/deployments/simple/ns.yml index 55893d533..ed2712cc8 100644 --- a/deployments/simple/ns.yml +++ b/deployments/simple/ns.yml @@ -1,4 +1,4 @@ apiVersion: v1 kind: Namespace metadata: - name: edg-coco + name: edg-default diff --git a/deployments/simple/portforwarder.yml b/deployments/simple/portforwarder.yml index 5271a78d2..d15748214 100644 --- a/deployments/simple/portforwarder.yml +++ b/deployments/simple/portforwarder.yml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Pod metadata: name: port-forwarder-coordinator - namespace: default + namespace: edg-default spec: containers: - name: port-forwarder @@ -11,7 +11,7 @@ spec: - name: LISTEN_PORT value: "1313" - name: FORWARD_HOST - value: coordinator.edg-coco + value: coordinator - name: FORWARD_PORT value: "1313" command: diff --git a/justfile b/justfile index 0f016ed01..229492a11 100644 --- a/justfile +++ b/justfile @@ -1,10 +1,11 @@ # Undeploy, rebuild, deploy. -default target=default_deploy_target: undeploy coordinator initializer openssl (deploy target) +default target=default_deploy_target: undeploy coordinator initializer openssl (deploy target) set verify # Build the coordinator, containerize and push it. coordinator: nix run .#push-coordinator -- "$container_registry/nunki/coordinator:latest" +# Build the openssl container and push it. openssl: nix run .#push-openssl -- "$container_registry/nunki/openssl:latest" @@ -13,7 +14,7 @@ initializer: nix run .#push-initializer -- "$container_registry/nunki/initializer:latest" default_deploy_target := "simple" -worspace_dir := "workspace" +workspace_dir := "workspace" # Generate policies, apply Kubernetes manifests. deploy target=default_deploy_target: (generate target) apply @@ -21,33 +22,43 @@ deploy target=default_deploy_target: (generate target) apply # Generate policies, update manifest. generate target=default_deploy_target: #!/usr/bin/env bash - mkdir -p ./{{worspace_dir}} - rm -rf ./{{worspace_dir}}/deployment - cp -R ./deployments/{{target}} ./{{worspace_dir}}/deployment - nix run .#yq-go -- -i ". \ - | with(select(.spec.template.spec.containers[].image | contains(\"nunki/coordinator\")); \ - .spec.template.spec.containers[0].image = \"${container_registry}/nunki/coordinator:latest\")" \ - ./{{worspace_dir}}/deployment/coordinator.yml - for f in ./{{worspace_dir}}/deployment/*.yml; do - nix run .#yq-go -- -i ". \ - | with(select(.spec.template.spec.initContainers[].image | contains(\"nunki/initializer\")); \ - .spec.template.spec.initContainers[0].image = \"${container_registry}/nunki/initializer:latest\")" \ - "${f}" - done + set -euo pipefail + mkdir -p ./{{workspace_dir}} + rm -rf ./{{workspace_dir}}/* + cp -R ./deployments/{{target}} ./{{workspace_dir}}/deployment + echo "{{target}}${namespace_suffix}" > ./{{workspace_dir}}/just.namespace + nix run .#patch-nunki-image-hashes -- ./{{workspace_dir}}/deployment + nix run .#kypatch images -- ./{{workspace_dir}}/deployment \ + --replace ghcr.io/edgelesssys ${container_registry} + nix run .#kypatch namespace -- ./{{workspace_dir}}/deployment \ + --replace edg-default {{target}}${namespace_suffix} + t=$(date +%s) nix run .#cli -- generate \ - -m ./{{worspace_dir}}/manifest.json \ - -p ./{{worspace_dir}} \ + -m ./{{workspace_dir}}/manifest.json \ + -p ./{{workspace_dir}} \ -s genpolicy-msft.json \ - ./{{worspace_dir}}/deployment/*.yml + ./{{workspace_dir}}/deployment/*.yml + duration=$(( $(date +%s) - $t )) + echo "Generated policies in $duration seconds." + echo "generate $duration" >> ./{{workspace_dir}}/just.perf # Apply Kubernetes manifests from /deployment apply: - kubectl apply -f ./{{worspace_dir}}/deployment/ns.yml - kubectl apply -f ./{{worspace_dir}}/deployment + kubectl apply -f ./{{workspace_dir}}/deployment/ns.yml + kubectl apply -f ./{{workspace_dir}}/deployment # Delete Kubernetes manifests. undeploy: - -kubectl delete -f ./{{worspace_dir}}/deployment + #!/usr/bin/env bash + set -euo pipefail + if [[ ! -d ./{{workspace_dir}} ]]; then + echo "No workspace directory found, nothing to undeploy." + exit 0 + fi + ns=$(cat ./{{workspace_dir}}/just.namespace) + if kubectl get ns $ns 2> /dev/null; then + kubectl delete -f ./{{workspace_dir}}/deployment + fi # Create a CoCo-enabled AKS cluster. create: @@ -56,26 +67,40 @@ create: # Set the manifest at the coordinator. set: #!/usr/bin/env bash - kubectl port-forward pod/port-forwarder-coordinator 1313 & + set -euo pipefail + ns=$(cat ./{{workspace_dir}}/just.namespace) + nix run .#kubectl-wait-ready -- $ns coordinator + kubectl -n $ns port-forward pod/port-forwarder-coordinator 1313 & PID=$! + trap "kill $PID" EXIT sleep 1 + t=$(date +%s) nix run .#cli -- set \ - -m ./{{worspace_dir}}/manifest.json \ + -m ./{{workspace_dir}}/manifest.json \ -c localhost:1313 \ - ./{{worspace_dir}}/deployment/*.yml - kill $PID + ./{{workspace_dir}}/deployment/*.yml + duration=$(( $(date +%s) - $t )) + echo "Set manifest in $duration seconds." + echo "set $duration" >> ./{{workspace_dir}}/just.perf # Verify the Coordinator. verify: #!/usr/bin/env bash - rm -rf ./{{worspace_dir}}/verify - kubectl port-forward pod/port-forwarder-coordinator 1313 & + set -euo pipefail + rm -rf ./{{workspace_dir}}/verify + ns=$(cat ./{{workspace_dir}}/just.namespace) + nix run .#kubectl-wait-ready -- $ns coordinator + kubectl -n $ns port-forward pod/port-forwarder-coordinator 1313 & PID=$! + trap "kill $PID" EXIT sleep 1 + t=$(date +%s) nix run .#cli -- verify \ -c localhost:1313 \ - -o ./{{worspace_dir}}/verify - kill $PID + -o ./{{workspace_dir}}/verify + duration=$(( $(date +%s) - $t )) + echo "Verified in $duration seconds." + echo "verify $duration" >> ./{{workspace_dir}}/just.perf # Load the kubeconfig from the running AKS cluster. get-credentials: @@ -83,6 +108,13 @@ get-credentials: --resource-group "$azure_resource_group" \ --name "$azure_resource_group" +# Load the kubeconfig from the CI AKS cluster. +get-credentials-ci: + nix run .#azure-cli -- aks get-credentials \ + --resource-group "nunki-ci" \ + --name "nunki-ci" \ + --admin + # Destroy a running AKS cluster. destroy: nix run .#destroy-coco-aks -- --name="$azure_resource_group" @@ -106,21 +138,14 @@ demodir: nix build .#nunki.cli cp ./result-cli/bin/cli "${d}/nunki" cp -R ./deployments/emojivoto "${d}/deployment" - nix run .#yq-go -- -i ". \ - | with(select(.spec.template.spec.containers[].image | contains(\"nunki/coordinator\")); \ - .spec.template.spec.containers[0].image = \"${container_registry}/nunki/coordinator:latest\")" \ - ${d}/deployment/coordinator.yml - for f in ${d}/deployment/*.yml; do - nix run .#yq-go -- -i ". \ - | with(select(.spec.template.spec.initContainers[].image | contains(\"nunki/initializer\")); \ - .spec.template.spec.initContainers[0].image = \"${container_registry}/nunki/initializer:latest\")" \ - "${f}" - done + nix run .#patch-nunki-image-hashes -- "${d}/deployment" + nix run .#kypatch images -- "${d}/deployment" \ + --replace ghcr.io/edgelesssys ${container_registry} echo "Demo directory ready at ${d}" # Cleanup auxiliary files, caches etc. -clean: - rm -rf ./{{worspace_dir}} +clean: undeploy + rm -rf ./{{workspace_dir}} rm -rf ./layers_cache # Template for the justfile.env file. @@ -129,6 +154,8 @@ rctemplate := ''' container_registry="" # Azure resource group/ resource name. Resource group will be created. azure_resource_group="" +# Namespace suffix, can be empty. Will be used when patching namespaces. +namespace_suffix="" ''' # Developer onboarding. diff --git a/packages/default.nix b/packages/default.nix index d0ab718e7..12f6aa81d 100644 --- a/packages/default.nix +++ b/packages/default.nix @@ -150,4 +150,63 @@ rec { runtimeInputs = [ go pkgs.golangci-lint ]; text = ''golangci-lint "$@"''; }; + + patch-nunki-image-hashes = writeShellApplication { + name = "patch-nunki-image-hashes"; + runtimeInputs = [ + coordinator + crane + initializer + kypatch + ]; + text = '' + targetPath=$1 + + tmpdir=$(mktemp -d) + trap 'rm -rf $tmpdir' EXIT + + gunzip < "${coordinator}" > "$tmpdir/coordinator.tar" + gunzip < "${initializer}" > "$tmpdir/initializer.tar" + + coordHash=$(crane digest --tarball "$tmpdir/coordinator.tar") + initHash=$(crane digest --tarball "$tmpdir/initializer.tar") + + kypatch images "$targetPath" \ + --replace "nunki/coordinator:latest" "nunki/coordinator@$coordHash" \ + --replace "nunki/initializer:latest" "nunki/initializer@$initHash" + ''; + }; + + kypatch = writeShellApplication { + name = "kypatch"; + runtimeInputs = [ yq-go ]; + text = builtins.readFile ./kypatch.sh; + }; + + kubectl-wait-ready = writeShellApplication { + name = "kubectl-wait-ready"; + runtimeInputs = [ kubectl ]; + text = '' + namespace=$1 + name=$2 + + timeout=180 + + interval=4 + while [ $timeout -gt 0 ]; do + if kubectl -n "$namespace" get pods | grep -q "$name"; then + break + fi + sleep "$interval" + timeout=$((timeout - interval)) + done + + kubectl wait \ + --namespace "$namespace" \ + --selector "run=$name" \ + --for=condition=Ready \ + --timeout="''${timeout}s" \ + pods + ''; + }; } diff --git a/packages/kypatch.sh b/packages/kypatch.sh new file mode 100644 index 000000000..a9a1fd23d --- /dev/null +++ b/packages/kypatch.sh @@ -0,0 +1,149 @@ +#!/user/bin/env bash + +set -euo pipefail + +function usage() { + cat <&2 +Usage: $0 [options] + +Options: + --replace Replace current value with new value + --help Show this help message +EOF +} + +function printReplaces() { + echo "Replacements:" >&2 + for replace in "${replaces[@]}"; do + currentImage=${replace%% *} + newImage=${replace##* } + echo " $currentImage => $newImage" >&2 + done +} + +function mapTypeToPaths() { + local -n outPaths=$1 + local type=$2 + + case $type in + images) + # shellcheck disable=SC2034 + outPaths=( + ".spec.containers[].image" + ".spec.template.spec.containers[].image" + ".spec.template.spec.initContainers[].image" + ) + ;; + namespace) + # shellcheck disable=SC2034 + outPaths=( + ".metadata.namespace" + ) + ;; + *) + echo "Unknown replace target type $type" >&2 + exit 1 + ;; + esac +} + +function extraSteps() { + local type=$1 + local file=$2 + local replace=$3 + + current=${replace%% *} + new=${replace##* } + + case $type in + namespace) + # Rename metadata.name if kind is Namespace + yq -i "\ + with( + select(.kind == \"Namespace\") | \ + select(.metadata.name | contains(\"${current}\")); \ + .metadata.name |= sub(\"${current}\", \"${new}\") \ + )" "$file" + ;; + esac +} + +function patchFile() { + local type=$1 + local file=$2 + shift 2 + local replaces=("$@") + + echo "Patching file $file" >&2 + + for replace in "${replaces[@]}"; do + current=${replace%% *} + new=${replace##* } + + local paths + mapTypeToPaths paths "$type" + + for p in "${paths[@]}"; do + yq -i "\ + with(select(${p} | contains(\"${current}\")); \ + ${p} |= sub(\"${current}\", \"${new}\") \ + )" "$file" + + done + + extraSteps "$type" "$file" "$replace" + done +} + +function patchRecursive() { + local type=$1 + local dir=$2 + shift 2 + local replaces=("$@") + + find "$dir" \ + -type f \ + -name '*.yaml' -o \ + -name '*.yml' | while IFS= read -r file; do + patchFile "$type" "$file" "${replaces[@]}" + done +} + +function main() { + positionalArgs=() + replaces=() + while [[ $# -gt 0 ]]; do + case $1 in + --replace) + replaces+=("$2 $3") + shift 3 # past flag, current, new + ;; + --help) + usage + exit 0 + ;; + -*) + echo "Unknown option $1" >&2 + exit 1 + ;; + *) + positionalArgs+=("$1") # save positional arg + shift # past argument + ;; + esac + done + set -- "${positionalArgs[@]}" # restore positional parameters + + type=$1 + targetPath=$2 + + printReplaces "${replaces[@]}" + + if [[ -d $targetPath ]]; then + patchRecursive "$type" "$targetPath" "${replaces[@]}" + exit 0 + fi + patchFile "$type" "$targetPath" "${replaces[@]}" +} + +main "$@"