diff --git a/bash/daml-export b/bash/daml-export index 2e9e458..8c13124 100755 --- a/bash/daml-export +++ b/bash/daml-export @@ -93,11 +93,24 @@ function exportRange { [ -n "$DAML_TOKEN" ] && args+=(--access-token-file "$DAML_TOKEN") java "${args[@]}" local export_exit=$? - if [ $export_exit -eq 0 ]; then - touch "$dir/export.good" - else + if [ $export_exit -ne 0 ]; then + # SUR-3644: surface the Java export failure to the caller so the main + # loop skips correction/build instead of reporting false success. + echo "REDO $dir" + return "$export_exit" + fi + # SUR-3644: Java reported success, but the build step depends on the + # exported sources existing. Treat missing artifacts as a failed export + # rather than touching export.good and proceeding on partial output. + local missing=() + [ -r "$dir/daml.yaml" ] || missing+=("daml.yaml") + [ -r "$dir/Export.daml" ] || missing+=("Export.daml") + if [ "${#missing[@]}" -gt 0 ]; then echo "REDO $dir" + log::error "daml-export: export of $dir is missing required files: ${missing[*]}" + return 1 fi + touch "$dir/export.good" sleep 1 else echo "Skipping complete export from $start to $stop" @@ -183,6 +196,9 @@ CUR_INT=$(hex_to_dec "$START_OFFSET") STOP_INT=$(hex_to_dec "$STOP_OFFSET") count=0 +# SUR-3644: track whether any range failed to export so the script can exit +# non-zero overall instead of masking the failure behind a clean build wait. +EXPORT_FAILED=false RUNNING_PROCS=() RUNNING_OUTPUT_DIRS=() # TO_BUILD removed (SUR-1871): the previous queue accumulated every @@ -277,12 +293,23 @@ while [ "$CUR_INT" -le "$STOP_INT" ]; do verifyExport "$OUTPUT_DIR" if [ $? -ge 2 ]; then - exportRange "$CUR_HEX" "$NEXT_HEX" "$OUTPUT_DIR" "$BASE_DIR" + # SUR-3644: gate correction/build on a successful export. A failed export + # (Java error or missing artifacts) must not be corrected/built. + if exportRange "$CUR_HEX" "$NEXT_HEX" "$OUTPUT_DIR" "$BASE_DIR"; then + export_ok=true + else + export_ok=false + fi + else + export_ok=true fi refresh_running_procs - if [ "${#RUNNING_PROCS[@]}" -lt "$MAX_PARALLEL" ]; then + if [ "$export_ok" != true ]; then + log::error "daml-export: skipping correction/build for $OUTPUT_DIR after failed export" + EXPORT_FAILED=true + elif [ "${#RUNNING_PROCS[@]}" -lt "$MAX_PARALLEL" ]; then correct_export "$OUTPUT_DIR" build "$OUTPUT_DIR" & RUNNING_PROCS+=($!) @@ -335,6 +362,12 @@ done < <(find "$TARGET_DIR" -name export.good -print | sort) wait_for_running_procs WAIT_STATUS=$? NORMAL_COMPLETION=true +# SUR-3644: a failed export earlier in the run must not be masked by a clean +# build wait — fail loudly so CI/automation does not proceed on missing or +# partial artifacts. +if [ "$EXPORT_FAILED" = true ]; then + exit 1 +fi if [ "$WAIT_STATUS" -ne 0 ]; then exit "$WAIT_STATUS" fi diff --git a/bash/minikube-test-environment b/bash/minikube-test-environment index 9a20a11..fa764cf 100755 --- a/bash/minikube-test-environment +++ b/bash/minikube-test-environment @@ -59,6 +59,7 @@ function start_or_create_minikube { --kubernetes-version "$KUBERNETES_VERSION" \ --driver="$MINIKUBE_DRIVER" \ --force-systemd=true \ + "--cni=$CNI" \ "--nodes=$NODES" \ "--memory=$NODE_MEMORY" fi @@ -68,6 +69,7 @@ function start_or_create_minikube { --kubernetes-version "$KUBERNETES_VERSION" \ --driver="$MINIKUBE_DRIVER" \ --force-systemd=true \ + "--cni=$CNI" \ "--nodes=$NODES" \ "--memory=$NODE_MEMORY" fi diff --git a/bash/replace-validator b/bash/replace-validator index a74b2aa..aea4510 100755 --- a/bash/replace-validator +++ b/bash/replace-validator @@ -119,6 +119,26 @@ function waitForPodTermination() { done } +# Pre-flight check that every requested node has a pod in the populated +# map. Emits a clear error naming the missing node and selector and returns +# non-zero so `main` can bail out BEFORE any key copy or label mutation, +# rather than aborting on an opaque `nounset` error when dereferencing a +# missing associative-array element. The map is passed by nameref. (SUR-3649) +function validateNodesMapped() { + local -n _map=${1:?} + local label=${2:?} + shift 2 + local node + local rc=0 + for node in "$@"; do + if [ -z "${_map[$node]+set}" ]; then + log::error "replace-validator: no pod found for node '$node' with selector '$label'" + rc=1 + fi + done + return "$rc" +} + function main() { set -euo pipefail local -A node2pod=() @@ -128,6 +148,9 @@ function main() { local -A pod2node=() # shellcheck disable=SC2153 mapPods "$LABEL" node2pod pod2node + # SUR-3649: fail cleanly before any destructive action when a requested + # node has no matching pod, instead of crashing on nounset below. + validateNodesMapped node2pod "$LABEL" "$FROM_NODE" "$TRGT_NODE" local from_pod=${node2pod[$FROM_NODE]} local to_pod=${node2pod[$TRGT_NODE]} diff --git a/tests/daml-export.bats b/tests/daml-export.bats index d3eb989..21571a9 100644 --- a/tests/daml-export.bats +++ b/tests/daml-export.bats @@ -249,6 +249,44 @@ EOF rm -f "$log_file" } +@test "daml-export exits non-zero and skips correction/build when java export fails (SUR-3644)" { + tmp=$(mktemp -d) + stub_bin=$(mktemp -d) + log_file=$(mktemp) + write_daml_stub "$stub_bin" + printf '%s\n' '#!/usr/bin/env bash' 'exit 1' >"$stub_bin/java" + chmod +x "$stub_bin/java" + + run env PATH="$stub_bin:$PATH" DAML_STUB_LOG="$log_file" DAML_EXPORT_SOURCE_ONLY= \ + bash "$DAML_EXPORT" -d "$tmp" -b 0 -e 1 -s 1 -P 5 + [ "$status" -ne 0 ] + [[ "$output" == *"REDO"* ]] + # correct_export must not run on a failed export. + [[ "$output" != *"Correct "* ]] + # No build was scheduled: the daml stub never logged a start. + run ! grep -q "^start:" "$log_file" + rm -rf "$tmp" "$stub_bin" + rm -f "$log_file" +} + +@test "daml-export exits non-zero when java succeeds but artifacts are missing (SUR-3644)" { + tmp=$(mktemp -d) + stub_bin=$(mktemp -d) + log_file=$(mktemp) + write_daml_stub "$stub_bin" + printf '%s\n' '#!/usr/bin/env bash' 'exit 0' >"$stub_bin/java" + chmod +x "$stub_bin/java" + + run env PATH="$stub_bin:$PATH" DAML_STUB_LOG="$log_file" DAML_EXPORT_SOURCE_ONLY= \ + bash "$DAML_EXPORT" -d "$tmp" -b 0 -e 1 -s 1 -P 5 + [ "$status" -ne 0 ] + [[ "$output" == *"missing required files"* ]] + # No build was scheduled: the daml stub never logged a start. + run ! grep -q "^start:" "$log_file" + rm -rf "$tmp" "$stub_bin" + rm -f "$log_file" +} + @test "daml-export does not schedule duplicate builds for same output dir (SUR-3352)" { tmp=$(mktemp -d) stub_bin=$(mktemp -d) diff --git a/tests/minikube-test-environment.bats b/tests/minikube-test-environment.bats index 72cd2c6..705f4b0 100644 --- a/tests/minikube-test-environment.bats +++ b/tests/minikube-test-environment.bats @@ -60,3 +60,15 @@ teardown() { [ "$status" -eq 0 ] grep -qx 'stop' "$MINIKUBE_ARGV_LOG" } + +@test "create passes the default CNI to minikube start (SUR-3648)" { + run env "PATH=$PATH" "MINIKUBE_ARGV_LOG=$MINIKUBE_ARGV_LOG" "$MK_ENV" create + grep -qx 'start' "$MINIKUBE_ARGV_LOG" + grep -qx -- '--cni=calico' "$MINIKUBE_ARGV_LOG" +} + +@test "create honors a CNI override on minikube start (SUR-3648)" { + run env "PATH=$PATH" "MINIKUBE_ARGV_LOG=$MINIKUBE_ARGV_LOG" CNI=cilium "$MK_ENV" create + grep -qx 'start' "$MINIKUBE_ARGV_LOG" + grep -qx -- '--cni=cilium' "$MINIKUBE_ARGV_LOG" +} diff --git a/tests/replace-validator.bats b/tests/replace-validator.bats index 91b2ebc..5eac86a 100644 --- a/tests/replace-validator.bats +++ b/tests/replace-validator.bats @@ -59,6 +59,64 @@ EOF [ "$status" -eq 0 ] } +@test "validateNodesMapped errors and exits non-zero when FROM_NODE has no pod (SUR-3649)" { + TEST_SCRIPT=$(mktemp) + cat >"$TEST_SCRIPT" <"$TEST_SCRIPT" <"$TEST_SCRIPT" <