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 }