Script for removing IBM Automation foundation from WebSphere Automation operator

The remove-iaf.sh script removes the dependency on both the IBM Automation foundation and IBM Automation foundation core operators from a single instance of the WebSphere Automation 1.5.3 operator. Removing this dependency enables the WebSphere Automation operator to be upgraded to version 1.6.0.

For instructions on using this script, see Removing IBM Automation foundation.
#!/bin/bash
#-------------------------------------------------------------------------------------------------------
# remove-iaf.sh - remove IBM Automation Foundation from a 1.5.3 IBM WebSphere Automation installation
#-------------------------------------------------------------------------------------------------------
#
#  This script removes dependency on the IBM Automation Foundation Operator for a "WebSphereAutomation" custom 
#  resource instance running version 1.5.3 of IBM WebSphere Automation (WSA) Operator, enabling WSA Operator 
#  upgrades to versions 1.6.0 or higher. It upgrades a single WSA instance only.
#
#  This script contains the following optional parameters:
#  
#    Optional parameters:
#      $1 (WSA_OPERATOR_NAMESPACE) - the namespace containing the 1.5.3 IBM WebSphere Automation Operator install
#      $2 (WSA_INSTANCE_NAMESPACE) - the namespace containing the "WebSphereAutomation" custom resource instance
# 
#  Usage:
#    1. arguments - ./remove-iaf.sh <WSA_OPERATOR_NAMESPACE> <WSA_INSTANCE_NAMESPACE>" 
#    2. stdin     - ./remove-iaf.sh"
#  
#-------------------------------------------------------------------------------------------------------
set -o nounset
set -o pipefail

WSA_OPERATOR_NAMESPACE="openshift-operators"
WSA_INSTANCE_NAMESPACE="wasautomation"
SCALE_TO_ZERO_VERSION="1.5.3"

get_csv_name() {
    oc get clusterserviceversion -n $WSA_OPERATOR_NAMESPACE -o name | grep "$1\." | cut -d '/' -f2
}

is_greater_than_or_equal_to_scale_to_zero_version() {
    semVersion=$(oc get clusterserviceversion -n $WSA_OPERATOR_NAMESPACE -o name | grep "ibm-websphere-automation\." | cut -d '/' -f2 | cut -d 'v' -f2)
    IFS='.' read -r -a semVersionArray <<< "$semVersion"
    greaterEqualThanScaleToZeroVersion="false"
    if [[ "${#semVersionArray[@]}" == "3" ]]; then
        IFS='.' read -r -a scaleToZeroVersion <<< "$SCALE_TO_ZERO_VERSION"
        if [[ "${semVersionArray[0]}" -gt "${scaleToZeroVersion[0]}" ]]; then
            greaterEqualThanScaleToZeroVersion="true"
        elif [[ "${semVersionArray[0]}" -eq "${scaleToZeroVersion[0]}" ]]; then
            if [[ "${semVersionArray[1]}" -gt "${scaleToZeroVersion[1]}" ]]; then
                greaterEqualThanScaleToZeroVersion="true"
            elif [[ "${semVersionArray[1]}" -eq "${scaleToZeroVersion[1]}" ]]; then
                if [[ "${semVersionArray[2]}" -ge "${scaleToZeroVersion[2]}" ]]; then
                    greaterEqualThanScaleToZeroVersion="true"
                fi
            fi
        fi
    fi
    if [[ "$greaterEqualThanScaleToZeroVersion" == "false" ]]; then
        echo "You must be on a version greater than $SCALE_TO_ZERO_VERSION to upgrade the WebSphere Automation operator. Exiting."
        exit 
    fi
}

get_subscription_name() {
    oc get subscription -n $WSA_OPERATOR_NAMESPACE -o name | grep "$1" | cut -d '/' -f2
}

delete_csv() {
    csv_count=$(oc get clusterserviceversion -n $WSA_OPERATOR_NAMESPACE -o name | grep "$1\." -c)
    csv=$(get_csv_name "$1")
    if [[ "$csv_count" == "1" ]]; then
        echo "    > Deleting ClusterServiceVersion..."
        oc delete clusterserviceversion -n $WSA_OPERATOR_NAMESPACE $csv
    elif [[ "$csv_count" == "0" ]]; then
        echo "    > ClusterServiceVersion not found."
    else
        echo "Error: Found multiple entries for CSV '$1'. Exiting."
        exit 1
    fi
}

