Skip to content

feat: add service.extraPorts and extraServices - #5

Merged
piotrlaczykowski merged 1 commit into
mainfrom
feat/service-extra-ports-and-extra-services
Aug 14, 2026
Merged

feat: add service.extraPorts and extraServices#5
piotrlaczykowski merged 1 commit into
mainfrom
feat/service-extra-ports-and-extra-services

Conversation

@piotrlaczykowski

Copy link
Copy Markdown
Contributor

What

Two ways to expose more than the single main-container port on a chart built on helm-framework:

service.extraPorts — appends ports to the existing Service, and declares a matching containerPort on the main container whenever targetPort resolves to a number. A string targetPort is treated as a reference to a port declared elsewhere and adds no container port.

service:
  port: 80
  targetPort: 8080
  extraPorts:
    - name: grpc
      port: 9090
      appProtocol: kubernetes.io/h2c
    - name: metrics
      port: 9100
      targetPort: 9101

extraServices — one additional <fullname>-<name> Service per enabled entry, always selecting the same pods. For a headless Service for pod-level discovery, or a second Service with a different type/annotations. Omitting ports mirrors the main Service's ports.

extraServices:
  - name: headless
    enabled: true
    clusterIP: None
    publishNotReadyAddresses: true
    # ports omitted -> same ports as the main Service
  - name: internal
    enabled: true
    type: LoadBalancer
    annotations:
      service.beta.kubernetes.io/azure-load-balancer-internal: "true"
    ports:
      - port: 443
        targetPort: http

Both render through a shared helm-framework.service.ports helper (main http + extraPorts + sidecar ports), so a mirroring extraService can't drift from the main Service.

Deliberately omitted as YAGNI, easy to add later: loadBalancerIP, ipFamilies, custom selectors on extra Services.

Validation

service.extraPorts joins the existing port-uniqueness check, which now tracks port names alongside numbers. extraServices entries are checked for a required unique name, port/name uniqueness within each entry, named ports once an entry exposes more than one, and a sessionAffinityConfig timeout set without ClientIP affinity.

Note for reviewers

The new port-name check also catches a pre-existing bug: two sidecars whose names share their first 12 characters both truncate to the same sc-<name> port name (e.g. sc-aaaaaaaaaaaa). That previously rendered a Service the API server rejects; it now fails at template time with an explanatory message. It's a fix, but it is a new template-time failure for any chart in that state — flagging it in case you'd rather it were dropped or downgraded.

Testing

  • Default render is byte-identical to main (both keys default to []) — verified by rendering the test template before and after.
  • Full render with extraPorts + a sidecar + 3 extraServices (one disabled): 3 Services emitted, disabled entry skipped, mirrored ports match, string-targetPort entry correctly absent from container ports.
  • 18 validation failure paths exercised, each failing with its intended message; the two allowed cases (disabled entry with invalid config, single unnamed port) still render.
  • helm lint clean on both charts; npm test 40/40, including the contract-consistency test that verifies both new keys are declared in values.yaml and read by templates.
  • Rendered YAML parses cleanly. No server-side schema validation was run — no cluster reachable and kubeconform isn't installed locally.

Docs

values.yaml documents both keys with commented examples; README.md, values-reference.md, and values-contract.md regenerated via helm-docs and scripts/generate-skill.mjs.

🤖 Generated with claude-flow

Two ways to expose more than the single main-container port:

- `service.extraPorts` appends ports to the existing Service and
  declares a matching `containerPort` on the main container whenever
  `targetPort` resolves to a number. A string `targetPort` references a
  port declared elsewhere, so no container port is added for it.
- `extraServices` renders one additional `<fullname>-<name>` Service per
  enabled entry, always selecting the same pods — for a headless Service
  for pod-level discovery, or a second Service with a different type or
  annotations. Omitting `ports` mirrors the main Service's ports.

Both render through a shared `helm-framework.service.ports` helper so a
mirroring extraService can't drift from the main Service.

Validation: `extraPorts` joins the existing port-uniqueness check, which
now tracks port *names* alongside numbers; `extraServices` entries are
checked for a required unique name, per-entry port/name uniqueness,
named ports once an entry exposes more than one, and a
sessionAffinityConfig timeout without ClientIP affinity.

The new port-name check also catches a pre-existing bug: two sidecars
whose names share their first 12 characters both truncate to the same
`sc-<name>` port name. That rendered a Service the API server rejects;
it now fails at template time with an explanatory message.

Both keys default to `[]`, so the default render is unchanged.

Co-Authored-By: claude-flow <ruv@ruv.net>
@piotrlaczykowski
piotrlaczykowski merged commit cf17dfe into main Aug 14, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants