Documentation
¶
Overview ¶
Package ambienttest isolates a test binary from the machine it is running on.
Burrow's client-side state is ambient by design, in two layers that both end at the home directory: the kubeconfig resolves $KUBECONFIG else ~/.kube/config, and the local config resolves $BURROW_CONFIG else ~/.burrow/config, with the scoped agent credentials (ADR-0038) as siblings of that file. Any code that reaches one of those resolvers without an explicit path therefore reads whatever the person running the tests happens to have configured.
In a test that is not merely non-determinism. On a maintainer's laptop the ambient kubeconfig names live production clusters, so a test that picks it up is a test that could act on a real one; and a test that writes through localconfig.Path with nothing set edits the developer's own ~/.burrow/config. Both have happened (issue #486): four tests resolved pinned kube contexts against the ambient kubeconfig and so failed on a configured machine while passing on CI's bare runners, and a `burrow install` run outside a fixture wrote a handle into a real ~/.burrow/config and broke the CLI it was installed from.
Isolate closes the whole class for a package by moving $HOME, $KUBECONFIG and $BURROW_CONFIG to an empty directory for the lifetime of the test binary. Every package's tests then start from exactly the bare machine CI runs on, whatever the developer has set up, so `task check` gives the same answer in both places. A test that wants state still supplies it: t.Setenv over the top of the sentinel is unaffected, and that is how the fixtures in this repo already work.
What this does not catch ¶
- A path a test is handed EXPLICITLY. Isolation is of the defaults only; a test that passes a real kubeconfig path, or reads BURROW_TEST_KUBECONFIG (the gated integration suites), reaches exactly what it names. That is deliberate — those suites exist to talk to a cluster — and it is why the integration gate is its own variable rather than the ambient one.
- Any state that is not keyed on $HOME or those two variables: a hardcoded absolute path, a Docker socket, $KUBERNETES_SERVICE_HOST, a network service on localhost.
- A home-derived path a dependency froze into a package-level var at init, since package initialization runs before TestMain. client-go does exactly this with clientcmd.RecommendedHomeFile, which is why $KUBECONFIG is set here rather than unset — but the same trick in another dependency would go unnoticed, and moving $HOME would not move it.
- Code paths run outside a Go test binary. `burrow` invoked from a shell — an agent's sanity check, a script in CI — has no TestMain and is isolated by nothing here.
- A package that does not opt in. Isolate has to be called from a TestMain, and nothing forces a new package to add one; a package that resolves ambient state and has no TestMain is unguarded and looks no different from one that needs no guard.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Isolate ¶
Isolate points $HOME, $KUBECONFIG and $BURROW_CONFIG at a fresh empty directory, runs the package's tests against it, removes the directory, and exits with the tests' status. Call it as the package's whole TestMain:
func TestMain(m *testing.M) { ambienttest.Isolate(m) }
Any package whose tests can reach a kubeconfig or a localconfig path — directly, or through a command they exercise — belongs behind it.
Types ¶
This section is empty.