Skip to content

Commit c7933eb

Browse files
authored
fix: target group verification did not work with named ports (argoproj#1485)
fix: target group verification did not work with named ports (argoproj#1485) Signed-off-by: Jesse Suen <[email protected]>
1 parent 2d3cd2a commit c7933eb

File tree

4 files changed

+124
-24
lines changed

4 files changed

+124
-24
lines changed

Dockerfile.dev

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
####################################################################################################
22
# argo-rollouts-dev
33
####################################################################################################
4-
FROM scratch
4+
FROM golang:1.16.3 as builder
5+
6+
RUN apt-get update && apt-get install -y \
7+
ca-certificates && \
8+
apt-get clean && \
9+
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
10+
11+
FROM scratch
12+
13+
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
514
COPY rollouts-controller-linux-amd64 /bin/rollouts-controller
15+
16+
# Use numeric user, allows kubernetes to identify this user as being
17+
# non-root when we use a security context with runAsNonRoot: true
18+
USER 999
19+
620
ENTRYPOINT [ "/bin/rollouts-controller" ]

docs/features/traffic-management/alb.md

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ the changes made to the Ingress object are reflected in the underlying AWS Targe
172172
Target Group IP verification available since Argo Rollouts v1.1
173173

174174
The AWS LoadBalancer controller can run in one of two modes:
175+
175176
* [Instance mode](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.2/how-it-works/#instance-mode)
176177
* [IP mode](https://kubernetes-sigs.github.io/aws-load-balancer-controller/v2.2/how-it-works/#ip-mode)
177178

@@ -277,12 +278,26 @@ spec:
277278
For this feature to work, the argo-rollouts deployment requires the following AWS API permissions
278279
under the [Elastic Load Balancing API](https://docs.aws.amazon.com/elasticloadbalancing/latest/APIReference/Welcome.html):
279280

280-
* `DescribeTargetGroups`
281-
* `DescribeLoadBalancers`
282-
* `DescribeListeners`
283-
* `DescribeRules`
284-
* `DescribeTags`
285-
* `DescribeTargetHealth`
281+
282+
```json
283+
{
284+
"Version": "2012-10-17",
285+
"Statement": [
286+
{
287+
"Action": [
288+
"elasticloadbalancing:DescribeTargetGroups",
289+
"elasticloadbalancing:DescribeLoadBalancers",
290+
"elasticloadbalancing:DescribeListeners",
291+
"elasticloadbalancing:DescribeRules",
292+
"elasticloadbalancing:DescribeTags",
293+
"elasticloadbalancing:DescribeTargetHealth"
294+
],
295+
"Resource": "*",
296+
"Effect": "Allow"
297+
}
298+
]
299+
}
300+
```
286301

287302
There are various ways of granting AWS privileges to the argo-rollouts pods, which is highly
288303
dependent to your cluster's AWS environment, and out-of-scope of this documentation. Some solutions

utils/aws/aws.go

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"strings"
78

89
"github.com/argoproj/argo-rollouts/utils/defaults"
910
"github.com/aws/aws-sdk-go-v2/config"
@@ -295,17 +296,32 @@ func toTargetGroupBinding(obj map[string]interface{}) (*TargetGroupBinding, erro
295296
return &tgb, nil
296297
}
297298

298-
// getNumericPort resolves the numeric port which a AWS TargetGroup targets.
299+
// getNumericTargetPort resolves the numeric port which a AWS TargetGroup targets.
299300
// This is needed in case the TargetGroupBinding's spec.serviceRef.Port is a string and not a number
301+
// and/or the Service's targetPort is a string and not a number
300302
// Returns 0 if unable to find matching port in given service.
301-
func getNumericPort(tgb TargetGroupBinding, svc corev1.Service) int32 {
302-
if portInt := tgb.Spec.ServiceRef.Port.IntValue(); portInt > 0 {
303-
return int32(portInt)
303+
func getNumericTargetPort(tgb TargetGroupBinding, svc corev1.Service, endpoints corev1.Endpoints) int32 {
304+
var servicePortNum int32
305+
var servicePortName string
306+
if portInt := tgb.Spec.ServiceRef.Port.IntVal; portInt > 0 {
307+
servicePortNum = portInt
308+
} else {
309+
servicePortName = tgb.Spec.ServiceRef.Port.String()
304310
}
305-
// port is a string and not a num
306311
for _, svcPort := range svc.Spec.Ports {
307-
if tgb.Spec.ServiceRef.Port.StrVal == svcPort.Name {
308-
return svcPort.Port
312+
if (servicePortName != "" && servicePortName == svcPort.Name) || (servicePortNum > 0 && servicePortNum == svcPort.Port) {
313+
if targetPortNum := svcPort.TargetPort.IntVal; targetPortNum > 0 {
314+
return targetPortNum
315+
}
316+
// targetPort is a string and not a num. Must resort to looking at endpoints
317+
targetPortName := svcPort.TargetPort.String()
318+
for _, subset := range endpoints.Subsets {
319+
for _, port := range subset.Ports {
320+
if port.Name == targetPortName {
321+
return port.Port
322+
}
323+
}
324+
}
309325
}
310326
}
311327
return 0
@@ -330,7 +346,7 @@ func VerifyTargetGroupBinding(ctx context.Context, logCtx *log.Entry, awsClnt Cl
330346
// We only need to verify target groups using AWS CNI (spec.targetType: ip)
331347
return nil, nil
332348
}
333-
port := getNumericPort(tgb, *svc)
349+
port := getNumericTargetPort(tgb, *svc, *endpoints)
334350
if port == 0 {
335351
logCtx.Warn("Unable to match TargetGroupBinding spec.serviceRef.port to Service spec.ports")
336352
return nil, nil
@@ -355,6 +371,9 @@ func VerifyTargetGroupBinding(ctx context.Context, logCtx *log.Entry, awsClnt Cl
355371
}
356372

357373
logCtx.Infof("verifying %d endpoint addresses (of %d targets)", len(endpointIPs), len(targets))
374+
var ignored []string
375+
var verified []string
376+
var unverified []string
358377

359378
// Iterate all registered targets in AWS TargetGroup. Mark all endpoint IPs which we see registered
360379
for _, target := range targets {
@@ -365,28 +384,33 @@ func VerifyTargetGroupBinding(ctx context.Context, logCtx *log.Entry, awsClnt Cl
365384
targetStr := fmt.Sprintf("%s:%d", *target.Target.Id, *target.Target.Port)
366385
_, isEndpointTarget := endpointIPs[targetStr]
367386
if !isEndpointTarget {
387+
ignored = append(ignored, targetStr)
368388
// this is a target for something not in the endpoint list (e.g. old endpoint entry). Ignore it
369389
continue
370390
}
371391
// Verify we see the endpoint IP registered to the TargetGroup
372392
// NOTE: we used to check health here, but health is not relevant for verifying service label change
373393
endpointIPs[targetStr] = true
394+
verified = append(verified, targetStr)
374395
}
375396

376397
tgvr := TargetGroupVerifyResult{
377398
Service: svc.Name,
378399
EndpointsTotal: len(endpointIPs),
379-
EndpointsRegistered: 0,
400+
EndpointsRegistered: len(verified),
380401
}
381402

382403
// Check if any of our desired endpoints are not yet registered
383404
for epIP, seen := range endpointIPs {
384405
if !seen {
385-
logCtx.Infof("Service endpoint IP %s not yet registered", epIP)
386-
} else {
387-
tgvr.EndpointsRegistered++
406+
unverified = append(unverified, epIP)
388407
}
389408
}
409+
410+
logCtx.Infof("Ignored targets: %s", strings.Join(ignored, ", "))
411+
logCtx.Infof("Verified targets: %s", strings.Join(verified, ", "))
412+
logCtx.Infof("Unregistered targets: %s", strings.Join(unverified, ", "))
413+
390414
tgvr.Verified = bool(tgvr.EndpointsRegistered == tgvr.EndpointsTotal)
391415
return &tgvr, nil
392416
}

utils/aws/aws_test.go

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,43 @@ func TestFindLoadBalancerByDNSName(t *testing.T) {
5757
}
5858
}
5959

60+
func TestGetNumericTargetPort(t *testing.T) {
61+
tgb := TargetGroupBinding{
62+
Spec: TargetGroupBindingSpec{
63+
ServiceRef: ServiceReference{
64+
Port: intstr.FromString("web"),
65+
},
66+
},
67+
}
68+
svc := corev1.Service{
69+
Spec: corev1.ServiceSpec{
70+
Ports: []corev1.ServicePort{
71+
{
72+
Name: "web",
73+
TargetPort: intstr.FromString("http"),
74+
},
75+
},
76+
},
77+
}
78+
eps := corev1.Endpoints{
79+
Subsets: []corev1.EndpointSubset{
80+
{
81+
Ports: []corev1.EndpointPort{
82+
{
83+
Name: "asdf",
84+
Port: 1234,
85+
},
86+
{
87+
Name: "http",
88+
Port: 4567,
89+
},
90+
},
91+
},
92+
},
93+
}
94+
assert.Equal(t, int32(4567), getNumericTargetPort(tgb, svc, eps))
95+
}
96+
6097
func TestGetTargetGroupMetadata(t *testing.T) {
6198
fakeELB, c := newFakeClient()
6299

@@ -254,6 +291,10 @@ func TestVerifyTargetGroupBinding(t *testing.T) {
254291
Spec: TargetGroupBindingSpec{
255292
TargetType: (*TargetType)(pointer.StringPtr("ip")),
256293
TargetGroupARN: "arn::1234",
294+
ServiceRef: ServiceReference{
295+
Name: "active",
296+
Port: intstr.FromInt(80),
297+
},
257298
},
258299
}
259300
ep := corev1.Endpoints{
@@ -274,6 +315,12 @@ func TestVerifyTargetGroupBinding(t *testing.T) {
274315
IP: "2.4.6.8", // not registered
275316
},
276317
},
318+
Ports: []corev1.EndpointPort{
319+
{
320+
Port: 8080,
321+
Protocol: "TCP",
322+
},
323+
},
277324
},
278325
},
279326
}
@@ -287,7 +334,7 @@ func TestVerifyTargetGroupBinding(t *testing.T) {
287334
Ports: []corev1.ServicePort{{
288335
Protocol: "TCP",
289336
Port: int32(80),
290-
TargetPort: intstr.FromInt(80),
337+
TargetPort: intstr.FromInt(8080),
291338
}},
292339
},
293340
}
@@ -297,25 +344,25 @@ func TestVerifyTargetGroupBinding(t *testing.T) {
297344
{
298345
Target: &elbv2types.TargetDescription{
299346
Id: pointer.StringPtr("1.2.3.4"),
300-
Port: pointer.Int32Ptr(80),
347+
Port: pointer.Int32Ptr(8080),
301348
},
302349
},
303350
{
304351
Target: &elbv2types.TargetDescription{
305352
Id: pointer.StringPtr("5.6.7.8"),
306-
Port: pointer.Int32Ptr(80),
353+
Port: pointer.Int32Ptr(8080),
307354
},
308355
},
309356
{
310357
Target: &elbv2types.TargetDescription{
311358
Id: pointer.StringPtr("2.4.6.8"), // irrelevant
312-
Port: pointer.Int32Ptr(81), // wrong port
359+
Port: pointer.Int32Ptr(8081), // wrong port
313360
},
314361
},
315362
{
316363
Target: &elbv2types.TargetDescription{
317364
Id: pointer.StringPtr("9.8.7.6"), // irrelevant ip
318-
Port: pointer.Int32Ptr(80),
365+
Port: pointer.Int32Ptr(8080),
319366
},
320367
},
321368
},

0 commit comments

Comments
 (0)