Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
16 changes: 14 additions & 2 deletions .github/workflows/controller-kind.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,15 @@ jobs:
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version-file: controller/go.mod
cache-dependency-path: controller/go.sum

- name: Run make deploy
working-directory: controller
run: make deploy
run: make deploy-ci -j --output-sync=target

e2e-test-operator:
runs-on: ubuntu-latest
Expand All @@ -27,6 +33,12 @@ jobs:
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5
with:
go-version-file: controller/go.mod
cache-dependency-path: controller/go.sum

- name: Run operator e2e test
working-directory: controller
run: make test-operator-e2e
run: make test-operator-e2e-ci -j --output-sync=target
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
54 changes: 32 additions & 22 deletions .github/workflows/python-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,6 @@ jobs:
with:
fetch-depth: 0

- name: Free up runner disk space
uses: mathio/gha-cleanup@aca3d43a05bf564f5fc69acb929a65b01797f22b # v1.1.2
with:
remove-browsers: true
verbose: true

- id: uv
run: echo "version=$(cat .uv-version)" >> "$GITHUB_OUTPUT"
- name: Install uv
Expand All @@ -96,19 +90,8 @@ jobs:
sudo chmod 0666 /dev/kvm /dev/vhost-vsock /dev/vhost-net

sudo apt-get update
sudo apt-get install -y qemu-system-arm qemu-system-x86 rpm2cpio cpio

- name: Install libgpiod-dev (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libgpiod-dev liblgpio-dev

- name: Install nftables and dnsmasq (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y nftables dnsmasq-base isc-dhcp-client dnsutils
sudo apt-get install -y qemu-system-arm qemu-system-x86 rpm2cpio cpio \
libgpiod-dev liblgpio-dev nftables dnsmasq-base isc-dhcp-client dnsutils

- name: Load kernel modules for DUT network tests (Linux)
if: runner.os == 'Linux'
Expand Down Expand Up @@ -145,7 +128,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-v2

- name: Download Fedora Cloud images
if: steps.cache-fedora-cloud-images.outputs.cache-hit != 'true'
Expand All @@ -155,12 +138,39 @@ 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 --no-network -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' \
--run-command 'ssh-keygen -A' \
--run-command 'sed -i "s/^#*PasswordAuthentication.*/PasswordAuthentication yes/" /etc/ssh/sshd_config' \
--run-command 'systemctl enable sshd' \
--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
5 changes: 5 additions & 0 deletions controller/Containerfile.ci
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM registry.access.redhat.com/ubi9/ubi-micro:9.8-1784702951@sha256:b1e86b97028b8fcfb6d85f997c39e6b6b67496163ef8d80d243220a4918e8bef
WORKDIR /
COPY manager router ./
USER 65532:65532
ENTRYPOINT ["/manager"]
5 changes: 5 additions & 0 deletions controller/Containerfile.exporter-set-controller.ci
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM registry.access.redhat.com/ubi9/ubi-micro:9.8-1784702951@sha256:b1e86b97028b8fcfb6d85f997c39e6b6b67496163ef8d80d243220a4918e8bef
WORKDIR /
COPY exporter-set-controller .
USER 65532:65532
ENTRYPOINT ["/exporter-set-controller"]
5 changes: 5 additions & 0 deletions controller/Containerfile.operator.ci
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM registry.access.redhat.com/ubi9/ubi-micro:9.8-1784702951@sha256:b1e86b97028b8fcfb6d85f997c39e6b6b67496163ef8d80d243220a4918e8bef
WORKDIR /
COPY manager .
USER 65532:65532
ENTRYPOINT ["/manager"]
28 changes: 28 additions & 0 deletions controller/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes
build-operator:
make -C deploy/operator build-installer docker-build

.PHONY: build-operator-ci
build-operator-ci:
make -C deploy/operator build-installer docker-build-ci

.PHONY: build
build: manifests generate fmt vet ## Build manager binary.
go build -ldflags "$(LDFLAGS)" -o bin/manager cmd/main.go
Expand All @@ -135,6 +139,14 @@ docker-build: ## Build docker image with the manager.
--build-arg BUILD_DATE=$(BUILD_DATE) \
-t ${IMG} -f Containerfile .

.PHONY: docker-build-ci
docker-build-ci: ## Build docker images from pre-compiled host binaries (fast CI path).
CGO_ENABLED=0 go build -ldflags "$(LDFLAGS)" -o bin/manager cmd/main.go
CGO_ENABLED=0 go build -ldflags "$(LDFLAGS)" -o bin/router cmd/router/main.go
CGO_ENABLED=0 go build -ldflags "$(LDFLAGS)" -o bin/exporter-set-controller cmd/exporter-set-controller/main.go
$(CONTAINER_TOOL) build -t ${IMG} -f Containerfile.ci bin/
$(CONTAINER_TOOL) build -t ${EXPORTER_SET_CONTROLLER_IMG} -f Containerfile.exporter-set-controller.ci bin/

.PHONY: docker-build-exporter-set-controller
docker-build-exporter-set-controller: ## Build docker image for the exporter-set-controller.
$(CONTAINER_TOOL) build \
Expand Down Expand Up @@ -206,14 +218,30 @@ ifeq ($(SKIP_BUILD),)
endif
./hack/deploy_with_operator.sh

.PHONY: deploy-ci
deploy-ci: cluster grpcurl ## CI-optimized deploy: host-compiled binaries, no multi-stage container builds.
ifeq ($(SKIP_BUILD),)
$(MAKE) docker-build-ci
$(MAKE) build-operator-ci
endif
./hack/deploy_with_operator.sh


.PHONY: deploy-operator
deploy-operator: docker-build docker-build-exporter-set-controller build-operator cluster grpcurl ## Deploy only the operator (without Jumpstarter CR)
NETWORKING_MODE=ingress DEPLOY_JUMPSTARTER=false ./hack/deploy_with_operator.sh

.PHONY: deploy-operator-ci
deploy-operator-ci: docker-build-ci build-operator-ci cluster grpcurl ## CI-optimized: host-compiled binaries, no multi-stage container builds.
NETWORKING_MODE=ingress DEPLOY_JUMPSTARTER=false ./hack/deploy_with_operator.sh

.PHONY: test-operator-e2e
test-operator-e2e: grpcurl deploy-operator
make -C deploy/operator test-e2e

.PHONY: test-operator-e2e-ci
test-operator-e2e-ci: grpcurl deploy-operator-ci ## CI-optimized e2e test (host-compiled Go, cached modules).
make -C deploy/operator test-e2e
.PHONY: operator-logs
operator-logs:
kubectl logs -n jumpstarter-operator-system -l app.kubernetes.io/name=jumpstarter-operator -f
Expand Down
5 changes: 5 additions & 0 deletions controller/deploy/operator/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,11 @@ docker-build: ## Build docker image with the manager.
--build-arg BUILD_DATE=$(BUILD_DATE) \
-t ${IMG} ../../ -f ../../Containerfile.operator

.PHONY: docker-build-ci
docker-build-ci: ## CI-optimized: host-compiled binary, no multi-stage build.
CGO_ENABLED=0 go build -ldflags "$(LDFLAGS)" -o bin/manager cmd/main.go
$(CONTAINER_TOOL) build -t ${IMG} -f ../../Containerfile.operator.ci bin/

.PHONY: docker-push
docker-push: ## Push docker image with the manager.
$(CONTAINER_TOOL) push ${IMG}
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
Loading
Loading