For basic guidelines around contributing, see the CONTRIBUTING document.
- Go 1.25+ (see go.mod for the exact version)
- Docker (builds run inside containers by default), 28.0 or newer for the NetworkPolicy e2e suite
- Make
All dependencies are managed as Go modules. To build:
make kube-routerBy default, builds run inside Docker for consistency. To build locally:
BUILD_IN_DOCKER=false make kube-router(Adding BUILD_IN_DOCKER=false will work for most other steps that utilize docker as well)
make containerThis compiles kube-router and builds a Docker image tagged with the current branch and architecture.
make pushBy default this pushes to the official cloudnativelabs/kube-router Docker Hub repository which most users will likely
not have access to.
Push to a different registry by setting image options:
make container IMG_FQDN=quay.io IMG_NAMESPACE=youruser IMG_TAG=customSpecify the target architecture with GOARCH:
GOARCH=arm64 make kube-router
GOARCH=s390x make containerSupported architectures: amd64 (default), arm64, arm, s390x, ppc64le, riscv64.
kube-router has four levels of testing, each with its own Makefile target, and each entering the automatic pipelines at a different point:
| Level | Target | Scope | Runs in CI on |
|---|---|---|---|
| Unit | make test |
Pure Go, no root, no kernel, mocked interfaces | Every PR and push (ci.yml checks phase) |
| Privileged | make test-privileged |
Real netlink/netns calls, needs root | Not wired into CI yet; run locally |
| nftables integration | make test-integration |
Real nft + kernel, no Kubernetes API |
Every PR and push |
| NetworkPolicy e2e | make e2e-netpol |
Full kube-router deployed to a Kind cluster | Every PR and push, 4-way matrix |
Canonically, the inner development loop is make gofmt-fix && make lint && make test-pretty, and that's
also the first CI gate: the checks phase of ci.yml runs lint, unit tests, and a
binary build on every PR and push, and the container build and release phases only run after it passes.
The integration and e2e levels live in ci-e2e-netpol.yml, which
also triggers on every PR and push: integration tests run bare on the runner (BUILD_IN_DOCKER=false
under sudo), while the e2e job fans out across a {iptables, nftables} x {default-deny on, off} matrix.
The slow e2e lifecycle specs (controller restart, chain GC) are gated behind E2E_LONG, which CI sets only
on push, tag, and manual dispatch - never on PRs - so PR feedback stays fast while merges to master,
version branches, and release tags get the full regression coverage.
Currently the privileged tests are the one level you have to remember to run yourself when touching
netlink-heavy code (pkg/routes, for example), since CI doesn't run them anywhere yet.
If you want to run every host-local level in one shot, make test-all chains the unit, privileged, and
integration targets. The e2e suite is deliberately not part of it, since it stands up a Kind cluster, but
make e2e-netpol-all runs the whole e2e matrix (slow lifecycle specs included) on fresh clusters, as many
at once as your machine has room for. make prep-release runs both.
Run the full unit test suite:
make testOr with formatted output (easier to understand for humans, uses gotestsum):
make test-prettyA few tests (netlink, netns) need root to run, so they're excluded from make test and live in
*_privileged_test.go files behind the privileged build tag. They run in a privileged container by
default:
make test-privilegedThe nftables backend of the NetworkPolicy controller has integration tests
(pkg/controllers/netpol/npc_nftables_integration_test.go) that exercise it against the real nft binary
and kernel via netlink, with no Kubernetes API server involved. They're behind the integration build tag,
so make test skips them. By default they run in a privileged container that has nftables installed:
make test-integrationIf you'd rather run them bare, you'll need root (or CAP_NET_ADMIN), nft 1.0.1 or newer, and a 5.2+
kernel. The tests skip themselves gracefully when nftables isn't usable:
BUILD_IN_DOCKER=false make test-integrationEach test creates an isolated, uniquely named nftables table and deletes it via t.Cleanup, so no kernel
state leaks between tests, even on failure. Because they talk to the real kernel, be sure to run them on a
Linux machine (or let the default in-Docker mode handle that for you).
The NetworkPolicy e2e suite runs against a local dual-stack Kind cluster and only needs docker 28.0 or newer,
since it loads images with docker save --platform. The harness downloads its own copy of kind into _output/
instead of using whatever is on your PATH, and takes the kind version and node image from KIND_VERSION and
KIND_NODE_IMAGE in ci-e2e-netpol.yml, so local runs and CI always use the
same pair. A full run builds the image, creates the cluster, deploys kube-router, and runs the suite:
make e2e-netpol E2E_LONG=1 BACKEND=nftables DEFAULT_DENY=trueThe cluster has a control plane and two workers. The suite puts servers on one worker and clients on the other, so nearly every spec exercises cross-node traffic (including the pod routes kube-router learns over BGP), while a couple of specs repeat with the clients on the server's node so that the same-node bridge path stays covered.
The suite runs specs in parallel via the ginkgo CLI. In order of precedence, the knobs you'll want are:
BACKEND-iptables- Which netpol backend to deploy (iptablesornftables).DEFAULT_DENY-false- Deploy kube-router with--netpol-default-deny=true; the 10 default-deny specs skip without it.E2E_LONG-NO_DEFAULT_SET- Set to1to run the slow lifecycle specs (controller restart, chain GC).E2E_PROCS-4- Ginkgo parallel processes. The default matches free GitHub runners; set1for a fully serial run.E2E_FOCUS/E2E_SKIP-NO_DEFAULT_SET- Regexes passed to ginkgo--focus/--skipso you can run a single spec instead of the whole suite.
Because a given deployment is either default-deny or not, and either iptables or nftables, covering every spec
takes four runs with different combinations (any spec whose gate isn't met reports as skipped, not failed).
make e2e-netpol-all does all four for you with E2E_LONG=1, building the image once and giving each
combination its own fresh cluster:
make e2e-netpol-allA suite takes about two and a half minutes, and the matrix runs as many of them at once as your machine has
room for, writing each one's log to _output/e2e-matrix/. It sizes that from the CPUs and memory docker can use
and from fs.inotify.max_user_instances, which is usually the tightest of the three. Each suite's three kind
nodes hold about 90 inotify instances at peak, and they all count against root's limit, which a lot of distros
leave at 128. That's room for exactly one suite, so the matrix takes about 10 minutes. If you raise the limit the
way kind's known issues
suggest, all four can run together:
sudo sysctl fs.inotify.max_user_instances=512Set E2E_PARALLEL to override how many suites run at once.
When you're iterating on a failing spec (this works equally well for a human or an AI agent, since nothing in the loop needs sudo), keep the cluster alive and rerun just the tests:
# once, to stand the cluster up and keep it around
SKIP_CLEANUP=1 make e2e-netpol BACKEND=nftables DEFAULT_DENY=true
# rebuild + reload + restart kube-router, only needed if you changed kube-router code
hack/e2e-netpol.sh refresh
# rerun a single spec by name
E2E_LONG=1 BACKEND=nftables E2E_FOCUS='chain GC' hack/e2e-netpol.sh run-tests
# when you're done
hack/e2e-netpol.sh delete-clusterCheck formatting:
make gofmtAuto-fix formatting:
make gofmt-fixRun all linters (golangci-lint + markdownlint):
make lintWhen modifying interfaces that have associated mocks, regenerate them:
make gomoqsOr regenerate a specific mock:
make pkg/controllers/proxy/linux_networking_moq.goThis project uses Conventional Commits.
Every commit message should follow the format <type>(<scope>): <description>.
Common types: feat, fix, doc, test, build, chore, fact (refactor).
The following scope abbreviations are used for the main controllers:
- NSC -- Network Services Controller (
pkg/controllers/proxy/) - NRC -- Network Routes Controller (
pkg/controllers/routing/) - NPC -- Network Policy Controller (
pkg/controllers/netpol/)
Examples:
feat(gobgp): add kube_router_bgp_peer_info metric
fix(aws.go): load region before attempting to assume a role
test(NSC): add comprehensive TCPMSS unit tests
git checkout -b feature_x
# Make changes...
make clean
make gofmt-fix
makeRun make help for a full list of available targets.
See RELEASE.md for more information.
kube-router uses Go modules. See the upstream documentation for more information.