Skip to content

Commit 660c99d

Browse files
author
Nont
committed
Fix the conversion and e2e
Signed-off-by: Nont <[email protected]>
1 parent 72babc6 commit 660c99d

File tree

4 files changed

+65
-48
lines changed

4 files changed

+65
-48
lines changed

pkg/webhook/managedresource/managedresource_validating_webhook.go

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"net/http"
1212

1313
admissionv1 "k8s.io/api/admission/v1"
14-
"k8s.io/apimachinery/pkg/api/meta"
14+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
1515
"k8s.io/apimachinery/pkg/runtime"
1616
"k8s.io/apimachinery/pkg/types"
1717
"k8s.io/klog/v2"
@@ -51,29 +51,41 @@ type managedResourceValidator struct {
5151
// Handle denies the resource admission if the request target object has a label or annotation key "fleet.azure.com".
5252
func (v *managedResourceValidator) Handle(_ context.Context, req admission.Request) admission.Response {
5353
namespacedName := types.NamespacedName{Name: req.Name, Namespace: req.Namespace}
54+
klog.V(1).InfoS("handling resource", "operation", req.Operation, "subResource", req.SubResource, "namespacedName", namespacedName)
55+
56+
var objs []runtime.RawExtension
5457
switch req.Operation {
55-
case admissionv1.Create, admissionv1.Update, admissionv1.Delete:
56-
klog.V(1).InfoS("handling resource", "operation", req.Operation, "subResource", req.SubResource, "namespacedName", namespacedName)
57-
for _, obj := range []runtime.Object{req.OldObject.Object, req.Object.Object} {
58-
labels, annotations, err := getLabelsAndAnnotations(obj)
59-
if err != nil {
60-
return admission.Errored(http.StatusInternalServerError, err)
61-
}
62-
if (managedByArm(labels) || managedByArm(annotations)) && !validation.IsAdminGroupUserOrWhiteListedUser(v.whiteListedUsers, req.UserInfo) {
63-
klog.V(2).InfoS(deniedResource, "user", req.UserInfo.Username, "groups", req.UserInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
64-
return admission.Denied(fmt.Sprintf(resourceDeniedFormat, req.Kind, req.Name, req.Namespace))
65-
}
58+
case admissionv1.Create:
59+
objs = append(objs, req.Object)
60+
case admissionv1.Update:
61+
objs = append(objs, req.Object, req.OldObject)
62+
case admissionv1.Delete:
63+
objs = append(objs, req.OldObject)
64+
}
65+
for _, obj := range objs {
66+
labels, annotations, err := getLabelsAndAnnotations(obj)
67+
if err != nil {
68+
return admission.Errored(http.StatusInternalServerError, err)
69+
}
70+
if (managedByArm(labels) || managedByArm(annotations)) && !validation.IsAdminGroupUserOrWhiteListedUser(v.whiteListedUsers, req.UserInfo) {
71+
klog.V(2).InfoS(deniedResource, "user", req.UserInfo.Username, "groups", req.UserInfo.Groups, "operation", req.Operation, "GVK", req.RequestKind, "subResource", req.SubResource, "namespacedName", namespacedName)
72+
return admission.Denied(fmt.Sprintf(resourceDeniedFormat, req.Kind, req.Name, req.Namespace))
6673
}
6774
}
6875
return admission.Allowed("")
6976
}
7077

71-
func getLabelsAndAnnotations(obj runtime.Object) (map[string]string, map[string]string, error) {
72-
accessor, err := meta.Accessor(obj)
78+
func getLabelsAndAnnotations(raw runtime.RawExtension) (map[string]string, map[string]string, error) {
79+
var obj runtime.Object
80+
if err := runtime.Convert_runtime_RawExtension_To_runtime_Object(&raw, &obj, nil); err != nil {
81+
return nil, nil, err
82+
}
83+
o, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
7384
if err != nil {
7485
return nil, nil, err
7586
}
76-
return accessor.GetLabels(), accessor.GetAnnotations(), nil
87+
u := unstructured.Unstructured{Object: o}
88+
return u.GetLabels(), u.GetAnnotations(), nil
7789
}
7890

7991
func managedByArm(m map[string]string) bool {

pkg/webhook/managedresource/managedresource_validating_webhook_test.go

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import (
2020
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
2121
)
2222

23-
func Test_managedResourceValidzaator_Handle(t *testing.T) {
23+
func Test_managedResourceValidator_Handle(t *testing.T) {
2424
const fleet1p = "fleet1p"
2525
validator := &managedResourceValidator{
2626
whiteListedUsers: []string{fleet1p},
@@ -49,9 +49,9 @@ func Test_managedResourceValidzaator_Handle(t *testing.T) {
4949
{
5050
name: "denied - error on getLabelsAndAnnotations failure",
5151
operation: admissionv1.Create,
52-
expectedResp: admission.Errored(http.StatusInternalServerError, fmt.Errorf("object does not implement the Object interfaces")),
52+
expectedResp: admission.Errored(http.StatusInternalServerError, fmt.Errorf("error decoding string from json: unexpected trailing data at offset 9")),
5353
modReq: func(req *admission.Request) {
54-
req.Object = runtime.RawExtension{Object: nil}
54+
req.Object = runtime.RawExtension{Raw: []byte(`"invalid"}`)} // Invalid object without labels or annotations
5555
},
5656
},
5757
{
@@ -125,24 +125,19 @@ func Test_managedResourceValidzaator_Handle(t *testing.T) {
125125
func Test_getLabelsAndAnnotations(t *testing.T) {
126126
tests := []struct {
127127
name string
128-
obj runtime.Object
128+
obj runtime.RawExtension
129129
wantLabels map[string]string
130130
wantAnnotations map[string]string
131131
expectError bool
132132
}{
133-
{
134-
name: "nil object - error",
135-
obj: nil,
136-
wantLabels: nil,
137-
wantAnnotations: nil,
138-
expectError: true,
139-
},
140133
{
141134
name: "object with labels and annotations",
142-
obj: &metav1.PartialObjectMetadata{
143-
ObjectMeta: metav1.ObjectMeta{
144-
Labels: map[string]string{"foo": "bar"},
145-
Annotations: map[string]string{"baz": "qux"},
135+
obj: runtime.RawExtension{
136+
Object: &metav1.PartialObjectMetadata{
137+
ObjectMeta: metav1.ObjectMeta{
138+
Labels: map[string]string{"foo": "bar"},
139+
Annotations: map[string]string{"baz": "qux"},
140+
},
146141
},
147142
},
148143
wantLabels: map[string]string{"foo": "bar"},
@@ -151,8 +146,10 @@ func Test_getLabelsAndAnnotations(t *testing.T) {
151146
},
152147
{
153148
name: "object with no labels or annotations",
154-
obj: &metav1.PartialObjectMetadata{
155-
ObjectMeta: metav1.ObjectMeta{},
149+
obj: runtime.RawExtension{
150+
Object: &metav1.PartialObjectMetadata{
151+
ObjectMeta: metav1.ObjectMeta{},
152+
},
156153
},
157154
wantLabels: nil,
158155
wantAnnotations: nil,

test/e2e/utils_test.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -709,18 +709,20 @@ func cleanupWorkResources() {
709709
cleanWorkResourcesOnCluster(hubCluster)
710710
}
711711

712-
func createManagedNamespace() corev1.Namespace {
713-
ns := corev1.Namespace{
712+
func managedNamespace() corev1.Namespace {
713+
return corev1.Namespace{
714714
ObjectMeta: metav1.ObjectMeta{
715715
Name: fmt.Sprintf(managedNamespaceTemplate, GinkgoParallelProcess()),
716716
Labels: map[string]string{
717717
managedresource.ManagedByArmKey: managedresource.ManagedByArmValue,
718718
},
719719
},
720720
}
721+
}
721722

723+
func createManagedNamespace() {
724+
ns := managedNamespace()
722725
Expect(hubClient.Create(ctx, &ns)).To(Succeed(), "Failed to create namespace %s", ns.Name)
723-
return ns
724726
}
725727

726728
func cleanWorkResourcesOnCluster(cluster *framework.Cluster) {
@@ -833,6 +835,7 @@ func checkIfRemovedWorkResourcesFromMemberClustersConsistently(clusters []*frame
833835
Consistently(workResourcesRemovedActual, consistentlyDuration, consistentlyInterval).Should(Succeed(), "Failed to remove work resources from member cluster %s consistently", memberCluster.ClusterName)
834836
}
835837
}
838+
836839
func checkNamespaceExistsWithOwnerRefOnMemberCluster(nsName, crpName string) {
837840
Consistently(func() error {
838841
ns := &corev1.Namespace{}

test/e2e/webhook_test.go

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,7 +1302,7 @@ var _ = Describe("webhook tests for operations on ARM managed resources", Ordere
13021302

13031303
AfterAll(func() {
13041304
By("deleting the managed namespace")
1305-
ns := createManagedNamespace()
1305+
ns := managedNamespace()
13061306
Expect(hubClient.Delete(ctx, &ns)).Should(SatisfyAny(Succeed(), utils.NotFoundMatcher{}), "Failed to delete the managed namespace")
13071307
Eventually(
13081308
func() error {
@@ -1315,53 +1315,58 @@ var _ = Describe("webhook tests for operations on ARM managed resources", Ordere
13151315

13161316
It("should deny create a managed resource namespace", func() {
13171317
Eventually(func(g Gomega) error {
1318-
createNs := createManagedNamespace()
1319-
createNs.Name = fmt.Sprintf("test-create-managed-ns-%d", GinkgoParallelProcess())
1320-
err := impersonateHubClient.Update(ctx, &createNs)
1318+
createNs := managedNamespace()
1319+
createNs.Name = "this-will-be-denied"
1320+
err := impersonateHubClient.Create(ctx, &createNs)
13211321
if k8sErrors.IsConflict(err) {
13221322
return err
13231323
}
13241324
var statusErr *k8sErrors.StatusError
13251325
g.Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Create managed namespace call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8sErrors.StatusError{})))
1326-
Expect(statusErr.ErrStatus.Message).Should(MatchRegexp("the operation on the managed resource type * is not allowed"))
1326+
Expect(statusErr.ErrStatus.Message).Should(MatchRegexp(".*the operation on the managed resource type .* is not allowed"))
13271327
return nil
13281328
}, testutils.PollTimeout, testutils.PollInterval).Should(Succeed())
13291329
})
13301330

13311331
It("should deny update a managed resource namespace", func() {
13321332
Eventually(func(g Gomega) error {
1333-
updateNamespace := createManagedNamespace()
1333+
updateNamespace := managedNamespace()
13341334
updateNamespace.Labels["foo"] = "NotManaged"
13351335
err := impersonateHubClient.Update(ctx, &updateNamespace)
13361336
if k8sErrors.IsConflict(err) {
13371337
return err
13381338
}
13391339
var statusErr *k8sErrors.StatusError
13401340
g.Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Update managed namespace call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8sErrors.StatusError{})))
1341-
Expect(statusErr.ErrStatus.Message).Should(MatchRegexp("the operation on the managed resource type * is not allowed"))
1341+
Expect(statusErr.ErrStatus.Message).Should(MatchRegexp(".*the operation on the managed resource type .* is not allowed"))
13421342
return nil
13431343
}, testutils.PollTimeout, testutils.PollInterval).Should(Succeed())
13441344
})
13451345

13461346
It("should deny delete a managed resource namespace", func() {
13471347
Eventually(func(g Gomega) error {
1348-
created := createManagedNamespace()
1348+
created := managedNamespace()
13491349
err := impersonateHubClient.Delete(ctx, &created)
13501350
if k8sErrors.IsConflict(err) {
13511351
return err
13521352
}
13531353
var statusErr *k8sErrors.StatusError
13541354
g.Expect(errors.As(err, &statusErr)).To(BeTrue(), fmt.Sprintf("Delete managed namespace call produced error %s. Error type wanted is %s.", reflect.TypeOf(err), reflect.TypeOf(&k8sErrors.StatusError{})))
1355-
Expect(statusErr.ErrStatus.Message).Should(MatchRegexp("the operation on the managed resource type * is not allowed"))
1355+
Expect(statusErr.ErrStatus.Message).Should(MatchRegexp(".*the operation on the managed resource type .* is not allowed"))
13561356
return nil
13571357
}, testutils.PollTimeout, testutils.PollInterval).Should(Succeed())
13581358
})
13591359

1360-
It("should allow create an unmanaged resource namespace", func() {
1360+
It("should allow create/update/delete an unmanaged resource namespace", func() {
13611361
Eventually(func(g Gomega) error {
1362-
creating := createManagedNamespace()
1363-
delete(creating.Labels, managedresource.ManagedByArmKey)
1364-
return impersonateHubClient.Create(ctx, &creating)
1362+
unmanaged := managedNamespace()
1363+
unmanaged.Name = "this-should-be-allowed"
1364+
delete(unmanaged.Labels, managedresource.ManagedByArmKey)
1365+
g.Expect(impersonateHubClient.Create(ctx, &unmanaged)).Should(SatisfyAny(Succeed(), utils.NotFoundMatcher{}), "Failed to create the unmanaged namespace")
1366+
unmanaged.Labels["foo"] = "NotManaged"
1367+
g.Expect(impersonateHubClient.Update(ctx, &unmanaged)).Should(SatisfyAny(Succeed(), utils.NotFoundMatcher{}), "Failed to update the unmanaged namespace")
1368+
g.Expect(impersonateHubClient.Delete(ctx, &unmanaged)).Should(SatisfyAny(Succeed(), utils.NotFoundMatcher{}), "Failed to delete the unmanaged namespace")
1369+
return nil
13651370
}, testutils.PollTimeout, testutils.PollInterval).Should(Succeed())
13661371
})
13671372
})

0 commit comments

Comments
 (0)