|
| 1 | +#!/usr/bin/env bash |
| 2 | +# |
| 3 | +# usage: burnin-blockstor.sh WORK_DIR [DURATION_SEC] |
| 4 | +# |
| 5 | +# Continuous PVC churn against the deployed blockstor stack. Each |
| 6 | +# iteration: |
| 7 | +# - kubectl apply RD + 2 Resources (one auto-primary, one peer) |
| 8 | +# - wait for both UpToDate (≤60 s) |
| 9 | +# - 1 MiB urandom write on Primary, capture md5 |
| 10 | +# - failover: Primary → Secondary, peer → Primary |
| 11 | +# - read on peer, compare md5 (must match — exits non-zero otherwise) |
| 12 | +# - delete Resource + RD via finalizer |
| 13 | +# |
| 14 | +# Default DURATION_SEC = 86400 (24h). The PLAN's "stand running for |
| 15 | +# 24h continuous PVC churn" item points at this script. |
| 16 | +# |
| 17 | +# Reports a summary every 60 iterations: pass/fail counts + recent |
| 18 | +# convergence timings. |
| 19 | + |
| 20 | +set -euo pipefail |
| 21 | + |
| 22 | +WORK_DIR=${1:?work_dir required} |
| 23 | +DURATION=${2:-86400} |
| 24 | + |
| 25 | +export KUBECONFIG="$WORK_DIR/kubeconfig" |
| 26 | + |
| 27 | +PRIMARY=test-worker-1 |
| 28 | +PEER=test-worker-2 |
| 29 | +NS=blockstor-system |
| 30 | +SIZE_KIB=65536 # 64 MiB |
| 31 | +DEADLINE=$(( $(date +%s) + DURATION )) |
| 32 | + |
| 33 | +PASS=0 |
| 34 | +FAIL=0 |
| 35 | +ITER=0 |
| 36 | + |
| 37 | +# on_node runs a command in the satellite pod scheduled on a given node. |
| 38 | +on_node() { |
| 39 | + local node=$1 |
| 40 | + shift |
| 41 | + local pod |
| 42 | + pod=$(kubectl -n "$NS" get pods -l app=blockstor-satellite \ |
| 43 | + -o "jsonpath={.items[?(@.spec.nodeName==\"${node}\")].metadata.name}") |
| 44 | + kubectl -n "$NS" exec "$pod" -- "$@" |
| 45 | +} |
| 46 | + |
| 47 | +cleanup_iter() { |
| 48 | + local rd=$1 |
| 49 | + kubectl delete --wait=true --timeout=30s "resource.blockstor.io.blockstor.io/${rd}.${PRIMARY}" 2>/dev/null || true |
| 50 | + kubectl delete --wait=true --timeout=30s "resource.blockstor.io.blockstor.io/${rd}.${PEER}" 2>/dev/null || true |
| 51 | + kubectl delete --wait=true --timeout=30s "resourcedefinition.blockstor.io.blockstor.io/${rd}" 2>/dev/null || true |
| 52 | +} |
| 53 | + |
| 54 | +while [[ $(date +%s) -lt $DEADLINE ]]; do |
| 55 | + ITER=$((ITER + 1)) |
| 56 | + RD="burnin-${ITER}" |
| 57 | + |
| 58 | + cat <<EOF | kubectl apply -f - >/dev/null |
| 59 | +apiVersion: blockstor.io.blockstor.io/v1alpha1 |
| 60 | +kind: ResourceDefinition |
| 61 | +metadata: {name: ${RD}} |
| 62 | +spec: |
| 63 | + volumeDefinitions: |
| 64 | + - {volumeNumber: 0, sizeKib: ${SIZE_KIB}} |
| 65 | +--- |
| 66 | +apiVersion: blockstor.io.blockstor.io/v1alpha1 |
| 67 | +kind: Resource |
| 68 | +metadata: {name: ${RD}.${PRIMARY}} |
| 69 | +spec: {resourceDefinitionName: ${RD}, nodeName: ${PRIMARY}, props: {StorPoolName: stand}} |
| 70 | +--- |
| 71 | +apiVersion: blockstor.io.blockstor.io/v1alpha1 |
| 72 | +kind: Resource |
| 73 | +metadata: {name: ${RD}.${PEER}} |
| 74 | +spec: {resourceDefinitionName: ${RD}, nodeName: ${PEER}, props: {StorPoolName: stand}} |
| 75 | +EOF |
| 76 | + |
| 77 | + # Wait for UpToDate convergence — bail out fast if it doesn't |
| 78 | + # land in 60 s, that's a regression we want to capture not paper over. |
| 79 | + BOTH=0 |
| 80 | + for _ in $(seq 1 30); do |
| 81 | + p1=$(on_node "$PRIMARY" drbdsetup status "$RD" 2>/dev/null | grep "disk:" | head -1 || true) |
| 82 | + p2=$(on_node "$PEER" drbdsetup status "$RD" 2>/dev/null | grep "disk:" | head -1 || true) |
| 83 | + if [[ "$p1" == *"disk:UpToDate"* && "$p2" == *"disk:UpToDate"* ]]; then |
| 84 | + BOTH=1 |
| 85 | + break |
| 86 | + fi |
| 87 | + sleep 2 |
| 88 | + done |
| 89 | + |
| 90 | + if [[ $BOTH -ne 1 ]]; then |
| 91 | + FAIL=$((FAIL + 1)) |
| 92 | + echo "[$(date -u +%FT%TZ)] iter=$ITER FAIL: convergence timeout" |
| 93 | + cleanup_iter "$RD" |
| 94 | + continue |
| 95 | + fi |
| 96 | + |
| 97 | + DEV=$(on_node "$PRIMARY" bash -c "grep -oE '/dev/drbd[0-9]+' /etc/drbd.d/${RD}.res | head -1") |
| 98 | + PRIMARY_MD5=$(on_node "$PRIMARY" bash -c " |
| 99 | + drbdadm primary ${RD} |
| 100 | + dd if=/dev/urandom of=${DEV} bs=1M count=1 status=none oflag=direct |
| 101 | + dd if=${DEV} bs=1M count=1 status=none iflag=direct | md5sum | awk '{print \$1}' |
| 102 | + drbdadm secondary ${RD} |
| 103 | + " | tail -1) |
| 104 | + |
| 105 | + PEER_MD5=$(on_node "$PEER" bash -c " |
| 106 | + drbdadm primary ${RD} |
| 107 | + dd if=${DEV} bs=1M count=1 status=none iflag=direct | md5sum | awk '{print \$1}' |
| 108 | + drbdadm secondary ${RD} |
| 109 | + " | tail -1) |
| 110 | + |
| 111 | + if [[ "$PRIMARY_MD5" == "$PEER_MD5" ]]; then |
| 112 | + PASS=$((PASS + 1)) |
| 113 | + else |
| 114 | + FAIL=$((FAIL + 1)) |
| 115 | + echo "[$(date -u +%FT%TZ)] iter=$ITER FAIL: md5 mismatch primary=$PRIMARY_MD5 peer=$PEER_MD5" |
| 116 | + fi |
| 117 | + |
| 118 | + cleanup_iter "$RD" |
| 119 | + |
| 120 | + if (( ITER % 60 == 0 )); then |
| 121 | + echo "[$(date -u +%FT%TZ)] iter=$ITER pass=$PASS fail=$FAIL elapsed=$(( $(date +%s) - DEADLINE + DURATION ))s" |
| 122 | + fi |
| 123 | +done |
| 124 | + |
| 125 | +echo "[$(date -u +%FT%TZ)] DONE iter=$ITER pass=$PASS fail=$FAIL" |
| 126 | +[[ $FAIL -eq 0 ]] || exit 1 |
0 commit comments