Skip to content

Commit 4fad8fe

Browse files
committed
Include example in docs, add additional tests
1 parent acd7f3d commit 4fad8fe

File tree

2 files changed

+310
-1
lines changed

2 files changed

+310
-1
lines changed

docs/guide/ingress/annotations.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,8 @@ Traffic Routing can be controlled with following annotations:
248248
ARN can be used in forward action(both simplified schema and advanced schema), it must be an targetGroup created outside of k8s, typically an targetGroup for legacy application.
249249
!!!note "use ServiceName/ServicePort in forward Action"
250250
ServiceName/ServicePort can be used in forward action(advanced schema only).
251+
!!!note "use ServiceNamespace in forward Action"
252+
ServiceNamespace can be used in combination with ServiceName/ServicePort in forward action to reference a Service in a different namespace than the Ingress resource (advanced schema only).
251253

252254
!!!warning ""
253255
[Auth related annotations](#authentication) on Service object will only be respected if a single TargetGroup in is used.
@@ -257,6 +259,7 @@ Traffic Routing can be controlled with following annotations:
257259
- redirect-to-eks: redirect to an external url
258260
- forward-single-tg: forward to a single targetGroup [**simplified schema**]
259261
- forward-multiple-tg: forward to multiple targetGroups with different weights and stickiness config [**advanced schema**]
262+
- forward-multiple-tg-multiple-ns: forward to multiple targetGroups with services in different namespaces
260263

261264
```yaml
262265
apiVersion: networking.k8s.io/v1
@@ -274,6 +277,8 @@ Traffic Routing can be controlled with following annotations:
274277
{"type":"forward","targetGroupARN": "arn-of-your-target-group"}
275278
alb.ingress.kubernetes.io/actions.forward-multiple-tg: >
276279
{"type":"forward","forwardConfig":{"targetGroups":[{"serviceName":"service-1","servicePort":"http","weight":20},{"serviceName":"service-2","servicePort":80,"weight":20},{"targetGroupARN":"arn-of-your-non-k8s-target-group","weight":60}],"targetGroupStickinessConfig":{"enabled":true,"durationSeconds":200}}}
280+
alb.ingress.kubernetes.io/actions.forward-multiple-tg-multiple-ns: >
281+
{"type":"forward","forwardConfig":{"targetGroups":[{"serviceName":"service-1","servicePort":"http","weight":20},{"serviceName":"service-2","servicePort":80,"serviceNamespace": "other-namespace", "weight":20}]}}
277282
spec:
278283
ingressClassName: alb
279284
rules:
@@ -307,6 +312,13 @@ Traffic Routing can be controlled with following annotations:
307312
name: forward-multiple-tg
308313
port:
309314
name: use-annotation
315+
- path: /path3
316+
pathType: Exact
317+
backend:
318+
service:
319+
name: forward-multiple-tg-multiple-ns
320+
port:
321+
name: use-annotation
310322
```
311323

312324
- <a name="conditions">`alb.ingress.kubernetes.io/conditions.${conditions-name}`</a> Provides a method for specifying routing conditions **in addition to original host/path condition on Ingress spec**.

pkg/ingress/model_build_actions_test.go

Lines changed: 298 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,315 @@ package ingress
22

33
import (
44
"context"
5+
"sort"
6+
"testing"
7+
58
awssdk "github.com/aws/aws-sdk-go-v2/aws"
69
"github.com/pkg/errors"
710
"github.com/stretchr/testify/assert"
811
corev1 "k8s.io/api/core/v1"
12+
networking "k8s.io/api/networking/v1"
913
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1014
"k8s.io/apimachinery/pkg/runtime"
15+
"k8s.io/apimachinery/pkg/types"
16+
"k8s.io/apimachinery/pkg/util/intstr"
1117
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
18+
"sigs.k8s.io/aws-load-balancer-controller/pkg/annotations"
19+
"sigs.k8s.io/aws-load-balancer-controller/pkg/model/core"
1220
elbv2model "sigs.k8s.io/aws-load-balancer-controller/pkg/model/elbv2"
1321
testclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
14-
"testing"
1522
)
1623

24+
func Test_defaultModelBuildTask_buildForwardAction(t *testing.T) {
25+
ing := ClassifiedIngress{
26+
Ing: &networking.Ingress{
27+
ObjectMeta: metav1.ObjectMeta{
28+
Namespace: "awesome-ns",
29+
Name: "ing-1",
30+
Annotations: map[string]string{
31+
"alb.ingress.kubernetes.io/target-type": "ip",
32+
},
33+
},
34+
},
35+
}
36+
portHTTP := intstr.FromString("http")
37+
svcSpec := corev1.ServiceSpec{
38+
Ports: []corev1.ServicePort{
39+
{
40+
Name: "http",
41+
Port: 80,
42+
},
43+
},
44+
}
45+
svc1 := &corev1.Service{
46+
ObjectMeta: metav1.ObjectMeta{
47+
Namespace: "awesome-ns",
48+
Name: "svc-1",
49+
Annotations: map[string]string{},
50+
},
51+
Spec: svcSpec,
52+
}
53+
svc2 := &corev1.Service{
54+
ObjectMeta: metav1.ObjectMeta{
55+
Namespace: "awesome-ns",
56+
Name: "svc-2",
57+
Annotations: map[string]string{},
58+
},
59+
Spec: svcSpec,
60+
}
61+
svc3 := &corev1.Service{
62+
ObjectMeta: metav1.ObjectMeta{
63+
Namespace: "awesome-ns-2",
64+
Name: "svc-3",
65+
Annotations: map[string]string{},
66+
},
67+
Spec: svcSpec,
68+
}
69+
70+
type args struct {
71+
ing ClassifiedIngress
72+
actionCfg Action
73+
}
74+
type env struct {
75+
services []*corev1.Service
76+
}
77+
type want struct {
78+
targetGroupARN core.StringToken
79+
service *corev1.Service
80+
weight *int32
81+
}
82+
83+
tests := []struct {
84+
name string
85+
env env
86+
args args
87+
wants []want
88+
wantErr error
89+
}{
90+
{
91+
name: "missing ForwardConfig",
92+
args: args{
93+
actionCfg: Action{},
94+
},
95+
wantErr: errors.New("missing ForwardConfig"),
96+
},
97+
{
98+
name: "single TargetGroup with TargetGroupARN",
99+
args: args{
100+
actionCfg: Action{
101+
ForwardConfig: &ForwardActionConfig{
102+
TargetGroups: []TargetGroupTuple{
103+
{
104+
TargetGroupARN: awssdk.String("arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-target-group"),
105+
},
106+
},
107+
},
108+
},
109+
},
110+
wants: []want{
111+
{targetGroupARN: core.LiteralStringToken("arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-target-group")},
112+
},
113+
},
114+
{
115+
name: "single TargetGroup with service",
116+
env: env{
117+
services: []*corev1.Service{svc1},
118+
},
119+
args: args{
120+
ing: ing,
121+
actionCfg: Action{
122+
ForwardConfig: &ForwardActionConfig{
123+
TargetGroups: []TargetGroupTuple{
124+
{
125+
ServiceName: awssdk.String("svc-1"),
126+
ServicePort: &portHTTP,
127+
},
128+
},
129+
},
130+
},
131+
},
132+
wants: []want{
133+
{service: svc1},
134+
},
135+
},
136+
{
137+
name: "multiple TargetGroups with services",
138+
env: env{
139+
services: []*corev1.Service{svc1, svc2},
140+
},
141+
args: args{
142+
ing: ing,
143+
actionCfg: Action{
144+
ForwardConfig: &ForwardActionConfig{
145+
TargetGroups: []TargetGroupTuple{
146+
{
147+
ServiceName: awssdk.String("svc-1"),
148+
ServicePort: &portHTTP,
149+
Weight: awssdk.Int32(80),
150+
},
151+
{
152+
ServiceName: awssdk.String("svc-2"),
153+
ServicePort: &portHTTP,
154+
Weight: awssdk.Int32(20),
155+
},
156+
},
157+
},
158+
},
159+
},
160+
wants: []want{
161+
{service: svc1, weight: awssdk.Int32(80)},
162+
{service: svc2, weight: awssdk.Int32(20)},
163+
},
164+
},
165+
{
166+
name: "multiple TargetGroups with mix of TargetGroupARN and service",
167+
env: env{
168+
services: []*corev1.Service{svc1},
169+
},
170+
args: args{
171+
ing: ing,
172+
actionCfg: Action{
173+
ForwardConfig: &ForwardActionConfig{
174+
TargetGroups: []TargetGroupTuple{
175+
{
176+
TargetGroupARN: awssdk.String("arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-target-group"),
177+
Weight: awssdk.Int32(80),
178+
},
179+
{
180+
ServiceName: awssdk.String("svc-1"),
181+
ServicePort: &portHTTP,
182+
Weight: awssdk.Int32(20),
183+
},
184+
},
185+
},
186+
},
187+
},
188+
wants: []want{
189+
{
190+
targetGroupARN: core.LiteralStringToken("arn:aws:elasticloadbalancing:us-east-2:123456789012:targetgroup/my-target-group"),
191+
weight: awssdk.Int32((80)),
192+
},
193+
{
194+
service: svc1,
195+
weight: awssdk.Int32(20),
196+
},
197+
},
198+
},
199+
{
200+
name: "multiple TargetGroups without weight",
201+
env: env{
202+
services: []*corev1.Service{svc1, svc2},
203+
},
204+
args: args{
205+
ing: ing,
206+
actionCfg: Action{
207+
ForwardConfig: &ForwardActionConfig{
208+
TargetGroups: []TargetGroupTuple{
209+
{
210+
ServiceName: awssdk.String("svc-1"),
211+
ServicePort: &portHTTP,
212+
},
213+
{
214+
ServiceName: awssdk.String("svc-2"),
215+
ServicePort: &portHTTP,
216+
},
217+
},
218+
},
219+
},
220+
},
221+
wants: []want{
222+
{service: svc1},
223+
{service: svc2},
224+
},
225+
},
226+
{
227+
name: "multiple TargetGroups with services in separate namespaces",
228+
env: env{
229+
services: []*corev1.Service{svc1, svc3},
230+
},
231+
args: args{
232+
ing: ing,
233+
actionCfg: Action{
234+
ForwardConfig: &ForwardActionConfig{
235+
TargetGroups: []TargetGroupTuple{
236+
{
237+
ServiceName: awssdk.String("svc-1"),
238+
ServicePort: &portHTTP,
239+
Weight: awssdk.Int32(80),
240+
},
241+
{
242+
ServiceNamespace: awssdk.String("awesome-ns-2"),
243+
ServiceName: awssdk.String("svc-3"),
244+
ServicePort: &portHTTP,
245+
Weight: awssdk.Int32(20),
246+
},
247+
},
248+
},
249+
},
250+
},
251+
wants: []want{
252+
{service: svc1, weight: awssdk.Int32(80)},
253+
{service: svc3, weight: awssdk.Int32(20)},
254+
},
255+
},
256+
}
257+
for _, tt := range tests {
258+
t.Run(tt.name, func(t *testing.T) {
259+
k8sSchema := runtime.NewScheme()
260+
clientgoscheme.AddToScheme(k8sSchema)
261+
k8sClient := testclient.NewClientBuilder().WithScheme(k8sSchema).Build()
262+
backendServices := map[types.NamespacedName]*corev1.Service{}
263+
for _, service := range tt.env.services {
264+
err := k8sClient.Create(context.Background(), service.DeepCopy())
265+
assert.NoError(t, err)
266+
key := types.NamespacedName{Namespace: service.ObjectMeta.Namespace, Name: service.ObjectMeta.Name}
267+
backendServices[key] = service
268+
}
269+
stack := core.NewDefaultStack(core.StackID(types.NamespacedName{Namespace: "awesome-ns", Name: "ing-1"}))
270+
task := &defaultModelBuildTask{
271+
k8sClient: k8sClient,
272+
annotationParser: annotations.NewSuffixAnnotationParser("alb.ingress.kubernetes.io"),
273+
backendServices: backendServices,
274+
enableIPTargetType: true,
275+
defaultBackendProtocol: elbv2model.ProtocolHTTP,
276+
defaultBackendProtocolVersion: elbv2model.ProtocolVersionHTTP1,
277+
stack: stack,
278+
tgByResID: make(map[string]*elbv2model.TargetGroup),
279+
}
280+
got, err := task.buildForwardAction(context.Background(), tt.args.ing, tt.args.actionCfg)
281+
if tt.wantErr != nil {
282+
assert.EqualError(t, err, tt.wantErr.Error())
283+
} else {
284+
assert.NoError(t, err)
285+
assert.Equal(t, elbv2model.ActionTypeForward, got.Type)
286+
assert.NotNil(t, got.ForwardConfig)
287+
288+
var gotTargetGroupBindings []*elbv2model.TargetGroupBindingResource
289+
err = stack.ListResources(&gotTargetGroupBindings)
290+
assert.NoError(t, err)
291+
sort.Slice(gotTargetGroupBindings, func(i, j int) bool {
292+
return gotTargetGroupBindings[i].ID() < gotTargetGroupBindings[j].ID()
293+
})
294+
295+
tgbIndex := 0
296+
for i, want := range tt.wants {
297+
if want.targetGroupARN != nil {
298+
assert.Equal(t, want.targetGroupARN, got.ForwardConfig.TargetGroups[i].TargetGroupARN)
299+
}
300+
if want.service != nil {
301+
tgb := gotTargetGroupBindings[tgbIndex]
302+
assert.Equal(t, want.service.ObjectMeta.Namespace, tgb.Spec.Template.ObjectMeta.Namespace)
303+
assert.Equal(t, want.service.ObjectMeta.Name, tgb.Spec.Template.Spec.ServiceRef.Name)
304+
assert.Equal(t, intstr.FromString(want.service.Spec.Ports[0].Name), tgb.Spec.Template.Spec.ServiceRef.Port)
305+
assert.Equal(t, want.weight, got.ForwardConfig.TargetGroups[i].Weight)
306+
tgbIndex += 1
307+
}
308+
}
309+
}
310+
})
311+
}
312+
}
313+
17314
func Test_defaultModelBuildTask_buildAuthenticateOIDCAction(t *testing.T) {
18315
type env struct {
19316
secrets []*corev1.Secret

0 commit comments

Comments
 (0)