#!/bin/bash
#
# ACM-27290 Full Verification Script
#
# Bug:  RHCOS 4.19+ ships /etc/containers/policy.json with signedBy entries
#       for registry.redhat.io and registry.access.redhat.com under the
#       docker transport. In disconnected environments the sigstores are
#       unreachable, causing "Source image rejected: A signature was required".
#
# Fix:  The assisted-service (PR #8609) injects insecureAcceptAnything for
#       those registries when BOTH conditions are met:
#         1. A mirror registry is configured (mirrorRegistryRef)
#         2. The annotation unsupported.agent-install.openshift.io/
#            assisted-service-allow-unrestricted-image-pulls: "true"
#            is set on AgentServiceConfig
#
# Prerequisites:
#   - Hub cluster deployed with ACM/MCE
#   - Spoke cluster deployed via assisted-service using OCP >= 4.19
#   - Disconnected environment with mirror registry
#
# Usage:
#   bash /home/kni/verify-acm27290-full.sh [spoke-namespace]
#   Default spoke namespace: spoke-0
#
#   Output: /home/kni/acm27290-verification-<timestamp>.log

set -o pipefail

export KUBECONFIG=/home/kni/clusterconfigs/auth/kubeconfig
NAMESPACE="${1:-spoke-0}"
CLUSTER="${NAMESPACE}"
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
LOGFILE="/home/kni/acm27290-verification-${TIMESTAMP}.log"
SPOKE_KUBECONFIG="/tmp/spoke-${NAMESPACE}-kubeconfig-$$"

PASS=0
FAIL=0
WARN=0
SKIP=0

trap "rm -f '$SPOKE_KUBECONFIG'" EXIT

# --- Output helpers ---
tee_log() { tee -a "$LOGFILE"; }
log()  { echo "[$(date '+%H:%M:%S')] $*" | tee_log; }
pass() { PASS=$((PASS+1)); echo "[$(date '+%H:%M:%S')] [PASS] $*" | tee_log; }
fail() { FAIL=$((FAIL+1)); echo "[$(date '+%H:%M:%S')] [FAIL] $*" | tee_log; }
warn() { WARN=$((WARN+1)); echo "[$(date '+%H:%M:%S')] [WARN] $*" | tee_log; }
skip() { SKIP=$((SKIP+1)); echo "[$(date '+%H:%M:%S')] [SKIP] $*" | tee_log; }
hr()   { echo "======================================================================" | tee_log; }
section() { echo "" | tee_log; hr; log "$*"; hr; echo "" | tee_log; }

# ======================================================================
section "ACM-27290 VERIFICATION — $(date)"
# ======================================================================
cat <<INFO | tee_log
Jira:      https://issues.redhat.com/browse/ACM-27290
Bug:       RHCOS 4.19+ enforces signedBy for registry.redhat.io and
           registry.access.redhat.com in /etc/containers/policy.json.
           In disconnected environments the sigstore is unreachable,
           so image pulls fail with "Source image rejected".
