k8s.io/apiserver@v0.31.1/pkg/endpoints/handlers/delete_test.go (about) 1 /* 2 Copyright 2019 The Kubernetes 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 handlers 18 19 import ( 20 "context" 21 "io" 22 "net/http" 23 "net/http/httptest" 24 "strings" 25 "sync/atomic" 26 "testing" 27 28 metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" 29 metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme" 30 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 "k8s.io/apimachinery/pkg/runtime" 32 "k8s.io/apimachinery/pkg/runtime/schema" 33 "k8s.io/apimachinery/pkg/runtime/serializer" 34 auditapis "k8s.io/apiserver/pkg/apis/audit" 35 "k8s.io/apiserver/pkg/audit" 36 "k8s.io/apiserver/pkg/endpoints/handlers/negotiation" 37 "k8s.io/apiserver/pkg/registry/rest" 38 "k8s.io/utils/pointer" 39 ) 40 41 type mockCodecs struct { 42 serializer.CodecFactory 43 err error 44 } 45 46 type mockCodec struct { 47 runtime.Codec 48 codecs *mockCodecs 49 } 50 51 func (p mockCodec) Encode(obj runtime.Object, w io.Writer) error { 52 err := p.Codec.Encode(obj, w) 53 p.codecs.err = err 54 return err 55 } 56 57 func (s *mockCodecs) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder { 58 out := s.CodecFactory.CodecForVersions(encoder, nil, gv, nil) 59 return &mockCodec{ 60 Codec: out, 61 codecs: s, 62 } 63 } 64 65 func TestDeleteResourceAuditLogRequestObject(t *testing.T) { 66 67 ctx := audit.WithAuditContext(context.TODO()) 68 ac := audit.AuditContextFrom(ctx) 69 ac.Event.Level = auditapis.LevelRequestResponse 70 71 policy := metav1.DeletePropagationBackground 72 deleteOption := &metav1.DeleteOptions{ 73 GracePeriodSeconds: pointer.Int64Ptr(30), 74 PropagationPolicy: &policy, 75 } 76 77 fakeCorev1GroupVersion := schema.GroupVersion{ 78 Group: "", 79 Version: "v1", 80 } 81 testScheme := runtime.NewScheme() 82 metav1.AddToGroupVersion(testScheme, fakeCorev1GroupVersion) 83 testCodec := serializer.NewCodecFactory(testScheme) 84 85 tests := []struct { 86 name string 87 object runtime.Object 88 gv schema.GroupVersion 89 serializer serializer.CodecFactory 90 ok bool 91 }{ 92 { 93 name: "meta built-in Codec encode v1.DeleteOptions", 94 object: &metav1.DeleteOptions{ 95 GracePeriodSeconds: pointer.Int64Ptr(30), 96 PropagationPolicy: &policy, 97 }, 98 gv: metav1.SchemeGroupVersion, 99 serializer: metainternalversionscheme.Codecs, 100 ok: true, 101 }, 102 { 103 name: "fake corev1 registered codec encode v1 DeleteOptions", 104 object: &metav1.DeleteOptions{ 105 GracePeriodSeconds: pointer.Int64Ptr(30), 106 PropagationPolicy: &policy, 107 }, 108 gv: metav1.SchemeGroupVersion, 109 serializer: testCodec, 110 ok: false, 111 }, 112 } 113 114 for _, test := range tests { 115 t.Run(test.name, func(t *testing.T) { 116 117 codecs := &mockCodecs{} 118 codecs.CodecFactory = test.serializer 119 120 audit.LogRequestObject(ctx, deleteOption, test.gv, schema.GroupVersionResource{ 121 Group: "", 122 Version: "v1", 123 Resource: "pods", 124 }, "", codecs) 125 126 err := codecs.err 127 if err != nil { 128 if test.ok { 129 t.Errorf("expect nil but got %#v", err) 130 } 131 t.Logf("encode object: %#v", err) 132 } else { 133 if !test.ok { 134 t.Errorf("expect err but got nil") 135 } 136 } 137 }) 138 } 139 } 140 141 func TestDeleteCollection(t *testing.T) { 142 req := &http.Request{ 143 Header: http.Header{}, 144 } 145 req.Header.Set("Content-Type", "application/json") 146 147 fakeCorev1GroupVersion := schema.GroupVersion{ 148 Group: "", 149 Version: "v1", 150 } 151 fakeCorev1Scheme := runtime.NewScheme() 152 fakeCorev1Scheme.AddKnownTypes(fakeCorev1GroupVersion, &metav1.DeleteOptions{}) 153 fakeCorev1Codec := serializer.NewCodecFactory(fakeCorev1Scheme) 154 155 tests := []struct { 156 name string 157 codecFactory serializer.CodecFactory 158 data []byte 159 expectErr string 160 }{ 161 // for issue: https://github.com/kubernetes/kubernetes/issues/111985 162 { 163 name: "decode '{}' to metav1.DeleteOptions with fakeCorev1Codecs", 164 codecFactory: fakeCorev1Codec, 165 data: []byte("{}"), 166 expectErr: "no kind \"DeleteOptions\" is registered", 167 }, 168 { 169 name: "decode '{}' to metav1.DeleteOptions with metainternalversionscheme.Codecs", 170 codecFactory: metainternalversionscheme.Codecs, 171 data: []byte("{}"), 172 expectErr: "", 173 }, 174 { 175 name: "decode versioned (corev1) DeleteOptions with metainternalversionscheme.Codecs", 176 codecFactory: metainternalversionscheme.Codecs, 177 data: []byte(`{"apiVersion":"v1","kind":"DeleteOptions","gracePeriodSeconds":123}`), 178 expectErr: "", 179 }, 180 { 181 name: "decode versioned (foo) DeleteOptions with metainternalversionscheme.Codecs", 182 codecFactory: metainternalversionscheme.Codecs, 183 data: []byte(`{"apiVersion":"foo/v1","kind":"DeleteOptions","gracePeriodSeconds":123}`), 184 expectErr: "", 185 }, 186 } 187 188 defaultGVK := metav1.SchemeGroupVersion.WithKind("DeleteOptions") 189 for _, test := range tests { 190 t.Run(test.name, func(t *testing.T) { 191 s, err := negotiation.NegotiateInputSerializer(req, false, test.codecFactory) 192 if err != nil { 193 t.Fatal(err) 194 } 195 196 options := &metav1.DeleteOptions{} 197 _, _, err = metainternalversionscheme.Codecs.DecoderToVersion(s.Serializer, defaultGVK.GroupVersion()).Decode(test.data, &defaultGVK, options) 198 if test.expectErr != "" { 199 if err == nil { 200 t.Fatalf("expect %s but got nil", test.expectErr) 201 } 202 if !strings.Contains(err.Error(), test.expectErr) { 203 t.Fatalf("expect %s but got %s", test.expectErr, err.Error()) 204 } 205 } 206 }) 207 } 208 } 209 210 func TestDeleteCollectionWithNoContextDeadlineEnforced(t *testing.T) { 211 var invokedGot, hasDeadlineGot int32 212 fakeDeleterFn := func(ctx context.Context, _ rest.ValidateObjectFunc, _ *metav1.DeleteOptions, _ *metainternalversion.ListOptions) (runtime.Object, error) { 213 // we expect CollectionDeleter to be executed once 214 atomic.AddInt32(&invokedGot, 1) 215 216 // we don't expect any context deadline to be set 217 if _, hasDeadline := ctx.Deadline(); hasDeadline { 218 atomic.AddInt32(&hasDeadlineGot, 1) 219 } 220 return nil, nil 221 } 222 223 // do the minimum setup to ensure that it gets as far as CollectionDeleter 224 scope := &RequestScope{ 225 Namer: &mockNamer{}, 226 Serializer: &fakeSerializer{ 227 serializer: runtime.NewCodec(runtime.NoopEncoder{}, runtime.NoopDecoder{}), 228 }, 229 } 230 handler := DeleteCollection(fakeCollectionDeleterFunc(fakeDeleterFn), false, scope, nil) 231 232 request, err := http.NewRequest("GET", "/test", nil) 233 if err != nil { 234 t.Fatalf("unexpected error: %v", err) 235 } 236 // the request context should not have any deadline by default 237 if _, hasDeadline := request.Context().Deadline(); hasDeadline { 238 t.Fatalf("expected request context to not have any deadline") 239 } 240 241 recorder := httptest.NewRecorder() 242 handler.ServeHTTP(recorder, request) 243 if atomic.LoadInt32(&invokedGot) != 1 { 244 t.Errorf("expected collection deleter to be invoked") 245 } 246 if atomic.LoadInt32(&hasDeadlineGot) > 0 { 247 t.Errorf("expected context to not have any deadline") 248 } 249 } 250 251 type fakeCollectionDeleterFunc func(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *metainternalversion.ListOptions) (runtime.Object, error) 252 253 func (f fakeCollectionDeleterFunc) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *metainternalversion.ListOptions) (runtime.Object, error) { 254 return f(ctx, deleteValidation, options, listOptions) 255 } 256 257 type fakeSerializer struct { 258 serializer runtime.Serializer 259 } 260 261 func (n *fakeSerializer) SupportedMediaTypes() []runtime.SerializerInfo { 262 return []runtime.SerializerInfo{ 263 { 264 MediaType: "application/json", 265 MediaTypeType: "application", 266 MediaTypeSubType: "json", 267 }, 268 } 269 } 270 func (n *fakeSerializer) EncoderForVersion(serializer runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder { 271 return n.serializer 272 } 273 func (n *fakeSerializer) DecoderToVersion(serializer runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder { 274 return n.serializer 275 }