From 98e165e3f59c0485fe12d0b821a9263ea17a0f0c Mon Sep 17 00:00:00 2001 From: xtine Date: Wed, 25 Sep 2024 10:45:00 -0400 Subject: [PATCH 1/7] Cilium basic structure --- pkg/i2gw/providers/cilium/cilium.go | 82 +++++++++++++++++++ pkg/i2gw/providers/cilium/converter.go | 26 ++++++ .../providers/cilium/resource_converter.go | 29 +++++++ 3 files changed, 137 insertions(+) create mode 100644 pkg/i2gw/providers/cilium/cilium.go create mode 100644 pkg/i2gw/providers/cilium/converter.go create mode 100644 pkg/i2gw/providers/cilium/resource_converter.go diff --git a/pkg/i2gw/providers/cilium/cilium.go b/pkg/i2gw/providers/cilium/cilium.go new file mode 100644 index 00000000..ca5a11f7 --- /dev/null +++ b/pkg/i2gw/providers/cilium/cilium.go @@ -0,0 +1,82 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 cilium + +import ( + "context" + "fmt" + + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate" + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +// The Name of the provider. +const Name = "cilium" +const CiliumIngressClass = "cilium" + +func init() { + i2gw.ProviderConstructorByName[Name] = NewProvider +} + +// Provider implements the i2gw.Provider interface. +type Provider struct { + storage *storage + resourceReader *resourceReader + resourcesToIRConverter *resourcesToIRConverter +} + +// NewProvider constructs and returns the ingress-nginx implementation of i2gw.Provider. +func NewProvider(conf *i2gw.ProviderConf) i2gw.Provider { + return &Provider{ + storage: newResourcesStorage(), + resourceReader: newResourceReader(conf), + resourcesToIRConverter: newResourcesToIRConverter(), + } +} + +// ToIR converts stored Ingress-Nginx API entities to intermediate.IR +// including the ingress-nginx specific features. +func (p *Provider) ToIR() (intermediate.IR, field.ErrorList) { + return p.resourcesToIRConverter.convert(p.storage) +} + +func (p *Provider) ToGatewayResources(ir intermediate.IR) (i2gw.GatewayResources, field.ErrorList) { + return common.ToGatewayResources(ir) + +} + +func (p *Provider) ReadResourcesFromCluster(ctx context.Context) error { + storage, err := p.resourceReader.readResourcesFromCluster(ctx) + if err != nil { + return fmt.Errorf("failed to read resources from cluster: %w", err) + } + + p.storage = storage + return nil +} + +func (p *Provider) ReadResourcesFromFile(_ context.Context, filename string) error { + storage, err := p.resourceReader.readResourcesFromFile(filename) + if err != nil { + return fmt.Errorf("failed to read resources from file: %w", err) + } + + p.storage = storage + return nil +} diff --git a/pkg/i2gw/providers/cilium/converter.go b/pkg/i2gw/providers/cilium/converter.go new file mode 100644 index 00000000..3a1aefaf --- /dev/null +++ b/pkg/i2gw/providers/cilium/converter.go @@ -0,0 +1,26 @@ +package cilium + +import ( + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" +) + +// converter implements the ToGatewayAPI function of i2gw.ResourceConverter interface. +type converter struct { + conf *i2gw.ProviderConf + + featureParsers []i2gw.FeatureParser + implementationSpecificOptions i2gw.ProviderImplementationSpecificOptions +} + +// newConverter returns an ingress-nginx converter instance. +func newConverter(conf *i2gw.ProviderConf) *converter { + return &converter{ + conf: conf, + featureParsers: []i2gw.FeatureParser{ + // The list of feature parsers comes here. + }, + implementationSpecificOptions: i2gw.ProviderImplementationSpecificOptions{ + // The list of the implementationSpecific ingress fields options comes here. + }, + } +} diff --git a/pkg/i2gw/providers/cilium/resource_converter.go b/pkg/i2gw/providers/cilium/resource_converter.go new file mode 100644 index 00000000..81c1ef55 --- /dev/null +++ b/pkg/i2gw/providers/cilium/resource_converter.go @@ -0,0 +1,29 @@ +package cilium + +import ( + "context" + + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" +) + +// converter implements the i2gw.CustomResourceReader interface. +type resourceReader struct { + conf *i2gw.ProviderConf +} + +// newResourceReader returns a resourceReader instance. +func newResourceReader(conf *i2gw.ProviderConf) *resourceReader { + return &resourceReader{ + conf: conf, + } +} + +func (r *resourceReader) ReadResourcesFromCluster(ctx context.Context) error { + // read example-gateway related resources from the cluster. + return nil +} + +func (r *resourceReader) ReadResourcesFromFiles(ctx context.Context, filename string) error { + // read example-gateway related resources from the file. + return nil +} From 60cc9e3748c48fb5a5a5c1163b49ef9be190fa1c Mon Sep 17 00:00:00 2001 From: xtine Date: Mon, 30 Sep 2024 16:27:02 -0400 Subject: [PATCH 2/7] Add base for Cilium --- cmd/print.go | 1 + pkg/i2gw/intermediate/provider_cilium.go | 21 +++++++ pkg/i2gw/providers/cilium/cilium.go | 9 ++- pkg/i2gw/providers/cilium/converter.go | 47 ++++++++++---- pkg/i2gw/providers/cilium/notification.go | 27 ++++++++ .../providers/cilium/resource_converter.go | 29 --------- pkg/i2gw/providers/cilium/resource_reader.go | 61 +++++++++++++++++++ pkg/i2gw/providers/cilium/storage.go | 32 ++++++++++ 8 files changed, 180 insertions(+), 47 deletions(-) create mode 100644 pkg/i2gw/intermediate/provider_cilium.go create mode 100644 pkg/i2gw/providers/cilium/notification.go delete mode 100644 pkg/i2gw/providers/cilium/resource_converter.go create mode 100644 pkg/i2gw/providers/cilium/resource_reader.go create mode 100644 pkg/i2gw/providers/cilium/storage.go diff --git a/cmd/print.go b/cmd/print.go index cf7d2f44..cb0491fb 100644 --- a/cmd/print.go +++ b/cmd/print.go @@ -38,6 +38,7 @@ import ( _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/istio" _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/kong" _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/openapi3" + _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/cilium" // Call init for notifications _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/notifications" diff --git a/pkg/i2gw/intermediate/provider_cilium.go b/pkg/i2gw/intermediate/provider_cilium.go new file mode 100644 index 00000000..5d6e8890 --- /dev/null +++ b/pkg/i2gw/intermediate/provider_cilium.go @@ -0,0 +1,21 @@ +/* +Copyright 2024 The Kubernetes Authors. + +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 intermediate + +type CiliumGatewayIR struct{} +type CiliumHTTPRouteIR struct{} +type CiliumServiceIR struct{} diff --git a/pkg/i2gw/providers/cilium/cilium.go b/pkg/i2gw/providers/cilium/cilium.go index ca5a11f7..2ad86c96 100644 --- a/pkg/i2gw/providers/cilium/cilium.go +++ b/pkg/i2gw/providers/cilium/cilium.go @@ -41,7 +41,7 @@ type Provider struct { resourcesToIRConverter *resourcesToIRConverter } -// NewProvider constructs and returns the ingress-nginx implementation of i2gw.Provider. +// NewProvider constructs and returns the apisix implementation of i2gw.Provider. func NewProvider(conf *i2gw.ProviderConf) i2gw.Provider { return &Provider{ storage: newResourcesStorage(), @@ -50,15 +50,14 @@ func NewProvider(conf *i2gw.ProviderConf) i2gw.Provider { } } -// ToIR converts stored Ingress-Nginx API entities to intermediate.IR -// including the ingress-nginx specific features. +// ToIR converts stored Cilium API entities to intermediate.IR +// including the cilium specific features. func (p *Provider) ToIR() (intermediate.IR, field.ErrorList) { - return p.resourcesToIRConverter.convert(p.storage) + return p.resourcesToIRConverter.convertToIR(p.storage) } func (p *Provider) ToGatewayResources(ir intermediate.IR) (i2gw.GatewayResources, field.ErrorList) { return common.ToGatewayResources(ir) - } func (p *Provider) ReadResourcesFromCluster(ctx context.Context) error { diff --git a/pkg/i2gw/providers/cilium/converter.go b/pkg/i2gw/providers/cilium/converter.go index 3a1aefaf..9d7342b8 100644 --- a/pkg/i2gw/providers/cilium/converter.go +++ b/pkg/i2gw/providers/cilium/converter.go @@ -1,26 +1,47 @@ -package cilium +package cilium import ( "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate" + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/util/validation/field" ) -// converter implements the ToGatewayAPI function of i2gw.ResourceConverter interface. -type converter struct { - conf *i2gw.ProviderConf - - featureParsers []i2gw.FeatureParser +// resourcesToIRConverter implements the ToIR function of i2gw.ResourcesToIRConverter interface. +type resourcesToIRConverter struct { + featureParsers []i2gw.FeatureParser implementationSpecificOptions i2gw.ProviderImplementationSpecificOptions } -// newConverter returns an ingress-nginx converter instance. -func newConverter(conf *i2gw.ProviderConf) *converter { - return &converter{ - conf: conf, - featureParsers: []i2gw.FeatureParser{ - // The list of feature parsers comes here. - }, +// newResourcesToIRConverter returns an apisix resourcesToIRConverter instance. +func newResourcesToIRConverter() *resourcesToIRConverter { + return &resourcesToIRConverter{ + featureParsers: []i2gw.FeatureParser{}, implementationSpecificOptions: i2gw.ProviderImplementationSpecificOptions{ // The list of the implementationSpecific ingress fields options comes here. }, } } + +func (c *resourcesToIRConverter) convertToIR(storage *storage) (intermediate.IR, field.ErrorList) { + ingressList := []networkingv1.Ingress{} + for _, ing := range storage.Ingresses { + ingressList = append(ingressList, *ing) + } + // Convert plain ingress resources to gateway resources, ignoring all + // provider-specific features. + ir, errs := common.ToIR(ingressList, c.implementationSpecificOptions) + if len(errs) > 0 { + return intermediate.IR{}, errs + } + + for _, parseFeatureFunc := range c.featureParsers { + // Apply the feature parsing function to the gateway resources, one by one. + parseErrs := parseFeatureFunc(ingressList, &ir) + // Append the parsing errors to the error list. + errs = append(errs, parseErrs...) + } + + return ir, errs +} diff --git a/pkg/i2gw/providers/cilium/notification.go b/pkg/i2gw/providers/cilium/notification.go new file mode 100644 index 00000000..ed1f877f --- /dev/null +++ b/pkg/i2gw/providers/cilium/notification.go @@ -0,0 +1,27 @@ +/* +Copyright 2024 The Kubernetes Authors. + +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 cilium + +import ( + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/notifications" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func notify(mType notifications.MessageType, message string, callingObject ...client.Object) { + newNotification := notifications.NewNotification(mType, message, callingObject...) + notifications.NotificationAggr.DispatchNotification(newNotification, string(Name)) +} diff --git a/pkg/i2gw/providers/cilium/resource_converter.go b/pkg/i2gw/providers/cilium/resource_converter.go deleted file mode 100644 index 81c1ef55..00000000 --- a/pkg/i2gw/providers/cilium/resource_converter.go +++ /dev/null @@ -1,29 +0,0 @@ -package cilium - -import ( - "context" - - "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" -) - -// converter implements the i2gw.CustomResourceReader interface. -type resourceReader struct { - conf *i2gw.ProviderConf -} - -// newResourceReader returns a resourceReader instance. -func newResourceReader(conf *i2gw.ProviderConf) *resourceReader { - return &resourceReader{ - conf: conf, - } -} - -func (r *resourceReader) ReadResourcesFromCluster(ctx context.Context) error { - // read example-gateway related resources from the cluster. - return nil -} - -func (r *resourceReader) ReadResourcesFromFiles(ctx context.Context, filename string) error { - // read example-gateway related resources from the file. - return nil -} diff --git a/pkg/i2gw/providers/cilium/resource_reader.go b/pkg/i2gw/providers/cilium/resource_reader.go new file mode 100644 index 00000000..8cc853cb --- /dev/null +++ b/pkg/i2gw/providers/cilium/resource_reader.go @@ -0,0 +1,61 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 cilium + +import ( + "context" + + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw" + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" + "k8s.io/apimachinery/pkg/util/sets" +) + +// resourceReader implements the i2gw.CustomResourceReader interface. +type resourceReader struct { + conf *i2gw.ProviderConf +} + +// newResourceReader returns a resourceReader instance. +func newResourceReader(conf *i2gw.ProviderConf) *resourceReader { + return &resourceReader{ + conf: conf, + } +} + +func (r *resourceReader) readResourcesFromCluster(ctx context.Context) (*storage, error) { + // read apisix related resources from cluster. + storage := newResourcesStorage() + + ingresses, err := common.ReadIngressesFromCluster(ctx, r.conf.Client, sets.New(CiliumIngressClass)) + if err != nil { + return nil, err + } + storage.Ingresses = ingresses + return storage, nil +} + +func (r *resourceReader) readResourcesFromFile(filename string) (*storage, error) { + // read apisix related resources from file. + storage := newResourcesStorage() + + ingresses, err := common.ReadIngressesFromFile(filename, r.conf.Namespace, sets.New[string](CiliumIngressClass)) + if err != nil { + return nil, err + } + storage.Ingresses = ingresses + return storage, nil +} diff --git a/pkg/i2gw/providers/cilium/storage.go b/pkg/i2gw/providers/cilium/storage.go new file mode 100644 index 00000000..ae225d48 --- /dev/null +++ b/pkg/i2gw/providers/cilium/storage.go @@ -0,0 +1,32 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 cilium + +import ( + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/types" +) + +type storage struct { + Ingresses map[types.NamespacedName]*networkingv1.Ingress +} + +func newResourcesStorage() *storage { + return &storage{ + Ingresses: map[types.NamespacedName]*networkingv1.Ingress{}, + } +} From f99192894af88a4cc78367d17eb575d528f81ebf Mon Sep 17 00:00:00 2001 From: xtine Date: Wed, 2 Oct 2024 10:47:01 -0400 Subject: [PATCH 3/7] Add Cilium to IR --- pkg/i2gw/intermediate/intermediate_representation.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/i2gw/intermediate/intermediate_representation.go b/pkg/i2gw/intermediate/intermediate_representation.go index 5fde45c5..9613c950 100644 --- a/pkg/i2gw/intermediate/intermediate_representation.go +++ b/pkg/i2gw/intermediate/intermediate_representation.go @@ -52,6 +52,7 @@ type GatewayContext struct { type ProviderSpecificGatewayIR struct { Apisix *ApisixGatewayIR + Cilium *CiliumGatewayIR Gce *GceGatewayIR IngressNginx *IngressNginxGatewayIR Istio *IstioGatewayIR @@ -71,6 +72,7 @@ type HTTPRouteContext struct { type ProviderSpecificHTTPRouteIR struct { Apisix *ApisixHTTPRouteIR + Cilium *CiliumHTTPRouteIR Gce *GceHTTPRouteIR IngressNginx *IngressNginxHTTPRouteIR Istio *IstioHTTPRouteIR @@ -82,6 +84,7 @@ type ProviderSpecificHTTPRouteIR struct { // extension features on Service. type ProviderSpecificServiceIR struct { Apisix *ApisixServiceIR + Cilium *CiliumServiceIR Gce *GceServiceIR IngressNginx *IngressNginxServiceIR Istio *IstioServiceIR From 4ab193c30268b7ac9d7e649425745b7d58a69be6 Mon Sep 17 00:00:00 2001 From: xtine Date: Wed, 9 Oct 2024 15:19:12 -0400 Subject: [PATCH 4/7] Add force-https annotation test --- pkg/i2gw/providers/cilium/annotations.go | 27 ++ pkg/i2gw/providers/cilium/converter.go | 6 +- pkg/i2gw/providers/cilium/force_https.go | 70 ++++ pkg/i2gw/providers/cilium/force_https_test.go | 341 ++++++++++++++++++ 4 files changed, 442 insertions(+), 2 deletions(-) create mode 100644 pkg/i2gw/providers/cilium/annotations.go create mode 100644 pkg/i2gw/providers/cilium/force_https.go create mode 100644 pkg/i2gw/providers/cilium/force_https_test.go diff --git a/pkg/i2gw/providers/cilium/annotations.go b/pkg/i2gw/providers/cilium/annotations.go new file mode 100644 index 00000000..fadfca65 --- /dev/null +++ b/pkg/i2gw/providers/cilium/annotations.go @@ -0,0 +1,27 @@ +/* +Copyright 2024 The Kubernetes Authors. + +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 cilium + +import "fmt" + +const ( + annotationPrefix = "ingress.cilium.io" +) + +func ciliumAnnotation(suffix string) string { + return fmt.Sprintf("%s/%s", annotationPrefix, suffix) +} diff --git a/pkg/i2gw/providers/cilium/converter.go b/pkg/i2gw/providers/cilium/converter.go index 9d7342b8..162ce454 100644 --- a/pkg/i2gw/providers/cilium/converter.go +++ b/pkg/i2gw/providers/cilium/converter.go @@ -14,10 +14,12 @@ type resourcesToIRConverter struct { implementationSpecificOptions i2gw.ProviderImplementationSpecificOptions } -// newResourcesToIRConverter returns an apisix resourcesToIRConverter instance. +// newResourcesToIRConverter returns a cilium resourcesToIRConverter instance. func newResourcesToIRConverter() *resourcesToIRConverter { return &resourcesToIRConverter{ - featureParsers: []i2gw.FeatureParser{}, + featureParsers: []i2gw.FeatureParser{ + forceHTTPSFeature, + }, implementationSpecificOptions: i2gw.ProviderImplementationSpecificOptions{ // The list of the implementationSpecific ingress fields options comes here. }, diff --git a/pkg/i2gw/providers/cilium/force_https.go b/pkg/i2gw/providers/cilium/force_https.go new file mode 100644 index 00000000..b858837d --- /dev/null +++ b/pkg/i2gw/providers/cilium/force_https.go @@ -0,0 +1,70 @@ +/* +Copyright 2024 The Kubernetes Authors. + +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 cilium + +import ( + "fmt" + + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate" + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/notifications" + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" + networkingv1 "k8s.io/api/networking/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/utils/ptr" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +func forceHTTPSFeature(ingresses []networkingv1.Ingress, ir *intermediate.IR) field.ErrorList { + var errs field.ErrorList + forceHTTPSAnnotation := ciliumAnnotation("force-https") + ruleGroups := common.GetRuleGroups(ingresses) + for _, rg := range ruleGroups { + + for _, rule := range rg.Rules { + if val, annotationFound := rule.Ingress.Annotations[forceHTTPSAnnotation]; val == "enabled" || val == "true" { + if rule.Ingress.Spec.Rules == nil { + continue + } + key := types.NamespacedName{Namespace: rule.Ingress.Namespace, Name: common.RouteName(rg.Name, rg.Host)} + + httpRoute, ok := ir.HTTPRoutes[key] + if !ok { + errs = append(errs, field.NotFound(field.NewPath("HTTPRoute"), key)) + } + + for i, rule := range httpRoute.Spec.Rules { + rule.Filters = append(rule.Filters, gatewayv1.HTTPRouteFilter{ + Type: gatewayv1.HTTPRouteFilterRequestRedirect, + RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{ + Scheme: ptr.To("https"), + StatusCode: ptr.To(int(301)), + }, + }) + rule.BackendRefs = nil + + httpRoute.Spec.Rules[i] = rule + + } + if annotationFound && ok { + notify(notifications.InfoNotification, fmt.Sprintf("parsed \"%v\" annotation of ingress and patched %v fields", forceHTTPSAnnotation, field.NewPath("httproute", "spec", "rules").Key("").Child("filters")), &httpRoute) + } + } + } + } + return errs +} diff --git a/pkg/i2gw/providers/cilium/force_https_test.go b/pkg/i2gw/providers/cilium/force_https_test.go new file mode 100644 index 00000000..1f9fec04 --- /dev/null +++ b/pkg/i2gw/providers/cilium/force_https_test.go @@ -0,0 +1,341 @@ +/* +Copyright 2024 The Kubernetes Authors. + +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 cilium + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate" + "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/validation/field" + "k8s.io/utils/ptr" + + networkingv1 "k8s.io/api/networking/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + gatewayv1 "sigs.k8s.io/gateway-api/apis/v1" +) + +func Test_forceHTTPSFeature(t *testing.T) { + testCases := []struct { + name string + ingress networkingv1.Ingress + initialHTTPRoute *gatewayv1.HTTPRoute + expectedHTTPRoute *gatewayv1.HTTPRoute + expectedError field.ErrorList + }{ + { + name: "force-https annontation present and set to enabled", + ingress: networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + Namespace: "default", + Annotations: map[string]string{ + "ingress.cilium.io/force-https": "enabled", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "foo.com", + }, + }, + }, + }, + initialHTTPRoute: &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-foo-com", + Namespace: "default", + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"foo.com"}, + Rules: []gatewayv1.HTTPRouteRule{ + { + BackendRefs: []gatewayv1.HTTPBackendRef{ + {BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}}, + }, + }, + }, + }, + }, + expectedHTTPRoute: &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-foo-com", + Namespace: "default", + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"foo.com"}, + Rules: []gatewayv1.HTTPRouteRule{ + { + Filters: []gatewayv1.HTTPRouteFilter{ + { + Type: gatewayv1.HTTPRouteFilterRequestRedirect, + RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{ + Scheme: ptr.To("https"), + StatusCode: ptr.To(int(301)), + }, + }, + }, + }, + }, + }, + }, + expectedError: field.ErrorList{}, + }, + { + name: "force-https annontation present and set to disabled", + ingress: networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + Namespace: "default", + Annotations: map[string]string{ + "ingress.cilium.io/force-https": "disabled", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "foo.com", + }, + }, + }, + }, + initialHTTPRoute: &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-foo-com", + Namespace: "default", + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"foo.com"}, + Rules: []gatewayv1.HTTPRouteRule{ + { + BackendRefs: []gatewayv1.HTTPBackendRef{ + {BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}}, + }, + }, + }, + }, + }, + expectedHTTPRoute: &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-foo-com", + Namespace: "default", + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"foo.com"}, + Rules: []gatewayv1.HTTPRouteRule{ + { + BackendRefs: []gatewayv1.HTTPBackendRef{ + {BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}}, + }, + }, + }, + }, + }, + }, + { + name: "force-https annontation present and set to true", + ingress: networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + Namespace: "default", + Annotations: map[string]string{ + "ingress.cilium.io/force-https": "true", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "foo.com", + }, + }, + }, + }, + initialHTTPRoute: &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-foo-com", + Namespace: "default", + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"foo.com"}, + Rules: []gatewayv1.HTTPRouteRule{ + { + BackendRefs: []gatewayv1.HTTPBackendRef{ + {BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}}, + }, + }, + }, + }, + }, + expectedHTTPRoute: &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-foo-com", + Namespace: "default", + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"foo.com"}, + Rules: []gatewayv1.HTTPRouteRule{ + { + Filters: []gatewayv1.HTTPRouteFilter{ + { + Type: gatewayv1.HTTPRouteFilterRequestRedirect, + RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{ + Scheme: ptr.To("https"), + StatusCode: ptr.To(int(301)), + }, + }, + }, + }, + }, + }, + }, + expectedError: field.ErrorList{}, + }, + { + name: "force-https annontation present and set to false", + ingress: networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + Namespace: "default", + Annotations: map[string]string{ + "ingress.cilium.io/force-https": "false", + }, + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "foo.com", + }, + }, + }, + }, + initialHTTPRoute: &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-foo-com", + Namespace: "default", + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"foo.com"}, + Rules: []gatewayv1.HTTPRouteRule{ + { + BackendRefs: []gatewayv1.HTTPBackendRef{ + {BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}}, + }, + }, + }, + }, + }, + expectedHTTPRoute: &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-foo-com", + Namespace: "default", + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"foo.com"}, + Rules: []gatewayv1.HTTPRouteRule{ + { + BackendRefs: []gatewayv1.HTTPBackendRef{ + {BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}}, + }, + }, + }, + }, + }, + expectedError: field.ErrorList{}, + }, + { + name: "force-https annotation not present", + ingress: networkingv1.Ingress{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress", + Namespace: "default", + }, + Spec: networkingv1.IngressSpec{ + Rules: []networkingv1.IngressRule{ + { + Host: "foo.com", + }, + }, + }, + }, + initialHTTPRoute: &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-foo-com", + Namespace: "default", + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"foo.com"}, + Rules: []gatewayv1.HTTPRouteRule{ + { + BackendRefs: []gatewayv1.HTTPBackendRef{ + {BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}}, + }, + }, + }, + }, + }, + expectedHTTPRoute: &gatewayv1.HTTPRoute{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-ingress-foo-com", + Namespace: "default", + }, + Spec: gatewayv1.HTTPRouteSpec{ + Hostnames: []gatewayv1.Hostname{"foo.com"}, + Rules: []gatewayv1.HTTPRouteRule{ + { + BackendRefs: []gatewayv1.HTTPBackendRef{ + {BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}}, + }, + }, + }, + }, + }, + expectedError: field.ErrorList{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ingresses := []networkingv1.Ingress{tc.ingress} + ir := &intermediate.IR{ + HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{ + {Name: tc.expectedHTTPRoute.Name, Namespace: tc.expectedHTTPRoute.Namespace}: { + HTTPRoute: *tc.initialHTTPRoute, + }, + }, + } + + errs := forceHTTPSFeature(ingresses, ir) + + if len(errs) != len(tc.expectedError) { + t.Errorf("expected %d errors, got %d", len(tc.expectedError), len(errs)) + } + + key := types.NamespacedName{Namespace: tc.ingress.Namespace, Name: common.RouteName(tc.ingress.Name, tc.ingress.Spec.Rules[0].Host)} + + actualHTTPRouteContext, ok := ir.HTTPRoutes[key] + if !ok { + t.Errorf("HTTPRoute not found: %v", key) + } + + if diff := cmp.Diff(*tc.expectedHTTPRoute, actualHTTPRouteContext.HTTPRoute); diff != "" { + t.Errorf("Unexpected HTTPRoute resource found, \n want: %+v\n got: %+v\n diff (-want +got):\n%s", *tc.expectedHTTPRoute, actualHTTPRouteContext.HTTPRoute, diff) + } + }) + } +} From 72b3a7359d00e4e02ff87f1eb03157825a8b7cda Mon Sep 17 00:00:00 2001 From: xtine Date: Wed, 9 Oct 2024 17:08:59 -0400 Subject: [PATCH 5/7] Add README for Cilium Add Cilium to main README --- README.md | 1 + pkg/i2gw/providers/cilium/README.md | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 pkg/i2gw/providers/cilium/README.md diff --git a/README.md b/README.md index aa1db5d7..9900a5d2 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ API. ## Supported providers * [apisix](pkg/i2gw/providers/apisix/README.md) +* [cilium](pkg./i2gw/providers/cilium/README.md) * [ingress-nginx](pkg/i2gw/providers/ingressnginx/README.md) * [istio](pkg/i2gw/providers/istio/README.md) * [gce](pkg/i2gw/providers/gce/README.md) diff --git a/pkg/i2gw/providers/cilium/README.md b/pkg/i2gw/providers/cilium/README.md new file mode 100644 index 00000000..080bc763 --- /dev/null +++ b/pkg/i2gw/providers/cilium/README.md @@ -0,0 +1,8 @@ +# Cilium Provider + +The project supports translating [Cilium](https://github.com/cilium/cilium) specific annotations. + +## Supported Annotations + +- `ingress.cilium.io/force-https:`: This annotation redirects HTTP requests to HTTPS with a `301` status code. + From fbcdff8aac50aa2299d7795417a1e7a979bc1687 Mon Sep 17 00:00:00 2001 From: xtine Date: Wed, 9 Oct 2024 17:20:14 -0400 Subject: [PATCH 6/7] Added missing boilerplate --- pkg/i2gw/providers/cilium/converter.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pkg/i2gw/providers/cilium/converter.go b/pkg/i2gw/providers/cilium/converter.go index 162ce454..b317cdb7 100644 --- a/pkg/i2gw/providers/cilium/converter.go +++ b/pkg/i2gw/providers/cilium/converter.go @@ -1,3 +1,19 @@ +/* +Copyright 2023 The Kubernetes Authors. + +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 cilium import ( From 56f362174282b8a104adc8dc66e29d8e797c88e3 Mon Sep 17 00:00:00 2001 From: xtine Date: Mon, 21 Oct 2024 10:18:45 -0400 Subject: [PATCH 7/7] Fix comments gofmt --- cmd/print.go | 2 +- pkg/i2gw/providers/cilium/cilium.go | 2 +- pkg/i2gw/providers/cilium/resource_reader.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/print.go b/cmd/print.go index cb0491fb..ff4ce90f 100644 --- a/cmd/print.go +++ b/cmd/print.go @@ -33,12 +33,12 @@ import ( // Call init function for the providers _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/apisix" + _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/cilium" _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/gce" _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/ingressnginx" _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/istio" _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/kong" _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/openapi3" - _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/cilium" // Call init for notifications _ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/notifications" diff --git a/pkg/i2gw/providers/cilium/cilium.go b/pkg/i2gw/providers/cilium/cilium.go index 2ad86c96..aab4ca9c 100644 --- a/pkg/i2gw/providers/cilium/cilium.go +++ b/pkg/i2gw/providers/cilium/cilium.go @@ -41,7 +41,7 @@ type Provider struct { resourcesToIRConverter *resourcesToIRConverter } -// NewProvider constructs and returns the apisix implementation of i2gw.Provider. +// NewProvider constructs and returns the cilium implementation of i2gw.Provider. func NewProvider(conf *i2gw.ProviderConf) i2gw.Provider { return &Provider{ storage: newResourcesStorage(), diff --git a/pkg/i2gw/providers/cilium/resource_reader.go b/pkg/i2gw/providers/cilium/resource_reader.go index 8cc853cb..8bf74976 100644 --- a/pkg/i2gw/providers/cilium/resource_reader.go +++ b/pkg/i2gw/providers/cilium/resource_reader.go @@ -37,7 +37,7 @@ func newResourceReader(conf *i2gw.ProviderConf) *resourceReader { } func (r *resourceReader) readResourcesFromCluster(ctx context.Context) (*storage, error) { - // read apisix related resources from cluster. + // read cilium related resources from cluster. storage := newResourcesStorage() ingresses, err := common.ReadIngressesFromCluster(ctx, r.conf.Client, sets.New(CiliumIngressClass)) @@ -49,7 +49,7 @@ func (r *resourceReader) readResourcesFromCluster(ctx context.Context) (*storage } func (r *resourceReader) readResourcesFromFile(filename string) (*storage, error) { - // read apisix related resources from file. + // read cilium related resources from file. storage := newResourcesStorage() ingresses, err := common.ReadIngressesFromFile(filename, r.conf.Namespace, sets.New[string](CiliumIngressClass))