diff --git a/acceptance/cmd/workspace/genie-clear-description/out.test.toml b/acceptance/cmd/workspace/genie-clear-description/out.test.toml new file mode 100644 index 00000000000..c7c6f58ed6e --- /dev/null +++ b/acceptance/cmd/workspace/genie-clear-description/out.test.toml @@ -0,0 +1 @@ +Cloud = true diff --git a/acceptance/cmd/workspace/genie-clear-description/output.txt b/acceptance/cmd/workspace/genie-clear-description/output.txt new file mode 100644 index 00000000000..18595383013 --- /dev/null +++ b/acceptance/cmd/workspace/genie-clear-description/output.txt @@ -0,0 +1,30 @@ +round 1: CONVERGED +round 2: CONVERGED +round 3: CONVERGED +round 4: CONVERGED +round 5: CONVERGED +round 6: CONVERGED +round 7: CONVERGED +round 8: CONVERGED +round 9: CONVERGED +round 10: CONVERGED +round 11: CONVERGED +round 12: CONVERGED +round 13: CONVERGED +round 14: CONVERGED +round 15: CONVERGED +round 16: CONVERGED +round 17: CONVERGED +round 18: CONVERGED +round 19: CONVERGED +round 20: CONVERGED +round 21: CONVERGED +round 22: CONVERGED +round 23: CONVERGED +round 24: CONVERGED +round 25: CONVERGED +round 26: CONVERGED +round 27: CONVERGED +round 28: CONVERGED +round 29: CONVERGED +round 30: CONVERGED diff --git a/acceptance/cmd/workspace/genie-clear-description/script b/acceptance/cmd/workspace/genie-clear-description/script new file mode 100644 index 00000000000..04c03ade6c0 --- /dev/null +++ b/acceptance/cmd/workspace/genie-clear-description/script @@ -0,0 +1,128 @@ +# Probe, not meant to be merged: clearing a Genie space description sometimes does not stick on +# cloud (flaky drift in bundle/invariant/no_drift with genie_space.yml.tmpl, ~3% of CI runs). This +# replays the requests the direct engine sends, using plain CLI commands, and polls GET afterwards. +# The poll pattern tells a stale read (old value, then cleared for good) from a lost update +# (cleared, then the old value comes back). +# +# Expected output is CONVERGED for every round; anything else prints the timeline. + +# Keep Git Bash on Windows from rewriting /Workspace/... into a C:/Program Files/Git/... path. +export MSYS_NO_PATHCONV=1 + +me=$($CLI current-user me -o json | jq -r .userName) +parent_path="/Workspace/Users/$me/genie-clear-description-$UNIQUE_NAME" +$CLI workspace mkdirs "$parent_path" + +cleanup() { + for id in $(cat LOG.space_ids 2>/dev/null); do + $CLI genie trash-space "$id" > /dev/null + done + $CLI workspace delete --recursive "$parent_path" +} +trap cleanup EXIT + +# The flake is rare, so take many samples: rounds run in parallel batches. +rounds=30 +batch=10 +if [ -n "${CLOUD_ENV:-}" ]; then + polls=15 + poll_interval=3 +else + polls=3 + poll_interval=0 +fi + +run_round() { + local round=$1 delay=$2 + local timeline="LOG.timeline.$round" title="test-genie-space-$UNIQUE_NAME-$round" + + # Same create body and missing-parent handling as ResourceGenieSpace.DoCreate. Each round gets a + # parent folder that does not exist yet, as on a bundle's first deploy: the first create fails, + # then the folder is created and the create retried. + local parent="$parent_path/r$round" body space_id + body="{ + \"warehouse_id\": \"$TEST_DEFAULT_WAREHOUSE_ID\", + \"title\": \"$title\", + \"description\": \"This is a test genie space\", + \"parent_path\": \"$parent\", + \"serialized_space\": \"{\\\"version\\\":1}\" + }" + if ! $CLI genie create-space -o json --json "$body" > "LOG.create.$round" 2> "LOG.create.err.$round"; then + $CLI workspace mkdirs "$parent" + $CLI genie create-space -o json --json "$body" > "LOG.create.$round" + fi + space_id=$(jq -r .space_id "LOG.create.$round") + rm -f "LOG.create.$round" "LOG.create.err.$round" + echo "$space_id" >> LOG.space_ids + local start=$SECONDS + + # The bundle deploy sets permissions right after creating the space, and the drift-check plan + # reads the space before the field-removal update; replay both. + $CLI permissions set genie "$space_id" --json '{"access_control_list": [{"group_name": "users", "permission_level": "CAN_READ"}]}' > /dev/null + $CLI genie get-space "$space_id" --include-serialized-space > /dev/null + + sleep "$delay" + + # Same update body as ResourceGenieSpace.DoUpdate after the description is removed from config: + # every field is sent, description is force-sent as empty, etag is omitted. + $CLI genie update-space "$space_id" -o json --json "{ + \"description\": \"\", + \"title\": \"$title\", + \"warehouse_id\": \"$TEST_DEFAULT_WAREHOUSE_ID\", + \"parent_path\": \"$parent\", + \"serialized_space\": \"{\\\"version\\\":1}\" + }" > "LOG.upd.$round" + local update_desc + update_desc=$(jq -r '.description // ""' "LOG.upd.$round") + echo "t=$((SECONDS - start)) update-response desc='$update_desc' etag=$(jq -r '.etag // ""' "LOG.upd.$round")" > "$timeline" + + # One letter per poll: C = description cleared, O = old description returned. + local pattern="" desc version etag + for i in $(seq "$polls"); do + $CLI genie get-space "$space_id" --include-serialized-space -o json > "LOG.get.$round" + desc=$(jq -r '.description // ""' "LOG.get.$round") + version=$(jq -r '.serialized_space // "{}" | fromjson | .version' "LOG.get.$round") + etag=$(jq -r '.etag // ""' "LOG.get.$round") + echo "t=$((SECONDS - start)) get desc='$desc' serialized_space.version=$version etag=$etag" >> "$timeline" + if [ -z "$desc" ]; then + pattern="${pattern}C" + else + pattern="${pattern}O" + fi + sleep "$poll_interval" + done + rm -f "LOG.get.$round" "LOG.upd.$round" + + local result + case "$pattern" in + *C*O*) result="LOST_UPDATE" ;; + *C*) [[ "$pattern" == C* ]] && result="CONVERGED" || result="STALE_READ" ;; + *) result="NOT_APPLIED" ;; + esac + if [ -n "$update_desc" ]; then + result="$result (update response still had the old description)" + fi + + { + echo "round $round: $result" + if [ "$result" != "CONVERGED" ]; then + echo " delay before update: ${delay}s, polls: $pattern" + sed 's/^/ /' "$timeline" + fi + } > "LOG.result.$round" +} + +for round in $(seq "$rounds"); do + # Spread the create->update delay; in the bundle flow plans and uploads sit in between. + delay=0 + [ -n "${CLOUD_ENV:-}" ] && delay=$(( (round - 1) % 4 * 4 )) + run_round "$round" "$delay" & + if (( round % batch == 0 )); then + wait + fi +done +wait + +for round in $(seq "$rounds"); do + cat "LOG.result.$round" +done diff --git a/acceptance/cmd/workspace/genie-clear-description/test.toml b/acceptance/cmd/workspace/genie-clear-description/test.toml new file mode 100644 index 00000000000..c7c6f58ed6e --- /dev/null +++ b/acceptance/cmd/workspace/genie-clear-description/test.toml @@ -0,0 +1 @@ +Cloud = true