@@ -6,24 +6,26 @@ import (
6
6
"github.com/pkg/errors"
7
7
corev1 "k8s.io/api/core/v1"
8
8
"k8s.io/apimachinery/pkg/types"
9
+ elbv2gw "sigs.k8s.io/aws-load-balancer-controller/apis/gateway/v1beta1"
10
+ "sigs.k8s.io/aws-load-balancer-controller/pkg/k8s"
9
11
"sigs.k8s.io/controller-runtime/pkg/client"
10
12
gwv1 "sigs.k8s.io/gateway-api/apis/v1"
13
+ gwbeta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
14
+ )
15
+
16
+ const (
17
+ serviceKind = "Service"
11
18
)
12
19
13
20
// Backend an abstraction on the Gateway Backend, meant to hide the underlying backend type from consumers (unless they really want to see it :))
14
21
type Backend struct {
15
- Service * corev1.Service
16
- ServicePort * corev1. ServicePort
17
- TypeSpecificBackend interface {}
18
- Weight int
19
- // Add TG config here //
22
+ Service * corev1.Service
23
+ ELBv2TargetGroupConfig * elbv2gw. TargetGroupConfiguration
24
+ ServicePort * corev1. ServicePort
25
+ TypeSpecificBackend interface {}
26
+ Weight int
20
27
}
21
28
22
- // TODOs:
23
- // 1/ Add reference grant checking
24
- // 2/ Add target group configuration resolution
25
-
26
- // NOTE: Currently routeKind is not used, however, we will need it to load TG specific configuration.
27
29
// commonBackendLoader this function will load the services and target group configurations associated with this gateway backend.
28
30
func commonBackendLoader (ctx context.Context , k8sClient client.Client , typeSpecificBackend interface {}, backendRef gwv1.BackendRef , routeIdentifier types.NamespacedName , routeKind string ) (* Backend , error ) {
29
31
@@ -40,23 +42,37 @@ func commonBackendLoader(ctx context.Context, k8sClient client.Client, typeSpeci
40
42
return nil , errors .Errorf ("Missing port in backend reference" )
41
43
}
42
44
43
- var namespace string
45
+ var svcNamespace string
44
46
if backendRef .Namespace == nil {
45
- namespace = routeIdentifier .Namespace
47
+ svcNamespace = routeIdentifier .Namespace
46
48
} else {
47
- namespace = string (* backendRef .Namespace )
49
+ svcNamespace = string (* backendRef .Namespace )
48
50
}
49
51
50
- // TODO - Need to implement reference grant check here
51
-
52
- svcName := types.NamespacedName {
53
- Namespace : namespace ,
52
+ svcIdentifier := types.NamespacedName {
53
+ Namespace : svcNamespace ,
54
54
Name : string (backendRef .Name ),
55
55
}
56
+
57
+ // Check for reference grant when performing crossname gateway -> route attachment
58
+ if svcNamespace != routeIdentifier .Namespace {
59
+ allowed , err := referenceGrantCheck (ctx , k8sClient , svcIdentifier , routeIdentifier , routeKind )
60
+ if err != nil {
61
+ return nil , errors .Wrapf (err , "Unable to perform reference grant check" )
62
+ }
63
+
64
+ // We should not give any hints about the existence of this resource, therefore, we return nil.
65
+ // That way, users can't infer if the route is missing because of a misconfigured service reference
66
+ // or the sentence grant is not allowing the connection.
67
+ if ! allowed {
68
+ return nil , nil
69
+ }
70
+ }
71
+
56
72
svc := & corev1.Service {}
57
- err := k8sClient .Get (ctx , svcName , svc )
73
+ err := k8sClient .Get (ctx , svcIdentifier , svc )
58
74
if err != nil {
59
- return nil , errors .Wrap (err , fmt .Sprintf ("Unable to fetch svc object %+v" , svcName ))
75
+ return nil , errors .Wrap (err , fmt .Sprintf ("Unable to fetch svc object %+v" , svcIdentifier ))
60
76
}
61
77
62
78
var servicePort * corev1.ServicePort
@@ -68,12 +84,16 @@ func commonBackendLoader(ctx context.Context, k8sClient client.Client, typeSpeci
68
84
}
69
85
}
70
86
87
+ tgConfig , err := lookUpTargetGroupConfiguration (ctx , k8sClient , k8s .NamespacedName (svc ))
88
+
89
+ if err != nil {
90
+ return nil , errors .Wrap (err , fmt .Sprintf ("Unable to fetch tg config object" ))
91
+ }
92
+
71
93
if servicePort == nil {
72
94
return nil , errors .Errorf ("Unable to find service port for port %d" , * backendRef .Port )
73
95
}
74
96
75
- // TODO - Need to TG CRD look up here
76
-
77
97
// Weight specifies the proportion of requests forwarded to the referenced
78
98
// backend. This is computed as weight/(sum of all weights in this
79
99
// BackendRefs list). For non-zero values, there may be some epsilon from
@@ -90,9 +110,74 @@ func commonBackendLoader(ctx context.Context, k8sClient client.Client, typeSpeci
90
110
weight = int (* backendRef .Weight )
91
111
}
92
112
return & Backend {
93
- Service : svc ,
94
- ServicePort : servicePort ,
95
- Weight : weight ,
96
- TypeSpecificBackend : typeSpecificBackend ,
113
+ Service : svc ,
114
+ ServicePort : servicePort ,
115
+ Weight : weight ,
116
+ TypeSpecificBackend : typeSpecificBackend ,
117
+ ELBv2TargetGroupConfig : tgConfig ,
97
118
}, nil
98
119
}
120
+
121
+ // lookUpTargetGroupConfiguration given a service, lookup the target group configuration associated with the service.
122
+ // recall that target group configuration always lives within the same namespace as the service.
123
+ func lookUpTargetGroupConfiguration (ctx context.Context , k8sClient client.Client , serviceMetadata types.NamespacedName ) (* elbv2gw.TargetGroupConfiguration , error ) {
124
+ tgConfigList := & elbv2gw.TargetGroupConfigurationList {}
125
+
126
+ // TODO - Add index
127
+ if err := k8sClient .List (ctx , tgConfigList , client .InNamespace (serviceMetadata .Namespace )); err != nil {
128
+ return nil , err
129
+ }
130
+
131
+ for _ , tgConfig := range tgConfigList .Items {
132
+ if tgConfig .Spec .TargetReference .Kind != nil && * tgConfig .Spec .TargetReference .Kind != serviceKind {
133
+ continue
134
+ }
135
+
136
+ // TODO - Add a webhook to validate that only one target group config references this service.
137
+ // TODO - Add an index for this
138
+ if tgConfig .Spec .TargetReference .Name == serviceMetadata .Name {
139
+ return & tgConfig , nil
140
+ }
141
+ }
142
+ return nil , nil
143
+ }
144
+
145
+ // Implements the reference grant API
146
+ // https://gateway-api.sigs.k8s.io/api-types/referencegrant/
147
+ func referenceGrantCheck (ctx context.Context , k8sClient client.Client , svcIdentifier types.NamespacedName , routeIdentifier types.NamespacedName , routeKind string ) (bool , error ) {
148
+ referenceGrantList := & gwbeta1.ReferenceGrantList {}
149
+ if err := k8sClient .List (ctx , referenceGrantList , client .InNamespace (svcIdentifier .Namespace )); err != nil {
150
+ return false , err
151
+ }
152
+
153
+ for _ , grant := range referenceGrantList .Items {
154
+ var routeAllowed bool
155
+
156
+ for _ , from := range grant .Spec .From {
157
+ // Kind check maybe?
158
+ if string (from .Kind ) == routeKind && string (from .Namespace ) == routeIdentifier .Namespace {
159
+ routeAllowed = true
160
+ break
161
+ }
162
+ }
163
+
164
+ if routeAllowed {
165
+ for _ , to := range grant .Spec .To {
166
+ // As this is a backend reference, we only care about the "Service" Kind.
167
+ if to .Kind != serviceKind {
168
+ continue
169
+ }
170
+
171
+ // If name is specified, we need to ensure that svc name matches the "to" name.
172
+ if to .Name != nil && string (* to .Name ) != svcIdentifier .Name {
173
+ continue
174
+ }
175
+
176
+ return true , nil
177
+ }
178
+
179
+ }
180
+ }
181
+
182
+ return false , nil
183
+ }
0 commit comments