From 1ada39a71d5c810f71032bfb06131e4dbca9a9fc Mon Sep 17 00:00:00 2001 From: John Howard Date: Tue, 26 May 2026 11:44:35 -0700 Subject: [PATCH 1/8] router: add pluggable networking providers --- .../internal/app/router/agentgateway.go | 141 ++++++++++ cmd/atenet/internal/app/router/controller.go | 25 +- cmd/atenet/internal/app/router/dashboard.html | 16 +- cmd/atenet/internal/app/router/envoy.go | 118 ++++++++ cmd/atenet/internal/app/router/envoyrunner.go | 258 ------------------ cmd/atenet/internal/app/router/health.go | 74 ++--- cmd/atenet/internal/app/router/provider.go | 102 +++++++ cmd/atenet/internal/app/router/proxyrunner.go | 183 +++++++++++++ cmd/atenet/internal/app/router/router.go | 80 +++--- cmd/atenet/internal/app/router/status.go | 2 + hack/install-ate.sh | 72 ++++- .../atenet-router-agentgateway.yaml | 197 +++++++++++++ manifests/ate-install/atenet-router.yaml | 4 +- .../base-agentgateway/kustomization.yaml | 25 ++ manifests/ate-install/base/kustomization.yaml | 25 ++ .../kind-agentgateway/kustomization.yaml | 43 +++ 16 files changed, 1008 insertions(+), 357 deletions(-) create mode 100644 cmd/atenet/internal/app/router/agentgateway.go create mode 100644 cmd/atenet/internal/app/router/envoy.go delete mode 100644 cmd/atenet/internal/app/router/envoyrunner.go create mode 100644 cmd/atenet/internal/app/router/provider.go create mode 100644 cmd/atenet/internal/app/router/proxyrunner.go create mode 100644 manifests/ate-install/atenet-router-agentgateway.yaml create mode 100644 manifests/ate-install/base-agentgateway/kustomization.yaml create mode 100644 manifests/ate-install/base/kustomization.yaml create mode 100644 manifests/ate-install/kind-agentgateway/kustomization.yaml diff --git a/cmd/atenet/internal/app/router/agentgateway.go b/cmd/atenet/internal/app/router/agentgateway.go new file mode 100644 index 00000000..04c3b09d --- /dev/null +++ b/cmd/atenet/internal/app/router/agentgateway.go @@ -0,0 +1,141 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package router + +import ( + "context" + "fmt" + "strings" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/util/intstr" +) + +type agentgatewayProvider struct { + cfg RouterConfig +} + +func (p agentgatewayProvider) Name() string { + return NetworkingModeAgentgateway +} + +func (p agentgatewayProvider) RequiresXDS() bool { + return false +} + +func (p agentgatewayProvider) ConfigMapData() map[string]string { + return map[string]string{"config.yaml": p.localConfig()} +} + +func (p agentgatewayProvider) Container() corev1.Container { + ports := []corev1.ContainerPort{ + {Name: "http", ContainerPort: int32(p.cfg.HttpPort)}, + {Name: "readiness", ContainerPort: 15021}, + {Name: "metrics", ContainerPort: 15020}, + } + if p.cfg.HttpsPort > 0 && tlsCertPath(p.cfg) != "" { + ports = append(ports, corev1.ContainerPort{Name: "https", ContainerPort: int32(p.cfg.HttpsPort)}) + } + + return corev1.Container{ + Name: "agentgateway", + Image: p.cfg.AgentgatewayImage, + Args: []string{"-f", "/etc/agentgateway/config.yaml"}, + Ports: ports, + VolumeMounts: []corev1.VolumeMount{ + {Name: "proxy-config", MountPath: "/etc/agentgateway"}, + }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Path: "/healthz/ready", + Port: intstr.FromInt32(15021), + }, + }, + PeriodSeconds: 10, + }, + } +} + +func (p agentgatewayProvider) ServicePorts() []corev1.ServicePort { + ports := []corev1.ServicePort{ + {Name: "http", Port: int32(p.cfg.HttpPort), TargetPort: intstr.FromString("http")}, + } + if p.cfg.HttpsPort > 0 && tlsCertPath(p.cfg) != "" { + ports = append(ports, corev1.ServicePort{Name: "https", Port: int32(p.cfg.HttpsPort), TargetPort: intstr.FromString("https")}) + } + return ports +} + +func (p agentgatewayProvider) CheckReady(ctx context.Context) (bool, string) { + return checkHTTPReady(ctx, "http://127.0.0.1:15021/healthz/ready", "") +} + +func (p agentgatewayProvider) localConfig() string { + httpRoute := p.routeBlock("substrate-http") + config := fmt.Sprintf(`# yaml-language-server: $schema=https://agentgateway.dev/schema/config +config: + adminAddr: "127.0.0.1:15000" + readinessAddr: "0.0.0.0:15021" + statsAddr: "0.0.0.0:15020" +binds: +- port: %d + listeners: + - name: http + protocol: HTTP + routes: +%s`, p.cfg.HttpPort, indent(httpRoute, 4)) + + if p.cfg.HttpsPort > 0 && tlsCertPath(p.cfg) != "" { + cert := tlsCertPath(p.cfg) + key := tlsKeyPath(p.cfg) + config += fmt.Sprintf(`- port: %d + listeners: + - name: https + protocol: HTTPS + tls: + cert: %q + key: %q + routes: +%s`, p.cfg.HttpsPort, cert, key, indent(p.routeBlock("substrate-https"), 4)) + } + + return config +} + +func (p agentgatewayProvider) routeBlock(name string) string { + return fmt.Sprintf(`- name: %s + matches: + - path: + pathPrefix: / + policies: + extProc: + host: %q + failureMode: failClosed + backends: + - dynamic: {} +`, name, fmt.Sprintf("%s:%d", p.cfg.ExtprocAddr, p.cfg.ExtprocPort)) +} + +func indent(s string, spaces int) string { + prefix := strings.Repeat(" ", spaces) + lines := strings.Split(s, "\n") + for i, line := range lines { + if line != "" { + lines[i] = prefix + line + } + } + return strings.Join(lines, "\n") +} diff --git a/cmd/atenet/internal/app/router/controller.go b/cmd/atenet/internal/app/router/controller.go index 055751fb..5701666b 100644 --- a/cmd/atenet/internal/app/router/controller.go +++ b/cmd/atenet/internal/app/router/controller.go @@ -31,9 +31,10 @@ type Controller struct { cfg RouterConfig xdsSrv *XdsServer extprocSrv *ExtProcServer + provider proxyProvider atStore atStore - envoyRunner *envoyrunner + proxyRunner *proxyrunner } func NewController( @@ -42,8 +43,11 @@ func NewController( cfg RouterConfig, xdsSrv *XdsServer, extprocSrv *ExtProcServer, + provider proxyProvider, ) *Controller { - xdsSrv.SetConfig(cfg.HttpPort, cfg.ExtprocPort, cfg.ExtprocAddr) + if xdsSrv != nil { + xdsSrv.SetConfig(cfg.HttpPort, cfg.ExtprocPort, cfg.ExtprocAddr) + } var store atStore if cfg.TemplatesFile != "" { @@ -58,9 +62,10 @@ func NewController( cfg: cfg, xdsSrv: xdsSrv, extprocSrv: extprocSrv, + provider: provider, atStore: store, - envoyRunner: newEnvoyRunner(k8sClient, cfg), + proxyRunner: newProxyRunner(k8sClient, cfg, provider), } } @@ -92,16 +97,18 @@ func (c *Controller) reconcile(ctx context.Context) error { return err } - if err := c.xdsSrv.UpdateSnapshot(); err != nil { - slog.ErrorContext(ctx, "xDS Configuration generation problem", slog.String("err", err.Error())) - return err + if c.provider.RequiresXDS() { + if err := c.xdsSrv.UpdateSnapshot(); err != nil { + slog.ErrorContext(ctx, "xDS Configuration generation problem", slog.String("err", err.Error())) + return err + } } if !c.cfg.Standalone && c.cfg.TemplatesFile == "" { - // Reconcile Envoy router Deployment and Kubernetes cluster entities - err := c.envoyRunner.reconcile(ctx) + // Reconcile router proxy Deployment and Kubernetes cluster entities. + err := c.proxyRunner.reconcile(ctx) if err != nil { - slog.ErrorContext(ctx, "Error during Envoy router reconciliation", slog.String("err", err.Error())) + slog.ErrorContext(ctx, "Error during router proxy reconciliation", slog.String("err", err.Error())) return err } } diff --git a/cmd/atenet/internal/app/router/dashboard.html b/cmd/atenet/internal/app/router/dashboard.html index 083d0d01..50d00ef1 100644 --- a/cmd/atenet/internal/app/router/dashboard.html +++ b/cmd/atenet/internal/app/router/dashboard.html @@ -252,12 +252,16 @@

atenet Router Status

Namespace Context {{ .Namespace }} +
+ Networking Mode + {{ .NetworkingMode }} +
Component Network Allocation