Skip to content
Closed
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
3903e2e
fix(e2e): pin nip.io baseDomain in /etc/hosts
bennyz Aug 9, 2026
2fda3f7
fix(e2e): don't treat kubectl stderr as query output
bennyz Aug 9, 2026
74a44ff
ci(e2e): retry a failed spec once before failing the job
bennyz Aug 9, 2026
ad447fc
test(e2e): continue after a failure in independent suites
bennyz Aug 9, 2026
82778ee
fix(grpc): separate DNS resolution timeout from connect timeout
bennyz Aug 9, 2026
beb50b7
test(e2e): accept every beforeLease hook failure outcome
bennyz Aug 9, 2026
5343f58
fix(shell): stop waiting when the exporter reports itself OFFLINE
bennyz Aug 9, 2026
0bcb0ec
test(e2e): create pagination fixtures with one kubectl apply
bennyz Aug 9, 2026
64a0630
test(e2e): reuse the hooks exporter when the config is unchanged
bennyz Aug 9, 2026
9c200ad
test(e2e): allow running containers in parallel behind E2E_PROCS
bennyz Aug 9, 2026
74eaff3
test(grpc): cover the insecure TLS resolve and connect paths
bennyz Aug 9, 2026
b559af2
fix(shell): stop waiting when the exporter goes offline after the lease
bennyz Aug 9, 2026
a811ac6
test(e2e): stop exporters gracefully and wait on real conditions
bennyz Aug 9, 2026
47e74f9
ci: restore the go module cache
bennyz Aug 9, 2026
ec5548d
test(e2e): scope the orphan exporter sweep to this tracker
bennyz Aug 9, 2026
b09cd1b
test(e2e): run the core suite in parallel with the other containers
bennyz Aug 9, 2026
8830bff
ci(e2e): cache controller tools and load kind images in parallel
bennyz Aug 9, 2026
eec87c6
try to reduce python test runtime
bennyz Aug 10, 2026
c6e4db8
fix(ci): add --no-network to virt-customize
bennyz Aug 10, 2026
9fc9088
perf(mitmproxy): share proxy process across tests in a class
bennyz Aug 10, 2026
8ee5c11
perf(ci): parallelize e2e operator build prerequisites
bennyz Aug 10, 2026
85a2808
perf(ci): compile Go on host with cached modules instead of inside co…
bennyz Aug 10, 2026
23e89f8
fix(ci): fix QEMU SSH failure and test-report race condition
bennyz Aug 10, 2026
1ac04ee
perf(ci): remove unnecessary disk cleanup + consolidate apt installs
bennyz Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build-images.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ jobs:
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: .go-version
cache-dependency-path: "**/go.sum"

- name: Build operator installer manifest
if: ${{ matrix.generate_installer && steps.check.outputs.skip != 'true' }}
Expand Down
44 changes: 44 additions & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ permissions:

env:
CONTAINER_TOOL: docker
# Retry a failed spec once before failing the job. Retries are reported as
# flakes in the ginkgo summary, so this hides nothing; it only stops a single
# infrastructure hiccup from failing an unrelated PR.
E2E_FLAKE_ATTEMPTS: "2"
# Run the non-Serial containers two at a time. The runner has 4 CPUs and the
# suite spends most of its time waiting on the cluster, so a second process
# overlaps the two long containers (hooks, core) rather than competing for CPU.
E2E_PROCS: "2"

jobs:
changes:
Expand Down Expand Up @@ -68,6 +76,7 @@ jobs:
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: .go-version
cache-dependency-path: "**/go.sum"

- name: Cache controller image
id: cache
Expand Down Expand Up @@ -107,6 +116,7 @@ jobs:
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: .go-version
cache-dependency-path: "**/go.sum"

- name: Cache operator artifacts
id: cache
Expand Down Expand Up @@ -151,6 +161,7 @@ jobs:
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: .go-version
cache-dependency-path: "**/go.sum"

