Chainsaw E2E Tests
This guide documents every Chainsaw E2E test in this repository, what each one validates, why it exists, and how to run suites via label selectors.
How test selection works
This repo uses label selectors for just run-e2e and GitHub Actions workflows:
just run-e2eexecutes:chainsaw test e2e --selector <selector> ...- Selector input is controlled by
CHAINSAW_SELECTOR(default:all) - Additional Chainsaw options can be passed through
CHAINSAW_FLAGS
Key references:
run-e2ecommand:justfile- default selector and flags:
justfile - reusable workflow:
.github/workflows/e2e.yaml - PR trigger workflow selector (
pr):.github/workflows/e2e-pr-trigger.yaml - nightly trigger workflow selector (
all):.github/workflows/e2e-nightly-trigger.yaml - manual trigger workflow selector (input):
.github/workflows/e2e-manual-trigger.yaml
Selector matrix
| Selector | Command | Runs | Typical use |
|---|---|---|---|
all | CHAINSAW_SELECTOR=all just run-e2e | All Chainsaw suites | Full local run / nightly parity |
pr | CHAINSAW_SELECTOR=pr just run-e2e | PR smoke tests | Matches PR workflow selector |
category.baseline | CHAINSAW_SELECTOR=category.baseline just run-e2e | Baseline lifecycle behavior | Verify core provider health quickly |
category.scheduling | CHAINSAW_SELECTOR=category.scheduling just run-e2e | Scheduling behavior suites | Debug pod placement / constraints |
category.consolidation | CHAINSAW_SELECTOR=category.consolidation just run-e2e | Consolidation behavior suites | Debug scale-down/replacement behavior |
category.drift | CHAINSAW_SELECTOR=category.drift just run-e2e | Drift behavior suites | Debug node replacement from config drift |
Test inventory and justification
| Test | Path | Selector groups | What it validates | Why we need it |
|---|---|---|---|---|
| minimal-lke-standard | e2e/minimal-lke-standard/chainsaw-test.yaml | all, pr, category.baseline | Baseline scale-up/scale-down behavior with NodeClaim lifecycle and instance-type transitions under workload changes | Catches broad regressions in core provisioning/reconciliation path and verifies cluster can recover back to zero |
| scheduling-nodepool-label-selector | e2e/scheduling-nodepool-label-selector/chainsaw-test.yaml | all, pr, category.scheduling | NodePool label targeting via pod nodeSelector, and confirms pods land on expected node/nodepool | Ensures constraint-based scheduling works and avoids silent misplacement of workloads |
| scheduling-blockstorage-topology | e2e/scheduling-blockstorage-topology/chainsaw-test.yaml | all, pr, category.scheduling | WaitForFirstConsumer claims with and without allowed topologies, scale-from-zero and replacement for matching bound PVCs, and rejection of incompatible PVC regions | Verifies predicted CSI region labels match CSI registration and prevent provisioning into an incompatible volume region |
| scheduling-taints-tolerations | e2e/scheduling-taints-tolerations/chainsaw-test.yaml | all, pr, category.scheduling | Taint/toleration behavior with one tolerated and one non-tolerated workload | Prevents regressions where taints are ignored or non-tolerated pods are scheduled incorrectly |
| scheduling-ephemeral-storage | e2e/scheduling-ephemeral-storage/chainsaw-test.yaml | all, pr, category.scheduling | Confirms Pod and DaemonSet ephemeral-storage requests schedule, worker and NodeClaim report storage, and a request above usable storage remains unschedulable without another NodeClaim | Guards predicted ephemeral-storage capacity, scheduling fit, and over-capacity rejection on Standard and Enterprise LKE |
| consolidation-empty-node-scale-down | e2e/consolidation-empty-node-scale-down/chainsaw-test.yaml | all, pr, category.consolidation | NodeClaim count reduction on a dedicated consolidation NodePool after scale-down | Validates empty/underutilized consolidation behavior with deterministic pool constraints |
| consolidation-non-empty-step-scale | e2e/consolidation-non-empty-step-scale/chainsaw-test.yaml | all, pr, category.consolidation | Non-empty replacement signal on a dedicated consolidation NodePool after stepped scale changes | Covers disruptive consolidation edge cases while avoiding default-pool sizing bias |
| drift-nodepool-labels-taints | e2e/drift-nodepool-labels-taints/chainsaw-test.yaml | all, pr, category.drift | Drift-triggered replacement when a drift-specific NodePool template labels/taints change | Ensures NodePool template mutations propagate through replacement in an isolated test-owned pool |
Running tests
Run selector-based suites (recommended)
# Full suite
CHAINSAW_SELECTOR=all just run-e2e
# PR subset
CHAINSAW_SELECTOR=pr just run-e2e
# Category-focused debug
CHAINSAW_SELECTOR=category.drift just run-e2e
Run one specific test directory directly (debug convenience)
Use direct Chainsaw invocation when you want exactly one test file without introducing single-test labels:
chainsaw test e2e/minimal-lke-standard --config .chainsaw.yaml
Cluster cleanup access
just destroy-lke-cluster <cluster_id> refreshes the cluster API ACL using CLUSTER_ACL_FLAGS (by default, the current runner’s public IPv4 address), then waits for Kubernetes API access before deleting test resources and the cluster. Cleanup jobs run on a separate runner from setup, so they cannot reuse setup’s IP allowlist.
Cleanup runner egress policies must allow ipv4.icanhazip.com:443 for IP discovery and the cluster API endpoint: *.linodelke.net:443 for standard LKE or *.enterprise.linodelke.net:6443 for LKE Enterprise.
Authoring and maintenance rules
When adding/updating an E2E test:
- Add labels for execution scope and category in
metadata.labels:- include
all - include
pronly when the test should run in the PR workflow (currently reserved forminimal-lke-standard) - include exactly one
category.*
- include
- Keep manifests close to ownership:
- put scenario-specific manifests next to that scenario’s
chainsaw-test.yaml - reserve
e2e/common/for manifests reused by multiple scenarios
- put scenario-specific manifests next to that scenario’s
- Treat the bootstrap cluster as having only the shared default
LinodeNodeClass; each Chainsaw scenario must create its own KarpenterNodePool. - Add the shared e2e isolation taint
e2e.linode.dev/test=true:NoScheduleto each test-ownedNodePool, and add the matching toleration to every workload that should run on that pool. - Keep test cleanup explicit to avoid cross-test leftovers.
- Update this document:
- add/update row in Selector matrix if a new selector is introduced
- add/update row in Test inventory and justification
- Ensure workflow selectors (
pr/all) still match intended scope.