#!/usr/bin/env bash set -euo pipefail # Read-only, secret-free evidence collector for the rails hosted by this reef. # The cluster API is intentionally reached through the operator SSH path; 6443 # remains non-public. Override only when the approved executor name changes. remote_host="${RAIL_RUNTIME_HOST:-railiance01}" ssh -o BatchMode=yes "$remote_host" 'bash -s' <<'REMOTE' set -euo pipefail context="$(kubectl config current-context)" server_version="$(kubectl version -o json | jq -r '.serverVersion.gitVersion')" nodes="$(kubectl get nodes -o json)" knative_deployments="$(kubectl -n knative-serving get deployments -o json)" kourier_service="$(kubectl -n kourier-system get service kourier -o json)" knative_services="$(kubectl get ksvc -A -o json)" ingress_classes="$(kubectl get ingressclass -o json)" storage_classes="$(kubectl get storageclass -o json)" network_policy_count="$(kubectl get networkpolicy -A -o json | jq '.items | length')" jq -n \ --arg context "$context" \ --arg server_version "$server_version" \ --argjson nodes "$nodes" \ --argjson deployments "$knative_deployments" \ --argjson kourier "$kourier_service" \ --argjson services "$knative_services" \ --argjson ingress_classes "$ingress_classes" \ --argjson storage_classes "$storage_classes" \ --argjson network_policy_count "$network_policy_count" \ '{ schema_version: "reef-railiance.rail-runtime-evidence/v1", collected_at: (now | todateiso8601), collector: "reef-railiance/tools/collect_rail_runtime_evidence.sh", executor: "operator-ssh", context: $context, kubernetes: { server_version: $server_version, nodes: { total: ($nodes.items | length), ready: ([$nodes.items[] | select(any(.status.conditions[]; .type == "Ready" and .status == "True"))] | length), members: [$nodes.items[] | { name: .metadata.name, roles: ([.metadata.labels | keys[] | select(startswith("node-role.kubernetes.io/")) | split("/")[1]] | sort), kubelet_version: .status.nodeInfo.kubeletVersion, allocatable: { cpu: .status.allocatable.cpu, memory: .status.allocatable.memory, pods: .status.allocatable.pods } }] }, ingress_classes: [$ingress_classes.items[].metadata.name] | sort, storage_classes: [$storage_classes.items[].metadata.name] | sort, network_policy_count: $network_policy_count }, knative: { deployments: [$deployments.items[] | { name: .metadata.name, desired: (.spec.replicas // 0), available: (.status.availableReplicas // 0), images: [.spec.template.spec.containers[].image] }] | sort_by(.name), all_deployments_available: (all($deployments.items[]; (.status.availableReplicas // 0) >= (.spec.replicas // 0))), ingress: { implementation: "net-kourier", service_type: $kourier.spec.type, cluster_ip: $kourier.spec.clusterIP }, services: [$services.items[] | { namespace: .metadata.namespace, name: .metadata.name, latest_created_revision: .status.latestCreatedRevisionName, latest_ready_revision: .status.latestReadyRevisionName, ready: (any(.status.conditions[]?; .type == "Ready" and .status == "True")) }] | sort_by(.namespace, .name) }, failure_domain: { independent_members_observed: ($nodes.items | length), result: (if ($nodes.items | length) > 1 then "requires-source-backed-review" else "fail-single-node" end) }, secret_values_collected: false }' REMOTE