- name: Cache exporterset-controller image
id: cache
Expand Down Expand Up @@ -310,6 +321,7 @@ jobs:
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: .go-version
cache-dependency-path: "**/go.sum"

- name: Load e2e artifacts
uses: ./.github/actions/load-e2e-artifacts
Expand All @@ -327,6 +339,16 @@ jobs:
sudo modprobe "$mod" 2>/dev/null || true
done

- name: Cache controller tools
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: controller/bin
key: controller-tools-${{ matrix.arch }}-kind${{ env.KIND_VERSION }}-kustomize${{ env.KUSTOMIZE_VERSION }}-grpcurl${{ env.GRPCURL_VERSION }}
env:
KIND_VERSION: v0.27.0
KUSTOMIZE_VERSION: v5.4.1
GRPCURL_VERSION: v1.9.2

- name: Setup e2e test environment
run: make e2e-setup
env:
Expand Down Expand Up @@ -374,6 +396,17 @@ jobs:
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: .go-version
cache-dependency-path: "**/go.sum"

- name: Cache controller tools
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: controller/bin
key: controller-tools-amd64-kind${{ env.KIND_VERSION }}-kustomize${{ env.KUSTOMIZE_VERSION }}-grpcurl${{ env.GRPCURL_VERSION }}
env:
KIND_VERSION: v0.27.0
KUSTOMIZE_VERSION: v5.4.1
GRPCURL_VERSION: v1.9.2

- name: Setup compat environment (old controller v0.8.1)
run: make e2e-compat-setup COMPAT_SCENARIO=old-controller
Expand Down Expand Up @@ -416,12 +449,23 @@ jobs:
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: .go-version
cache-dependency-path: "**/go.sum"

- name: Load e2e artifacts
uses: ./.github/actions/load-e2e-artifacts
with:
arch: amd64

- name: Cache controller tools
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: controller/bin
key: controller-tools-amd64-kind${{ env.KIND_VERSION }}-kustomize${{ env.KUSTOMIZE_VERSION }}-grpcurl${{ env.GRPCURL_VERSION }}
env:
KIND_VERSION: v0.27.0
KUSTOMIZE_VERSION: v5.4.1
GRPCURL_VERSION: v1.9.2

- name: Setup compat environment (old client v0.7.4)
run: make e2e-compat-setup COMPAT_SCENARIO=old-client
env:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ jobs:
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: .go-version
cache-dependency-path: "**/go.sum"

- name: Run go linter
working-directory: controller
Expand Down
30 changes: 27 additions & 3 deletions .github/workflows/python-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ jobs:
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: python/packages/jumpstarter-driver-qemu/images
key: fedora-cloud-43-1.6
key: fedora-cloud-43-1.6-preconfigured-v1

- name: Download Fedora Cloud images
if: steps.cache-fedora-cloud-images.outputs.cache-hit != 'true'
Expand All @@ -155,12 +155,36 @@ jobs:
"https://iad.mirror.rackspace.com/fedora/releases/43/Cloud/${arch}/images/Fedora-Cloud-Base-Generic-43-1.6.${arch}.qcow2"
done

- name: Pre-configure Fedora Cloud images for fast boot
if: runner.os == 'Linux' && steps.cache-fedora-cloud-images.outputs.cache-hit != 'true'
run: |
sudo apt-get install -y libguestfs-tools
arch=x86_64
img="python/packages/jumpstarter-driver-qemu/images/Fedora-Cloud-Base-Generic-43-1.6.${arch}.qcow2"
sudo virt-customize -a "$img" \
--hostname demo \
--run-command 'useradd -m -s /bin/bash jumpstarter' \
--password jumpstarter:password:password \
--run-command 'echo "jumpstarter ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/jumpstarter' \
--run-command 'systemctl disable cloud-init cloud-init-local cloud-config cloud-final' \
--run-command 'rm -rf /var/lib/cloud' \
--selinux-relabel

