feat: migrate to controller-runtime v0.23 operator#6
Conversation
Bumps all k8s.io dependencies from v0.34.1 to v0.35.0 to prepare for controller-runtime v0.23.2 (unreleased, release-0.23 branch). Adds ginkgo/v2 as indirect dep; controller-runtime and cert-controller will be added when imported. All existing tests pass.
Generate ApplyConfiguration types for all CRDs (IPPool, NodeSlicePool, OverlappingRangeIPReservation) to enable Server-Side Apply where safe.
- Add kubebuilder validation markers (+kubebuilder:validation:MinLength) - Add shortNames: ipp, nsp, orip for kubectl convenience - Add printcolumn markers for Range, PodRef, SliceSize, etc. - Add +kubebuilder:subresource:status on NodeSlicePool - Fix NodeSlicePoolStatus godoc (was 'desired state', now 'observed state') - Add omitempty to NodeSlicePoolStatus.Allocations - Use runtime.NewSchemeBuilder() (controller-runtime convention) - Remove unused localSchemeBuilder and empty init() - Proper field-level godoc on IPAllocation/OverlappingRangeIPReservationSpec - Fix generate-code.sh to output CRDs to both doc/crds and helm chart - Regenerate CRD YAMLs and deepcopy with controller-gen v0.20
- cmd/operator/main.go: Cobra root command with scheme registration (clientgoscheme + whereaboutsv1alpha1 + nadv1) - cmd/operator/controller.go: leader-elected controller subcommand with health/readiness probes, Prometheus metrics, reconcile interval - cmd/operator/webhook.go: webhook subcommand with cert-controller rotation, readiness gating on cert bootstrap - internal/controller/setup.go: stub for reconciler registration - internal/webhook/setup.go: Runnable that waits for certs then registers webhooks, with ReadyCheck helper - internal/webhook/certrotator/: thin wrapper around cert-controller rotator.AddRotator with telekom CA config - controller-runtime v0.23.2 (unreleased, branch tip 4dbfa5c6) - cert-controller v0.15.0, cobra v1.10.2 - Vendor synced and verified
…ingRangeIPReservation
- operator-install.yaml: Deployment (2 replicas, leader election) + RBAC - webhook-install.yaml: Deployment + Service (443→9443) + RBAC - validatingwebhookconfiguration.yaml: 3 webhooks with matchConditions CEL bypass - daemonset-install.yaml: Remove ip-control-loop, cron ConfigMap; CNI-only RBAC - node-slice-controller.yaml: Add deprecation notice (superseded by operator)
Delete directories replaced by the new controller-runtime operator: - cmd/controlloop/ (old ip-control-loop entry point) - cmd/nodeslicecontroller/ (old node-slice-controller entry point) - pkg/controlloop/ (old pod watcher using informers+workqueues) - pkg/node-controller/ (old node-slice controller logic) - pkg/reconciler/ (old CronJob-based IP reconciler) Update hack/build-go.sh and Dockerfile to remove old binary targets (ip-control-loop, node-slice-controller). Run go mod tidy + vendor to prune unused dependencies (gocron, etc.).
…tecture - Replace old pkg/reconciler, pkg/controlloop, cmd/controlloop references - Document operator, reconcilers, webhooks, cert-controller - Update Ginkgo v1→v2 references - Update binary list: two binaries instead of three
There was a problem hiding this comment.
Pull request overview
This PR migrates the Whereabouts control-plane components from hand-rolled informers/workqueues to a proper controller-runtime v0.23 operator. The CNI binary is unchanged.
Changes:
- Replaces
ip-control-loopandnode-slice-controllerwith a singlewhereabouts-operatorbinary (Cobra subcommands:controllerandwebhook) - Adds three new controller-runtime reconcilers (
IPPoolReconciler,NodeSliceReconciler,OverlappingRangeReconciler) and validating webhooks for all three CRDs - Fixes several CNI storage layer bugs (per-retry context timeout, nil guard on leader elector, scoped
errperipRange, resetskipOverlappingRangeUpdate)
Reviewed changes
Copilot reviewed 71 out of 2554 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
cmd/operator/main.go, controller.go, webhook.go |
New Cobra-based operator entry point with controller and webhook subcommands |
internal/controller/ippool_controller.go |
New IPPoolReconciler replacing CronJob-based reconciler |
internal/controller/nodeslice_controller.go |
New NodeSliceReconciler replacing node-slice-controller |
internal/controller/overlappingrange_controller.go |
New OverlappingRangeReconciler for orphaned reservation cleanup |
internal/controller/*_test.go, internal/webhook/*_test.go |
Ginkgo v2 unit tests for new reconcilers and webhooks |
internal/webhook/ippool_webhook.go, nodeslicepool_webhook.go, overlappingrange_webhook.go |
Validating webhooks for all three CRDs |
internal/webhook/certrotator/certrotator.go |
Thin wrapper around cert-controller for TLS rotation |
internal/webhook/setup.go |
Runnable that gates webhook registration until certs are ready |
pkg/storage/kubernetes/ipam.go |
Bug fixes: per-retry context, nil guard on LE, scoped err, reset skipOverlappingRangeUpdate |
pkg/api/whereabouts.cni.cncf.io/v1alpha1/*_types.go |
Added kubebuilder shortNames, printColumns, field-level doc comments |
pkg/api/whereabouts.cni.cncf.io/v1alpha1/register.go |
Simplified SchemeBuilder initialization |
doc/crds/*.yaml, deployment/whereabouts-chart/crds/*.yaml |
Updated CRD manifests (shortNames, printColumns, status subresource) and new operator/webhook install manifests |
cmd/controlloop/, cmd/nodeslicecontroller/, pkg/controlloop/, pkg/reconciler/ |
Deleted old entry points and reconciliation code |
go.mod |
Bumps k8s.io/* to v0.35.0, adds controller-runtime v0.23, ginkgo v2, cobra, cert-controller |
hack/build-go.sh, Dockerfile |
Build system updated to produce whereabouts-operator instead of old binaries |
Various *_test.go |
Migrated Ginkgo v1 imports to v2 |
CLAUDE.md, .github/copilot-instructions.md, proposal.md |
Documentation updated to reflect new architecture |
Comments suppressed due to low confidence (1)
internal/controller/ippool_controller.go:1
- The CIDR validation in
validateIPPoolis skipped whenpool.Spec.Rangeis empty (""). An empty range is semantically invalid — an IPPool without a range cannot be used for allocation. The condition should be changed to reject an empty range, consistent with the+kubebuilder:validation:MinLength=1marker added to the type and howNodeSlicePoolValidatorhandles an empty range.
// Copyright 2025 Deutsche Telekom
Fixes 3 CRITICAL (VWC name mismatch, cmdDel swallows errors, multi-range dealloc abort), 11 HIGH (byteSliceSub borrow bug, signal-aware context, cmdDelFunc philosophy, cmdCheck no-op, defer-in-loop, Helm chart binary refs, deposed deadlock, ORIP rollback, webhook failurePolicy, version ldflags), 3 CNI spec (error wrapping, zero IPs check, flat config non-fatal), 8 Copilot PR findings (unused var, requestCtx threading, double blank line, error swallowing, immutability enforcement, error message accuracy). Adds NodeSliceReconciler tests (11 specs), webhook ValidateUpdate tests, fixes existing test for error convention.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 89 out of 2572 changed files in this pull request and generated 7 comments.
Comments suppressed due to low confidence (1)
pkg/storage/kubernetes/ipam.go:1
- The
resultchannel (buffered with capacity 2) is never read after thedeposedcase sends tostopMin the updated code. Looking at the fullIPManagementfunction in the context, the second goroutine reads fromstopMand then writes toresult, butresultis never consumed —wg.Wait()is used instead. Theresultchannel write in the leader-election goroutine will block if neither goroutine reads from it, potentially causing a goroutine leak. This was latent before the change, but the PR's refactoring of thedeposedcase (now sending tostopMinstead ofresult) means the flow should be carefully verified to ensureresultis still drained. Specifically, afterwg.Wait(),resultmay have an unread value if the leader-election goroutine completes — confirm that theresult <- (<-res)write in the second goroutine (which unblocks afterstopM) does not block due to capacity issues when the channel already has an entry.
package kubernetes
…it Helm RBAC, add finalizers - Replace internal addrToInt/intToAddr with netutils.BigForIP from k8s.io/utils/net in IPGetOffset, IPAddOffset, and DivideRangeBySize (pkg/iphelpers) - Migrate fakek8sclient.NewSimpleClientset to NewClientset (cmd tests) - Suppress SA1019 for generated whereabouts fake (lacks apply configs) - Remove deprecated result.Requeue assertions from controller tests - Split Helm chart RBAC: separate SA/ClusterRole/CRB for webhook and operator - Add IPPool finalizer for clean overlapping-reservation teardown - Add shared validation package (internal/validation) - Harden webhooks with typed admission.Validator[T] API - Add extnetip dependency for netip range operations - Rewrite iphelpers to use netip.Addr/Prefix throughout
CI fix: - e2e-setup-kind-cluster.sh: use operator-install.yaml instead of node-slice-controller.yaml (binary /node-slice-controller doesn't exist, only /whereabouts-operator). Update wait label to app=whereabouts-operator. Code hardening (copilot review): - Remove IPPool finalizer: IPPools are written by CNI binary out-of-band; if operator is down, finalizer blocks IPPool deletion indefinitely. OverlappingRangeReconciler handles ORIP cleanup asynchronously. - cmdDel: return nil on failure (CNI spec: DEL must be idempotent, returning errors blocks pod deletion) - cmdDel: fix off-by-one in retry loop (was doing 4 attempts not 3) - allocationKeyToIP: reject negative offsets before uint64 cast - denormalizeIPName: convert from recursive to iterative (bounded) - ipam.go rollback: use context.Background() instead of cancelled ctx - ipam.go: DRY inline rollback via rollbackCommitted() helper - ipam.go: set skipOverlappingRangeUpdate when dealloc finds no allocation (prevents nil IP passed to UpdateOverlappingRangeAllocation) - ipam.go: include attempt count in retry error message - ensureOwnerRef: return error instead of silently swallowing - config.go: restore defer jsonFile.Close() before ReadAll - daemonset-install.yaml: background token-watcher.sh + sleep infinity (foreground process blocks container readiness) - NodeSlicePool webhook: warn on spec.range/sliceSize changes - validation_test.go: document intentional multiple-slash leniency
…d go.mod pseudo-version comments
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 135 out of 2934 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
hack/test-go.sh:1
- This line is in the deleted file and was already removed, so this issue is fixed.
deployment/whereabouts-chart/templates/validatingwebhookconfiguration.yaml
Outdated
Show resolved
Hide resolved
- Fix retry() defaults: use :- (default) instead of := (assign) in both e2e setup scripts to avoid overwriting global RETRY_MAX/INTERVAL/TIMEOUT - Remove dead TIMEOUT_K8="5000s" initial assignment (immediately shadowed) - Fix incomplete block comment in validation_extended_test.go - Add explicit zapcore.InfoLevel for info case, zapcore.ErrorLevel with Development=false for error case, and new warn log level support - Extract duplicated CEL matchConditions into whereabouts.webhookBypassCondition named Helm template
deployment/whereabouts-chart/crds/whereabouts.cni.cncf.io_nodeslicepools.yaml
Show resolved
Hide resolved
- make PatchHelper DeepCopy type assertions safe (no panic) - replace panic in controller flag setup with utilruntime.Must - fix verify-codegen diff result accumulation and robust cp semantics - add operator nodeSelector/tolerations override support in Helm values/template - clarify cert volume startup/readiness comment and cni-args hostPath purpose - improve validation test naming/comment clarity for whitespace cases - drain FakeRecorder events in overlapping range controller tests - normalize warning text punctuation in IPPool webhook
deployment/whereabouts-chart/templates/operator-clusterrole.yaml
Outdated
Show resolved
Hide resolved
- clarify jsonEqual fallback semantics for malformed second blob - rename slice-size test case for no-leading-slash clarity - update daemonset cni-args comment to current install-cni binary flow - include warn in --log-level flag description - scope operator secret permissions to webhook secret resourceName
| before := getCounterValue(webhookValidationTotal.WithLabelValues("ippool", "create", "allowed")) | ||
| recordValidation("ippool", "create", nil) | ||
| after := getCounterValue(webhookValidationTotal.WithLabelValues("ippool", "create", "allowed")) | ||
| Expect(after - before).To(Equal(float64(1))) |
There was a problem hiding this comment.
The Prometheus CounterVec used here is a package-level variable registered in init(). Because these tests share the same counter across test cases (metrics are not reset between It blocks), a test that reads the counter before and after an operation is valid only if no other concurrent test increments the same label combination. However, the getCounterValue helper calls WithLabelValues, which also increments the label-value pair's internal label set if it didn't exist yet — this is fine — but test isolation is not guaranteed if tests run in parallel and hit the same ("ippool", "create", "allowed") label vector. More concretely, getCounterValue calls Write on the result of WithLabelValues, not on a snapshot — each call to WithLabelValues returns (or creates) the counter for those labels, so the before and after reads from separate WithLabelValues() calls are consistent as long as no parallel test races on the same labels. The real risk is that any test in the same suite (or even a real webhook call during test) could increment the counter between the before and after reads. Consider using a fresh prometheus.NewRegistry() per test and registering a new CounterVec to isolate test state.
| {name: "negative with slash", input: "/-1", want: 0, wantErr: true}, | ||
| {name: "very large number", input: "9999", want: 0, wantErr: true}, | ||
| {name: "float", input: "24.5", want: 0, wantErr: true}, | ||
| {name: "hex", input: "0x18", want: 0, wantErr: true}, |
There was a problem hiding this comment.
strconv.Atoi in Go does not interpret leading-zero strings as octal — it always parses in base 10. So "024" is parsed as 24, which is within the valid range [1, 128], and the test correctly expects no error and a value of 24. However, the test case name "leading zero" with wantErr: false could be misleading to future contributors who might expect octal parsing. Consider adding a comment to the test case to clarify that Go's strconv.Atoi treats "024" as decimal 24, not octal 20, and that this is the intended behavior. This is a documentation concern rather than a code defect, but it's worth noting since the existing comment on line 97 for "whitespace padded" already returns an error, and having adjacent cases with different error expectations and no explanation could be confusing.
| {name: "hex", input: "0x18", want: 0, wantErr: true}, | |
| {name: "hex", input: "0x18", want: 0, wantErr: true}, | |
| // Go's strconv.Atoi parses "024" as decimal 24 (base 10), not octal 20; this is the intended behavior. |
| - apiGroups: | ||
| - "" | ||
| resources: | ||
| - secrets | ||
| verbs: | ||
| - create | ||
| - get | ||
| - list | ||
| - patch | ||
| - update | ||
| - watch |
There was a problem hiding this comment.
Same issue as in the Helm operator-clusterrole.yaml: secrets access here has no resourceNames restriction at all, granting the operator broad read/write access to all Secrets in the cluster. The create verb legitimately cannot use resourceNames, but get, list, watch, update, and patch should be restricted to the specific webhook cert Secret (e.g., whereabouts-webhook-cert). Add a second rule with resourceNames: [whereabouts-webhook-cert] for the non-create verbs to follow least-privilege.
| cp -a "${TMP_DIFFROOT_PKG}/." "${DIFFROOT_PKG}/" | ||
|
|
||
| echo "diffing api/ against freshly generated codegen" | ||
| if ! diff -Naupr "${DIFFROOT_API}" "${TMP_DIFFROOT_API}"; then | ||
| ret=1 | ||
| fi | ||
| cp -a "${TMP_DIFFROOT_API}/." "${DIFFROOT_API}/" |
There was a problem hiding this comment.
The cp -a restore steps on lines 30 and 36 run unconditionally, even when diff detected changes (i.e., the code is out of date). This overwrites the freshly-generated code with the original snapshot that was saved before running update-codegen.sh, undoing the regenerated output. The copies back should be conditional on the diff having succeeded (no changes), or removed entirely — the original intent of a verify script is typically to leave the working tree clean and just report the exit code, not to restore the pre-existing content.
| cp -a "${TMP_DIFFROOT_PKG}/." "${DIFFROOT_PKG}/" | |
| echo "diffing api/ against freshly generated codegen" | |
| if ! diff -Naupr "${DIFFROOT_API}" "${TMP_DIFFROOT_API}"; then | |
| ret=1 | |
| fi | |
| cp -a "${TMP_DIFFROOT_API}/." "${DIFFROOT_API}/" | |
| echo "diffing api/ against freshly generated codegen" | |
| if ! diff -Naupr "${DIFFROOT_API}" "${TMP_DIFFROOT_API}"; then | |
| ret=1 | |
| fi |
Migrate to controller-runtime v0.23 Operator
Summary
Replaces the hand-rolled informers/workqueues reconciliation logic (
ip-control-loop,node-slice-controller,pkg/reconciler) with a proper controller-runtime v0.23 operator. Additionally rewrites the CNI install script as a Go binary, introduces validating webhooks, Prometheus metrics, comprehensive test coverage, Helm chart improvements, security hardening, and extensive documentation.Scope: 181 files changed (excl. vendor/generated), +15,667 / −6,879 lines across 36 commits.
Changes
1. Dependencies & Tooling
open-policy-agent/cert-controller) for automated webhook TLS certificate rotation. "github.com/onsi/ginkgo/v2")--with-applyconfigfor Server-Side Apply (SSA) typed configurations.golangci.yml, 234+ lines) with 50+ enabled linters includinggocritic,nolintlint,revive,gosec,errorlintgo.mod/go.sum2. New Operator Binary (
cmd/operator/)Cobra-based entry point (
whereabouts-operator) with a singlecontrollersubcommand::8080via controller-runtime:8081--metrics-bind-address,--health-probe-bind-address,--leader-elect,--leader-elect-lease-duration,--leader-elect-renew-deadline,--leader-elect-retry-period,--webhook-port,--webhook-cert-dirFiles:
cmd/operator/main.go,cmd/operator/controller.go3. Reconcilers (
internal/controller/)DisruptionTargeteviction condition, pending pods (RequeueAfter), IPv6 normalization (net.IP.Equal), malformed pod annotations. SetsStalecondition on IPPool when orphans are removed.spec.configJSON.podRefagainst live pods in the cluster.Supporting code:
patchhelper.go: Cluster-API-inspiredPatchHelper— computes minimal diffs (spec via MergePatch, status via SSA) to reduce API server load. SHA-256 content hashing for change detection.conditions.go: Typed conditions (Stale,Ready) with severity levels, following Cluster-API condition conventions.metrics.go: Prometheus counters/gauges for reconciliation operations (allocations cleaned, errors, pool sizes, reconcile duration).setup.go: Wires all reconcilers + webhooks into the controller-runtime manager with readiness/liveness checks.4. Validating Webhooks (
internal/webhook/)Uses controller-runtime v0.23 typed
admission.Validator[T]API:podRefmust be"namespace/name"formatSliceSizemust be string-encoded integer 1–128podRefin spec must be"namespace/name"formatValidatingWebhookConfigurationincludes CEL expressions to skip validation for the CNI ServiceAccount (performance optimization for hot-path ADD/DEL operations)internal/webhook/certrotator/wrapscert-controllerfor automatic TLS certificate management with configurable namespace, secret name, and webhook name5. Shared Validation Library (
internal/validation/)Extracted common validation logic used by both webhooks and future admission policies:
ValidateCIDR()— validates CIDR notation usingnet.ParseCIDRValidatePodRef()— validates"namespace/name"format with non-empty componentsValidateSliceSize()— validates string-encoded integer in range [1, 128]6. Install-CNI Go Binary (
cmd/install-cni/)Replaces the shell scripts (
script/install-cni.sh,script/lib.sh,script/token-watcher.sh) with a pure Go implementation:whereaboutsto host CNI bin directory with temp-file + atomic renamewhereabouts.confto configured pathfsnotify, regenerates kubeconfig on changeFiles:
cmd/install-cni/main.go(410 lines),cmd/install-cni/main_test.go(948 lines, 39 test cases)7. Bug Fixes (CNI Storage Layer)
Critical fixes in
pkg/storage/kubernetes/ipam.go:context.WithTimeoutinsideRETRYLOOPerrperipRangeloop iterationskipOverlappingRangeUpdateperipRange8. IP Math & Helpers (
pkg/iphelpers/)math/big.Int— eliminates overflow for large IPv6 subnetsk8s.io/utils/netfor range parsing where applicable9. CRD Types & Conventions
New API types in
api/whereabouts.cni.cncf.io/v1alpha1/(kubebuilder v4 layout):IPPool,NodeSlicePool,OverlappingRangeIPReservationwith full kubebuilder markers+kubebuilder:shortNamemarkers:ipp,nsp,orip+kubebuilder:printcolumnmarkers:Range,Ageconditions.go) following Cluster-API patternsPROJECTfile for kubebuilder scaffolding metadataDeleted old types in
pkg/api/— superseded by the newapi/package.10. Kubebuilder Configuration (
config/)Full kubebuilder v4 config directory:
11. Helm Chart Updates (
deployment/whereabouts-chart/)operator-clusterrole.yaml,operator-clusterrolebinding.yaml,operator-serviceaccount.yaml,operator-pdb.yaml,validatingwebhookconfiguration.yaml,webhook-service.yamlcluster_role.yaml(split CNI vs operator RBAC),daemonset.yaml(slimmed to CNI-only),node-slice-controller.yaml(deprecated, superseded by operator)configmap.yaml(ip-control-loop cron config — no longer needed)operator.image,operator.replicas,operator.resources,webhook.*configuration12. Prometheus Metrics (
internal/controller/metrics.go,internal/webhook/metrics.go)Controller metrics:
whereabouts_ippool_allocations_cleaned_totalwhereabouts_ippool_reconcile_errors_totalwhereabouts_ippool_sizewhereabouts_ippool_allocationswhereabouts_ippool_reconcile_duration_secondsWebhook metrics:
whereabouts_webhook_requests_totalwhereabouts_webhook_denied_total13. Security Hardening
runAsNonRoot: true,runAsUser: 65532(nonroot),readOnlyRootFilesystem: trueallowPrivilegeEscalation: false, dropped all capabilities14. Build System & CI
make build,make docker-build,make test,make test-skip-static,make generate,make manifests,make lint,make lint-fix,make kindwhereabouts(CNI) andwhereabouts-operatorbinaries, plusinstall-cnitest.yml(staticcheck + golangci-lint),build.yml, release workflows for new binary targetshack/build-go.sh,hack/test-go.sh,hack/generate-code.sh,hack/install-kubebuilder-tools.sh,hack/update-deps.sh(functionality moved to Makefile targets)15. Documentation
doc/architecture.mddoc/metrics.mddoc/proposals/operator-controller-runtime-migration.mddoc/extended-configuration.mddoc/developer_notes.mdCHANGELOG.mdCONTRIBUTING.mdREADME.mdCLAUDE.md.github/copilot-instructions.md16. Existing Package Improvements
pkg/config/pkg/logging/pkg/types/pkg/version/pkg/storage/kubernetes/pkg/allocate/%w→%sin non-wrapping format contexts (lint compliance)17. E2E Test Updates
e2e/util/util.gowith proper Go template →json.Marshalfor NAD generatione2e/client/) for new API typeshack/e2e-setup-kind-cluster.sh: Updated for operator deployment18. Removed Old Code
cmd/controlloop/cmd/operator/withcontrollersubcommandcmd/nodeslicecontroller/cmd/operator/(NodeSliceReconciler)pkg/controlloop/(430+ lines)internal/controller/ippool_controller.gopkg/node-controller/(623+ lines)internal/controller/nodeslice_controller.gopkg/reconciler/(1,460+ lines)internal/controller/reconcilerspkg/api/(old CRD types)api/whereabouts.cni.cncf.io/v1alpha1/script/install-cni.sh,script/lib.sh,script/token-watcher.shcmd/install-cni/main.godoc/crds/*.yaml(old static manifests)config/kubebuilder manifests + Helm charthack/build-go.sh,hack/test-go.sh, etc.19. Upstream Feature Implementations
Implements, tests, and documents several upstream feature requests and bug fixes:
pkg/iphelpers/updated:FirstUsableIP,LastUsableIP,HasUsableIPs,CountUsableIPsnow support /32 (1 IP), /31 (2 IPs per RFC 3021), /128, /127. 10 new unit tests + 4 e2e tests (single IP, point-to-point, IPv6).exclude_gateway(bool) config field. When enabled withgatewayset, auto-adds gateway/32 or /128 to each range's OmitRanges. 5 new config tests + 1 e2e test.pod.DeletionTimestamp != nil— treats terminating pods during node shutdown as orphaned. 2 new controller tests.optimistic_ipam(bool) config field. When enabled, CNI ADD/DEL bypass leader election and callIPManagementKubernetesUpdatedirectly, relying on optimistic concurrency. 2 config tests + 3 e2e tests.whereabouts.cni.cncf.io/preferred-ip. During allocation, attempts the preferred IP first; falls back to lowest-available if taken or excluded. Supports IPv4/IPv6. 6 allocate tests + 3 e2e tests.enable_l3(bool) config field. When enabled, uses network address and broadcast address for allocation (no.0skip, no broadcast skip). 9 new tests (4 allocate + 4 config + 1 allocate) + 1 e2e test.Total new tests for upstream features: 42 unit tests + 12 e2e tests.
All new features are documented in
doc/extended-configuration.mdwith configuration examples.20. Upstream Bug Coverage Tests
16 new unit tests covering confirmed upstream bugs to prevent regression:
GetExcludeSubnetsForNodeSlice)net.IP.Equalcomparison in allocationsnetwork_nameTest Coverage
~470 test cases across new and updated test files:
ippool_controller_test.go,controller_extended_test.gonodeslice_controller_test.gooverlappingrange_controller_test.gometrics_test.goippool_webhook_test.gonodeslicepool_webhook_test.gooverlappingrange_webhook_test.gometrics_test.gocertrotator_test.govalidation_test.go,validation_extended_test.gomain_test.gowhereabouts/main_test.goiphelpers_extended_test.goconfig_extended_test.go,config_test.gotypes_test.gologging_extended_test.goallocate_test.goextended_test.goversion_test.goe2e_test.goTests use controller-runtime
envtest(with real etcd + API server) for reconciler and webhook tests, andfake.NewClientsetfor unit tests.Binaries
whereabouts(CNI plugin)whereabouts(CNI plugin) — unchangedip-control-loop(reconciler)whereabouts-operator controllernode-slice-controller(Fast IPAM)whereabouts-operator controllerwhereabouts-operator controller(includes webhook server)install-cni.sh(shell script)install-cni(Go binary)Migration Path
whereabouts-operator controller) — handles reconciliation + webhooksip-control-loopandnode-slice-controllerDeployments/CronJobsValidatingWebhookConfigurationfor CRD validation