From 707193f3a9d0540f693d97293410de2a26bad78a Mon Sep 17 00:00:00 2001 From: kahirokunn Date: Fri, 13 Mar 2026 10:08:24 +0900 Subject: [PATCH] Add Gateway API ingress support to kn operator plugin Signed-off-by: kahirokunn --- pkg/command/enable/ingress.go | 30 +++- pkg/command/enable/ingress_test.go | 35 ++++- pkg/command/enable/overlay/ks_ingress.yaml | 4 + pkg/command/install/install.go | 17 ++- pkg/command/install/install_test.go | 130 ++++++++++++++++++ pkg/command/install/overlay/ks_ingress.yaml | 4 + .../install/testdata/overlay/ks_ingress.yaml | 4 + 7 files changed, 213 insertions(+), 11 deletions(-) diff --git a/pkg/command/enable/ingress.go b/pkg/command/enable/ingress.go index e0f9c489..f76c44f9 100644 --- a/pkg/command/enable/ingress.go +++ b/pkg/command/enable/ingress.go @@ -29,10 +29,11 @@ import ( var overlayContent string type ingressFlags struct { - Istio bool - Kourier bool - Contour bool - Namespace string + Istio bool + Kourier bool + Contour bool + GatewayAPI bool + Namespace string } var ingressCmdFlags ingressFlags @@ -48,7 +49,9 @@ func newIngressCommand(p *pkg.OperatorParams) *cobra.Command { # Enable the ingress kourier for Knative Serving kn-operator enable ingress --kourier --namespace knative-serving # Enable the ingress contour for Knative Serving - kn-operator enable ingress --contour --namespace knative-serving`, + kn-operator enable ingress --contour --namespace knative-serving + # Enable the ingress gateway-api for Knative Serving + kn-operator enable ingress --gateway-api --namespace knative-serving`, RunE: func(cmd *cobra.Command, args []string) error { err := validateIngressFlags(ingressCmdFlags) if err != nil { @@ -73,6 +76,10 @@ func newIngressCommand(p *pkg.OperatorParams) *cobra.Command { ingress = "Contour" } + if ingressCmdFlags.GatewayAPI { + ingress = "Gateway API" + } + fmt.Fprintf(cmd.OutOrStdout(), "The ingress %s was enabled in the namespace '%s'.\n", ingress, ingressCmdFlags.Namespace) return nil }, @@ -81,6 +88,7 @@ func newIngressCommand(p *pkg.OperatorParams) *cobra.Command { enableIngressCmd.Flags().BoolVar(&ingressCmdFlags.Istio, "istio", false, "The flag to enable the ingress istio") enableIngressCmd.Flags().BoolVar(&ingressCmdFlags.Kourier, "kourier", false, "The flag to enable the ingress kourier") enableIngressCmd.Flags().BoolVar(&ingressCmdFlags.Contour, "contour", false, "The flag to enable the ingress contour") + enableIngressCmd.Flags().BoolVar(&ingressCmdFlags.GatewayAPI, "gateway-api", false, "The flag to enable the ingress gateway-api") enableIngressCmd.Flags().StringVarP(&ingressCmdFlags.Namespace, "namespace", "n", "", "The namespace of the Knative Operator or the Knative component") return enableIngressCmd @@ -101,6 +109,10 @@ func validateIngressFlags(ingressCMDFlags ingressFlags) error { count++ } + if ingressCMDFlags.GatewayAPI { + count++ + } + if count == 0 { return fmt.Errorf("You need to enable at least one ingress for Knative Serving.") } @@ -135,8 +147,12 @@ func getYamlValuesContent(ingressCMDFlags ingressFlags) string { ingressClass = "contour.ingress.networking.knative.dev" } - content := fmt.Sprintf("#@data/values\n---\nnamespace: %s\nkourier: %t\nistio: %t\ncontour: %t\ningressClass: %s", - ingressCMDFlags.Namespace, ingressCMDFlags.Kourier, ingressCMDFlags.Istio, ingressCMDFlags.Contour, ingressClass) + if ingressCMDFlags.GatewayAPI { + ingressClass = "gateway-api.ingress.networking.knative.dev" + } + + content := fmt.Sprintf("#@data/values\n---\nnamespace: %s\nkourier: %t\nistio: %t\ncontour: %t\ngatewayAPI: %t\ningressClass: %s", + ingressCMDFlags.Namespace, ingressCMDFlags.Kourier, ingressCMDFlags.Istio, ingressCMDFlags.Contour, ingressCMDFlags.GatewayAPI, ingressClass) return content } diff --git a/pkg/command/enable/ingress_test.go b/pkg/command/enable/ingress_test.go index 142f7375..d60611a3 100644 --- a/pkg/command/enable/ingress_test.go +++ b/pkg/command/enable/ingress_test.go @@ -32,14 +32,30 @@ func TestValidateIngressFlags(t *testing.T) { name: "Only Istio enabled", ingressCMDFlags: ingressFlags{Istio: true}, expectedError: nil, + }, { + name: "Only GatewayAPI enabled", + ingressCMDFlags: ingressFlags{GatewayAPI: true}, + expectedError: nil, }, { name: "No ingress enabled", - ingressCMDFlags: ingressFlags{Istio: false, Kourier: false, Contour: false}, + ingressCMDFlags: ingressFlags{Istio: false, Kourier: false, Contour: false, GatewayAPI: false}, expectedError: fmt.Errorf("You need to enable at least one ingress for Knative Serving."), }, { name: "Istio and Kourier enabled", ingressCMDFlags: ingressFlags{Istio: true, Kourier: true}, expectedError: fmt.Errorf("You can specify only one ingress for Knative Serving."), + }, { + name: "GatewayAPI and Istio enabled", + ingressCMDFlags: ingressFlags{GatewayAPI: true, Istio: true}, + expectedError: fmt.Errorf("You can specify only one ingress for Knative Serving."), + }, { + name: "GatewayAPI and Kourier enabled", + ingressCMDFlags: ingressFlags{GatewayAPI: true, Kourier: true}, + expectedError: fmt.Errorf("You can specify only one ingress for Knative Serving."), + }, { + name: "GatewayAPI and Contour enabled", + ingressCMDFlags: ingressFlags{GatewayAPI: true, Contour: true}, + expectedError: fmt.Errorf("You can specify only one ingress for Knative Serving."), }, { name: "Istio, Contour and Kourier enabled", ingressCMDFlags: ingressFlags{Istio: true, Kourier: true, Contour: true}, @@ -77,6 +93,7 @@ namespace: test-serving kourier: false istio: true contour: false +gatewayAPI: false ingressClass: istio.ingress.networking.knative.dev`, }, { name: "Knative Serving with Kourier enabled", @@ -90,6 +107,7 @@ namespace: test-serving kourier: true istio: false contour: false +gatewayAPI: false ingressClass: kourier.ingress.networking.knative.dev`, }, { name: "Knative Serving with Contour enabled", @@ -103,7 +121,22 @@ namespace: test-serving kourier: false istio: false contour: true +gatewayAPI: false ingressClass: contour.ingress.networking.knative.dev`, + }, { + name: "Knative Serving with GatewayAPI enabled", + ingressCMDFlags: ingressFlags{ + Namespace: "test-serving", + GatewayAPI: true, + }, + expectedResult: `#@data/values +--- +namespace: test-serving +kourier: false +istio: false +contour: false +gatewayAPI: true +ingressClass: gateway-api.ingress.networking.knative.dev`, }} { t.Run(tt.name, func(t *testing.T) { result := getYamlValuesContent(tt.ingressCMDFlags) diff --git a/pkg/command/enable/overlay/ks_ingress.yaml b/pkg/command/enable/overlay/ks_ingress.yaml index 0c521c7b..73a6323d 100644 --- a/pkg/command/enable/overlay/ks_ingress.yaml +++ b/pkg/command/enable/overlay/ks_ingress.yaml @@ -24,6 +24,10 @@ spec: contour: #@overlay/match missing_ok=True enabled: #@ data.values.contour + #@overlay/match missing_ok=True + gateway-api: + #@overlay/match missing_ok=True + enabled: #@ data.values.gatewayAPI #@overlay/match missing_ok=True config: #@overlay/match missing_ok=True diff --git a/pkg/command/install/install.go b/pkg/command/install/install.go index 322d7277..f77586d0 100644 --- a/pkg/command/install/install.go +++ b/pkg/command/install/install.go @@ -68,6 +68,7 @@ type installCmdFlags struct { Istio bool Kourier bool Contour bool + GatewayAPI bool } var ( @@ -100,10 +101,11 @@ func (flags *installCmdFlags) fill_defaults() { } // Set the default ingress istio to true - if strings.EqualFold(flags.Component, common.ServingComponent) && !flags.Kourier && !flags.Contour && !flags.Istio { + if strings.EqualFold(flags.Component, common.ServingComponent) && !flags.Kourier && !flags.Contour && !flags.Istio && !flags.GatewayAPI { flags.Istio = true flags.Kourier = false flags.Contour = false + flags.GatewayAPI = false } } @@ -148,6 +150,7 @@ func NewInstallCommand(p *pkg.OperatorParams) *cobra.Command { installCmd.Flags().BoolVar(&installFlags.Istio, "istio", false, "The flag to enable the ingress istio") installCmd.Flags().BoolVar(&installFlags.Kourier, "kourier", false, "The flag to enable the ingress kourier") installCmd.Flags().BoolVar(&installFlags.Contour, "contour", false, "The flag to enable the ingress contour") + installCmd.Flags().BoolVar(&installFlags.GatewayAPI, "gateway-api", false, "The flag to enable the ingress gateway-api") return installCmd } @@ -251,6 +254,10 @@ func validateIngressFlags(installFlags *installCmdFlags) error { count++ } + if installFlags.GatewayAPI { + count++ + } + if strings.EqualFold(installFlags.Component, common.ServingComponent) { if count > 1 { return fmt.Errorf("You can specify only one ingress for Knative Serving.") @@ -357,8 +364,12 @@ func getYamlValuesContent(installFlags *installCmdFlags) string { ingressClass = "contour.ingress.networking.knative.dev" } - content = fmt.Sprintf("%s\nkourier: %t\nistio: %t\ncontour: %t\ningressClass: %s", - content, installFlags.Kourier, installFlags.Istio, installFlags.Contour, ingressClass) + if installFlags.GatewayAPI { + ingressClass = "gateway-api.ingress.networking.knative.dev" + } + + content = fmt.Sprintf("%s\nkourier: %t\nistio: %t\ncontour: %t\ngatewayAPI: %t\ningressClass: %s", + content, installFlags.Kourier, installFlags.Istio, installFlags.Contour, installFlags.GatewayAPI, ingressClass) return content } diff --git a/pkg/command/install/install_test.go b/pkg/command/install/install_test.go index 20ea9b7b..58557a13 100644 --- a/pkg/command/install/install_test.go +++ b/pkg/command/install/install_test.go @@ -101,6 +101,19 @@ func TestFillDefaultsForInstallCmdFlags(t *testing.T) { Version: common.Latest, Istio: true, }, + }, { + name: "Empty istio namespace, namespace and version for serving with GatewayAPI", + inputFlags: installCmdFlags{ + Component: "serving", + GatewayAPI: true, + }, + expectedFlags: installCmdFlags{ + Component: "serving", + IstioNamespace: common.DefaultIstioNamespace, + Namespace: common.DefaultKnativeServingNamespace, + Version: common.Latest, + GatewayAPI: true, + }, }, { name: "Empty namespace and version for eventing", inputFlags: installCmdFlags{ @@ -151,6 +164,20 @@ func TestGetOverlayYamlContent(t *testing.T) { IstioNamespace: "test", }, expectedFile: "testdata/overlay/ks_istio_ns.yaml", + }, { + name: "Knative Serving with GatewayAPI", + installFlags: installCmdFlags{ + Component: "serving", + GatewayAPI: true, + }, + expectedFile: "testdata/overlay/ks_ingress.yaml", + }, { + name: "Knative Serving with Contour", + installFlags: installCmdFlags{ + Component: "serving", + Contour: true, + }, + expectedFile: "testdata/overlay/ks_ingress.yaml", }, { name: "Knative Eventing", installFlags: installCmdFlags{ @@ -248,7 +275,42 @@ version: '1.0' kourier: true istio: false contour: false +gatewayAPI: false ingressClass: kourier.ingress.networking.knative.dev`, + }, { + name: "Knative Serving with gateway-api and version", + installFlags: installCmdFlags{ + Version: "1.0", + Component: "serving", + GatewayAPI: true, + }, + expectedResult: `#@data/values +--- +name: knative-serving +namespace: knative-serving +version: '1.0' +kourier: false +istio: false +contour: false +gatewayAPI: true +ingressClass: gateway-api.ingress.networking.knative.dev`, + }, { + name: "Knative Serving with contour and version", + installFlags: installCmdFlags{ + Version: "1.0", + Component: "serving", + Contour: true, + }, + expectedResult: `#@data/values +--- +name: knative-serving +namespace: knative-serving +version: '1.0' +kourier: false +istio: false +contour: true +gatewayAPI: false +ingressClass: contour.ingress.networking.knative.dev`, }, { name: "Knative Serving with istio and version", installFlags: installCmdFlags{ @@ -327,6 +389,74 @@ namespace: test`, } } +func TestValidateIngressFlags(t *testing.T) { + for _, tt := range []struct { + name string + installFlags installCmdFlags + expectedError error + }{{ + name: "Serving with only Istio enabled", + installFlags: installCmdFlags{ + Component: "serving", + Istio: true, + }, + expectedError: nil, + }, { + name: "Serving with only GatewayAPI enabled", + installFlags: installCmdFlags{ + Component: "serving", + GatewayAPI: true, + }, + expectedError: nil, + }, { + name: "Serving with GatewayAPI and Istio enabled", + installFlags: installCmdFlags{ + Component: "serving", + GatewayAPI: true, + Istio: true, + }, + expectedError: fmt.Errorf("You can specify only one ingress for Knative Serving."), + }, { + name: "Serving with GatewayAPI and Kourier enabled", + installFlags: installCmdFlags{ + Component: "serving", + GatewayAPI: true, + Kourier: true, + }, + expectedError: fmt.Errorf("You can specify only one ingress for Knative Serving."), + }, { + name: "Serving with GatewayAPI and Contour enabled", + installFlags: installCmdFlags{ + Component: "serving", + GatewayAPI: true, + Contour: true, + }, + expectedError: fmt.Errorf("You can specify only one ingress for Knative Serving."), + }, { + name: "Eventing with GatewayAPI enabled", + installFlags: installCmdFlags{ + Component: "eventing", + GatewayAPI: true, + }, + expectedError: fmt.Errorf("You can only specify the ingress for Knative Serving."), + }, { + name: "No component with no ingress", + installFlags: installCmdFlags{ + Component: "serving", + }, + expectedError: nil, + }} { + t.Run(tt.name, func(t *testing.T) { + err := validateIngressFlags(&tt.installFlags) + if tt.expectedError == nil { + testingUtil.AssertEqual(t, err, nil) + } else { + testingUtil.AssertEqual(t, err.Error(), tt.expectedError.Error()) + } + }) + } +} + func TestVersionWebhook(t *testing.T) { for _, tt := range []struct { name string diff --git a/pkg/command/install/overlay/ks_ingress.yaml b/pkg/command/install/overlay/ks_ingress.yaml index acc9a491..7af8be93 100644 --- a/pkg/command/install/overlay/ks_ingress.yaml +++ b/pkg/command/install/overlay/ks_ingress.yaml @@ -28,6 +28,10 @@ spec: contour: #@overlay/match missing_ok=True enabled: #@ data.values.contour + #@overlay/match missing_ok=True + gateway-api: + #@overlay/match missing_ok=True + enabled: #@ data.values.gatewayAPI #@overlay/match missing_ok=True config: #@overlay/match missing_ok=True diff --git a/pkg/command/install/testdata/overlay/ks_ingress.yaml b/pkg/command/install/testdata/overlay/ks_ingress.yaml index acc9a491..7af8be93 100644 --- a/pkg/command/install/testdata/overlay/ks_ingress.yaml +++ b/pkg/command/install/testdata/overlay/ks_ingress.yaml @@ -28,6 +28,10 @@ spec: contour: #@overlay/match missing_ok=True enabled: #@ data.values.contour + #@overlay/match missing_ok=True + gateway-api: + #@overlay/match missing_ok=True + enabled: #@ data.values.gatewayAPI #@overlay/match missing_ok=True config: #@overlay/match missing_ok=True