- name: Run pytest
working-directory: python
env:
PYTEST_ADDOPTS: "--cov-report=xml --log-level=CRITICAL --log-cli-level=CRITICAL"
PYTEST_ADDOPTS: "--log-level=CRITICAL --log-cli-level=CRITICAL"
run: |
make test -j4 LOGS_DIR=${{ runner.temp }}/test-logs
changed_pkgs=$(git diff --name-only --relative origin/${{ github.base_ref || 'main' }}...HEAD -- packages/ \
| sed -n 's|^packages/\([^/]*\)/.*|\1|p' | sort -u | tr '\n' ' ')
echo "Coverage-enabled packages: ${changed_pkgs:-all (fallback)}"
if [ -z "$changed_pkgs" ]; then
# Fallback: workflow_dispatch or merge_group without a base ref
export PYTEST_ADDOPTS="--cov=. --cov-report=xml $PYTEST_ADDOPTS"
make test -j8 LOGS_DIR=${{ runner.temp }}/test-logs
else
make test -j8 LOGS_DIR=${{ runner.temp }}/test-logs COV_PACKAGES="$changed_pkgs"
fi

- name: Upload test logs
if: always()
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release-operator-installer.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7
with:
go-version-file: .go-version
cache-dependency-path: "**/go.sum"

- name: Build operator installer manifest
env:
Expand Down
33 changes: 25 additions & 8 deletions controller/hack/deploy_with_operator.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,40 @@ if [ "${USE_CERTMANAGER}" = "true" ]; then
fi
fi

# load the container images into the cluster
load_image "${IMG}"
load_image "${OPERATOR_IMG}"
load_image "${EXPORTER_SET_CONTROLLER_IMG}"
# Exporter + QEMU runtime images are required for ExporterSet QEMU e2e / samples.
# Missing images are skipped so plain controller deploys still work.
# Load container images into the cluster in parallel. Each `kind load` is I/O
# bound (piping a tarball into the node's containerd), so overlapping them cuts
# wall-clock to roughly the cost of the single largest image.
_load_pids=()
_load_failed=0

load_image "${IMG}" &
_load_pids+=($!)
load_image "${OPERATOR_IMG}" &
_load_pids+=($!)
load_image "${EXPORTER_SET_CONTROLLER_IMG}" &
_load_pids+=($!)

if container_image_exists "${EXPORTER_IMG}"; then
load_image "${EXPORTER_IMG}"
load_image "${EXPORTER_IMG}" &
_load_pids+=($!)
else
echo -e "${YELLOW}Skipping load of exporter image (not present locally): ${EXPORTER_IMG}${NC}"
fi
if container_image_exists "${QEMU_RUNTIME_IMG}"; then
load_image "${QEMU_RUNTIME_IMG}"
load_image "${QEMU_RUNTIME_IMG}" &
_load_pids+=($!)
else
echo -e "${YELLOW}Skipping load of qemu-runtime image (not present locally): ${QEMU_RUNTIME_IMG}${NC}"
fi

for pid in "${_load_pids[@]}"; do
wait "${pid}" || _load_failed=1
done
if [ "${_load_failed}" -eq 1 ]; then
echo -e "${RED}One or more images failed to load${NC}"
exit 1
fi

