From f6ce16fc1cbd80de5bacfaad6f3055a6f1682c33 Mon Sep 17 00:00:00 2001 From: Robert Gildein Date: Tue, 11 Aug 2026 16:11:27 +0200 Subject: [PATCH] Add two Claude Code skills in .claude/skills/: - containerlab: deploy a multi-vendor topology (Nokia SRL, Juniper vJunos-Evolved, Cisco N9Kv) with configurable output path and gnmic validation - netop: run local checks (vet, lint, fmt, unit tests, gNMI integration tests) and manual integration tests against real devices (apply CRs, validate with gnmic) Extend AGENTS.md with make vet and make test-gnmi in the after-changes checklist, check rules, and structured check output format for agent-reported results. Signed-off-by: Robert Gildein --- .agents/skills/README.md | 37 ++++ .agents/skills/containerlab/SKILL.md | 154 +++++++++++++++++ .agents/skills/netop/SKILL.md | 244 +++++++++++++++++++++++++++ .claude/skills | 1 + AGENTS.md | 21 +++ README.md | 5 + 6 files changed, 462 insertions(+) create mode 100644 .agents/skills/README.md create mode 100644 .agents/skills/containerlab/SKILL.md create mode 100644 .agents/skills/netop/SKILL.md create mode 120000 .claude/skills diff --git a/.agents/skills/README.md b/.agents/skills/README.md new file mode 100644 index 000000000..fa90c976e --- /dev/null +++ b/.agents/skills/README.md @@ -0,0 +1,37 @@ +# Claude Code Skills + +This project includes [Claude Code](https://claude.ai/code) skills for interactive development workflows. +Skills live in `.agents/skills/` (symlinked from `.claude/skills/`) and are invoked via slash commands. + +## `/containerlab` + +Deploy a multi-vendor containerlab topology for local testing: + +``` +/containerlab +/containerlab --topology config/samples/my-topology.clab.yml +/containerlab --image ghcr.io/nokia/srlinux:25.3.2 +/containerlab --output ~/labs/dev.clab.yml +``` + +Defaults to a topology with Nokia SRL, Juniper vJunos-Evolved, and Cisco N9Kv. +Waits for all nodes to become healthy, writes a gnmic config, and validates +capabilities on all nodes. Prints a summary with gNMI endpoints on completion. + +## `/netop` + +Run local checks and/or manual integration tests against real devices: + +``` +/netop # run both phases +/netop check # local checks only (no cluster needed) +/netop test # integration test only +/netop test --device 172.20.20.2:57400 --cr config/samples/openconfig_v1alpha1_banner.yaml +``` + +**Phase 1 — check:** runs `make vet`, `make lint`, `make fmt`, `make test`, `make test-gnmi` +and prints a structured report. See AGENTS.md for commands, rules, and output format. + +**Phase 2 — test:** deploys the operator to a local kind cluster, applies CRs, +and validates device state via gnmic. Installs cert-manager and builds/loads the +controller image if needed. diff --git a/.agents/skills/containerlab/SKILL.md b/.agents/skills/containerlab/SKILL.md new file mode 100644 index 000000000..58f156513 --- /dev/null +++ b/.agents/skills/containerlab/SKILL.md @@ -0,0 +1,154 @@ +--- +name: containerlab +description: Deploy a containerlab network device. Defaults to a topology with Nokia SRL, Juniper vJunos-Evolved, and Cisco N9Kv. Pass a topology file path or image to override. +argument-hint: [--topology ] [--image ] [--output ] +allowed-tools: [Bash, Read, Write, AskUserQuestion] +allowed-bash: ["containerlab *", "docker *", "gnmic *", "curl *"] +--- + +# containerlab + +Deploys a containerlab network device topology. + +> **Scope limit:** Only `containerlab` (deploy/inspect/destroy) and `gnmic` (`capabilities` and `get`) may be used. Do not SSH into containerlab nodes, run `docker exec`, modify VM networking or Docker configuration, or make any other changes to the device environment — ask the user first. + +## Arguments + +Parse `$ARGUMENTS` for: +- `--topology ` → use this topology file instead of the default +- `--image ` → override the device image in the default topology +- `--output ` → write the generated topology file here (default: `/tmp/dev.clab.yml`) + +If no topology is provided, use the default topology defined below. + +## Deploy network device + +If the user provided `--topology `, deploy that file directly: + +```bash +containerlab deploy -d -t +``` + +Otherwise, write the default topology to `--output` (default: `/tmp/dev.clab.yml`), substituting `--image` if provided (default: `ghcr.io/nokia/srlinux:26.7.1`): + +```yaml +name: dev-topology + +topology: + nodes: + vjunos: + kind: juniper_vjunosevolved + image: vrnetlab/juniper_vjunosevolved:26.2R1.7-EVO + ports: + - 8001:22 + - 57401:57400 + + nokia_srl: + kind: nokia_srlinux + image: ghcr.io/nokia/srlinux:26.7.1 + startup-config: |- + system name host-name nokia + system grpc-server mgmt yang-models openconfig + ports: + - 8002:22 + - 57402:57400 + + cisco_nxos: + kind: cisco_n9kv + image: vrnetlab/cisco_n9kv:9300-10.6.3-lite + env: + QEMU_MEMORY: 6144 + QEMU_SMP: 2 + startup-config: | + hostname cisco_nxos + feature ospf + feature openconfig + feature grpc + grpc use-vrf management + no ip domain-lookup + ports: + - 8003:22 + - 57403:50051 + + links: [] +``` + +Write it to `$OUTPUT_PATH`, then deploy: + +```bash +containerlab deploy -d -t $OUTPUT_PATH +``` + +If the container already exists: + +```bash +containerlab deploy -d --reconfigure -t +``` + +Wait for all nodes to be healthy (poll every 15s, up to 10 minutes) using `docker inspect` — more reliable than parsing `containerlab inspect` table output: + +```bash +docker inspect --format '{{.Name}}: {{.State.Health.Status}}' ... +``` + +Repeat until all vrnetlab nodes show `healthy`. Nokia SRL has no health check — it's ready when running. For vrnetlab-based nodes (Juniper, Cisco) this can take 8–15 minutes. + +Once healthy, write a gnmic config file at `$OUTPUT_DIR/gnmic-config.yaml` (where `$OUTPUT_DIR` is the directory of `$OUTPUT_PATH`) with all nodes from the topology. Use `127.0.0.1` with the forwarded port for each node. For Nokia SRL use `skip-verify: true`; for vrnetlab nodes omit it (plain gRPC). + +Default credentials, ports and TLS settings per node kind: +- Nokia SRL: `admin / NokiaSrl1!`, forwarded port, `skip-verify: true` +- Juniper vJunosEvolved: `admin / admin@123`, forwarded port, `insecure: true` (if the image has TLS configured, use `skip-verify: true` instead) +- Cisco N9Kv: `admin / admin`, forwarded port, `skip-verify: true` + +Example for a topology with all three: + +```yaml +timeout: 10s + +targets: + juniper: + address: 127.0.0.1:57401 + username: admin + password: admin@123 + insecure: true + + srl: + address: 127.0.0.1:57402 + username: admin + password: NokiaSrl1! + skip-verify: true + + nxos: + address: 127.0.0.1:57403 + username: admin + password: admin + skip-verify: true +``` + +Then validate all nodes at once: + +```bash +gnmic --config $OUTPUT_DIR/gnmic-config.yaml capabilities +``` + +Show the supported encodings and YANG model count per node. If a node fails, show the error and note it may still be booting. + +## Summary + +Once all nodes are healthy, show the full topology state: + +```bash +containerlab inspect -a +``` + +Print a final summary: +- Containerlab version +- For each node: name, kind, management IP, gNMI endpoint, health state, gnmic capabilities result (encodings supported, YANG model count) +- Next step: run `/netop` to test device + +## References + +- **containerlab**: https://containerlab.dev/cmd/ +- **Nokia SRL containerlab kind**: https://containerlab.dev/manual/kinds/nokia_srlinux/ +- **Juniper vJunos-Evolved containerlab kind**: https://containerlab.dev/manual/kinds/vjunos-evolved/ +- **Cisco NX-OS containerlab kind**: https://containerlab.dev/manual/kinds/cisco_nxos/ diff --git a/.agents/skills/netop/SKILL.md b/.agents/skills/netop/SKILL.md new file mode 100644 index 000000000..036c53eeb --- /dev/null +++ b/.agents/skills/netop/SKILL.md @@ -0,0 +1,244 @@ +--- +name: netop +description: Run local checks (vet, lint, test, gnmi test) and/or manual integration tests against real devices (build, deploy, apply CRs, validate with gnmic). Each phase can be run independently. +argument-hint: [check | test | all] [--device ] [--cr ] [--expected ] +allowed-tools: [Bash, Read, AskUserQuestion] +allowed-bash: ["make vet", "make fmt", "make test", "make test-gnmi", "make lint", "kubectl get *", "kubectl describe *", "kubectl logs *", "kubectl apply *", "kubectl wait *", "kind get *", "gnmic *", "git diff *", "git status", "docker info"] +--- + +# netop + +**Purpose:** You are a tester. Your role is to verify that the operator and its providers work correctly — running checks, deploying to test environments, applying CRs, and validating device state. You read logs, configuration, and test results. You do not change network-operator source code or device state outside of normal operator reconciliation without explicit user approval. + +Two independent phases — run one or both: + +- **check** — local checks: vet, lint, fmt, unit tests, gNMI integration tests (host only, no cluster needed) +- **test** — manual integration test against real devices: observe operator, apply CRs, validate with gnmic + +Parse `$ARGUMENTS`: +- `check` → run Phase 1 only +- `test` → run Phase 2 only +- `all` or no argument → run both phases +- `--device ` → device target for gnmic validation (can be specified multiple times) +- `--cr ` → CR file to apply in Phase 2 +- `--expected ` → expected gnmic state to compare against + +All commands run from the repo root. + +--- + +## Permissions model + +All commands listed in `allowed-bash` run freely — no prompt needed. + +**Requires explicit user approval before executing:** +- Any change to network-operator source code (Go files, types, controllers, webhooks, config) +- Any action that directly modifies device state outside of normal operator reconciliation (e.g. manual gnmic Set, out-of-band config fixes) +- Destructive teardown: `make kind-delete`, `make undeploy`, `kubectl delete` + +**Runs freely in Phase 2 (no approval needed):** +- Creating a kind cluster or deploying the operator (if not present) +- Applying Device CRs +- Applying test CRs + +When one of these is needed, describe exactly what will run and why, then wait for approval before proceeding. + +--- + +## Phase 1: Check + +Follow **After Making Changes** in AGENTS.md — commands, rules, and output format are all defined there. Each step can be skipped if the user asks for a specific one. + +--- + +## Phase 2: Test + +Manual integration test against one or more real devices. + +### Step 2.0: Gather test parameters + +If not provided via `$ARGUMENTS`, ask the user: + +1. **Device(s):** address:port and credentials for each target (e.g. Nokia SRL at `172.20.20.2:57400`, Juniper at `172.20.20.4:57401`). Default credentials for Nokia SRL: `admin / NokiaSrl1!`. +2. **What to test:** which CR kind and what configuration (e.g. "OpenConfig DNS with servers 8.8.8.8 and 1.1.1.1"). If the user points to a sample file, use that. +3. **Expected output:** optional — if provided, compare gnmic response against it. Otherwise infer from the CR spec. + +Store for use in later steps: +``` +DEVICES=( ...) +CREDENTIALS=( ...) +TEST_CR= +EXPECTED= +``` + +### Step 2.1: Observe kind cluster + +First verify Docker is available (regardless of whether it's Docker Desktop, colima, or any other runtime): + +```bash +docker info 2>&1 | head -5 +``` + +If Docker is not running or not accessible, tell the user and stop — do not attempt to start any Docker runtime. + +Then check cluster state — read only: + +```bash +kind get clusters +kubectl get nodes +kubectl get pods -n network-operator-system +``` + +If the `network-operator` cluster does not exist or the operator is not running, proceed with setup automatically. + +### Step 2.1a: Install cert-manager (if not present) + +cert-manager is required before deploying the network operator. Check if it is already installed: + +```bash +kubectl get namespace cert-manager 2>/dev/null +``` + +If not present, install it and wait for it to be ready + +```bash +kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.18.2/cert-manager.yaml +kubectl wait --for=condition=Available deployment --all -n cert-manager --timeout=120s +``` + +### Step 2.1b: Build, load and deploy (if cluster was just created or operator not deployed) + +Follow the **Development Workflow** in AGENTS.md. Use a fixed local image tag `network-operator-dev:latest` to avoid colliding with the upstream image. The kind cluster name is `network-operator` and the kubectl context is `kind-network-operator`. + +Then wait for it to be ready: + +```bash +kubectl --context kind-network-operator wait --for=condition=Available deployment/network-operator-controller-manager -n network-operator-system --timeout=120s +``` + +If the operator crashes, check logs before asking the user — do not restart or redeploy without understanding the error. + +### Step 2.2: Observe operator + +Read the current operator state and logs — no changes: + +```bash +kubectl get deployment -n network-operator-system network-operator-controller-manager +kubectl logs -n network-operator-system -l control-plane=controller-manager --tail=50 +``` + +### Step 2.3: Observe Device resources + +Read existing Device CRs: + +```bash +kubectl get device -A +kubectl describe device -A +``` + +If no Device CRs exist for the target devices, write the Device CR + Secret manifests to `/tmp/netop-devices.yaml` and apply them: + +```bash +kubectl apply -f /tmp/netop-devices.yaml +``` + +### Step 2.4: Apply test CRs + +If the user has provided a CR to apply, run `kubectl apply -f ` directly. + +Write any generated CR manifests to `/tmp/netop--.yaml` (e.g. `/tmp/netop-banner-vjunos.yaml`) and apply from there: + +```bash +kubectl apply -f /tmp/netop--.yaml +``` + +If the user pointed to a testdata file (`test/gnmi/testdata/`), parse it first (read-only): + +``` +-- / -- + +-- state -- + +``` + +Show the CR YAML section, use the state section as expected output, then apply it. + +After applying (if approved), observe reconciliation — read only: + +```bash +kubectl get -A +kubectl logs -n network-operator-system -l control-plane=controller-manager --tail=50 +``` + +Expect `READY=True`. If not ready within ~30s, show operator logs — do not restart or redeploy. + +### Step 2.5: Validate with gnmic + +For each CR and each device, find the gNMI path from the provider source (`internal/provider/openconfig/.go`, look for `XPath()`), then run: + +```bash +gnmic -a --port -u -p '' --skip-verify --encoding JSON_IETF get --path '' +``` + +Always print the full JSON response without truncation. This is read-only — no gnmic Set calls. + +**Validation:** +- Expected output provided → compare field by field +- No expected output → infer expected values from the CR spec and validate those fields + +### Phase 2 summary + +Print a structured report after all CRs are validated. + +For each CR tested: +1. Applied YAML: `kubectl get -n -o yaml` +2. gnmic command and full JSON response +3. Relevant operator logs: `kubectl logs ... | grep -i '\|error\|warn'` + +End with a summary table: + +``` + Test Report + ┌──────────────┬──────────┬───────────┬───────┬──────────────────────────────┬──────────────────┬─────────────────┐ + │ CR Name │ Kind │ Namespace │ Ready │ gNMI Path │ Device │ Result │ + ├──────────────┼──────────┼───────────┼───────┼──────────────────────────────┼──────────────────┼─────────────────┤ + │ dns │ DNS │ default │ True │ openconfig-system:system/dns │ 172.20.20.2:57400│ ✓ value matches │ + │ dns │ DNS │ default │ True │ openconfig-system:system/dns │ 172.20.20.4:57401│ ✓ value matches │ + └──────────────┴──────────┴───────────┴───────┴──────────────────────────────┴──────────────────┴─────────────────┘ +``` + +Results: +- `✓ value matches` — gnmic response matches expected/inferred value +- `✗ mismatch` — differs (show diff inline below table) +- `✗ not found` — gnmic returned empty or error + +--- + +## Cleanup + +Only perform cleanup steps when the user explicitly asks. Always ask for confirmation before any destructive action. + +Undeploy the operator: + +```bash +make undeploy +``` + +Delete the kind cluster: + +```bash +make kind-delete +``` + +--- + +## References + +- **go vet**: https://pkg.go.dev/cmd/vet +- **golangci-lint**: https://golangci-lint.run +- **gnmic**: https://gnmic.openconfig.net +- **kind**: https://kind.sigs.k8s.io/docs/user/quick-start/ +- **kubectl**: https://kubernetes.io/docs/reference/kubectl/ +- **OpenConfig YANG schemas**: https://openconfig.net/projects/models/schemadocs/ +- **Juniper vJunos-Evolved**: https://www.juniper.net/documentation/us/en/software/vjunos/vjunos-evolved/ +- **Cisco NX-OS gNMI**: https://developer.cisco.com/docs/nx-os/#!using-gnmi diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 000000000..2b7a412b8 --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index c31b258dc..e3b99437c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -38,8 +38,10 @@ make charts # Regenerate Helm chart manifests ```bash make fmt # Format code +make vet # Static analysis make lint # Lint code style make test # Run unit tests (uses envtest: real K8s API + etcd) +make test-gnmi # gNMI integration tests (fake server, no cluster needed) ``` Tests use **Ginkgo + Gomega** (BDD style) for controller and webhook tests — check `suite_test.go` in each package for setup. @@ -50,6 +52,25 @@ All other packages use the **standard library `testing` package** with table-dri **All of `make fmt lint test` must pass before work is considered complete.** +**Check rules:** +- Stop if `make vet` fails — these are likely bugs that need manual fixes +- If `make lint` fails, do NOT run `make lint-fix` automatically — ask the user first +- Run `make fmt` + `git diff --name-only` only when vet or lint failed; report drift but do NOT stage or commit + +**Check output format** (report after running all steps): + +``` + Local Check Report + ───────────────────────────────────────────────────── + Vet: ✓ passed (or ✗ N issues) + Lint: ✓ passed (or ✗ N issues) + Fmt: ✓ no drift (or ✗ N files need formatting) ← only shown when vet or lint failed + Unit tests: ✓ N passed, 0 failed (or ✗ list failing tests) + gNMI tests: ✓ N passed, 0 failed (or ✗ list failing testdata files + diff) + ───────────────────────────────────────────────────── + Overall: ✓ all checks passed (or ✗ see above) +``` + ## Commits - Run `git log` first and match the existing commit style diff --git a/README.md b/README.md index 3d8c35cf4..18f2144c2 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,11 @@ Users can just run kubectl apply -f to install the project kubectl apply -f https://raw.githubusercontent.com//network-operator//dist/install.yaml ``` +## Claude Code Skills + +This project includes [Claude Code](https://claude.ai/code) skills for interactive development workflows. +See [.agents/skills/README.md](.agents/skills/README.md) for available skills and usage. + ## Support, Feedback, Contributing This project is open to feature requests/suggestions, bug reports etc. via [GitHub issues](https://github.com/ironcore-dev/network-operator/issues). Contribution and feedback are encouraged and always welcome. For more information about how to contribute, the project structure, as well as additional contribution information, see our [Contribution Guidelines](CONTRIBUTING.md).