delete_subscription() {
    csv_count=$(oc get subscription -n $WSA_OPERATOR_NAMESPACE -o name | grep "$1" -c)
    sub=$(get_subscription_name "$1")
    if [[ "$csv_count" == "1" ]]; then
        echo "    > Deleting Subscription..."
        oc delete subscription -n $WSA_OPERATOR_NAMESPACE $sub
    elif [[ "$csv_count" == "0" ]]; then
        echo "    > Subscription not found."
    else
        echo "Error: Found multiple entries for Subscription '$1'. Exiting."
        exit 1
    fi
}

delete_operator() {
    echo "==> Deleting operator $1"
    delete_csv $1
    if [[ "$1" == "ibm-automation" ]]; then
        delete_subscription "ibm-automation-v"
    elif [[ "$WSA_OPERATOR_NAMESPACE" == "openshift-operators" ]]; then
        delete_subscription "websphere-automation"
        delete_subscription $1
    else
        delete_subscription $1
    fi
}

scale_to_zero() {
    scaleToZero=$1
    if [[ "$scaleToZero" == "true" ]]; then
        oc get websphereautomation -n $WSA_INSTANCE_NAMESPACE -o name | cut -d '/' -f2 | xargs oc patch websphereautomation -n $WSA_INSTANCE_NAMESPACE -p "{\"spec\":{\"scaleToZero\": $scaleToZero}}" --type=merge
    else
        oc get websphereautomation -n $WSA_INSTANCE_NAMESPACE -o name | cut -d '/' -f2 | xargs oc patch websphereautomation -n $WSA_INSTANCE_NAMESPACE -p "[{ \"op\": \"remove\", \"path\": \"/spec/scaleToZero\" }]" --type=json
    fi
}

delete_all() {
    resource=$1
    echo "==> Deleting all $resource."
    oc get $resource -n $WSA_INSTANCE_NAMESPACE -o name | cut -d '/' -f2 | xargs oc delete $resource -n $WSA_INSTANCE_NAMESPACE
}

remove_coupled_fields() {
    resource=$1
    name=$2
    echo "==> Remove coupled fields in $resource $name."
    oc patch $resource -n $WSA_INSTANCE_NAMESPACE $name -p "{\"metadata\":{\"ownerReferences\":null}}" --type=merge
    oc patch $resource -n $WSA_INSTANCE_NAMESPACE $name -p "{\"metadata\":{\"finalizers\":null}}" --type=merge
    oc patch $resource -n $WSA_INSTANCE_NAMESPACE $name -p "{\"metadata\":{\"labels\":{\"app.kubernetes.io/managed-by\":null}}}" --type=merge
    sleep 2
    check_coupled_fields $resource $name
}

check_coupled_fields() {
    resource=$1
    name=$2
    owner_references_count=$(oc get $resource $name -n $WSA_INSTANCE_NAMESPACE -o jsonpath='{.metadata}' | grep "ownerReferences" -c)
    if [[ "$owner_references_count" -ne "0" ]]; then
        echo "Could not delete '.metadata.ownerReferences' field in $resource $name. Exiting."
        exit 1
    fi
    managed_by_count=$(oc get $resource $name -n $WSA_INSTANCE_NAMESPACE -o jsonpath='{.metadata.labels}' | grep "app.kubernetes.io/managed-by" -c)
    if [[ "$managed_by_count" -ne "0" ]]; then
        echo "Could not delete '.metadata.labels["app.kubernetes.io/managed-by"]' field in $resource $name. Exiting."
        exit 1
    fi
    finalizers_count=$(oc get $resource $name -n $WSA_INSTANCE_NAMESPACE -o jsonpath='{.metadata.finalizers}' | grep "\"" -c)
    if [[ "$finalizers_count" -ne "0" ]]; then
        echo "Could not delete '.metadata.finalizers' field in $resource $name. Exiting."
        exit 1
    fi
    echo "  > Successfully updated fields!" 
}

