Skip to content

Commit aa48864

Browse files
committed
e2e tests for NLB instance mode
1 parent bde666a commit aa48864

13 files changed

+1109
-526
lines changed
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package service
2+
3+
import (
4+
"context"
5+
awssdk "github.com/aws/aws-sdk-go/aws"
6+
elbv2sdk "github.com/aws/aws-sdk-go/service/elbv2"
7+
. "github.com/onsi/gomega"
8+
"github.com/pkg/errors"
9+
"sigs.k8s.io/aws-load-balancer-controller/test/framework"
10+
"sigs.k8s.io/aws-load-balancer-controller/test/framework/utils"
11+
"strconv"
12+
)
13+
14+
type TargetGroupHC struct {
15+
Protocol string
16+
Path string
17+
Port string
18+
Interval int64
19+
Timeout int64
20+
HealthyThreshold int64
21+
UnhealthyThreshold int64
22+
}
23+
24+
type LoadBalancerExpectation struct {
25+
Name string
26+
Type string
27+
Scheme string
28+
TargetType string
29+
Listeners map[string]string // listener port, protocol
30+
TargetGroups map[string]string // target group port, protocol
31+
NumTargets int
32+
TargetGroupHC *TargetGroupHC
33+
}
34+
35+
func verifyAWSLoadBalancerResources(ctx context.Context, f *framework.Framework, lbARN string, expected LoadBalancerExpectation) error {
36+
lb, err := f.LBManager.GetLoadBalancerFromARN(ctx, lbARN)
37+
Expect(err).NotTo(HaveOccurred())
38+
err = verifyLoadBalancerName(ctx, f, lb, expected.Name)
39+
Expect(err).NotTo(HaveOccurred())
40+
err = verifyLoadBalancerType(ctx, f, lb, expected.Type, expected.Scheme)
41+
Expect(err).NotTo(HaveOccurred())
42+
err = verifyLoadBalancerListeners(ctx, f, lbARN, expected.Listeners)
43+
Expect(err).NotTo(HaveOccurred())
44+
err = verifyLoadBalancerTargetGroups(ctx, f, lbARN, expected)
45+
Expect(err).NotTo(HaveOccurred())
46+
return nil
47+
}
48+
49+
func verifyLoadBalancerName(_ context.Context, f *framework.Framework, lb *elbv2sdk.LoadBalancer, lbName string) error {
50+
if len(lbName) > 0 {
51+
Expect(awssdk.StringValue(lb.LoadBalancerName)).To(Equal(lbName))
52+
}
53+
return nil
54+
}
55+
56+
func verifyLoadBalancerType(_ context.Context, f *framework.Framework, lb *elbv2sdk.LoadBalancer, lbType, lbScheme string) error {
57+
Expect(awssdk.StringValue(lb.Type)).To(Equal(lbType))
58+
Expect(awssdk.StringValue(lb.Scheme)).To(Equal(lbScheme))
59+
return nil
60+
}
61+
62+
func verifyLoadBalancerAttributes(ctx context.Context, f *framework.Framework, lbARN string, expectedAttrs map[string]string) error {
63+
lbAttrs, err := f.LBManager.GetLoadBalancerAttributes(ctx, lbARN)
64+
Expect(err).NotTo(HaveOccurred())
65+
for _, attr := range lbAttrs {
66+
if val, ok := expectedAttrs[awssdk.StringValue(attr.Key)]; ok && val != awssdk.StringValue(attr.Value) {
67+
return errors.Errorf("Attribute %v, expected %v, actual %v", awssdk.StringValue(attr.Key), val, awssdk.StringValue(attr.Value))
68+
}
69+
}
70+
return nil
71+
}
72+
73+
func verifyLoadBalancerTags(ctx context.Context, f *framework.Framework, lbARN string, expectedTags map[string]string) bool {
74+
lbTags, err := f.LBManager.GetLoadBalancerTags(ctx, lbARN)
75+
Expect(err).NotTo(HaveOccurred())
76+
matchedTags := 0
77+
for _, tag := range lbTags {
78+
if val, ok := expectedTags[awssdk.StringValue(tag.Key)]; ok && val == awssdk.StringValue(tag.Value) {
79+
matchedTags++
80+
}
81+
}
82+
return matchedTags == len(expectedTags)
83+
}
84+
85+
func getLoadBalancerListenerProtocol(ctx context.Context, f *framework.Framework, lbARN string, port string) string {
86+
protocol := ""
87+
listeners, err := f.LBManager.GetLoadBalancerListeners(ctx, lbARN)
88+
Expect(err).ToNot(HaveOccurred())
89+
for _, ls := range listeners {
90+
if strconv.Itoa(int(awssdk.Int64Value(ls.Port))) == port {
91+
protocol = awssdk.StringValue(ls.Protocol)
92+
}
93+
}
94+
return protocol
95+
}
96+
97+
func verifyLoadBalancerListeners(ctx context.Context, f *framework.Framework, lbARN string, listenersMap map[string]string) error {
98+
listeners, err := f.LBManager.GetLoadBalancerListeners(ctx, lbARN)
99+
Expect(err).ToNot(HaveOccurred())
100+
Expect(len(listeners)).To(Equal(len(listenersMap)))
101+
102+
for _, ls := range listeners {
103+
portStr := strconv.Itoa(int(awssdk.Int64Value(ls.Port)))
104+
Expect(listenersMap).Should(HaveKey(portStr))
105+
Expect(awssdk.StringValue(ls.Protocol)).To(Equal(listenersMap[portStr]))
106+
}
107+
return nil
108+
}
109+
110+
func verifyLoadBalancerTargetGroups(ctx context.Context, f *framework.Framework, lbARN string, expected LoadBalancerExpectation) error {
111+
targetGroups, err := f.TGManager.GetTargetGroupsForLoadBalancer(ctx, lbARN)
112+
Expect(err).ToNot(HaveOccurred())
113+
Expect(len(targetGroups)).To(Equal(len(expected.TargetGroups)))
114+
for _, tg := range targetGroups {
115+
Expect(awssdk.StringValue(tg.TargetType)).To(Equal(expected.TargetType))
116+
Expect(awssdk.StringValue(tg.Protocol)).To(Equal(expected.TargetGroups[strconv.Itoa(int(awssdk.Int64Value(tg.Port)))]))
117+
err = verifyTargetGroupHealthCheckConfig(tg, expected.TargetGroupHC)
118+
Expect(err).NotTo(HaveOccurred())
119+
err = verifyTargetGroupNumRegistered(ctx, f, awssdk.StringValue(tg.TargetGroupArn), expected.NumTargets)
120+
Expect(err).NotTo(HaveOccurred())
121+
}
122+
return nil
123+
}
124+
125+
func verifyTargetGroupHealthCheckConfig(tg *elbv2sdk.TargetGroup, hc *TargetGroupHC) error {
126+
if hc != nil {
127+
Expect(awssdk.StringValue(tg.HealthCheckProtocol)).To(Equal(hc.Protocol))
128+
Expect(awssdk.StringValue(tg.HealthCheckPath)).To(Equal(hc.Path))
129+
Expect(awssdk.StringValue(tg.HealthCheckPort)).To(Equal(hc.Port))
130+
Expect(awssdk.Int64Value(tg.HealthCheckIntervalSeconds)).To(Equal(hc.Interval))
131+
Expect(awssdk.Int64Value(tg.HealthCheckTimeoutSeconds)).To(Equal(hc.Timeout))
132+
Expect(awssdk.Int64Value(tg.HealthyThresholdCount)).To(Equal(hc.HealthyThreshold))
133+
Expect(awssdk.Int64Value(tg.UnhealthyThresholdCount)).To(Equal(hc.UnhealthyThreshold))
134+
}
135+
return nil
136+
}
137+
138+
func verifyTargetGroupNumRegistered(ctx context.Context, f *framework.Framework, tgARN string, expectedTargets int) error {
139+
if expectedTargets == 0 {
140+
return nil
141+
}
142+
Eventually(func() bool {
143+
numTargets, err := f.TGManager.GetCurrentTargetCount(ctx, tgARN)
144+
Expect(err).ToNot(HaveOccurred())
145+
return numTargets == expectedTargets
146+
}, utils.PollTimeoutShort, utils.PollIntervalMedium).Should(BeTrue())
147+
return nil
148+
}
149+
150+
func waitUntilTargetsAreHealthy(ctx context.Context, f *framework.Framework, lbARN string, expectedTargetCount int) error {
151+
targetGroups, err := f.TGManager.GetTargetGroupsForLoadBalancer(ctx, lbARN)
152+
Expect(err).ToNot(HaveOccurred())
153+
Expect(len(targetGroups)).To(Not(BeZero()))
154+
// Check the first target group
155+
tgARN := awssdk.StringValue(targetGroups[0].TargetGroupArn)
156+
157+
Eventually(func() (bool, error) {
158+
return f.TGManager.CheckTargetGroupHealthy(ctx, tgARN, expectedTargetCount)
159+
}, utils.PollTimeoutLong, utils.PollIntervalLong).Should(BeTrue())
160+
return nil
161+
}
162+
163+
func getTargetGroupHealthCheckProtocol(ctx context.Context, f *framework.Framework, lbARN string) string {
164+
targetGroups, err := f.TGManager.GetTargetGroupsForLoadBalancer(ctx, lbARN)
165+
Expect(err).ToNot(HaveOccurred())
166+
return awssdk.StringValue(targetGroups[0].HealthCheckProtocol)
167+
}
168+
169+
func verifyTargetGroupAttributes(ctx context.Context, f *framework.Framework, lbARN string, expectedAttributes map[string]string) bool {
170+
targetGroups, err := f.TGManager.GetTargetGroupsForLoadBalancer(ctx, lbARN)
171+
Expect(err).ToNot(HaveOccurred())
172+
Expect(len(targetGroups)).To(Not(BeZero()))
173+
// Check the first target group
174+
tgARN := awssdk.StringValue(targetGroups[0].TargetGroupArn)
175+
tgAttrs, err := f.TGManager.GetTargetGroupAttributes(ctx, tgARN)
176+
Expect(err).NotTo(HaveOccurred())
177+
matchedAttrs := 0
178+
for _, attr := range tgAttrs {
179+
if val, ok := expectedAttributes[awssdk.StringValue(attr.Key)]; ok && val == awssdk.StringValue(attr.Value) {
180+
matchedAttrs++
181+
}
182+
}
183+
return matchedAttrs == len(expectedAttributes)
184+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package service
2+
3+
import (
4+
"context"
5+
appsv1 "k8s.io/api/apps/v1"
6+
corev1 "k8s.io/api/core/v1"
7+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8+
"k8s.io/apimachinery/pkg/util/intstr"
9+
"sigs.k8s.io/aws-load-balancer-controller/test/framework"
10+
)
11+
12+
const (
13+
defaultTestImage = "kishorj/hello-multi:v1"
14+
appContainerPort = 80
15+
defaultNumReplicas = 3
16+
defaultName = "instance-e2e"
17+
)
18+
19+
type NLBInstanceTestStack struct {
20+
resourceStack *resourceStack
21+
}
22+
23+
func (s *NLBInstanceTestStack) Deploy(ctx context.Context, f *framework.Framework, svcAnnotations map[string]string) error {
24+
dp := s.buildDeploymentSpec()
25+
svc := s.buildServiceSpec(ctx, svcAnnotations)
26+
s.resourceStack = NewResourceStack(dp, svc, "service-instance-e2e", false)
27+
28+
return s.resourceStack.Deploy(ctx, f)
29+
}
30+
31+
func (s *NLBInstanceTestStack) UpdateServiceAnnotation(ctx context.Context, f *framework.Framework, svcAnnotations map[string]string) error {
32+
return s.resourceStack.UpdateServiceAnnotations(ctx, f, svcAnnotations)
33+
}
34+
35+
func (s *NLBInstanceTestStack) UpdateServiceTrafficPolicy(ctx context.Context, f *framework.Framework, trafficPolicy corev1.ServiceExternalTrafficPolicyType) error {
36+
return s.resourceStack.UpdateServiceTrafficPolicy(ctx, f, trafficPolicy)
37+
}
38+
39+
func (s *NLBInstanceTestStack) ScaleDeployment(ctx context.Context, f *framework.Framework, numReplicas int32) error {
40+
return s.resourceStack.ScaleDeployment(ctx, f, numReplicas)
41+
}
42+
43+
func (s *NLBInstanceTestStack) Cleanup(ctx context.Context, f *framework.Framework) error {
44+
return s.resourceStack.Cleanup(ctx, f)
45+
}
46+
47+
func (s *NLBInstanceTestStack) GetLoadBalancerIngressHostName() string {
48+
return s.resourceStack.GetLoadBalancerIngressHostname()
49+
}
50+
51+
func (s *NLBInstanceTestStack) buildDeploymentSpec() *appsv1.Deployment {
52+
numReplicas := int32(defaultNumReplicas)
53+
labels := map[string]string{
54+
"app.kubernetes.io/name": "multi-port",
55+
"app.kubernetes.io/instance": defaultName,
56+
}
57+
return &appsv1.Deployment{
58+
ObjectMeta: metav1.ObjectMeta{
59+
Name: defaultName,
60+
},
61+
Spec: appsv1.DeploymentSpec{
62+
Replicas: &numReplicas,
63+
Selector: &metav1.LabelSelector{
64+
MatchLabels: labels,
65+
},
66+
Template: corev1.PodTemplateSpec{
67+
ObjectMeta: metav1.ObjectMeta{
68+
Labels: labels,
69+
},
70+
Spec: corev1.PodSpec{
71+
Containers: []corev1.Container{
72+
{
73+
Name: "app",
74+
ImagePullPolicy: corev1.PullAlways,
75+
Image: defaultTestImage,
76+
Ports: []corev1.ContainerPort{
77+
{
78+
ContainerPort: appContainerPort,
79+
},
80+
},
81+
},
82+
},
83+
},
84+
},
85+
},
86+
}
87+
}
88+
89+
func (s *NLBInstanceTestStack) buildServiceSpec(ctx context.Context, annotations map[string]string) *corev1.Service {
90+
labels := map[string]string{
91+
"app.kubernetes.io/name": "multi-port",
92+
"app.kubernetes.io/instance": defaultName,
93+
}
94+
svc := &corev1.Service{
95+
ObjectMeta: metav1.ObjectMeta{
96+
Name: defaultName,
97+
Annotations: map[string]string{
98+
"service.beta.kubernetes.io/aws-load-balancer-type": "external",
99+
"service.beta.kubernetes.io/aws-load-balancer-target-type": "nlb-instance",
100+
},
101+
},
102+
Spec: corev1.ServiceSpec{
103+
Type: corev1.ServiceTypeLoadBalancer,
104+
Selector: labels,
105+
Ports: []corev1.ServicePort{
106+
{
107+
Port: 80,
108+
TargetPort: intstr.FromInt(80),
109+
Protocol: corev1.ProtocolTCP,
110+
},
111+
},
112+
},
113+
}
114+
// Override annotations based on the argument
115+
for key, value := range annotations {
116+
svc.Annotations[key] = value
117+
}
118+
119+
return svc
120+
}

0 commit comments

Comments
 (0)