From 053589a419a9cefded38b4dc78d04d22cfd1beed Mon Sep 17 00:00:00 2001 From: cmoulliard Date: Wed, 2 Oct 2024 17:57:47 +0200 Subject: [PATCH 1/4] Merge the 2 scripts into one: qshift Signed-off-by: cmoulliard --- .gitignore | 1 + README.md | 10 +- bin/provision-namespace | 255 --------------------- bin/qshift | 479 ++++++++++++++++++++++++++++++++++++++++ bin/start-dev | 218 ------------------ 5 files changed, 485 insertions(+), 478 deletions(-) delete mode 100755 bin/provision-namespace create mode 100755 bin/qshift delete mode 100755 bin/start-dev diff --git a/.gitignore b/.gitignore index 8c7b5a6..921049c 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,4 @@ app-config.qshift.yaml # E2E test reports e2e-test-report/ /temp/ +/bin/_temp/ diff --git a/README.md b/README.md index 58c8a93..9aed101 100644 --- a/README.md +++ b/README.md @@ -223,9 +223,9 @@ EOF quarkus-dev 32s Running True ``` -**Important**: Alternatively, you can use our bash script able to execute all steps: [provision-namespace](bin%2Fprovision-namespace) +**Important**: Alternatively, you can use our bash script able to execute all the steps: [qshift](bin%2Fqshift) ```bash -./bin/provision-namespace -h +./bin/qshift provision-namespace -h This script will create a new namespace, set your registry creds, install a KubeVirt VM using your ssh key and configure ArgoCD to access your resources ! @@ -239,7 +239,7 @@ Options: ``` Here is by example, how you could define the arguments ```bash -./bin/provision-namespace \ +./bin/qshift provision-namespace \ -n my-namespace \ -d ":" \ -q ":" \ @@ -316,13 +316,13 @@ A helper script that will create the `app-config.local.yaml` file and start the The script will also infer all the variables from the developers environment and also supports overriding inferred values using flags. ```bash -./bin/start-dev +./bin/qshift dev ``` To see all the available options supported by the script: ```bash -./bin/start-dev --help +./bin/qshift dev -h ``` ## Automate a scenario diff --git a/bin/provision-namespace b/bin/provision-namespace deleted file mode 100755 index 77c3e03..0000000 --- a/bin/provision-namespace +++ /dev/null @@ -1,255 +0,0 @@ -#!/bin/bash - -# This script will: -# - Create a new namespace, -# - Set the registry credentials to pull/push images: dockerhub, quay.io, etc, -# - Install a KubeVirt VM using your ssh key -# - Configure ArgoCD to access your resources -# Note: You can define the registry credentials using the parameters otherwise the script ask you to select for podman - $HOME/.config/containers/auth.json or docker - "$HOME/.docker/config.json" your file if they exist" - -# Define the green and red color escape sequences -GREEN='\033[0;32m' -RED='\033[0;31m' -NC='\033[0m' # No Color - -# Print key-value pair in green if value is not empty, otherwise in red -printv() { - if [ -z "$2" ]; then - echo -e "${RED}$1${NC}=${RED}${NC}" - else - echo -e "${GREEN}$1${NC}=$2" - fi -} - -# Print key-password pair in green if value is not empty, otherwise in red, replacing password with '*' -printp() { - if [ -z "$2" ]; then - echo -e "${RED}$1${NC}=${RED}${NC}" - else - echo -e "${GREEN}$1${NC}=********" - fi -} - -execute_kubectl() { - local command=$1 - if $DRY_RUN; then - echo "kubectl --dry-run=client $command " - kubectl --dry-run=client $command - else - echo "kubectl $command" - kubectl $command - fi -} - -execute_oc() { - local command=$1 - if $DRY_RUN; then - echo "oc $command " - else - echo "oc $command" - oc $command - fi -} - -# Set default values -DRY_RUN=false -PUBLIC_KEY_PASS=$HOME/.ssh/id_rsa.pub -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) -TEMP_DIR=$SCRIPT_DIR/_temp - -mkdir -p $TEMP_DIR - - -# Display usage -function usage() { - echo "Usage: $0 [options]" - echo "" - echo "This script will create a new namespace, set your registry credentials to pull/push images, install a KubeVirt VM using your ssh key and configure ArgoCD to access your resources !" - echo "Note: You can define the registry credentials using the parameters otherwise the script ask you to select for podman - $HOME/.config/containers/auth.json or docker - "$HOME/.docker/config.json" your file if they exist" - - echo "" - echo "Options:" - echo " -n, --namespace Your namespace on the QShift cluster (mandatory)." - echo " -q, --quay-cred The Quay registry credential: username:password to be used to push on quay.io." - echo " -o, --quay-org The Quay registry organization hosting your images on quay.io." - echo " -d, --docker-cred The docker registry credential: username:password on dockerhub." - echo " -k, --key-path The path of your public to ssh to the VM." - echo " --dry-run Run the kubectl command with dry-run=client" - exit 1 -} - -while [[ $# -gt 0 ]]; do - case $1 in - -n|--namespace) - NAMESPACE=$2 - shift 2 - ;; - -q|--quay) - QUAY_CRED=$2 - shift 2 - ;; - -o|--quay-org) - QUAY_ORG=$2 - shift 2 - ;; - -d|--docker) - DOCKER_CRED=$2 - shift 2 - ;; - -k|--key-path) - PUBLIC_KEY_PASS=$2 - shift 2 - ;; - --dry-run) - DRY_RUN=true - shift - ;; - -h|--help) - usage - exit 0 - ;; - -*|--*) - echo "Unknown option $1" - usage - exit 1 - ;; - *) - POSITIONAL_ARGS+=("$1") # save positional arg - shift # past argument - ;; - esac -done - -if [[ -z ${NAMESPACE+x} ]]; then - echo "Error: NAMESPACE is not set" >&2 - usage - exit 1 -fi - -# List of registry credentials files to check -registry_creds_files=( - "$HOMEE/.docker/config.json" - "$HOMEE/.config/containers/auth.json" -) -available_files=() - -# Check which files exist and do not contain the exclude word -for file in "${registry_creds_files[@]}"; do - if [[ -f "$file" ]]; then - # Check if the file does NOT contain: oskeychain as won't work to create the secret - if ! grep -q "osxkeychain" "$file"; then - available_files+=("$file") - fi - fi -done - -if [[ ${#available_files[@]} -eq 0 ]]; then - echo "No registry credentials files are available for selection, so we will use your DOCKER_CRED or QUAY_CRED arguments if provided" - echo "" -else - # Show the user a list of available files and ask them to select one - echo "Please select your credentials file; podman or docker if password are not managed by osxkeychain:" - - select file in "${available_files[@]}"; do - if [[ -n "$file" ]]; then - REGISTRY_CREDS_FILE=$file - break - else - echo "Invalid choice, try again." - fi - done -fi - -echo "Let's creating the resources" -echo "Using as values ..." -printv "Your namespace" "$NAMESPACE" -printv "Registry credentials file" "$REGISTRY_CREDS_FILE" -printv "Quay registry username" "${QUAY_CRED%:*}" -printp "Quay registry password" "${QUAY_CRED##*/}" -printv "Quay registry organization" "$QUAY_ORG" -printv "Dockerhub registry username" "${DOCKER_CRED%:*}" -printp "Dockerhub registry password" "${DOCKER_CRED##*/}" -printv "Public key path" "$PUBLIC_KEY_PASS" -echo "....." - -echo "### Creating new project for: $NAMESPACE" -execute_oc "new-project $NAMESPACE" - -echo "### Creating resources ..." -if [[ "$REGISTRY_CREDS_FILE" ]]; then - execute_kubectl "create secret generic dockerconfig-secret --from-file=$REGISTRY_CREDS_FILE" - echo "" -else - if [[ (-n "$QUAY_CRED" && -n "$QUAY_ORG") || -n "$DOCKER_CRED" ]]; then - QUAY_CRED_BASE64=$(echo -n "$QUAY_CRED" | base64) - DOCKER_CRED_BASE64=$(echo -n "$DOCKER_CRED" | base64) - - cat < $TEMP_DIR/config.json -{ - "auths": { - "quay.io/$QUAY_ORG": { - "auth": "$QUAY_CRED_BASE64" - }, - "https://index.docker.io/v1/": { - "auth": "$DOCKER_CRED_BASE64" - } - } -} -EOF - execute_kubectl "create secret generic dockerconfig-secret --from-file=$TEMP_DIR/config.json" - echo "" - else - echo "## No registry DOCKER_CRED or QUAY_CRED arguments have been defined !" - exit 1 - fi -fi - -echo "### Creating the backstage service account" -execute_kubectl "create sa my-backstage" -echo "" - -echo "Give cluster-admin role to the backstage SA to access the kubernetes API resources" -cat < $TEMP_DIR/rbac.yml -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: backstage-$NAMESPACE-cluster-access -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: -- kind: ServiceAccount - name: my-backstage - namespace: $NAMESPACE -EOF - -execute_kubectl "apply -f $TEMP_DIR/rbac.yml" -echo "" - -echo "### Creating a secret hosting your SSH public key" -execute_kubectl "create secret generic quarkus-dev-ssh-key --from-file=key=$PUBLIC_KEY_PASS" -echo "" - -echo "### Creating the Fedora podman virtual machine" -execute_kubectl "apply -f manifest/installation/virt/quarkus-dev-virtualmachine.yml" -echo "" - -echo "### Apply the label: argocd.argoproj.io/managed-by= on your namespace" -execute_kubectl "label namespace $NAMESPACE argocd.argoproj.io/managed-by=openshift-gitops" -echo "" - -echo "### Adding your namespace to the parameter: sourceNamespaces of the ArgoCD CR ..." -ARGOCD_NAME="argocd" -NAMESPACE_ARGOCD="openshift-gitops" - -# Fetch the current ArgoCD resource -ARGOCD_JSON=$(kubectl get argocd $ARGOCD_NAME -n $NAMESPACE_ARGOCD -o json) - -if echo "$ARGOCD_JSON" | jq -e --arg ns "$NAMESPACE" '.spec.sourceNamespaces | index($ns)' > /dev/null; then - echo "Namespace '$NAMESPACE' already exists in sourceNamespaces." -else - echo "Adding namespace '$NAMESPACE' to sourceNamespaces." - PATCH=$(echo "$ARGOCD_JSON" | jq --arg ns "$NAMESPACE" '.spec.sourceNamespaces += [$ns] | {spec: {sourceNamespaces: .spec.sourceNamespaces}}') - kubectl patch argocd $ARGOCD_NAME -n $NAMESPACE_ARGOCD --type merge --patch "$PATCH" -fi diff --git a/bin/qshift b/bin/qshift new file mode 100755 index 0000000..f43fb55 --- /dev/null +++ b/bin/qshift @@ -0,0 +1,479 @@ +#!/bin/bash + + +########################################## +######## BEGIN - COMMON FUNCTIONS ######## +########################################## +# Define the green and red color escape sequences +GREEN='\033[0;32m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# Fallback to the second argument if the first is empty +setf() { + if [ -z "${!1}" ]; then + export $1="$2" + fi +} + +# Print key-value pair in green if value is not empty, otherwise in red +printv() { + if [ -z "$2" ]; then + echo -e "${RED}$1${NC}=${RED}${NC}" + else + echo -e "${GREEN}$1${NC}=$2" + fi +} + +# Print key-password pair in green if value is not empty, otherwise in red, replacing password with '*' +printp() { + if [ -z "$2" ]; then + echo -e "${RED}$1${NC}=${RED}${NC}" + else + echo -e "${GREEN}$1${NC}=********" + fi +} + +# Wait for process to finish while showing a spinner +waitp() { + local pid=$1 + local message=$2 + local delay=0.1 + local spinner_chars="/-\|" + echo -n "$messge" + while [ -d /proc/$pid ]; do + for i in $(seq 0 3); do + echo -ne "$message ${spinner_chars:i:1}" "\r" + sleep $delay + done + done + + # Check the exit status of the process + wait $pid + local exit_status=$? + + # Replace spinner with a check mark or an X based on the result + if [ $exit_status -eq 0 ]; then + echo -e "$message ${GREEN}✔${NC}" + else + echo -e "$message ${RED}✘${NC}" + fi +} +execute_kubectl() { + local command=$1 + if $DRY_RUN; then + echo "kubectl --dry-run=client $command " + kubectl --dry-run=client $command + else + echo "kubectl $command" + kubectl $command + fi +} + +execute_oc() { + local command=$1 + if $DRY_RUN; then + echo "oc $command " + else + echo "oc $command" + oc $command + fi +} + +######################################## +######## END - COMMON FUNCTIONS ######## +######################################## + +# Function for 'dev' subcommand +dev() { + usage() { + echo "Usage: $0 dev [options]" + echo "Options:" + echo " -t, --title The title of the Backstage instance" + echo " -o, --org <org> The organization name" + echo " -u, --github-user <github-user> The GitHub user" + echo " --github-token <github-token> The GitHub token" + echo " --gitea-host <host> The Gitea Host" + echo " --gitea-user <gitea-user> The Gitea username" + echo " --gitea-password <gitea-password> The Gitea username" + exit 1 + } + + # Set defaults + setf "BACKSTAGE_TITLE" "My Backstage" + setf "BACKSTAGE_ORG" "q-shift" + setf "BACKSTAGE_GITHUB_USER" "q-shift" + setf "GITHUB_PERSONAL_ACCESS_TOKEN" "" + setf "BACKSTAGE_TEAM_NAME" "team" + setf "BACKSTAGE_APP_BASE_URL" "http://localhost:3000" + setf "BACKSTAGE_BACKEND_BASE_URL" "http://localhost:7007" + setf "BACKSTAGE_AUTH_SECRET" `node -p 'require("crypto").randomBytes(24).toString("base64")'` + + # + # Optionally override the default values (above) with command line arguments: + # -t: Title + # -o: Organization + # --github-user: GitHub User + # --github-token: GitHub Token + # + while [[ $# -gt 0 ]]; do + echo "Processing $1" + case $1 in + -t|--title) + BACKSTAGE_TITLE=$2 + shift 2 + ;; + -o|--org) + BACKSTAGE_ORG=$2 + shift 2 + ;; + -u|--github-user) + BACKSTAGE_GITHUB_USER=$2 + shift 2 + ;; + --github-token) + GITHUB_PERSONAL_ACCESS_TOKEN=$2 + shift 2 + ;; + --gitea-url) + GITEA_HOST=$2 + shift 2 + ;; + --gitea-user) + GITEA_USERNAME=$2 + shift 2 + ;; + --gitea-password) + GITEA_PASSWORD=$2 + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + -*|--*) + echo "Unknown option $1" + usage + exit 1 + ;; + *) + POSITIONAL_ARGS+=("$1") # save positional arg + shift # past argument + ;; + esac + done + + echo "Generating app-config.local.yaml." + + # if kubectl does not exist, then exit + if ! command -v kubectl &> /dev/null; then + echo "kubectl is not installed. Aborting" + exit 1 + fi + + # check if ~/.kube/config exists and if not, then exit + if [ ! -f "$HOME/.kube/config" ]; then + echo "Kubernetes config file ($HOME/.kube/config) does not exist. Aborting" + exit 1 + fi + + # Run kubectl version asynchronously + kubectl version > /dev/null 2>&1 & + KUBECTL_PID=$! + + # Show spinner while waiting for kubectl to complete + waitp $KUBECTL_PID "Checking connection to the Kubernetes cluster" + + # Wait for kubectl to finish + wait $KUBECTL_PID + KUBECTL_EXIT_CODE=$? + + # Check exit code and print result + if [ $KUBECTL_EXIT_CODE -ne 0 ]; then + exit 1 + fi + + # Kubernetes + setf KUBERNETES_CONFIG_PATH "$HOME/.kube/config" + setf KUBERNETES_API_URL `kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'` + setf KUBERNETES_USER `kubectl config view --minify -o jsonpath='{.users[0].name}'` + setf KUBERNETES_TOKEN `cat $KUBERNETES_CONFIG_PATH | grep -A 2 "name: $KUBERNETES_USER" | grep token | awk '{print $2}'` + + + # Argo CD + setf ARGOCD_ADMIN_USER admin + setf ARGOCD_ADMIN_PASSWORD `kubectl get secrets -n openshift-gitops argocd-cluster -o yaml | grep password | awk '{print $2}' | base64 -d` + setf ARGOCD_COOKIE `kubectl get secrets -n openshift-gitops argocd-secret -o yaml | grep clientSecret | awk '{print $2}' | base64 -d` + setf ARGOCD_SERVER "https://openshift-gitops-server-openshift-gitops.apps.qshift.snowdrop.dev" + + # Github + if [ -z "$GITHUB_PERSONAL_ACCESS_TOKEN" ]; then + if command -v gh &> /dev/null; then + setf GITHUB_PERSONAL_ACCESS_TOKEN `gh auth status -t | grep Token: | awk -F": " '{print $2}'` + else + echo "GITHUB_PERSONAL_ACCESS_TOKEN is not defined and can't be read from 'gh' command. Aborting" + exit 1 + fi + fi + + # Gitea + setf GITEA_HOST "localhost" + setf GITEA_USERNAME "admin" + setf GITEA_PASSWORD "admin" + + echo "Using values..." + printv "BACKSTAGE_TITLE" "$BACKSTAGE_TITLE" + printv "BACKSTAGE_ORG" "$BACKSTAGE_ORG" + printv "BACKSTAGE_GITHUB_USER" "$BACKSTAGE_GITHUB_USER" + printp "GITHUB_PERSONAL_ACCESS_TOKEN" "${GITHUB_PERSONAL_ACCESS_TOKEN:0:4}******" + printv "GITEA_HOST" "$GITEA_HOST" + printv "GITEA_USERNAME" "$GITEA_USERNAME" + printp "GITEA_PASSWORD" "$GITEA_PASSWORD" + printv "KUBERNETES_API_URL" "$KUBERNETES_API_URL" + printv "KUBERNETES_USER" "$KUBERNETES_USER" + printp "KUBERNETES_TOKEN" "$KUBERNETES_TOKEN" + printv "ARGOCD_SERVER" "$ARGOCD_SERVER" + printv "ARGOCD_ADMIN_USER" "$ARGOCD_ADMIN_USER" + printp "ARGOCD_ADMIN_PASSWORD" "$ARGOCD_ADMIN_PASSWORD" + + envsubst < manifest/templates/app-config.qshift.tmpl > app-config.local.yaml + export NODE_TLS_REJECT_UNAUTHORIZED=0 + + echo "Starting Backstage..." + # Run Backstage in Development Mode + yarn dev +} + +# Function for 'provision-namespace' subcommand +provision_namespace() { + # This command will: + # - Create a new namespace, + # - Set the registry credentials to pull/push images: dockerhub, quay.io, etc, + # - Install a KubeVirt VM using your ssh key + # - Configure ArgoCD to access your resources + # Note: You can define the registry credentials using the parameters otherwise the script ask you to select for podman - $HOME/.config/containers/auth.json or docker - "$HOME/.docker/config.json" your file if they exist" + + function usage() { + echo "Usage: $0 provision-namespace [options]" + echo "" + echo "This script will create a new namespace, set your registry credentials to pull/push images, install a KubeVirt VM using your ssh key and configure ArgoCD to access your resources !" + echo "Note: You can define the registry credentials using the parameters otherwise the script ask you to select for podman - $HOME/.config/containers/auth.json or docker - "$HOME/.docker/config.json" your file if they exist" + + echo "" + echo "Options:" + echo " -n, --namespace <namespace> Your namespace on the QShift cluster (mandatory)." + echo " -q, --quay-cred <quay_username:quay_password> The Quay registry credential: username:password to be used to push on quay.io." + echo " -o, --quay-org <quay_organization> The Quay registry organization hosting your images on quay.io." + echo " -d, --docker-cred <docker_username:docker_password> The docker registry credential: username:password on dockerhub." + echo " -k, --key-path <public_key_path> The path of your public to ssh to the VM." + echo " --dry-run Run the kubectl command with dry-run=client" + exit 1 + } + + # Set default values + DRY_RUN=false + PUBLIC_KEY_PASS=$HOME/.ssh/id_rsa.pub + SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + TEMP_DIR=$SCRIPT_DIR/_temp + + mkdir -p $TEMP_DIR + + while [[ $# -gt 0 ]]; do + case $1 in + -n|--namespace) + NAMESPACE=$2 + shift 2 + ;; + -q|--quay) + QUAY_CRED=$2 + shift 2 + ;; + -o|--quay-org) + QUAY_ORG=$2 + shift 2 + ;; + -d|--docker) + DOCKER_CRED=$2 + shift 2 + ;; + -k|--key-path) + PUBLIC_KEY_PASS=$2 + shift 2 + ;; + --dry-run) + DRY_RUN=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + -*|--*) + echo "Unknown option $1" + usage + exit 1 + ;; + *) + POSITIONAL_ARGS+=("$1") # save positional arg + shift # past argument + ;; + esac + done + + if [[ -z ${NAMESPACE+x} ]]; then + echo "Error: NAMESPACE is not set" >&2 + usage + exit 1 + fi + + # List of registry credentials files to check + registry_creds_files=( + "$HOMEE/.docker/config.json" + "$HOMEE/.config/containers/auth.json" + ) + available_files=() + + # Check which files exist and do not contain the exclude word + for file in "${registry_creds_files[@]}"; do + if [[ -f "$file" ]]; then + # Check if the file does NOT contain: oskeychain as won't work to create the secret + if ! grep -q "osxkeychain" "$file"; then + available_files+=("$file") + fi + fi + done + + if [[ ${#available_files[@]} -eq 0 ]]; then + echo "No registry credentials files are available for selection, so we will use your DOCKER_CRED or QUAY_CRED arguments if provided" + echo "" + else + # Show the user a list of available files and ask them to select one + echo "Please select your credentials file; podman or docker if password are not managed by osxkeychain:" + + select file in "${available_files[@]}"; do + if [[ -n "$file" ]]; then + REGISTRY_CREDS_FILE=$file + break + else + echo "Invalid choice, try again." + fi + done + fi + + echo "Let's creating the resources" + echo "Using as values ..." + printv "Your namespace" "$NAMESPACE" + printv "Registry credentials file" "$REGISTRY_CREDS_FILE" + printv "Quay registry username" "${QUAY_CRED%:*}" + printp "Quay registry password" "${QUAY_CRED##*/}" + printv "Quay registry organization" "$QUAY_ORG" + printv "Dockerhub registry username" "${DOCKER_CRED%:*}" + printp "Dockerhub registry password" "${DOCKER_CRED##*/}" + printv "Public key path" "$PUBLIC_KEY_PASS" + echo "....." + + echo "### Creating new project for: $NAMESPACE" + execute_oc "new-project $NAMESPACE" + + echo "### Creating resources ..." + if [[ "$REGISTRY_CREDS_FILE" ]]; then + execute_kubectl "create secret generic dockerconfig-secret --from-file=$REGISTRY_CREDS_FILE" + echo "" + else + if [[ (-n "$QUAY_CRED" && -n "$QUAY_ORG") || -n "$DOCKER_CRED" ]]; then + QUAY_CRED_BASE64=$(echo -n "$QUAY_CRED" | base64) + DOCKER_CRED_BASE64=$(echo -n "$DOCKER_CRED" | base64) + + cat <<EOF > $TEMP_DIR/config.json +{ + "auths": { + "quay.io/$QUAY_ORG": { + "auth": "$QUAY_CRED_BASE64" + }, + "https://index.docker.io/v1/": { + "auth": "$DOCKER_CRED_BASE64" + } + } +} +EOF + execute_kubectl "create secret generic dockerconfig-secret --from-file=$TEMP_DIR/config.json" + echo "" + else + echo "## No registry DOCKER_CRED or QUAY_CRED arguments have been defined !" + exit 1 + fi + fi + + echo "### Creating the backstage service account" + execute_kubectl "create sa my-backstage" + echo "" + + echo "Give cluster-admin role to the backstage SA to access the kubernetes API resources" + cat <<EOF > $TEMP_DIR/rbac.yml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: backstage-$NAMESPACE-cluster-access +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: +- kind: ServiceAccount + name: my-backstage + namespace: $NAMESPACE +EOF + + execute_kubectl "apply -f $TEMP_DIR/rbac.yml" + echo "" + + echo "### Creating a secret hosting your SSH public key" + execute_kubectl "create secret generic quarkus-dev-ssh-key --from-file=key=$PUBLIC_KEY_PASS" + echo "" + + echo "### Creating the Fedora podman virtual machine" + execute_kubectl "apply -f manifest/installation/virt/quarkus-dev-virtualmachine.yml" + echo "" + + echo "### Apply the label: argocd.argoproj.io/managed-by=<argocd_namespace> on your namespace" + execute_kubectl "label namespace $NAMESPACE argocd.argoproj.io/managed-by=openshift-gitops" + echo "" + + echo "### Adding your namespace to the parameter: sourceNamespaces of the ArgoCD CR ..." + ARGOCD_NAME="argocd" + NAMESPACE_ARGOCD="openshift-gitops" + + # Fetch the current ArgoCD resource + ARGOCD_JSON=$(kubectl get argocd $ARGOCD_NAME -n $NAMESPACE_ARGOCD -o json) + + if echo "$ARGOCD_JSON" | jq -e --arg ns "$NAMESPACE" '.spec.sourceNamespaces | index($ns)' > /dev/null; then + echo "Namespace '$NAMESPACE' already exists in sourceNamespaces." + else + echo "Adding namespace '$NAMESPACE' to sourceNamespaces." + PATCH=$(echo "$ARGOCD_JSON" | jq --arg ns "$NAMESPACE" '.spec.sourceNamespaces += [$ns] | {spec: {sourceNamespaces: .spec.sourceNamespaces}}') + kubectl patch argocd $ARGOCD_NAME -n $NAMESPACE_ARGOCD --type merge --patch "$PATCH" + fi +} + +# Check if at least one argument is provided +if [[ $# -lt 1 ]]; then + echo "Usage: $0 {dev|provision-namespace} [OPTIONS]" + exit 1 +fi + +# Determine the subcommand +case "$1" in + dev) + shift # Remove the subcommand from the arguments + dev "$@" + ;; + provision-namespace) + shift # Remove the subcommand from the arguments + provision_namespace "$@" + ;; + *) + echo "Invalid subcommand: $1" + echo "Usage: $0 {dev|provision-namespace} [OPTIONS]" + exit 1 + ;; +esac \ No newline at end of file diff --git a/bin/start-dev b/bin/start-dev deleted file mode 100755 index 2ffde4b..0000000 --- a/bin/start-dev +++ /dev/null @@ -1,218 +0,0 @@ -#!/bin/bash - -# -# Render app-config.local.yaml from template -# - -# Define the green and red color escape sequences -GREEN='\033[0;32m' -RED='\033[0;31m' -NC='\033[0m' # No Color - -# Fallback to the second argument if the first is empty -setf() { - if [ -z "${!1}" ]; then - export $1="$2" - fi -} - -# Print key-value pair in green if value is not empty, otherwise in red -printv() { - if [ -z "$2" ]; then - echo -e "${RED}$1${NC}=${RED}<empty>${NC}" - else - echo -e "${GREEN}$1${NC}=$2" - fi -} - -# Print key-password pair in green if value is not empty, otherwise in red, replacing password with '*' -printp() { - if [ -z "$2" ]; then - echo -e "${RED}$1${NC}=${RED}<empty>${NC}" - else - echo -e "${GREEN}$1${NC}=********" - fi -} - -# Wait for process to finish while showing a spinner -waitp() { - local pid=$1 - local message=$2 - local delay=0.1 - local spinner_chars="/-\|" - echo -n "$messge" - while [ -d /proc/$pid ]; do - for i in $(seq 0 3); do - echo -ne "$message ${spinner_chars:i:1}" "\r" - sleep $delay - done - done - - # Check the exit status of the process - wait $pid - local exit_status=$? - - # Replace spinner with a check mark or an X based on the result - if [ $exit_status -eq 0 ]; then - echo -e "$message ${GREEN}✔${NC}" - else - echo -e "$message ${RED}✘${NC}" - fi -} - -usage() { - echo "Usage: $0 [options]" - echo "Options:" - echo " -t, --title <title> The title of the Backstage instance" - echo " -o, --org <org> The organization name" - echo " -u, --github-user <github-user> The GitHub user" - echo " --github-token <github-token> The GitHub token" - echo " --gitea-host <host> The Gitea Host" - echo " --gitea-user <gitea-user> The Gitea username" - echo " --gitea-password <gitea-password> The Gitea username" - exit 1 -} - -# Set defaults -setf "BACKSTAGE_TITLE" "My Backstage" -setf "BACKSTAGE_ORG" "q-shift" -setf "BACKSTAGE_GITHUB_USER" "q-shift" -setf "GITHUB_PERSONAL_ACCESS_TOKEN" "" -setf "BACKSTAGE_TEAM_NAME" "team" -setf "BACKSTAGE_APP_BASE_URL" "http://localhost:3000" -setf "BACKSTAGE_BACKEND_BASE_URL" "http://localhost:7007" -setf "BACKSTAGE_AUTH_SECRET" `node -p 'require("crypto").randomBytes(24).toString("base64")'` - -# -# Optionally override the default values (above) with command line arguments: -# -t: Title -# -o: Organization -# --github-user: GitHub User -# --github-token: GitHub Token -# -while [[ $# -gt 0 ]]; do - echo "Processing $1" - case $1 in - -t|--title) - BACKSTAGE_TITLE=$2 - shift 2 - ;; - -o|--org) - BACKSTAGE_ORG=$2 - shift 2 - ;; - -u|--github-user) - BACKSTAGE_GITHUB_USER=$2 - shift 2 - ;; - --github-token) - GITHUB_PERSONAL_ACCESS_TOKEN=$2 - shift 2 - ;; - --gitea-url) - GITEA_HOST=$2 - shift 2 - ;; - --gitea-user) - GITEA_USERNAME=$2 - shift 2 - ;; - --gitea-password) - GITEA_PASSWORD=$2 - shift 2 - ;; - -h|--help) - usage - exit 0 - ;; - -*|--*) - echo "Unknown option $1" - usage - exit 1 - ;; - *) - POSITIONAL_ARGS+=("$1") # save positional arg - shift # past argument - ;; - esac -done - -echo "Generating app-config.local.yaml." - -# if kubectl does not exist, then exit -if ! command -v kubectl &> /dev/null; then - echo "kubectl is not installed. Aborting" - exit 1 -fi - -# check if ~/.kube/config exists and if not, then exit -if [ ! -f "$HOME/.kube/config" ]; then - echo "Kubernetes config file ($HOME/.kube/config) does not exist. Aborting" - exit 1 -fi - -# Run kubectl version asynchronously -kubectl version > /dev/null 2>&1 & -KUBECTL_PID=$! - -# Show spinner while waiting for kubectl to complete -waitp $KUBECTL_PID "Checking connection to the Kubernetes cluster" - -# Wait for kubectl to finish -wait $KUBECTL_PID -KUBECTL_EXIT_CODE=$? - -# Check exit code and print result -if [ $KUBECTL_EXIT_CODE -ne 0 ]; then - exit 1 -fi - -# Kubernetes -setf KUBERNETES_CONFIG_PATH "$HOME/.kube/config" -setf KUBERNETES_API_URL `kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'` -setf KUBERNETES_USER `kubectl config view --minify -o jsonpath='{.users[0].name}'` -setf KUBERNETES_TOKEN `cat $KUBERNETES_CONFIG_PATH | grep -A 2 "name: $KUBERNETES_USER" | grep token | awk '{print $2}'` - - -# Argo CD -setf ARGOCD_ADMIN_USER admin -setf ARGOCD_ADMIN_PASSWORD `kubectl get secrets -n openshift-gitops argocd-cluster -o yaml | grep password | awk '{print $2}' | base64 -d` -setf ARGOCD_COOKIE `kubectl get secrets -n openshift-gitops argocd-secret -o yaml | grep clientSecret | awk '{print $2}' | base64 -d` -setf ARGOCD_SERVER "https://openshift-gitops-server-openshift-gitops.apps.qshift.snowdrop.dev" - -# Github -if [ -z "$GITHUB_PERSONAL_ACCESS_TOKEN" ]; then - if command -v gh &> /dev/null; then - setf GITHUB_PERSONAL_ACCESS_TOKEN `gh auth status -t | grep Token: | awk -F": " '{print $2}'` - else - echo "GITHUB_PERSONAL_ACCESS_TOKEN is not defined and can't be read from 'gh' command. Aborting" - exit 1 - fi -fi - -# Gitea -setf GITEA_HOST "localhost" -setf GITEA_USERNAME "admin" -setf GITEA_PASSWORD "admin" - -echo "Using values..." -printv "BACKSTAGE_TITLE" "$BACKSTAGE_TITLE" -printv "BACKSTAGE_ORG" "$BACKSTAGE_ORG" -printv "BACKSTAGE_GITHUB_USER" "$BACKSTAGE_GITHUB_USER" -printp "GITHUB_PERSONAL_ACCESS_TOKEN" "${GITHUB_PERSONAL_ACCESS_TOKEN:0:4}******" -printv "GITEA_HOST" "$GITEA_HOST" -printv "GITEA_USERNAME" "$GITEA_USERNAME" -printp "GITEA_PASSWORD" "$GITEA_PASSWORD" -printv "KUBERNETES_API_URL" "$KUBERNETES_API_URL" -printv "KUBERNETES_USER" "$KUBERNETES_USER" -printp "KUBERNETES_TOKEN" "$KUBERNETES_TOKEN" -printv "ARGOCD_SERVER" "$ARGOCD_SERVER" -printv "ARGOCD_ADMIN_USER" "$ARGOCD_ADMIN_USER" -printp "ARGOCD_ADMIN_PASSWORD" "$ARGOCD_ADMIN_PASSWORD" - -envsubst < manifest/templates/app-config.qshift.tmpl > app-config.local.yaml -export NODE_TLS_REJECT_UNAUTHORIZED=0 - -echo "Starting Backstage..." -# Run Backstage in Development Mode -yarn dev From 3544dfe6c73f632da4c893418a318efc35078365 Mon Sep 17 00:00:00 2001 From: cmoulliard <cmoulliard@redhat.com> Date: Wed, 2 Oct 2024 18:00:26 +0200 Subject: [PATCH 2/4] Rename section title Signed-off-by: cmoulliard <cmoulliard@redhat.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9aed101..9e1cfdc 100644 --- a/README.md +++ b/README.md @@ -310,7 +310,7 @@ export NODE_TLS_REJECT_UNAUTHORIZED=0 yarn dev ``` -### Using the start-dev script +### Using the qshift script A helper script that will create the `app-config.local.yaml` file and start the backstage application locally is available. The script will also infer all the variables from the developers environment and also supports overriding inferred values using flags. From d2fad04f7d20e3f2803b4829cda50ae9acae3e8a Mon Sep 17 00:00:00 2001 From: cmoulliard <cmoulliard@redhat.com> Date: Wed, 2 Oct 2024 18:14:11 +0200 Subject: [PATCH 3/4] Swap the section to run backstage locally or on the cluster. Created a script section to cover the 2 commands Signed-off-by: cmoulliard <cmoulliard@redhat.com> --- README.md | 124 +++++++++++++++++++++++++++--------------------------- 1 file changed, 63 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index 9e1cfdc..40c822f 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,3 @@ - -* [Backstage QShift Showcase](#backstage-qshift-showcase) - * [Prerequisites](#prerequisites) - * [Provision an ocp cluster](#provision-an-ocp-cluster) - * [Kubevirt](#kubevirt) - * [GitOps](#gitops) - * [Tekton](#tekton) - * [Backstage instructions](#backstage-instructions) - * [First steps](#first-steps) - * [Deploy and use Backstage on OCP](#deploy-and-use-backstage-on-ocp) - * [Run backstage locally](#run-backstage-locally) - * [Curl backstage](#curl-backstage) - * [Clean up](#clean-up) - # Backstage QShift Showcase The backstage QShift application has been designed to showcase QShift (Quarkus on OpenShift). It is composed of the following plugins and integrated with different backend systems: @@ -108,14 +94,14 @@ kubectl apply -f subscription-pipelines.yml ## Backstage instructions This section explains how to use Backstage: -- [Deployed](#deploy-and-use-backstage-on-ocp) on an OpenShift cluster or -- Running [locally](#run-backstage-locally) +- [locally](#run-backstage-locally) +- [Deployed](#deploy-and-use-backstage-on-ocp) on an OpenShift cluster (e.g: https://console-openshift-console.apps.qshift.snowdrop.dev/) ### First steps -Before to install and use our Backstage application, it is needed to perform some steps such as: +Before to install and use our QShift Backstage application, it is needed to perform some steps such as: - Create an OpenShift project -- Provide your registry credentials (quay.io, docker, etc) as `config.json` file +- Provide your registry credentials (quay.io, docker, etc.) as `config.json` file - Create a ServiceAccount for backstage (needed to get the token) - Create a ClusterRoleBinding for the SA to allow backstage to access the Kube API resources - Create the Fedora Podman VM using your public key to ssh @@ -223,7 +209,44 @@ EOF <MY_NAMESPACE> quarkus-dev 32s Running True ``` -**Important**: Alternatively, you can use our bash script able to execute all the steps: [qshift](bin%2Fqshift) +**Note**: Alternatively, you can use the `Qshift` bash script automating all such steps as described at the section [Using the qshif script](using-the-qshift-script) + +We are now ready to deploy and use backstage within your project as documented at the following section. + +### Run backstage locally + +Create your `app-config.local.yaml` file using the [app-config.qshift.tmpl](manifest%2Ftemplates%2Fapp-config.qshift.tmpl) file and set the different +url/password/tokens using the env [backstage_env_secret.tmpl](manifest%2Ftemplates%2Fbackstage_env_secret.tmpl) like this + +```bash +cp manifest/templates/backstage_env_secret.tmpl backstage_env_secret.env + +# Edit the backstage_env_secret.env and set the different url/password/tokens !! + +export $(grep -v '^#' backstage_env_secret.env | xargs) +envsubst < manifest/templates/app-config.qshift.tmpl > app-config.local.yaml +``` + +**Warning**: If you use node 20, then export the following env var `export NODE_OPTIONS=--no-node-snapshot` as documented [here](https://backstage.io/docs/getting-started/configuration/#create-a-new-component-using-a-software-template). + +Next run the following commands to start the front and backend using the `app-config.local.yaml` config file: + +You can now open the backstage URL `http://localhodt:3000`, select from the left menu `/create` and scaffold a new project using the template `Create a Quarkus application` + +```sh +yarn install +export NODE_TLS_REJECT_UNAUTHORIZED=0 +yarn dev +``` + +**Note**: Alternatively, you can use the `Qshift` bash script automating all such steps as described at the section [Using the qshif script](using-the-qshift-script) + +### Using the qshift script + +The `qshift` bash script provide 2 subcommands to either provision the namespace on the cluster or set create locally the `app-config-local.yaml` file with the proper credentials, etc. + +#### Subcommand: provision-namespace + ```bash ./bin/qshift provision-namespace -h @@ -237,6 +260,7 @@ Options: -k, --key-path <public_key_path> The path of your public to ssh to the VM (optional) --dry-run Run the kubectl command with dry-run=client ``` + Here is by example, how you could define the arguments ```bash ./bin/qshift provision-namespace \ @@ -248,7 +272,26 @@ Here is by example, how you could define the arguments ``` **Tips**: To execute the kubectl and oc commands of the script in `dry-run` mode, pass as argument `--dry-run` -We are now ready to deploy and use backstage within your project as documented at the following section. +#### Subcommand: dev + +The command `./bin/qshift dev` will create an `app-config.local.yaml` file locally using the needed URLs to access: ArgoCD, Gitea, the cluster, etc. like the proper credentials and start the `backstage` application. + +The script will infer all the variables from the developers environment and also supports overriding inferred values using flags. + +```bash +./bin/qshift dev -h +Processing -h +Usage: ./bin/qshift dev [options] +Options: + -t, --title <title> The title of the Backstage instance + -o, --org <org> The organization name + -u, --github-user <github-user> The GitHub user + --github-token <github-token> The GitHub token + --gitea-host <host> The Gitea Host + --gitea-user <gitea-user> The Gitea username + --gitea-password <gitea-password> The Gitea username + +``` ### Deploy and use Backstage on OCP @@ -284,47 +327,6 @@ Verify if backstage is alive using the URL: `https://backstage-<MY_NAMESPACE>.<O - Create a Quarkus Chatbot that consumes an API - Create a Quarkus Application from Quickstarts -### Run backstage locally - -Create your `app-config.local.yaml` file using the [app-config.qshift.tmpl](manifest%2Ftemplates%2Fapp-config.qshift.tmpl) file and set the different -url/password/tokens using the env [backstage_env_secret.tmpl](manifest%2Ftemplates%2Fbackstage_env_secret.tmpl) like this - -```bash -cp manifest/templates/backstage_env_secret.tmpl backstage_env_secret.env - -# Edit the backstage_env_secret.env and set the different url/password/tokens !! - -export $(grep -v '^#' backstage_env_secret.env | xargs) -envsubst < manifest/templates/app-config.qshift.tmpl > app-config.local.yaml -``` - -**Warning**: If you use node 20, then export the following env var `export NODE_OPTIONS=--no-node-snapshot` as documented [here](https://backstage.io/docs/getting-started/configuration/#create-a-new-component-using-a-software-template). - -Next run the following commands to start the front and backend using the `app-config.local.yaml` config file: - -You can now open the backstage URL `http://localhodt:3000`, select from the left menu `/create` and scaffold a new project using the template `Create a Quarkus application` - -```sh -yarn install -export NODE_TLS_REJECT_UNAUTHORIZED=0 -yarn dev -``` - -### Using the qshift script - -A helper script that will create the `app-config.local.yaml` file and start the backstage application locally is available. -The script will also infer all the variables from the developers environment and also supports overriding inferred values using flags. - -```bash -./bin/qshift dev -``` - -To see all the available options supported by the script: - -```bash -./bin/qshift dev -h -``` - ## Automate a scenario ### Prerequisites From 1f98d4c90674198cc42a802d21a1e12030dea158 Mon Sep 17 00:00:00 2001 From: cmoulliard <cmoulliard@redhat.com> Date: Wed, 2 Oct 2024 18:15:19 +0200 Subject: [PATCH 4/4] Add missing # to access internally the section Signed-off-by: cmoulliard <cmoulliard@redhat.com> --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 40c822f..f2691b6 100644 --- a/README.md +++ b/README.md @@ -209,7 +209,7 @@ EOF <MY_NAMESPACE> quarkus-dev 32s Running True ``` -**Note**: Alternatively, you can use the `Qshift` bash script automating all such steps as described at the section [Using the qshif script](using-the-qshift-script) +**Note**: Alternatively, you can use the `Qshift` bash script automating all such steps as described at the section [Using the qshif script](#using-the-qshift-script) We are now ready to deploy and use backstage within your project as documented at the following section. @@ -239,7 +239,7 @@ export NODE_TLS_REJECT_UNAUTHORIZED=0 yarn dev ``` -**Note**: Alternatively, you can use the `Qshift` bash script automating all such steps as described at the section [Using the qshif script](using-the-qshift-script) +**Note**: Alternatively, you can use the `Qshift` bash script automating all such steps as described at the section [Using the qshif script](#using-the-qshift-script) ### Using the qshift script