#!/bin/sh
# Fake kubectl for hack/migration-59-vmi-hotplug-volumes.bats. It serves only
# the calls migration 59 and lib/cozystack-version.sh make, and logs each one
# to $FAKE_CMDLOG.
#
# The VMs live in $FAKE_STATE/vms.json and a JSON patch is applied to that
# file, so a second run of the migration reads what the first one wrote.
#
#   FAKE_CMDLOG      file to append a one-line record of each call to
#   FAKE_STATE       directory holding vms.json and vmis.json
#   FAKE_NO_KUBEVIRT non-empty => the VirtualMachine CRD is absent
#   FAKE_CRD_FAIL    non-empty => reading the VirtualMachine CRD exits 1
#   FAKE_LIST_FAIL   non-empty => listing VMs exits 1 with this stderr
#   FAKE_VMI_LIST_FAIL non-empty => listing VMIs exits 1 with this stderr
#   FAKE_PATCH_FAIL  non-empty => every patch exits 1 with this stderr
set -u

log() { [ -n "${FAKE_CMDLOG:-}" ] && printf '%s\n' "$*" >> "$FAKE_CMDLOG"; }
log "KUBECTL $*"

args="$*"
arg_after() { k="$1"; shift; p=""; for a in "$@"; do [ "$p" = "$k" ] && { printf '%s' "$a"; return; }; p="$a"; done; }

case "$args" in
  *"apply --filename -"*|*"apply -f -"*)
    manifest=$(cat)
    case "$manifest" in
      *"kind: ConfigMap"*)
        v=$(printf '%s\n' "$manifest" | sed -n 's/^  version: "\{0,1\}\([0-9]\{1,\}\)"\{0,1\}$/\1/p' | head -1)
        log "STAMP ${v:-unknown}"
        exit 0
        ;;
    esac
    log "APPLY-UNKNOWN"
    printf 'fake kubectl: unmodelled apply\n' >&2
    exit 1
    ;;

  *"get crd virtualmachines.kubevirt.io"*)
    if [ -n "${FAKE_CRD_FAIL:-}" ]; then printf '%s\n' "$FAKE_CRD_FAIL" >&2; exit 1; fi
    [ -n "${FAKE_NO_KUBEVIRT:-}" ] || echo "customresourcedefinition.apiextensions.k8s.io/virtualmachines.kubevirt.io"
    exit 0
    ;;

  *"get virtualmachines.kubevirt.io"*)
    if [ -n "${FAKE_LIST_FAIL:-}" ]; then printf '%s\n' "$FAKE_LIST_FAIL" >&2; exit 1; fi
    cat "$FAKE_STATE/vms.json" || exit 1
    exit 0
    ;;

  *"get virtualmachineinstances.kubevirt.io"*)
    if [ -n "${FAKE_VMI_LIST_FAIL:-}" ]; then printf '%s\n' "$FAKE_VMI_LIST_FAIL" >&2; exit 1; fi
    cat "$FAKE_STATE/vmis.json" || exit 1
    exit 0
    ;;

  *"patch virtualmachines.kubevirt.io"*)
    if [ -n "${FAKE_PATCH_FAIL:-}" ]; then printf '%s\n' "$FAKE_PATCH_FAIL" >&2; exit 1; fi
    ns=$(arg_after -n "$@")
    name=$(arg_after virtualmachines.kubevirt.io "$@")
    patch=$(arg_after -p "$@")
    # Refuse a stale resourceVersion the way the API server does, then apply
    # every add op and bump the resourceVersion.
    rv=$(jq -r --arg ns "$ns" --arg n "$name" \
      '.items[] | select(.metadata.namespace == $ns and .metadata.name == $n) | .metadata.resourceVersion' \
      "$FAKE_STATE/vms.json")
    want=$(printf '%s' "$patch" | jq -r '.[] | select(.op == "test" and .path == "/metadata/resourceVersion") | .value')
    if [ -z "$want" ] || [ "$want" != "$rv" ]; then
      printf 'Error from server (Conflict): test failed\n' >&2
      exit 1
    fi
    jq --arg ns "$ns" --arg n "$name" --argjson p "$patch" '
      .items |= map(
        if .metadata.namespace == $ns and .metadata.name == $n then
          reduce ($p[] | select(.op == "add")) as $op (.;
            setpath($op.path | ltrimstr("/") | split("/"); $op.value))
          | .metadata.resourceVersion = ((.metadata.resourceVersion | tonumber) + 1 | tostring)
        else . end)' "$FAKE_STATE/vms.json" > "$FAKE_STATE/vms.json.new"
    mv "$FAKE_STATE/vms.json.new" "$FAKE_STATE/vms.json"
    vols=$(jq -r --arg ns "$ns" --arg n "$name" \
      '.items[] | select(.metadata.namespace == $ns and .metadata.name == $n) | [.spec.template.spec.volumes[].name] | join(",")' \
      "$FAKE_STATE/vms.json")
    disks=$(jq -r --arg ns "$ns" --arg n "$name" \
      '.items[] | select(.metadata.namespace == $ns and .metadata.name == $n) | [.spec.template.spec.domain.devices.disks[].name] | join(",")' \
      "$FAKE_STATE/vms.json")
    log "PATCH-VM $ns/$name volumes=$vols disks=$disks"
    exit 0
    ;;
esac

log "UNHANDLED $args"
printf 'fake kubectl: unmodelled call: %s\n' "$args" >&2
exit 1