Fix:       assisted-service (PR #8609) injects insecureAcceptAnything
           for those registries under the docker transport when mirror
           registry + allow-unrestricted-image-pulls annotation are set.
Spoke:     ${NAMESPACE}
Log:       ${LOGFILE}
INFO

# ======================================================================
section "TEST 1: Hub environment baseline"
# ======================================================================

HUB_OCP=$(oc get clusterversion -o jsonpath='{.items[0].status.desired.version}' 2>/dev/null)
ACM_VER=$(oc get multiclusterhub -A -o jsonpath='{.items[0].status.currentVersion}' 2>/dev/null)
MCE_VER=$(oc get multiclusterengine -o jsonpath='{.items[0].status.currentVersion}' 2>/dev/null)
AI_IMAGE=$(oc get deployment assisted-service -n multicluster-engine \
    -o jsonpath='{.spec.template.spec.containers[0].image}' 2>/dev/null)

log "Hub OCP version:        $HUB_OCP"
log "ACM version:            $ACM_VER"
log "MCE version:            $MCE_VER"
log "Assisted-service image: $AI_IMAGE"

if [ -n "$HUB_OCP" ] && [ -n "$ACM_VER" ]; then
    pass "Hub cluster is running OCP $HUB_OCP with ACM $ACM_VER / MCE $MCE_VER"
else
    fail "Could not determine hub cluster version"
fi

# ======================================================================
section "TEST 2: Disconnected environment prerequisites"
# ======================================================================

# 2a. AgentServiceConfig annotation
log "2a. Checking AgentServiceConfig annotation..."
ANNOTATION=$(oc get agentserviceconfig agent \
    -o jsonpath='{.metadata.annotations.unsupported\.agent-install\.openshift\.io/assisted-service-allow-unrestricted-image-pulls}' \
    2>/dev/null)

if [ "$ANNOTATION" = "true" ]; then
    pass "AgentServiceConfig has allow-unrestricted-image-pulls=true annotation"
else
    fail "AgentServiceConfig MISSING allow-unrestricted-image-pulls annotation (value: '$ANNOTATION')"
    log "  The fix requires this annotation. Without it, the permissive policy.json"
    log "  will NOT be injected into the discovery ignition (see Omer's comment on ACM-27290)."
fi

# 2b. Mirror registry reference
log "2b. Checking mirror registry configuration..."
MIRROR_REF=$(oc get agentserviceconfig agent \
    -o jsonpath='{.spec.mirrorRegistryRef.name}' 2>/dev/null)

if [ -n "$MIRROR_REF" ]; then
    pass "AgentServiceConfig has mirrorRegistryRef: $MIRROR_REF"
else
    # Check InfraEnv for mirror ref
    INFRAENV_MIRROR=$(oc get infraenv "$CLUSTER" -n "$NAMESPACE" \
        -o jsonpath='{.spec.mirrorRegistryRef.name}' 2>/dev/null)
    if [ -n "$INFRAENV_MIRROR" ]; then
        pass "InfraEnv has mirrorRegistryRef: $INFRAENV_MIRROR"
    else
        fail "No mirrorRegistryRef found on AgentServiceConfig or InfraEnv"
        log "  The fix requires a mirror registry reference to trigger the policy override."
    fi
fi

# 2c. ImageDigestMirrorSets / ImageContentSourcePolicy
log "2c. Checking image mirroring configuration on hub..."
IDMS_COUNT=$(oc get imagedigestmirrorset -A --no-headers 2>/dev/null | wc -l)
ICSP_COUNT=$(oc get imagecontentsourcepolicy --no-headers 2>/dev/null | wc -l)
log "  ImageDigestMirrorSets: $IDMS_COUNT"
log "  ImageContentSourcePolicies: $ICSP_COUNT"

if [ "$IDMS_COUNT" -gt 0 ] || [ "$ICSP_COUNT" -gt 0 ]; then
    pass "Image mirroring is configured ($IDMS_COUNT IDMS, $ICSP_COUNT ICSP)"
else
    warn "No IDMS or ICSP found — environment may not be fully disconnected"
fi

# 2d. External registry unreachable (confirms disconnected)
log "2d. Verifying registry.redhat.io is NOT reachable (disconnected)..."
FIRST_NODE=$(oc get nodes -o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
REG_CHECK=$(oc debug node/"$FIRST_NODE" --quiet \
    -- chroot /host curl -sI --connect-timeout 5 https://registry.redhat.io/v2/ 2>&1)
REG_RC=$?

if echo "$REG_CHECK" | grep -q "HTTP/" 2>/dev/null; then
    warn "registry.redhat.io IS reachable — this may NOT be a disconnected environment"
else
    pass "registry.redhat.io is NOT reachable — confirmed disconnected environment"
fi

# ======================================================================
section "TEST 3: assisted-service applied the fix (ForceInsecurePolicy)"
# ======================================================================

log "Searching assisted-service logs for ForceInsecurePolicy..."
FORCE_LINES=$(oc logs deployment/assisted-service -n multicluster-engine --tail=50000 2>&1 \
    | grep "ForceInsecurePolicy=true")
FORCE_COUNT=$(echo "$FORCE_LINES" | grep -c "ForceInsecurePolicy=true")

if [ "$FORCE_COUNT" -gt 0 ]; then
    pass "assisted-service applied ForceInsecurePolicy=true ($FORCE_COUNT log entries)"
    log "  Sample log entry:"
    echo "$FORCE_LINES" | head -1 | tee_log
else
    fail "No ForceInsecurePolicy=true log entries found in assisted-service"
    log "  The fix was NOT applied during discovery ignition generation."
fi

# ======================================================================
section "TEST 4: No signature rejection errors during installation"
# ======================================================================

SIG_ERRORS=$(oc logs deployment/assisted-service -n multicluster-engine --tail=50000 2>&1 \
    | grep -ic 'Source image rejected\|signature was required')

if [ "$SIG_ERRORS" -eq 0 ]; then
    pass "Zero signature rejection errors in assisted-service logs"
else
    fail "$SIG_ERRORS signature rejection errors found in assisted-service logs"
    log "  Error samples:"
    oc logs deployment/assisted-service -n multicluster-engine --tail=50000 2>&1 \
        | grep -i 'Source image rejected\|signature was required' | head -5 | tee_log
fi

# ======================================================================
section "TEST 5: Spoke cluster installation completed"
# ======================================================================

COMPLETED=$(oc get agentclusterinstall "$CLUSTER" -n "$NAMESPACE" \
    -o jsonpath='{.status.conditions[?(@.type=="Completed")].status}' 2>/dev/null)
FAILED=$(oc get agentclusterinstall "$CLUSTER" -n "$NAMESPACE" \
    -o jsonpath='{.status.conditions[?(@.type=="Failed")].status}' 2>/dev/null)
ACI_STATE=$(oc get agentclusterinstall "$CLUSTER" -n "$NAMESPACE" \
    -o jsonpath='{.status.debugInfo.state}' 2>/dev/null)

log "ACI state: $ACI_STATE"
log "Completed: $COMPLETED  |  Failed: $FAILED"

if [ "$COMPLETED" = "True" ] && [ "$FAILED" = "False" ]; then
    pass "Spoke cluster installation completed successfully (state: $ACI_STATE)"
else
    fail "Spoke cluster installation did not complete (Completed=$COMPLETED, Failed=$FAILED, State=$ACI_STATE)"
fi

log "Agent status:"
oc get agents -n "$NAMESPACE" \
    -o custom-columns='HOSTNAME:.spec.hostname,ROLE:.spec.role,STATUS:.status.debugInfo.state,STAGE:.status.progress.currentStage' \
    2>&1 | tee_log

# ======================================================================
section "TEST 6: Spoke cluster accessible and healthy"
# ======================================================================

log "Extracting spoke kubeconfig..."
oc get secret "${CLUSTER}-admin-kubeconfig" -n "$NAMESPACE" \
    -o jsonpath='{.data.kubeconfig}' 2>/dev/null | base64 -d > "$SPOKE_KUBECONFIG" 2>/dev/null

if [ ! -s "$SPOKE_KUBECONFIG" ]; then
    fail "Could not extract spoke kubeconfig — skipping all node-level checks"
    section "TESTS 7-11: SKIPPED (no spoke kubeconfig)"
    skip "Node-level tests skipped"
else
    SPOKE_VERSION=$(KUBECONFIG="$SPOKE_KUBECONFIG" oc get clusterversion \
        -o jsonpath='{.items[0].status.desired.version}' 2>/dev/null)
    SPOKE_NODES=$(KUBECONFIG="$SPOKE_KUBECONFIG" oc get nodes -o jsonpath='{.items[*].metadata.name}' 2>/dev/null)
    SPOKE_RHCOS=$(KUBECONFIG="$SPOKE_KUBECONFIG" oc get nodes -o jsonpath='{.items[0].status.nodeInfo.osImage}' 2>/dev/null)

    log "Spoke OCP version: $SPOKE_VERSION"
    log "Spoke RHCOS:       $SPOKE_RHCOS"
    log "Spoke nodes:       $SPOKE_NODES"

    # Check that spoke is 4.19+
    SPOKE_MINOR=$(echo "$SPOKE_VERSION" | cut -d. -f2)
    if [ "$SPOKE_MINOR" -ge 19 ] 2>/dev/null; then
        pass "Spoke is OCP 4.$SPOKE_MINOR — affected by the bug (4.19+)"
    else
        warn "Spoke is OCP 4.$SPOKE_MINOR — NOT affected by the bug (< 4.19)"
        log "  ACM-27290 only affects OCP 4.19+. This test may not be conclusive."
    fi

    KUBECONFIG="$SPOKE_KUBECONFIG" oc get nodes -o wide 2>&1 | tee_log

# ======================================================================
section "TEST 7: /etc/containers/policy.json on spoke nodes (CORE CHECK)"
# ======================================================================

    cat <<'EXPLAIN' | tee_log
This is the core verification for ACM-27290.

RHCOS 4.19+ ostree base ships:
  "docker": {
    "registry.redhat.io":        [{"type": "signedBy", ...}]
    "registry.access.redhat.com": [{"type": "signedBy", ...}]
  }

The fix must override this so the installed nodes have either:
  a) No signedBy entries for those registries (removed), OR
  b) Explicit insecureAcceptAnything entries for those registries

EXPLAIN

    POLICY_ALL_PASS=true

    for NODE in $SPOKE_NODES; do
        log "--- Node: $NODE ---"

        # Get the ostree base (original, buggy) policy.json
        OSTREE_POLICY=$(KUBECONFIG="$SPOKE_KUBECONFIG" oc debug node/"$NODE" --quiet \
            -- chroot /host bash -c '
COMMIT=$(rpm-ostree status -b --json 2>/dev/null | python3 -c "import json,sys; print(json.load(sys.stdin)[\"deployments\"][0][\"checksum\"])" 2>/dev/null)
ostree --repo=/ostree/repo cat $COMMIT /usr/etc/containers/policy.json 2>/dev/null
' 2>/dev/null)

        # Get the active (fixed) policy.json
        ACTIVE_POLICY=$(KUBECONFIG="$SPOKE_KUBECONFIG" oc debug node/"$NODE" --quiet \
            -- chroot /host cat /etc/containers/policy.json 2>/dev/null)

        if [ -z "$ACTIVE_POLICY" ]; then
            fail "Node $NODE: could not read /etc/containers/policy.json"
            POLICY_ALL_PASS=false
            continue
        fi

        # Show both for comparison
        log "Ostree base (original RHCOS default):"
        echo "$OSTREE_POLICY" | python3 -m json.tool 2>/dev/null | tee_log
        echo "" | tee_log

        log "Active on node (after fix):"
        echo "$ACTIVE_POLICY" | python3 -m json.tool 2>/dev/null | tee_log
        echo "" | tee_log

        # Verify the ostree base HAD the signedBy entries (confirms the bug exists)
        OSTREE_HAS_SIGNED=$(echo "$OSTREE_POLICY" | python3 -c "
import json, sys
try:
    p = json.load(sys.stdin)
    docker = p.get('transports', {}).get('docker', {})
    rhi = any(e.get('type') == 'signedBy' for e in docker.get('registry.redhat.io', []))
    rac = any(e.get('type') == 'signedBy' for e in docker.get('registry.access.redhat.com', []))
    print('YES' if (rhi or rac) else 'NO')
except:
    print('ERROR')
" 2>/dev/null)

        if [ "$OSTREE_HAS_SIGNED" = "YES" ]; then
            pass "Node $NODE: ostree base CONFIRMS signedBy entries exist (bug condition present)"
        elif [ "$OSTREE_HAS_SIGNED" = "NO" ]; then
            warn "Node $NODE: ostree base does NOT have signedBy entries (bug may not apply to this RHCOS)"
        else
            warn "Node $NODE: could not parse ostree base policy.json"
        fi

        # Verify the active policy.json does NOT have signedBy
        RESULT=$(echo "$ACTIVE_POLICY" | python3 -c "
import json, sys

p = json.load(sys.stdin)
docker = p.get('transports', {}).get('docker', {})

checks = {
    'registry.redhat.io': docker.get('registry.redhat.io', []),
    'registry.access.redhat.com': docker.get('registry.access.redhat.com', [])
}

all_ok = True
for reg, policies in checks.items():
    has_insecure = any(e.get('type') == 'insecureAcceptAnything' for e in policies)
    has_signed   = any(e.get('type') == 'signedBy' for e in policies)

    if has_insecure and not has_signed:
        print(f'  {reg}: insecureAcceptAnything [OK]')
    elif has_signed and not has_insecure:
        print(f'  {reg}: signedBy ONLY — BUG STILL PRESENT [FAIL]')
        all_ok = False
    elif has_signed and has_insecure:
        print(f'  {reg}: has BOTH insecureAcceptAnything and signedBy [WARN]')
    elif not policies:
        default = p.get('default', [])
        def_insecure = any(e.get('type') == 'insecureAcceptAnything' for e in default)
        if def_insecure:
            print(f'  {reg}: no docker entry, global default=insecureAcceptAnything [OK]')
        else:
            print(f'  {reg}: no docker entry, global default NOT insecureAcceptAnything [FAIL]')
            all_ok = False
    else:
        print(f'  {reg}: unexpected: {policies} [UNKNOWN]')
        all_ok = False

default_type = [e.get('type') for e in p.get('default', [])]
print(f'  global default: {default_type}')

sys.exit(0 if all_ok else 1)
" 2>&1)

        POLICY_RC=$?
        echo "$RESULT" | tee_log

        if [ "$POLICY_RC" -eq 0 ]; then
            pass "Node $NODE: active policy.json is correctly configured (no restrictive signedBy)"
        else
            fail "Node $NODE: active policy.json STILL has restrictive signedBy — ACM-27290 NOT fixed"
            POLICY_ALL_PASS=false
        fi

        # Confirm file was modified from RPM original
        RPM_VERIFY=$(KUBECONFIG="$SPOKE_KUBECONFIG" oc debug node/"$NODE" --quiet \
            -- chroot /host rpm -V containers-common 2>/dev/null | grep policy.json)
        if echo "$RPM_VERIFY" | grep -q "S.5" 2>/dev/null; then
            log "  rpm -V confirms policy.json was modified from RPM default (Size+MD5 changed)"
        fi
        echo "" | tee_log
    done

    if [ "$POLICY_ALL_PASS" = true ]; then
        pass "TEST 7 OVERALL: All spoke nodes have correct policy.json"
    else
        fail "TEST 7 OVERALL: Some spoke nodes have incorrect policy.json"
    fi

# ======================================================================
section "TEST 8: MachineConfig contains the fixed policy.json"
# ======================================================================

    log "Checking MachineConfigs on spoke cluster for policy.json..."

    MC_FOUND=false
    for MC_NAME in 01-master-container-runtime 01-worker-container-runtime; do
        MC_POLICY=$(KUBECONFIG="$SPOKE_KUBECONFIG" oc get machineconfig "$MC_NAME" -o json 2>/dev/null \
            | python3 -c "
import json, sys, base64
from urllib.parse import unquote
data = json.load(sys.stdin)
storage = data.get('spec', {}).get('config', {}).get('storage', {})
for f in storage.get('files', []):
    if 'policy.json' in f.get('path', ''):
        contents = f.get('contents', {}).get('source', '')
        if contents.startswith('data:,'):
            print(unquote(contents[6:]))
        elif ',' in contents:
            raw = contents.split(',', 1)[1]
            try:
                print(base64.b64decode(raw).decode())
            except:
                print(raw)
" 2>/dev/null)

        if [ -n "$MC_POLICY" ]; then
            MC_FOUND=true
            log "MachineConfig $MC_NAME policy.json:"
            echo "$MC_POLICY" | python3 -m json.tool 2>/dev/null | tee_log

            HAS_SIGNED=$(echo "$MC_POLICY" | python3 -c "
import json, sys
p = json.load(sys.stdin)
docker = p.get('transports', {}).get('docker', {})
signed = any(
    any(e.get('type') == 'signedBy' for e in entries)
    for entries in docker.values()
)
print('YES' if signed else 'NO')
" 2>/dev/null)

            if [ "$HAS_SIGNED" = "NO" ]; then
                pass "MachineConfig $MC_NAME: policy.json has no signedBy entries"
            else
                fail "MachineConfig $MC_NAME: policy.json still contains signedBy entries"
            fi
        fi
    done

    if [ "$MC_FOUND" = false ]; then
        warn "No MachineConfig with policy.json found — fix may use a different mechanism"
    fi

# ======================================================================
section "TEST 9: CRI-O logs on spoke nodes (no signature errors)"
# ======================================================================

    for NODE in $SPOKE_NODES; do
        SIG_COUNT=$(KUBECONFIG="$SPOKE_KUBECONFIG" oc debug node/"$NODE" --quiet \
            -- chroot /host journalctl -u crio --no-pager -q 2>/dev/null \
            | grep -ic 'Source image rejected\|signature was required')

        if [ "$SIG_COUNT" -eq 0 ]; then
            pass "Node $NODE: no signature rejection errors in CRI-O"
        else
            fail "Node $NODE: $SIG_COUNT signature rejection errors in CRI-O"
            KUBECONFIG="$SPOKE_KUBECONFIG" oc debug node/"$NODE" --quiet \
                -- chroot /host journalctl -u crio --no-pager -q 2>/dev/null \
                | grep -i 'Source image rejected\|signature was required' | tail -5 | tee_log
        fi
    done

# ======================================================================
section "TEST 10: No ImagePullBackOff / ErrImagePull on spoke"
# ======================================================================

    BAD_PODS=$(KUBECONFIG="$SPOKE_KUBECONFIG" oc get pods -A 2>/dev/null \
        | grep -i 'ImagePullBackOff\|ErrImagePull')

    if [ -z "$BAD_PODS" ]; then
        pass "No ImagePullBackOff or ErrImagePull pods on spoke"
    else
        # Filter out known disconnected-expected failures (marketplace)
        MARKETPLACE_ONLY=true
        NON_MKT=$(echo "$BAD_PODS" | grep -v 'openshift-marketplace')
        if [ -n "$NON_MKT" ]; then
            MARKETPLACE_ONLY=false
        fi

        if [ "$MARKETPLACE_ONLY" = true ]; then
            warn "ImagePullBackOff pods only in openshift-marketplace (expected in disconnected)"
            echo "$BAD_PODS" | tee_log
            log "  These are catalog source pods that try to pull from registry.redhat.io"
            log "  directly. This is expected in disconnected environments and is NOT"
            log "  related to ACM-27290 (it's a missing CatalogSource mirror configuration)."
        else
            fail "ImagePullBackOff pods found outside openshift-marketplace:"
            echo "$NON_MKT" | tee_log
        fi
    fi

# ======================================================================
section "TEST 11: Spoke ClusterOperators health"
# ======================================================================

    KUBECONFIG="$SPOKE_KUBECONFIG" oc get co 2>&1 | tee_log
    echo "" | tee_log

    DEGRADED_CO=$(KUBECONFIG="$SPOKE_KUBECONFIG" oc get co 2>/dev/null \
        | awk 'NR>1 && ($3!="True" || $4!="False" || $5!="False") {print $0}')

    if [ -z "$DEGRADED_CO" ]; then
        pass "All spoke ClusterOperators are healthy"
    else
        # Check if only marketplace is degraded (expected in disconnected)
        MKT_ONLY=$(echo "$DEGRADED_CO" | grep -v marketplace)
        if [ -z "$MKT_ONLY" ]; then
            warn "Only marketplace ClusterOperator is not fully healthy (expected in disconnected)"
        else
            warn "Some spoke ClusterOperators are not fully healthy:"
            echo "$DEGRADED_CO" | tee_log
        fi
    fi

fi  # end of spoke kubeconfig block

# ======================================================================
section "FINAL SUMMARY"
# ======================================================================

cat <<SUMMARY | tee_log

Jira:  ACM-27290
Date:  $(date)

Environment:
  Hub OCP:         ${HUB_OCP:-unknown}
  Spoke OCP:       ${SPOKE_VERSION:-unknown}
  Spoke RHCOS:     ${SPOKE_RHCOS:-unknown}
  ACM:             ${ACM_VER:-unknown}
  MCE:             ${MCE_VER:-unknown}
  Disconnected:    Yes
  Mirror Registry: ${MIRROR_REF:-${INFRAENV_MIRROR:-unknown}}
  Unrestricted annotation: ${ANNOTATION:-not set}

Preconditions for the fix:
  1. mirrorRegistryRef configured:                           $([ -n "$MIRROR_REF" ] || [ -n "$INFRAENV_MIRROR" ] && echo 'YES' || echo 'NO')
  2. allow-unrestricted-image-pulls annotation set to true:  $([ "$ANNOTATION" = "true" ] && echo 'YES' || echo 'NO')

Results:
  PASS: ${PASS}
  FAIL: ${FAIL}
  WARN: ${WARN}
  SKIP: ${SKIP}

SUMMARY

if [ "$FAIL" -eq 0 ]; then
    cat <<VERDICT | tee_log
VERDICT: PASS — ACM-27290 fix is verified

The assisted-service correctly overrode the restrictive RHCOS 4.19+
policy.json on all spoke nodes. The signedBy entries for
registry.redhat.io and registry.access.redhat.com under the docker
transport have been replaced, allowing image pulls from mirror
registries to succeed without signature verification in the
disconnected environment.

No signature rejection errors occurred during installation or at
runtime (CRI-O logs). The spoke cluster is fully operational.
VERDICT
else
    cat <<VERDICT | tee_log
VERDICT: FAIL — ACM-27290 fix verification failed

Review the [FAIL] entries above for details.

Common causes:
  - Missing annotation: unsupported.agent-install.openshift.io/
    assisted-service-allow-unrestricted-image-pulls must be "true"
  - Missing mirrorRegistryRef on AgentServiceConfig or InfraEnv
  - Spoke OCP version < 4.19 (bug does not apply)
  - assisted-service image does not include PR #8609
VERDICT
fi

hr
log "Full log saved to: $LOGFILE"
hr
