Skip to content

Commit

Permalink
Istio provider: add e2e test with file examples/fixtures.
Browse files Browse the repository at this point in the history
  • Loading branch information
dpasiukevich committed Dec 30, 2023
1 parent 23e8389 commit d0bc03b
Show file tree
Hide file tree
Showing 11 changed files with 707 additions and 0 deletions.
170 changes: 170 additions & 0 deletions pkg/i2gw/providers/istio/e2e_file_converter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
/*
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 istio

import (
"bytes"
"context"
"fmt"
"io/fs"
"os"
"path/filepath"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/kubernetes-sigs/ingress2gateway/pkg/i2gw"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
gatewayv1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
)

const fixturesDir = "./fixtures"

func TestFileConversion(t *testing.T) {
ctx := context.Background()

filepath.WalkDir(filepath.Join(fixturesDir, "input"), func(path string, d fs.DirEntry, err error) error {
if err != nil {
t.Fatalf(err.Error())
}
if d.IsDir() {
return nil
}

istioProvider := NewProvider(&i2gw.ProviderConf{})

err = istioProvider.ReadResourcesFromFile(ctx, path)
if err != nil {
t.Fatalf("Failed to read input from file %v: %v", d.Name(), err.Error())
}

gotGatewayResources, errList := istioProvider.ToGatewayAPI(i2gw.InputResources{})
if len(errList) > 0 {
t.Fatalf("unexpected errors during input conversion for file %v: %v", d.Name(), errList.ToAggregate().Error())
}

outputFile := filepath.Join(fixturesDir, "output", d.Name())
wantGatewayResources, err := readGatewayResourcesFromFile(t, outputFile)
if err != nil {
t.Fatalf("failed to read wantGatewayResources from file %v: %v", outputFile, err.Error())
}

if !apiequality.Semantic.DeepEqual(gotGatewayResources.Gateways, wantGatewayResources.Gateways) {
t.Errorf("Gateways diff for file %v (-want +got): %s", d.Name(), cmp.Diff(wantGatewayResources.Gateways, gotGatewayResources.Gateways))
}

if !apiequality.Semantic.DeepEqual(gotGatewayResources.HTTPRoutes, wantGatewayResources.HTTPRoutes) {
t.Errorf("HTTPRoutes diff for file %v (-want +got): %s", d.Name(), cmp.Diff(wantGatewayResources.HTTPRoutes, gotGatewayResources.HTTPRoutes))
}

if !apiequality.Semantic.DeepEqual(gotGatewayResources.TLSRoutes, wantGatewayResources.TLSRoutes) {
t.Errorf("TLSRoutes diff for file %v (-want +got): %s", d.Name(), cmp.Diff(wantGatewayResources.TLSRoutes, gotGatewayResources.TLSRoutes))
}

if !apiequality.Semantic.DeepEqual(gotGatewayResources.TCPRoutes, wantGatewayResources.TCPRoutes) {
t.Errorf("TCPRoutes diff for file %v (-want +got): %s", d.Name(), cmp.Diff(wantGatewayResources.TCPRoutes, gotGatewayResources.TCPRoutes))
}

if !apiequality.Semantic.DeepEqual(gotGatewayResources.ReferenceGrants, wantGatewayResources.ReferenceGrants) {
t.Errorf("ReferenceGrants diff for file %v (-want +got): %s", d.Name(), cmp.Diff(wantGatewayResources.ReferenceGrants, gotGatewayResources.ReferenceGrants))
}

return nil
})
}

func readGatewayResourcesFromFile(t *testing.T, filename string) (*i2gw.GatewayResources, error) {
t.Helper()

stream, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("failed to read file %v: %w", filename, err)
}

unstructuredObjects, err := i2gw.ExtractObjectsFromReader(bytes.NewReader(stream), "")
if err != nil {
return nil, fmt.Errorf("failed to extract objects: %w", err)
}

res := i2gw.GatewayResources{
Gateways: make(map[types.NamespacedName]gatewayv1.Gateway),
HTTPRoutes: make(map[types.NamespacedName]gatewayv1.HTTPRoute),
TLSRoutes: make(map[types.NamespacedName]gatewayv1alpha2.TLSRoute),
TCPRoutes: make(map[types.NamespacedName]gatewayv1alpha2.TCPRoute),
ReferenceGrants: make(map[types.NamespacedName]gatewayv1alpha2.ReferenceGrant),
}

for _, obj := range unstructuredObjects {
switch objKind := obj.GetKind(); objKind {
case "Gateway":
var gw gatewayv1.Gateway
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &gw); err != nil {
return nil, fmt.Errorf("failed to parse k8s gateway object: %w", err)
}
res.Gateways[types.NamespacedName{
Namespace: gw.Namespace,
Name: gw.Name,
}] = gw
case "HTTPRoute":
var httpRoute gatewayv1.HTTPRoute
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &httpRoute); err != nil {
return nil, fmt.Errorf("failed to parse k8s gateway HTTPRoute object: %w", err)
}

res.HTTPRoutes[types.NamespacedName{
Namespace: httpRoute.Namespace,
Name: httpRoute.Name,
}] = httpRoute
case "TLSRoute":
var tlsRoute gatewayv1alpha2.TLSRoute
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &tlsRoute); err != nil {
return nil, fmt.Errorf("failed to parse k8s gateway TLSRoute object: %w", err)
}

res.TLSRoutes[types.NamespacedName{
Namespace: tlsRoute.Namespace,
Name: tlsRoute.Name,
}] = tlsRoute
case "TCPRoute":
var tcpRoute gatewayv1alpha2.TCPRoute
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &tcpRoute); err != nil {
return nil, fmt.Errorf("failed to parse k8s gateway TCPRoute object: %w", err)
}

res.TCPRoutes[types.NamespacedName{
Namespace: tcpRoute.Namespace,
Name: tcpRoute.Name,
}] = tcpRoute
case "ReferenceGrant":
var referenceGrant gatewayv1alpha2.ReferenceGrant
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &referenceGrant); err != nil {
return nil, fmt.Errorf("failed to parse k8s gateway ReferenceGrant object: %w", err)
}

res.ReferenceGrants[types.NamespacedName{
Namespace: referenceGrant.Namespace,
Name: referenceGrant.Name,
}] = referenceGrant
default:
return nil, fmt.Errorf("unknown object kind: %v", objKind)
}
}

return &res, nil
}
85 changes: 85 additions & 0 deletions pkg/i2gw/providers/istio/fixtures/input/1-gateway.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
name: my-gateway
namespace: test
spec:
selector:
app: istio-ingressgateway
istio: ingressgateway
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- test.com
- port:
number: 443
protocol: HTTPS
hosts:
- "bookinfo/*.bookinfo.com"
- "*"
- "bookinfo" # short service-name inferred hosts are ignored; in future converter may expand this to the fqdn required by k8s api gateway
tls:
httpsRedirect: true
mode: SIMPLE
# all following tls related fields are ignored as there's no direct mapping to the k8s gateway api
serverCertificate: /etc/certs/servercert.pem
privateKey: /etc/certs/privatekey.pem
credentialName: bookinfo-secret
caCertificates: /etc/certs/caCertificates
subjectAltNames: ["v1"]
verifyCertificateSpki: ["v1"]
verifyCertificateHash: ["v1"]
minProtocolVersion: TLSV1_0
maxProtocolVersion: TLSV1_3
cipherSuites: ["v1"]
bind: 1.2.3.4 # is ignored
- port:
number: 8080
protocol: TCP
hosts:
- "*/foo.example.com"
- "./foo.example.com"
- port:
number: 8443
protocol: TLS
hosts:
- "prod/*.tls.com"
- "*.tls.com"
- port: # converted to HTTP protocol as there's no TLS section
number: 8143
protocol: HTTP2
hosts:
- "http2.dev"
- port: # converted to HTTPS protocol as there's TLS section
number: 8144
protocol: HTTP2
hosts:
- "http2.dev"
tls:
mode: SIMPLE
- port: # converted to HTTP protocol as there's no TLS section
number: 8180
protocol: GRPC
hosts:
- "*"
- port: # converted to HTTPS protocol as there's TLS section
number: 8181
protocol: GRPC
hosts:
- "*"
tls:
mode: SIMPLE
- port: # converted to TCP protocol
number: 2379
protocol: MONGO
hosts:
- "*"
- port: # this server is ignored because of the unknown protocol
number: 8000
name: unknown-protocol
protocol: TEST
hosts:
- "*"
110 changes: 110 additions & 0 deletions pkg/i2gw/providers/istio/fixtures/input/2-virtualservice-http.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
namespace: test
name: reviews-route
spec:
gateways:
- "my-gateway"
hosts:
- reviews.prod.svc.cluster.local
- reviews.test.svc.cluster.local
http:
- name: "v2"
match:
- uri:
prefix: "/wpcatalog"
headers:
h1:
exact: v1
queryParams:
q1:
exact: v2
method:
exact: GET
- uri:
exact: "/consumercatalog"
headers:
h2:
regex: v3
queryParams:
q2:
regex: v4
- uri:
regex: "/catalog[0-9]+"
# all fields for match below are ignored
scheme:
exact: "value"
authority:
exact: "value"
port: 8080
sourceLabels:
k: v
ignoreUriCase: true
withoutHeaders:
header1:
exact: value
sourceNamespace: test
statPrefix: stats
gateways: ["gw1"]
redirect:
uri: /v1/bookRatings
redirectCode: 302
scheme: http
port: 8080
# authority & derivePort are ignored
authority: newratings.default.svc.cluster.local
rewrite:
uri: "/newcatalog"
# authority & uriRegexRewrite are ignored
authority: test
uriRegexRewrite:
match: pattern
rewrite: new-pattern
mirror:
host: reviews # interpreted as reviews.test.svc.cluster.local
subset: v1
mirrors:
- destination:
host: reviewsA # interpreted as reviews.test.svc.cluster.local
percentage:
value: 50
- destination:
host: reviewsB.prod.svc.cluster.local # interpreted as reviews.test.svc.cluster.local
percentage:
value: 50
route:
- destination:
host: reviews.prod.svc.cluster.local
subset: v2
timeout: 5s
headers:
request:
add:
h1: v1
set:
h2: v2
remove:
- h3
response:
add:
h4: v4
set:
h5: v5
remove:
- h6
# the remaning fields are ignored
directResponse:
status: 503
delegate:
name: reviews
retries:
attempts: 3
fault:
abort:
percentage:
value: 0.1
httpStatus: 400
corsPolicy:
allowOrigins:
- exact: https://example.com
26 changes: 26 additions & 0 deletions pkg/i2gw/providers/istio/fixtures/input/3-virtualservice-tls.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
namespace: test
name: reviews-route
spec:
gateways:
- "my-gateway"
hosts:
- reviews.prod.svc.cluster.local
- reviews.test.svc.cluster.local
tls:
- match:
- port: 443
sniHosts:
- login.bookinfo.com
route:
- destination:
host: login.prod.svc.cluster.local
- match:
- port: 443
sniHosts:
- reviews.bookinfo.com
route:
- destination:
host: reviews
Loading

0 comments on commit d0bc03b

Please sign in to comment.