apiVersion: v1
data:
  swift-ring-tool: |
    #!/usr/bin/env bash
    TARFILE="/tmp/swiftrings.tar.gz"
    BASE_URL="https://kubernetes.default.svc/api/v1/namespaces/${NAMESPACE}/configmaps"
    URL="${BASE_URL}/${CM_NAME}"
    DEVICESFILE="/var/lib/config-data/ring-devices/devices.txt"

    # Credentials to be used by curl
    export CURL_CA_BUNDLE=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
    TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

    function get() {
        # Get the ConfigMap with the Swiftrings if it exists. If it exists, untar it
        # and update the rings. If it does not exist, create a new ConfigMap using a
        # POST request. If the response code is neither 200 or 404 fail early, b/c it
        # is unclear if there might be an existing set of rings that should not be
        # overwritten
        HTTP_CODE=$(/usr/bin/curl \
            -H "Authorization: Bearer $TOKEN" \
            -o "${CM_NAME}.json" \
            -w "%{http_code}" \
            --silent --show-error --fail-with-body \
            -X GET "${URL}")

        case $HTTP_CODE in
            "200")
                # Configmap was found
                # Get JSON keyvalue without jq
                grep -e '"swiftrings.tar.gz": ".*"' "${CM_NAME}.json"  | cut -f 4 -d '"' | base64 -d > $TARFILE
                [ -s $TARFILE ] && tar --totals -xzf $TARFILE
                grep -e '"account.ring.gz": ".*"' "${CM_NAME}.json"  | cut -f 4 -d '"' | base64 -d > account.ring.gz
                grep -e '"container.ring.gz": ".*"' "${CM_NAME}.json"  | cut -f 4 -d '"' | base64 -d > container.ring.gz
                grep -e '"object.ring.gz": ".*"' "${CM_NAME}.json"  | cut -f 4 -d '"' | base64 -d > object.ring.gz
            ;;

            "404")
                rm "${CM_NAME}.json"
            ;;

            *)
                cat "${CM_NAME}.json"
                rm "${CM_NAME}.json"
                exit 1
            ;;
        esac
    }


    function init() {
        # Create new rings if not existing
        for f in account container object; do
            [ ! -e ${f}.builder ] && [ -s ${f}.ring.gz ] && { echo "${f}.ring.gz file found without ${f}.builder - exiting" ; exit 0; }
            [ ! -e ${f}.builder ] && swift-ring-builder ${f}.builder create ${SWIFT_PART_POWER} ${SWIFT_REPLICAS} ${SWIFT_MIN_PART_HOURS} || true
        done
    }


    function update() {
        # Iterate over all devices from the list created by the SwiftStorage CR.
        while read REGION ZONE HOST DEVICE_NAME WEIGHT NODE; do
            # Check if host/disk exists and only add if not
            swift-ring-builder account.builder search --ip $HOST --device $DEVICE_NAME
            [ $? -eq 2 ] && swift-ring-builder account.builder add --region $REGION --zone $ZONE --ip $HOST --port 6202 --device $DEVICE_NAME --weight $WEIGHT --meta "$NODE"

            # Check if host/disk exists and only add if not
            swift-ring-builder container.builder search --ip $HOST --device $DEVICE_NAME
            [ $? -eq 2 ] && swift-ring-builder container.builder add --region $REGION --zone $ZONE --ip $HOST --port 6201 --device $DEVICE_NAME --weight $WEIGHT --meta "$NODE"

            # Check if host/disk exists and only add if not
            swift-ring-builder object.builder search --ip $HOST --device $DEVICE_NAME
            [ $? -eq 2 ] && swift-ring-builder object.builder add --region $REGION --zone $ZONE --ip $HOST --port 6200 --device $DEVICE_NAME --weight $WEIGHT --meta "$NODE"
    done < $DEVICESFILE
    }


    function metaswap() {
    /usr/bin/env python3 << EOF
    from swift.common.ring import RingBuilder
    builder = RingBuilder.load("$1")
    for dev in builder.devs:
        if dev and dev.get('meta') and dev.get('device') == 'pv':
            dev['meta'], dev['ip'] = dev['ip'], dev['meta']
    builder.save("$1")
    EOF
    }


    function rebalance() {
        for f in *.builder; do
            metaswap $f || exit
            swift-ring-builder $f rebalance
            metaswap $f || exit
            swift-ring-builder $f write_ring
        done
    }


    function forced_rebalance() {
        for f in *.builder; do
            swift-ring-builder $f pretend_min_part_hours_passed
        done
        rebalance
    }


    function drain() {
        for f in *.builder; do
            swift-ring-builder $f set_weight --ip $1 0 --yes
        done
    }


    function remove() {
        for f in *.builder; do
            swift-ring-builder $f remove --ip "$1"
        done
    }


    function push() {
        # Tar up all the ring data and either create or update the SwiftRing ConfigMap
        BINARY_DATA=`tar --totals -cz *.builder *.ring.gz backups/*.builder | /usr/bin/base64 -w 0`
        ACCOUNT_RING_GZ=`base64 -w 0 account.ring.gz`
        CONTAINER_RING_GZ=`base64 -w 0 container.ring.gz`
        OBJECT_RING_GZ=`base64 -w 0 object.ring.gz`

        if [ ! -e "${CM_NAME}.json" ]; then
            METHOD="POST"
            URL="${BASE_URL}"
            VERSION=""
        else
            METHOD="PUT"
            VERSION="$(grep -oP '"resourceVersion": ".*"' "${CM_NAME}.json"),"
        fi

        CONFIGMAP_JSON='{
            "apiVersion":"v1",
            "kind":"ConfigMap",
            "metadata":{
                '${VERSION}'
                "name":"'${CM_NAME}'",
                "namespace":"'${NAMESPACE}'",
                "finalizers": ["openstack.org/swiftring"],
                "ownerReferences": [
                    {
                        "apiVersion": "'${OWNER_APIVERSION}'",
                        "kind": "'${OWNER_KIND}'",
                        "name": "'${OWNER_NAME}'",
                        "uid": "'${OWNER_UID}'"
                    }
                ]
            },
            "binaryData":{
                "swiftrings.tar.gz": "'${BINARY_DATA}'",
                "account.ring.gz": "'${ACCOUNT_RING_GZ}'",
                "container.ring.gz": "'${CONTAINER_RING_GZ}'",
                "object.ring.gz": "'${OBJECT_RING_GZ}'"
            }
        }'

        # https://kubernetes.io/docs/reference/kubernetes-api/config-and-storage-resources/config-map-v1/#update-replace-the-specified-configmap
        RESPONSE_BODY=$(/usr/bin/curl \
            -H "Authorization: Bearer $TOKEN" \
            --data-binary "${CONFIGMAP_JSON}" \
            -H 'Content-Type: application/json' \
            --silent --show-error --fail-with-body \
            -X "${METHOD}" "${URL}")
        [ $? -ne 0 ] && echo -e "${RESPONSE_BODY}" || true
    }


    function usage() {
        echo "Usage: $0 [get|init|update|rebalance|push|all]"
        echo
        echo "get        Fetch rings from ConfigMap ${CM_NAME}"
        echo "init       Create new .builder files if not existing"
        echo "update     Update rings using input from ${DEVICESFILE}"
        echo "rebalance  Rebalance rings"
        echo "push       Push rings to ConfigMap ${CM_NAME}"
        echo "all        All above operations in sequence"
        echo
        echo "drain      Set weight of all node devices to 0"
        echo "remove     Remove node from all rings"
        echo
    }


    case $1 in
        "all")
            get
            init
            update
            rebalance
            push
        ;;

        "get")
            get
        ;;

        "init")
            init
        ;;

        "update")
            update
        ;;

        "drain")
            drain $2
        ;;

        "remove")
            remove $2
        ;;

        "metaswap")
            metaswap $2
        ;;

        "rebalance")
            rebalance
        ;;

        "forced_rebalance")
            forced_rebalance
        ;;

        "push")
            push
        ;;

        *)
            usage
            exit 1
        ;;
    esac
kind: ConfigMap
metadata:
  creationTimestamp: '2026-04-02T13:58:55Z'
  labels:
    job-name: swift-ring-rebalance
  name: swift-ring-scripts
  namespace: openstack
  ownerReferences:
  - apiVersion: swift.openstack.org/v1beta1
    blockOwnerDeletion: true
    controller: true
    kind: SwiftRing
    name: swift-ring
    uid: c2304195-2a54-4641-9556-c81c8d8b8a64
  resourceVersion: '41652'
  uid: 03b22695-8081-4eb7-b569-7406d06897f1
