Kubernetes NodePort Services must use a port in the apiserver's
configured nodeport range (default 30000-32767). NatsBasicScore's
first cut accepted any port via `.node_port(port)`, which was fine
for strict use of the capital-N NodePort Service type, but made
the demo's "use NATS client port 4222 directly from the host"
story awkward.
Replace the `node_port: Option<i32>` field with a proper
`NatsServiceType` enum (ClusterIP | NodePort(i32) | LoadBalancer).
Three builder methods — one per variant. LoadBalancer is the right
idiom for the demo: k3d's built-in `klipper-lb` fronts
LoadBalancer Services on their `port` (not their nodePort), so
`k3d cluster create -p 4222:4222@loadbalancer` delivers external
traffic straight to the Service's client port. No nodeport range
juggling.
Signatures:
NatsBasicScore::new(name, namespace) // ClusterIP default
.node_port(30422) // NodePort(30422)
.load_balancer() // LoadBalancer
.jetstream(true)
.image("docker.io/library/nats:2.10-alpine")
Tests: 5 pass. New assertion: `load_balancer()` produces a Service
with type LoadBalancer and no pinned nodePort (apiserver assigns).
Consumers:
- `example_iot_nats_install` gets a `--expose {cluster-ip | node-port
| load-balancer}` flag (default `load-balancer` since that's what
the demo wants). The legacy `--node-port N` flag survives as the
NodePort port value.
- `smoke-a4.sh` asks for `--expose load-balancer`, matching its
`-p 4222:4222@loadbalancer` k3d port mapping.