From 98e165e3f59c0485fe12d0b821a9263ea17a0f0c Mon Sep 17 00:00:00 2001
From: xtine <xtineskim@gmail.com>
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 <xtineskim@gmail.com>
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 <xtineskim@gmail.com>
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 <xtineskim@gmail.com>
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 <xtineskim@gmail.com>
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 <xtineskim@gmail.com>
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 <xtineskim@gmail.com>
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))