Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add script to generate diffs between a cluster and the local branch #231

Merged
merged 3 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/diff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Diff
on:
pull_request: {}

jobs:
render-diff:
runs-on: ubuntu-latest
steps:

- name: Wait on Workflow
uses: lucasssvaz/wait-on-workflow@v1
with:
workflow: pr.yml
max-wait: 3
timeout: 60
sha: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Extract branch name
shell: bash
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
id: extract_branch

- name: Trigger diff on internal gitlab
run: |
curl -X POST \
--fail \
-F token=${{ secrets.GITLAB_CI_TOKEN }} \
-F ref=add/pipeline \
-F "variables[BRANCH]=${{ steps.extract_branch.outputs.branch }}" \
https://git.vshn.net/api/v4/projects/58084/trigger/pipeline
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ __debug_bin*

# Ignore crossplane packages
*.xpkg

hack/res
hack/tmp
hack/diff/function.yaml
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,11 @@ bootstrap: api-bootstrap generate ## API bootstrapping, create a new claim/compo
.PHONY: install-proxy
install-proxy:
kubectl apply -f hack/functionproxy

.PHONY: render-diff
render-diff: export IMG_TAG=$(shell git rev-parse --abbrev-ref HEAD | sed 's/\//_/g')
render-diff: ## Render diff between the cluster in KUBECONF and the local branch
# We check if the image is pullable, if so we pull it, otherwise we build the image
# this will speed up the compare in CI/CD environments.
if ! docker pull $(IMG); then $(MAKE) docker-build-branchtag; fi
hack/diff/compare.sh
146 changes: 146 additions & 0 deletions hack/diff/compare.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
#!/bin/bash

set -e

[ -z "${KUBECONFIG}" ] && echo "Please export KUBECONFIG" && exit 1

# get the state and all objects from each composite
function get_state() {
type="$1"
name="$2"
dir_name="hack/tmp/$type-$name"

echo "getting state of $type/$name"

mkdir -p "$dir_name"

while read -r res_type res_name
do
kubectl get "$res_type" "$res_name" -oyaml > "$dir_name/$res_type-$res_name.yaml"
done <<< "$(kubectl get "$type" "$name" -oyaml | yq -r '.spec.resourceRefs | .[] | .kind + " " + .name')"

}

# also get the claim namespace
function get_claim_namespace() {
type="$1"
name="$2"
dir_name="hack/tmp/$type-$name"

ns=$(kubectl get "$type" "$name" -oyaml | yq -r '.metadata.labels["crossplane.io/claim-namespace"]')
kubectl get ns "$ns" -oyaml > "$dir_name/namespace.yaml"

}

function run_single_diff() {
type="$1"
name="$2"
dir_name="hack/tmp/$type-$name"
res_dir_name="hack/res/$type/$name"

mkdir -p "$res_dir_name"

kubectl get "$type" "$name" -oyaml > hack/tmp/xr.yaml
comp=$(kubectl get "$type" "$name" -oyaml | yq -r '.spec.compositionRef.name')
echo "composition: $comp $type/$name"
kubectl get compositions.apiextensions.crossplane.io "$comp" -oyaml > hack/tmp/composition.yaml
crank_func render hack/tmp/xr.yaml hack/tmp/composition.yaml hack/diff/function.yaml -o "$dir_name" > "$res_dir_name/$3.yaml"
}

function crank_func() {
mkdir -p .work/bin
[ ! -f .work/bin/crank ] && curl -s https://releases.crossplane.io/stable/v1.17.0/bin/linux_amd64/crank -o .work/bin/crank
chmod +x .work/bin/crank
if .work/bin/crank -h > /dev/null 2>&1; then .work/bin/crank "$@";
else go run github.com/crossplane/crossplane/cmd/[email protected] "$@"; fi
}

function get_running_func_version() {
version=$(kubectl get function function-appcat -oyaml | yq -r '.spec.package' | cut -d ":" -f2)
echo "${version%"-func"}"
}

