Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/build-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ jobs:
uses: golangci/golangci-lint-action@v9
with:
version: v2.5.0
# Build golangci-lint from source with the workflow's Go toolchain.
# go.mod targets go 1.26.x and no released golangci-lint binary is yet
# built with go >= 1.26, so binary install-mode fails its built-with
# version guard. goinstall compiles it with the runner's Go.
install-mode: goinstall

- name: Generate manifests
run: make manifests
Expand Down
11 changes: 11 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ linters:
- dupl
- lll
path: internal/*
# Repeated fixture strings in tests are not worth extracting to constants.
- linters:
- goconst
path: _test\.go
# Deprecations surfaced by the controller-runtime 0.24 / apimachinery bump
# (old events API via GetEventRecorderFor, scheme.Builder). The migration
# off these is tracked separately and is out of scope for this change.
- linters:
- staticcheck
text: 'SA1019'
path: (cmd/operator/main\.go|api/v1/groupversion_info\.go)
paths:
- third_party$
- builtin$
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build the manager binary
FROM golang:1.25 AS builder
FROM golang:1.26 AS builder
ARG TARGETOS
ARG TARGETARCH

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
href="https://github.com/nebari-dev/nebari-operator/releases/latest"><img
src="https://img.shields.io/github/v/release/nebari-dev/nebari-operator?logo=github&label=release" alt="Latest
Release"></a> <a href="https://golang.org"><img
src="https://img.shields.io/badge/Go-1.25+-00ADD8?logo=go&logoColor=white" alt="Go 1.25+"></a> <a
src="https://img.shields.io/badge/Go-1.26+-00ADD8?logo=go&logoColor=white" alt="Go 1.26+"></a> <a
href="https://kubernetes.io"></a>
</p>

Expand Down Expand Up @@ -191,7 +191,7 @@ See the [Configuration Reference](docs/configuration-reference.md) for all avail

| Tool | Version | Notes |
| --- | --- | --- |
| `go` | 1.25+ | Controller and tests |
| `go` | 1.26+ | Controller and tests |
| `docker` or `podman` | 24+ | Image builds |
| `kubectl` | 1.28+ | Cluster interaction |
| `make` | any | Build automation |
Expand Down
7 changes: 7 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ rules:
- gateway.networking.k8s.io
resources:
- httproutes
- listenersets
verbs:
- create
- delete
Expand All @@ -77,6 +78,12 @@ rules:
- patch
- update
- watch
- apiGroups:
- gateway.networking.k8s.io
resources:
- listenersets/status
verbs:
- get
- apiGroups:
- rbac.authorization.k8s.io
resources:
Expand Down
7 changes: 6 additions & 1 deletion dev/scripts/services/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,13 @@ kubectl create namespace envoy-gateway-system --dry-run=client -o yaml | kubectl

# Install Envoy Gateway with Helm
log_info "Installing Envoy Gateway via Helm (this may take a few minutes)..."
# TEMPORARY (remove before approval): bumped from v1.2.4 so the e2e/dev cluster
# ships the standard gateway.networking.k8s.io/v1 ListenerSet CRD (Gateway API
# v1.5+), which the per-app ListenerSet reconcile requires. This pre-empts NIC's
# foundational Envoy Gateway pin; reconcile with NIC's EG upgrade
# (nebari-infrastructure-core#496) before this merges rather than hardcoding it here.
helm upgrade --install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.2.4 \
--version v1.8.2 \
--namespace envoy-gateway-system \
--wait \
--timeout 5m 2>&1 | grep -v "unrecognized format"
Expand Down
37 changes: 37 additions & 0 deletions docs/reconcilers/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -740,3 +740,40 @@ These match the resources deployed by the foundational infrastructure via ArgoCD
- [Gateway API Documentation](https://gateway-api.sigs.k8s.io/)
- [Envoy Gateway](https://gateway.envoyproxy.io/)
- [cert-manager](https://cert-manager.io/)

## TLS listener ownership: per-app ListenerSet (ADR-0011 Option 2)

The operator no longer mutates the shared platform Gateway to attach each app's
HTTPS listener. Instead it owns a per-app **`ListenerSet`**
(`gateway.networking.k8s.io/v1`, Standard channel) in the **NebariApp's own
namespace**, attached to the shared Gateway via `spec.parentRef`. The app's TLS
`Certificate` and secret are co-located in that same namespace and
owner-referenced to the NebariApp, so they are garbage-collected with it (no
cross-namespace label bookkeeping, no `ReferenceGrant`). Generated `HTTPRoute`s
attach to the ListenerSet once it is serving.

This removes the shared-Gateway co-ownership that previously left the platform
`gateway-config` GitOps app permanently OutOfSync.

### Staged, status-gated migration

The cutover is automatic and per-NebariApp, with no user-facing strategy flag:

1. The ListenerSet is always reconciled.
2. Until it reports `Accepted=True` **and** `Programmed=True`, the operator keeps
the legacy per-app listener on the shared Gateway in place and routes attach
there. On an Envoy Gateway that does not reconcile ListenerSet (**pre-v1.8**)
the conditions never flip, so per-app TLS is unaffected.
3. Once Programmed, routes retarget to the ListenerSet and the legacy
shared-Gateway listener is removed.

Runtime requirement for the ListenerSet path: **Envoy Gateway v1.8.2+**
(the version that reconciles the stable `ListenerSet`).

### `routing.tls.secretName` (user-provided secrets)

Under the ListenerSet path a user-provided TLS secret is resolved in the
**NebariApp's namespace** (co-located with the ListenerSet), not the Gateway
namespace. Place the secret alongside the NebariApp. During the transitional
window the legacy Gateway-namespace lookup still applies until the ListenerSet is
Programmed.
110 changes: 55 additions & 55 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
module github.com/nebari-dev/nebari-operator

go 1.25.6
go 1.26.5

require (
github.com/Nerzal/gocloak/v13 v13.9.0
github.com/cert-manager/cert-manager v1.18.6
github.com/envoyproxy/gateway v1.6.3
github.com/onsi/ginkgo/v2 v2.23.4
github.com/onsi/gomega v1.37.0
k8s.io/api v0.34.1
k8s.io/apimachinery v0.34.1
k8s.io/client-go v0.34.1
sigs.k8s.io/controller-runtime v0.22.4
sigs.k8s.io/gateway-api v1.4.1
github.com/envoyproxy/gateway v1.8.2
github.com/onsi/ginkgo/v2 v2.28.0
github.com/onsi/gomega v1.39.1
k8s.io/api v0.36.2
k8s.io/apimachinery v0.36.2
k8s.io/client-go v0.36.2
sigs.k8s.io/controller-runtime v0.24.1
sigs.k8s.io/gateway-api v1.5.1
)

require (
cel.dev/expr v0.24.0 // indirect
cel.dev/expr v0.25.1 // indirect
github.com/Masterminds/semver/v3 v3.5.0 // indirect
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
Expand All @@ -27,30 +28,28 @@ require (
github.com/evanphx/json-patch v5.9.11+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.9.11 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fsnotify/fsnotify v1.10.1 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-openapi/jsonpointer v0.22.1 // indirect
github.com/go-openapi/jsonreference v0.21.2 // indirect
github.com/go-openapi/jsonpointer v0.23.1 // indirect
github.com/go-openapi/jsonreference v0.21.6 // indirect
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-openapi/swag/jsonname v0.25.1 // indirect
github.com/go-openapi/swag/jsonname v0.26.1 // indirect
github.com/go-resty/resty/v2 v2.7.0 // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.3.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.26.0 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/pprof v0.0.0-20250607225305-033d6d78b36a // indirect
github.com/google/pprof v0.0.0-20260115054156-294ebfa9ad83 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.29.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mailru/easyjson v0.9.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
Expand All @@ -59,53 +58,54 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.67.1 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
github.com/prometheus/common v0.67.5 // indirect
github.com/prometheus/procfs v0.20.1 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/spf13/cobra v1.10.1 // indirect
github.com/spf13/cobra v1.10.2 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/stoewer/go-strcase v1.3.1 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.8.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
go.opentelemetry.io/otel v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.44.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.44.0 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/otel/sdk v1.44.0 // indirect
go.opentelemetry.io/otel/trace v1.44.0 // indirect
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.yaml.in/yaml/v2 v2.4.3 // indirect
go.uber.org/zap v1.28.0 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/oauth2 v0.32.0 // indirect
golang.org/x/sync v0.18.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/term v0.37.0 // indirect
golang.org/x/text v0.31.0 // indirect
golang.org/x/time v0.13.0 // indirect
golang.org/x/tools v0.38.0 // indirect
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
golang.org/x/mod v0.36.0 // indirect
golang.org/x/net v0.55.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.21.0 // indirect
golang.org/x/sys v0.46.0 // indirect
golang.org/x/term v0.44.0 // indirect
golang.org/x/text v0.38.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.45.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 // indirect
google.golang.org/grpc v1.76.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect
google.golang.org/grpc v1.81.1 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.34.1 // indirect
k8s.io/apiserver v0.34.1 // indirect
k8s.io/component-base v0.34.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
k8s.io/utils v0.0.0-20250820121507-0af2bda4dd1d // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 // indirect
k8s.io/apiextensions-apiserver v0.36.2 // indirect
k8s.io/apiserver v0.36.2 // indirect
k8s.io/component-base v0.36.2 // indirect
k8s.io/klog/v2 v2.140.0 // indirect
k8s.io/kube-openapi v0.0.0-20260317180543-43fb72c5454a // indirect
k8s.io/streaming v0.36.2 // indirect
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
sigs.k8s.io/yaml v1.6.0 // indirect
)
Loading