diff --git a/.gitignore b/.gitignore index 297b9fe0..406ed4ec 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ /*.so /*.test /bin +/hack/config/ /installer/*.tar /rhtap-cli /tmp diff --git a/hack/ci-set-org-vars.sh b/hack/ci-set-org-vars.sh new file mode 100755 index 00000000..35b3b009 --- /dev/null +++ b/hack/ci-set-org-vars.sh @@ -0,0 +1,185 @@ +#!/usr/bin/env bash + +# Set the variables/secrets in the CIs + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_DIR="$( + cd "$(dirname "$0")" >/dev/null + pwd +)" + +usage() { + echo " +Usage: + ${0##*/} [options] + +Optional arguments: + -e, --env-file ENVFILE + Environment variables definitions (default: $SCRIPT_DIR/private.env) + -n, --namespace NAMESPACE + RHTAP installation namespace (default: rhtap) + -d, --debug + Activate tracing/debug mode. + -h, --help + Display this message. + +Example: + ${0##*/} -e private.env +" >&2 +} + +parse_args() { + NAMESPACE="rhtap" + ENVFILE="$SCRIPT_DIR/private.env" + while [[ $# -gt 0 ]]; do + case $1 in + -e | --env-file) + ENVFILE="$2" + shift + ;; + -n | --namespace) + NAMESPACE="$2" + shift + ;; + -d | --debug) + set -x + DEBUG="--debug" + export DEBUG + ;; + -h | --help) + usage + exit 0 + ;; + *) + echo "[ERROR] Unknown argument: $1" + usage + exit 1 + ;; + esac + shift + done + + # shellcheck disable=SC1090 + source "$ENVFILE" +} + +getValues() { + COSIGN_SECRET_KEY="$(oc get secrets -n openshift-pipelines signing-secrets -o json | yq '.data.["cosign.key"]')" + COSIGN_SECRET_PASSWORD="$(oc get secrets -n openshift-pipelines signing-secrets -o json | yq '.data.["cosign.password"]')" + COSIGN_PUBLIC_KEY="$(oc get secrets -n openshift-pipelines signing-secrets -o json | yq '.data.["cosign.pub"]')" + + for REGISTRY in "artifactory" "nexus" "quay"; do + REGISTRY_SECRET="rhtap-$REGISTRY-integration" # notsecret + if ! oc get secrets -n "$NAMESPACE" "$REGISTRY_SECRET" -o name >/dev/null 2>&1; then + continue + fi + REGISTRY_ENDPOINT="$(oc get secrets -n "$NAMESPACE" "$REGISTRY_SECRET" -o json | jq -r '.data.url | @base64d')" + IMAGE_REGISTRY="${REGISTRY_ENDPOINT//https:\/\//}" + IMAGE_REGISTRY_USER="$( + oc get secrets -n "$NAMESPACE" "$REGISTRY_SECRET" -o json \ + | jq -r '.data.".dockerconfigjson" | @base64d' \ + | jq -r '.auths | to_entries[0].value.auth | @base64d' \ + | cut -d: -f1 + )" + IMAGE_REGISTRY_PASSWORD="$( + oc get secrets -n "$NAMESPACE" "$REGISTRY_SECRET" -o json \ + | jq -r '.data.".dockerconfigjson" | @base64d' \ + | jq -r '.auths | to_entries[0].value.auth | @base64d' \ + | cut -d: -f2- + )" + break + done + + SECRET="rhtap-acs-integration" + ROX_CENTRAL_ENDPOINT="$(oc get secrets -n "$NAMESPACE" "$SECRET" -o json | yq '.data.endpoint | @base64d')" + ROX_API_TOKEN="$(oc get secrets -n "$NAMESPACE" "$SECRET" -o json | yq '.data.token | @base64d')" + + REKOR_HOST="https://$(oc get routes -n rhtap-tas -l "app.kubernetes.io/name=rekor-server" -o jsonpath="{.items[0].spec.host}")" + TUF_MIRROR="https://$(oc get routes -n rhtap-tas -l "app.kubernetes.io/name=tuf" -o jsonpath="{.items[0].spec.host}")" +} + +getSCMs() { + SCM_LIST=( ) + for SCM in github gitlab; do + SECRET="rhtap-$SCM-integration" # notsecret + if kubectl get secrets -n "$NAMESPACE" "$SECRET" >/dev/null 2>&1 ; then + SCM_LIST+=("$SCM") + fi + done +} + +setVars() { + setVar COSIGN_SECRET_PASSWORD "$COSIGN_SECRET_PASSWORD" + setVar COSIGN_SECRET_KEY "$COSIGN_SECRET_KEY" + setVar COSIGN_PUBLIC_KEY "$COSIGN_PUBLIC_KEY" + setVar GITOPS_AUTH_PASSWORD "$GIT_TOKEN" + setVar IMAGE_REGISTRY "$IMAGE_REGISTRY" + setVar IMAGE_REGISTRY_PASSWORD "$IMAGE_REGISTRY_PASSWORD" + setVar IMAGE_REGISTRY_USER "$IMAGE_REGISTRY_USER" + setVar QUAY_IO_CREDS_PSW "$IMAGE_REGISTRY_PASSWORD" + setVar QUAY_IO_CREDS_USR "$IMAGE_REGISTRY_USER" + setVar REKOR_HOST "$REKOR_HOST" + setVar ROX_CENTRAL_ENDPOINT "$ROX_CENTRAL_ENDPOINT" + setVar ROX_API_TOKEN "$ROX_API_TOKEN" + setVar TUF_MIRROR "$TUF_MIRROR" +} + +setVar() { + NAME=$1 + VALUE=$2 + echo -n "Setting $NAME in $GIT_ORG: " + "${SCM}SetVar" + sleep .5 # rate limiting to prevent issues +} + +githubGetValues() { + SECRET="rhtap-github-integration" + GIT_ORG="$GITHUB__ORG" + GIT_TOKEN="$(oc get secrets -n "$NAMESPACE" "$SECRET" -o json | yq '.data.token | @base64d')" +} + +githubSetVar() { + gh secret set "$NAME" -b "$VALUE" --org "$GIT_ORG" --visibility all +} + +gitlabGetValues() { + SECRET="rhtap-gitlab-integration" + GIT_TOKEN="$(oc get secrets -n "$NAMESPACE" "$SECRET" -o json | yq '.data.token | @base64d')" + GIT_ORG="$GITLAB__ORG" + URL="https://$(oc get secrets -n "$NAMESPACE" "$SECRET" -o json | yq '.data.host | @base64d')" + PID=$(curl -s -L --header "PRIVATE-TOKEN: $GIT_TOKEN" "$URL/api/v4/groups/$GIT_ORG" | jq ".id") +} + +gitlabSetVar() { + result=$( + curl -s --request POST --header "PRIVATE-TOKEN: $GIT_TOKEN" \ + "$URL/api/v4/groups/$PID/variables" --form "key=$NAME" --form "value=$VALUE" + ) + if echo "$result" | grep -q "has already been taken"; then + result=$( + curl -s --request PUT --header "PRIVATE-TOKEN: $GIT_TOKEN" \ + "$URL/api/v4/groups/$PID/variables/$NAME" --form "value=$VALUE" + ) + fi + echo "$result" | jq --compact-output 'del(.description, .key, .value, .variable_type)' +} + +main() { + parse_args "$@" + getValues + getSCMs + for SCM in "${SCM_LIST[@]}"; do + echo "# $SCM ##################################################" + "${SCM}GetValues" + setVars + echo + done + echo "Success" +} + +if [ "${BASH_SOURCE[0]}" == "$0" ]; then + main "$@" +fi diff --git a/hack/deploy.sh b/hack/deploy.sh new file mode 100755 index 00000000..8257d167 --- /dev/null +++ b/hack/deploy.sh @@ -0,0 +1,286 @@ +#!/usr/bin/env bash + +# Manage a full deployment of RHTAP based on values from an env file +# and a few parameters + +# shellcheck disable=SC2016 + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_DIR="$( + cd "$(dirname "$0")" >/dev/null + pwd +)" + +usage() { + echo " +Usage: + ${0##*/} [options] + +Optional arguments: + -c, --container NAME + Run the cli from a container. Use 'dev' to build from source. + -e, --env-file + Environment variables definitions (default: $SCRIPT_DIR/private.env) + -i, --integration INTEGRATION + Use an external service [acs, bitbucket, ci, dh, github, gitlab, gitops, + jenkins, quay, tas, tpa]. + -d, --debug + Activate tracing/debug mode. + -h, --help + Display this message. + +Example: + ${0##*/} -e private.env -i acs -i quay +" >&2 +} + +parse_args() { + PROJECT_DIR="$( + cd "$(dirname "$SCRIPT_DIR")" >/dev/null + pwd + )" + ENVFILE="$SCRIPT_DIR/private.env" + KUBECONFIG="${KUBECONFIG:-$HOME/.kube/config}" + CLI_BIN="run_bin" + CLI="$CLI_BIN" + while [[ $# -gt 0 ]]; do + case $1 in + -c|--container) + CLI_IMAGE="$2" + CLI="run_container" + CLI_PORT="8228" + shift + ;; + -e | --env-file) + ENVFILE="$2" + shift + ;; + -i|--integration) + case $2 in + acs) + ACS=1 + ;; + bitbucket) + BITBUCKET=1 + ;; + ci) + CI=1 + ;; + dh) + DH=1 + ;; + github) + GITHUB=1 + ;; + gitlab) + GITLAB=1 + ;; + gitops) + GITOPS=1 + ;; + jenkins) + JENKINS=1 + ;; + quay) + QUAY=1 + ;; + tas) + TAS=1 + ;; + tpa) + TPA=1 + ;; + *) + echo "[ERROR] Unknown integration: $1" + usage + ;; + esac + shift + ;; + -d | --debug) + set -x + DEBUG="--debug" + export DEBUG + ;; + -h | --help) + usage + exit 0 + ;; + *) + echo "[ERROR] Unknown argument: $1" + usage + exit 1 + ;; + esac + shift + done + + # shellcheck disable=SC1090 + source "$ENVFILE" +} + +init() { + cd "$PROJECT_DIR" +} + +build() { + if [ "$CLI" = "run_bin" ]; then + make + else + if [ "${CLI_IMAGE:-}" = "dev" ]; then + podman build . -t rhtap-cli + CLI_IMAGE="rhtap-cli:latest" + fi + fi +} + +init_config() { + CONFIG_DIR="$SCRIPT_DIR/config" + mkdir -p "$CONFIG_DIR" + CONFIG="$CONFIG_DIR/config.yaml" + VALUES="$CONFIG_DIR/values.yaml.tpl" + + unshare + + cp "$PROJECT_DIR/installer/config.yaml" "$CONFIG" + cp "$PROJECT_DIR/installer/charts/values.yaml.tpl" "$VALUES" + cp "$KUBECONFIG" "$CONFIG_DIR/kubeconfig" + KUBECONFIG="$CONFIG_DIR/kubeconfig" + + NAMESPACE="$(yq '.rhtapCLI.namespace' "$CONFIG")" + export NAMESPACE + + # shellcheck disable=SC1090 + source "$ENVFILE" +} + +rhtap_cli() { + $CLI "$@" +} + +run_bin() { + eval "$PROJECT_DIR/bin/rhtap-cli --config='$CONFIG' $*" +} + +run_container() { + podman run \ + --entrypoint="bash" \ + --env-file="$ENVFILE" \ + --publish "$CLI_PORT:$CLI_PORT" \ + --rm \ + --volume="$KUBECONFIG:/rhtap-cli/.kube/config:Z,U" \ + --volume="$CONFIG:/rhtap-cli/my-config.yaml:Z,U" \ + "$CLI_IMAGE" \ + -c "rhtap-cli --config=my-config.yaml $*" + unshare +} + +unshare() { + podman unshare chown -R 0:0 "$CONFIG_DIR" +} + +configure() { + if [ -n "${CATALOG_URL:-}" ]; then + export CATALOG_URL + yq -i '.rhtapCLI.features.redHatDeveloperHub.properties.catalogURL=strenv(CATALOG_URL)' "$CONFIG" + fi + + if [[ -n "${ACS:-}" ]]; then + yq -i '.rhtapCLI.features.redHatAdvancedClusterSecurity.enabled=false' "$CONFIG" + fi + if [[ -n "${CI:-}" ]]; then + sed -i 's/\( *ci\): .*/\1: true/' "$VALUES" + fi + if [[ -n "${DH:-}" ]]; then + yq -i '.rhtapCLI.dependencies[] |= select(.chart == "charts/rhtap-dh").enabled = false' "$CONFIG" + fi + if [[ -n "${GITOPS:-}" ]]; then + yq -i '.rhtapCLI.features.openShiftGitOps.enabled=false' "$CONFIG" + fi + if [[ -n "${QUAY:-}" ]]; then + yq -i '.rhtapCLI.features.redHatQuay.enabled=false' "$CONFIG" + fi + if [[ -n "${TAS:-}" ]]; then + yq -i '.rhtapCLI.features.trustedArtifactSigner.enabled=false' "$CONFIG" + fi + if [[ -n "${TPA:-}" ]]; then + yq -i '.rhtapCLI.features.trustedProfileAnalyzer.enabled=false' "$CONFIG" + fi +} + +integrations() { + if [[ -n "${ACS:-}" ]]; then + rhtap_cli integration acs --force \ + --endpoint='"$ACS__CENTRAL_ENDPOINT"' \ + --token='"$ACS__API_TOKEN"' + fi + if [[ -n "${BITBUCKET:-}" ]]; then + rhtap_cli integration bitbucket --force \ + --app-password='"$BITBUCKET__APP_PASSWORD"' \ + --host='"$BITBUCKET__HOST"' \ + --username='"$BITBUCKET__USERNAME"' + fi + if [[ -n "${GITHUB:-}" ]]; then + if ! kubectl get secret -n "$NAMESPACE" rhtap-github-integration >/dev/null 2>&1; then + rhtap_cli integration github-app \ + --create \ + --token='"$GITHUB__ORG_TOKEN"' \ + --org='"$GITHUB__ORG"' \ + "rhtap-$GITHUB__ORG-$(date +%m%d-%H%M)" + fi + fi + if [[ -n "${GITLAB:-}" ]]; then + if [[ -n "${GITLAB__APP__CLIENT__ID:-}" && -n "${GITLAB__APP__CLIENT__SECRET:-}" ]]; then + rhtap_cli integration gitlab --force \ + --app-id='"$GITLAB__APP__CLIENT__ID"' \ + --app-secret='"$GITLAB__APP__CLIENT__SECRET"' \ + --host='"$GITLAB__HOST"' \ + --token='"$GITLAB__TOKEN"' + else + rhtap_cli integration gitlab --force \ + --host='"$GITLAB__HOST"' \ + --token='"$GITLAB__TOKEN"' + fi + fi + if [[ -n "${JENKINS:-}" ]]; then + rhtap_cli integration jenkins --force \ + --token='"$JENKINS__TOKEN"' \ + --url='"$JENKINS__URL"' \ + --username='"$JENKINS__USERNAME"' + fi + if [[ -n "${QUAY:-}" ]]; then + rhtap_cli integration quay --force \ + --dockerconfigjson='"$QUAY__DOCKERCONFIGJSON"' \ + --token='"$QUAY__API_TOKEN"' --url='"$QUAY__URL"' + fi +} + +deploy() { + time rhtap_cli deploy "${DEBUG:-}" +} + +configure_ci() { + "$SCRIPT_DIR/ci-set-org-vars.sh" --env-file "$ENVFILE" +} + +action() { + build + init_config + configure + integrations + deploy + configure_ci +} + +main() { + parse_args "$@" + init + action +} + +if [ "${BASH_SOURCE[0]}" == "$0" ]; then + main "$@" +fi diff --git a/hack/private.env.template b/hack/private.env.template new file mode 100644 index 00000000..8b1af05b --- /dev/null +++ b/hack/private.env.template @@ -0,0 +1,60 @@ +# +# ACS +# +ACS__API_TOKEN=ey****bar +ACS__CENTRAL_ENDPOINT=foo.bar.com:443 + +# +# DH +# +# CATALOG_URL="https://github.com/redhat-appstudio/tssc-sample-templates/blob/main/all.yaml" + +# +# SCM +# + +# bitbucket.org +BITBUCKET__APP_PASSWORD=foo****bar +BITBUCKET__HOST=bitbucket.org +BITBUCKET__USERNAME=myUser +BITBUCKET__PROJECT=myProject +BITBUCKET__WORKSPACE=myWorkspace + + +# github.com +GITHUB__ORG=myOrg +GITHUB__ORG_TOKEN=ghp_foo****bar + + +# gitlab.com +GITLAB__HOST=gitlab.com +# The application is used for DH auth +# GITLAB__APP__CLIENT__ID=foo****bar +# GITLAB__APP__CLIENT__SECRET=gloas-foo***bar +GITLAB__ORG=myOrg +GITLAB__TOKEN=glpat-foo****bar + +# +# CI +# + +# Jenkins +JENKINS__TOKEN=foo****bar +JENKINS__URL=http://foobar.com +JENKINS__USERNAME=myUser + +# +# Image registries +# + +# Artifactory +# TODO + +# Nexus +# TODO + +# Quay +QUAY__API_TOKEN=foo****bar +QUAY__DOCKERCONFIGJSON='{"auths":{"quay.io":{"auth":"foo****bar=","email":""}}}' +QUAY__HOST=quay.io +QUAY__URL=https://quay.io diff --git a/hack/reset.sh b/hack/reset.sh new file mode 100755 index 00000000..6c063ad6 --- /dev/null +++ b/hack/reset.sh @@ -0,0 +1,167 @@ +#!/usr/bin/env bash + +# Reset a service, namespace or cluster, which is helpful for +# development and testing. + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_DIR="$( + cd "$(dirname "$0")" >/dev/null + pwd +)" + +usage() { + echo " +Usage: + ${0##*/} [options] + +Optional arguments: + -c, --cluster + Reset all RHTAP namespaces in the cluster. + -e, --env-file + Environment variables definitions (default: $SCRIPT_DIR/private.env) + -n, --namespace NAMESPACE + Delete the specified namespace. The option can be repeated + to delete multiple namespaces. + -i, --integration INTEGRATION + Service to reset [dh, github, gitlab]. The option can be repeated + to reset multiple services. + -d, --debug + Activate tracing/debug mode. + -h, --help + Display this message. + +Example: + ${0##*/} -e private.env -i github -i gitlab +" >&2 +} + +parse_args() { + ENVFILE="$SCRIPT_DIR/private.env" + NAMESPACES=() + while [[ $# -gt 0 ]]; do + case $1 in + -c|--cluster) + CLUSTER=1 + ;; + -e | --env-file) + ENVFILE="$2" + shift + ;; + -i|--integration) + case $2 in + dh) + if kubectl get backstages.rhdh.redhat.com --ignore-not-found 2>/dev/null; then + DH=1 + fi + ;; + github) + GITHUB=1 + ;; + gitlab) + GITLAB=1 + ;; + *) + echo "[ERROR] Unknown integration: $1" + usage + ;; + esac + shift + ;; + -n|--namespace) + NAMESPACES+=("$2") + shift + ;; + -d | --debug) + set -x + DEBUG="--debug" + export DEBUG + ;; + -h | --help) + usage + exit 0 + ;; + *) + echo "[ERROR] Unknown argument: $1" + usage + exit 1 + ;; + esac + shift + done + + # shellcheck disable=SC1090 + source "$ENVFILE" +} + +init() { + PROJECT_DIR="$( + cd "$(dirname "$SCRIPT_DIR")" >/dev/null + pwd + )" + cd "$PROJECT_DIR" +} + +action() { + CONFIG="$PROJECT_DIR/installer/config.yaml" + NAMESPACE="$(yq '.rhtapCLI.namespace' "$CONFIG")" + + if [ -n "${CLUSTER:-}" ]; then + echo '# Cluster' + for ns in $(kubectl get namespaces -o name | grep "/$NAMESPACE" | cut -d/ -f2); do + NAMESPACES+=("$ns") + done + echo + fi + if [ -n "${NAMESPACES[*]}" ]; then + echo "# Namespaces" + for ns in "${NAMESPACES[@]}"; do + kubectl delete namespace "$ns" & + echo "Deleting namespace $ns..." + for cr in "applications.argoproj.io" "kafkatopics.kafka.strimzi.io"; do + sleep 3 + while [ "$(oc get "$cr" -n "$ns" -o name | wc -l)" != "0" ]; do + for kt in $(oc get "$cr" -n "$ns" -o name); do + kubectl patch "$kt" -n "$ns" -p '{"metadata":{"finalizers": null}}' --type=merge + done + done + done + done + wait + echo "✓ Deleted all namespaces" + echo + fi + if [ -n "${DH:-}" ]; then + echo '# Developer Hub' + kubectl delete backstages.rhdh.redhat.com -n "$NAMESPACE" developer-hub --ignore-not-found --wait=true + kubectl delete persistentvolumeclaims -n "$NAMESPACE" data-backstage-psql-developer-hub-0 --ignore-not-found --wait=true + echo + fi + if [ -n "${GITHUB:-}" ]; then + echo '# GitHub' + for REPO in $(gh repo list "$GITHUB__ORG" --json url | yq '.[].url'); do + gh repo delete --yes "$REPO" + done + echo + fi + if [ -n "${GITLAB:-}" ]; then + echo '# GitLab' + for REPO in $(glab repo list -g "$GITLAB__ORG" --output json | yq '.[].path_with_namespace'); do + glab repo delete --yes "$REPO" + echo "✓ Deleted repository $REPO" + done 2>/dev/null + echo + fi +} + +main() { + parse_args "$@" + init + action +} + +if [ "${BASH_SOURCE[0]}" == "$0" ]; then + main "$@" +fi