Skip to content

Commit e8ab832

Browse files
M00nF1shkishorj
andauthored
add e2e tests based on a vanilla ingress (#2294)
Co-authored-by: Kishor Joshi <[email protected]>
1 parent 06739ab commit e8ab832

File tree

9 files changed

+852
-23
lines changed

9 files changed

+852
-23
lines changed

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ module sigs.k8s.io/aws-load-balancer-controller
33
go 1.16
44

55
require (
6+
github.com/Masterminds/sprig/v3 v3.2.2 // indirect
67
github.com/aws/aws-sdk-go v1.40.7
8+
github.com/fatih/color v1.7.0 // indirect
9+
github.com/gavv/httpexpect/v2 v2.3.1 // indirect
710
github.com/go-logr/logr v0.4.0
811
github.com/golang/mock v1.6.0
912
github.com/google/go-cmp v0.5.6
1013
github.com/onsi/ginkgo v1.16.4
11-
github.com/onsi/gomega v1.13.0
14+
github.com/onsi/gomega v1.16.0
1215
github.com/pkg/errors v0.9.1
1316
github.com/prometheus/client_golang v1.11.0
1417
github.com/spf13/pflag v1.0.5

go.sum

Lines changed: 46 additions & 0 deletions
Large diffs are not rendered by default.

test/e2e/ingress/vanilla_ingress_test.go

Lines changed: 419 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package fixture
2+
3+
import (
4+
"context"
5+
"github.com/pkg/errors"
6+
apierrs "k8s.io/apimachinery/pkg/api/errors"
7+
"k8s.io/apimachinery/pkg/util/wait"
8+
"sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
9+
"sigs.k8s.io/aws-load-balancer-controller/test/framework"
10+
"sigs.k8s.io/aws-load-balancer-controller/test/framework/utils"
11+
"sigs.k8s.io/controller-runtime/pkg/client"
12+
"sigs.k8s.io/controller-runtime/pkg/client/apiutil"
13+
"sort"
14+
)
15+
16+
var (
17+
defaultKindOrdering = []string{
18+
"IngressClass",
19+
"Namespace",
20+
"Service",
21+
"Deployment",
22+
"Ingress",
23+
}
24+
)
25+
26+
// NewK8SResourceStack constructs new Resource stack.
27+
func NewK8SResourceStack(tf *framework.Framework, objs ...client.Object) *k8sResourceStack {
28+
return &k8sResourceStack{
29+
tf: tf,
30+
objs: objs,
31+
provisionedObjs: nil,
32+
kindOrdering: defaultKindOrdering,
33+
}
34+
}
35+
36+
type k8sResourceStack struct {
37+
tf *framework.Framework
38+
objs []client.Object
39+
provisionedObjs []client.Object
40+
kindOrdering []string
41+
}
42+
43+
func (s *k8sResourceStack) Setup(ctx context.Context) error {
44+
scheme := s.tf.K8sClient.Scheme()
45+
sortedObjs, err := s.sortedObjectsOrderingByKind(s.kindOrdering)
46+
if err != nil {
47+
return err
48+
}
49+
for _, obj := range sortedObjs {
50+
objKind, _ := apiutil.GVKForObject(obj, scheme)
51+
s.tf.Logger.Info("creating resource", "kind", objKind.Kind, "key", k8s.NamespacedName(obj))
52+
if err := s.tf.K8sClient.Create(ctx, obj); err != nil {
53+
return err
54+
}
55+
s.provisionedObjs = append(s.provisionedObjs, obj)
56+
s.tf.Logger.Info("created resource", "kind", objKind.Kind, "key", k8s.NamespacedName(obj))
57+
}
58+
return nil
59+
}
60+
61+
func (s *k8sResourceStack) TearDown(ctx context.Context) error {
62+
scheme := s.tf.K8sClient.Scheme()
63+
for i := len(s.provisionedObjs) - 1; i >= 0; i-- {
64+
obj := s.provisionedObjs[i]
65+
objKind, _ := apiutil.GVKForObject(obj, scheme)
66+
s.tf.Logger.Info("deleting resource", "kind", objKind.Kind, "key", k8s.NamespacedName(obj))
67+
if err := s.tf.K8sClient.Delete(ctx, obj); err != nil {
68+
return err
69+
}
70+
s.tf.Logger.Info("deleted resource", "kind", objKind.Kind, "key", k8s.NamespacedName(obj))
71+
72+
s.tf.Logger.Info("wait resource becomes deleted", "kind", objKind.Kind, "key", k8s.NamespacedName(obj))
73+
if err := s.waitUntilResourceDeleted(ctx, obj); err != nil {
74+
return err
75+
}
76+
s.tf.Logger.Info("resource becomes deleted", "kind", objKind.Kind, "key", k8s.NamespacedName(obj))
77+
}
78+
return nil
79+
}
80+
81+
type objWithOrder struct {
82+
obj client.Object
83+
order int
84+
}
85+
86+
func (s *k8sResourceStack) sortedObjectsOrderingByKind(kindOrdering []string) ([]client.Object, error) {
87+
ordering := make(map[string]int, len(kindOrdering))
88+
for i, k := range kindOrdering {
89+
ordering[k] = i
90+
}
91+
92+
scheme := s.tf.K8sClient.Scheme()
93+
var objWithOrderList []objWithOrder
94+
for _, obj := range s.objs {
95+
objKind, err := apiutil.GVKForObject(obj, scheme)
96+
if err != nil {
97+
return nil, err
98+
}
99+
order, ok := ordering[objKind.Kind]
100+
if !ok {
101+
return nil, errors.Errorf("ordering for %v Kind is not configured", objKind)
102+
}
103+
objWithOrderList = append(objWithOrderList, objWithOrder{
104+
obj: obj,
105+
order: order,
106+
})
107+
}
108+
sort.SliceStable(objWithOrderList, func(i, j int) bool {
109+
return objWithOrderList[i].order < objWithOrderList[j].order
110+
})
111+
112+
var sortedObjs []client.Object
113+
for _, objWithOrder := range objWithOrderList {
114+
sortedObjs = append(sortedObjs, objWithOrder.obj)
115+
}
116+
return sortedObjs, nil
117+
}
118+
119+
func (s *k8sResourceStack) waitUntilResourceDeleted(ctx context.Context, obj client.Object) error {
120+
observedObj := obj.DeepCopyObject().(client.Object)
121+
return wait.PollImmediateUntil(utils.PollIntervalShort, func() (bool, error) {
122+
if err := s.tf.K8sClient.Get(ctx, k8s.NamespacedName(obj), observedObj); err != nil {
123+
if apierrs.IsNotFound(err) {
124+
return true, nil
125+
}
126+
return false, err
127+
}
128+
return false, nil
129+
}, ctx.Done())
130+
}

test/framework/framework.go

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
k8sresources "sigs.k8s.io/aws-load-balancer-controller/test/framework/resources/k8s"
1616
"sigs.k8s.io/aws-load-balancer-controller/test/framework/utils"
1717
ctrl "sigs.k8s.io/controller-runtime"
18-
"sigs.k8s.io/controller-runtime/pkg/cache"
1918
"sigs.k8s.io/controller-runtime/pkg/client"
2019
)
2120

@@ -35,7 +34,7 @@ type Framework struct {
3534

3635
HTTPVerifier http.Verifier
3736

38-
Logger logr.Logger
37+
Logger utils.GinkgoLogger
3938
}
4039

4140
func InitFramework() (*Framework, error) {
@@ -49,24 +48,7 @@ func InitFramework() (*Framework, error) {
4948
clientgoscheme.AddToScheme(k8sSchema)
5049
elbv2api.AddToScheme(k8sSchema)
5150

52-
ctx := ctrl.SetupSignalHandler()
53-
cache, err := cache.New(restCfg, cache.Options{Scheme: k8sSchema})
54-
if err != nil {
55-
return nil, err
56-
}
57-
go func() {
58-
cache.Start(ctx)
59-
}()
60-
cache.WaitForCacheSync(ctx)
61-
realClient, err := client.New(restCfg, client.Options{Scheme: k8sSchema})
62-
if err != nil {
63-
return nil, err
64-
}
65-
66-
k8sClient, err := client.NewDelegatingClient(client.NewDelegatingClientInput{
67-
CacheReader: cache,
68-
Client: realClient,
69-
})
51+
k8sClient, err := client.New(restCfg, client.Options{Scheme: k8sSchema})
7052
if err != nil {
7153
return nil, err
7254
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package manifest
2+
3+
import (
4+
"fmt"
5+
"github.com/aws/aws-sdk-go/aws"
6+
appsv1 "k8s.io/api/apps/v1"
7+
corev1 "k8s.io/api/core/v1"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/apimachinery/pkg/util/intstr"
10+
)
11+
12+
// NewFixedResponseServiceBuilder constructs a builder that capable to build manifest for an HTTP service with fixed response.
13+
func NewFixedResponseServiceBuilder() *fixedResponseServiceBuilder {
14+
return &fixedResponseServiceBuilder{
15+
replicas: 1,
16+
httpBody: "Hello World!",
17+
port: 80,
18+
targetPort: 8080,
19+
svcType: corev1.ServiceTypeNodePort,
20+
svcAnnotations: nil,
21+
}
22+
}
23+
24+
type fixedResponseServiceBuilder struct {
25+
replicas int32
26+
httpBody string
27+
port int32
28+
targetPort int32
29+
svcType corev1.ServiceType
30+
svcAnnotations map[string]string
31+
}
32+
33+
func (b *fixedResponseServiceBuilder) WithReplicas(replicas int32) *fixedResponseServiceBuilder {
34+
b.replicas = replicas
35+
return b
36+
}
37+
38+
func (b *fixedResponseServiceBuilder) WithHTTPBody(httpBody string) *fixedResponseServiceBuilder {
39+
b.httpBody = httpBody
40+
return b
41+
}
42+
43+
func (b *fixedResponseServiceBuilder) WithPort(port int32) *fixedResponseServiceBuilder {
44+
b.port = port
45+
return b
46+
}
47+
48+
func (b *fixedResponseServiceBuilder) WithTargetPort(targetPort int32) *fixedResponseServiceBuilder {
49+
b.targetPort = targetPort
50+
return b
51+
}
52+
53+
func (b *fixedResponseServiceBuilder) WithServiceType(svcType corev1.ServiceType) *fixedResponseServiceBuilder {
54+
b.svcType = svcType
55+
return b
56+
}
57+
58+
func (b *fixedResponseServiceBuilder) WithServiceAnnotations(svcAnnotations map[string]string) *fixedResponseServiceBuilder {
59+
b.svcAnnotations = svcAnnotations
60+
return b
61+
}
62+
63+
func (b *fixedResponseServiceBuilder) Build(namespace string, name string) (*appsv1.Deployment, *corev1.Service) {
64+
dp := b.buildDeployment(namespace, name)
65+
svc := b.buildService(namespace, name)
66+
return dp, svc
67+
}
68+
69+
// TODO: have a deployment builder that been called by this component :D.
70+
func (b *fixedResponseServiceBuilder) buildDeployment(namespace string, name string) *appsv1.Deployment {
71+
podLabels := b.buildPodLabels(name)
72+
dp := &appsv1.Deployment{
73+
ObjectMeta: metav1.ObjectMeta{
74+
Namespace: namespace,
75+
Name: name,
76+
},
77+
Spec: appsv1.DeploymentSpec{
78+
Selector: &metav1.LabelSelector{
79+
MatchLabels: podLabels,
80+
},
81+
Replicas: aws.Int32(b.replicas),
82+
Template: corev1.PodTemplateSpec{
83+
ObjectMeta: metav1.ObjectMeta{
84+
Labels: podLabels,
85+
},
86+
Spec: corev1.PodSpec{
87+
Containers: []corev1.Container{
88+
{
89+
Name: "app",
90+
Image: "970805265562.dkr.ecr.us-west-2.amazonaws.com/colorteller:latest",
91+
Ports: []corev1.ContainerPort{
92+
{
93+
ContainerPort: b.targetPort,
94+
},
95+
},
96+
Env: []corev1.EnvVar{
97+
{
98+
Name: "SERVER_PORT",
99+
Value: fmt.Sprintf("%d", b.targetPort),
100+
},
101+
{
102+
Name: "COLOR",
103+
Value: b.httpBody,
104+
},
105+
},
106+
},
107+
},
108+
},
109+
},
110+
},
111+
}
112+
return dp
113+
}
114+
115+
func (b *fixedResponseServiceBuilder) buildService(namespace string, name string) *corev1.Service {
116+
podLabels := b.buildPodLabels(name)
117+
svc := &corev1.Service{
118+
ObjectMeta: metav1.ObjectMeta{
119+
Namespace: namespace,
120+
Name: name,
121+
Annotations: b.svcAnnotations,
122+
},
123+
Spec: corev1.ServiceSpec{
124+
Type: b.svcType,
125+
Selector: podLabels,
126+
Ports: []corev1.ServicePort{
127+
{
128+
Port: b.port,
129+
TargetPort: intstr.FromInt(int(b.targetPort)),
130+
Protocol: corev1.ProtocolTCP,
131+
},
132+
},
133+
},
134+
}
135+
return svc
136+
}
137+
138+
func (b *fixedResponseServiceBuilder) buildPodLabels(name string) map[string]string {
139+
return map[string]string{
140+
"app.kubernetes.io/name": name,
141+
}
142+
}

0 commit comments

Comments
 (0)