k8s.io/apiserver@v0.31.1/pkg/endpoints/handlers/response_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  	"fmt"
    22  	"io"
    23  	"net/http"
    24  	"reflect"
    25  	"testing"
    26  	"time"
    27  
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"k8s.io/apimachinery/pkg/runtime/schema"
    32  	examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
    33  	"k8s.io/apiserver/pkg/endpoints/request"
    34  	"k8s.io/apiserver/pkg/registry/rest"
    35  )
    36  
    37  var _ runtime.CacheableObject = &mockCacheableObject{}
    38  
    39  type mockCacheableObject struct {
    40  	gvk schema.GroupVersionKind
    41  	obj runtime.Object
    42  }
    43  
    44  // DeepCopyObject implements runtime.Object interface.
    45  func (m *mockCacheableObject) DeepCopyObject() runtime.Object {
    46  	panic("DeepCopy unimplemented for mockCacheableObject")
    47  }
    48  
    49  // GetObjectKind implements runtime.Object interface.
    50  func (m *mockCacheableObject) GetObjectKind() schema.ObjectKind {
    51  	return m
    52  }
    53  
    54  // GroupVersionKind implements schema.ObjectKind interface.
    55  func (m *mockCacheableObject) GroupVersionKind() schema.GroupVersionKind {
    56  	return m.gvk
    57  }
    58  
    59  // SetGroupVersionKind implements schema.ObjectKind interface.
    60  func (m *mockCacheableObject) SetGroupVersionKind(gvk schema.GroupVersionKind) {
    61  	m.gvk = gvk
    62  }
    63  
    64  // CacheEncode implements runtime.CacheableObject interface.
    65  func (m *mockCacheableObject) CacheEncode(id runtime.Identifier, encode func(runtime.Object, io.Writer) error, w io.Writer) error {
    66  	return encode(m.obj.DeepCopyObject(), w)
    67  }
    68  
    69  // GetObject implements runtime.CacheableObject interface.
    70  func (m *mockCacheableObject) GetObject() runtime.Object {
    71  	return m.obj
    72  }
    73  
    74  type mockNamer struct{}
    75  
    76  func (*mockNamer) Namespace(_ *http.Request) (string, error)           { return "", nil }
    77  func (*mockNamer) Name(_ *http.Request) (string, string, error)        { return "", "", nil }
    78  func (*mockNamer) ObjectName(_ runtime.Object) (string, string, error) { return "", "", nil }
    79  
    80  type mockEncoder struct {
    81  	obj runtime.Object
    82  }
    83  
    84  func (e *mockEncoder) Encode(obj runtime.Object, _ io.Writer) error {
    85  	e.obj = obj
    86  	return nil
    87  }
    88  
    89  func (e *mockEncoder) Identifier() runtime.Identifier {
    90  	return runtime.Identifier("")
    91  }
    92  
    93  func TestCacheableObject(t *testing.T) {
    94  	pomGVK := metav1.SchemeGroupVersion.WithKind("PartialObjectMetadata")
    95  	tableGVK := metav1.SchemeGroupVersion.WithKind("Table")
    96  
    97  	status := &metav1.Status{Status: "status"}
    98  	pod := &examplev1.Pod{
    99  		ObjectMeta: metav1.ObjectMeta{
   100  			Name:      "name",
   101  			Namespace: "namespace",
   102  		},
   103  	}
   104  	podMeta := &metav1.PartialObjectMetadata{
   105  		ObjectMeta: metav1.ObjectMeta{
   106  			Name:      "name",
   107  			Namespace: "namespace",
   108  		},
   109  	}
   110  	podMeta.GetObjectKind().SetGroupVersionKind(pomGVK)
   111  	podTable := &metav1.Table{
   112  		Rows: []metav1.TableRow{
   113  			{
   114  				Cells: []interface{}{pod.Name, pod.CreationTimestamp.Time.UTC().Format(time.RFC3339)},
   115  			},
   116  		},
   117  	}
   118  
   119  	tableConvertor := rest.NewDefaultTableConvertor(examplev1.Resource("Pod"))
   120  
   121  	testCases := []struct {
   122  		desc   string
   123  		object runtime.Object
   124  		opts   *metav1beta1.TableOptions
   125  		target *schema.GroupVersionKind
   126  
   127  		expectedUnwrap bool
   128  		expectedObj    runtime.Object
   129  		expectedErr    error
   130  	}{
   131  		{
   132  			desc:        "metav1.Status",
   133  			object:      status,
   134  			expectedObj: status,
   135  			expectedErr: nil,
   136  		},
   137  		{
   138  			desc:        "cacheableObject nil convert",
   139  			object:      &mockCacheableObject{obj: pod},
   140  			target:      nil,
   141  			expectedObj: pod,
   142  			expectedErr: nil,
   143  		},
   144  		{
   145  			desc:        "cacheableObject as PartialObjectMeta",
   146  			object:      &mockCacheableObject{obj: pod},
   147  			target:      &pomGVK,
   148  			expectedObj: podMeta,
   149  			expectedErr: nil,
   150  		},
   151  		{
   152  			desc:        "cacheableObject as Table",
   153  			object:      &mockCacheableObject{obj: pod},
   154  			opts:        &metav1beta1.TableOptions{NoHeaders: true, IncludeObject: metav1.IncludeNone},
   155  			target:      &tableGVK,
   156  			expectedObj: podTable,
   157  			expectedErr: nil,
   158  		},
   159  	}
   160  
   161  	for _, test := range testCases {
   162  		t.Run(test.desc, func(t *testing.T) {
   163  			internalEncoder := &mockEncoder{}
   164  			watchEncoder := newWatchEmbeddedEncoder(
   165  				request.WithRequestInfo(context.TODO(), &request.RequestInfo{}),
   166  				internalEncoder, test.target, test.opts,
   167  				&RequestScope{
   168  					Namer:          &mockNamer{},
   169  					TableConvertor: tableConvertor,
   170  				},
   171  			)
   172  
   173  			err := watchEncoder.Encode(test.object, nil)
   174  			if err != test.expectedErr {
   175  				t.Errorf("unexpected error: %v, expected: %v", err, test.expectedErr)
   176  			}
   177  			if a, e := internalEncoder.obj, test.expectedObj; !reflect.DeepEqual(a, e) {
   178  				t.Errorf("unexpected result: %#v, expected: %#v", a, e)
   179  			}
   180  		})
   181  	}
   182  }
   183  
   184  func TestAsPartialObjectMetadataList(t *testing.T) {
   185  	var remainingItemCount int64 = 10
   186  	pods := &examplev1.PodList{
   187  		ListMeta: metav1.ListMeta{
   188  			ResourceVersion:    "10",
   189  			Continue:           "continuetoken",
   190  			RemainingItemCount: &remainingItemCount,
   191  		},
   192  	}
   193  
   194  	pomGVs := []schema.GroupVersion{metav1beta1.SchemeGroupVersion, metav1.SchemeGroupVersion}
   195  	for _, gv := range pomGVs {
   196  		t.Run(fmt.Sprintf("as %s PartialObjectMetadataList", gv), func(t *testing.T) {
   197  			list, err := asPartialObjectMetadataList(pods, gv)
   198  			if err != nil {
   199  				t.Fatalf("failed to transform object: %v", err)
   200  			}
   201  
   202  			var listMeta metav1.ListMeta
   203  			switch gv {
   204  			case metav1beta1.SchemeGroupVersion:
   205  				listMeta = list.(*metav1beta1.PartialObjectMetadataList).ListMeta
   206  			case metav1.SchemeGroupVersion:
   207  				listMeta = list.(*metav1.PartialObjectMetadataList).ListMeta
   208  			}
   209  			if !reflect.DeepEqual(pods.ListMeta, listMeta) {
   210  				t.Errorf("unexpected list metadata: %v, expected: %v", listMeta, pods.ListMeta)
   211  			}
   212  		})
   213  	}
   214  }
   215  
   216  func TestWatchEncoderIdentifier(t *testing.T) {
   217  	eventFields := reflect.VisibleFields(reflect.TypeOf(metav1.WatchEvent{}))
   218  	if len(eventFields) != 2 {
   219  		t.Error("New field was added to metav1.WatchEvent.")
   220  		t.Error("  Ensure that the following places are updated accordingly:")
   221  		t.Error("  - watchEncoder::doEncode method when creating outEvent")
   222  		t.Error("  - watchEncoder::typeIdentifier to capture all relevant fields in identifier")
   223  	}
   224  }