Skip to content

Commit 5f79569

Browse files
authoredNov 28, 2024··
Add Cilium to ingress2gateway (#199)
* Cilium basic structure * Add base for Cilium * Add Cilium to IR * Add force-https annotation test * Add README for Cilium Add Cilium to main README * Added missing boilerplate * Fix comments gofmt
1 parent e9621a1 commit 5f79569

13 files changed

+738
-0
lines changed
 

‎README.md

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ API.
2020
## Supported providers
2121

2222
* [apisix](pkg/i2gw/providers/apisix/README.md)
23+
* [cilium](pkg./i2gw/providers/cilium/README.md)
2324
* [ingress-nginx](pkg/i2gw/providers/ingressnginx/README.md)
2425
* [istio](pkg/i2gw/providers/istio/README.md)
2526
* [gce](pkg/i2gw/providers/gce/README.md)

‎cmd/print.go

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333

3434
// Call init function for the providers
3535
_ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/apisix"
36+
_ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/cilium"
3637
_ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/gce"
3738
_ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/ingressnginx"
3839
_ "github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/istio"

‎pkg/i2gw/intermediate/intermediate_representation.go

+3
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ type GatewayContext struct {
5252

5353
type ProviderSpecificGatewayIR struct {
5454
Apisix *ApisixGatewayIR
55+
Cilium *CiliumGatewayIR
5556
Gce *GceGatewayIR
5657
IngressNginx *IngressNginxGatewayIR
5758
Istio *IstioGatewayIR
@@ -71,6 +72,7 @@ type HTTPRouteContext struct {
7172

7273
type ProviderSpecificHTTPRouteIR struct {
7374
Apisix *ApisixHTTPRouteIR
75+
Cilium *CiliumHTTPRouteIR
7476
Gce *GceHTTPRouteIR
7577
IngressNginx *IngressNginxHTTPRouteIR
7678
Istio *IstioHTTPRouteIR
@@ -82,6 +84,7 @@ type ProviderSpecificHTTPRouteIR struct {
8284
// extension features on Service.
8385
type ProviderSpecificServiceIR struct {
8486
Apisix *ApisixServiceIR
87+
Cilium *CiliumServiceIR
8588
Gce *GceServiceIR
8689
IngressNginx *IngressNginxServiceIR
8790
Istio *IstioServiceIR
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package intermediate
18+
19+
type CiliumGatewayIR struct{}
20+
type CiliumHTTPRouteIR struct{}
21+
type CiliumServiceIR struct{}

‎pkg/i2gw/providers/cilium/README.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Cilium Provider
2+
3+
The project supports translating [Cilium](https://github.com/cilium/cilium) specific annotations.
4+
5+
## Supported Annotations
6+
7+
- `ingress.cilium.io/force-https:`: This annotation redirects HTTP requests to HTTPS with a `301` status code.
8+
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cilium
18+
19+
import "fmt"
20+
21+
const (
22+
annotationPrefix = "ingress.cilium.io"
23+
)
24+
25+
func ciliumAnnotation(suffix string) string {
26+
return fmt.Sprintf("%s/%s", annotationPrefix, suffix)
27+
}

‎pkg/i2gw/providers/cilium/cilium.go

+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cilium
18+
19+
import (
20+
"context"
21+
"fmt"
22+
23+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
24+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate"
25+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common"
26+
"k8s.io/apimachinery/pkg/util/validation/field"
27+
)
28+
29+
// The Name of the provider.
30+
const Name = "cilium"
31+
const CiliumIngressClass = "cilium"
32+
33+
func init() {
34+
i2gw.ProviderConstructorByName[Name] = NewProvider
35+
}
36+
37+
// Provider implements the i2gw.Provider interface.
38+
type Provider struct {
39+
storage *storage
40+
resourceReader *resourceReader
41+
resourcesToIRConverter *resourcesToIRConverter
42+
}
43+
44+
// NewProvider constructs and returns the cilium implementation of i2gw.Provider.
45+
func NewProvider(conf *i2gw.ProviderConf) i2gw.Provider {
46+
return &Provider{
47+
storage: newResourcesStorage(),
48+
resourceReader: newResourceReader(conf),
49+
resourcesToIRConverter: newResourcesToIRConverter(),
50+
}
51+
}
52+
53+
// ToIR converts stored Cilium API entities to intermediate.IR
54+
// including the cilium specific features.
55+
func (p *Provider) ToIR() (intermediate.IR, field.ErrorList) {
56+
return p.resourcesToIRConverter.convertToIR(p.storage)
57+
}
58+
59+
func (p *Provider) ToGatewayResources(ir intermediate.IR) (i2gw.GatewayResources, field.ErrorList) {
60+
return common.ToGatewayResources(ir)
61+
}
62+
63+
func (p *Provider) ReadResourcesFromCluster(ctx context.Context) error {
64+
storage, err := p.resourceReader.readResourcesFromCluster(ctx)
65+
if err != nil {
66+
return fmt.Errorf("failed to read resources from cluster: %w", err)
67+
}
68+
69+
p.storage = storage
70+
return nil
71+
}
72+
73+
func (p *Provider) ReadResourcesFromFile(_ context.Context, filename string) error {
74+
storage, err := p.resourceReader.readResourcesFromFile(filename)
75+
if err != nil {
76+
return fmt.Errorf("failed to read resources from file: %w", err)
77+
}
78+
79+
p.storage = storage
80+
return nil
81+
}
+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cilium
18+
19+
import (
20+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
21+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate"
22+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common"
23+
networkingv1 "k8s.io/api/networking/v1"
24+
"k8s.io/apimachinery/pkg/util/validation/field"
25+
)
26+
27+
// resourcesToIRConverter implements the ToIR function of i2gw.ResourcesToIRConverter interface.
28+
type resourcesToIRConverter struct {
29+
featureParsers []i2gw.FeatureParser
30+
implementationSpecificOptions i2gw.ProviderImplementationSpecificOptions
31+
}
32+
33+
// newResourcesToIRConverter returns a cilium resourcesToIRConverter instance.
34+
func newResourcesToIRConverter() *resourcesToIRConverter {
35+
return &resourcesToIRConverter{
36+
featureParsers: []i2gw.FeatureParser{
37+
forceHTTPSFeature,
38+
},
39+
implementationSpecificOptions: i2gw.ProviderImplementationSpecificOptions{
40+
// The list of the implementationSpecific ingress fields options comes here.
41+
},
42+
}
43+
}
44+
45+
func (c *resourcesToIRConverter) convertToIR(storage *storage) (intermediate.IR, field.ErrorList) {
46+
ingressList := []networkingv1.Ingress{}
47+
for _, ing := range storage.Ingresses {
48+
ingressList = append(ingressList, *ing)
49+
}
50+
// Convert plain ingress resources to gateway resources, ignoring all
51+
// provider-specific features.
52+
ir, errs := common.ToIR(ingressList, c.implementationSpecificOptions)
53+
if len(errs) > 0 {
54+
return intermediate.IR{}, errs
55+
}
56+
57+
for _, parseFeatureFunc := range c.featureParsers {
58+
// Apply the feature parsing function to the gateway resources, one by one.
59+
parseErrs := parseFeatureFunc(ingressList, &ir)
60+
// Append the parsing errors to the error list.
61+
errs = append(errs, parseErrs...)
62+
}
63+
64+
return ir, errs
65+
}
+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cilium
18+
19+
import (
20+
"fmt"
21+
22+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate"
23+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/notifications"
24+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common"
25+
networkingv1 "k8s.io/api/networking/v1"
26+
"k8s.io/apimachinery/pkg/types"
27+
"k8s.io/apimachinery/pkg/util/validation/field"
28+
"k8s.io/utils/ptr"
29+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
30+
)
31+
32+
func forceHTTPSFeature(ingresses []networkingv1.Ingress, ir *intermediate.IR) field.ErrorList {
33+
var errs field.ErrorList
34+
forceHTTPSAnnotation := ciliumAnnotation("force-https")
35+
ruleGroups := common.GetRuleGroups(ingresses)
36+
for _, rg := range ruleGroups {
37+
38+
for _, rule := range rg.Rules {
39+
if val, annotationFound := rule.Ingress.Annotations[forceHTTPSAnnotation]; val == "enabled" || val == "true" {
40+
if rule.Ingress.Spec.Rules == nil {
41+
continue
42+
}
43+
key := types.NamespacedName{Namespace: rule.Ingress.Namespace, Name: common.RouteName(rg.Name, rg.Host)}
44+
45+
httpRoute, ok := ir.HTTPRoutes[key]
46+
if !ok {
47+
errs = append(errs, field.NotFound(field.NewPath("HTTPRoute"), key))
48+
}
49+
50+
for i, rule := range httpRoute.Spec.Rules {
51+
rule.Filters = append(rule.Filters, gatewayv1.HTTPRouteFilter{
52+
Type: gatewayv1.HTTPRouteFilterRequestRedirect,
53+
RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
54+
Scheme: ptr.To("https"),
55+
StatusCode: ptr.To(int(301)),
56+
},
57+
})
58+
rule.BackendRefs = nil
59+
60+
httpRoute.Spec.Rules[i] = rule
61+
62+
}
63+
if annotationFound && ok {
64+
notify(notifications.InfoNotification, fmt.Sprintf("parsed \"%v\" annotation of ingress and patched %v fields", forceHTTPSAnnotation, field.NewPath("httproute", "spec", "rules").Key("").Child("filters")), &httpRoute)
65+
}
66+
}
67+
}
68+
}
69+
return errs
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cilium
18+
19+
import (
20+
"testing"
21+
22+
"github.com/google/go-cmp/cmp"
23+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/intermediate"
24+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common"
25+
"k8s.io/apimachinery/pkg/types"
26+
"k8s.io/apimachinery/pkg/util/validation/field"
27+
"k8s.io/utils/ptr"
28+
29+
networkingv1 "k8s.io/api/networking/v1"
30+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31+
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
32+
)
33+
34+
func Test_forceHTTPSFeature(t *testing.T) {
35+
testCases := []struct {
36+
name string
37+
ingress networkingv1.Ingress
38+
initialHTTPRoute *gatewayv1.HTTPRoute
39+
expectedHTTPRoute *gatewayv1.HTTPRoute
40+
expectedError field.ErrorList
41+
}{
42+
{
43+
name: "force-https annontation present and set to enabled",
44+
ingress: networkingv1.Ingress{
45+
ObjectMeta: metav1.ObjectMeta{
46+
Name: "test-ingress",
47+
Namespace: "default",
48+
Annotations: map[string]string{
49+
"ingress.cilium.io/force-https": "enabled",
50+
},
51+
},
52+
Spec: networkingv1.IngressSpec{
53+
Rules: []networkingv1.IngressRule{
54+
{
55+
Host: "foo.com",
56+
},
57+
},
58+
},
59+
},
60+
initialHTTPRoute: &gatewayv1.HTTPRoute{
61+
ObjectMeta: metav1.ObjectMeta{
62+
Name: "test-ingress-foo-com",
63+
Namespace: "default",
64+
},
65+
Spec: gatewayv1.HTTPRouteSpec{
66+
Hostnames: []gatewayv1.Hostname{"foo.com"},
67+
Rules: []gatewayv1.HTTPRouteRule{
68+
{
69+
BackendRefs: []gatewayv1.HTTPBackendRef{
70+
{BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}},
71+
},
72+
},
73+
},
74+
},
75+
},
76+
expectedHTTPRoute: &gatewayv1.HTTPRoute{
77+
ObjectMeta: metav1.ObjectMeta{
78+
Name: "test-ingress-foo-com",
79+
Namespace: "default",
80+
},
81+
Spec: gatewayv1.HTTPRouteSpec{
82+
Hostnames: []gatewayv1.Hostname{"foo.com"},
83+
Rules: []gatewayv1.HTTPRouteRule{
84+
{
85+
Filters: []gatewayv1.HTTPRouteFilter{
86+
{
87+
Type: gatewayv1.HTTPRouteFilterRequestRedirect,
88+
RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
89+
Scheme: ptr.To("https"),
90+
StatusCode: ptr.To(int(301)),
91+
},
92+
},
93+
},
94+
},
95+
},
96+
},
97+
},
98+
expectedError: field.ErrorList{},
99+
},
100+
{
101+
name: "force-https annontation present and set to disabled",
102+
ingress: networkingv1.Ingress{
103+
ObjectMeta: metav1.ObjectMeta{
104+
Name: "test-ingress",
105+
Namespace: "default",
106+
Annotations: map[string]string{
107+
"ingress.cilium.io/force-https": "disabled",
108+
},
109+
},
110+
Spec: networkingv1.IngressSpec{
111+
Rules: []networkingv1.IngressRule{
112+
{
113+
Host: "foo.com",
114+
},
115+
},
116+
},
117+
},
118+
initialHTTPRoute: &gatewayv1.HTTPRoute{
119+
ObjectMeta: metav1.ObjectMeta{
120+
Name: "test-ingress-foo-com",
121+
Namespace: "default",
122+
},
123+
Spec: gatewayv1.HTTPRouteSpec{
124+
Hostnames: []gatewayv1.Hostname{"foo.com"},
125+
Rules: []gatewayv1.HTTPRouteRule{
126+
{
127+
BackendRefs: []gatewayv1.HTTPBackendRef{
128+
{BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}},
129+
},
130+
},
131+
},
132+
},
133+
},
134+
expectedHTTPRoute: &gatewayv1.HTTPRoute{
135+
ObjectMeta: metav1.ObjectMeta{
136+
Name: "test-ingress-foo-com",
137+
Namespace: "default",
138+
},
139+
Spec: gatewayv1.HTTPRouteSpec{
140+
Hostnames: []gatewayv1.Hostname{"foo.com"},
141+
Rules: []gatewayv1.HTTPRouteRule{
142+
{
143+
BackendRefs: []gatewayv1.HTTPBackendRef{
144+
{BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}},
145+
},
146+
},
147+
},
148+
},
149+
},
150+
},
151+
{
152+
name: "force-https annontation present and set to true",
153+
ingress: networkingv1.Ingress{
154+
ObjectMeta: metav1.ObjectMeta{
155+
Name: "test-ingress",
156+
Namespace: "default",
157+
Annotations: map[string]string{
158+
"ingress.cilium.io/force-https": "true",
159+
},
160+
},
161+
Spec: networkingv1.IngressSpec{
162+
Rules: []networkingv1.IngressRule{
163+
{
164+
Host: "foo.com",
165+
},
166+
},
167+
},
168+
},
169+
initialHTTPRoute: &gatewayv1.HTTPRoute{
170+
ObjectMeta: metav1.ObjectMeta{
171+
Name: "test-ingress-foo-com",
172+
Namespace: "default",
173+
},
174+
Spec: gatewayv1.HTTPRouteSpec{
175+
Hostnames: []gatewayv1.Hostname{"foo.com"},
176+
Rules: []gatewayv1.HTTPRouteRule{
177+
{
178+
BackendRefs: []gatewayv1.HTTPBackendRef{
179+
{BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}},
180+
},
181+
},
182+
},
183+
},
184+
},
185+
expectedHTTPRoute: &gatewayv1.HTTPRoute{
186+
ObjectMeta: metav1.ObjectMeta{
187+
Name: "test-ingress-foo-com",
188+
Namespace: "default",
189+
},
190+
Spec: gatewayv1.HTTPRouteSpec{
191+
Hostnames: []gatewayv1.Hostname{"foo.com"},
192+
Rules: []gatewayv1.HTTPRouteRule{
193+
{
194+
Filters: []gatewayv1.HTTPRouteFilter{
195+
{
196+
Type: gatewayv1.HTTPRouteFilterRequestRedirect,
197+
RequestRedirect: &gatewayv1.HTTPRequestRedirectFilter{
198+
Scheme: ptr.To("https"),
199+
StatusCode: ptr.To(int(301)),
200+
},
201+
},
202+
},
203+
},
204+
},
205+
},
206+
},
207+
expectedError: field.ErrorList{},
208+
},
209+
{
210+
name: "force-https annontation present and set to false",
211+
ingress: networkingv1.Ingress{
212+
ObjectMeta: metav1.ObjectMeta{
213+
Name: "test-ingress",
214+
Namespace: "default",
215+
Annotations: map[string]string{
216+
"ingress.cilium.io/force-https": "false",
217+
},
218+
},
219+
Spec: networkingv1.IngressSpec{
220+
Rules: []networkingv1.IngressRule{
221+
{
222+
Host: "foo.com",
223+
},
224+
},
225+
},
226+
},
227+
initialHTTPRoute: &gatewayv1.HTTPRoute{
228+
ObjectMeta: metav1.ObjectMeta{
229+
Name: "test-ingress-foo-com",
230+
Namespace: "default",
231+
},
232+
Spec: gatewayv1.HTTPRouteSpec{
233+
Hostnames: []gatewayv1.Hostname{"foo.com"},
234+
Rules: []gatewayv1.HTTPRouteRule{
235+
{
236+
BackendRefs: []gatewayv1.HTTPBackendRef{
237+
{BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}},
238+
},
239+
},
240+
},
241+
},
242+
},
243+
expectedHTTPRoute: &gatewayv1.HTTPRoute{
244+
ObjectMeta: metav1.ObjectMeta{
245+
Name: "test-ingress-foo-com",
246+
Namespace: "default",
247+
},
248+
Spec: gatewayv1.HTTPRouteSpec{
249+
Hostnames: []gatewayv1.Hostname{"foo.com"},
250+
Rules: []gatewayv1.HTTPRouteRule{
251+
{
252+
BackendRefs: []gatewayv1.HTTPBackendRef{
253+
{BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}},
254+
},
255+
},
256+
},
257+
},
258+
},
259+
expectedError: field.ErrorList{},
260+
},
261+
{
262+
name: "force-https annotation not present",
263+
ingress: networkingv1.Ingress{
264+
ObjectMeta: metav1.ObjectMeta{
265+
Name: "test-ingress",
266+
Namespace: "default",
267+
},
268+
Spec: networkingv1.IngressSpec{
269+
Rules: []networkingv1.IngressRule{
270+
{
271+
Host: "foo.com",
272+
},
273+
},
274+
},
275+
},
276+
initialHTTPRoute: &gatewayv1.HTTPRoute{
277+
ObjectMeta: metav1.ObjectMeta{
278+
Name: "test-ingress-foo-com",
279+
Namespace: "default",
280+
},
281+
Spec: gatewayv1.HTTPRouteSpec{
282+
Hostnames: []gatewayv1.Hostname{"foo.com"},
283+
Rules: []gatewayv1.HTTPRouteRule{
284+
{
285+
BackendRefs: []gatewayv1.HTTPBackendRef{
286+
{BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}},
287+
},
288+
},
289+
},
290+
},
291+
},
292+
expectedHTTPRoute: &gatewayv1.HTTPRoute{
293+
ObjectMeta: metav1.ObjectMeta{
294+
Name: "test-ingress-foo-com",
295+
Namespace: "default",
296+
},
297+
Spec: gatewayv1.HTTPRouteSpec{
298+
Hostnames: []gatewayv1.Hostname{"foo.com"},
299+
Rules: []gatewayv1.HTTPRouteRule{
300+
{
301+
BackendRefs: []gatewayv1.HTTPBackendRef{
302+
{BackendRef: gatewayv1.BackendRef{BackendObjectReference: gatewayv1.BackendObjectReference{Name: "foo", Port: ptr.To(gatewayv1.PortNumber(3000))}}},
303+
},
304+
},
305+
},
306+
},
307+
},
308+
expectedError: field.ErrorList{},
309+
},
310+
}
311+
312+
for _, tc := range testCases {
313+
t.Run(tc.name, func(t *testing.T) {
314+
ingresses := []networkingv1.Ingress{tc.ingress}
315+
ir := &intermediate.IR{
316+
HTTPRoutes: map[types.NamespacedName]intermediate.HTTPRouteContext{
317+
{Name: tc.expectedHTTPRoute.Name, Namespace: tc.expectedHTTPRoute.Namespace}: {
318+
HTTPRoute: *tc.initialHTTPRoute,
319+
},
320+
},
321+
}
322+
323+
errs := forceHTTPSFeature(ingresses, ir)
324+
325+
if len(errs) != len(tc.expectedError) {
326+
t.Errorf("expected %d errors, got %d", len(tc.expectedError), len(errs))
327+
}
328+
329+
key := types.NamespacedName{Namespace: tc.ingress.Namespace, Name: common.RouteName(tc.ingress.Name, tc.ingress.Spec.Rules[0].Host)}
330+
331+
actualHTTPRouteContext, ok := ir.HTTPRoutes[key]
332+
if !ok {
333+
t.Errorf("HTTPRoute not found: %v", key)
334+
}
335+
336+
if diff := cmp.Diff(*tc.expectedHTTPRoute, actualHTTPRouteContext.HTTPRoute); diff != "" {
337+
t.Errorf("Unexpected HTTPRoute resource found, \n want: %+v\n got: %+v\n diff (-want +got):\n%s", *tc.expectedHTTPRoute, actualHTTPRouteContext.HTTPRoute, diff)
338+
}
339+
})
340+
}
341+
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
Copyright 2024 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cilium
18+
19+
import (
20+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/notifications"
21+
"sigs.k8s.io/controller-runtime/pkg/client"
22+
)
23+
24+
func notify(mType notifications.MessageType, message string, callingObject ...client.Object) {
25+
newNotification := notifications.NewNotification(mType, message, callingObject...)
26+
notifications.NotificationAggr.DispatchNotification(newNotification, string(Name))
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cilium
18+
19+
import (
20+
"context"
21+
22+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
23+
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw/providers/common"
24+
"k8s.io/apimachinery/pkg/util/sets"
25+
)
26+
27+
// resourceReader implements the i2gw.CustomResourceReader interface.
28+
type resourceReader struct {
29+
conf *i2gw.ProviderConf
30+
}
31+
32+
// newResourceReader returns a resourceReader instance.
33+
func newResourceReader(conf *i2gw.ProviderConf) *resourceReader {
34+
return &resourceReader{
35+
conf: conf,
36+
}
37+
}
38+
39+
func (r *resourceReader) readResourcesFromCluster(ctx context.Context) (*storage, error) {
40+
// read cilium related resources from cluster.
41+
storage := newResourcesStorage()
42+
43+
ingresses, err := common.ReadIngressesFromCluster(ctx, r.conf.Client, sets.New(CiliumIngressClass))
44+
if err != nil {
45+
return nil, err
46+
}
47+
storage.Ingresses = ingresses
48+
return storage, nil
49+
}
50+
51+
func (r *resourceReader) readResourcesFromFile(filename string) (*storage, error) {
52+
// read cilium related resources from file.
53+
storage := newResourcesStorage()
54+
55+
ingresses, err := common.ReadIngressesFromFile(filename, r.conf.Namespace, sets.New[string](CiliumIngressClass))
56+
if err != nil {
57+
return nil, err
58+
}
59+
storage.Ingresses = ingresses
60+
return storage, nil
61+
}

‎pkg/i2gw/providers/cilium/storage.go

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
Copyright 2023 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package cilium
18+
19+
import (
20+
networkingv1 "k8s.io/api/networking/v1"
21+
"k8s.io/apimachinery/pkg/types"
22+
)
23+
24+
type storage struct {
25+
Ingresses map[types.NamespacedName]*networkingv1.Ingress
26+
}
27+
28+
func newResourcesStorage() *storage {
29+
return &storage{
30+
Ingresses: map[types.NamespacedName]*networkingv1.Ingress{},
31+
}
32+
}

0 commit comments

Comments
 (0)
Please sign in to comment.