Skip to content

Commit 3f89fd8

Browse files
zijun726911Zijun Wang
andauthored
Add mapper.policyToTargetRefObj() helper function (#398)
* - Add resourceMapper.VpcAssociationPolicyToGateway() method - Add vpcAssociationPolicyEventHandler.MapToGateway() method - Make gateway_controller optionally watches the `VpcAssociationPolicy` * address comments * address PR comments --------- Co-authored-by: Zijun Wang <zijunw@amazon.com>
1 parent 2385943 commit 3f89fd8

File tree

7 files changed

+265
-59
lines changed

7 files changed

+265
-59
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,7 @@ go.work*
1212
**/envFile
1313

1414
# IDE files/directories
15-
.idea/
15+
.idea/
16+
17+
# gomock generated prog.go
18+
pkg/aws/services/gomock_reflect_*

cmd/aws-application-networking-k8s/main.go

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,10 @@ import (
2020
"flag"
2121
"os"
2222

23+
"github.com/go-logr/zapr"
24+
2325
"github.com/aws/aws-application-networking-k8s/pkg/aws"
2426
"github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog"
25-
"github.com/go-logr/zapr"
2627

2728
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
2829
// to ensure that exec-entrypoint and run can make use of them.
@@ -34,18 +35,20 @@ import (
3435
ctrl "sigs.k8s.io/controller-runtime"
3536
"sigs.k8s.io/controller-runtime/pkg/healthz"
3637

37-
"github.com/aws/aws-application-networking-k8s/controllers"
38-
//+kubebuilder:scaffold:imports
39-
"github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1"
40-
"github.com/aws/aws-application-networking-k8s/pkg/config"
41-
"github.com/aws/aws-application-networking-k8s/pkg/k8s"
42-
"github.com/aws/aws-application-networking-k8s/pkg/latticestore"
4338
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
4439
"k8s.io/apimachinery/pkg/runtime/schema"
4540
"sigs.k8s.io/external-dns/endpoint"
4641
gateway_api_v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
4742
gateway_api_v1beta1 "sigs.k8s.io/gateway-api/apis/v1beta1"
4843
mcs_api "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1"
44+
45+
"github.com/aws/aws-application-networking-k8s/controllers"
46+
47+
//+kubebuilder:scaffold:imports
48+
"github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1"
49+
"github.com/aws/aws-application-networking-k8s/pkg/config"
50+
"github.com/aws/aws-application-networking-k8s/pkg/k8s"
51+
"github.com/aws/aws-application-networking-k8s/pkg/latticestore"
4952
)
5053

5154
var (
@@ -70,12 +73,15 @@ func addOptionalCRDs(scheme *runtime.Scheme) {
7073
scheme.AddKnownTypes(dnsEndpoint, &endpoint.DNSEndpoint{}, &endpoint.DNSEndpointList{})
7174
metav1.AddToGroupVersion(scheme, dnsEndpoint)
7275

73-
targetGroupPolicy := schema.GroupVersion{
74-
Group: "application-networking.k8s.aws",
76+
awsGatewayControllerCRDGroupVersion := schema.GroupVersion{
77+
Group: v1alpha1.GroupName,
7578
Version: "v1alpha1",
7679
}
77-
scheme.AddKnownTypes(targetGroupPolicy, &v1alpha1.TargetGroupPolicy{}, &v1alpha1.TargetGroupPolicyList{})
78-
metav1.AddToGroupVersion(scheme, targetGroupPolicy)
80+
scheme.AddKnownTypes(awsGatewayControllerCRDGroupVersion, &v1alpha1.TargetGroupPolicy{}, &v1alpha1.TargetGroupPolicyList{})
81+
metav1.AddToGroupVersion(scheme, awsGatewayControllerCRDGroupVersion)
82+
83+
scheme.AddKnownTypes(awsGatewayControllerCRDGroupVersion, &v1alpha1.VpcAssociationPolicy{}, &v1alpha1.VpcAssociationPolicyList{})
84+
metav1.AddToGroupVersion(scheme, awsGatewayControllerCRDGroupVersion)
7985
}
8086

8187
func main() {

controllers/eventhandlers/mapper.go

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@ package eventhandlers
22

33
import (
44
"context"
5-
"github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1"
6-
"github.com/aws/aws-application-networking-k8s/pkg/k8s"
7-
"github.com/aws/aws-application-networking-k8s/pkg/model/core"
8-
"github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog"
5+
"fmt"
6+
97
corev1 "k8s.io/api/core/v1"
108
"k8s.io/apimachinery/pkg/api/errors"
119
"k8s.io/apimachinery/pkg/types"
1210
"sigs.k8s.io/controller-runtime/pkg/client"
1311
gateway_api_v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
1412
gateway_api "sigs.k8s.io/gateway-api/apis/v1beta1"
1513
mcs_api "sigs.k8s.io/mcs-api/pkg/apis/v1alpha1"
14+
15+
"github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1"
16+
"github.com/aws/aws-application-networking-k8s/pkg/k8s"
17+
"github.com/aws/aws-application-networking-k8s/pkg/model/core"
18+
"github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog"
1619
)
1720

1821
type resourceMapper struct {
@@ -21,16 +24,16 @@ type resourceMapper struct {
2124
}
2225

2326
const (
24-
coreGroupName = "" // empty means core by definition
2527
serviceKind = "Service"
2628
serviceImportKind = "ServiceImport"
29+
gatewayKind = "Gateway"
2730
)
2831

2932
func (r *resourceMapper) ServiceToRoutes(ctx context.Context, svc *corev1.Service, routeType core.RouteType) []core.Route {
3033
if svc == nil {
3134
return nil
3235
}
33-
return r.backendRefToRoutes(ctx, svc, coreGroupName, serviceKind, routeType)
36+
return r.backendRefToRoutes(ctx, svc, corev1.GroupName, serviceKind, routeType)
3437
}
3538

3639
func (r *resourceMapper) ServiceImportToRoutes(ctx context.Context, svc *mcs_api.ServiceImport, routeType core.RouteType) []core.Route {
@@ -63,49 +66,81 @@ func (r *resourceMapper) EndpointsToService(ctx context.Context, ep *corev1.Endp
6366
}
6467

6568
func (r *resourceMapper) TargetGroupPolicyToService(ctx context.Context, tgp *v1alpha1.TargetGroupPolicy) *corev1.Service {
66-
if tgp == nil {
67-
return nil
68-
}
69-
policyName := k8s.NamespacedName(tgp).String()
69+
return policyToTargetRefObj(r, ctx, tgp, &corev1.Service{})
70+
}
7071

71-
targetRef := tgp.Spec.TargetRef
72-
if targetRef == nil {
73-
r.log.Infow("TargetGroupPolicy does not have targetRef, skipping",
74-
"policyName", policyName)
75-
return nil
76-
}
77-
if targetRef.Group != coreGroupName || targetRef.Kind != serviceKind {
78-
r.log.Infow("Detected non-Service TargetGroupPolicy attachment, skipping",
79-
"policyName", policyName, "targetRef", targetRef)
80-
return nil
81-
}
82-
namespace := tgp.Namespace
83-
if targetRef.Namespace != nil && namespace != string(*targetRef.Namespace) {
84-
r.log.Infow("Detected cross namespace TargetGroupPolicy attachment, skipping",
85-
"policyName", policyName, "targetRef", targetRef)
86-
return nil
72+
func (r *resourceMapper) VpcAssociationPolicyToGateway(ctx context.Context, vap *v1alpha1.VpcAssociationPolicy) *gateway_api.Gateway {
73+
return policyToTargetRefObj(r, ctx, vap, &gateway_api.Gateway{})
74+
}
75+
76+
func policyToTargetRefObj[T client.Object](r *resourceMapper, ctx context.Context, policy core.Policy, retObj T) T {
77+
null := *new(T)
78+
if policy == nil {
79+
return null
8780
}
81+
policyNamespacedName := policy.GetNamespacedName()
8882

89-
svcName := types.NamespacedName{
90-
Namespace: namespace,
83+
targetRef := policy.GetTargetRef()
84+
if targetRef == nil {
85+
r.log.Infow("Policy does not have targetRef, skipping",
86+
"policyName", policyNamespacedName)
87+
return null
88+
}
89+
expectedGroup, expectedKind, err := k8sResourceTypeToGroupAndKind(retObj)
90+
if err != nil {
91+
r.log.Errorw("Failed to get expected GroupKind for targetRefObj",
92+
"policyName", policyNamespacedName,
93+
"targetRef", targetRef,
94+
"reason", err.Error())
95+
return null
96+
}
97+
98+
if targetRef.Group != expectedGroup || targetRef.Kind != expectedKind {
99+
r.log.Infow("Detected targetRef GroupKind and expected retObj GroupKind are different, skipping",
100+
"policyName", policyNamespacedName,
101+
"targetRef", targetRef,
102+
"expectedGroup", expectedGroup,
103+
"expectedKind", expectedKind)
104+
return null
105+
}
106+
if targetRef.Namespace != nil && policyNamespacedName.Namespace != string(*targetRef.Namespace) {
107+
r.log.Infow("Detected Policy and TargetRef namespace are different, skipping",
108+
"policyNamespacedName", policyNamespacedName, "targetRef", targetRef,
109+
"targetRef.Namespace", targetRef.Namespace,
110+
"policyNamespacedName.Namespace", policyNamespacedName.Namespace)
111+
return null
112+
}
113+
114+
key := types.NamespacedName{
115+
Namespace: policyNamespacedName.Namespace,
91116
Name: string(targetRef.Name),
92117
}
93-
svc := &corev1.Service{}
94-
if err := r.client.Get(ctx, svcName, svc); err != nil {
118+
if err := r.client.Get(ctx, key, retObj); err != nil {
95119
if errors.IsNotFound(err) {
96-
r.log.Debugw("TargetGroupPolicy is referring to non-existent service, skipping",
97-
"policyName", policyName, "serviceName", svcName.String())
120+
r.log.Debugw("Policy is referring to a non-existent targetRefObj, skipping",
121+
"policyName", policyNamespacedName, "targetRef", targetRef)
98122
} else {
99123
// Still gracefully skipping the event but errors other than NotFound are bad sign.
100124
r.log.Errorw("Failed to query targetRef of TargetGroupPolicy",
101-
"policyName", policyName, "serviceName", svcName.String(), "reason", err.Error())
125+
"policyName", policyNamespacedName, "targetRef", targetRef, "reason", err.Error())
102126
}
103-
return nil
127+
return null
104128
}
105129
r.log.Debugw("TargetGroupPolicy change on Service detected",
106-
"policyName", policyName, "serviceName", svcName.String())
130+
"policyName", policyNamespacedName, "targetRef", targetRef)
107131

108-
return svc
132+
return retObj
133+
}
134+
135+
func k8sResourceTypeToGroupAndKind(obj client.Object) (gateway_api.Group, gateway_api.Kind, error) {
136+
switch obj.(type) {
137+
case *corev1.Service:
138+
return corev1.GroupName, serviceKind, nil
139+
case *gateway_api.Gateway:
140+
return gateway_api.GroupName, gatewayKind, nil
141+
default:
142+
return "", "", fmt.Errorf("un-registered obj type: %T", obj)
143+
}
109144
}
110145

111146
func (r *resourceMapper) backendRefToRoutes(ctx context.Context, obj client.Object, group, kind string, routeType core.RouteType) []core.Route {

controllers/eventhandlers/mapper_test.go

Lines changed: 107 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@ package eventhandlers
33
import (
44
"context"
55
"errors"
6-
mock_client "github.com/aws/aws-application-networking-k8s/mocks/controller-runtime/client"
7-
"github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1"
8-
"github.com/aws/aws-application-networking-k8s/pkg/model/core"
9-
"github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog"
6+
"testing"
7+
108
"github.com/golang/mock/gomock"
119
"github.com/stretchr/testify/assert"
1210
corev1 "k8s.io/api/core/v1"
1311
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1412
"k8s.io/utils/pointer"
1513
gateway_api_v1alpha2 "sigs.k8s.io/gateway-api/apis/v1alpha2"
1614
gateway_api "sigs.k8s.io/gateway-api/apis/v1beta1"
17-
"testing"
15+
16+
mock_client "github.com/aws/aws-application-networking-k8s/mocks/controller-runtime/client"
17+
"github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1"
18+
"github.com/aws/aws-application-networking-k8s/pkg/model/core"
19+
"github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog"
1820
)
1921

2022
func createHTTPRoute(name, namespace string, backendRef gateway_api.BackendObjectReference) gateway_api.HTTPRoute {
@@ -194,3 +196,103 @@ func TestTargetGroupPolicyToService(t *testing.T) {
194196
}
195197
}
196198
}
199+
200+
func TestVpcAssociationPolicyToGateway(t *testing.T) {
201+
c := gomock.NewController(t)
202+
defer c.Finish()
203+
204+
ns1 := "default"
205+
ns2 := "non-default"
206+
207+
testCases := []struct {
208+
testCaseName string
209+
namespace string
210+
targetKind gateway_api.Kind
211+
targetNamespace *gateway_api.Namespace
212+
gatewayFound bool
213+
expectSuccess bool
214+
}{
215+
{
216+
testCaseName: "namespace not match",
217+
namespace: ns1,
218+
targetKind: "Gateway",
219+
targetNamespace: (*gateway_api.Namespace)(&ns2),
220+
expectSuccess: false,
221+
},
222+
{
223+
testCaseName: "targetKind not match scenario 1",
224+
namespace: ns1,
225+
targetKind: "NotGateway",
226+
targetNamespace: (*gateway_api.Namespace)(&ns1),
227+
expectSuccess: false,
228+
},
229+
{
230+
testCaseName: "targetKind not match scenario 2",
231+
namespace: ns1,
232+
targetKind: "Service",
233+
targetNamespace: (*gateway_api.Namespace)(&ns1),
234+
expectSuccess: false,
235+
},
236+
{
237+
testCaseName: "gateway not found",
238+
namespace: ns1,
239+
targetKind: "Gateway",
240+
targetNamespace: (*gateway_api.Namespace)(&ns1),
241+
gatewayFound: false,
242+
expectSuccess: false,
243+
},
244+
{
245+
testCaseName: "gateway found, targetRef namespace match",
246+
namespace: ns1,
247+
targetKind: "Gateway",
248+
targetNamespace: (*gateway_api.Namespace)(&ns1),
249+
gatewayFound: true,
250+
expectSuccess: true,
251+
},
252+
{
253+
testCaseName: "gateway found, targetRef namespace not defined",
254+
namespace: ns1,
255+
targetKind: "Gateway",
256+
targetNamespace: nil,
257+
gatewayFound: true,
258+
expectSuccess: true,
259+
},
260+
}
261+
262+
for _, tt := range testCases {
263+
mockClient := mock_client.NewMockClient(c)
264+
mapper := &resourceMapper{log: gwlog.FallbackLogger, client: mockClient}
265+
if tt.gatewayFound {
266+
mockClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
267+
} else {
268+
mockClient.EXPECT().Get(gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("fail")).AnyTimes()
269+
}
270+
var targetRefGroupName string
271+
if tt.targetKind == "Gateway" {
272+
targetRefGroupName = gateway_api.GroupName
273+
} else if tt.targetKind == "Service" {
274+
targetRefGroupName = corev1.GroupName
275+
}
276+
277+
gw := mapper.VpcAssociationPolicyToGateway(context.Background(), &v1alpha1.VpcAssociationPolicy{
278+
ObjectMeta: metav1.ObjectMeta{
279+
Name: "test--vpc-association-policy",
280+
Namespace: tt.namespace,
281+
},
282+
283+
Spec: v1alpha1.VpcAssociationPolicySpec{
284+
TargetRef: &gateway_api_v1alpha2.PolicyTargetReference{
285+
Group: gateway_api.Group(targetRefGroupName),
286+
Kind: tt.targetKind,
287+
Name: "test-gw",
288+
Namespace: tt.targetNamespace,
289+
},
290+
},
291+
})
292+
if tt.expectSuccess {
293+
assert.NotNil(t, gw)
294+
} else {
295+
assert.Nil(t, gw)
296+
}
297+
}
298+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package eventhandlers
2+
3+
import (
4+
"context"
5+
6+
"github.com/aws/aws-application-networking-k8s/pkg/apis/applicationnetworking/v1alpha1"
7+
"github.com/aws/aws-application-networking-k8s/pkg/k8s"
8+
"github.com/aws/aws-application-networking-k8s/pkg/utils/gwlog"
9+
10+
"sigs.k8s.io/controller-runtime/pkg/client"
11+
"sigs.k8s.io/controller-runtime/pkg/handler"
12+
"sigs.k8s.io/controller-runtime/pkg/reconcile"
13+
)
14+
15+
type vpcAssociationPolicyEventHandler struct {
16+
log gwlog.Logger
17+
client client.Client
18+
mapper *resourceMapper
19+
}
20+
21+
func NewVpcAssociationPolicyEventHandler(log gwlog.Logger, client client.Client) *vpcAssociationPolicyEventHandler {
22+
return &vpcAssociationPolicyEventHandler{log: log, client: client,
23+
mapper: &resourceMapper{log: log, client: client}}
24+
}
25+
26+
func (h *vpcAssociationPolicyEventHandler) MapToGateway() handler.EventHandler {
27+
return handler.EnqueueRequestsFromMapFunc(func(obj client.Object) []reconcile.Request {
28+
if vap, ok := obj.(*v1alpha1.VpcAssociationPolicy); ok {
29+
if gw := h.mapper.VpcAssociationPolicyToGateway(context.Background(), vap); gw != nil {
30+
return []reconcile.Request{{NamespacedName: k8s.NamespacedName(gw)}}
31+
}
32+
}
33+
return nil
34+
})
35+
}

0 commit comments

Comments
 (0)