# Deploy the operator
echo -e "${GREEN}Deploying Jumpstarter operator ...${NC}"
kubectl apply -f deploy/operator/dist/install.yaml
Expand Down
18 changes: 17 additions & 1 deletion e2e/lib/common.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,23 @@ run_ginkgo() {
timeout="60m"
fi

local flags=(-v --show-node-events --trace --timeout "${timeout}")
# Retry a failed spec instead of failing the whole suite. The e2e suite talks
# to a real cluster over the network, so a spec can fail for reasons that have
# nothing to do with the code under test (a slow DNS answer, a pod scheduled
# late, a router connection dropped). A retried spec is still reported as
# flaky in the summary, so genuine instability stays visible.
local flake_attempts="${E2E_FLAKE_ATTEMPTS:-1}"

# Run top-level containers concurrently when asked. Off by default: the
# suite shares one cluster and one runner, so more processes is not free.
# Containers that touch host-global state or the shared client config are
# marked Serial and still run one at a time, after the parallel ones.
local procs="${E2E_PROCS:-1}"

local flags=(-v --show-node-events --trace --timeout "${timeout}" --flake-attempts "${flake_attempts}")
if [ "${procs}" -gt 1 ]; then
flags+=(--procs "${procs}")
fi
Comment on lines +82 to +98

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C2 'E2E_(FLAKE_ATTEMPTS|PROCS)' .github e2e Makefile 2>/dev/null || true

Repository: jumpstarter-dev/jumpstarter

Length of output: 1268


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '70,115p' e2e/lib/common.sh
printf '\n--- behavioral probe ---\n'
(
  set +e
  E2E_FLAKE_ATTEMPTS=abc E2E_PROCS=0 bash -c '
    trap '\''exit_status=$?; printf "exit_status=%d\n" "$exit_status"\'' EXIT
    local flake_attempts="${E2E_FLAKE_ATTEMPTS:-1}"
    local procs="${E2E_PROCS:-1}"
    local flags=(-v --show-node-events --trace --timeout "${timeout}" --flake-attempts "${flake_attempts}")
    if [ "${procs}" -gt 1 ]; then
      flags+=(--procs "${procs}")
    fi
    printf "%s\n" "${flags[@]}"
  '
)

Repository: jumpstarter-dev/jumpstarter

Length of output: 1884


🏁 Script executed:

#!/bin/bash
set -euo pipefail
printf '%s\n' '--- E2E invocation / common.sh function reference ---'
rg -n 'run-e2e|common\.sh|E2E_FLAKE_ATTEMPTS|E2E_PROCS|ginkgo' e2e Makefile .github/workflows/e2e.yaml 2>/dev/null || true

printf '\n%s\n' '--- behavior probe with non-numeric E2E_FLAKE_ATTEMPTS and valid E2E_PROCS ---'
(
  set +e
  E2E_FLAKE_ATTEMPTS=abc E2E_PROCS=2 bash -c '
    trap '\''exit_status=$?; printf "behavior_probe_exit_status=%d\n" "$exit_status"\'' EXIT
    local flake_attempts="${E2E_FLAKE_ATTEMPTS:-1}"
    local procs="${E2E_PROCS:-1}"
    local flags=(-v --show-node-events --trace --timeout "${timeout}" --flake-attempts "${flake_attempts}")
    if [ "${procs}" -gt 1 ]; then
      flags+=(--procs "${procs}")
    fi
    printf "flags=%s\n" "${flags[*]}"
  ' timeout=30m label_filter=
)

printf '\n%s\n' '--- behavior probe with numeric E2E_FLAKE_ATTEMPTS and non-numeric E2E_PROCS ---'
(
  set +e
  E2E_FLAKE_ATTEMPTS=2 E2E_PROCS=bad bash -c '
    trap '\''exit_status=$?; printf "behavior_probe_exit_status=%d\n" "$exit_status"\'' EXIT
    local flake_attempts="${E2E_FLAKE_ATTEMPTS:-1}"
    local procs="${E2E_PROCS:-1}"
    local flags=(-v --show-node-events --trace --timeout "${timeout}" --flake-attempts "${flake_attempts}")
    if [ "${procs}" -gt 1 ]; then
      flags+=(--procs "${procs}")
    fi
    printf "flags=%s\n" "${flags[*]}"
  ' timeout=30m label_filter=
)

printf '%s\n' '--- behavior probe with E2E_PROCS=0 ---'
(
  set +e
  E2E_FLAKE_ATTEMPTS=2 E2E_PROCS=0 bash -c '
    local flake_attempts="${E2E_FLAKE_ATTEMPTS:-1}"
    local procs="${E2E_PROCS:-1}"
    local flags=(-v --show-node-events --trace --timeout "${timeout}" --flake-attempts "${flake_attempts}")
    if [ "${procs}" -gt 1 ]; then
      flags+=(--procs "${procs}")
    fi
    printf "flags=%s\n" "${flags[*]}"
  ' timeout=30m label_filter=
)

Repository: jumpstarter-dev/jumpstarter

Length of output: 2989


🏁 Script executed:

#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
text = Path("e2e/lib/common.sh").read_text()
print("common.txt exists:", Path("e2e/lib/common.sh").exists())
print(text[text.index("run_ginkgo()"):text.index("}", text.index("run_ginkgo()")+1):1200])

import subprocess
for e_flake in ["abc", "", None]: e_flake = f"E2E_FLAKE_ATTEMPTS={e_flake}" if e_flake is not None else ""
    for e_procs in ["bad", "0", "2", ""]:
        e_procs_full = f"E2E_PROCS={e_procs}" if e_procs else ""
        cmd = ["bash","-c",
            "local flake_attempts=\"${E2E_FLAKE_ATTEMPTS:-1}\"; local procs=\"${E2E_PROCS:-1}\";"
            "local flags=(-v --show-node-events --trace --timeout \"30m\" --flake-attempts \"${flake_attempts}\");"
            "if [ \"${procs}\" -gt 1 ]; then flags+=(--procs \"${procs}\"); fi;"
            "printf '%s\\n' \"${flags[*]}\""
        ]
        if e_flake: cmd = ["bash"]+cmd; env={}; proc_full=e_procs_full.split("=",1); env[proc_full[0]]=proc_full[1] if len(proc_full)>1 else ""
PY

Repository: jumpstarter-dev/jumpstarter

Length of output: 268


🏁 Script executed:

#!/bin/bash
set -u
printf '%s\n' '--- run-ginkgo context ---'
sed -n '66,103p' e2e/lib/common.sh

printf '%s\n' '--- run-e2e call context ---'
sed -n '58,72p' e2e/run-e2e.sh

printf '%s\n' '--- make e2e targets ---'
sed -n '190,218p' Makefile

tmpdir="$(mktemp -d)"
cd "$tmpdir"
printf '%s\n' '--- shell probe wrapper ---'
trap 'rm -rf "$WD"' EXIT
run_probe() {
  local name="$1"
  printf '%s: ' "$name"
  E2E_FLAKE_ATTEMPTS="$2" E2E_PROCS="$3" bash -c '
    local flake_attempts="${E2E_FLAKE_ATTEMPTS:-1}"
    local procs="${E2E_PROCS:-1}"
    local flags=(-v --show-node-events --trace --timeout 30m --flake-attempts "${flake_attempts}")
    if [ "${procs}" -gt 1 ]; then
      flags+=(--procs "${procs}")
    fi
    printf "%s\n" "${flags[*]}"
  ' timeout=30m label_filter=
  printf "exit=%s\n" "${PIPESTATUS[1]}"
}
run_probe "nonnumeric flake" abc 2
run_probe "empty flake" "" 2
run_probe "numeric zero" 2 0
run_probe "nonnumeric procs" 2 bad
run_probe "numeric zero procs" 2 0
run_probe "valid" 2 2

Repository: jumpstarter-dev/jumpstarter

Length of output: 3380


🏁 Script executed:

#!/bin/bash
set -u
tmpdir="$(mktemp -d)"
cd "$tmpdir"

run_probe() {
  local name="$1"
  printf '%s: ' "$name"
  (
    E2E_FLAKE_ATTEMPTS="$2" E2E_PROCS="$3" bash -c '
      local flaky="${E2E_FLAKE_ATTEMPTS:-1}"
      local procs="${E2E_PROCS:-1}"
      local flags=(-v --show-node-events --trace --timeout 30m --flake-attempts "${flaky}")
      if [ "${procs}" -gt 1 ]; then
        flags+=(--procs "${procs}")
      fi
      printf "%s\n" "${flags[*]}"
    '
  )
  status="${PIPESTATUS[0]}"
  if [ "$status" -ne 0 ]; then
    printf "exit=%s\n" "$status"
  fi
}

run_probe "nonnumeric flake" abc 2
run_probe "empty flake" "" 2
run_probe "numeric zero procs" 2 0
run_probe "nonnumeric procs" 2 bad
run_probe "numeric zero procs" 2 0
run_probe "valid" 2 2

printf '%s\n' '--- compatible run call ---'
sed -n '60,78p' e2e/compat/run.sh

Repository: jumpstarter-dev/jumpstarter

Length of output: 1940


🏁 Script executed:

#!/bin/bash
set -u
tmpdir="$(mktemp -d)"
cd "$tmpdir" || exit 1

cat > /tmp/runner.sh <<'BASH'
run_ginkgo() {
    local test_dir="$1"
    shift
    local label_filter="${1:-}"
    shift || true

    local timeout="30m"
    local flake_attempts="${E2E_FLAKE_ATTEMPTS:-1}"
    local procs="${E2E_PROCS:-1}"

    local flags=(-v --show-node-events --trace --timeout "${timeout}" --flake-attempts "${flake_attempts}")
    if [ "${procs}" -gt 1 ]; then
        flags+=(--procs "${procs}")
    fi
    if [ -n "$label_filter" ]; then
        flags+=(--label-filter "$label_filter")
    fi

    printf 'flags=%s\n' "${flags[*]}"
}
run_ginkgo "$@"
BASH

run_probe() {
  local name="$1"
  printf '%s: ' "$name"
  (
    E2E_FLAKE_ATTEMPTS="$2" E2E_PROCS="$3" bash /tmp/runner.sh "$1"
  )
  status="${PIPESTATUS[0]}"
  if [ "$status" -ne 0 ]; then
    printf "exit=%s\n" "$status"
  fi
}

run_probe "nonnumeric flake" abc 2
run_probe "empty flake" "" 2
run_probe "empty default flake" "unset" 2
run_probe "numeric zero procs" 2 0
run_probe "nonnumeric procs" 2 bad
run_probe "valid" 2 2

printf '%s\n' '--- compatible run call ---'
sed -n '60,78p' e2e/compat/run.sh

Repository: jumpstarter-dev/jumpstarter

Length of output: 872


🏁 Script executed:

#!/bin/bash
set -euo pipefail
git ls-files | rg '(^|/)e2e/compat/run\.sh$|(^|/)\.github/workflows/e2e\.yaml$'
git ls-files | rg '(^|/)e2e/run-e2e\.sh$|(^|/)Makefile$'

Repository: jumpstarter-dev/jumpstarter

Length of output: 469


Validate E2E runner integer settings.

E2E_PROCS is passed through an integer comparison, so a non-numeric value emits [: integer expression expected and Ginkgo runs without --procs. Fail early for both E2E_FLAKE_ATTEMPTS and E2E_PROCS before constructing the Ginkgo command.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/lib/common.sh` around lines 82 - 98, Validate E2E_FLAKE_ATTEMPTS and
E2E_PROCS as numeric integer settings immediately after they are assigned and
before constructing flags in the surrounding runner setup. Fail early with a
clear error for either invalid value, while preserving the existing defaults and
conditional --procs behavior for valid values.

if [ -n "$label_filter" ]; then
flags+=(--label-filter "$label_filter")
fi
Expand Down
30 changes: 30 additions & 0 deletions e2e/setup-e2e.sh
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,35 @@ deploy_controller() {
# shellcheck source=lib/install.sh
source "$SCRIPT_DIR/lib/install.sh"

# Pin the ingress hostnames in /etc/hosts so that host-side clients (jmp, curl)
# never depend on public DNS.
#
# baseDomain is a nip.io wildcard name of the form jumpstarter.<IP>.nip.io, so
# every jmp invocation would otherwise resolve <prefix>.jumpstarter.<IP>.nip.io
# against a public resolver. A slow or rate-limited lookup burns the client's
# whole connect budget and surfaces as "Timeout connecting to grpc....:8082".
# The IP is already embedded in the name, so we can serve the same answer
# locally. Non-nip.io base domains are left alone.
pin_basedomain_hosts_entries() {
local basedomain="$1"
local ip

ip=$(echo "${basedomain}" | sed -nE 's/^.*\.([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)\.nip\.io$/\1/p')
if [ -z "${ip}" ]; then
log_info "baseDomain ${basedomain} is not a nip.io name, leaving DNS resolution alone"
return 0
fi

if grep -q "grpc.${basedomain}" /etc/hosts 2>/dev/null; then
log_info "✓ ${basedomain} entries already in /etc/hosts"
return 0
fi

log_warn "About to add ${basedomain} entries to /etc/hosts (requires sudo)"
echo "${ip} ${basedomain} grpc.${basedomain} router.${basedomain} login.${basedomain}" | sudo tee -a /etc/hosts
log_info "✓ Pinned ${basedomain} to ${ip} in /etc/hosts"
}

# Step 6: Setup test environment
setup_test_environment() {
log_info "Setting up test environment..."
Expand All @@ -290,6 +319,7 @@ setup_test_environment() {
log_error "Failed to get baseDomain from Jumpstarter CR in namespace ${JS_NAMESPACE}"
exit 1
fi
pin_basedomain_hosts_entries "${BASEDOMAIN}"
export ENDPOINT="grpc.${BASEDOMAIN}:8082"
export LOGIN_ENDPOINT="login.${BASEDOMAIN}:8086"
log_info "Controller endpoint: $ENDPOINT"
Expand Down
2 changes: 1 addition & 1 deletion e2e/test/auth_logging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (
// with legacy (controller-issued) tokens so the token lives in a local config
// file where the test can corrupt it. It is intentionally NOT labelled for the
// compat suites — old controller images do not have auth-failure logging.
var _ = Describe("Auth Failure Logging E2E Tests", Label("auth-logging"), Ordered, func() {
var _ = Describe("Auth Failure Logging E2E Tests", Label("auth-logging"), Ordered, ContinueOnFailure, func() {
const (
clientName = "test-client-authlog"
exporterName = "test-exporter-authlog"
Expand Down
3 changes: 2 additions & 1 deletion e2e/test/compat_old_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ import (
. "github.com/onsi/gomega" //nolint:revive
)

var _ = Describe("Compat: Old Client E2E Tests", Label("compat", "old-client"), Ordered, func() {
// Serial: installs an older client into the shared environment.
var _ = Describe("Compat: Old Client E2E Tests", Label("compat", "old-client"), Ordered, Serial, func() {
var (
tracker *ProcessTracker
ns string
Expand Down
3 changes: 2 additions & 1 deletion e2e/test/compat_old_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ import (
. "github.com/onsi/gomega" //nolint:revive
)

var _ = Describe("Compat: Old Controller E2E Tests", Label("compat", "old-controller"), Ordered, func() {
// Serial: replaces the running controller and switches the active client.
var _ = Describe("Compat: Old Controller E2E Tests", Label("compat", "old-controller"), Ordered, Serial, func() {
var (
tracker *ProcessTracker
ns string
Expand Down
2 changes: 1 addition & 1 deletion e2e/test/direct_listener_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
. "github.com/onsi/gomega" //nolint:revive
)

var _ = Describe("Direct Listener E2E Tests", Label("direct-listener"), Ordered, func() {
var _ = Describe("Direct Listener E2E Tests", Label("direct-listener"), Ordered, ContinueOnFailure, func() {
var (
tracker *ProcessTracker
listenerPort = 19090
Expand Down
4 changes: 3 additions & 1 deletion e2e/test/dut_network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ func sudoArgs(args ...string) (string, []string) {
return args[0], args[1:]
}

var _ = Describe("DUT Network E2E Tests", Label("dut-network"), Ordered, func() {
// Serial: builds veth pairs, bridges and nftables rules in the host network
// namespace, and drives dnsmasq. There is only one host to share.
var _ = Describe("DUT Network E2E Tests", Label("dut-network"), Ordered, ContinueOnFailure, Serial, func() {
var (
tracker *ProcessTracker
listenerPort = 19091
Expand Down
Loading
Loading