Skip to content

Commit fa4a077

Browse files
pkg: test ResourceDeletionProtection valid webhook
In this commit, we add unit tests for the `ValidatingAdmission` validation webhook specifically for the resource deletion protection feature. These tests ensure that the webhook correctly handles various admission scenarios: - Tests the behavior when decoding the request object fails, verifying that admission is denied with an appropriate error message. - Ensures that resources labeled with `DeletionProtectionAlways` are denied deletion, validating that the error message is returned. - Confirms that resources without the deletion protection label are allowed to be deleted without errors. - Validates that resources with other labels are not impacted and are allowed to be deleted. - Confirms that non-delete operations (e.g., create) are allowed by default. Signed-off-by: Mohamed Awnallah <[email protected]>
1 parent 32c2ef7 commit fa4a077

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
/*
2+
Copyright 2024 The Karmada Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package resourcedeletionprotection
18+
19+
import (
20+
"context"
21+
"errors"
22+
"fmt"
23+
"net/http"
24+
"reflect"
25+
"testing"
26+
27+
admissionv1 "k8s.io/api/admission/v1"
28+
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
29+
"k8s.io/apimachinery/pkg/runtime"
30+
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
31+
32+
workv1alpha2 "github.com/karmada-io/karmada/pkg/apis/work/v1alpha2"
33+
)
34+
35+
type fakeValidationDecoder struct {
36+
err error
37+
obj runtime.Object
38+
}
39+
40+
// Decode mocks the Decode method of admission.Decoder.
41+
func (f *fakeValidationDecoder) Decode(_ admission.Request, obj runtime.Object) error {
42+
if f.err != nil {
43+
return f.err
44+
}
45+
if f.obj != nil {
46+
reflect.ValueOf(obj).Elem().Set(reflect.ValueOf(f.obj).Elem())
47+
}
48+
return nil
49+
}
50+
51+
// DecodeRaw mocks the DecodeRaw method of admission.Decoder.
52+
func (f *fakeValidationDecoder) DecodeRaw(rawObj runtime.RawExtension, obj runtime.Object) error {
53+
if f.err != nil {
54+
return f.err
55+
}
56+
if rawObj.Object != nil {
57+
reflect.ValueOf(obj).Elem().Set(reflect.ValueOf(rawObj.Object).Elem())
58+
}
59+
return nil
60+
}
61+
62+
func TestValidatingAdmission_Handle(t *testing.T) {
63+
tests := []struct {
64+
name string
65+
decoder admission.Decoder
66+
req admission.Request
67+
want admission.Response
68+
}{
69+
{
70+
name: "Handle_DecodeRawError_DeniesAdmission",
71+
decoder: &fakeValidationDecoder{
72+
err: errors.New("decode error"),
73+
},
74+
req: admission.Request{
75+
AdmissionRequest: admissionv1.AdmissionRequest{
76+
Operation: admissionv1.Delete,
77+
},
78+
},
79+
want: admission.Errored(http.StatusBadRequest, errors.New("decode error")),
80+
},
81+
{
82+
name: "Handle_DeleteWithProtectionLabel_DeniesAdmission",
83+
decoder: &fakeValidationDecoder{},
84+
req: admission.Request{
85+
AdmissionRequest: admissionv1.AdmissionRequest{
86+
Operation: admissionv1.Delete,
87+
OldObject: runtime.RawExtension{
88+
Object: &unstructured.Unstructured{
89+
Object: map[string]interface{}{
90+
"metadata": map[string]interface{}{
91+
"labels": map[string]interface{}{
92+
workv1alpha2.DeletionProtectionLabelKey: workv1alpha2.DeletionProtectionAlways,
93+
},
94+
},
95+
},
96+
},
97+
},
98+
},
99+
},
100+
want: admission.Denied(fmt.Sprintf("This resource is protected, please make sure to remove the label: %s", workv1alpha2.DeletionProtectionLabelKey)),
101+
},
102+
{
103+
name: "Handle_DeleteWithoutLabel_AllowsAdmission",
104+
decoder: &fakeValidationDecoder{},
105+
req: admission.Request{
106+
AdmissionRequest: admissionv1.AdmissionRequest{
107+
Operation: admissionv1.Delete,
108+
OldObject: runtime.RawExtension{
109+
Object: &unstructured.Unstructured{
110+
Object: map[string]interface{}{
111+
"metadata": map[string]interface{}{
112+
"labels": map[string]interface{}{},
113+
},
114+
},
115+
},
116+
},
117+
},
118+
},
119+
want: admission.Allowed(""),
120+
},
121+
{
122+
name: "Handle_DeleteWithDifferentLabel_AllowsAdmission",
123+
decoder: &fakeValidationDecoder{},
124+
req: admission.Request{
125+
AdmissionRequest: admissionv1.AdmissionRequest{
126+
Operation: admissionv1.Delete,
127+
OldObject: runtime.RawExtension{
128+
Object: &unstructured.Unstructured{
129+
Object: map[string]interface{}{
130+
"metadata": map[string]interface{}{
131+
"labels": map[string]interface{}{
132+
"some-other-label": "some-value",
133+
},
134+
},
135+
},
136+
},
137+
},
138+
},
139+
},
140+
want: admission.Allowed(""),
141+
},
142+
{
143+
name: "Handle_CreateOperation_AllowsAdmission",
144+
decoder: &fakeValidationDecoder{
145+
err: errors.New("decode error"),
146+
},
147+
req: admission.Request{
148+
AdmissionRequest: admissionv1.AdmissionRequest{
149+
Operation: admissionv1.Create,
150+
},
151+
},
152+
want: admission.Allowed(""),
153+
},
154+
}
155+
156+
for _, tt := range tests {
157+
t.Run(tt.name, func(t *testing.T) {
158+
v := &ValidatingAdmission{
159+
Decoder: tt.decoder,
160+
}
161+
got := v.Handle(context.Background(), tt.req)
162+
if !reflect.DeepEqual(got, tt.want) {
163+
t.Errorf("Handle() = %v, want %v", got, tt.want)
164+
}
165+
})
166+
}
167+
}

0 commit comments

Comments
 (0)