function get_pnt_func_version() {
kubectl get function function-patch-and-transform -oyaml | yq -r '.spec.package' | cut -d ":" -f2
}

function template_func_file() {
export PNT_VERSION=$1
export APPCAT_VERSION=$2
cat "$(dirname "$0")/function.yaml.tmpl" | envsubst > "$(dirname "$0")/function.yaml"
}

function diff_func() {

while read -r type name rest
do
# we only get the state on the first run for two reasons:
# speed things up
# avoid any diffs that could come from actual changes on the cluster
[ "first" == "$1" ] && get_state "$type" "$name"
get_claim_namespace "$type" "$name"
run_single_diff "$type" "$name" "$1"
done <<< "$(kubectl get composite --no-headers | sed 's/\// /g' )"

}

# do the diff
function first_diff() {
diff_func "first"
}

function second_diff() {
diff_func "second"
}


function compare() {
for f in hack/res/*/*;
do
echo "comparing $f"
# enable color
# ignore changed array order
# exclude nested managedFields in kube objects
# exclude nested resourceVersion
# don't print the huge dyff header
dyff between \
--color=on \
-i \
--exclude-regexp "spec.forProvider.manifest.metadata.managedFields.*" \
--exclude "spec.forProvider.manifest.metadata.resourceVersion" \
--omit-header \
"$f/first.yaml" "$f/second.yaml"
# diff "$f/first.yaml" "$f/second.yaml"
done
}

function dyff() {
mkdir -p .work/bin
[ ! -f .work/bin/dyff ] && curl -sL https://github.com/homeport/dyff/releases/download/v1.9.1/dyff_1.9.1_linux_amd64.tar.gz -o .work/bin/dyff.tar.gz && \
tar xvfz .work/bin/dyff.tar.gz -C .work/bin > /dev/null 2>&1
chmod +x .work/bin/dyff
if .work/bin/dyff version > /dev/null 2>&1 ; then .work/bin/dyff "$@";
else go run github.com/homeport/dyff/cmd/[email protected] "$@"; fi
}

function clean() {
rm -rf hack/tmp
rm -rf hack/res
rm -rf "$(dirname "$0")/function.yaml"
}

clean
trap clean EXIT

template_func_file "$(get_pnt_func_version)" "$(get_running_func_version)"

echo "Render live manifests"
first_diff

template_func_file "$(get_pnt_func_version)" "$(git rev-parse --abbrev-ref HEAD | sed 's/\//_/g')"

echo "Render against branch"
second_diff

echo "Comparing"
compare
17 changes: 17 additions & 0 deletions hack/diff/function.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: pkg.crossplane.io/v1beta1
kind: Function
metadata:
name: function-appcat
annotations:
render.crossplane.io/runtime-docker-cleanup: Stop
spec:
package: ghcr.io/vshn/appcat:$APPCAT_VERSION
---
apiVersion: pkg.crossplane.io/v1
kind: Function
metadata:
name: function-patch-and-transform
annotations:
render.crossplane.io/runtime-docker-cleanup: Stop
spec:
package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:$PNT_VERSION
12 changes: 12 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"

"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"github.com/vshn/appcat/v4/cmd"
)
Expand All @@ -22,6 +23,16 @@ func init() {

func main() {

cmd, _, err := rootCmd.Find(os.Args[1:])

// default to functions if no cmd is given
// necessary to properly support `crank render`
// from https://github.com/spf13/cobra/issues/823#issuecomment-870027246
if err == nil && cmd.Use == rootCmd.Use && cmd.Flags().Parse(os.Args[1:]) != pflag.ErrHelp {
args := append([]string{"functions"}, os.Args[1:]...)
rootCmd.SetArgs(args)
}

if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
Expand All @@ -35,6 +46,7 @@ var (
Short: "AppCat",
Long: "AppCat controllers, api servers, grpc servers and probers",
PersistentPreRunE: setupLogging,
Use: "appcat",
}
)

Expand Down
Loading
Loading