Skip to main content

e2e-spec

Use when adding, changing or fixing e2e specs under test/e2e/ in kuik (the Kind suite run by task test-e2e), or when deciding what a PR should check end to end.

소스 정보

저장소
enix/kube-image-keeper
최근 소스 활동
2026년 9월 30일 17:09
감지된 SKILL.md 언어
영어
스타
754
포크
60

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
e2e-spec
description
Use when adding, changing or fixing e2e specs under test/e2e/ in kuik (the Kind suite run by task test-e2e), or when deciding what a PR should check end to end.
argument-hint
[behaviour to cover]
allowed-tools
Bash(task test-outline *) Read Grep Glob Edit Write
# e2e specs An e2e spec checks what only a real cluster can answer: the API server, admission, the kubelet, the chart as deployed. Everything else belongs to a unit or envtest spec. Target: `$ARGUMENTS`. ## When e2e specs come last in a PR ([0012](../../../notes/0012-e2e-specs-last-in-a-pr.md)): once the PR is otherwise ready (automated review addressed, unit and envtest specs green, the feature believed to work), one final `test` commit adds them. A failure is fixed by `fixup!` commits, autosquashed once like review fixes. This is the one exception to "a `test` commit before each `feat` commit" of [`tests.md`](../../rules/tests.md); unit and envtest specs keep that order. The reason: a Kind run takes minutes. On a PR, the suite runs only under the `e2e-ready` label ([0013](../../../notes/0013-e2e-ready-label.md)): once the PR is reviewed, its e2e specs are pushed and its `fixup!` commits are autosquashed, ask the user to add it. The required `E2E` check fails until then, unless the PR changes no path the suite depends on. ## Workflow 1. **Outline first.** **REQUIRED SUB-SKILL:** follow [`test-outline`](../test-outline/SKILL.md): the `It` / `Entry` strings are drafted as `PIt` / `PEntry`, shown with `task test-outline -- ./test/e2e/`, and reviewed before any body. 2. **One spec per rule**, never one per verb, kind or field ([`tests.md`](../../rules/tests.md)). Drop a case a unit or envtest spec already proves. 3. **Bodies**, with the patterns below, in the final `test` commit. 4. **Run** only through `task test-e2e`, on an isolated Kind cluster, never against a real cluster. The task creates `KIND_CLUSTER` when it is missing and deletes it afterwards only in that case. An existing Kind cluster of that name is reused as is and the suite installs and uninstalls kuik on it, so never point `KIND_CLUSTER` at a cluster used for anything else. Give the run a dedicated kubeconfig: ```sh KUBECONFIG=<tmp>/e2e-kubeconfig task test-e2e ``` `kind create cluster` switches the current context of the kubeconfig it writes to, and the suite uninstalls the CRDs at the end: on a shared kubeconfig, a later `kubectl` could hit the wrong cluster. The e2e tasks are outbound for the confirm-outbound hook: the user approves each run. ## Patterns | Need | Do | | ---- | -- | | An origin that is unavailable | A `.invalid` host (`kuik-e2e.invalid/...`): it fails fast, at DNS | | A slow or blackholed candidate | A documentation address (`192.0.2.1`, TEST-NET-1, never routed): the connection hangs until the timeout | | A registry that answers | `registry.k8s.io/pause:3.10`: HTTPS, non-root, fits the restricted Pod Security profile | | Every webhook replica has seen a new CR or config | Each replica has its own informers and config: poll `replicasRouting` until each replica answers as expected. It sends an AdmissionReview to each pod through `kubectl proxy`. Admission through the API server, a server-side dry run included, reaches one replica only | | A rewrite happened | Assert the rewritten image and the `kuik.enix.io/rewrites` annotation (`kuik.enix.io/no-alternatives` when every candidate failed) | | A counter moved | The kuik counters are per replica: `webhookMetric` reads one on each webhook pod (`kubectl get --raw /api/v1/namespaces/<ns>/pods/<pod>:8080/proxy/metrics`). Compare pod by pod when every replica must move, as after a config reload; sum when any replica may serve the admission | | kuik left a pod untouched | Proves nothing alone. Order the specs so that a rewrite proves the webhook is called before any "untouched" spec | | A config change | `setConfig` (`kubectl patch` of the ConfigMap), never `helm upgrade`, and allow up to 3m for the kubelet to sync it into the pods. Prove it loaded through behaviour: `webhook.availabilityCheck.timeout: 1ms` fails every probe, so a replica that loaded it stops rewriting | The suite needs internet access: there is no in-cluster registry and no containerd or Kind configuration (a plain HTTP registry does not work: the webhook probes an original over HTTPS even when it matches an `insecure: true` entry). cert-manager is pulled the same way. The chart is deployed once per suite, in `BeforeSuite` / `AfterSuite`, and never a second time: Ginkgo shuffles the top-level containers, so one container's `BeforeAll` would not cover the others. A top-level `AfterEach` dumps logs, events and pod descriptions on failure. ## Fixtures and helpers - [`e2e_suite_test.go`](../../../test/e2e/e2e_suite_test.go): `BeforeSuite` builds the manager image, loads it into Kind, installs cert-manager unless present (`CERT_MANAGER_INSTALL_SKIP=true` skips it) and runs `deployChart` (the `kuik-system` namespace under the restricted Pod Security profile, `make install`, `make deploy`). `AfterSuite` runs `undeployChart` and removes cert-manager if the suite installed it. The top-level `AfterEach` dumps logs, events and pod descriptions on failure. The default `Eventually` timeout is 2m. Every command is bounded: `kubectl` (1m), `makeTarget` (10m), `runBounded` for any other command, and `kubectlCommand` for a caller that builds the command before `utils.Run`. - [`e2e_test.go`](../../../test/e2e/e2e_test.go): the chart's names as constants (`namespace`, `deploymentName`, `processSelector`, `metricsPort`...) and the `Manager` container. Its specs: the 3 Deployments are available, each process runs under its own ServiceAccount, the RBAC table of `docs/v3/architecture.md` (`canI`, over `kubectl auth can-i --as`), the webhook metrics endpoint (a `curl-metrics` pod, `getMetricsOutput`), the cert-manager Secret and the CA injection. - [`routing_test.go`](../../../test/e2e/routing_test.go): the `Pod routing` container. Its pods run in `kuik-e2e-routing`, a namespace labelled `kuik.enix.io/e2e=true` that its ImageAlternatives select. Helpers: - `podManifest`, `createPod`: a restricted-compatible `pause` pod, created through admission once. - `podImage`, `podPhase`, `kuikAnnotations`: the admitted image, the phase, and only the `kuik.enix.io/*` annotations. - `replicasRouting`, `everyReplica`: whether each webhook pod rewrites an unreachable origin, asked directly through `kubectl proxy` (`startAPIProxy`, in `BeforeAll`). - `webhookMetric`: a counter on each webhook pod. - `setConfig`: replaces the global config file in its ConfigMap. - `scaleWebhook`, `webhookPods`, `webhookRestarts`, `deploymentAvailable`: the webhook Deployment and its pods; `webhookRestarts` is what a "without a restart" spec compares. - `kubectlStdin`: kubectl with a manifest on stdin. - [`test/utils`](../../../test/utils/utils.go): `Run` (runs a command from the project directory), `LoadImageToKindClusterWithName`, `GetNonEmptyLines`, the cert-manager install helpers. ## Common mistakes | Mistake | Fix | | ------- | --- | | e2e specs written with the first `test` commit | Add them in the last commit, once the PR is ready | | Running the suite on the current kubeconfig, or on a real cluster | `KUBECONFIG=<tmp>/e2e-kubeconfig task test-e2e`, Kind only | | Creating the pod right after the CR | Poll `replicasRouting` until every replica sees the CR | | Waiting on a server-side dry run | It reaches one replica only: ask each pod with `replicasRouting` | | Reading the metrics of one webhook pod | Read every replica with `webhookMetric` | | Proving a config reload from a log line | Prove it through routing, with a config that changes it | | `kubectl create --raw` to a webhook pod | It sends no `Content-Type`, which the webhook refuses: POST through `kubectl proxy` | | An HTTP call without a timeout inside `Eventually` | `Eventually` never interrupts a hung poll: bound the client | | A bare `exec.Command` | `utils.Run` waits for the process with no deadline: use `kubectl`, `makeTarget` or `runBounded` | | An "untouched" spec with nothing proving the webhook ran | Order it after a spec that shows a rewrite | | An in-cluster plain HTTP registry | `registry.k8s.io` for an answer, `.invalid` for a failure | | `helm upgrade` to change the config | `kubectl patch` the ConfigMap, wait up to 3m |
GitHub에서 보기