wait_for_wsa_ready() {
    retries=$1

    while true
    do
        echo "==> Waiting for WebSphere Automation operator to start... ($retries retries)"
        alive=$(oc get deployments websphere-automation-operator-controller-manager -n $WSA_OPERATOR_NAMESPACE -o name | grep "deployment.apps" -c)
        if [[ "$alive" -eq "1" ]]; then
            available_replicas=$(oc get deployments websphere-automation-operator-controller-manager -n $WSA_OPERATOR_NAMESPACE -o=jsonpath="{range .items[*]}{.status.availableReplicas}")
            [[ "$available_replicas" -eq "1" ]] && break
        fi
        ((retries-=1))
        if (( retries < 0 )); then
            echo "  > Waited too long for WSA to start. Exiting."
            exit 1
        fi
        sleep 10
    done

    # Wait 20 seconds for reconcile loop to kick in
    sleep 20

    retries=$1
    while true
    do
        echo "==> Waiting for WebSphere Automation operator to be ready... ($retries retries)"
        # There should only be one WSA instance in a single namespace, so xargs takes one element only
        wsa_ready=$(oc get websphereautomation -o name -n $WSA_INSTANCE_NAMESPACE | cut -d '/' -f2 | xargs oc get websphereautomation -n $WSA_INSTANCE_NAMESPACE -o jsonpath='{.status.conditions}' | grep "All prerequisites and installed components are ready" -c)
        [[ "$wsa_ready" -eq "1" ]] && break
        ((retries-=1))
        if (( retries < 0 )); then
            echo "  > Waited too long for WSA to be ready. Exiting."
            exit 1
        fi
        sleep 10
    done
}

wait_for_kafka_claim_deleted() {
    retries=10
    while true
    do
        echo "==> Waiting for KafkaClaim to be deleted..."
        kafka_claim_deleted=$(oc get kafkaclaims -o name -n $WSA_INSTANCE_NAMESPACE | grep "kafkaclaim" -c)
        [[ "$kafka_claim_deleted" -eq "0" ]] && break
        ((retries-=1))
        if (( retries < 0 )); then
            echo "  > Waited too long for KafkaClaims to be deleted. Exiting."
            exit 1
        fi
        sleep 10
    done
}

wait_for_kafka_composite_deleted() {
    retries=10
    while true
    do
        echo "==> Waiting for KafkaClaim to be deleted..."
        kafka_composite_deleted=$(oc get kafkacomposites -o name -n $WSA_INSTANCE_NAMESPACE | grep "kafkacomposite" -c)
        [[ "$kafka_composite_deleted" -eq "0" ]] && break
        ((retries-=1))
        if (( retries < 0 )); then
            echo "  > Waited too long for KafkaComposites to be deleted. Exiting."
            exit 1
        fi
        sleep 10
    done
}

get_user_input() {
    num_args=$#
    if [[ "$num_args" -eq "0" ]]; then
        read -p 'WebSphere Automation operator namespace: ' WSA_OPERATOR_NAMESPACE
        read -p 'WebSphere Automation instance namespace: ' WSA_INSTANCE_NAMESPACE
    elif [[ "$num_args" -eq "2" ]]; then
        WSA_OPERATOR_NAMESPACE=$1
        WSA_INSTANCE_NAMESPACE=$2
    else
        echo "Usage:"
        echo " 1. arguments - ./remove-iaf.sh <WSA_OPERATOR_NAMESPACE> <WSA_INSTANCE_NAMESPACE>" 
        echo " 2. stdin     - ./remove-iaf.sh"
        exit 
    fi
    if [[ -z "$WSA_OPERATOR_NAMESPACE" ]]; then
        echo "==> Error: Must set the WebSphere Automation operator's namespace. Exiting."
        exit
    fi
    if [[ -z "$WSA_INSTANCE_NAMESPACE" ]]; then
        echo "==> Error: Must set the WebSphere Automation instance's namespace. Exiting."
        exit
    fi
    echo "==> WebSphere Automation operator namespace is set to: $WSA_OPERATOR_NAMESPACE"
    echo "==> WebSphere Automation instance namespace is set to: $WSA_INSTANCE_NAMESPACE"
}

validate_user_input() {
    # Validate oc login
    oc status > /dev/null 2>&1 && true
    if [[ $? -ne 0 ]]; then
        echo "==> Error: Run 'oc login' to log into a cluster before running remove-iaf.sh. Exiting."
        exit 1
    fi

    # Validate project  
    wsa_operator_project=$(oc get project $WSA_OPERATOR_NAMESPACE -o name | grep "project.project.openshift.io" -c)
    wsa_instance_project=$(oc get project $WSA_INSTANCE_NAMESPACE -o name | grep "project.project.openshift.io" -c)
    if [[ "$wsa_operator_project" != "1" ]]; then
        echo "==> Error: WebSphere Automation operator namespace does not exist. Not continuing with IBM Automation Foundation removal. Exiting."
        exit
    fi
    if [[ "$wsa_instance_project" != "1" ]]; then
        echo "==> Error: WebSphere Automation instance namespace does not exist. Not continuing with IBM Automation Foundation removal. Exiting."
        exit
    fi

    # Validate controller-manager
    wsa_operator=$(oc get deployment websphere-automation-operator-controller-manager -n $WSA_OPERATOR_NAMESPACE -o name | grep "deployment.apps" -c)
    if [[ "$wsa_operator" != "1" ]]; then
        echo "==> Error: WebSphere Automation operator is not installed. Not continuing with IBM Automation Foundation removal. Exiting."
        exit
    fi

    # Validate WSA instance
    wsa_instance=$(oc get websphereautomation -n $WSA_INSTANCE_NAMESPACE -o name | grep "websphereautomation.automation.websphere.ibm.com" -c)
    if [[ "$wsa_operator" != "1" ]]; then
        if [[ "$wsa_operator" == "0" ]]; then
            echo "==> Error: There is no WebSphere Automation CR instance in this namespace. Not continuing with IBM Automation Foundation removal. Exiting." 
        else
            echo "==> Error: There must only be one WebSphere Automation CR instance in this namespace. Not continuing with IBM Automation Foundation removal. Exiting." 
        fi
        exit
    fi
    oc project $WSA_INSTANCE_NAMESPACE
}

