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  }