From a0a35c027acc1a0a4f5466cd426bab26155ff10b Mon Sep 17 00:00:00 2001 From: Jakub Cechacek Date: Mon, 18 Sep 2023 15:57:15 +0200 Subject: [PATCH] DBZ-6738 OLM support scripts --- .gitignore | 5 +- scripts/create-olm-bundle-image.sh | 67 ++++++++++++ scripts/create-olm-bundle.sh | 94 +++++++++++++++++ scripts/create-olm-test-catalog.sh | 149 +++++++++++++++++++++++++++ scripts/create-olm-test-resources.sh | 84 +++++++++++++++ scripts/functions.sh | 38 +++++++ 6 files changed, 436 insertions(+), 1 deletion(-) create mode 100755 scripts/create-olm-bundle-image.sh create mode 100755 scripts/create-olm-bundle.sh create mode 100755 scripts/create-olm-test-catalog.sh create mode 100755 scripts/create-olm-test-resources.sh create mode 100644 scripts/functions.sh diff --git a/.gitignore b/.gitignore index bf781c7..b216644 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,7 @@ nb-configuration.xml /.quarkus/cli/plugins/ # kubernetes -kubernetes.json \ No newline at end of file +kubernetes.json + +# OLM build workspace +olm/ \ No newline at end of file diff --git a/scripts/create-olm-bundle-image.sh b/scripts/create-olm-bundle-image.sh new file mode 100755 index 0000000..e3bd497 --- /dev/null +++ b/scripts/create-olm-bundle-image.sh @@ -0,0 +1,67 @@ +#! /usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source "$SCRIPT_DIR/functions.sh" +checkDependencies + +OPTS=`getopt -o v:i: --long version:,input:,bundle-image:,push -n 'parse-options' -- "$@"` +if [ $? != 0 ] ; then echo "Failed parsing options." >&2 ; exit 1 ; fi +eval set -- "$OPTS" + +# Set defaults +VERSION="all" +BUNDLE_IMAGE_PULL_URL="quay.io/debezium/operator-bundle" +INPUT_DIR_BASE="$PWD/olm/bundles" +PUSH_IMAGES=false + +# Process script options +while true; do + case "$1" in + -v | --version ) VERSION=$2; shift; shift ;; + -i | --input ) INPUT_DIR_BASE=$2; shift; shift ;; + --bundle-image ) BUNDLE_IMAGE_PULL_URL=$2; shift; shift ;; + --push ) PUSH_IMAGES=true; shift ;; + -- ) shift; break ;; + * ) break ;; + esac +done + +if [[ ! -d "$INPUT_DIR_BASE" ]]; then + echo "Input directory $INPUT_DIR_BASE not exists!" + exit 1 +fi + +echo "" +echo "Creating OLM bundle image" +echo "Input dir: $INPUT_DIR_BASE" +echo "Bundle version(s): $VERSION" +echo "" + +if [[ $VERSION = "all" ]]; then + BUNDLES=( $INPUT_DIR_BASE/*/ ) +else + BUNDLES=( "$INPUT_DIR_BASE/$VERSION" ) +fi + + +for bundle in "${BUNDLES[@]}"; do + name="$(csvName $bundle)" + version="$(csvVersion $bundle)" + path="$(echo "${bundle%/}")" + image="$BUNDLE_IMAGE_PULL_URL:$version" + + echo "" + echo "Building bundle from $bundle" + echo "Bundle image: $image" + echo "" + + docker build -t "$image" -f "$path/bundle.Dockerfile" "$path" + + if [[ "$PUSH_IMAGES" = true ]]; then + echo "" + echo "Pushing image: $image" + docker push "$image" + fi +done; + diff --git a/scripts/create-olm-bundle.sh b/scripts/create-olm-bundle.sh new file mode 100755 index 0000000..b0df628 --- /dev/null +++ b/scripts/create-olm-bundle.sh @@ -0,0 +1,94 @@ +#! /usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source "$SCRIPT_DIR/functions.sh" +checkDependencies + +OPTS=`getopt -o v:t:r:i:o:f --long version:,tag:,replaces:,input:,output:,image:,force -n 'parse-options' -- "$@"` +if [ $? != 0 ] ; then echo "Failed parsing options." >&2 ; exit 1 ; fi +eval set -- "$OPTS" + +# Set defaults +REPLACES_VERSION="none" +OUTPUT_DIR_BASE="$PWD/olm/bundles" +IMAGE_PULL_URL="quay.io/debezium/operator" +FORCE=false + +# Process script options +while true; do + case "$1" in + -v | --version ) VERSION=$2; shift; shift ;; + -t | --tag ) TAG=$2; shift; shift ;; + -r | --replaces ) REPLACES_VERSION=$2; shift; shift ;; + -i | --input ) INPUT_DIR=$2; shift; shift ;; + -o | --output ) OUTPUT_DIR_BASE=$2; shift; shift ;; + -f | --force ) FORCE=true; shift ;; + --image ) IMAGE_PULL_URL=$2; shift; shift ;; + -- ) shift; break ;; + * ) break ;; + esac +done + +if [[ ! -d "$INPUT_DIR" ]]; then + echo "Input directory $INPUT_DIR not exists!" + exit 1 +fi + +# Set variables +OUTPUT_DIR="${OUTPUT_DIR_BASE%/}/$VERSION" +OPERATOR_IMAGE="$IMAGE_PULL_URL:${TAG:-$VERSION}" + +if [[ -d "$OUTPUT_DIR" && "$FORCE" = true ]]; then + echo "Removing exiting output directory '$OUTPUT_DIR'" + rm -rf "$OUTPUT_DIR" +fi + +if [[ -d "$OUTPUT_DIR" ]]; then + echo "Directory $OUTPUT_DIR already exists!" + echo "Use -f / --force to overwrite" + exit 2 +fi + +echo "" +echo "Creating OLM bundle for version '$VERSION 'replacing version '$REPLACES_VERSION'" +echo "Operator image: $OPERATOR_IMAGE" +echo "Input dir: $INPUT_DIR" +echo "" + +mkdir -p "$OUTPUT_DIR_BASE" +# Copy the files generated by Quarkus during the maven build +cp -r "$INPUT_DIR" "$OUTPUT_DIR" + +# Find and rename the CSV YAML to include version +CSV_PATH="$(csvPath "$OUTPUT_DIR")" +# Rename +mv "$CSV_PATH" "$OUTPUT_DIR/manifests/debezium-operator.v$VERSION.clusterserviceversion.yaml" +# Update CSV path +CSV_PATH="$(csvPath "$OUTPUT_DIR")" + +# Insert operator image coordinate +yq ea -i ".metadata.annotations.containerImage = \"$OPERATOR_IMAGE\"" "$CSV_PATH" +yq ea -i ".spec.install.spec.deployments[0].spec.template.spec.containers[0].image = \"$OPERATOR_IMAGE\"" "$CSV_PATH" + +# Edit the CSV version, replaces, etc. +yq ea -i ".metadata.annotations.createdAt = \"$(date "+%D %T")\"" "$CSV_PATH" +yq ea -i ".spec.version = \"$VERSION\"" "$CSV_PATH" +yq ea -i ".metadata.name = \"debezium-operator.v$VERSION\"" "$CSV_PATH" + +if [[ $REPLACES_VERSION = "none" ]]; then + yq ea -i "del(.spec.replaces)" "$CSV_PATH" +else + yq ea -i ".spec.replaces = \"debezium-operator.v$REPLACES_VERSION\"" "$CSV_PATH" +fi + +# Wire OLM target namespaces and QOSDK configuration together +# Required since QOSDK doesn't generate this! +# OLM standard is to set the olm.targetNamespaces annotation on operator deployments +# whereas QOSDK uses the QUARKUS_OPERATOR_SDK_NAMESPACES environment variable for the same purpose +yq ea -i '.spec.install.spec.deployments[0].spec.template.spec.containers[0].env += [{"name": "QUARKUS_OPERATOR_SDK_NAMESPACES", "valueFrom": {"fieldRef": {"fieldPath": "metadata.annotations['"'"'olm.targetNamespaces'"'"']"}}}]' "$CSV_PATH" + +echo "" +echo "OLM bundle created!" +echo "Bundle dir: $OUTPUT_DIR" +echo "" \ No newline at end of file diff --git a/scripts/create-olm-test-catalog.sh b/scripts/create-olm-test-catalog.sh new file mode 100755 index 0000000..9e3657b --- /dev/null +++ b/scripts/create-olm-test-catalog.sh @@ -0,0 +1,149 @@ +#! /usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source "$SCRIPT_DIR/functions.sh" +checkDependencies + +OPTS=`getopt -o b:i:o:n:c:v:f --long bundle:,input:,output:,name:,channel:,version:,catalog-image:,bundle-image:,force,push -n 'parse-options' -- "$@"` +if [ $? != 0 ] ; then echo "Failed parsing options." >&2 ; exit 1 ; fi +eval set -- "$OPTS" + +# Set defaults +BUNDLE_VERSION="all" +BUNDLE_IMAGE_PULL_URL="quay.io/debezium/operator-bundle" +CATALOG_VERSION="latest" +CATALOG_IMAGE_PULL_URL="quay.io/debezium/operator-catalog" +INPUT_DIR_BASE="$PWD/olm/bundles" +OUTPUT_DIR_BASE="$PWD/olm/catalog" +CATALOG_NAME="debezium-operator" +CATALOG_CHANNEL="alpha" +PUSH_IMAGE=false +FORCE=false + +# Process script options +while true; do + case "$1" in + -n | --name ) CATALOG_NAME=$2; shift; shift ;; + -b | --bundle ) BUNDLE_VERSION=$2; shift; shift ;; + -i | --input ) INPUT_DIR_BASE=$2; shift; shift ;; + -o | --output ) OUTPUT_DIR_BASE=$2; shift; shift ;; + -c | --channel ) CATALOG_CHANNEL=$2; shift; shift ;; + -v | --version ) CATALOG_VERSION=$2; shift; shift ;; + -f | --force ) FORCE=true; shift ;; + --push ) PUSH_IMAGE=true; shift ;; + --catalog-image ) CATALOG_IMAGE=$2; shift; shift ;; + --bundle-image ) BUNDLE_IMAGE_PULL_URL=$2; shift; shift ;; + -- ) shift; break ;; + * ) break ;; + esac +done + +if [[ ! -d "$INPUT_DIR_BASE" ]]; then + echo "Input directory $INPUT_DIR_BASE not exists!" + exit 1 +fi + +# Set variables +CATALOG_DIR="$OUTPUT_DIR_BASE/$CATALOG_NAME" +CATALOG_MANIFEST_FILE="$CATALOG_DIR/operator.yaml" +CATALOG_DOCKER_FILE="$OUTPUT_DIR_BASE/$CATALOG_NAME.Dockerfile" +CATALOG_IMAGE="$CATALOG_IMAGE_PULL_URL:$CATALOG_VERSION" + +echo "" +echo "Creating OLM catalog" +echo "Bundle input dir: $INPUT_DIR_BASE" +echo "Bundle bundle(s): $BUNDLE_VERSION" +echo "" + +if [[ $BUNDLE_VERSION = "all" ]]; then + BUNDLES=( $INPUT_DIR_BASE/*/ ) +else + BUNDLES=( "$INPUT_DIR_BASE/$BUNDLE_VERSION" ) +fi + +if [[ -d "$CATALOG_DIR" && "$FORCE" = true ]]; then + echo "Removing exiting catalog directory '$CATALOG_DIR'" + rm -rf "$CATALOG_DIR" +fi + +if [[ -f "$CATALOG_DOCKER_FILE" && "$FORCE" = true ]]; then + echo "Removing exiting catalog dockerfile '$CATALOG_DOCKER_FILE'" + rm -rf "$CATALOG_DOCKER_FILE" +fi + +if [[ -d "$CATALOG_DIR" ]]; then + echo "Directory $CATALOG_DIR already exists!" + echo "Use -f / --force to overwrite" + exit 2 +fi + +if [[ -d "$CATALOG_DOCKER_FILE" ]]; then + echo "Dockerfile $CATALOG_DOCKER_FILE already exists!" + echo "Use -f / --force to overwrite" + exit 3 +fi + +# Generate dockerfile and initialize catalog manifest +mkdir -p "$CATALOG_DIR" +opm generate dockerfile "$CATALOG_DIR" +opm init "$CATALOG_NAME" --default-channel="$CATALOG_CHANNEL" --output yaml > "$CATALOG_MANIFEST_FILE" + +# Render each bundle +for bundle in "${BUNDLES[@]}"; do + name="$(csvName $bundle)" + version="$(csvVersion $bundle)" + image="$BUNDLE_IMAGE_PULL_URL:$version" + + echo "" + echo "Rendering bundle '$name'" + echo "Bundle directory: " $bundle + echo "Bundle image: $image " + echo "" + + opm render "$image" --output=yaml >> "$CATALOG_MANIFEST_FILE" +done; + + +# Write out channel declaration +echo "" +echo "Creating channel '$name'" +echo "" +cat << EOF >> "$CATALOG_MANIFEST_FILE" +--- +schema: olm.channel +package: $CATALOG_NAME +name: $CATALOG_CHANNEL +entries: +EOF + +# Write out channel entries +for bundle in "${BUNDLES[@]}"; do + name="$(csvName $bundle)" + replaces="$(csvReplaces $bundle)" + + echo " - name: $name" >> "$CATALOG_MANIFEST_FILE" + [ $replaces != "null" ] && echo " replaces: $replaces" >> "$CATALOG_MANIFEST_FILE" +done; + + +# Validate generated catalog +opm validate "$CATALOG_DIR" + +# Build image +docker build -t "$CATALOG_IMAGE" -f "$CATALOG_DOCKER_FILE" "$OUTPUT_DIR_BASE" + +echo "" +echo "Catalog created!" +echo "Output dir: $OUTPUT_DIR_BASE" +echo "Name: $CATALOG_NAME" +echo "Channel: $CATALOG_CHANNEL" +echo "Image: $CATALOG_IMAGE" +echo "" + + +if [[ "$PUSH_IMAGE" = true ]]; then + echo "" + echo "Pushing image: $CATALOG_IMAGE" + docker push "$CATALOG_IMAGE" +fi diff --git a/scripts/create-olm-test-resources.sh b/scripts/create-olm-test-resources.sh new file mode 100755 index 0000000..6fd276a --- /dev/null +++ b/scripts/create-olm-test-resources.sh @@ -0,0 +1,84 @@ +#! /usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" +source "$SCRIPT_DIR/functions.sh" +checkDependencies + +OPTS=`getopt -o b:o:n:c:v: --long bundle:,output:,namespace:,channel:,version:,catalog-image: -n 'parse-options' -- "$@"` +if [ $? != 0 ] ; then echo "Failed parsing options." >&2 ; exit 1 ; fi +eval set -- "$OPTS" + +# Set defaults +CATALOG_VERSION="latest" +CATALOG_IMAGE="quay.io/debezium/operator-catalog:latest" +OUTPUT_DIR_BASE="$PWD/olm/test-resources" +CATALOG_NAME="debezium-operator-catalog" +CATALOG_NAMESPACE="olm" +SUBSCRIPTION_CHANNEL="alpha" +SUBSCRIPTION_NS="operators" + +# Process script options +while true; do + case "$1" in + -n | --namespace ) SUBSCRIPTION_NS=$2; shift; shift ;; + -b | --bundle ) BUNDLE_VERSION=$2; shift; shift ;; + -o | --output ) OUTPUT_DIR_BASE=$2; shift; shift ;; + -c | --channel ) SUBSCRIPTION_CHANNEL=$2; shift; shift ;; + -v | --version ) CATALOG_VERSION=$2; shift; shift ;; + --watch-namespace ) WATCH_NS=$2; shift; shift ;; + --catalog-image ) CATALOG_IMAGE=$2; shift; shift ;; + --catalog-ns ) CATALOG_NAMESPACE=$2; shift; shift ;; + -- ) shift; break ;; + * ) break ;; + esac +done + + +# Set variables +SUBSCRIPTION_BUNDLE="debezium-operator.v$BUNDLE_VERSION" + + +rm -rf "$OUTPUT_DIR_BASE" +mkdir -p "$OUTPUT_DIR_BASE" + + +echo "" +echo "Creating OLM test resources" +echo "Output dir: $OUTPUT_DIR_BASE" +echo "" + +cat << EOF >> "$OUTPUT_DIR_BASE/catalog.yaml" +apiVersion: operators.coreos.com/v1alpha1 +kind: CatalogSource +metadata: + name: $CATALOG_NAME + namespace: $CATALOG_NAMESPACE +spec: + grpcPodConfig: + securityContextConfig: restricted + sourceType: grpc + image: $CATALOG_IMAGE + displayName: Debezium Test Catalog + publisher: Me + updateStrategy: + registryPoll: + interval: 10m +EOF + +cat << EOF >> "$OUTPUT_DIR_BASE/subscription.yaml" +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: debezium-operator-subscription + namespace: $SUBSCRIPTION_NS +spec: + installPlanApproval: Automatic + name: debezium-operator + source: $CATALOG_NAME + sourceNamespace: $CATALOG_NAMESPACE + startingCSV: $SUBSCRIPTION_BUNDLE +EOF + + + diff --git a/scripts/functions.sh b/scripts/functions.sh new file mode 100644 index 0000000..df6ac0c --- /dev/null +++ b/scripts/functions.sh @@ -0,0 +1,38 @@ +function csvPath() { + find "$1" -type f -name '*.clusterserviceversion.yaml' +} + +function csvName() { + yq ".metadata.name" $(csvPath $1) +} + +function csvVersion() { + yq ".spec.version" $(csvPath $1) +} + +function csvReplaces() { + yq ".spec.replaces" $(csvPath $1) +} + +function requireGnuGetopt() { + getopt_version="$(getopt --version)" + if ! [[ "$getopt_version" =~ .*"getopt from util-linux".* ]]; then + echo "GNU getopt is required" + echo "On MacOS it can be installed by running 'brew install gnu-getopt'" + exit 256 + fi +} + +function requireYq() { + if ! command -v yq &> /dev/null + then + echo "Missing yq!" + echo "https://github.com/mikefarah/yq#install" + exit 256 + fi +} + +function checkDependencies() { + requireGnuGetopt + requireYq +} \ No newline at end of file