# Start here
get_user_input $@
validate_user_input
# Check that scaleToZero feature exists before upgrading
is_greater_than_or_equal_to_scale_to_zero_version
echo ""
echo "==> Starting IBM Automation Foundation removal..."

# Delete IAF core and base
delete_operator "ibm-automation-core"
delete_operator "ibm-automation"

# Scale down WSA, WSS, WSH replicas to 0 (if applicable)
scale_to_zero "true"

wait_for_wsa_ready 50
echo "  > Successfully scaled down WebSphere Automation, Health and Secure replicas!"

# Delete WSA Operator and rest of IAF Operators
delete_operator "ibm-websphere-automation"
delete_operator "ibm-automation-eventprocessing"
delete_operator "ibm-automation-flink"
delete_operator "ibm-automation-elastic"

# Backup Kafka cluster (if applicable)
oc get kafkaclaims iaf-system -n $WSA_INSTANCE_NAMESPACE -o name
iaf_kafka_found=$?
if [[ "$iaf_kafka_found" -eq "0" ]]; then
    echo "==> Kafka was found. Writing configuration into remove-iaf.sh-kafka.yaml."
    oc get kafkas iaf-system -n $WSA_INSTANCE_NAMESPACE -o yaml > remove-iaf.sh-kafka.yaml
    # Delete Kafka Claim instances "iaf-system" and "websphereauto-kafka-auth"
    oc delete kafkaclaims -n $WSA_INSTANCE_NAMESPACE iaf-system
    oc delete kafkaclaims -n $WSA_INSTANCE_NAMESPACE websphereauto-kafka-auth
else
    echo "==> Kafka was not found. Continuing with removal."
fi

# Remove ownerReference, managed-by, and finalizer fields (if applicable)
remove_coupled_fields "zenservice" "iaf-zen-cpdservice"
remove_coupled_fields "cartridge" "websphereauto"
remove_coupled_fields "certificate" "iaf-system-automationui-aui-zen-ca"
remove_coupled_fields "certificate" "iaf-system-automationui-aui-zen-cert"
remove_coupled_fields "issuer" "iaf-system-automationui-aui-zen-issuer"
remove_coupled_fields "issuer" "iaf-system-automationui-aui-zen-ss-issuer"
remove_coupled_fields "secret" "external-tls-secret"

# Delete IAF instances
delete_all "cartridges"
delete_all "automationuiconfigs"
delete_all "automationbases"
delete_all "cartridgerequirements"

# Re-apply Kafka cluster
if [[ "$iaf_kafka_found" -eq "0" ]]; then
    wait_for_kafka_claim_deleted
    wait_for_kafka_composite_deleted
    oc apply -f remove-iaf.sh-kafka.yaml -n $WSA_INSTANCE_NAMESPACE
    kafka_updated=$?
    if [[ "$kafka_updated" -ne "0" ]]; then
        echo "==> Could not re-apply backup Kafka cluster from file remove-iaf.sh-kafka.yaml!"
        echo "  > Manually add the remove-iaf.sh-kafka.yaml file to the cluster before calling remove-iaf.sh again. Exiting."
        exit
    fi
fi

# Restore WSA, WSS, WSH replicas
scale_to_zero "false"

echo "==> IBM Automation Foundation removal complete!"
echo "    Your OpenShift cluster is ready to install IBM WebSphere Automation Operator version >=1.6.0."
echo "    To continue this in-place migration, please install the latest driver in the OpenShift UI using OperatorHub."