diff --git a/pkg/i2gw/intermediate/provider_gce.go b/pkg/i2gw/intermediate/provider_gce.go index d37aec68..4cfa94ad 100644 --- a/pkg/i2gw/intermediate/provider_gce.go +++ b/pkg/i2gw/intermediate/provider_gce.go @@ -18,6 +18,10 @@ package intermediate type GceGatewayIR struct { EnableHTTPSRedirect bool + SslPolicy *SslPolicyConfig +} +type SslPolicyConfig struct { + Name string } type GceHTTPRouteIR struct{} type GceServiceIR struct { diff --git a/pkg/i2gw/providers/gce/extensions/input_extensions.go b/pkg/i2gw/providers/gce/extensions/input_extensions.go index 762bf7c1..6dabe62b 100644 --- a/pkg/i2gw/providers/gce/extensions/input_extensions.go +++ b/pkg/i2gw/providers/gce/extensions/input_extensions.go @@ -21,6 +21,7 @@ import ( "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate" backendconfigv1 "k8s.io/ingress-gce/pkg/apis/backendconfig/v1" + frontendconfigv1beta1 "k8s.io/ingress-gce/pkg/apis/frontendconfig/v1beta1" ) func ValidateBeConfig(beConfig *backendconfigv1.BackendConfig) error { @@ -52,3 +53,9 @@ func BuildIRSecurityPolicyConfig(beConfig *backendconfigv1.BackendConfig) *inter Name: beConfig.Spec.SecurityPolicy.Name, } } + +func BuildIRSslPolicyConfig(feConfig *frontendconfigv1beta1.FrontendConfig) *intermediate.SslPolicyConfig { + return &intermediate.SslPolicyConfig{ + Name: *feConfig.Spec.SslPolicy, + } +} diff --git a/pkg/i2gw/providers/gce/extensions/output_extensions.go b/pkg/i2gw/providers/gce/extensions/output_extensions.go index 19b4ca2e..5962e000 100644 --- a/pkg/i2gw/providers/gce/extensions/output_extensions.go +++ b/pkg/i2gw/providers/gce/extensions/output_extensions.go @@ -21,7 +21,7 @@ import ( "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate" ) -func BuildBackendPolicySessionAffinityConfig(serviceIR intermediate.ProviderSpecificServiceIR) *gkegatewayv1.SessionAffinityConfig { +func BuildGCPBackendPolicySessionAffinityConfig(serviceIR intermediate.ProviderSpecificServiceIR) *gkegatewayv1.SessionAffinityConfig { affinityType := serviceIR.Gce.SessionAffinity.AffinityType saConfig := gkegatewayv1.SessionAffinityConfig{ Type: &affinityType, @@ -32,7 +32,11 @@ func BuildBackendPolicySessionAffinityConfig(serviceIR intermediate.ProviderSpec return &saConfig } -func BuildBackendPolicySecurityPolicyConfig(serviceIR intermediate.ProviderSpecificServiceIR) *string { +func BuildGCPBackendPolicySecurityPolicyConfig(serviceIR intermediate.ProviderSpecificServiceIR) *string { securityPolicy := serviceIR.Gce.SecurityPolicy.Name return &securityPolicy } + +func BuildGCPGatewayPolicySecurityPolicyConfig(gatewayIR intermediate.ProviderSpecificGatewayIR) string { + return gatewayIR.Gce.SslPolicy.Name +} diff --git a/pkg/i2gw/providers/gce/gateway_converter.go b/pkg/i2gw/providers/gce/gateway_converter.go index 2c973d73..e7f3f86a 100644 --- a/pkg/i2gw/providers/gce/gateway_converter.go +++ b/pkg/i2gw/providers/gce/gateway_converter.go @@ -42,13 +42,58 @@ func (c *irToGatewayResourcesConverter) irToGateway(ir intermediate.IR) (i2gw.Ga if len(errs) != 0 { return i2gw.GatewayResources{}, errs } + buildGceGatewayExtensions(ir, &gatewayResources) buildGceServiceExtensions(ir, &gatewayResources) return gatewayResources, nil } +func buildGceGatewayExtensions(ir intermediate.IR, gatewayResources *i2gw.GatewayResources) { + for gwyKey, gatewayContext := range ir.Gateways { + gwyPolicy := addGatewayPolicyIfConfigured(gwyKey, gatewayContext.ProviderSpecificIR) + if gwyPolicy == nil { + continue + } + obj, err := i2gw.CastToUnstructured(gwyPolicy) + if err != nil { + notify(notifications.ErrorNotification, "Failed to cast GCPGatewayPolicy to unstructured", gwyPolicy) + continue + } + gatewayResources.GatewayExtensions = append(gatewayResources.GatewayExtensions, *obj) + } +} + +func addGatewayPolicyIfConfigured(gatewayNamespacedName types.NamespacedName, gatewayIR intermediate.ProviderSpecificGatewayIR) *gkegatewayv1.GCPGatewayPolicy { + if gatewayIR.Gce == nil { + return nil + } + // If there is no specification related to GCPGatewayPolicy feature, return nil. + if gatewayIR.Gce.SslPolicy == nil { + return nil + } + gcpGatewayPolicy := gkegatewayv1.GCPGatewayPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: gatewayNamespacedName.Namespace, + Name: gatewayNamespacedName.Name, + }, + Spec: gkegatewayv1.GCPGatewayPolicySpec{ + Default: &gkegatewayv1.GCPGatewayPolicyConfig{}, + TargetRef: gatewayv1alpha2.NamespacedPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: gatewayv1.ObjectName(gatewayNamespacedName.Name), + }, + }, + } + gcpGatewayPolicy.SetGroupVersionKind(GCPGatewayPolicyGVK) + if gatewayIR.Gce.SslPolicy != nil { + gcpGatewayPolicy.Spec.Default.SslPolicy = extensions.BuildGCPGatewayPolicySecurityPolicyConfig(gatewayIR) + } + return &gcpGatewayPolicy +} + func buildGceServiceExtensions(ir intermediate.IR, gatewayResources *i2gw.GatewayResources) { for svcKey, serviceIR := range ir.Services { - bePolicy := addBackendPolicyIfConfigured(svcKey, serviceIR) + bePolicy := addGCPBackendPolicyIfConfigured(svcKey, serviceIR) if bePolicy == nil { continue } @@ -61,11 +106,11 @@ func buildGceServiceExtensions(ir intermediate.IR, gatewayResources *i2gw.Gatewa } } -func addBackendPolicyIfConfigured(serviceNamespacedName types.NamespacedName, serviceIR intermediate.ProviderSpecificServiceIR) *gkegatewayv1.GCPBackendPolicy { +func addGCPBackendPolicyIfConfigured(serviceNamespacedName types.NamespacedName, serviceIR intermediate.ProviderSpecificServiceIR) *gkegatewayv1.GCPBackendPolicy { if serviceIR.Gce == nil { return nil } - backendPolicy := gkegatewayv1.GCPBackendPolicy{ + gcpBackendPolicy := gkegatewayv1.GCPBackendPolicy{ ObjectMeta: metav1.ObjectMeta{ Namespace: serviceNamespacedName.Namespace, Name: serviceNamespacedName.Name, @@ -79,14 +124,14 @@ func addBackendPolicyIfConfigured(serviceNamespacedName types.NamespacedName, se }, }, } - backendPolicy.SetGroupVersionKind(GCPBackendPolicyGVK) + gcpBackendPolicy.SetGroupVersionKind(GCPBackendPolicyGVK) if serviceIR.Gce.SessionAffinity != nil { - backendPolicy.Spec.Default.SessionAffinity = extensions.BuildBackendPolicySessionAffinityConfig(serviceIR) + gcpBackendPolicy.Spec.Default.SessionAffinity = extensions.BuildGCPBackendPolicySessionAffinityConfig(serviceIR) } if serviceIR.Gce.SecurityPolicy != nil { - backendPolicy.Spec.Default.SecurityPolicy = extensions.BuildBackendPolicySecurityPolicyConfig(serviceIR) + gcpBackendPolicy.Spec.Default.SecurityPolicy = extensions.BuildGCPBackendPolicySecurityPolicyConfig(serviceIR) } - return &backendPolicy + return &gcpBackendPolicy } diff --git a/pkg/i2gw/providers/gce/gateway_converter_test.go b/pkg/i2gw/providers/gce/gateway_converter_test.go index ace82377..4f17b542 100644 --- a/pkg/i2gw/providers/gce/gateway_converter_test.go +++ b/pkg/i2gw/providers/gce/gateway_converter_test.go @@ -36,9 +36,10 @@ import ( ) const ( - testGatewayName = "test-gateway" - testHTTPRouteName = "test-http-route" - testSaBackendPolicyName = testServiceName + testGatewayName = "test-gateway" + testHTTPRouteName = "test-http-route" + testSaGCPBackendPolicyName = testServiceName + testSslGCPGatewayPolicyName = testGatewayName ) var ( @@ -96,7 +97,7 @@ var ( }, ObjectMeta: metav1.ObjectMeta{ Namespace: testNamespace, - Name: testSaBackendPolicyName, + Name: testSaGCPBackendPolicyName, }, Spec: gkegatewayv1.GCPBackendPolicySpec{ Default: &gkegatewayv1.GCPBackendPolicyConfig{ @@ -120,7 +121,7 @@ var ( }, ObjectMeta: metav1.ObjectMeta{ Namespace: testNamespace, - Name: testSaBackendPolicyName, + Name: testSaGCPBackendPolicyName, }, Spec: gkegatewayv1.GCPBackendPolicySpec{ Default: &gkegatewayv1.GCPBackendPolicyConfig{ @@ -143,7 +144,7 @@ var ( }, ObjectMeta: metav1.ObjectMeta{ Namespace: testNamespace, - Name: testSaBackendPolicyName, + Name: testSaGCPBackendPolicyName, }, Spec: gkegatewayv1.GCPBackendPolicySpec{ Default: &gkegatewayv1.GCPBackendPolicyConfig{ @@ -156,6 +157,27 @@ var ( }, }, } + + testSslGCPGatewayPolicy = gkegatewayv1.GCPGatewayPolicy{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "networking.gke.io/v1", + Kind: "GCPGatewayPolicy", + }, + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: testSslGCPGatewayPolicyName, + }, + Spec: gkegatewayv1.GCPGatewayPolicySpec{ + Default: &gkegatewayv1.GCPGatewayPolicyConfig{ + SslPolicy: testSslPolicy, + }, + TargetRef: v1alpha2.NamespacedPolicyTargetReference{ + Group: "gateway.networking.k8s.io", + Kind: "Gateway", + Name: gatewayv1.ObjectName(testGatewayName), + }, + }, + } ) func Test_irToGateway(t *testing.T) { @@ -171,6 +193,10 @@ func Test_irToGateway(t *testing.T) { if err != nil { t.Errorf("Failed to generate unstructured GCP Backend Policy with Security Policy feature: %v", err) } + testSslGCPGatewayPolicyUnstructured, err := i2gw.CastToUnstructured(&testSslGCPGatewayPolicy) + if err != nil { + t.Errorf("Failed to generate unstructured GCP Gateway Policy with Ssl Policy feature: %v", err) + } testCases := []struct { name string @@ -287,6 +313,38 @@ func Test_irToGateway(t *testing.T) { }, expectedErrors: field.ErrorList{}, }, + { + name: "ingress with a Frontend Config specifying Ssl Policy", + ir: intermediate.IR{ + Gateways: map[types.NamespacedName]intermediate.GatewayContext{ + {Namespace: testNamespace, Name: testGatewayName}: { + Gateway: testGateway, + ProviderSpecificIR: intermediate.ProviderSpecificGatewayIR{ + Gce: &intermediate.GceGatewayIR{ + SslPolicy: &intermediate.SslPolicyConfig{Name: testSslPolicy}, + }, + }, + }, + }, + HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ + {Namespace: testNamespace, Name: testHTTPRouteName}: { + HTTPRoute: testHTTPRoute, + }, + }, + }, + expectedGatewayResources: i2gw.GatewayResources{ + Gateways: map[types.NamespacedName]gatewayv1.Gateway{ + {Namespace: testNamespace, Name: testGatewayName}: testGateway, + }, + HTTPRoutes: map[types.NamespacedName]gatewayv1.HTTPRoute{ + {Namespace: testNamespace, Name: testHTTPRouteName}: testHTTPRoute, + }, + GatewayExtensions: []unstructured.Unstructured{ + *testSslGCPGatewayPolicyUnstructured, + }, + }, + expectedErrors: field.ErrorList{}, + }, } for _, tc := range testCases { diff --git a/pkg/i2gw/providers/gce/gce.go b/pkg/i2gw/providers/gce/gce.go index a355ce86..024aad2e 100644 --- a/pkg/i2gw/providers/gce/gce.go +++ b/pkg/i2gw/providers/gce/gce.go @@ -25,6 +25,7 @@ import ( "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/notifications" "k8s.io/apimachinery/pkg/util/validation/field" backendconfigv1 "k8s.io/ingress-gce/pkg/apis/backendconfig/v1" + frontendconfigv1beta1 "k8s.io/ingress-gce/pkg/apis/frontendconfig/v1beta1" ) const ProviderName = "gce" @@ -42,12 +43,15 @@ type Provider struct { } func NewProvider(conf *i2gw.ProviderConf) i2gw.Provider { - // Add BackendConfig to Schema when reading in-cluster so these resources - // can be recognized. + // Add BackendConfig and FrontendConfig to Schema when reading in-cluster + // so these resources can be recognized. if conf.Client != nil { if err := backendconfigv1.AddToScheme(conf.Client.Scheme()); err != nil { notify(notifications.ErrorNotification, "Failed to add v1 BackendConfig Scheme") } + if err := frontendconfigv1beta1.AddToScheme(conf.Client.Scheme()); err != nil { + notify(notifications.ErrorNotification, "Failed to add v1beta1 FrontendConfig Scheme") + } } return &Provider{ storage: newResourcesStorage(), diff --git a/pkg/i2gw/providers/gce/ir_converter.go b/pkg/i2gw/providers/gce/ir_converter.go index e0f11066..b3b03d53 100644 --- a/pkg/i2gw/providers/gce/ir_converter.go +++ b/pkg/i2gw/providers/gce/ir_converter.go @@ -32,6 +32,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/validation/field" backendconfigv1 "k8s.io/ingress-gce/pkg/apis/backendconfig/v1" + frontendconfigv1beta1 "k8s.io/ingress-gce/pkg/apis/frontendconfig/v1beta1" ) type contextKey int @@ -82,10 +83,71 @@ func (c *resourcesToIRConverter) convertToIR(storage *storage) (intermediate.IR, if len(errs) > 0 { return intermediate.IR{}, errs } + buildGceGatewayIR(c.ctx, storage, &ir) buildGceServiceIR(c.ctx, storage, &ir) return ir, errs } +func buildGceGatewayIR(ctx context.Context, storage *storage, ir *intermediate.IR) { + if ir.Gateways == nil { + ir.Gateways = make(map[types.NamespacedName]intermediate.GatewayContext) + } + + feConfigToGwys := getFrontendConfigMapping(ctx, storage) + for feConfigKey, feConfig := range storage.FrontendConfigs { + if feConfig == nil { + continue + } + gceGatewayIR := feConfigToGceGatewayIR(feConfig) + gateways := feConfigToGwys[feConfigKey] + + for _, gwyKey := range gateways { + gatewayContext := ir.Gateways[gwyKey] + gatewayContext.ProviderSpecificIR.Gce = &gceGatewayIR + ir.Gateways[gwyKey] = gatewayContext + } + } +} + +type gatewayNames []types.NamespacedName + +func getFrontendConfigMapping(ctx context.Context, storage *storage) map[types.NamespacedName]gatewayNames { + feConfigToGwys := make(map[types.NamespacedName]gatewayNames) + + for _, ingress := range storage.Ingresses { + gwyKey := types.NamespacedName{Namespace: ingress.Namespace, Name: common.GetIngressClass(*ingress)} + // ing := types.NamespacedName{Namespace: ingress.Namespace, Name: ingress.Name} + ctx = context.WithValue(ctx, serviceKey, ingress) + + feConfigName, exists := getFrontendConfigAnnotation(ingress) + if exists { + feConfigKey := types.NamespacedName{Namespace: ingress.Namespace, Name: feConfigName} + feConfigToGwys[feConfigKey] = append(feConfigToGwys[feConfigKey], gwyKey) + continue + } + + } + return feConfigToGwys +} + +// Get names of the FrontendConfig in the cluster based on the FrontendConfig +// annotation on k8s Services. +func getFrontendConfigAnnotation(ing *networkingv1.Ingress) (string, bool) { + val, ok := ing.ObjectMeta.Annotations[frontendConfigKey] + if !ok { + return "", false + } + return val, true +} + +func feConfigToGceGatewayIR(feConfig *frontendconfigv1beta1.FrontendConfig) intermediate.GceGatewayIR { + var gceGatewayIR intermediate.GceGatewayIR + if feConfig.Spec.SslPolicy != nil { + gceGatewayIR.SslPolicy = extensions.BuildIRSslPolicyConfig(feConfig) + } + return gceGatewayIR +} + type serviceNames []types.NamespacedName func buildGceServiceIR(ctx context.Context, storage *storage, ir *intermediate.IR) { diff --git a/pkg/i2gw/providers/gce/ir_converter_test.go b/pkg/i2gw/providers/gce/ir_converter_test.go index 3ee8ceec..9b06a608 100644 --- a/pkg/i2gw/providers/gce/ir_converter_test.go +++ b/pkg/i2gw/providers/gce/ir_converter_test.go @@ -35,6 +35,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/validation/field" backendconfigv1 "k8s.io/ingress-gce/pkg/apis/backendconfig/v1" + frontendconfigv1beta1 "k8s.io/ingress-gce/pkg/apis/frontendconfig/v1beta1" gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" ) @@ -46,43 +47,28 @@ const ( gExact = gatewayv1.PathMatchExact implSpecificPathType = networkingv1.PathTypeImplementationSpecific - testNamespace = "default" - testHost = "test.mydomain.com" - testServiceName = "test-service" - testBackendConfigName = "test-backendconfig" - testSecurityPolicy = "test-security-policy" - testCookieTTLSec = int64(10) + testNamespace = "default" + testHost = "test.mydomain.com" + testIngressName = "test-ingress" + testServiceName = "test-service" + testBackendConfigName = "test-backendconfig" + testFrontendConfigName = "test-frontendconfig" + testSecurityPolicy = "test-security-policy" + testCookieTTLSec = int64(10) + testSslPolicy = "test-ssl-policy" ) func Test_convertToIR(t *testing.T) { - extIngClassIngressName := "gce-ingress-class" - intIngClassIngressName := "gce-internal-ingress-class" - noIngClassIngressName := "no-ingress-class" - - testExtIngress := getTestIngress(testNamespace, extIngClassIngressName, testServiceName, true) - testIntIngress := getTestIngress(testNamespace, intIngClassIngressName, testServiceName, false) - testCases := []struct { - name string - ingresses map[types.NamespacedName]*networkingv1.Ingress - services map[types.NamespacedName]*apiv1.Service - backendConfigs map[types.NamespacedName]*backendconfigv1.BackendConfig + name string + + modify func(storage *storage) expectedIR intermediate.IR expectedErrors field.ErrorList }{ { - name: "gce ingress class", - ingresses: map[types.NamespacedName]*networkingv1.Ingress{ - {Namespace: testNamespace, Name: extIngClassIngressName}: testExtIngress, - }, - services: map[types.NamespacedName]*apiv1.Service{ - {Namespace: testNamespace, Name: testServiceName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testServiceName, - }, - }, - }, + name: "gce ingress class", + modify: func(_ *storage) {}, expectedIR: intermediate.IR{ Gateways: map[types.NamespacedName]intermediate.GatewayContext{ {Namespace: testNamespace, Name: gceIngressClass}: { @@ -100,9 +86,9 @@ func Test_convertToIR(t *testing.T) { }, }, HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ - {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName)}: { + {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName)}: { HTTPRoute: gatewayv1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName), Namespace: testNamespace}, + ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName), Namespace: testNamespace}, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{{ @@ -141,16 +127,11 @@ func Test_convertToIR(t *testing.T) { }, { name: "gce-internal ingress class", - ingresses: map[types.NamespacedName]*networkingv1.Ingress{ - {Namespace: testNamespace, Name: intIngClassIngressName}: testIntIngress, - }, - services: map[types.NamespacedName]*apiv1.Service{ - {Namespace: testNamespace, Name: testServiceName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testServiceName, - }, - }, + modify: func(storage *storage) { + // Update Ingress class to be gceL7ILBIngressClass. + testIngress := storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] + testIngress.Annotations = map[string]string{networkingv1beta1.AnnotationIngressClass: gceL7ILBIngressClass} + storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] = testIngress }, expectedIR: intermediate.IR{ Gateways: map[types.NamespacedName]intermediate.GatewayContext{ @@ -165,14 +146,13 @@ func Test_convertToIR(t *testing.T) { Protocol: gatewayv1.HTTPProtocolType, Hostname: common.PtrTo(gatewayv1.Hostname(testHost)), }}, - }, - }, + }}, }, }, HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ - {Namespace: testNamespace, Name: "gce-internal-ingress-class-test-mydomain-com"}: { + {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName)}: { HTTPRoute: gatewayv1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{Name: "gce-internal-ingress-class-test-mydomain-com", Namespace: testNamespace}, + ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName), Namespace: testNamespace}, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{{ @@ -211,42 +191,11 @@ func Test_convertToIR(t *testing.T) { }, { name: "empty ingress class, default to gce ingress class", - ingresses: map[types.NamespacedName]*networkingv1.Ingress{ - {Namespace: testNamespace, Name: noIngClassIngressName}: { - ObjectMeta: metav1.ObjectMeta{ - Name: noIngClassIngressName, - Namespace: testNamespace, - }, - Spec: networkingv1.IngressSpec{ - Rules: []networkingv1.IngressRule{{ - Host: testHost, - IngressRuleValue: networkingv1.IngressRuleValue{ - HTTP: &networkingv1.HTTPIngressRuleValue{ - Paths: []networkingv1.HTTPIngressPath{{ - Path: "/", - PathType: common.PtrTo(iPrefix), - Backend: networkingv1.IngressBackend{ - Service: &networkingv1.IngressServiceBackend{ - Name: testServiceName, - Port: networkingv1.ServiceBackendPort{ - Number: 80, - }, - }, - }, - }}, - }, - }, - }}, - }, - }, - }, - services: map[types.NamespacedName]*apiv1.Service{ - {Namespace: testNamespace, Name: testServiceName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testServiceName, - }, - }, + modify: func(storage *storage) { + // Update Ingress class to be "". + testIngress := storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] + testIngress.Annotations = map[string]string{} + storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] = testIngress }, expectedIR: intermediate.IR{ Gateways: map[types.NamespacedName]intermediate.GatewayContext{ @@ -266,9 +215,9 @@ func Test_convertToIR(t *testing.T) { }, }, HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ - {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", noIngClassIngressName)}: { + {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName)}: { HTTPRoute: gatewayv1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", noIngClassIngressName), Namespace: testNamespace}, + ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName), Namespace: testNamespace}, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{{ @@ -307,43 +256,12 @@ func Test_convertToIR(t *testing.T) { }, { name: "gce implementation-specific with /*, map to / Prefix", - ingresses: map[types.NamespacedName]*networkingv1.Ingress{ - {Namespace: testNamespace, Name: extIngClassIngressName}: { - ObjectMeta: metav1.ObjectMeta{ - Name: extIngClassIngressName, - Namespace: testNamespace, - Annotations: map[string]string{networkingv1beta1.AnnotationIngressClass: gceIngressClass}, - }, - Spec: networkingv1.IngressSpec{ - Rules: []networkingv1.IngressRule{{ - Host: testHost, - IngressRuleValue: networkingv1.IngressRuleValue{ - HTTP: &networkingv1.HTTPIngressRuleValue{ - Paths: []networkingv1.HTTPIngressPath{{ - Path: "/*", - PathType: common.PtrTo(implSpecificPathType), - Backend: networkingv1.IngressBackend{ - Service: &networkingv1.IngressServiceBackend{ - Name: testServiceName, - Port: networkingv1.ServiceBackendPort{ - Number: 80, - }, - }, - }, - }}, - }, - }, - }}, - }, - }, - }, - services: map[types.NamespacedName]*apiv1.Service{ - {Namespace: testNamespace, Name: testServiceName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testServiceName, - }, - }, + modify: func(storage *storage) { + testIngress := storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] + pathToModify := &testIngress.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0] + pathToModify.Path = "/*" + pathToModify.PathType = common.PtrTo(implSpecificPathType) + storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] = testIngress }, expectedIR: intermediate.IR{ Gateways: map[types.NamespacedName]intermediate.GatewayContext{ @@ -363,9 +281,9 @@ func Test_convertToIR(t *testing.T) { }, }, HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ - {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName)}: { + {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName)}: { HTTPRoute: gatewayv1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName), Namespace: testNamespace}, + ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName), Namespace: testNamespace}, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{{ @@ -404,43 +322,12 @@ func Test_convertToIR(t *testing.T) { }, { name: "gce implementation-specific with /foo/*, converted to /foo", - ingresses: map[types.NamespacedName]*networkingv1.Ingress{ - {Namespace: testNamespace, Name: extIngClassIngressName}: { - ObjectMeta: metav1.ObjectMeta{ - Name: extIngClassIngressName, - Namespace: testNamespace, - Annotations: map[string]string{networkingv1beta1.AnnotationIngressClass: gceIngressClass}, - }, - Spec: networkingv1.IngressSpec{ - Rules: []networkingv1.IngressRule{{ - Host: testHost, - IngressRuleValue: networkingv1.IngressRuleValue{ - HTTP: &networkingv1.HTTPIngressRuleValue{ - Paths: []networkingv1.HTTPIngressPath{{ - Path: "/foo/*", - PathType: common.PtrTo(implSpecificPathType), - Backend: networkingv1.IngressBackend{ - Service: &networkingv1.IngressServiceBackend{ - Name: testServiceName, - Port: networkingv1.ServiceBackendPort{ - Number: 80, - }, - }, - }, - }}, - }, - }, - }}, - }, - }, - }, - services: map[types.NamespacedName]*apiv1.Service{ - {Namespace: testNamespace, Name: testServiceName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testServiceName, - }, - }, + modify: func(storage *storage) { + testIngress := storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] + pathToModify := &testIngress.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0] + pathToModify.Path = "/foo/*" + pathToModify.PathType = common.PtrTo(implSpecificPathType) + storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] = testIngress }, expectedIR: intermediate.IR{ Gateways: map[types.NamespacedName]intermediate.GatewayContext{ @@ -460,9 +347,9 @@ func Test_convertToIR(t *testing.T) { }, }, HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ - {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName)}: { + {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName)}: { HTTPRoute: gatewayv1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName), Namespace: testNamespace}, + ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName), Namespace: testNamespace}, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{{ @@ -501,43 +388,12 @@ func Test_convertToIR(t *testing.T) { }, { name: "gce implementation-specific without wildcard path, map to Prefix", - ingresses: map[types.NamespacedName]*networkingv1.Ingress{ - {Namespace: testNamespace, Name: extIngClassIngressName}: { - ObjectMeta: metav1.ObjectMeta{ - Name: extIngClassIngressName, - Namespace: testNamespace, - Annotations: map[string]string{networkingv1beta1.AnnotationIngressClass: gceIngressClass}, - }, - Spec: networkingv1.IngressSpec{ - Rules: []networkingv1.IngressRule{{ - Host: testHost, - IngressRuleValue: networkingv1.IngressRuleValue{ - HTTP: &networkingv1.HTTPIngressRuleValue{ - Paths: []networkingv1.HTTPIngressPath{{ - Path: "/foo", - PathType: common.PtrTo(implSpecificPathType), - Backend: networkingv1.IngressBackend{ - Service: &networkingv1.IngressServiceBackend{ - Name: testServiceName, - Port: networkingv1.ServiceBackendPort{ - Number: 80, - }, - }, - }, - }}, - }, - }, - }}, - }, - }, - }, - services: map[types.NamespacedName]*apiv1.Service{ - {Namespace: testNamespace, Name: testServiceName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testServiceName, - }, - }, + modify: func(storage *storage) { + testIngress := storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] + pathToModify := &testIngress.Spec.Rules[0].IngressRuleValue.HTTP.Paths[0] + pathToModify.Path = "/foo" + pathToModify.PathType = common.PtrTo(implSpecificPathType) + storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] = testIngress }, expectedIR: intermediate.IR{ Gateways: map[types.NamespacedName]intermediate.GatewayContext{ @@ -557,9 +413,9 @@ func Test_convertToIR(t *testing.T) { }, }, HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ - {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName)}: { + {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName)}: { HTTPRoute: gatewayv1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName), Namespace: testNamespace}, + ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName), Namespace: testNamespace}, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{{ @@ -598,32 +454,21 @@ func Test_convertToIR(t *testing.T) { }, { name: "ingress with a Backend Config specifying CLIENT_IP type session affinity config", - ingresses: map[types.NamespacedName]*networkingv1.Ingress{ - {Namespace: testNamespace, Name: extIngClassIngressName}: testExtIngress, - }, - services: map[types.NamespacedName]*apiv1.Service{ - {Namespace: testNamespace, Name: testServiceName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testServiceName, - Annotations: map[string]string{ - backendConfigKey: `{"default":"test-backendconfig"}`, - }, - }, - }, - }, - backendConfigs: map[types.NamespacedName]*backendconfigv1.BackendConfig{ - {Namespace: testNamespace, Name: testBackendConfigName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testBackendConfigName, - }, - Spec: backendconfigv1.BackendConfigSpec{ - SessionAffinity: &backendconfigv1.SessionAffinityConfig{ - AffinityType: saTypeClientIP, - }, + modify: func(storage *storage) { + testService := storage.Services[types.NamespacedName{Namespace: testNamespace, Name: testServiceName}] + testService.Annotations = map[string]string{ + backendConfigKey: `{"default":"test-backendconfig"}`, + } + storage.Services[types.NamespacedName{Namespace: testNamespace, Name: testServiceName}] = testService + + beConfigSpec := backendconfigv1.BackendConfigSpec{ + SessionAffinity: &backendconfigv1.SessionAffinityConfig{ + AffinityType: saTypeClientIP, }, - }, + } + storage.BackendConfigs = map[types.NamespacedName]*backendconfigv1.BackendConfig{ + {Namespace: testNamespace, Name: testBackendConfigName}: getTestBackendConfig(testNamespace, testBackendConfigName, beConfigSpec), + } }, expectedIR: intermediate.IR{ Gateways: map[types.NamespacedName]intermediate.GatewayContext{ @@ -643,9 +488,9 @@ func Test_convertToIR(t *testing.T) { }, }, HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ - {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName)}: { + {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName)}: { HTTPRoute: gatewayv1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName), Namespace: testNamespace}, + ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName), Namespace: testNamespace}, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{{ @@ -693,33 +538,22 @@ func Test_convertToIR(t *testing.T) { }, { name: "ingress with a Backend Config specifying GENERATED_COOKIE type session affinity config", - ingresses: map[types.NamespacedName]*networkingv1.Ingress{ - {Namespace: testNamespace, Name: extIngClassIngressName}: testExtIngress, - }, - services: map[types.NamespacedName]*apiv1.Service{ - {Namespace: testNamespace, Name: testServiceName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testServiceName, - Annotations: map[string]string{ - backendConfigKey: `{"default":"test-backendconfig"}`, - }, - }, - }, - }, - backendConfigs: map[types.NamespacedName]*backendconfigv1.BackendConfig{ - {Namespace: testNamespace, Name: testBackendConfigName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testBackendConfigName, - }, - Spec: backendconfigv1.BackendConfigSpec{ - SessionAffinity: &backendconfigv1.SessionAffinityConfig{ - AffinityType: saTypeCookie, - AffinityCookieTtlSec: common.PtrTo(testCookieTTLSec), - }, + modify: func(storage *storage) { + testService := storage.Services[types.NamespacedName{Namespace: testNamespace, Name: testServiceName}] + testService.Annotations = map[string]string{ + backendConfigKey: `{"default":"test-backendconfig"}`, + } + storage.Services[types.NamespacedName{Namespace: testNamespace, Name: testServiceName}] = testService + + beConfigSpec := backendconfigv1.BackendConfigSpec{ + SessionAffinity: &backendconfigv1.SessionAffinityConfig{ + AffinityType: saTypeCookie, + AffinityCookieTtlSec: common.PtrTo(testCookieTTLSec), }, - }, + } + storage.BackendConfigs = map[types.NamespacedName]*backendconfigv1.BackendConfig{ + {Namespace: testNamespace, Name: testBackendConfigName}: getTestBackendConfig(testNamespace, testBackendConfigName, beConfigSpec), + } }, expectedIR: intermediate.IR{ Gateways: map[types.NamespacedName]intermediate.GatewayContext{ @@ -739,9 +573,9 @@ func Test_convertToIR(t *testing.T) { }, }, HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ - {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName)}: { + {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName)}: { HTTPRoute: gatewayv1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName), Namespace: testNamespace}, + ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName), Namespace: testNamespace}, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{{ @@ -790,32 +624,21 @@ func Test_convertToIR(t *testing.T) { }, { name: "ingress with a Backend Config specifying Security Policy", - ingresses: map[types.NamespacedName]*networkingv1.Ingress{ - {Namespace: testNamespace, Name: extIngClassIngressName}: testExtIngress, - }, - services: map[types.NamespacedName]*apiv1.Service{ - {Namespace: testNamespace, Name: testServiceName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testServiceName, - Annotations: map[string]string{ - backendConfigKey: `{"default":"test-backendconfig"}`, - }, - }, - }, - }, - backendConfigs: map[types.NamespacedName]*backendconfigv1.BackendConfig{ - {Namespace: testNamespace, Name: testBackendConfigName}: { - ObjectMeta: metav1.ObjectMeta{ - Namespace: testNamespace, - Name: testBackendConfigName, - }, - Spec: backendconfigv1.BackendConfigSpec{ - SecurityPolicy: &backendconfigv1.SecurityPolicyConfig{ - Name: testSecurityPolicy, - }, + modify: func(storage *storage) { + testService := storage.Services[types.NamespacedName{Namespace: testNamespace, Name: testServiceName}] + testService.Annotations = map[string]string{ + backendConfigKey: `{"default":"test-backendconfig"}`, + } + storage.Services[types.NamespacedName{Namespace: testNamespace, Name: testServiceName}] = testService + + beConfigSpec := backendconfigv1.BackendConfigSpec{ + SecurityPolicy: &backendconfigv1.SecurityPolicyConfig{ + Name: testSecurityPolicy, }, - }, + } + storage.BackendConfigs = map[types.NamespacedName]*backendconfigv1.BackendConfig{ + {Namespace: testNamespace, Name: testBackendConfigName}: getTestBackendConfig(testNamespace, testBackendConfigName, beConfigSpec), + } }, expectedIR: intermediate.IR{ Gateways: map[types.NamespacedName]intermediate.GatewayContext{ @@ -835,9 +658,9 @@ func Test_convertToIR(t *testing.T) { }, }, HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ - {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName)}: { + {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName)}: { HTTPRoute: gatewayv1.HTTPRoute{ - ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", extIngClassIngressName), Namespace: testNamespace}, + ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName), Namespace: testNamespace}, Spec: gatewayv1.HTTPRouteSpec{ CommonRouteSpec: gatewayv1.CommonRouteSpec{ ParentRefs: []gatewayv1.ParentReference{{ @@ -883,17 +706,104 @@ func Test_convertToIR(t *testing.T) { }, expectedErrors: field.ErrorList{}, }, + { + name: "ingress with a Frontend Config specifying Ssl Policy", + modify: func(storage *storage) { + testIngress := storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] + if testIngress.Annotations == nil { + testIngress.Annotations = map[string]string{} + } + testIngress.Annotations[frontendConfigKey] = testFrontendConfigName + storage.Ingresses[types.NamespacedName{Namespace: testNamespace, Name: testIngressName}] = testIngress + + feConfigSpec := frontendconfigv1beta1.FrontendConfigSpec{ + SslPolicy: common.PtrTo(testSslPolicy), + } + storage.FrontendConfigs = map[types.NamespacedName]*frontendconfigv1beta1.FrontendConfig{ + {Namespace: testNamespace, Name: testFrontendConfigName}: getTestFrontendConfig(testNamespace, testFrontendConfigName, feConfigSpec), + } + }, + expectedIR: intermediate.IR{ + Gateways: map[types.NamespacedName]intermediate.GatewayContext{ + {Namespace: testNamespace, Name: gceIngressClass}: { + Gateway: gatewayv1.Gateway{ + ObjectMeta: metav1.ObjectMeta{Name: gceIngressClass, Namespace: testNamespace}, + Spec: gatewayv1.GatewaySpec{ + GatewayClassName: gceL7GlobalExternalManagedGatewayClass, + Listeners: []gatewayv1.Listener{{ + Name: "test-mydomain-com-http", + Port: 80, + Protocol: gatewayv1.HTTPProtocolType, + Hostname: common.PtrTo(gatewayv1.Hostname(testHost)), + }}, + }, + }, + ProviderSpecificIR: intermediate.ProviderSpecificGatewayIR{ + Gce: &intermediate.GceGatewayIR{ + SslPolicy: &intermediate.SslPolicyConfig{Name: testSslPolicy}, + }, + }, + }, + }, + HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ + {Namespace: testNamespace, Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName)}: { + HTTPRoute: gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s-test-mydomain-com", testIngressName), Namespace: testNamespace}, + Spec: gatewayv1.HTTPRouteSpec{ + CommonRouteSpec: gatewayv1.CommonRouteSpec{ + ParentRefs: []gatewayv1.ParentReference{{ + Name: gceIngressClass, + }}, + }, + Hostnames: []gatewayv1.Hostname{gatewayv1.Hostname(testHost)}, + Rules: []gatewayv1.HTTPRouteRule{ + { + Matches: []gatewayv1.HTTPRouteMatch{ + { + Path: &gatewayv1.HTTPPathMatch{ + Type: common.PtrTo(gPathPrefix), + Value: common.PtrTo("/"), + }, + }, + }, + BackendRefs: []gatewayv1.HTTPBackendRef{ + { + BackendRef: gatewayv1.BackendRef{ + BackendObjectReference: gatewayv1.BackendObjectReference{ + Name: gatewayv1.ObjectName(testServiceName), + Port: common.PtrTo(gatewayv1.PortNumber(80)), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + expectedErrors: field.ErrorList{}, + }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - provider := NewProvider(&i2gw.ProviderConf{}) gceProvider := provider.(*Provider) gceProvider.storage = newResourcesStorage() - gceProvider.storage.Ingresses = tc.ingresses - gceProvider.storage.Services = tc.services - gceProvider.storage.BackendConfigs = tc.backendConfigs + gceProvider.storage.Ingresses = map[types.NamespacedName]*networkingv1.Ingress{ + {Namespace: testNamespace, Name: testIngressName}: getTestIngress(testNamespace, testIngressName, testServiceName), + } + gceProvider.storage.Services = map[types.NamespacedName]*apiv1.Service{ + {Namespace: testNamespace, Name: testServiceName}: { + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: testServiceName, + }, + }, + } + tc.modify(gceProvider.storage) // TODO(#113) we pass an empty i2gw.InputResources temporarily until we change ToIR function on the interface ir, errs := gceProvider.irConverter.convertToIR(gceProvider.storage) @@ -952,13 +862,13 @@ func Test_convertToIR(t *testing.T) { } } -func getTestIngress(ingressNamespace, ingressName, serviceName string, isExternalIngress bool) *networkingv1.Ingress { - iPrefix := networkingv1.PathTypePrefix - - ing := networkingv1.Ingress{ +// getTestIngress returns a template GKE External Ingress. +func getTestIngress(namespace, name, serviceName string) *networkingv1.Ingress { + return &networkingv1.Ingress{ ObjectMeta: metav1.ObjectMeta{ - Name: ingressName, - Namespace: ingressNamespace, + Namespace: namespace, + Name: name, + Annotations: map[string]string{networkingv1beta1.AnnotationIngressClass: gceIngressClass}, }, Spec: networkingv1.IngressSpec{ Rules: []networkingv1.IngressRule{{ @@ -967,7 +877,7 @@ func getTestIngress(ingressNamespace, ingressName, serviceName string, isExterna HTTP: &networkingv1.HTTPIngressRuleValue{ Paths: []networkingv1.HTTPIngressPath{{ Path: "/", - PathType: &iPrefix, + PathType: common.PtrTo(iPrefix), Backend: networkingv1.IngressBackend{ Service: &networkingv1.IngressServiceBackend{ Name: serviceName, @@ -982,13 +892,26 @@ func getTestIngress(ingressNamespace, ingressName, serviceName string, isExterna }}, }, } - if isExternalIngress { - ing.Annotations = map[string]string{networkingv1beta1.AnnotationIngressClass: gceIngressClass} - } else { - ing.Annotations = map[string]string{networkingv1beta1.AnnotationIngressClass: gceL7ILBIngressClass} +} + +func getTestBackendConfig(namespace, name string, spec backendconfigv1.BackendConfigSpec) *backendconfigv1.BackendConfig { + return &backendconfigv1.BackendConfig{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Spec: spec, } +} - return &ing +func getTestFrontendConfig(namespace, name string, spec frontendconfigv1beta1.FrontendConfigSpec) *frontendconfigv1beta1.FrontendConfig { + return &frontendconfigv1beta1.FrontendConfig{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + }, + Spec: spec, + } } func TestGetBackendConfigMapping(t *testing.T) { diff --git a/pkg/i2gw/providers/gce/resource_reader.go b/pkg/i2gw/providers/gce/resource_reader.go index ec9f701c..e3512e45 100644 --- a/pkg/i2gw/providers/gce/resource_reader.go +++ b/pkg/i2gw/providers/gce/resource_reader.go @@ -31,6 +31,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" backendconfigv1 "k8s.io/ingress-gce/pkg/apis/backendconfig/v1" + frontendconfigv1beta1 "k8s.io/ingress-gce/pkg/apis/frontendconfig/v1beta1" ) // GCE supports the following Ingress Class values: @@ -75,6 +76,12 @@ func (r *reader) readResourcesFromCluster(ctx context.Context) (*storage, error) return nil, err } storage.BackendConfigs = backendConfigs + + frontConfigs, err := r.readFrontendConfigsFromCluster(ctx) + if err != nil { + return nil, err + } + storage.FrontendConfigs = frontConfigs return storage, nil } @@ -123,12 +130,26 @@ func (r *reader) readBackendConfigsFromCluster(ctx context.Context) (map[types.N return backendConfigs, nil } +func (r *reader) readFrontendConfigsFromCluster(ctx context.Context) (map[types.NamespacedName]*frontendconfigv1beta1.FrontendConfig, error) { + var frontendConfigList frontendconfigv1beta1.FrontendConfigList + err := r.conf.Client.List(ctx, &frontendConfigList) + if err != nil { + return nil, fmt.Errorf("failed to get frontendConfigs from the cluster: %w", err) + } + frontendConfigs := make(map[types.NamespacedName]*frontendconfigv1beta1.FrontendConfig) + for i, frontendConfig := range frontendConfigList.Items { + frontendConfigs[types.NamespacedName{Namespace: frontendConfig.Namespace, Name: frontendConfig.Name}] = &frontendConfigList.Items[i] + } + return frontendConfigs, nil +} + func (r *reader) readUnstructuredObjects(objects []*unstructured.Unstructured) (*storage, error) { res := newResourcesStorage() ingresses := make(map[types.NamespacedName]*networkingv1.Ingress) services := make(map[types.NamespacedName]*apiv1.Service) backendConfigs := make(map[types.NamespacedName]*backendconfigv1.BackendConfig) + frontendConfigs := make(map[types.NamespacedName]*frontendconfigv1beta1.FrontendConfig) for _, f := range objects { if f.GroupVersionKind().Empty() { @@ -165,6 +186,15 @@ func (r *reader) readUnstructuredObjects(objects []*unstructured.Unstructured) ( } backendConfigs[types.NamespacedName{Namespace: backendConfig.Namespace, Name: backendConfig.Name}] = &backendConfig } + if f.GetAPIVersion() == "networking.gke.io/v1beta1" && f.GetKind() == "FrontendConfig" { + var frontendConfig frontendconfigv1beta1.FrontendConfig + err := runtime.DefaultUnstructuredConverter. + FromUnstructured(f.UnstructuredContent(), &frontendConfig) + if err != nil { + return nil, err + } + frontendConfigs[types.NamespacedName{Namespace: frontendConfig.Namespace, Name: frontendConfig.Name}] = &frontendConfig + } } res.Ingresses = ingresses res.Services = services diff --git a/pkg/i2gw/providers/gce/storage.go b/pkg/i2gw/providers/gce/storage.go index 71fac57a..4b1fe23e 100644 --- a/pkg/i2gw/providers/gce/storage.go +++ b/pkg/i2gw/providers/gce/storage.go @@ -21,6 +21,7 @@ import ( networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/types" backendconfigv1 "k8s.io/ingress-gce/pkg/apis/backendconfig/v1" + frontendconfigv1beta1 "k8s.io/ingress-gce/pkg/apis/frontendconfig/v1beta1" ) type storage struct { @@ -32,12 +33,18 @@ type storage struct { // `beta.cloud.google.com/backend-config` annotation on its Services. // BackendConfig map is keyed by the namespaced name of the BackendConfig. BackendConfigs map[types.NamespacedName]*backendconfigv1.BackendConfig + // FrontendConfigs is a GKE Ingress extension, and it is associated to an + // GKE Ingress through specifying `networking.gke.io/v1beta1.FrontendConfig` + // on an Ingress. + // FrontendConfig map is keyed by the namespaced name of the FrontendConfig. + FrontendConfigs map[types.NamespacedName]*frontendconfigv1beta1.FrontendConfig } func newResourcesStorage() *storage { return &storage{ - Ingresses: make(map[types.NamespacedName]*networkingv1.Ingress), - Services: make(map[types.NamespacedName]*apiv1.Service), - BackendConfigs: make(map[types.NamespacedName]*backendconfigv1.BackendConfig), + Ingresses: make(map[types.NamespacedName]*networkingv1.Ingress), + Services: make(map[types.NamespacedName]*apiv1.Service), + BackendConfigs: make(map[types.NamespacedName]*backendconfigv1.BackendConfig), + FrontendConfigs: make(map[types.NamespacedName]*frontendconfigv1beta1.FrontendConfig), } } diff --git a/pkg/i2gw/providers/gce/types.go b/pkg/i2gw/providers/gce/types.go index 974bb745..b47fd9e8 100644 --- a/pkg/i2gw/providers/gce/types.go +++ b/pkg/i2gw/providers/gce/types.go @@ -26,10 +26,19 @@ const ( gceL7RegionalInternalGatewayClass = "gke-l7-rilb" backendConfigKey = "cloud.google.com/backend-config" betaBackendConfigKey = "beta.cloud.google.com/backend-config" + frontendConfigKey = "networking.gke.io/v1beta1.FrontendConfig" ) -var GCPBackendPolicyGVK = schema.GroupVersionKind{ - Group: "networking.gke.io", - Version: "v1", - Kind: "GCPBackendPolicy", -} +var ( + GCPBackendPolicyGVK = schema.GroupVersionKind{ + Group: "networking.gke.io", + Version: "v1", + Kind: "GCPBackendPolicy", + } + + GCPGatewayPolicyGVK = schema.GroupVersionKind{ + Group: "networking.gke.io", + Version: "v1", + Kind: "GCPGatewayPolicy", + } +)