{"id":50392812,"url":"https://github.com/mwiget/kindbnkctl","last_synced_at":"2026-05-30T19:01:29.095Z","repository":{"id":359087927,"uuid":"1244440306","full_name":"mwiget/kindbnkctl","owner":"mwiget","description":"Single-binary CLI that deploys F5 BIG-IP Next for Kubernetes (BNK) 2.3.0 on a 2-node kind cluster with TMM in demo mode.","archived":false,"fork":false,"pushed_at":"2026-05-20T13:23:09.000Z","size":188,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-20T13:47:37.907Z","etag":null,"topics":["big-ip-next","bnk","bnk-forge","cli","demo-mode","f5","golang","kind","kubernetes","tmm"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mwiget.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-20T09:10:04.000Z","updated_at":"2026-05-20T13:23:44.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mwiget/kindbnkctl","commit_stats":null,"previous_names":["mwiget/kindbnkctl"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/mwiget/kindbnkctl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwiget%2Fkindbnkctl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwiget%2Fkindbnkctl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwiget%2Fkindbnkctl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwiget%2Fkindbnkctl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mwiget","download_url":"https://codeload.github.com/mwiget/kindbnkctl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mwiget%2Fkindbnkctl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33705207,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["big-ip-next","bnk","bnk-forge","cli","demo-mode","f5","golang","kind","kubernetes","tmm"],"created_at":"2026-05-30T19:01:28.405Z","updated_at":"2026-05-30T19:01:29.087Z","avatar_url":"https://github.com/mwiget.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# kindbnkctl\n\n![BNK](https://img.shields.io/badge/BNK-2.3.0-0a3a5c)\n![Kubernetes](https://img.shields.io/badge/Kubernetes-1.30.8-326ce5?logo=kubernetes\u0026logoColor=white)\n![kind](https://img.shields.io/badge/kind-v0.26%2B-1f6feb)\n![Go](https://img.shields.io/github/go-mod/go-version/mwiget/kindbnkctl)\n![License](https://img.shields.io/github/license/mwiget/kindbnkctl)\n![Last commit](https://img.shields.io/github/last-commit/mwiget/kindbnkctl)\n[![Release](https://img.shields.io/github/v/release/mwiget/kindbnkctl?label=download)](https://github.com/mwiget/kindbnkctl/releases/latest)\n\nSingle-binary CLI that deploys F5 BIG-IP Next for Kubernetes (BNK) 2.3.0\non a two-node [kind](https://kind.sigs.k8s.io/) cluster — one combined\ncontrol-plane + worker, one worker dedicated to TMM running in demo\nmode (virtio inside the pod netns; no DPU, no SR-IOV, no Multus).\n\nAimed at low-spec corporate laptops where dpubnkctl's bare-metal +\nDPU pipeline is overkill. Same poc.yaml-driven, resume-safe shape;\nmuch shorter pipeline.\n\n## What this tool does\n\nDrives a BNK deployment in three phases:\n\n1. **cluster up** — `kind create cluster`, install Calico (acts as a\n   simulator for larger SR-IOV deployments), create internal + external\n   docker bridge networks and attach both to every kind node container,\n   label the worker for TMM, fetch kubeconfig.\n2. **deploy prereqs** — namespaces, FAR pull secret, cert-manager.\n3. **deploy flo + cne** — FLO from the release-manifest chart at\n   `repo.f5.com`, License CR with the operator's JWT, CNEInstance with\n   `advanced.demoMode.enabled: true` and TMM pinned via `nodeSelector:\n   app=f5-tmm`.\n\nSymmetric **`destroy`** unwinds it: bnk-forge unregister → `kind\ndelete cluster` → docker network rm.\n\n## Pinned versions\n\n| Component | Version |\n|---|---|\n| BNK | 2.3.0 |\n| CNE release manifest | 2.3.0-3.2598.3-0.0.170 |\n| Kubernetes (kind node image) | 1.30.8 (kind v0.26 ships this) |\n| Calico | v3.28.2 |\n| cert-manager | v1.16.2 |\n| FLO chart | resolved at deploy time from the release manifest |\n\n## Minimum host resources\n\n|                          | Cluster floor       | With bnk-forge      | Free disk |\n|--------------------------|---------------------|---------------------|-----------|\n| Linux (host docker)      | **12 cores · 24 GB**| **14 cores · 26 GB**| **~10 GB**|\n| macOS / Windows Docker Desktop | **12 CPUs · 24 GB allocated to the VM** | **14 CPUs · 26 GB** | **~10 GB** |\n\n(Configured in Docker Desktop → Settings → Resources. Rancher Desktop /\nColima use the same numbers — same underlying Linux VM model.)\n\n### Why so much for a \"demo\"\n\nIn the two-node shape every F5 pod lands on `demo-worker` —\n`demo-control-plane` holds the standard `node-role.kubernetes.io/control-plane`\ntaint and the BNK 2.3.0 charts don't tolerate it. The Kubernetes\nscheduler admits pods against their `requests`, not their actual\nRSS, and the chart reserves heavily:\n\n| Pod (on the worker)                 | Memory request | CPU request |\n|---|---|---|\n| f5-tmm                              | 9204 Mi        | 4100m       |\n| f5-cne-controller (4 containers)    | 1600 Mi        | 1080m       |\n| f5-downloader                       | 1000 Mi        | 500m        |\n| f5-spk-csrc                         | 1024 Mi        | 500m        |\n| f5-crdconversion                    | 1024 Mi        | 500m        |\n| f5-dssm-db / -sentinel              | 1152 Mi each   | 600m each   |\n| f5-observer / -receiver             | 500 Mi each    | 512m / 1c   |\n| f5-observer-operator                | 256 Mi         | 250m        |\n| f5-spk-cwc                          | 640 Mi         | 556m        |\n| f5-afm                              | 512 Mi         | 500m        |\n| f5-ipam-ctlr / f5-rabbit            | 512 Mi each    | 100m / 300m |\n| otel-collector / flo                | 256 Mi each    | 500m / 250m |\n| **Sum on the worker**               | **~20 Gi**     | **~12 cores**|\n\nEach kind node container reports the docker daemon's full memory and\nCPU as its allocatable — kind does not partition. So the worker won't\nschedule the full stack until the daemon (or Docker Desktop VM) is\nsized above the request total, plus ~4 Gi headroom for the\ncontrol-plane pods sharing the same VM and kernel overhead in both\nnode containers.\n\n### What the cluster actually uses\n\nSteady-state, after `CNEInstance.Available=True`, the cluster's real\nRSS is much smaller than the reservation:\n\n| Component                        | Working set    | CPU       |\n|---|---|---|\n| TMM pod (worker)                 | ~1.2 GB        | ~100m     |\n| kube-apiserver                   | ~900 MB        | ~150m     |\n| All other F5 pods (~20)          | ~1.0 GB        | ~470m     |\n| Calico + coredns + etcd + kube-* | ~700 MB        | ~150m     |\n| Kernel / runtime per node        | ~500 MB × 2    | —         |\n| **Total cluster RSS**            | **~4.5 GB pod + ~1 GB overhead** | **~900m steady, ~1.2c during TMM init** |\n\nSo the cluster lives inside ~6 GB of real memory once it's up — it\njust won't *get there* without first satisfying the K8s scheduler's\n~20 Gi worker reservation.\n\n### Symptom when the floor is too low\n\n`kindbnkctl e2e` reaches `[5/5] deploy-cne` and stalls. Six pods\n(`f5-dssm-db-0`, `f5-dssm-sentinel-0`, `f5-spk-cwc-*`,\n`f5-observer-{0,receiver-0,operator-*}`) sit `Pending` with\n`FailedScheduling: Insufficient memory` on the worker, and\n`CNEInstance.Available` never goes true. Quick check:\n\n```bash\nkubectl --kubeconfig \u003cpoc\u003e/artifacts/kubeconfig describe node demo-worker \\\n  | grep -E \"Allocatable:|Allocated resources:\" -A6\n```\n\nIf `memory Requests` is ≥99% of `Allocatable`, raise the docker\ndaemon allocation and re-run from the failed phase (`kindbnkctl\ndeploy cne …`) — it's idempotent.\n\n### Disk\n\n1.4 GB (kindest/node image) + ~2.4 GB (F5 container images pulled to\nthe worker) + ~0.5 GB (cert-manager, alpine/k8s tooling, manifests) +\n~5 GB headroom for kind cluster state and logs.\n\n`kindbnkctl doctor` reports the host's actual CPU count and fails\nwhen it falls below `MinBaseline`. Override the constants in\n`internal/version/version.go` if you've tuned chart values to reduce\nrequests.\n\n## bnk-forge integration\n\nIf a local [bnk-forge](https://github.com/sp-prod-field/bnk-forge)\nclone exists at `~/git/bnk-forge` (or `$KINDBNKCTL_BNK_FORGE_PATH`)\nwhen `kindbnkctl init` runs, the new PoC's `bnk_forge:` block is\npre-filled and enabled. On `cluster up`, kindbnkctl best-effort\nregisters the kind cluster with bnk-forge — if the local bnk-forge\nstack isn't running, the auto-hook logs a clean skip and continues.\n\n**`kindbnkctl` never installs or starts bnk-forge for you.** If it's\nconfigured but not running, bring it up manually (`cd ~/git/bnk-forge\n\u0026\u0026 make deploy`) then `kindbnkctl bnk-forge launch` to register\nafter the fact.\n\n## Download\n\nPrebuilt binaries for each tagged release are on the\n[**GitHub Releases page**](https://github.com/mwiget/kindbnkctl/releases/latest) —\nthree archives per release plus a `checksums.txt`:\n\n| Platform | Archive |\n|---|---|\n| Linux (Intel/AMD) | `kindbnkctl_\u003cversion\u003e_linux_amd64.tar.gz` |\n| Linux (ARM64) | `kindbnkctl_\u003cversion\u003e_linux_arm64.tar.gz` |\n| macOS (Apple Silicon) | `kindbnkctl_\u003cversion\u003e_darwin_arm64.tar.gz` |\n\nOne-liner install (Linux amd64; swap the suffix for your platform):\n\n```bash\nVERSION=$(curl -fsSL https://api.github.com/repos/mwiget/kindbnkctl/releases/latest | sed -n 's/.*\"tag_name\": *\"\\([^\"]*\\)\".*/\\1/p')\ncurl -fsSL \"https://github.com/mwiget/kindbnkctl/releases/download/${VERSION}/kindbnkctl_${VERSION#v}_linux_amd64.tar.gz\" \\\n  | tar -xz -C /tmp kindbnkctl\nsudo install -m 0755 /tmp/kindbnkctl /usr/local/bin/kindbnkctl\nkindbnkctl version\n```\n\nReleases follow `v\u003cbnk-version\u003e-\u003cn\u003e` — e.g. `v2.3.0-1`, `v2.3.0-2`.\nThe `2.3.0` prefix tracks the pinned BNK release; the `-n` suffix\nincrements per kindbnkctl-only iteration.\n\nOr build from source — see [Repo layout](#repo-layout-the-binary-itself)\nbelow.\n\n## Requirements\n\n| Tool | Why |\n|---|---|\n| **Docker** or **Podman** | kind runs Kubernetes nodes as containers; FLO + cert-gen also shell into an `alpine/k8s:1.31.5` container at deploy time |\n| **kind** | cluster bring-up |\n| **kubectl** | cluster reads/writes (apply, wait, label) |\n| **helm** | cert-manager + FLO install, release-manifest pull |\n| **git** *(optional)* | `init` git-inits the PoC repo (skippable with `--no-git`) |\n\nVerify after install:\n\n```bash\nkindbnkctl doctor\n```\n\nWhat customers supply themselves, dropped into `keys/` of the PoC repo\n(delivered through F5's normal channels):\n\n- FAR tarball — image-pull credentials for `repo.f5.com`\n- JWT — TEEM activation token\n\n## Quick start\n\n```bash\n# 1. Create a fresh PoC repo. Auto-detects ~/git/bnk-forge.\nkindbnkctl init demo --customer \"Acme\"\ncd demo\n\n# 2. Drop the operator-supplied files into keys/.\ncp /path/to/f5-far-auth-key.tgz keys/\ncp /path/to/license.jwt          keys/.jwt\n\n# 3. Confirm poc.yaml is clean.\nkindbnkctl validate\n\n# 4. Run the pipeline (~10–20 min with a warm docker cache).\nkindbnkctl e2e --yolo --confirm-cluster demo\n\n# 5. Tear down (symmetric):\nkindbnkctl destroy --yolo --confirm-cluster demo\n```\n\n## Per-phase invocation\n\nIf you'd rather drive the phases one at a time for diagnostics:\n\n```bash\nkindbnkctl cluster up      --yolo --confirm-cluster demo\nkindbnkctl deploy prereqs  --yolo --confirm-deploy  demo\nkindbnkctl deploy flo      --yolo --confirm-deploy  demo\nkindbnkctl deploy cne      --yolo --confirm-deploy  demo\n```\n\nEvery phase is idempotent and gated by `--yolo` plus a typo-guard.\n\n## Repo layout (the binary itself)\n\n```\ncmd/kindbnkctl/        main entrypoint\ninternal/cli/          cobra commands (init, validate, doctor, cluster,\n                       deploy, destroy, e2e, bnk-forge, version)\ninternal/poc/          poc.yaml schema + I/O\ninternal/cluster/      kind + docker wrappers\ninternal/deploy/       cert-manager, FLO, License CR, CWC cert-gen\ninternal/bnkforge/     bnk-forge HTTP client (copy-fork of dpubnkctl)\ninternal/embedded/     go:embed AGENTS.md, CLAUDE.md, templates/\ninternal/version/      build-stamped + BNK 2.3.0 pins + min-spec floor\n```\n\n## Repo layout (a PoC created by `kindbnkctl init`)\n\n```\npoc.yaml         declarative state — source of truth\nAGENTS.md        operator + agent guide\nCLAUDE.md        @AGENTS.md include\njournal/         append-only markdown log\nartifacts/       rendered kind.yaml, kubeconfig, helm values, CWC certs\nkeys/            gitignored — FAR tgz + JWT live here\n.gitignore       excludes all secret material\n```\n\n## Network topology\n\nThe shape after a full `e2e` plus `bgp-peer-frr` (everything the\nother scenarios build on). One docker bridge on the host (the\nkind cluster's own); two kind node containers; a Multus-managed\nLinux bridge inside the worker carries BGP traffic between TMM\nand the FRR helper pod. Scenario backends are plain Calico pods\n— the Gateway IPs they serve get plumbed via BGP, so the\nbackends don't need to be on the NAD themselves.\n\n```\n+----------------------------------------------------------------------------+\n| HOST  (Linux or macOS Docker Desktop)                                      |\n|                                                                            |\n|   docker bridge: kind  172.18.0.0/16                                       |\n|       |                                                                    |\n+-------|--------------------------------------------------------------------+\n        |\n+-------+--------------+   +-------------------------------------------------+\n| smoke-control-plane  |   | smoke-worker  (kind node container)             |\n| (kind node container)|   | label: app=f5-tmm                               |\n| eth0 172.18.0.2      |   | eth0 172.18.0.3                                 |\n|                      |   |                                                 |\n| pods:                |   |  +-------------------------------------------+  |\n|   Calico  Multus     |   |  | TMM pod        ns=default  app=f5-tmm     |  |\n|   FLO     CWC        |   |  | 6 containers:                             |  |\n|   cert-manager       |   |  |   f5-tmm                                  |  |\n|   ...                |   |  |   f5-tmm-routing  (= ZeBOS)               |  |\n+----------------------+   |  |   debug  blobd  toda-observer  ipsec      |  |\n                           |  | Interfaces:                               |  |\n                           |  |   net1   192.168.99.X/24  Multus NAD      |  |\n                           |  |          (BGP source, no eth0 hook)       |  |\n                           |  |   eth0   10.244.x.x/32   Calico (kube-API |  |\n                           |  |          + ZeBOS bgpd kernel listener)    |  |\n                           |  |   xeth0  no IP    Calico veth #2, TMM     |  |\n                           |  |          userspace raw frames             |  |\n                           |  |   tmm    169.254.0.253/24  virtio, pod    |  |\n                           |  |          default route to TMM DP          |  |\n                           |  |   tunl0  DOWN     Calico IPIP placeholder |  |\n                           |  +-------------------------------------------+  |\n                           |  +-------------------------------------------+  |\n                           |  | FRR pod        ns=scn-bgp  app=scn-frr    |  |\n                           |  | 1 container:   frr (zebra + bgpd)         |  |\n                           |  |   net1   192.168.99.Y/24  Multus NAD      |  |\n                           |  |          (BGP peer + curl source)         |  |\n                           |  |   eth0   10.244.x.x/32   Calico           |  |\n                           |  +-------------------------------------------+  |\n                           |             ^                                   |\n                           |             |  BGP TCP/179 + scenario curls     |\n                           |             v  over br-bnk-bgp, L2              |\n                           |  +========================================+     |\n                           |  ||  br-bnk-bgp   Linux bridge in node    ||    |\n                           |  ||  netns, created by the bridge-CNI     ||    |\n                           |  ||  plugin via NAD name=bnk-bgp ;        ||    |\n                           |  ||  host-local IPAM 192.168.99.20-250    ||    |\n                           |  ||  on /24                               ||    |\n                           |  +========================================+     |\n                           |                                                 |\n                           |  +-------------------------------------------+  |\n                           |  | scenario backends  (plain Calico pods —   |  |\n                           |  | no NAD attachment, no node pinning)       |  |\n                           |  |   nginx        ns=scn-httproute-e2e       |  |\n                           |  |   pp-backend   ns=scn-proxy               |  |\n                           |  |   ext-backend  ns=scn-extres   (Pool      |  |\n                           |  |     member references its Calico podIP)   |  |\n                           |  +-------------------------------------------+  |\n                           |                                                 |\n                           |  DaemonSets in node netns:                      |\n                           |    Calico-node     Multus thick                 |\n                           |    f5-coremond (if how-to #4 ran)               |\n                           +-------------------------------------------------+\n\nBGP session:\n  TMM/ZeBOS  AS 65000  =======  net1 \u003c-\u003e net1, L2 over br-bnk-bgp  =======\u003e  FRR  AS 65001\n                                                                             listen-range\n                                                                             192.168.99.0/24\n                                                                             peer-group\n                                                                             from-tmm\n\n  TMM ZeBOS advertises (redistribute kernel, at router-bgp scope —\n  silently dropped if placed inside address-family ipv4):\n    192.168.99.0/24      (net1 connected)\n    203.0.113.100/32     Gateway scn-gateway        (http-routing-e2e)\n    203.0.113.101/32     Gateway scn-extres-gw      (external-resource-pool)\n    203.0.113.102/32     Gateway scn-proxy-gw       (proxy-protocol-l4)\n\n  FRR installs each /32 as a kernel route:\n    203.0.113.100/32 via 192.168.99.X dev net1 proto bgp\n  so any client in the FRR pod can curl the Gateway addresses\n  end-to-end via the NAD bridge, completely bypassing TMM's eth0\n  TCP hook. This is what http-routing-e2e and external-resource-pool\n  rely on for their data-plane assertions.\n```\n\nKey knob: `CNEInstance.spec.advanced.tmm.env TMM_MAPRES_ADDL_VETHS_ON_DP=FALSE`\nis set by `bgp-peer-frr`. With this `TRUE` (TMM's default for\ndemoMode), `mapres` grabs `net1` for the userspace data plane and\nflushes its kernel IP — ZeBOS then has nothing to source-bind\nto. Flipping it `FALSE` lets `net1` stay a normal Linux interface\nwith its NAD-assigned IP so the kernel TCP stack handles BGP\ntraffic ordinarily.\n\n## Scenarios — testing F5 how-tos against the running cluster\n\nAfter `e2e` brings the cluster up, drive named test scenarios against\nit. Each scenario maps to one F5 how-to article (or sub-article) and\nexercises a slice of BNK functionality end-to-end: render manifests\ninto `artifacts/scenarios/\u003cname\u003e/`, apply them, assert reconciled\nstate, write a JSON+md report under `reports/\u003ctimestamp\u003e/scenarios/`.\n\n```bash\nkindbnkctl scenario list                            # all known scenarios + rating\nkindbnkctl scenario run http-routing --poc ./demo   # apply + verify + report\nkindbnkctl scenario run http-routing --dry-run      # render manifests only\nkindbnkctl scenario clean http-routing              # delete what was applied\n```\n\nRating is a stable hint about what's testable in the 2-node / demo-TMM\nshape:\n\n| Rating | Meaning |\n|---|---|\n| green | fully testable here |\n| amber | partially testable — control-plane verifies, data-plane plumbing missing |\n| red   | requires real DPUs / BGP peers / etc.; listed for discoverability, never executed |\n\nScoring of the [F5 BNK how-tos index](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/):\n\n| # | How-to | Rating | Scenario | Wall time |\n|---|---|---|---|---|\n| 1 | [Restrict access to sensitive data](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/spk-admin-access-api.html) | 🟢 | [`cwc-admin-access`](internal/scenarios/cwcadminaccess) | 9s |\n| 2 | [Components needing cluster-wide access](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/spk-whole-cluster.html) | 🟢 | [`cluster-wide-watch`](internal/scenarios/clusterwidewatch) | 4s |\n| 3 | [Set up dynamic routing with BGP](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/spk-zebos-config.html) | 🟢 | [`bgp-peer-frr`](internal/scenarios/bgppeer) | 3m19s |\n| 4 | [Set up core file collection](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/spk-coremond.html) | 🟢 | [`core-file-collection`](internal/scenarios/corefiles) | 3m01s |\n| 6 | [Configure Token Counting and Enforcement](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/configure-token-counting-and-enforcement.html) | 🟢 | [`ai-token-counting`](internal/scenarios/aitokencount) | 25s |\n| 7 | [Semantic AI Model Caching](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/ai-related-features/ai-semantic-caching.html) (sub-article of [AI Traffic Optimization](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/ai-related-features/index.html)) | 🟢 | [`ai-semantic-cache`](internal/scenarios/aisemcache) | 22s |\n| 8 | [HTTP traffic steering with Gateway API HTTPRoute](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/Configure-HTTP-traffic-steering-with-Gateway-API-HTTPRoute.html) | 🟢 | [`http-routing-e2e`](internal/scenarios/httproutee2e) | 21s |\n| 9 | [Proxy Protocol iRule support for L4 routes](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/proxy-protocol.html) | 🟢 | [`proxy-protocol-l4`](internal/scenarios/proxyprotocol) | 24s |\n| 10 | [Load Balance Traffic to External Resources](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/configure-external-resource-load-balancing.html) | 🟢 | [`external-resource-pool`](internal/scenarios/extrespool) | 14s |\n\nPlus four scenarios drawn from the BNK Use-Cases / CRD pages rather\nthan the how-tos index:\n\n| Use-case | Rating | Scenario |\n|---|---|---|\n| [Dynamic IP address allocation (FIC for Gateway API)](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/use-cases/bnk-ficforgatewayapi.html) | 🟡 | [`fic-dynamic-ip`](internal/scenarios/ficdynamicip) |\n| TCP load balancer ([`L4Route`](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/custom-resource-definitions/bnk-gateway-api-l4route.html) protocol=TCP, weighted backends) | 🟢 | [`tcp-l4-loadbalance`](internal/scenarios/tcpl4lb) |\n| UDP load balancer ([`L4Route`](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/custom-resource-definitions/bnk-gateway-api-l4route.html) protocol=UDP, socat echo) | 🟢 | [`udp-l4-loadbalance`](internal/scenarios/udpl4lb) |\n| gRPC routing ([`GRPCRoute`](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/custom-resource-definitions/bnk-gateway-api-grpcroute.html), grpcbin backend, grpcurl client) | 🟡 | [`grpc-loadbalance`](internal/scenarios/grpcroute) |\n\n`fic-dynamic-ip` (🟡): manifest-side configuration applies cleanly\n(F5BnkGateway, Gateway w/ infrastructure.parametersRef, HTTPRoute)\nbut Gateway never reaches Programmed=True. f5-cne-controller logs\n\"No IPAM found for Gateway\" — the F5BnkGateway pool isn't auto-\nconverted into IPAM/IPAMRange CRs in this BNK 2.3.0 demo\ndeployment. The scenario asserts the control-plane state and\nsurfaces the AddressNotAssigned condition as informational.\n\n`grpc-loadbalance` (🟡): GRPCRoute reconciles, Gateway reaches\nProgrammed=True, BGP route propagates, grpcurl installs SHA-\nverified, and a direct grpcurl-to-backend Service call lists\nall gRPC services successfully. But cleartext gRPC traffic\nthrough the Gateway returns RST_STREAM(INTERNAL_ERROR) — TMM's\nstandard HTTP/json/httprouter profile chain (visible in audit\nlogs) breaks gRPC framing. Setting `appProtocol: kubernetes.io/h2c`\non the Service didn't change it. Likely needs HTTPS listener\nwith TLS + ALPN h2 (kindbnkctl doesn't plumb TLS) or a BNK\nprofile override not yet exposed through the Gateway API CRDs.\n\nWall times measured on a fresh `e2e` (cluster destroy + redeploy)\nrunning 2026-05-21 on a Linux laptop. The two TMM-restarting\nscenarios (`bgp-peer-frr` + `core-file-collection`) dominate at\n~3 minutes each; the others are tens of seconds because they\neither don't touch TMM or piggyback on the bridge already up.\n`kindbnkctl scenario run --all` runs every green-rated scenario\nin topo-sorted dependency order, writing an aggregate\n`reports/\u003cstamp\u003e/run.{json,md}` summary alongside the per-scenario\nJSONs.\n\nCluster bring-up itself (`kindbnkctl e2e`) is **5m44s**:\nvalidate 0s · cluster-up 48s · deploy-prereqs 20s · deploy-flo\n24s · deploy-cne 4m12s (includes waiting on `bnk-gatewayclass`\nto reach `Accepted=True` — required to keep first-run scenario\nGateways from being marked-for-deletion by the controller).\n\nHow-tos **#5** ([DOCA Offloads on DPU](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/traffic-offload.html)),\n**#11** ([Static Active-Standby Interface Bonding](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/configure-static-active-standby-bonding.html)),\nand **#12** ([TMOS DNS Service Integration with CIS](https://clouddocs.f5.com/bigip-next-for-kubernetes/latest/how-tos/configure-tmos-dns-service-integration-with-container-ingress-services.html))\nare omitted from the table because they require resources kind\ncan't provide: DPU silicon (#5), bondable physical NICs (#11),\nand a real upstream BIG-IP GTM box (#12). They remain valid BNK\nfeatures outside the kindbnkctl shape.\n\nRatings are assigned only after a scenario is built and run.\nImplemented scenarios that pan out land as 🟢; ones that\nhit a real architectural barrier on kind+demoMode get 🟡\nwith the gap documented in the scenario's `Description()`.\nEmpty cell = scenario not yet built.\n\n`bgp-peer-frr` (green) deploys a real BGP session between an FRR\npod and TMM's ZeBOS daemon, peered over a Multus\nNetworkAttachmentDefinition (bridge CNI) on a per-node Linux\nbridge. The NAD path bypasses TMM's eth0 TCP hook entirely —\nBGP rides net1 in both pods, exchanging prefixes via the bridge.\nSix assertions pass: Multus DaemonSet Ready, both pods have net1\nin the 192.168.99.0/24 NAD range, ZeBOS sees the neighbor, BGP\nsession Established, and FRR's BGP table has at least one prefix\nlearned from TMM (via `redistribute kernel`).\n\n`http-routing-e2e` (green) — depends on `bgp-peer-frr` for the\nNAD plumbing. Applies a GatewayClass + Gateway (static\nspec.addresses=203.0.113.100) + HTTPRoute + nginx backend.\nTMM's ZeBOS (via `redistribute kernel`) advertises 203.0.113.100/32\ninto BGP; FRR installs the kernel route via net1; the verify\nstep execs 5 curls from inside the FRR pod, which already has\nthe route. All 6 assertions pass including the 5×curl. Path:\nFRR socket → FRR kernel route → net1 → bnk-bgp bridge → TMM net1\n→ Gateway listener → nginx. TMM's eth0 TCP hook is completely\nbypassed.\n\nReproduce manually:\n\n```bash\nkubectl -n scn-bgp exec deploy/scn-frr -c frr -- \\\n  curl -sS -H 'Host: kindbnkctl.local' http://203.0.113.100/\n# → kindbnkctl-scenario-httproute-e2e-OK\n```\n\n`external-resource-pool` (green) — demonstrates how-to #10 (load\nbalance to non-Service backends) via the BNK `Pool` CR. HTTPRoute\n`backendRefs` points at a `Pool {group:k8s.f5net.com, kind:Pool}`\ninstead of a Service; `Pool.spec.members` lists endpoints by\nIP+port. On kind, the \"external\" backend is an nginx pod attached\nto the bnk-bgp NAD (same bridge TMM uses), with its NAD IP\nauto-discovered and rendered into the Pool CR. Gateway address\nis 203.0.113.101 to avoid collision with `http-routing-e2e`.\n\n`cwc-admin-access` (green) — implements how-to #1 (restrict access\nto sensitive data). Demonstrates BNK's dual-gate access control\non the CWC admin API: mTLS at the TLS layer + bearer token at\nthe HTTP layer. Both materials are produced by the deploy-flo\nphase already (cwc-license-client-certs Secret + cwc-auth-token\nSecret in f5-cne-core); the scenario just replicates them into\nits own namespace, spawns a curl probe pod, and runs three\nrequests against https://f5-spk-cwc.f5-cne-core.svc:38081/status:\nauthenticated (expect 200 + license JSON), no Authorization\nheader (expect 401 \"invalid token format\"), bogus token\n(expect 401 \"invalid token\"). Independent of bgp-peer-frr —\nthis is a pure runtime-access check.\n\n`proxy-protocol-l4` (green) — implements how-to #9 (PROXY-protocol\niRule on an L4 route). The new BNK CRs reconcile (`F5BigCneIrule`\nProgrammed, `L4Route` Accepted, `BNKNetPolicy` ResolvedRefs True),\nTMM proxies the TCP traffic, FRR learns the Gateway IP via BGP,\nand 5/5 curls from FRR through the Gateway return the marker body\nwith the parsed `proxy_addr` set to FRR's NAD IP — proving the\niRule's `TCP::respond` prepended the PROXY v1 line before nginx\nsaw the request. Load-bearing knob: `L4Route.spec.pvaAccelerationMode:\ndisabled`, which keeps the data path in TMM's TCL slow path. With\nthe default `full/assisted` PVA mode, TMM hardware-offloads the\nconnection after handshake and `TCP::respond` fires in the VM but\ncan't reach the offloaded wire — symptoms are 200 OK from nginx\nturning into \"broken header\" errors and curl `(52) Empty reply`.\n\n`core-file-collection` (green) — implements how-to #4 (set up\ncore file collection). One-line CNEInstance.spec.coreCollection.\nenabled=true flip plus `advanced.coremon.hostPath=true` so the\nCoreMond DaemonSet survives kind's single-node-RWO storage class.\nFLO auto-creates a CoreMond CR + DaemonSet in f5-cne-core and\nadds kernel-cores / f5-core-store / tmm-core volumes to the TMM\nDeployment template. The scenario asserts the CR exists, the\nDaemonSet is Running, and the CNEInstance condition\n`CoremondAvailable=True`. The how-to's \"kill -11 to force a\ncrash\" verification step is intentionally NOT automated —\ncrashing TMM mid-scenario destabilises the cluster, and the\nfollow-up \"did a core file land in /var/crash\" check needs a\nprivileged node-level read we'd rather not bake in. Operators\ncan run the kill manually after the scenario and inspect the\nkind worker container's filesystem to confirm capture.\n\n## Reference run report\n\nA complete `e2e --with-scenarios` report from a clean cluster\nis checked in at\n[`examples/reports/run-smoke-2026-05-22T09-00-55Z.md`](examples/reports/run-smoke-2026-05-22T09-00-55Z.md)\nso a reader can see the full report shape (versions, host\nresources, cluster topology, F5 control-plane pods, every deploy\nphase, and every scenario row) without running anything locally.\n\nReproduce on your own host with:\n\n```bash\nkindbnkctl e2e \\\n  --poc \u003cpocdir\u003e \\\n  --yolo --confirm-cluster \u003cpocname\u003e \\\n  --with-scenarios \\\n  --no-resume\n```\n\nOutput lands at `\u003cpocdir\u003e/reports/\u003cstamp\u003e/run-\u003cpocname\u003e-\u003cstamp\u003e.md`\n(plus the JSON twin, per-phase logs under `logs/`, and\nper-scenario JSONs under `scenarios/`).\nThe checked-in report ran 15m59s end-to-end: ~6m deploy\n(validate → cluster-up → deploy-prereqs/flo/cne) plus ~9m\nrunning 11 green scenarios topo-sorted by dependency order\n(the two amber scenarios — `fic-dynamic-ip` and `grpc-loadbalance`\n— are skipped by `--all` and must be run explicitly).\n\n## Testing\n\n```bash\nmake test    # Go unit tests (poc, deploy, cluster, scenarios)\nmake smoke   # unit tests + Layer A CLI smoke (no cluster required, ~5s)\n```\n\n`make smoke` is the gate to run before pushing — it covers the\nnon-cluster-dependent surface area in one shot.\n\n## Design references\n\n- **[dpubnkctl](https://github.com/mwiget/dpubnkctl)** — the\n  bare-metal + DPU sister tool. kindbnkctl is a copy-fork:\n  `internal/poc`, `internal/cluster`, `internal/cli` are rewritten\n  for the kind path; `internal/bnkforge`, `internal/deploy` are\n  forked verbatim with minor adjustments (local kubectl/helm\n  instead of containerized).\n- **[f5-bnk-udf](https://github.com/f5devcentral/f5-bnk-udf/tree/v2.2.0)**\n  (branch `v2.2.0`) — the inspiration for the BNK-on-host shape:\n  `advanced.demoMode.enabled: true` + node label + nodeSelector,\n  ZeBOS dynamic-routing ConfigMap pattern, multi-worker\n  topology. Same CNEInstance recipe family; kindbnkctl adapts it\n  to a two-node kind cluster with Multus NADs replacing the\n  macvlan-on-bare-metal approach used in f5-bnk-udf.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmwiget%2Fkindbnkctl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmwiget%2Fkindbnkctl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmwiget%2Fkindbnkctl/lists"}