diff --git a/cmd/main.go b/cmd/main.go index ce15ea6..fc149c6 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -135,6 +135,7 @@ func main() { GatewayName: os.Getenv("SEI_GATEWAY_NAME"), GatewayNamespace: os.Getenv("SEI_GATEWAY_NAMESPACE"), GatewayDomain: os.Getenv("SEI_GATEWAY_DOMAIN"), + GatewaySectionName: os.Getenv("SEI_GATEWAY_SECTION_NAME"), } if err := platformCfg.Validate(); err != nil { @@ -180,13 +181,14 @@ func main() { //nolint:staticcheck // migrating to events.EventRecorder API is a separate effort recorder := mgr.GetEventRecorderFor("seinodedeployment-controller") if err := (&nodedeploymentcontroller.SeiNodeDeploymentReconciler{ - Client: kc, - Scheme: mgr.GetScheme(), - Recorder: recorder, - ControllerSA: controllerSA, - GatewayName: platformCfg.GatewayName, - GatewayNamespace: platformCfg.GatewayNamespace, - GatewayDomain: platformCfg.GatewayDomain, + Client: kc, + Scheme: mgr.GetScheme(), + Recorder: recorder, + ControllerSA: controllerSA, + GatewayName: platformCfg.GatewayName, + GatewayNamespace: platformCfg.GatewayNamespace, + GatewayDomain: platformCfg.GatewayDomain, + GatewaySectionName: platformCfg.GatewaySectionName, PlanExecutor: &planner.Executor[*seiv1alpha1.SeiNodeDeployment]{ Client: kc, ConfigFor: func(ctx context.Context, group *seiv1alpha1.SeiNodeDeployment) task.ExecutionConfig { diff --git a/internal/controller/nodedeployment/controller.go b/internal/controller/nodedeployment/controller.go index 5b4c6a0..652be0a 100644 --- a/internal/controller/nodedeployment/controller.go +++ b/internal/controller/nodedeployment/controller.go @@ -40,12 +40,12 @@ type SeiNodeDeploymentReconciler struct { // controller can always reach the seictl sidecar. ControllerSA string - // GatewayName, GatewayNamespace, and GatewayDomain identify the platform - // Gateway for HTTPRoute parentRefs and hostname derivation. - // Read from SEI_GATEWAY_NAME / SEI_GATEWAY_NAMESPACE / SEI_GATEWAY_DOMAIN. - GatewayName string - GatewayNamespace string - GatewayDomain string + // GatewayName, GatewayNamespace, GatewayDomain, and GatewaySectionName + // identify the platform Gateway for HTTPRoute parentRefs and hostname derivation. + GatewayName string + GatewayNamespace string + GatewayDomain string + GatewaySectionName string // PlanExecutor drives group-level task plans (e.g. genesis assembly). PlanExecutor planner.PlanExecutor[*seiv1alpha1.SeiNodeDeployment] diff --git a/internal/controller/nodedeployment/networking.go b/internal/controller/nodedeployment/networking.go index d9c90d5..de8babf 100644 --- a/internal/controller/nodedeployment/networking.go +++ b/internal/controller/nodedeployment/networking.go @@ -265,7 +265,7 @@ func (r *SeiNodeDeploymentReconciler) reconcileHTTPRoutes(ctx context.Context, g } for _, er := range routes { - desired := generateHTTPRoute(group, er, r.GatewayName, r.GatewayNamespace) + desired := generateHTTPRoute(group, er, r.GatewayName, r.GatewayNamespace, r.GatewaySectionName) if err := ctrl.SetControllerReference(group, desired, r.Scheme); err != nil { return fmt.Errorf("setting owner reference on HTTPRoute %s: %w", er.Name, err) } @@ -318,7 +318,7 @@ func (r *SeiNodeDeploymentReconciler) deleteOrphanedHTTPRoutes(ctx context.Conte return nil } -func generateHTTPRoute(group *seiv1alpha1.SeiNodeDeployment, er effectiveRoute, gatewayName, gatewayNamespace string) *unstructured.Unstructured { +func generateHTTPRoute(group *seiv1alpha1.SeiNodeDeployment, er effectiveRoute, gatewayName, gatewayNamespace, gatewaySectionName string) *unstructured.Unstructured { svcName := externalServiceName(group) hostnames := make([]any, len(er.Hostnames)) @@ -330,6 +330,9 @@ func generateHTTPRoute(group *seiv1alpha1.SeiNodeDeployment, er effectiveRoute, "name": gatewayName, "namespace": gatewayNamespace, } + if gatewaySectionName != "" { + parentRef["sectionName"] = gatewaySectionName + } route := &unstructured.Unstructured{ Object: map[string]any{ diff --git a/internal/controller/nodedeployment/networking_test.go b/internal/controller/nodedeployment/networking_test.go index cf42623..8d2aa67 100644 --- a/internal/controller/nodedeployment/networking_test.go +++ b/internal/controller/nodedeployment/networking_test.go @@ -184,7 +184,7 @@ func TestGenerateHTTPRoute_HostnamePattern(t *testing.T) { g.Expect(routes).NotTo(BeEmpty()) for _, er := range routes { - route := generateHTTPRoute(group, er, "sei-gateway", "istio-system") + route := generateHTTPRoute(group, er, "sei-gateway", "istio-system", "") spec := route.Object["spec"].(map[string]any) hostnames := spec["hostnames"].([]any) g.Expect(hostnames).To(HaveLen(1)) @@ -229,7 +229,7 @@ func TestGenerateHTTPRoute_BasicFields(t *testing.T) { routes := resolveEffectiveRoutes(group, "prod.platform.sei.io") g.Expect(routes).NotTo(BeEmpty()) - route := generateHTTPRoute(group, routes[0], "sei-gateway", "istio-system") + route := generateHTTPRoute(group, routes[0], "sei-gateway", "istio-system", "") g.Expect(route.GetNamespace()).To(Equal("sei")) @@ -251,7 +251,7 @@ func TestGenerateHTTPRoute_ManagedByAnnotation(t *testing.T) { } routes := resolveEffectiveRoutes(group, "prod.platform.sei.io") - route := generateHTTPRoute(group, routes[0], "sei-gateway", "istio-system") + route := generateHTTPRoute(group, routes[0], "sei-gateway", "istio-system", "") g.Expect(route.GetAnnotations()).To(HaveKeyWithValue("sei.io/managed-by", "seinodedeployment")) } @@ -264,7 +264,7 @@ func TestGenerateHTTPRoute_BackendRef(t *testing.T) { } routes := resolveEffectiveRoutes(group, "prod.platform.sei.io") - route := generateHTTPRoute(group, routes[0], "sei-gateway", "istio-system") + route := generateHTTPRoute(group, routes[0], "sei-gateway", "istio-system", "") spec := route.Object["spec"].(map[string]any) rules := spec["rules"].([]any) @@ -297,7 +297,7 @@ func TestGenerateHTTPRoute_GRPCRoutePort(t *testing.T) { } g.Expect(grpcRoute.Name).NotTo(BeEmpty(), "grpc route should exist") - httpRoute := generateHTTPRoute(group, grpcRoute, "sei-gateway", "istio-system") + httpRoute := generateHTTPRoute(group, grpcRoute, "sei-gateway", "istio-system", "") spec := httpRoute.Object["spec"].(map[string]any) hostnames := spec["hostnames"].([]any) g.Expect(hostnames).To(ConsistOf("pacific-1-rpc.grpc.prod.platform.sei.io")) @@ -319,6 +319,39 @@ func TestIsProtocolActiveForMode_EVMMapping(t *testing.T) { g.Expect(isProtocolActiveForMode("grpc", activePorts)).To(BeFalse()) } +// --- Gateway sectionName --- + +func TestGenerateHTTPRoute_SectionName_IncludedWhenSet(t *testing.T) { + g := NewWithT(t) + group := newTestGroup("pacific-1", "sei") + group.Spec.Networking = &seiv1alpha1.NetworkingConfig{ + Service: &seiv1alpha1.ExternalServiceConfig{}, + } + routes := resolveEffectiveRoutes(group, "test.platform.sei.io") + route := generateHTTPRoute(group, routes[0], "sei-gateway", "gateway", "https") + + spec := route.Object["spec"].(map[string]any) + parentRefs := spec["parentRefs"].([]any) + ref := parentRefs[0].(map[string]any) + g.Expect(ref["sectionName"]).To(Equal("https")) +} + +func TestGenerateHTTPRoute_SectionName_OmittedWhenEmpty(t *testing.T) { + g := NewWithT(t) + group := newTestGroup("pacific-1", "sei") + group.Spec.Networking = &seiv1alpha1.NetworkingConfig{ + Service: &seiv1alpha1.ExternalServiceConfig{}, + } + routes := resolveEffectiveRoutes(group, "test.platform.sei.io") + route := generateHTTPRoute(group, routes[0], "sei-gateway", "gateway", "") + + spec := route.Object["spec"].(map[string]any) + parentRefs := spec["parentRefs"].([]any) + ref := parentRefs[0].(map[string]any) + _, hasSectionName := ref["sectionName"] + g.Expect(hasSectionName).To(BeFalse()) +} + // --- AuthorizationPolicy --- func TestGenerateAuthorizationPolicy_BasicStructure(t *testing.T) { diff --git a/internal/platform/platform.go b/internal/platform/platform.go index 41bc9a5..9aa6604 100644 --- a/internal/platform/platform.go +++ b/internal/platform/platform.go @@ -37,9 +37,10 @@ type Config struct { GenesisBucket string GenesisRegion string - GatewayName string - GatewayNamespace string - GatewayDomain string + GatewayName string + GatewayNamespace string + GatewayDomain string + GatewaySectionName string } // Validate returns an error if required fields are missing. @@ -67,6 +68,7 @@ func (c Config) Validate() error { "SEI_GATEWAY_NAME": c.GatewayName, "SEI_GATEWAY_NAMESPACE": c.GatewayNamespace, "SEI_GATEWAY_DOMAIN": c.GatewayDomain, + "SEI_GATEWAY_SECTION_NAME": c.GatewaySectionName, } for name, val := range required { if val == "" { diff --git a/internal/platform/platformtest/config.go b/internal/platform/platformtest/config.go index 9d90b27..2c85aa7 100644 --- a/internal/platform/platformtest/config.go +++ b/internal/platform/platformtest/config.go @@ -29,5 +29,6 @@ func Config() platform.Config { GatewayName: "sei-gateway", GatewayNamespace: "istio-system", GatewayDomain: "test.platform.sei.io", + GatewaySectionName: "https", } }