Skip to content

Commit f72ec2d

Browse files
authored
feature: forward backend for ingress as annotation zalando.org/skipper-backend (#3665)
feature: [forward backend](https://opensource.zalando.com/skipper/reference/backends/#forward-backend) for ingress as annotation `zalando.org/skipper-backend`. RouteGroup validation makes sure that the backend type is recognized. dataclients/kubernetes/definitions/testdata/validation/test-forward.log is an empty file, because if log file does not exist test would be skipped. Signed-off-by: Sandor Szücs <[email protected]>
1 parent 859fe9b commit f72ec2d

24 files changed

+378
-0
lines changed

dataclients/kubernetes/definitions/ingressv1.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ const (
1010
IngressFilterAnnotation = "zalando.org/skipper-filter"
1111
IngressPredicateAnnotation = "zalando.org/skipper-predicate"
1212
IngressRoutesAnnotation = "zalando.org/skipper-routes"
13+
IngressBackendAnnotation = "zalando.org/skipper-backend"
1314
)
1415

1516
var errInvalidPortType = errors.New("invalid port type")

dataclients/kubernetes/definitions/testdata/validation/test-forward.log

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
apiVersion: zalando.org/v1
2+
kind: RouteGroup
3+
metadata:
4+
name: my-test
5+
spec:
6+
backends:
7+
- name: fwd
8+
type: forward
9+
routes:
10+
- pathSubtree: /
11+
backends:
12+
- backendName: fwd

dataclients/kubernetes/ingress.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ type ingressContext struct {
3535
logger *logger
3636
annotationFilters []*eskip.Filter
3737
annotationPredicate string
38+
annotationBackend string
39+
forwardBackendURL string
3840
extraRoutes []*eskip.Route
3941
backendWeights map[string]float64
4042
pathMode PathMode
@@ -58,6 +60,7 @@ type ingress struct {
5860
forceKubernetesService bool
5961
backendTrafficAlgorithm BackendTrafficAlgorithm
6062
defaultLoadBalancerAlgorithm string
63+
forwardBackendURL string
6164
kubernetesAnnotationPredicates []AnnotationPredicates
6265
kubernetesAnnotationFiltersAppend []AnnotationFilters
6366
kubernetesEastWestRangeAnnotationPredicates []AnnotationPredicates
@@ -69,9 +72,26 @@ var nonWord = regexp.MustCompile(`\W`)
6972
var errNotAllowedExternalName = errors.New("ingress with not allowed external name service")
7073

7174
func (ic *ingressContext) addHostRoute(host string, route *eskip.Route) {
75+
ic.applyBackend(route)
7276
ic.hostRoutes[host] = append(ic.hostRoutes[host], route)
7377
}
7478

79+
func (ic *ingressContext) applyBackend(route *eskip.Route) {
80+
if ic.forwardBackendURL == "" || ic.annotationBackend == "" || route == nil {
81+
return
82+
}
83+
if be, err := eskip.BackendTypeFromString(ic.annotationBackend); err != nil {
84+
return
85+
} else {
86+
switch be {
87+
case eskip.ForwardBackend:
88+
route.BackendType = eskip.NetworkBackend
89+
route.Backend = ic.forwardBackendURL
90+
route.Filters = []*eskip.Filter{}
91+
}
92+
}
93+
}
94+
7595
func newIngress(o Options) *ingress {
7696
return &ingress{
7797
provideHTTPSRedirect: o.ProvideHTTPSRedirect,
@@ -86,6 +106,7 @@ func newIngress(o Options) *ingress {
86106
forceKubernetesService: o.ForceKubernetesService,
87107
backendTrafficAlgorithm: o.BackendTrafficAlgorithm,
88108
defaultLoadBalancerAlgorithm: o.DefaultLoadBalancerAlgorithm,
109+
forwardBackendURL: o.ForwardBackendURL,
89110
kubernetesAnnotationPredicates: o.KubernetesAnnotationPredicates,
90111
kubernetesAnnotationFiltersAppend: o.KubernetesAnnotationFiltersAppend,
91112
kubernetesEastWestRangeAnnotationPredicates: o.KubernetesEastWestRangeAnnotationPredicates,
@@ -262,6 +283,21 @@ func annotationFilter(m *definitions.Metadata, logger *logger) []*eskip.Filter {
262283
return nil
263284
}
264285

286+
// parse backend annotation
287+
func annotationBackend(m *definitions.Metadata) (eskip.BackendType, error) {
288+
if s, ok := m.Annotations[definitions.IngressBackendAnnotation]; ok {
289+
return eskip.BackendTypeFromString(s)
290+
}
291+
return 0, fmt.Errorf("annotation not found")
292+
}
293+
294+
func annotationBackendString(m *definitions.Metadata) string {
295+
if be, err := annotationBackend(m); err == nil {
296+
return be.String()
297+
}
298+
return ""
299+
}
300+
265301
// parse predicate annotation
266302
func annotationPredicate(m *definitions.Metadata) string {
267303
var annotationPredicate string
@@ -350,6 +386,22 @@ func hasCatchAllRoutes(routes []*eskip.Route) bool {
350386
return false
351387
}
352388

389+
func (ing *ingress) applyBackend(i *definitions.IngressV1Item, r *eskip.Route) {
390+
if ing.forwardBackendURL == "" || r == nil {
391+
return
392+
}
393+
if be, err := annotationBackend(i.Metadata); err != nil {
394+
return
395+
} else {
396+
switch be {
397+
case eskip.ForwardBackend:
398+
r.BackendType = eskip.NetworkBackend
399+
r.Backend = ing.forwardBackendURL
400+
r.Filters = []*eskip.Filter{}
401+
}
402+
}
403+
}
404+
353405
// convert logs if an invalid found, but proceeds with the valid ones.
354406
// Reporting failures in Ingress status is not possible, because
355407
// Ingress status field only supports IP and Hostname as string.
@@ -372,6 +424,7 @@ func (ing *ingress) convert(state *clusterState, df defaultFilters, r *certregis
372424
ewIngInfo[r.Id] = []string{i.Metadata.Namespace, i.Metadata.Name}
373425
}
374426
}
427+
ing.applyBackend(i, r)
375428
}
376429

377430
for host, rs := range hostRoutes {

dataclients/kubernetes/ingress_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ func TestIngressV1AnnotationConfig(t *testing.T) {
2727
kubernetestest.FixturesToTest(t,
2828
"testdata/ingressV1/annotation-predicates",
2929
"testdata/ingressV1/annotation-filters",
30+
"testdata/ingressV1/annotation-backends",
3031
)
3132
}

dataclients/kubernetes/ingressv1.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,8 @@ func (ing *ingress) ingressV1Route(
433433
logger: logger,
434434
annotationFilters: annotationFilter(i.Metadata, logger),
435435
annotationPredicate: annotationPredicate(i.Metadata),
436+
annotationBackend: annotationBackendString(i.Metadata),
437+
forwardBackendURL: ing.forwardBackendURL,
436438
extraRoutes: extraRoutes(i.Metadata),
437439
backendWeights: backendWeights(i.Metadata, logger),
438440
pathMode: pathMode(i.Metadata, ing.pathMode, logger),
@@ -446,9 +448,11 @@ func (ing *ingress) ingressV1Route(
446448
var route *eskip.Route
447449
if r, ok, err := ing.convertDefaultBackendV1(ic, ing.forceKubernetesService); ok {
448450
route = r
451+
ic.applyBackend(route)
449452
} else if err != nil {
450453
ic.logger.Errorf("Failed to convert default backend: %v", err)
451454
}
455+
452456
for _, rule := range i.Spec.Rules {
453457
err := ing.addSpecRuleV1(ic, rule)
454458
if err != nil {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kube_default__myapp__example_test____myapp:
2+
Host("^(example[.]test[.]?(:[0-9]+)?)$")
3+
-> "http://forward.example";
4+
5+
kube_default__myapp__0__example_test____: Host("^(example[.]test[.]?(:[0-9]+)?)$") && Method("OPTIONS") -> <shunt>;
6+
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
apiVersion: networking.k8s.io/v1
2+
kind: Ingress
3+
metadata:
4+
name: myapp
5+
namespace: default
6+
annotations:
7+
zalando.org/skipper-backend: forward
8+
zalando.org/skipper-routes: Method("OPTIONS") -> status(200) -> <shunt>
9+
spec:
10+
rules:
11+
- host: example.test
12+
http:
13+
paths:
14+
- backend:
15+
service:
16+
name: myapp
17+
port:
18+
number: 80
19+
pathType: ImplementationSpecific
20+
---
21+
apiVersion: v1
22+
kind: Service
23+
metadata:
24+
labels:
25+
application: myapp
26+
name: myapp
27+
spec:
28+
clusterIP: 10.3.190.97
29+
ports:
30+
- name: main
31+
port: 80
32+
protocol: TCP
33+
targetPort: 7272
34+
selector:
35+
application: myapp
36+
type: ClusterIP
37+
---
38+
apiVersion: v1
39+
kind: Endpoints
40+
metadata:
41+
labels:
42+
application: myapp
43+
name: myapp
44+
namespace: default
45+
subsets:
46+
- addresses:
47+
- ip: 10.2.9.103
48+
- ip: 10.2.9.104
49+
ports:
50+
- name: main
51+
port: 7272
52+
protocol: TCP
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
kube_default__myapp__example_test____myapp:
2+
Host("^(example[.]test[.]?(:[0-9]+)?)$")
3+
-> "http://forward.example";

0 commit comments

Comments
 (0)