Skip to content

Commit 5a49d40

Browse files
[Revert the revert] Change ingress fetching to be isolated per provider (#116)
* pass namespace in ProviderConf and make read from file account for namepsaces * move ingress-nginx to fetch its own ingresses * move Kong to fetch its own ingresses * Deprecate ingress fetching from the generic package Also added issue numbers for TODOs * add helper common functions for reading ingresses and extract them from file
1 parent f34adf7 commit 5a49d40

19 files changed

+539
-333
lines changed

pkg/i2gw/ingress2gateway.go

+4-107
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,10 @@ limitations under the License.
1717
package i2gw
1818

1919
import (
20-
"bytes"
2120
"context"
22-
"errors"
2321
"fmt"
24-
"io"
25-
"os"
2622

27-
networkingv1 "k8s.io/api/networking/v1"
28-
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
29-
"k8s.io/apimachinery/pkg/runtime"
3023
"k8s.io/apimachinery/pkg/util/validation/field"
31-
kubeyaml "k8s.io/apimachinery/pkg/util/yaml"
3224
"sigs.k8s.io/controller-runtime/pkg/client"
3325
"sigs.k8s.io/controller-runtime/pkg/client/config"
3426
)
@@ -45,30 +37,19 @@ func ToGatewayAPIResources(ctx context.Context, namespace string, inputFile stri
4537
}
4638
cl = client.NewNamespacedClient(cl, namespace)
4739

48-
var ingresses networkingv1.IngressList
49-
5040
providerByName, err := constructProviders(&ProviderConf{
51-
Client: cl,
41+
Client: cl,
42+
Namespace: namespace,
5243
}, providers)
5344
if err != nil {
5445
return nil, err
5546
}
5647

57-
resources := InputResources{}
58-
5948
if inputFile != "" {
60-
if err = ConstructIngressesFromFile(&ingresses, inputFile, namespace); err != nil {
61-
return nil, fmt.Errorf("failed to read ingresses from file: %w", err)
62-
}
63-
resources.Ingresses = ingresses.Items
6449
if err = readProviderResourcesFromFile(ctx, providerByName, inputFile); err != nil {
6550
return nil, err
6651
}
6752
} else {
68-
if err = ConstructIngressesFromCluster(ctx, cl, &ingresses); err != nil {
69-
return nil, fmt.Errorf("failed to read ingresses from cluster: %w", err)
70-
}
71-
resources.Ingresses = ingresses.Items
7253
if err = readProviderResourcesFromCluster(ctx, providerByName); err != nil {
7354
return nil, err
7455
}
@@ -79,7 +60,8 @@ func ToGatewayAPIResources(ctx context.Context, namespace string, inputFile stri
7960
errs field.ErrorList
8061
)
8162
for _, provider := range providerByName {
82-
providerGatewayResources, conversionErrs := provider.ToGatewayAPI(resources)
63+
// TODO(#113) Remove input resources from ToGatewayAPI function
64+
providerGatewayResources, conversionErrs := provider.ToGatewayAPI(InputResources{})
8365
errs = append(errs, conversionErrs...)
8466
gatewayResources = append(gatewayResources, providerGatewayResources)
8567
}
@@ -108,14 +90,6 @@ func readProviderResourcesFromCluster(ctx context.Context, providerByName map[Pr
10890
return nil
10991
}
11092

111-
func ConstructIngressesFromCluster(ctx context.Context, cl client.Client, ingressList *networkingv1.IngressList) error {
112-
err := cl.List(ctx, ingressList)
113-
if err != nil {
114-
return fmt.Errorf("failed to get ingresses from the cluster: %w", err)
115-
}
116-
return nil
117-
}
118-
11993
// constructProviders constructs a map of concrete Provider implementations
12094
// by their ProviderName.
12195
func constructProviders(conf *ProviderConf, providers []string) (map[ProviderName]Provider, error) {
@@ -133,83 +107,6 @@ func constructProviders(conf *ProviderConf, providers []string) (map[ProviderNam
133107
return providerByName, nil
134108
}
135109

136-
// ExtractObjectsFromReader extracts all objects from a reader,
137-
// which is created from YAML or JSON input files.
138-
// It retrieves all objects, including nested ones if they are contained within a list.
139-
func ExtractObjectsFromReader(reader io.Reader) ([]*unstructured.Unstructured, error) {
140-
d := kubeyaml.NewYAMLOrJSONDecoder(reader, 4096)
141-
var objs []*unstructured.Unstructured
142-
for {
143-
u := &unstructured.Unstructured{}
144-
if err := d.Decode(&u); err != nil {
145-
if errors.Is(err, io.EOF) {
146-
break
147-
}
148-
return objs, fmt.Errorf("failed to unmarshal manifest: %w", err)
149-
}
150-
if u == nil {
151-
continue
152-
}
153-
objs = append(objs, u)
154-
}
155-
156-
finalObjs := []*unstructured.Unstructured{}
157-
for _, obj := range objs {
158-
tmpObjs := []*unstructured.Unstructured{}
159-
if obj.IsList() {
160-
err := obj.EachListItem(func(object runtime.Object) error {
161-
unstructuredObj, ok := object.(*unstructured.Unstructured)
162-
if ok {
163-
tmpObjs = append(tmpObjs, unstructuredObj)
164-
return nil
165-
}
166-
return fmt.Errorf("resource list item has unexpected type")
167-
})
168-
if err != nil {
169-
return nil, err
170-
}
171-
} else {
172-
tmpObjs = append(tmpObjs, obj)
173-
}
174-
finalObjs = append(finalObjs, tmpObjs...)
175-
}
176-
177-
return finalObjs, nil
178-
}
179-
180-
// ConstructIngressesFromFile reads the inputFile in either json/yaml formats,
181-
// then deserialize the file into Ingresses resources.
182-
// All ingresses will be pushed into the supplied IngressList for return.
183-
func ConstructIngressesFromFile(l *networkingv1.IngressList, inputFile string, namespace string) error {
184-
stream, err := os.ReadFile(inputFile)
185-
if err != nil {
186-
return err
187-
}
188-
189-
reader := bytes.NewReader(stream)
190-
objs, err := ExtractObjectsFromReader(reader)
191-
if err != nil {
192-
return err
193-
}
194-
195-
for _, f := range objs {
196-
if namespace != "" && f.GetNamespace() != namespace {
197-
continue
198-
}
199-
if !f.GroupVersionKind().Empty() && f.GroupVersionKind().Kind == "Ingress" {
200-
var i networkingv1.Ingress
201-
err = runtime.DefaultUnstructuredConverter.
202-
FromUnstructured(f.UnstructuredContent(), &i)
203-
if err != nil {
204-
return err
205-
}
206-
l.Items = append(l.Items, i)
207-
}
208-
209-
}
210-
return nil
211-
}
212-
213110
func aggregatedErrs(errs field.ErrorList) error {
214111
errMsg := fmt.Errorf("\n# Encountered %d errors", len(errs))
215112
for _, err := range errs {

pkg/i2gw/ingress2gateway_test.go

-127
Original file line numberDiff line numberDiff line change
@@ -17,140 +17,13 @@ limitations under the License.
1717
package i2gw
1818

1919
import (
20-
"context"
2120
"fmt"
2221
"testing"
2322

24-
"github.com/google/go-cmp/cmp"
25-
networkingv1 "k8s.io/api/networking/v1"
26-
apiequality "k8s.io/apimachinery/pkg/api/equality"
27-
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2823
"k8s.io/apimachinery/pkg/runtime"
2924
"sigs.k8s.io/controller-runtime/pkg/client/fake"
3025
)
3126

32-
func Test_constructIngressesFromFile(t *testing.T) {
33-
ingress1 := ingress(443, "ingress1", "namespace1")
34-
ingress2 := ingress(80, "ingress2", "namespace2")
35-
ingressNoNamespace := ingress(80, "ingress-no-namespace", "")
36-
37-
testCases := []struct {
38-
name string
39-
filePath string
40-
namespace string
41-
wantIngressList []networkingv1.Ingress
42-
}{
43-
{
44-
name: "Test yaml input file with multiple resources with no namespace flag",
45-
filePath: "testdata/input-file.yaml",
46-
namespace: "",
47-
wantIngressList: []networkingv1.Ingress{ingress1, ingress2, ingressNoNamespace},
48-
}, {
49-
name: "Test json input file with multiple resources with no namespace flag",
50-
filePath: "testdata/input-file.json",
51-
namespace: "",
52-
wantIngressList: []networkingv1.Ingress{ingress1, ingress2, ingressNoNamespace},
53-
}, {
54-
name: "Test yaml input file with multiple resources with namespace1 flag",
55-
filePath: "testdata/input-file.yaml",
56-
namespace: "namespace1",
57-
wantIngressList: []networkingv1.Ingress{ingress1},
58-
},
59-
}
60-
61-
for _, tc := range testCases {
62-
t.Run(tc.name, func(t *testing.T) {
63-
gotIngressList := &networkingv1.IngressList{}
64-
err := ConstructIngressesFromFile(gotIngressList, tc.filePath, tc.namespace)
65-
if err != nil {
66-
t.Errorf("Failed to open test file: %v", err)
67-
}
68-
compareIngressLists(t, gotIngressList, tc.wantIngressList)
69-
})
70-
}
71-
}
72-
73-
func ingress(port int32, name, namespace string) networkingv1.Ingress {
74-
iPrefix := networkingv1.PathTypePrefix
75-
ingressClassName := fmt.Sprintf("ingressClass-%s", name)
76-
var objMeta metav1.ObjectMeta
77-
if namespace != "" {
78-
objMeta = metav1.ObjectMeta{Name: name, ResourceVersion: "999", Namespace: namespace}
79-
} else {
80-
objMeta = metav1.ObjectMeta{Name: name, ResourceVersion: "999"}
81-
}
82-
83-
ing := networkingv1.Ingress{
84-
TypeMeta: metav1.TypeMeta{
85-
Kind: "Ingress",
86-
APIVersion: "networking.k8s.io/v1",
87-
},
88-
ObjectMeta: objMeta,
89-
Spec: networkingv1.IngressSpec{
90-
IngressClassName: &ingressClassName,
91-
Rules: []networkingv1.IngressRule{{
92-
IngressRuleValue: networkingv1.IngressRuleValue{
93-
HTTP: &networkingv1.HTTPIngressRuleValue{
94-
Paths: []networkingv1.HTTPIngressPath{{
95-
Path: fmt.Sprintf("/path-%s", name),
96-
PathType: &iPrefix,
97-
Backend: networkingv1.IngressBackend{
98-
Service: &networkingv1.IngressServiceBackend{
99-
Name: fmt.Sprintf("service-%s", name),
100-
Port: networkingv1.ServiceBackendPort{
101-
Number: port,
102-
},
103-
},
104-
},
105-
}},
106-
},
107-
},
108-
}},
109-
},
110-
}
111-
return ing
112-
}
113-
114-
func compareIngressLists(t *testing.T, gotIngressList *networkingv1.IngressList, wantIngressList []networkingv1.Ingress) {
115-
for i, got := range gotIngressList.Items {
116-
want := wantIngressList[i]
117-
if !apiequality.Semantic.DeepEqual(got, want) {
118-
t.Errorf("Expected Ingress %d to be %+v\n Got: %+v\n Diff: %s", i, want, got, cmp.Diff(want, got))
119-
}
120-
}
121-
}
122-
123-
func Test_constructIngressesFromCluster(t *testing.T) {
124-
ingress1 := ingress(443, "ingress1", "namespace1")
125-
ingress2 := ingress(80, "ingress2", "namespace2")
126-
testCases := []struct {
127-
name string
128-
runtimeObjs []runtime.Object
129-
wantIngresses []networkingv1.Ingress
130-
}{{
131-
name: "Test cluster client with 2 resources",
132-
runtimeObjs: []runtime.Object{&ingress1, &ingress2},
133-
wantIngresses: []networkingv1.Ingress{ingress1, ingress2},
134-
}, {
135-
name: "Test cluster client without resources",
136-
runtimeObjs: []runtime.Object{},
137-
wantIngresses: []networkingv1.Ingress{},
138-
},
139-
}
140-
141-
for _, tc := range testCases {
142-
t.Run(tc.name, func(t *testing.T) {
143-
gotIngresses := &networkingv1.IngressList{}
144-
cl := fake.NewClientBuilder().WithRuntimeObjects(tc.runtimeObjs...).Build()
145-
err := ConstructIngressesFromCluster(context.Background(), cl, gotIngresses)
146-
if err != nil {
147-
t.Errorf("test failed unexpectedly: %v", err)
148-
}
149-
compareIngressLists(t, gotIngresses, tc.wantIngresses)
150-
})
151-
}
152-
}
153-
15427
func Test_constructProviders(t *testing.T) {
15528
supportProviders := []string{"ingress-nginx"}
15629
for _, provider := range supportProviders {

pkg/i2gw/provider.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ type ProviderConstructor func(conf *ProviderConf) Provider
4242
// ProviderConf contains all the configuration required for every concrete
4343
// Provider implementation.
4444
type ProviderConf struct {
45-
Client client.Client
45+
Client client.Client
46+
Namespace string
4647
}
4748

4849
// The Provider interface specifies the required functionality which needs to be

0 commit comments

Comments
 (0)