1/*
2Copyright 2014 The Kubernetes Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package endpoints
18
19import (
20	"bytes"
21	"compress/gzip"
22	"context"
23	"encoding/json"
24	"errors"
25	"fmt"
26	"io"
27	"io/ioutil"
28	"math/rand"
29	"net/http"
30	"net/http/httptest"
31	"net/http/httputil"
32	"net/url"
33	"reflect"
34	"strconv"
35	"strings"
36	"sync"
37	"testing"
38	"time"
39
40	restful "github.com/emicklei/go-restful"
41
42	fuzzer "k8s.io/apimachinery/pkg/api/apitesting/fuzzer"
43	apiequality "k8s.io/apimachinery/pkg/api/equality"
44	apierrors "k8s.io/apimachinery/pkg/api/errors"
45	"k8s.io/apimachinery/pkg/api/meta"
46	metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
47	metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
48	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
49	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
50	metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1"
51	"k8s.io/apimachinery/pkg/fields"
52	"k8s.io/apimachinery/pkg/labels"
53	"k8s.io/apimachinery/pkg/runtime"
54	"k8s.io/apimachinery/pkg/runtime/schema"
55	"k8s.io/apimachinery/pkg/runtime/serializer"
56	"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
57	"k8s.io/apimachinery/pkg/types"
58	"k8s.io/apimachinery/pkg/util/diff"
59	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
60	"k8s.io/apimachinery/pkg/util/sets"
61	"k8s.io/apimachinery/pkg/watch"
62	"k8s.io/apiserver/pkg/admission"
63	auditinternal "k8s.io/apiserver/pkg/apis/audit"
64	"k8s.io/apiserver/pkg/apis/example"
65	examplefuzzer "k8s.io/apiserver/pkg/apis/example/fuzzer"
66	examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
67	"k8s.io/apiserver/pkg/audit"
68	auditpolicy "k8s.io/apiserver/pkg/audit/policy"
69	genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
70	"k8s.io/apiserver/pkg/endpoints/handlers/negotiation"
71	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
72	"k8s.io/apiserver/pkg/endpoints/request"
73	genericapitesting "k8s.io/apiserver/pkg/endpoints/testing"
74	"k8s.io/apiserver/pkg/features"
75	"k8s.io/apiserver/pkg/registry/rest"
76	utilfeature "k8s.io/apiserver/pkg/util/feature"
77	featuregatetesting "k8s.io/component-base/featuregate/testing"
78)
79
80type alwaysMutatingDeny struct{}
81
82func (alwaysMutatingDeny) Admit(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error) {
83	return admission.NewForbidden(a, errors.New("Mutating admission control is denying all modifications"))
84}
85
86func (alwaysMutatingDeny) Handles(operation admission.Operation) bool {
87	return true
88}
89
90var _ admission.MutationInterface = &alwaysMutatingDeny{}
91
92type alwaysValidatingDeny struct{}
93
94func (alwaysValidatingDeny) Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error) {
95	return admission.NewForbidden(a, errors.New("Validating admission control is denying all modifications"))
96}
97
98func (alwaysValidatingDeny) Handles(operation admission.Operation) bool {
99	return true
100}
101
102var _ admission.ValidationInterface = &alwaysValidatingDeny{}
103
104// This creates fake API versions, similar to api/latest.go.
105var testAPIGroup = "test.group"
106var testAPIGroup2 = "test.group2"
107var testInternalGroupVersion = schema.GroupVersion{Group: testAPIGroup, Version: runtime.APIVersionInternal}
108var testGroupVersion = schema.GroupVersion{Group: testAPIGroup, Version: "version"}
109var newGroupVersion = schema.GroupVersion{Group: testAPIGroup, Version: "version2"}
110var testGroup2Version = schema.GroupVersion{Group: testAPIGroup2, Version: "version"}
111var testInternalGroup2Version = schema.GroupVersion{Group: testAPIGroup2, Version: runtime.APIVersionInternal}
112var prefix = "apis"
113
114var grouplessGroupVersion = schema.GroupVersion{Group: "", Version: "v1"}
115var grouplessInternalGroupVersion = schema.GroupVersion{Group: "", Version: runtime.APIVersionInternal}
116var grouplessPrefix = "api"
117
118var groupVersions = []schema.GroupVersion{grouplessGroupVersion, testGroupVersion, newGroupVersion}
119
120var scheme = runtime.NewScheme()
121var codecs = serializer.NewCodecFactory(scheme)
122
123var codec = codecs.LegacyCodec(groupVersions...)
124var testCodec = codecs.LegacyCodec(testGroupVersion)
125var newCodec = codecs.LegacyCodec(newGroupVersion)
126var parameterCodec = runtime.NewParameterCodec(scheme)
127
128var accessor = meta.NewAccessor()
129var selfLinker runtime.SelfLinker = accessor
130var admissionControl admission.Interface
131
132func init() {
133	metav1.AddToGroupVersion(scheme, metav1.SchemeGroupVersion)
134
135	// unnamed core group
136	scheme.AddUnversionedTypes(grouplessGroupVersion, &metav1.Status{})
137	metav1.AddToGroupVersion(scheme, grouplessGroupVersion)
138
139	utilruntime.Must(example.AddToScheme(scheme))
140	utilruntime.Must(examplev1.AddToScheme(scheme))
141}
142
143func addGrouplessTypes() {
144	scheme.AddKnownTypes(grouplessGroupVersion,
145		&genericapitesting.Simple{}, &genericapitesting.SimpleList{}, &metav1.ListOptions{},
146		&metav1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{})
147	scheme.AddKnownTypes(grouplessInternalGroupVersion,
148		&genericapitesting.Simple{}, &genericapitesting.SimpleList{},
149		&genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{})
150
151	utilruntime.Must(genericapitesting.RegisterConversions(scheme))
152}
153
154func addTestTypes() {
155	scheme.AddKnownTypes(testGroupVersion,
156		&genericapitesting.Simple{}, &genericapitesting.SimpleList{},
157		&metav1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
158		&genericapitesting.SimpleXGSubresource{})
159	scheme.AddKnownTypes(testGroupVersion, &examplev1.Pod{})
160	scheme.AddKnownTypes(testInternalGroupVersion,
161		&genericapitesting.Simple{}, &genericapitesting.SimpleList{},
162		&genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
163		&genericapitesting.SimpleXGSubresource{})
164	scheme.AddKnownTypes(testInternalGroupVersion, &example.Pod{})
165	// Register SimpleXGSubresource in both testGroupVersion and testGroup2Version, and also their
166	// their corresponding internal versions, to verify that the desired group version object is
167	// served in the tests.
168	scheme.AddKnownTypes(testGroup2Version, &genericapitesting.SimpleXGSubresource{})
169	scheme.AddKnownTypes(testInternalGroup2Version, &genericapitesting.SimpleXGSubresource{})
170	metav1.AddToGroupVersion(scheme, testGroupVersion)
171
172	utilruntime.Must(genericapitesting.RegisterConversions(scheme))
173}
174
175func addNewTestTypes() {
176	scheme.AddKnownTypes(newGroupVersion,
177		&genericapitesting.Simple{}, &genericapitesting.SimpleList{},
178		&metav1.DeleteOptions{}, &genericapitesting.SimpleGetOptions{}, &genericapitesting.SimpleRoot{},
179		&examplev1.Pod{},
180	)
181	metav1.AddToGroupVersion(scheme, newGroupVersion)
182
183	utilruntime.Must(genericapitesting.RegisterConversions(scheme))
184}
185
186func init() {
187	// Certain API objects are returned regardless of the contents of storage:
188	// api.Status is returned in errors
189
190	addGrouplessTypes()
191	addTestTypes()
192	addNewTestTypes()
193
194	scheme.AddFieldLabelConversionFunc(grouplessGroupVersion.WithKind("Simple"),
195		func(label, value string) (string, string, error) {
196			return label, value, nil
197		},
198	)
199	scheme.AddFieldLabelConversionFunc(testGroupVersion.WithKind("Simple"),
200		func(label, value string) (string, string, error) {
201			return label, value, nil
202		},
203	)
204	scheme.AddFieldLabelConversionFunc(newGroupVersion.WithKind("Simple"),
205		func(label, value string) (string, string, error) {
206			return label, value, nil
207		},
208	)
209}
210
211// defaultAPIServer exposes nested objects for testability.
212type defaultAPIServer struct {
213	http.Handler
214	container *restful.Container
215}
216
217// uses the default settings
218func handle(storage map[string]rest.Storage) http.Handler {
219	return handleInternal(storage, admissionControl, selfLinker, nil)
220}
221
222// tests using a custom self linker
223func handleLinker(storage map[string]rest.Storage, selfLinker runtime.SelfLinker) http.Handler {
224	return handleInternal(storage, admissionControl, selfLinker, nil)
225}
226
227func handleInternal(storage map[string]rest.Storage, admissionControl admission.Interface, selfLinker runtime.SelfLinker, auditSink audit.Sink) http.Handler {
228	container := restful.NewContainer()
229	container.Router(restful.CurlyRouter{})
230	mux := container.ServeMux
231
232	template := APIGroupVersion{
233		Storage: storage,
234
235		Creater:         scheme,
236		Convertor:       scheme,
237		UnsafeConvertor: runtime.UnsafeObjectConvertor(scheme),
238		Defaulter:       scheme,
239		Typer:           scheme,
240		Linker:          selfLinker,
241		RootScopedKinds: sets.NewString("SimpleRoot"),
242
243		EquivalentResourceRegistry: runtime.NewEquivalentResourceRegistry(),
244
245		ParameterCodec: parameterCodec,
246
247		Admit: admissionControl,
248	}
249
250	// groupless v1 version
251	{
252		group := template
253		group.Root = "/" + grouplessPrefix
254		group.GroupVersion = grouplessGroupVersion
255		group.OptionsExternalVersion = &grouplessGroupVersion
256		group.Serializer = codecs
257		if _, err := (&group).InstallREST(container); err != nil {
258			panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err))
259		}
260	}
261
262	// group version 1
263	{
264		group := template
265		group.Root = "/" + prefix
266		group.GroupVersion = testGroupVersion
267		group.OptionsExternalVersion = &testGroupVersion
268		group.Serializer = codecs
269		if _, err := (&group).InstallREST(container); err != nil {
270			panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err))
271		}
272	}
273
274	// group version 2
275	{
276		group := template
277		group.Root = "/" + prefix
278		group.GroupVersion = newGroupVersion
279		group.OptionsExternalVersion = &newGroupVersion
280		group.Serializer = codecs
281		if _, err := (&group).InstallREST(container); err != nil {
282			panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err))
283		}
284	}
285	longRunningCheck := func(r *http.Request, requestInfo *request.RequestInfo) bool {
286		// simplified long-running check
287		return requestInfo.Verb == "watch" || requestInfo.Verb == "proxy"
288	}
289	fakeChecker := auditpolicy.FakeChecker(auditinternal.LevelRequestResponse, nil)
290	handler := genericapifilters.WithAudit(mux, auditSink, fakeChecker, longRunningCheck)
291	handler = genericapifilters.WithRequestDeadline(handler, auditSink, fakeChecker, longRunningCheck, codecs, 60*time.Second)
292	handler = genericapifilters.WithRequestInfo(handler, testRequestInfoResolver())
293
294	return &defaultAPIServer{handler, container}
295}
296
297func testRequestInfoResolver() *request.RequestInfoFactory {
298	return &request.RequestInfoFactory{
299		APIPrefixes:          sets.NewString("api", "apis"),
300		GrouplessAPIPrefixes: sets.NewString("api"),
301	}
302}
303
304func TestSimpleSetupRight(t *testing.T) {
305	s := &genericapitesting.Simple{ObjectMeta: metav1.ObjectMeta{Name: "aName"}}
306	wire, err := runtime.Encode(codec, s)
307	if err != nil {
308		t.Fatal(err)
309	}
310	s2, err := runtime.Decode(codec, wire)
311	if err != nil {
312		t.Fatal(err)
313	}
314	if !reflect.DeepEqual(s, s2) {
315		t.Fatalf("encode/decode broken:\n%#v\n%#v\n", s, s2)
316	}
317}
318
319func TestSimpleOptionsSetupRight(t *testing.T) {
320	s := &genericapitesting.SimpleGetOptions{}
321	wire, err := runtime.Encode(codec, s)
322	if err != nil {
323		t.Fatal(err)
324	}
325	s2, err := runtime.Decode(codec, wire)
326	if err != nil {
327		t.Fatal(err)
328	}
329	if !reflect.DeepEqual(s, s2) {
330		t.Fatalf("encode/decode broken:\n%#v\n%#v\n", s, s2)
331	}
332}
333
334type SimpleRESTStorage struct {
335	lock sync.Mutex
336
337	errors map[string]error
338	list   []genericapitesting.Simple
339	item   genericapitesting.Simple
340
341	updated *genericapitesting.Simple
342	created *genericapitesting.Simple
343
344	stream *SimpleStream
345
346	deleted       string
347	deleteOptions *metav1.DeleteOptions
348
349	actualNamespace  string
350	namespacePresent bool
351
352	// These are set when Watch is called
353	fakeWatch                  *watch.FakeWatcher
354	requestedLabelSelector     labels.Selector
355	requestedFieldSelector     fields.Selector
356	requestedResourceVersion   string
357	requestedResourceNamespace string
358
359	expectedResourceNamespace string
360
361	// If non-nil, called inside the WorkFunc when answering update, delete, create.
362	// obj receives the original input to the update, delete, or create call.
363	injectedFunction func(obj runtime.Object) (returnObj runtime.Object, err error)
364}
365
366func (storage *SimpleRESTStorage) NamespaceScoped() bool {
367	return true
368}
369
370func (storage *SimpleRESTStorage) ConvertToTable(ctx context.Context, obj runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
371	return rest.NewDefaultTableConvertor(schema.GroupResource{Resource: "simple"}).ConvertToTable(ctx, obj, tableOptions)
372}
373
374func (storage *SimpleRESTStorage) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
375	storage.checkContext(ctx)
376	result := &genericapitesting.SimpleList{
377		ListMeta: metav1.ListMeta{
378			ResourceVersion: "10",
379			SelfLink:        "/test/link",
380		},
381		Items: storage.list,
382	}
383	storage.requestedLabelSelector = labels.Everything()
384	if options != nil && options.LabelSelector != nil {
385		storage.requestedLabelSelector = options.LabelSelector
386	}
387	storage.requestedFieldSelector = fields.Everything()
388	if options != nil && options.FieldSelector != nil {
389		storage.requestedFieldSelector = options.FieldSelector
390	}
391	return result, storage.errors["list"]
392}
393
394type SimpleStream struct {
395	version     string
396	accept      string
397	contentType string
398	err         error
399
400	io.Reader
401	closed bool
402}
403
404func (s *SimpleStream) Close() error {
405	s.closed = true
406	return nil
407}
408
409func (obj *SimpleStream) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
410func (obj *SimpleStream) DeepCopyObject() runtime.Object {
411	panic("SimpleStream does not support DeepCopy")
412}
413
414func (s *SimpleStream) InputStream(_ context.Context, version, accept string) (io.ReadCloser, bool, string, error) {
415	s.version = version
416	s.accept = accept
417	return s, false, s.contentType, s.err
418}
419
420type OutputConnect struct {
421	response string
422}
423
424func (h *OutputConnect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
425	w.Write([]byte(h.response))
426}
427
428func (storage *SimpleRESTStorage) Get(ctx context.Context, id string, options *metav1.GetOptions) (runtime.Object, error) {
429	storage.checkContext(ctx)
430	if id == "binary" {
431		return storage.stream, storage.errors["get"]
432	}
433	return storage.item.DeepCopy(), storage.errors["get"]
434}
435
436func (storage *SimpleRESTStorage) checkContext(ctx context.Context) {
437	storage.actualNamespace, storage.namespacePresent = request.NamespaceFrom(ctx)
438}
439
440func (storage *SimpleRESTStorage) Delete(ctx context.Context, id string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
441	storage.checkContext(ctx)
442	storage.deleted = id
443	storage.deleteOptions = options
444	if err := storage.errors["delete"]; err != nil {
445		return nil, false, err
446	}
447	if err := deleteValidation(ctx, &storage.item); err != nil {
448		return nil, false, err
449	}
450	var obj runtime.Object = &metav1.Status{Status: metav1.StatusSuccess}
451	var err error
452	if storage.injectedFunction != nil {
453		obj, err = storage.injectedFunction(&genericapitesting.Simple{ObjectMeta: metav1.ObjectMeta{Name: id}})
454	}
455	return obj, true, err
456}
457
458func (storage *SimpleRESTStorage) New() runtime.Object {
459	return &genericapitesting.Simple{}
460}
461
462func (storage *SimpleRESTStorage) NewList() runtime.Object {
463	return &genericapitesting.SimpleList{}
464}
465
466func (storage *SimpleRESTStorage) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
467	storage.checkContext(ctx)
468	storage.created = obj.(*genericapitesting.Simple)
469	if err := storage.errors["create"]; err != nil {
470		return nil, err
471	}
472	var err error
473	if storage.injectedFunction != nil {
474		obj, err = storage.injectedFunction(obj)
475	}
476	if err := createValidation(ctx, obj); err != nil {
477		return nil, err
478	}
479	return obj, err
480}
481
482func (storage *SimpleRESTStorage) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
483	storage.checkContext(ctx)
484	obj, err := objInfo.UpdatedObject(ctx, &storage.item)
485	if err != nil {
486		return nil, false, err
487	}
488	storage.updated = obj.(*genericapitesting.Simple)
489	if err := storage.errors["update"]; err != nil {
490		return nil, false, err
491	}
492	if storage.injectedFunction != nil {
493		obj, err = storage.injectedFunction(obj)
494	}
495	if err := updateValidation(ctx, &storage.item, obj); err != nil {
496		return nil, false, err
497	}
498	return obj, false, err
499}
500
501// Implement ResourceWatcher.
502func (storage *SimpleRESTStorage) Watch(ctx context.Context, options *metainternalversion.ListOptions) (watch.Interface, error) {
503	storage.lock.Lock()
504	defer storage.lock.Unlock()
505	storage.checkContext(ctx)
506	storage.requestedLabelSelector = labels.Everything()
507	if options != nil && options.LabelSelector != nil {
508		storage.requestedLabelSelector = options.LabelSelector
509	}
510	storage.requestedFieldSelector = fields.Everything()
511	if options != nil && options.FieldSelector != nil {
512		storage.requestedFieldSelector = options.FieldSelector
513	}
514	storage.requestedResourceVersion = ""
515	if options != nil {
516		storage.requestedResourceVersion = options.ResourceVersion
517	}
518	storage.requestedResourceNamespace = request.NamespaceValue(ctx)
519	if err := storage.errors["watch"]; err != nil {
520		return nil, err
521	}
522	storage.fakeWatch = watch.NewFake()
523	return storage.fakeWatch, nil
524}
525
526func (storage *SimpleRESTStorage) Watcher() *watch.FakeWatcher {
527	storage.lock.Lock()
528	defer storage.lock.Unlock()
529	return storage.fakeWatch
530}
531
532// Implement Connecter
533type ConnecterRESTStorage struct {
534	connectHandler http.Handler
535	handlerFunc    func() http.Handler
536
537	emptyConnectOptions    runtime.Object
538	receivedConnectOptions runtime.Object
539	receivedID             string
540	receivedResponder      rest.Responder
541	takesPath              string
542}
543
544// Implement Connecter
545var _ = rest.Connecter(&ConnecterRESTStorage{})
546
547func (s *ConnecterRESTStorage) New() runtime.Object {
548	return &genericapitesting.Simple{}
549}
550
551func (s *ConnecterRESTStorage) Connect(ctx context.Context, id string, options runtime.Object, responder rest.Responder) (http.Handler, error) {
552	s.receivedConnectOptions = options
553	s.receivedID = id
554	s.receivedResponder = responder
555	if s.handlerFunc != nil {
556		return s.handlerFunc(), nil
557	}
558	return s.connectHandler, nil
559}
560
561func (s *ConnecterRESTStorage) ConnectMethods() []string {
562	return []string{"GET", "POST", "PUT", "DELETE"}
563}
564
565func (s *ConnecterRESTStorage) NewConnectOptions() (runtime.Object, bool, string) {
566	if len(s.takesPath) > 0 {
567		return s.emptyConnectOptions, true, s.takesPath
568	}
569	return s.emptyConnectOptions, false, ""
570}
571
572type MetadataRESTStorage struct {
573	*SimpleRESTStorage
574	types []string
575}
576
577func (m *MetadataRESTStorage) ProducesMIMETypes(method string) []string {
578	return m.types
579}
580
581func (m *MetadataRESTStorage) ProducesObject(verb string) interface{} {
582	return nil
583}
584
585var _ rest.StorageMetadata = &MetadataRESTStorage{}
586
587type GetWithOptionsRESTStorage struct {
588	*SimpleRESTStorage
589	optionsReceived runtime.Object
590	takesPath       string
591}
592
593func (r *GetWithOptionsRESTStorage) Get(ctx context.Context, name string, options runtime.Object) (runtime.Object, error) {
594	if _, ok := options.(*genericapitesting.SimpleGetOptions); !ok {
595		return nil, fmt.Errorf("Unexpected options object: %#v", options)
596	}
597	r.optionsReceived = options
598	return r.SimpleRESTStorage.Get(ctx, name, &metav1.GetOptions{})
599}
600
601func (r *GetWithOptionsRESTStorage) NewGetOptions() (runtime.Object, bool, string) {
602	if len(r.takesPath) > 0 {
603		return &genericapitesting.SimpleGetOptions{}, true, r.takesPath
604	}
605	return &genericapitesting.SimpleGetOptions{}, false, ""
606}
607
608var _ rest.GetterWithOptions = &GetWithOptionsRESTStorage{}
609
610type GetWithOptionsRootRESTStorage struct {
611	*SimpleTypedStorage
612	optionsReceived runtime.Object
613	takesPath       string
614}
615
616func (r *GetWithOptionsRootRESTStorage) NamespaceScoped() bool {
617	return false
618}
619
620func (r *GetWithOptionsRootRESTStorage) Get(ctx context.Context, name string, options runtime.Object) (runtime.Object, error) {
621	if _, ok := options.(*genericapitesting.SimpleGetOptions); !ok {
622		return nil, fmt.Errorf("Unexpected options object: %#v", options)
623	}
624	r.optionsReceived = options
625	return r.SimpleTypedStorage.Get(ctx, name, &metav1.GetOptions{})
626}
627
628func (r *GetWithOptionsRootRESTStorage) NewGetOptions() (runtime.Object, bool, string) {
629	if len(r.takesPath) > 0 {
630		return &genericapitesting.SimpleGetOptions{}, true, r.takesPath
631	}
632	return &genericapitesting.SimpleGetOptions{}, false, ""
633}
634
635var _ rest.GetterWithOptions = &GetWithOptionsRootRESTStorage{}
636
637type NamedCreaterRESTStorage struct {
638	*SimpleRESTStorage
639	createdName string
640}
641
642func (storage *NamedCreaterRESTStorage) Create(ctx context.Context, name string, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
643	storage.checkContext(ctx)
644	storage.created = obj.(*genericapitesting.Simple)
645	storage.createdName = name
646	if err := storage.errors["create"]; err != nil {
647		return nil, err
648	}
649	var err error
650	if storage.injectedFunction != nil {
651		obj, err = storage.injectedFunction(obj)
652	}
653	if err := createValidation(ctx, obj); err != nil {
654		return nil, err
655	}
656	return obj, err
657}
658
659type SimpleTypedStorage struct {
660	errors   map[string]error
661	item     runtime.Object
662	baseType runtime.Object
663
664	actualNamespace  string
665	namespacePresent bool
666}
667
668func (storage *SimpleTypedStorage) New() runtime.Object {
669	return storage.baseType
670}
671
672func (storage *SimpleTypedStorage) Get(ctx context.Context, id string, options *metav1.GetOptions) (runtime.Object, error) {
673	storage.checkContext(ctx)
674	return storage.item.DeepCopyObject(), storage.errors["get"]
675}
676
677func (storage *SimpleTypedStorage) checkContext(ctx context.Context) {
678	storage.actualNamespace, storage.namespacePresent = request.NamespaceFrom(ctx)
679}
680
681func bodyOrDie(response *http.Response) string {
682	defer response.Body.Close()
683	body, err := ioutil.ReadAll(response.Body)
684	if err != nil {
685		panic(err)
686	}
687	return string(body)
688}
689
690func extractBody(response *http.Response, object runtime.Object) (string, error) {
691	return extractBodyDecoder(response, object, codec)
692}
693
694func extractBodyDecoder(response *http.Response, object runtime.Object, decoder runtime.Decoder) (string, error) {
695	defer response.Body.Close()
696	body, err := ioutil.ReadAll(response.Body)
697	if err != nil {
698		return string(body), err
699	}
700	return string(body), runtime.DecodeInto(decoder, body, object)
701}
702
703func extractBodyObject(response *http.Response, decoder runtime.Decoder) (runtime.Object, string, error) {
704	defer response.Body.Close()
705	body, err := ioutil.ReadAll(response.Body)
706	if err != nil {
707		return nil, string(body), err
708	}
709	obj, err := runtime.Decode(decoder, body)
710	return obj, string(body), err
711}
712
713func TestNotFound(t *testing.T) {
714	type T struct {
715		Method string
716		Path   string
717		Status int
718	}
719	cases := map[string]T{
720		// Positive checks to make sure everything is wired correctly
721		"groupless GET root":       {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusOK},
722		"groupless GET namespaced": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusOK},
723
724		"groupless GET long prefix": {"GET", "/" + grouplessPrefix + "/", http.StatusNotFound},
725
726		"groupless root PATCH method":                 {"PATCH", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
727		"groupless root GET missing storage":          {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/blah", http.StatusNotFound},
728		"groupless root GET with extra segment":       {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
729		"groupless root DELETE without extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
730		"groupless root DELETE with extra segment":    {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
731		"groupless root PUT without extra segment":    {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
732		"groupless root PUT with extra segment":       {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
733		"groupless root watch missing storage":        {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/", http.StatusInternalServerError},
734
735		"groupless namespaced PATCH method":                 {"PATCH", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
736		"groupless namespaced GET long prefix":              {"GET", "/" + grouplessPrefix + "/", http.StatusNotFound},
737		"groupless namespaced GET missing storage":          {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/blah", http.StatusNotFound},
738		"groupless namespaced GET with extra segment":       {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
739		"groupless namespaced POST with extra segment":      {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
740		"groupless namespaced DELETE without extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
741		"groupless namespaced DELETE with extra segment":    {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
742		"groupless namespaced PUT without extra segment":    {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
743		"groupless namespaced PUT with extra segment":       {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
744		"groupless namespaced watch missing storage":        {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/", http.StatusInternalServerError},
745		"groupless namespaced watch with bad method":        {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
746		"groupless namespaced watch param with bad method":  {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar?watch=true", http.StatusMethodNotAllowed},
747
748		// Positive checks to make sure everything is wired correctly
749		"GET root": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots", http.StatusOK},
750		// TODO: JTL: "GET root item":       {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar", http.StatusOK},
751		"GET namespaced": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusOK},
752		// TODO: JTL: "GET namespaced item": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar", http.StatusOK},
753
754		"GET long prefix": {"GET", "/" + prefix + "/", http.StatusNotFound},
755
756		"root PATCH method":           {"PATCH", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
757		"root GET missing storage":    {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/blah", http.StatusNotFound},
758		"root GET with extra segment": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
759		// TODO: JTL: "root POST with extra segment":      {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar", http.StatusMethodNotAllowed},
760		"root DELETE without extra segment": {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
761		"root DELETE with extra segment":    {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
762		"root PUT without extra segment":    {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
763		"root PUT with extra segment":       {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
764		"root watch missing storage":        {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/", http.StatusInternalServerError},
765		// TODO: JTL: "root watch with bad method":        {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/simpleroot/bar", http.StatusMethodNotAllowed},
766
767		"namespaced PATCH method":                 {"PATCH", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
768		"namespaced GET long prefix":              {"GET", "/" + prefix + "/", http.StatusNotFound},
769		"namespaced GET missing storage":          {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/blah", http.StatusNotFound},
770		"namespaced GET with extra segment":       {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
771		"namespaced POST with extra segment":      {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
772		"namespaced DELETE without extra segment": {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
773		"namespaced DELETE with extra segment":    {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
774		"namespaced PUT without extra segment":    {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
775		"namespaced PUT with extra segment":       {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
776		"namespaced watch missing storage":        {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/", http.StatusInternalServerError},
777		"namespaced watch with bad method":        {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
778		"namespaced watch param with bad method":  {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar?watch=true", http.StatusMethodNotAllowed},
779	}
780	handler := handle(map[string]rest.Storage{
781		"simples":     &SimpleRESTStorage{},
782		"simpleroots": &SimpleRESTStorage{},
783	})
784	server := httptest.NewServer(handler)
785	defer server.Close()
786	client := http.Client{}
787	for k, v := range cases {
788		request, err := http.NewRequest(v.Method, server.URL+v.Path, nil)
789		if err != nil {
790			t.Fatalf("unexpected error: %v", err)
791		}
792
793		response, err := client.Do(request)
794		if err != nil {
795			t.Errorf("unexpected error: %v", err)
796		}
797
798		if response.StatusCode != v.Status {
799			t.Errorf("Expected %d for %s (%s), Got %#v", v.Status, v.Method, k, response)
800		}
801	}
802}
803
804type UnimplementedRESTStorage struct{}
805
806func (UnimplementedRESTStorage) NamespaceScoped() bool {
807	return true
808}
809
810func (UnimplementedRESTStorage) New() runtime.Object {
811	return &genericapitesting.Simple{}
812}
813
814// TestUnimplementedRESTStorage ensures that if a rest.Storage does not implement a given
815// method, that it is literally not registered with the server.  In the past,
816// we registered everything, and returned method not supported if it didn't support
817// a verb.  Now we literally do not register a storage if it does not implement anything.
818// TODO: in future, we should update proxy/redirect
819func TestUnimplementedRESTStorage(t *testing.T) {
820	type T struct {
821		Method  string
822		Path    string
823		ErrCode int
824	}
825	cases := map[string]T{
826		"groupless GET object":    {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo/bar", http.StatusNotFound},
827		"groupless GET list":      {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo", http.StatusNotFound},
828		"groupless POST list":     {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo", http.StatusNotFound},
829		"groupless PUT object":    {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo/bar", http.StatusNotFound},
830		"groupless DELETE object": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo/bar", http.StatusNotFound},
831		"groupless watch list":    {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/foo", http.StatusNotFound},
832		"groupless watch object":  {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/foo/bar", http.StatusNotFound},
833		"groupless proxy object":  {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/proxy/foo/bar", http.StatusNotFound},
834
835		"GET object":    {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo/bar", http.StatusNotFound},
836		"GET list":      {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo", http.StatusNotFound},
837		"POST list":     {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo", http.StatusNotFound},
838		"PUT object":    {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo/bar", http.StatusNotFound},
839		"DELETE object": {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo/bar", http.StatusNotFound},
840		"watch list":    {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/foo", http.StatusNotFound},
841		"watch object":  {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/foo/bar", http.StatusNotFound},
842		"proxy object":  {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/proxy/foo/bar", http.StatusNotFound},
843	}
844	handler := handle(map[string]rest.Storage{
845		"foo": UnimplementedRESTStorage{},
846	})
847	server := httptest.NewServer(handler)
848	defer server.Close()
849	client := http.Client{}
850	for k, v := range cases {
851		request, err := http.NewRequest(v.Method, server.URL+v.Path, bytes.NewReader([]byte(`{"kind":"Simple","apiVersion":"version"}`)))
852		if err != nil {
853			t.Fatalf("unexpected error: %v", err)
854		}
855
856		response, err := client.Do(request)
857		if err != nil {
858			t.Fatalf("unexpected error: %v", err)
859		}
860		defer response.Body.Close()
861		data, err := ioutil.ReadAll(response.Body)
862		if err != nil {
863			t.Fatalf("unexpected error: %v", err)
864		}
865		if response.StatusCode != v.ErrCode {
866			t.Errorf("%s: expected %d for %s, Got %s", k, v.ErrCode, v.Method, string(data))
867			continue
868		}
869	}
870}
871
872type OnlyGetRESTStorage struct {
873	UnimplementedRESTStorage
874}
875
876func (OnlyGetRESTStorage) Get(ctx context.Context, id string, options *metav1.GetOptions) (runtime.Object, error) {
877	return nil, nil
878}
879
880func (OnlyGetRESTStorage) NewList() runtime.Object {
881	return &genericapitesting.SimpleList{}
882}
883
884func (OnlyGetRESTStorage) List(ctx context.Context, options *metainternalversion.ListOptions) (runtime.Object, error) {
885	return nil, nil
886}
887
888func (OnlyGetRESTStorage) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
889	return nil, nil
890}
891
892// TestSomeUnimplementedRESTStorage ensures that if a rest.Storage does
893// not implement a given method, that it is literally not registered
894// with the server. We need to have at least one verb supported inorder
895// to get a MethodNotAllowed rather than NotFound error.
896func TestSomeUnimplementedRESTStorage(t *testing.T) {
897	type T struct {
898		Method  string
899		Path    string
900		ErrCode int
901	}
902
903	cases := map[string]T{
904		"groupless POST list":         {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/foo", http.StatusMethodNotAllowed},
905		"groupless PUT object":        {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/foo/bar", http.StatusMethodNotAllowed},
906		"groupless DELETE object":     {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/foo/bar", http.StatusMethodNotAllowed},
907		"groupless DELETE collection": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/foo", http.StatusMethodNotAllowed},
908		"POST list":                   {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo", http.StatusMethodNotAllowed},
909		"PUT object":                  {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo/bar", http.StatusMethodNotAllowed},
910		"DELETE object":               {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo/bar", http.StatusMethodNotAllowed},
911		"DELETE collection":           {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo", http.StatusMethodNotAllowed},
912	}
913	handler := handle(map[string]rest.Storage{
914		"foo": OnlyGetRESTStorage{},
915	})
916	server := httptest.NewServer(handler)
917	defer server.Close()
918	client := http.Client{}
919	for k, v := range cases {
920		request, err := http.NewRequest(v.Method, server.URL+v.Path, bytes.NewReader([]byte(`{"kind":"Simple","apiVersion":"version"}`)))
921		if err != nil {
922			t.Fatalf("unexpected error: %v", err)
923		}
924
925		response, err := client.Do(request)
926		if err != nil {
927			t.Fatalf("unexpected error: %v", err)
928		}
929		defer response.Body.Close()
930		data, err := ioutil.ReadAll(response.Body)
931		if err != nil {
932			t.Fatalf("unexpected error: %v", err)
933		}
934		if response.StatusCode != v.ErrCode {
935			t.Errorf("%s: expected %d for %s, Got %s", k, v.ErrCode, v.Method, string(data))
936			continue
937		}
938	}
939}
940
941func TestList(t *testing.T) {
942	testCases := []struct {
943		url       string
944		namespace string
945		selfLink  string
946		legacy    bool
947		label     string
948		field     string
949	}{
950		// Groupless API
951
952		// legacy namespace param is ignored
953		{
954			url:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple?namespace=",
955			namespace: "",
956			selfLink:  "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
957			legacy:    true,
958		},
959		{
960			url:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple?namespace=other",
961			namespace: "",
962			selfLink:  "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
963			legacy:    true,
964		},
965		{
966			url:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple?namespace=other&labelSelector=a%3Db&fieldSelector=c%3Dd",
967			namespace: "",
968			selfLink:  "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
969			legacy:    true,
970			label:     "a=b",
971			field:     "c=d",
972		},
973		// legacy api version is honored
974		{
975			url:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
976			namespace: "",
977			selfLink:  "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
978			legacy:    true,
979		},
980		{
981			url:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
982			namespace: "other",
983			selfLink:  "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
984			legacy:    true,
985		},
986		{
987			url:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
988			namespace: "other",
989			selfLink:  "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
990			legacy:    true,
991			label:     "a=b",
992			field:     "c=d",
993		},
994		// list items across all namespaces
995		{
996			url:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
997			namespace: "",
998			selfLink:  "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
999			legacy:    true,
1000		},
1001		// list items in a namespace in the path
1002		{
1003			url:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/simple",
1004			namespace: "default",
1005			selfLink:  "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/simple",
1006		},
1007		{
1008			url:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
1009			namespace: "other",
1010			selfLink:  "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
1011		},
1012		{
1013			url:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
1014			namespace: "other",
1015			selfLink:  "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
1016			label:     "a=b",
1017			field:     "c=d",
1018		},
1019		// list items across all namespaces
1020		{
1021			url:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
1022			namespace: "",
1023			selfLink:  "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
1024		},
1025
1026		// Group API
1027
1028		// legacy namespace param is ignored
1029		{
1030			url:       "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple?namespace=",
1031			namespace: "",
1032			selfLink:  "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
1033			legacy:    true,
1034		},
1035		{
1036			url:       "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple?namespace=other",
1037			namespace: "",
1038			selfLink:  "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
1039			legacy:    true,
1040		},
1041		{
1042			url:       "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple?namespace=other&labelSelector=a%3Db&fieldSelector=c%3Dd",
1043			namespace: "",
1044			selfLink:  "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
1045			legacy:    true,
1046			label:     "a=b",
1047			field:     "c=d",
1048		},
1049		// legacy api version is honored
1050		{
1051			url:       "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
1052			namespace: "",
1053			selfLink:  "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
1054			legacy:    true,
1055		},
1056		{
1057			url:       "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/simple",
1058			namespace: "other",
1059			selfLink:  "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/simple",
1060			legacy:    true,
1061		},
1062		{
1063			url:       "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
1064			namespace: "other",
1065			selfLink:  "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/simple",
1066			legacy:    true,
1067			label:     "a=b",
1068			field:     "c=d",
1069		},
1070		// list items across all namespaces
1071		{
1072			url:       "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
1073			namespace: "",
1074			selfLink:  "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
1075			legacy:    true,
1076		},
1077		// list items in a namespace in the path
1078		{
1079			url:       "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/default/simple",
1080			namespace: "default",
1081			selfLink:  "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/default/simple",
1082		},
1083		{
1084			url:       "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/other/simple",
1085			namespace: "other",
1086			selfLink:  "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/other/simple",
1087		},
1088		{
1089			url:       "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
1090			namespace: "other",
1091			selfLink:  "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/other/simple",
1092			label:     "a=b",
1093			field:     "c=d",
1094		},
1095		// list items across all namespaces
1096		{
1097			url:       "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/simple",
1098			namespace: "",
1099			selfLink:  "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/simple",
1100		},
1101	}
1102	for i, testCase := range testCases {
1103		storage := map[string]rest.Storage{}
1104		simpleStorage := SimpleRESTStorage{expectedResourceNamespace: testCase.namespace}
1105		storage["simple"] = &simpleStorage
1106		selfLinker := &setTestSelfLinker{
1107			t:           t,
1108			namespace:   testCase.namespace,
1109			expectedSet: testCase.selfLink,
1110		}
1111		var handler = handleInternal(storage, admissionControl, selfLinker, nil)
1112		server := httptest.NewServer(handler)
1113		defer server.Close()
1114
1115		resp, err := http.Get(server.URL + testCase.url)
1116		if err != nil {
1117			t.Errorf("%d: unexpected error: %v", i, err)
1118			continue
1119		}
1120		defer resp.Body.Close()
1121		if resp.StatusCode != http.StatusOK {
1122			t.Errorf("%d: unexpected status: %d from url %s, Expected: %d, %#v", i, resp.StatusCode, testCase.url, http.StatusOK, resp)
1123			body, err := ioutil.ReadAll(resp.Body)
1124			if err != nil {
1125				t.Errorf("%d: unexpected error: %v", i, err)
1126				continue
1127			}
1128			t.Logf("%d: body: %s", i, string(body))
1129			continue
1130		}
1131		if utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) == selfLinker.called {
1132			t.Errorf("%d: unexpected selfLinker.called: %v", i, selfLinker.called)
1133		}
1134		if !simpleStorage.namespacePresent {
1135			t.Errorf("%d: namespace not set", i)
1136		} else if simpleStorage.actualNamespace != testCase.namespace {
1137			t.Errorf("%d: %q unexpected resource namespace: %s", i, testCase.url, simpleStorage.actualNamespace)
1138		}
1139		if simpleStorage.requestedLabelSelector == nil || simpleStorage.requestedLabelSelector.String() != testCase.label {
1140			t.Errorf("%d: unexpected label selector: expected=%v got=%v", i, testCase.label, simpleStorage.requestedLabelSelector)
1141		}
1142		if simpleStorage.requestedFieldSelector == nil || simpleStorage.requestedFieldSelector.String() != testCase.field {
1143			t.Errorf("%d: unexpected field selector: expected=%v got=%v", i, testCase.field, simpleStorage.requestedFieldSelector)
1144		}
1145	}
1146}
1147
1148func TestRequestsWithInvalidQuery(t *testing.T) {
1149	storage := map[string]rest.Storage{}
1150
1151	storage["simple"] = &SimpleRESTStorage{expectedResourceNamespace: "default"}
1152	storage["withoptions"] = GetWithOptionsRESTStorage{}
1153
1154	var handler = handleInternal(storage, admissionControl, selfLinker, nil)
1155	server := httptest.NewServer(handler)
1156	defer server.Close()
1157
1158	for i, test := range []struct {
1159		postfix string
1160		method  string
1161	}{
1162		{"/simple?labelSelector=<invalid>", http.MethodGet},
1163		{"/simple/foo?gracePeriodSeconds=<invalid>", http.MethodDelete},
1164		// {"/simple?labelSelector=<value>", http.MethodDelete}, TODO: implement DeleteCollection in  SimpleRESTStorage
1165		// {"/simple/foo?export=<invalid>", http.MethodGet}, TODO: there is no invalid bool in conversion. Should we be more strict?
1166		// {"/simple/foo?resourceVersion=<invalid>", http.MethodGet}, TODO: there is no invalid resourceVersion. Should we be more strict?
1167		// {"/withoptions?labelSelector=<invalid>", http.MethodGet}, TODO: SimpleGetOptions is always valid. Add more validation that can fail.
1168	} {
1169		baseURL := server.URL + "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default"
1170		url := baseURL + test.postfix
1171		r, err := http.NewRequest(test.method, url, nil)
1172		if err != nil {
1173			t.Errorf("%d: unexpected error: %v", i, err)
1174			continue
1175		}
1176		resp, err := http.DefaultClient.Do(r)
1177		if err != nil {
1178			t.Errorf("%d: unexpected error: %v", i, err)
1179			continue
1180		}
1181		defer resp.Body.Close()
1182		if resp.StatusCode != http.StatusBadRequest {
1183			t.Errorf("%d: unexpected status: %d from url %s, Expected: %d, %#v", i, resp.StatusCode, url, http.StatusBadRequest, resp)
1184			body, err := ioutil.ReadAll(resp.Body)
1185			if err != nil {
1186				t.Errorf("%d: unexpected error: %v", i, err)
1187				continue
1188			}
1189			t.Logf("%d: body: %s", i, string(body))
1190		}
1191	}
1192}
1193
1194func TestListCompression(t *testing.T) {
1195	testCases := []struct {
1196		url            string
1197		namespace      string
1198		selfLink       string
1199		legacy         bool
1200		label          string
1201		field          string
1202		acceptEncoding string
1203	}{
1204		// list items in a namespace in the path
1205		{
1206			url:            "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/simple",
1207			namespace:      "default",
1208			selfLink:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/simple",
1209			acceptEncoding: "",
1210		},
1211		{
1212			url:            "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/simple",
1213			namespace:      "default",
1214			selfLink:       "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/simple",
1215			acceptEncoding: "gzip",
1216		},
1217	}
1218	for i, testCase := range testCases {
1219		storage := map[string]rest.Storage{}
1220		simpleStorage := SimpleRESTStorage{
1221			expectedResourceNamespace: testCase.namespace,
1222			list: []genericapitesting.Simple{
1223				{Other: strings.Repeat("0123456789abcdef", (128*1024/16)+1)},
1224			},
1225		}
1226		storage["simple"] = &simpleStorage
1227		selfLinker := &setTestSelfLinker{
1228			t:           t,
1229			namespace:   testCase.namespace,
1230			expectedSet: testCase.selfLink,
1231		}
1232		var handler = handleInternal(storage, admissionControl, selfLinker, nil)
1233
1234		handler = genericapifilters.WithRequestInfo(handler, newTestRequestInfoResolver())
1235
1236		server := httptest.NewServer(handler)
1237
1238		defer server.Close()
1239
1240		req, err := http.NewRequest("GET", server.URL+testCase.url, nil)
1241		if err != nil {
1242			t.Errorf("%d: unexpected error: %v", i, err)
1243			continue
1244		}
1245		// It's necessary to manually set Accept-Encoding here
1246		// to prevent http.DefaultClient from automatically
1247		// decoding responses
1248		req.Header.Set("Accept-Encoding", testCase.acceptEncoding)
1249		resp, err := http.DefaultClient.Do(req)
1250		if err != nil {
1251			t.Errorf("%d: unexpected error: %v", i, err)
1252			continue
1253		}
1254		defer resp.Body.Close()
1255		if resp.StatusCode != http.StatusOK {
1256			t.Errorf("%d: unexpected status: %d from url %s, Expected: %d, %#v", i, resp.StatusCode, testCase.url, http.StatusOK, resp)
1257			body, err := ioutil.ReadAll(resp.Body)
1258			if err != nil {
1259				t.Errorf("%d: unexpected error: %v", i, err)
1260				continue
1261			}
1262			t.Logf("%d: body: %s", i, string(body))
1263			continue
1264		}
1265		if utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) == selfLinker.called {
1266			t.Errorf("%d: unexpected selfLinker.called: %v", i, selfLinker.called)
1267		}
1268		if !simpleStorage.namespacePresent {
1269			t.Errorf("%d: namespace not set", i)
1270		} else if simpleStorage.actualNamespace != testCase.namespace {
1271			t.Errorf("%d: %q unexpected resource namespace: %s", i, testCase.url, simpleStorage.actualNamespace)
1272		}
1273		if simpleStorage.requestedLabelSelector == nil || simpleStorage.requestedLabelSelector.String() != testCase.label {
1274			t.Errorf("%d: unexpected label selector: %v", i, simpleStorage.requestedLabelSelector)
1275		}
1276		if simpleStorage.requestedFieldSelector == nil || simpleStorage.requestedFieldSelector.String() != testCase.field {
1277			t.Errorf("%d: unexpected field selector: %v", i, simpleStorage.requestedFieldSelector)
1278		}
1279
1280		var decoder *json.Decoder
1281		if testCase.acceptEncoding == "gzip" {
1282			gzipReader, err := gzip.NewReader(resp.Body)
1283			if err != nil {
1284				t.Fatalf("unexpected error creating gzip reader: %v", err)
1285			}
1286			decoder = json.NewDecoder(gzipReader)
1287		} else {
1288			decoder = json.NewDecoder(resp.Body)
1289		}
1290		var itemOut genericapitesting.SimpleList
1291		err = decoder.Decode(&itemOut)
1292		if err != nil {
1293			t.Errorf("failed to read response body as SimpleList: %v", err)
1294		}
1295	}
1296}
1297
1298func TestLogs(t *testing.T) {
1299	handler := handle(map[string]rest.Storage{})
1300	server := httptest.NewServer(handler)
1301	defer server.Close()
1302	client := http.Client{}
1303
1304	request, err := http.NewRequest("GET", server.URL+"/logs", nil)
1305	if err != nil {
1306		t.Errorf("unexpected error: %v", err)
1307	}
1308
1309	response, err := client.Do(request)
1310	if err != nil {
1311		t.Errorf("unexpected error: %v", err)
1312	}
1313
1314	body, err := ioutil.ReadAll(response.Body)
1315	if err != nil {
1316		t.Fatalf("unexpected error: %v", err)
1317	}
1318	t.Logf("Data: %s", string(body))
1319}
1320
1321func TestErrorList(t *testing.T) {
1322	storage := map[string]rest.Storage{}
1323	simpleStorage := SimpleRESTStorage{
1324		errors: map[string]error{"list": fmt.Errorf("test Error")},
1325	}
1326	storage["simple"] = &simpleStorage
1327	handler := handle(storage)
1328	server := httptest.NewServer(handler)
1329	defer server.Close()
1330
1331	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple")
1332	if err != nil {
1333		t.Fatalf("unexpected error: %v", err)
1334	}
1335
1336	if resp.StatusCode != http.StatusInternalServerError {
1337		t.Errorf("Unexpected status: %d, Expected: %d, %#v", resp.StatusCode, http.StatusInternalServerError, resp)
1338	}
1339}
1340
1341func TestNonEmptyList(t *testing.T) {
1342	storage := map[string]rest.Storage{}
1343	simpleStorage := SimpleRESTStorage{
1344		list: []genericapitesting.Simple{
1345			{
1346				ObjectMeta: metav1.ObjectMeta{Name: "something", Namespace: "other"},
1347				Other:      "foo",
1348			},
1349		},
1350	}
1351	storage["simple"] = &simpleStorage
1352	handler := handle(storage)
1353	server := httptest.NewServer(handler)
1354	defer server.Close()
1355
1356	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple")
1357	if err != nil {
1358		t.Fatalf("unexpected error: %v", err)
1359	}
1360
1361	if resp.StatusCode != http.StatusOK {
1362		t.Errorf("Unexpected status: %d, Expected: %d, %#v", resp.StatusCode, http.StatusOK, resp)
1363		body, err := ioutil.ReadAll(resp.Body)
1364		if err != nil {
1365			t.Fatalf("unexpected error: %v", err)
1366		}
1367		t.Logf("Data: %s", string(body))
1368	}
1369
1370	var listOut genericapitesting.SimpleList
1371	body, err := extractBody(resp, &listOut)
1372	if err != nil {
1373		t.Fatalf("unexpected error: %v", err)
1374	}
1375	t.Log(body)
1376
1377	if len(listOut.Items) != 1 {
1378		t.Errorf("Unexpected response: %#v", listOut)
1379		return
1380	}
1381	if listOut.Items[0].Other != simpleStorage.list[0].Other {
1382		t.Errorf("Unexpected data: %#v, %s", listOut.Items[0], string(body))
1383	}
1384	if !utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) {
1385		if listOut.SelfLink != "/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/simple" {
1386			t.Errorf("unexpected list self link: %#v", listOut)
1387		}
1388		expectedSelfLink := "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/simple/something"
1389		if listOut.Items[0].ObjectMeta.SelfLink != expectedSelfLink {
1390			t.Errorf("Unexpected data: %#v, %s", listOut.Items[0].ObjectMeta.SelfLink, expectedSelfLink)
1391		}
1392	}
1393}
1394
1395func TestSelfLinkSkipsEmptyName(t *testing.T) {
1396	storage := map[string]rest.Storage{}
1397	simpleStorage := SimpleRESTStorage{
1398		list: []genericapitesting.Simple{
1399			{
1400				ObjectMeta: metav1.ObjectMeta{Namespace: "other"},
1401				Other:      "foo",
1402			},
1403		},
1404	}
1405	storage["simple"] = &simpleStorage
1406	handler := handle(storage)
1407	server := httptest.NewServer(handler)
1408	defer server.Close()
1409
1410	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple")
1411	if err != nil {
1412		t.Fatalf("unexpected error: %v", err)
1413	}
1414
1415	if resp.StatusCode != http.StatusOK {
1416		t.Errorf("Unexpected status: %d, Expected: %d, %#v", resp.StatusCode, http.StatusOK, resp)
1417		body, err := ioutil.ReadAll(resp.Body)
1418		if err != nil {
1419			t.Fatalf("unexpected error: %v", err)
1420		}
1421		t.Logf("Data: %s", string(body))
1422	}
1423	var listOut genericapitesting.SimpleList
1424	body, err := extractBody(resp, &listOut)
1425	if err != nil {
1426		t.Fatalf("unexpected error: %v", err)
1427	}
1428
1429	if len(listOut.Items) != 1 {
1430		t.Errorf("Unexpected response: %#v", listOut)
1431		return
1432	}
1433	if listOut.Items[0].Other != simpleStorage.list[0].Other {
1434		t.Errorf("Unexpected data: %#v, %s", listOut.Items[0], string(body))
1435	}
1436	if !utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) {
1437		if listOut.SelfLink != "/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/simple" {
1438			t.Errorf("unexpected list self link: %#v", listOut)
1439		}
1440		expectedSelfLink := ""
1441		if listOut.Items[0].ObjectMeta.SelfLink != expectedSelfLink {
1442			t.Errorf("Unexpected data: %#v, %s", listOut.Items[0].ObjectMeta.SelfLink, expectedSelfLink)
1443		}
1444	}
1445}
1446
1447func TestRootSelfLink(t *testing.T) {
1448	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.RemoveSelfLink, false)()
1449
1450	storage := map[string]rest.Storage{}
1451	simpleStorage := GetWithOptionsRootRESTStorage{
1452		SimpleTypedStorage: &SimpleTypedStorage{
1453			baseType: &genericapitesting.SimpleRoot{}, // a root scoped type
1454			item: &genericapitesting.SimpleRoot{
1455				ObjectMeta: metav1.ObjectMeta{Name: "foo"},
1456				Other:      "foo",
1457			},
1458		},
1459		takesPath: "atAPath",
1460	}
1461	storage["simple"] = &simpleStorage
1462	storage["simple/sub"] = &simpleStorage
1463	handler := handle(storage)
1464	server := httptest.NewServer(handler)
1465	defer server.Close()
1466
1467	testCases := []struct {
1468		url      string
1469		selfLink string
1470	}{
1471		{
1472			url:      server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple/foo",
1473			selfLink: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple/foo",
1474		},
1475		{
1476			url:      server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple/foo/sub",
1477			selfLink: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple/foo/sub",
1478		},
1479	}
1480
1481	for _, test := range testCases {
1482		resp, err := http.Get(test.url)
1483		if err != nil {
1484			t.Fatalf("unexpected error: %v", err)
1485		}
1486
1487		if resp.StatusCode != http.StatusOK {
1488			t.Errorf("Unexpected status: %d, Expected: %d, %#v", resp.StatusCode, http.StatusOK, resp)
1489			body, err := ioutil.ReadAll(resp.Body)
1490			if err != nil {
1491				t.Fatalf("unexpected error: %v", err)
1492			}
1493			t.Logf("Data: %s", string(body))
1494		}
1495		var out genericapitesting.SimpleRoot
1496		if _, err := extractBody(resp, &out); err != nil {
1497			t.Fatalf("unexpected error: %v", err)
1498		}
1499
1500		if out.SelfLink != test.selfLink {
1501			t.Errorf("unexpected self link: %#v", out.SelfLink)
1502		}
1503	}
1504}
1505
1506func TestMetadata(t *testing.T) {
1507	simpleStorage := &MetadataRESTStorage{&SimpleRESTStorage{}, []string{"text/plain"}}
1508	h := handle(map[string]rest.Storage{"simple": simpleStorage})
1509	ws := h.(*defaultAPIServer).container.RegisteredWebServices()
1510	if len(ws) == 0 {
1511		t.Fatal("no web services registered")
1512	}
1513	matches := map[string]int{}
1514	for _, w := range ws {
1515		for _, r := range w.Routes() {
1516			t.Logf("%v %v %#v", r.Method, r.Path, r.Produces)
1517			s := strings.Join(r.Produces, ",")
1518			i := matches[s]
1519			matches[s] = i + 1
1520		}
1521	}
1522	cs := []func() bool{
1523		func() bool {
1524			return matches["text/plain,application/json,application/yaml,application/vnd.kubernetes.protobuf"] == 0
1525		},
1526		func() bool {
1527			return matches["application/json,application/yaml,application/vnd.kubernetes.protobuf,application/json;stream=watch,application/vnd.kubernetes.protobuf;stream=watch"] == 0
1528		},
1529		func() bool {
1530			return matches["application/json,application/yaml,application/vnd.kubernetes.protobuf"] == 0
1531		},
1532		func() bool {
1533			return len(matches) != 4
1534		},
1535	}
1536	for i, c := range cs {
1537		if c() {
1538			t.Errorf("[%d]unexpected mime types: %#v", i, matches)
1539		}
1540	}
1541}
1542
1543func TestGet(t *testing.T) {
1544	storage := map[string]rest.Storage{}
1545	simpleStorage := SimpleRESTStorage{
1546		item: genericapitesting.Simple{
1547			Other: "foo",
1548		},
1549	}
1550	selfLinker := &setTestSelfLinker{
1551		t:           t,
1552		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id",
1553		name:        "id",
1554		namespace:   "default",
1555	}
1556	storage["simple"] = &simpleStorage
1557	handler := handleLinker(storage, selfLinker)
1558	server := httptest.NewServer(handler)
1559	defer server.Close()
1560
1561	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id")
1562	if err != nil {
1563		t.Fatalf("unexpected error: %v", err)
1564	}
1565	if resp.StatusCode != http.StatusOK {
1566		t.Fatalf("unexpected response: %#v", resp)
1567	}
1568	var itemOut genericapitesting.Simple
1569	body, err := extractBody(resp, &itemOut)
1570	if err != nil {
1571		t.Errorf("unexpected error: %v", err)
1572	}
1573
1574	if itemOut.Name != simpleStorage.item.Name {
1575		t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
1576	}
1577	if utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) == selfLinker.called {
1578		t.Errorf("unexpected selfLinker.called: %v", selfLinker.called)
1579	}
1580}
1581
1582func BenchmarkGet(b *testing.B) {
1583	storage := map[string]rest.Storage{}
1584	simpleStorage := SimpleRESTStorage{
1585		item: genericapitesting.Simple{
1586			Other: "foo",
1587		},
1588	}
1589	selfLinker := &setTestSelfLinker{
1590		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id",
1591		name:        "id",
1592		namespace:   "default",
1593	}
1594	storage["simple"] = &simpleStorage
1595	handler := handleLinker(storage, selfLinker)
1596	server := httptest.NewServer(handler)
1597	defer server.Close()
1598
1599	u := server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id"
1600
1601	b.ResetTimer()
1602	for i := 0; i < b.N; i++ {
1603		resp, err := http.Get(u)
1604		if err != nil {
1605			b.Fatalf("unexpected error: %v", err)
1606		}
1607		if resp.StatusCode != http.StatusOK {
1608			b.Fatalf("unexpected response: %#v", resp)
1609		}
1610		if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
1611			b.Fatalf("unable to read body")
1612		}
1613	}
1614	b.StopTimer()
1615}
1616
1617func BenchmarkGetNoCompression(b *testing.B) {
1618	storage := map[string]rest.Storage{}
1619	simpleStorage := SimpleRESTStorage{
1620		item: genericapitesting.Simple{
1621			Other: "foo",
1622		},
1623	}
1624	selfLinker := &setTestSelfLinker{
1625		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id",
1626		name:        "id",
1627		namespace:   "default",
1628	}
1629	storage["simple"] = &simpleStorage
1630	handler := handleLinker(storage, selfLinker)
1631	server := httptest.NewServer(handler)
1632	defer server.Close()
1633
1634	client := &http.Client{
1635		Transport: &http.Transport{
1636			DisableCompression: true,
1637		},
1638	}
1639
1640	u := server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id"
1641
1642	b.ResetTimer()
1643	for i := 0; i < b.N; i++ {
1644		resp, err := client.Get(u)
1645		if err != nil {
1646			b.Fatalf("unexpected error: %v", err)
1647		}
1648		if resp.StatusCode != http.StatusOK {
1649			b.Fatalf("unexpected response: %#v", resp)
1650		}
1651		if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
1652			b.Fatalf("unable to read body")
1653		}
1654	}
1655	b.StopTimer()
1656}
1657
1658func TestGetCompression(t *testing.T) {
1659	storage := map[string]rest.Storage{}
1660	simpleStorage := SimpleRESTStorage{
1661		item: genericapitesting.Simple{
1662			Other: strings.Repeat("0123456789abcdef", (128*1024/16)+1),
1663		},
1664	}
1665	selfLinker := &setTestSelfLinker{
1666		t:           t,
1667		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id",
1668		name:        "id",
1669		namespace:   "default",
1670	}
1671
1672	storage["simple"] = &simpleStorage
1673	handler := handleLinker(storage, selfLinker)
1674	handler = genericapifilters.WithRequestInfo(handler, newTestRequestInfoResolver())
1675	server := httptest.NewServer(handler)
1676	defer server.Close()
1677
1678	tests := []struct {
1679		acceptEncoding string
1680	}{
1681		{acceptEncoding: ""},
1682		{acceptEncoding: "gzip"},
1683	}
1684
1685	for _, test := range tests {
1686		req, err := http.NewRequest("GET", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/id", nil)
1687		if err != nil {
1688			t.Fatalf("unexpected error creating request: %v", err)
1689		}
1690		// It's necessary to manually set Accept-Encoding here
1691		// to prevent http.DefaultClient from automatically
1692		// decoding responses
1693		req.Header.Set("Accept-Encoding", test.acceptEncoding)
1694		resp, err := http.DefaultClient.Do(req)
1695		if err != nil {
1696			t.Fatalf("unexpected error: %v", err)
1697		}
1698		if resp.StatusCode != http.StatusOK {
1699			t.Fatalf("unexpected response: %#v", resp)
1700		}
1701		var decoder *json.Decoder
1702		if test.acceptEncoding == "gzip" {
1703			gzipReader, err := gzip.NewReader(resp.Body)
1704			if err != nil {
1705				t.Fatalf("unexpected error creating gzip reader: %v", err)
1706			}
1707			decoder = json.NewDecoder(gzipReader)
1708		} else {
1709			decoder = json.NewDecoder(resp.Body)
1710		}
1711		var itemOut genericapitesting.Simple
1712		err = decoder.Decode(&itemOut)
1713		if err != nil {
1714			t.Errorf("unexpected error: %v", err)
1715		}
1716		body, err := ioutil.ReadAll(resp.Body)
1717		if err != nil {
1718			t.Errorf("unexpected error reading body: %v", err)
1719		}
1720
1721		if itemOut.Name != simpleStorage.item.Name {
1722			t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
1723		}
1724		if utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) == selfLinker.called {
1725			t.Errorf("unexpected selfLinker.called: %v", selfLinker.called)
1726		}
1727	}
1728}
1729
1730func TestGetPretty(t *testing.T) {
1731	storage := map[string]rest.Storage{}
1732	simpleStorage := SimpleRESTStorage{
1733		item: genericapitesting.Simple{
1734			Other: "foo",
1735		},
1736	}
1737	selfLinker := &setTestSelfLinker{
1738		t:           t,
1739		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id",
1740		name:        "id",
1741		namespace:   "default",
1742	}
1743	storage["simple"] = &simpleStorage
1744	handler := handleLinker(storage, selfLinker)
1745	server := httptest.NewServer(handler)
1746	defer server.Close()
1747
1748	tests := []struct {
1749		accept    string
1750		userAgent string
1751		params    url.Values
1752		pretty    bool
1753	}{
1754		{accept: runtime.ContentTypeJSON},
1755		{accept: "application/json;pretty=0"},
1756		{accept: runtime.ContentTypeJSON, userAgent: "kubectl"},
1757		{accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"0"}}},
1758
1759		{pretty: true, accept: runtime.ContentTypeJSON, userAgent: "curl"},
1760		{pretty: true, accept: runtime.ContentTypeJSON, userAgent: "Mozilla/5.0"},
1761		{pretty: true, accept: runtime.ContentTypeJSON, userAgent: "Wget"},
1762		{pretty: true, accept: "application/json;pretty=1"},
1763		{pretty: true, accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"1"}}},
1764		{pretty: true, accept: runtime.ContentTypeJSON, params: url.Values{"pretty": {"true"}}},
1765	}
1766	for i, test := range tests {
1767		u, err := url.Parse(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id")
1768		if err != nil {
1769			t.Fatal(err)
1770		}
1771		u.RawQuery = test.params.Encode()
1772		req := &http.Request{Method: "GET", URL: u}
1773		req.Header = http.Header{}
1774		req.Header.Set("Accept", test.accept)
1775		req.Header.Set("User-Agent", test.userAgent)
1776		resp, err := http.DefaultClient.Do(req)
1777		if err != nil {
1778			t.Fatal(err)
1779		}
1780		if resp.StatusCode != http.StatusOK {
1781			t.Fatal(err)
1782		}
1783		var itemOut genericapitesting.Simple
1784		body, err := extractBody(resp, &itemOut)
1785		if err != nil {
1786			t.Fatal(err)
1787		}
1788		// to get stable ordering we need to use a go type
1789		unstructured := genericapitesting.Simple{}
1790		if err := json.Unmarshal([]byte(body), &unstructured); err != nil {
1791			t.Fatal(err)
1792		}
1793		var expect string
1794		if test.pretty {
1795			out, err := json.MarshalIndent(unstructured, "", "  ")
1796			if err != nil {
1797				t.Fatal(err)
1798			}
1799			expect = string(out)
1800		} else {
1801			out, err := json.Marshal(unstructured)
1802			if err != nil {
1803				t.Fatal(err)
1804			}
1805			expect = string(out) + "\n"
1806		}
1807		if expect != body {
1808			t.Errorf("%d: body did not match expected:\n%s\n%s", i, body, expect)
1809		}
1810	}
1811}
1812
1813func TestGetTable(t *testing.T) {
1814	now := metav1.Now()
1815	obj := genericapitesting.Simple{
1816		ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "ns1", ResourceVersion: "10", SelfLink: "/blah", CreationTimestamp: now, UID: types.UID("abcdef0123")},
1817		Other:      "foo",
1818	}
1819
1820	m, err := meta.Accessor(&obj)
1821	if err != nil {
1822		t.Fatal(err)
1823	}
1824	var encodedV1Beta1Body []byte
1825	{
1826		partial := meta.AsPartialObjectMetadata(m)
1827		partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata"))
1828		encodedBody, err := runtime.Encode(metainternalversionscheme.Codecs.LegacyCodec(metav1beta1.SchemeGroupVersion), partial)
1829		if err != nil {
1830			t.Fatal(err)
1831		}
1832		// the codec includes a trailing newline that is not present during decode
1833		encodedV1Beta1Body = bytes.TrimSpace(encodedBody)
1834	}
1835	var encodedV1Body []byte
1836	{
1837		partial := meta.AsPartialObjectMetadata(m)
1838		partial.GetObjectKind().SetGroupVersionKind(metav1.SchemeGroupVersion.WithKind("PartialObjectMetadata"))
1839		encodedBody, err := runtime.Encode(metainternalversionscheme.Codecs.LegacyCodec(metav1.SchemeGroupVersion), partial)
1840		if err != nil {
1841			t.Fatal(err)
1842		}
1843		// the codec includes a trailing newline that is not present during decode
1844		encodedV1Body = bytes.TrimSpace(encodedBody)
1845	}
1846
1847	metaDoc := metav1.ObjectMeta{}.SwaggerDoc()
1848
1849	tests := []struct {
1850		accept     string
1851		params     url.Values
1852		pretty     bool
1853		expected   *metav1.Table
1854		statusCode int
1855		item       bool
1856	}{
1857		{
1858			accept:     "application/json;as=Table;v=v1alpha1;g=meta.k8s.io",
1859			statusCode: http.StatusNotAcceptable,
1860		},
1861		{
1862			accept:     runtime.ContentTypeProtobuf + ";as=Table;v=v1beta1;g=meta.k8s.io",
1863			statusCode: http.StatusNotAcceptable,
1864		},
1865		{
1866			accept:     runtime.ContentTypeProtobuf + ";as=Table;v=v1;g=meta.k8s.io",
1867			statusCode: http.StatusNotAcceptable,
1868		},
1869
1870		{
1871			item:   true,
1872			accept: "application/json;as=Table;v=v1;g=meta.k8s.io",
1873			expected: &metav1.Table{
1874				TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1"},
1875				ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"},
1876				ColumnDefinitions: []metav1.TableColumnDefinition{
1877					{Name: "Name", Type: "string", Format: "name", Description: metaDoc["name"]},
1878					{Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
1879				},
1880				Rows: []metav1.TableRow{
1881					{Cells: []interface{}{"foo1", now.Time.UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedV1Body}},
1882				},
1883			},
1884		},
1885		{
1886			item:   true,
1887			accept: "application/json;as=Table;v=v1beta1;g=meta.k8s.io",
1888			expected: &metav1.Table{
1889				TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"},
1890				ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"},
1891				ColumnDefinitions: []metav1.TableColumnDefinition{
1892					{Name: "Name", Type: "string", Format: "name", Description: metaDoc["name"]},
1893					{Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
1894				},
1895				Rows: []metav1.TableRow{
1896					{Cells: []interface{}{"foo1", now.Time.UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedV1Beta1Body}},
1897				},
1898			},
1899		},
1900		{
1901			item: true,
1902			accept: strings.Join([]string{
1903				runtime.ContentTypeProtobuf + ";as=Table;v=v1beta1;g=meta.k8s.io",
1904				"application/json;as=Table;v=v1beta1;g=meta.k8s.io",
1905			}, ","),
1906			expected: &metav1.Table{
1907				TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"},
1908				ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"},
1909				ColumnDefinitions: []metav1.TableColumnDefinition{
1910					{Name: "Name", Type: "string", Format: "name", Description: metaDoc["name"]},
1911					{Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
1912				},
1913				Rows: []metav1.TableRow{
1914					{Cells: []interface{}{"foo1", now.Time.UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedV1Beta1Body}},
1915				},
1916			},
1917		},
1918		{
1919			item:   true,
1920			accept: "application/json;as=Table;v=v1beta1;g=meta.k8s.io",
1921			params: url.Values{"includeObject": []string{"Metadata"}},
1922			expected: &metav1.Table{
1923				TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"},
1924				ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"},
1925				ColumnDefinitions: []metav1.TableColumnDefinition{
1926					{Name: "Name", Type: "string", Format: "name", Description: metaDoc["name"]},
1927					{Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
1928				},
1929				Rows: []metav1.TableRow{
1930					{Cells: []interface{}{"foo1", now.Time.UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedV1Beta1Body}},
1931				},
1932			},
1933		},
1934		{
1935			accept: "application/json;as=Table;v=v1beta1;g=meta.k8s.io",
1936			params: url.Values{"includeObject": []string{"Metadata"}},
1937			expected: &metav1.Table{
1938				TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"},
1939				ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/test/link"},
1940				ColumnDefinitions: []metav1.TableColumnDefinition{
1941					{Name: "Name", Type: "string", Format: "name", Description: metaDoc["name"]},
1942					{Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
1943				},
1944				Rows: []metav1.TableRow{
1945					{Cells: []interface{}{"foo1", now.Time.UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedV1Beta1Body}},
1946				},
1947			},
1948		},
1949	}
1950	for i, test := range tests {
1951		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
1952			storage := map[string]rest.Storage{}
1953			simpleStorage := SimpleRESTStorage{
1954				item: obj,
1955				list: []genericapitesting.Simple{obj},
1956			}
1957			selfLinker := &setTestSelfLinker{
1958				t:           t,
1959				expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple",
1960				namespace:   "default",
1961			}
1962			if test.item {
1963				selfLinker.expectedSet += "/id"
1964				selfLinker.name = "id"
1965			}
1966			storage["simple"] = &simpleStorage
1967			handler := handleLinker(storage, selfLinker)
1968			server := httptest.NewServer(handler)
1969			defer server.Close()
1970
1971			var id string
1972			if test.item {
1973				id = "/id"
1974			}
1975			u, err := url.Parse(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple" + id)
1976			if err != nil {
1977				t.Fatal(err)
1978			}
1979			u.RawQuery = test.params.Encode()
1980			req := &http.Request{Method: "GET", URL: u}
1981			req.Header = http.Header{}
1982			req.Header.Set("Accept", test.accept)
1983			resp, err := http.DefaultClient.Do(req)
1984			if err != nil {
1985				t.Fatal(err)
1986			}
1987			if test.statusCode != 0 {
1988				if resp.StatusCode != test.statusCode {
1989					t.Errorf("%d: unexpected response: %#v", i, resp)
1990				}
1991				obj, _, err := extractBodyObject(resp, unstructured.UnstructuredJSONScheme)
1992				if err != nil {
1993					t.Fatalf("%d: unexpected body read error: %v", i, err)
1994				}
1995				gvk := schema.GroupVersionKind{Version: "v1", Kind: "Status"}
1996				if obj.GetObjectKind().GroupVersionKind() != gvk {
1997					t.Fatalf("%d: unexpected error body: %#v", i, obj)
1998				}
1999				return
2000			}
2001			if resp.StatusCode != http.StatusOK {
2002				t.Errorf("%d: unexpected response: %#v", i, resp)
2003			}
2004			var itemOut metav1.Table
2005			body, err := extractBody(resp, &itemOut)
2006			if err != nil {
2007				t.Fatal(err)
2008			}
2009			if !reflect.DeepEqual(test.expected, &itemOut) {
2010				t.Log(body)
2011				t.Errorf("%d: did not match: %s", i, diff.ObjectReflectDiff(test.expected, &itemOut))
2012			}
2013		})
2014	}
2015}
2016
2017func TestWatchTable(t *testing.T) {
2018	obj := genericapitesting.Simple{
2019		ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "ns1", ResourceVersion: "10", SelfLink: "/blah", CreationTimestamp: metav1.NewTime(time.Unix(1, 0)), UID: types.UID("abcdef0123")},
2020		Other:      "foo",
2021	}
2022
2023	m, err := meta.Accessor(&obj)
2024	if err != nil {
2025		t.Fatal(err)
2026	}
2027	partial := meta.AsPartialObjectMetadata(m)
2028	partial.GetObjectKind().SetGroupVersionKind(metav1beta1.SchemeGroupVersion.WithKind("PartialObjectMetadata"))
2029	encodedBody, err := runtime.Encode(metainternalversionscheme.Codecs.LegacyCodec(metav1beta1.SchemeGroupVersion), partial)
2030	if err != nil {
2031		t.Fatal(err)
2032	}
2033	// the codec includes a trailing newline that is not present during decode
2034	encodedBody = bytes.TrimSpace(encodedBody)
2035
2036	encodedBodyV1, err := runtime.Encode(metainternalversionscheme.Codecs.LegacyCodec(metav1.SchemeGroupVersion), partial)
2037	if err != nil {
2038		t.Fatal(err)
2039	}
2040	// the codec includes a trailing newline that is not present during decode
2041	encodedBodyV1 = bytes.TrimSpace(encodedBodyV1)
2042
2043	metaDoc := metav1.ObjectMeta{}.SwaggerDoc()
2044
2045	s := metainternalversionscheme.Codecs.SupportedMediaTypes()[0].Serializer
2046
2047	tests := []struct {
2048		accept string
2049		params url.Values
2050		send   func(w *watch.FakeWatcher)
2051
2052		expected    []*metav1.WatchEvent
2053		contentType string
2054		statusCode  int
2055		item        bool
2056	}{
2057		{
2058			accept:     "application/json;as=Table;v=v1alpha1;g=meta.k8s.io",
2059			statusCode: http.StatusNotAcceptable,
2060		},
2061		{
2062			accept: "application/json;as=Table;v=v1beta1;g=meta.k8s.io",
2063			send: func(w *watch.FakeWatcher) {
2064				w.Add(&obj)
2065			},
2066			expected: []*metav1.WatchEvent{
2067				{
2068					Type: "ADDED",
2069					Object: runtime.RawExtension{
2070						Raw: []byte(strings.TrimSpace(runtime.EncodeOrDie(s, &metav1.Table{
2071							TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"},
2072							ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"},
2073							ColumnDefinitions: []metav1.TableColumnDefinition{
2074								{Name: "Name", Type: "string", Format: "name", Description: metaDoc["name"]},
2075								{Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
2076							},
2077							Rows: []metav1.TableRow{
2078								{Cells: []interface{}{"foo1", time.Unix(1, 0).UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedBody}},
2079							},
2080						}))),
2081					},
2082				},
2083			},
2084		},
2085		{
2086			accept: "application/json;as=Table;v=v1beta1;g=meta.k8s.io",
2087			send: func(w *watch.FakeWatcher) {
2088				w.Add(&obj)
2089				w.Modify(&obj)
2090			},
2091			expected: []*metav1.WatchEvent{
2092				{
2093					Type: "ADDED",
2094					Object: runtime.RawExtension{
2095						Raw: []byte(strings.TrimSpace(runtime.EncodeOrDie(s, &metav1.Table{
2096							TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"},
2097							ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"},
2098							ColumnDefinitions: []metav1.TableColumnDefinition{
2099								{Name: "Name", Type: "string", Format: "name", Description: metaDoc["name"]},
2100								{Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
2101							},
2102							Rows: []metav1.TableRow{
2103								{Cells: []interface{}{"foo1", time.Unix(1, 0).UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedBody}},
2104							},
2105						}))),
2106					},
2107				},
2108				{
2109					Type: "MODIFIED",
2110					Object: runtime.RawExtension{
2111						Raw: []byte(strings.TrimSpace(runtime.EncodeOrDie(s, &metav1.Table{
2112							TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1beta1"},
2113							ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"},
2114							Rows: []metav1.TableRow{
2115								{Cells: []interface{}{"foo1", time.Unix(1, 0).UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedBody}},
2116							},
2117						}))),
2118					},
2119				},
2120			},
2121		},
2122		{
2123			accept: "application/json;as=Table;v=v1;g=meta.k8s.io",
2124			send: func(w *watch.FakeWatcher) {
2125				w.Add(&obj)
2126				w.Modify(&obj)
2127			},
2128			expected: []*metav1.WatchEvent{
2129				{
2130					Type: "ADDED",
2131					Object: runtime.RawExtension{
2132						Raw: []byte(strings.TrimSpace(runtime.EncodeOrDie(s, &metav1.Table{
2133							TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1"},
2134							ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"},
2135							ColumnDefinitions: []metav1.TableColumnDefinition{
2136								{Name: "Name", Type: "string", Format: "name", Description: metaDoc["name"]},
2137								{Name: "Created At", Type: "date", Description: metaDoc["creationTimestamp"]},
2138							},
2139							Rows: []metav1.TableRow{
2140								{Cells: []interface{}{"foo1", time.Unix(1, 0).UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedBodyV1}},
2141							},
2142						}))),
2143					},
2144				},
2145				{
2146					Type: "MODIFIED",
2147					Object: runtime.RawExtension{
2148						Raw: []byte(strings.TrimSpace(runtime.EncodeOrDie(s, &metav1.Table{
2149							TypeMeta: metav1.TypeMeta{Kind: "Table", APIVersion: "meta.k8s.io/v1"},
2150							ListMeta: metav1.ListMeta{ResourceVersion: "10", SelfLink: "/blah"},
2151							Rows: []metav1.TableRow{
2152								{Cells: []interface{}{"foo1", time.Unix(1, 0).UTC().Format(time.RFC3339)}, Object: runtime.RawExtension{Raw: encodedBodyV1}},
2153							},
2154						}))),
2155					},
2156				},
2157			},
2158		},
2159	}
2160	for i, test := range tests {
2161		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
2162			storage := map[string]rest.Storage{}
2163			simpleStorage := SimpleRESTStorage{
2164				item: obj,
2165				list: []genericapitesting.Simple{obj},
2166			}
2167
2168			selfLinker := &setTestSelfLinker{
2169				t:           t,
2170				expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple",
2171				namespace:   "default",
2172			}
2173			if test.item {
2174				selfLinker.expectedSet += "/id"
2175				selfLinker.name = "id"
2176			}
2177			storage["simple"] = &simpleStorage
2178			handler := handleLinker(storage, selfLinker)
2179			server := httptest.NewServer(handler)
2180			defer server.Close()
2181
2182			var id string
2183			if test.item {
2184				id = "/id"
2185			}
2186			u, err := url.Parse(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple")
2187			if err != nil {
2188				t.Fatal(err)
2189			}
2190			if test.params == nil {
2191				test.params = url.Values{}
2192			}
2193			if test.item {
2194				test.params["fieldSelector"] = []string{fmt.Sprintf("metadata.name=%s", id)}
2195			}
2196			test.params["watch"] = []string{"1"}
2197
2198			u.RawQuery = test.params.Encode()
2199			req := &http.Request{Method: "GET", URL: u}
2200			req.Header = http.Header{}
2201			req.Header.Set("Accept", test.accept)
2202			resp, err := http.DefaultClient.Do(req)
2203			if err != nil {
2204				t.Fatal(err)
2205			}
2206			defer resp.Body.Close()
2207			if test.statusCode != 0 {
2208				if resp.StatusCode != test.statusCode {
2209					t.Fatalf("%d: unexpected response: %#v", i, resp)
2210				}
2211				obj, _, err := extractBodyObject(resp, unstructured.UnstructuredJSONScheme)
2212				if err != nil {
2213					t.Fatalf("%d: unexpected body read error: %v", i, err)
2214				}
2215				gvk := schema.GroupVersionKind{Version: "v1", Kind: "Status"}
2216				if obj.GetObjectKind().GroupVersionKind() != gvk {
2217					t.Fatalf("%d: unexpected error body: %#v", i, obj)
2218				}
2219				return
2220			}
2221			if resp.StatusCode != http.StatusOK {
2222				t.Fatalf("%d: unexpected response: %#v", i, resp)
2223			}
2224
2225			go func() {
2226				defer simpleStorage.fakeWatch.Stop()
2227				test.send(simpleStorage.fakeWatch)
2228			}()
2229
2230			body, err := ioutil.ReadAll(resp.Body)
2231			if err != nil {
2232				t.Fatal(err)
2233			}
2234			t.Logf("Body:\n%s", string(body))
2235			d := watcher(resp.Header.Get("Content-Type"), ioutil.NopCloser(bytes.NewReader(body)))
2236			var actual []*metav1.WatchEvent
2237			for {
2238				var event metav1.WatchEvent
2239				_, _, err := d.Decode(nil, &event)
2240				if err == io.EOF {
2241					break
2242				}
2243				if err != nil {
2244					t.Fatal(err)
2245				}
2246				actual = append(actual, &event)
2247			}
2248			if !reflect.DeepEqual(test.expected, actual) {
2249				for i := range test.expected {
2250					if i >= len(actual) {
2251						break
2252					}
2253					t.Logf("%s", diff.StringDiff(string(test.expected[i].Object.Raw), string(actual[i].Object.Raw)))
2254				}
2255				t.Fatalf("unexpected: %s", diff.ObjectReflectDiff(test.expected, actual))
2256			}
2257		})
2258	}
2259}
2260
2261func watcher(mediaType string, r io.ReadCloser) streaming.Decoder {
2262	info, ok := runtime.SerializerInfoForMediaType(metainternalversionscheme.Codecs.SupportedMediaTypes(), mediaType)
2263	if !ok || info.StreamSerializer == nil {
2264		panic(info)
2265	}
2266	streamSerializer := info.StreamSerializer
2267	fr := streamSerializer.Framer.NewFrameReader(r)
2268	d := streaming.NewDecoder(fr, streamSerializer.Serializer)
2269	return d
2270}
2271
2272func TestGetPartialObjectMetadata(t *testing.T) {
2273	now := metav1.Time{metav1.Now().Rfc3339Copy().Local()}
2274	storage := map[string]rest.Storage{}
2275	simpleStorage := SimpleRESTStorage{
2276		item: genericapitesting.Simple{
2277			ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "ns1", CreationTimestamp: now, UID: types.UID("abcdef0123")},
2278			Other:      "foo",
2279		},
2280		list: []genericapitesting.Simple{
2281			{
2282				ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "ns1", CreationTimestamp: now, UID: types.UID("newer")},
2283				Other:      "foo",
2284			},
2285			{
2286				ObjectMeta: metav1.ObjectMeta{Name: "foo2", Namespace: "ns2", CreationTimestamp: now, UID: types.UID("older")},
2287				Other:      "bar",
2288			},
2289		},
2290	}
2291	selfLinker := &setTestSelfLinker{
2292		t:              t,
2293		expectedSet:    "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id",
2294		alternativeSet: sets.NewString("/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple"),
2295		name:           "id",
2296		namespace:      "default",
2297	}
2298	storage["simple"] = &simpleStorage
2299	handler := handleLinker(storage, selfLinker)
2300	server := httptest.NewServer(handler)
2301	defer server.Close()
2302
2303	tests := []struct {
2304		accept     string
2305		params     url.Values
2306		pretty     bool
2307		list       bool
2308		expected   runtime.Object
2309		expectKind schema.GroupVersionKind
2310		statusCode int
2311	}{
2312		{
2313			accept:     "application/json;as=PartialObjectMetadata;v=v1alpha1;g=meta.k8s.io",
2314			statusCode: http.StatusNotAcceptable,
2315		},
2316		{
2317			accept:     "application/json;as=PartialObjectMetadata;v=v1alpha1;g=meta.k8s.io, application/json",
2318			expectKind: schema.GroupVersionKind{Kind: "Simple", Group: testGroupVersion.Group, Version: testGroupVersion.Version},
2319		},
2320		{
2321			accept:     "application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io, application/json",
2322			expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1beta1"},
2323		},
2324		{
2325			list:       true,
2326			accept:     "application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io",
2327			statusCode: http.StatusNotAcceptable,
2328		},
2329
2330		// verify preferred version overrides supported version
2331		{
2332			accept:     "application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io, application/json;as=PartialObjectMetadata;v=v1;g=meta.k8s.io, application/json",
2333			expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1beta1"},
2334		},
2335		{
2336			accept:     "application/json;as=PartialObjectMetadata;v=v1;g=meta.k8s.io, application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io, application/json",
2337			expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1"},
2338		},
2339		{
2340			accept:     "application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io, application/json;as=PartialObjectMetadata;v=v1;g=meta.k8s.io",
2341			expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1beta1"},
2342		},
2343		{
2344			accept:     "application/json;as=PartialObjectMetadata;v=v1;g=meta.k8s.io, application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io",
2345			expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1"},
2346		},
2347
2348		{
2349			list:       true,
2350			accept:     "application/json;as=PartialObjectMetadata;v=v1alpha1;g=meta.k8s.io, application/json",
2351			expectKind: schema.GroupVersionKind{Kind: "SimpleList", Group: testGroupVersion.Group, Version: testGroupVersion.Version},
2352		},
2353		{
2354			list:       true,
2355			accept:     "application/json;as=PartialObjectMetadataList;v=v1beta1;g=meta.k8s.io, application/json",
2356			expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadataList", Group: "meta.k8s.io", Version: "v1beta1"},
2357		},
2358		{
2359			accept:     "application/json;as=PartialObjectMetadataList;v=v1beta1;g=meta.k8s.io",
2360			statusCode: http.StatusNotAcceptable,
2361		},
2362		{
2363			accept: "application/json;as=PartialObjectMetadata;v=v1beta1;g=meta.k8s.io",
2364			expected: &metav1beta1.PartialObjectMetadata{
2365				ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "ns1", CreationTimestamp: now, UID: types.UID("abcdef0123")},
2366			},
2367			expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1beta1"},
2368		},
2369		{
2370			accept: "application/json;as=PartialObjectMetadata;v=v1;g=meta.k8s.io",
2371			expected: &metav1.PartialObjectMetadata{
2372				ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "ns1", CreationTimestamp: now, UID: types.UID("abcdef0123")},
2373			},
2374			expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadata", Group: "meta.k8s.io", Version: "v1"},
2375		},
2376		{
2377			list:   true,
2378			accept: "application/json;as=PartialObjectMetadataList;v=v1beta1;g=meta.k8s.io",
2379			expected: &metav1beta1.PartialObjectMetadataList{
2380				ListMeta: metav1.ListMeta{
2381					ResourceVersion: "10",
2382					SelfLink:        "/test/link",
2383				},
2384				Items: []metav1beta1.PartialObjectMetadata{
2385					{
2386						TypeMeta:   metav1.TypeMeta{APIVersion: "meta.k8s.io/v1beta1", Kind: "PartialObjectMetadata"},
2387						ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "ns1", CreationTimestamp: now, UID: types.UID("newer")},
2388					},
2389					{
2390						TypeMeta:   metav1.TypeMeta{APIVersion: "meta.k8s.io/v1beta1", Kind: "PartialObjectMetadata"},
2391						ObjectMeta: metav1.ObjectMeta{Name: "foo2", Namespace: "ns2", CreationTimestamp: now, UID: types.UID("older")},
2392					},
2393				},
2394			},
2395			expectKind: schema.GroupVersionKind{Kind: "PartialObjectMetadataList", Group: "meta.k8s.io", Version: "v1beta1"},
2396		},
2397	}
2398	for i, test := range tests {
2399		suffix := "/namespaces/default/simple/id"
2400		if test.list {
2401			suffix = "/namespaces/default/simple"
2402		}
2403		u, err := url.Parse(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + suffix)
2404		if err != nil {
2405			t.Fatal(err)
2406		}
2407		u.RawQuery = test.params.Encode()
2408		req := &http.Request{Method: "GET", URL: u}
2409		req.Header = http.Header{}
2410		req.Header.Set("Accept", test.accept)
2411		resp, err := http.DefaultClient.Do(req)
2412		if err != nil {
2413			t.Fatal(err)
2414		}
2415		if test.statusCode != 0 {
2416			if resp.StatusCode != test.statusCode {
2417				t.Errorf("%d: unexpected response: %#v", i, resp)
2418			}
2419			obj, _, err := extractBodyObject(resp, unstructured.UnstructuredJSONScheme)
2420			if err != nil {
2421				t.Errorf("%d: unexpected body read error: %v", i, err)
2422				continue
2423			}
2424			gvk := schema.GroupVersionKind{Version: "v1", Kind: "Status"}
2425			if obj.GetObjectKind().GroupVersionKind() != gvk {
2426				t.Errorf("%d: unexpected error body: %#v", i, obj)
2427			}
2428			continue
2429		}
2430		if resp.StatusCode != http.StatusOK {
2431			t.Errorf("%d: invalid status: %#v\n%s", i, resp, bodyOrDie(resp))
2432			continue
2433		}
2434		body := ""
2435		if test.expected != nil {
2436			itemOut, d, err := extractBodyObject(resp, metainternalversionscheme.Codecs.LegacyCodec(metav1beta1.SchemeGroupVersion))
2437			if err != nil {
2438				t.Fatal(err)
2439			}
2440			if !reflect.DeepEqual(test.expected, itemOut) {
2441				t.Errorf("%d: did not match: %s", i, diff.ObjectReflectDiff(test.expected, itemOut))
2442			}
2443			body = d
2444		} else {
2445			d, err := ioutil.ReadAll(resp.Body)
2446			if err != nil {
2447				t.Fatal(err)
2448			}
2449			body = string(d)
2450		}
2451		obj := &unstructured.Unstructured{}
2452		if err := json.Unmarshal([]byte(body), obj); err != nil {
2453			t.Fatal(err)
2454		}
2455		if obj.GetObjectKind().GroupVersionKind() != test.expectKind {
2456			t.Errorf("%d: unexpected kind: %#v", i, obj.GetObjectKind().GroupVersionKind())
2457		}
2458	}
2459}
2460
2461func TestGetBinary(t *testing.T) {
2462	simpleStorage := SimpleRESTStorage{
2463		stream: &SimpleStream{
2464			contentType: "text/plain",
2465			Reader:      bytes.NewBufferString("response data"),
2466		},
2467	}
2468	stream := simpleStorage.stream
2469	server := httptest.NewServer(handle(map[string]rest.Storage{"simple": &simpleStorage}))
2470	defer server.Close()
2471
2472	req, err := http.NewRequest("GET", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/binary", nil)
2473	if err != nil {
2474		t.Fatalf("unexpected error: %v", err)
2475	}
2476	req.Header.Add("Accept", "text/other, */*")
2477	resp, err := http.DefaultClient.Do(req)
2478	if err != nil {
2479		t.Fatalf("unexpected error: %v", err)
2480	}
2481	if resp.StatusCode != http.StatusOK {
2482		t.Fatalf("unexpected response: %#v", resp)
2483	}
2484	body, err := ioutil.ReadAll(resp.Body)
2485	if err != nil {
2486		t.Errorf("unexpected error: %v", err)
2487	}
2488	if !stream.closed || stream.version != testGroupVersion.String() || stream.accept != "text/other, */*" ||
2489		resp.Header.Get("Content-Type") != stream.contentType || string(body) != "response data" {
2490		t.Errorf("unexpected stream: %#v", stream)
2491	}
2492}
2493
2494func validateSimpleGetOptionsParams(t *testing.T, route *restful.Route) {
2495	// Validate name and description
2496	expectedParams := map[string]string{
2497		"param1":  "description for param1",
2498		"param2":  "description for param2",
2499		"atAPath": "",
2500	}
2501	for _, p := range route.ParameterDocs {
2502		data := p.Data()
2503		if desc, exists := expectedParams[data.Name]; exists {
2504			if desc != data.Description {
2505				t.Errorf("unexpected description for parameter %s: %s\n", data.Name, data.Description)
2506			}
2507			delete(expectedParams, data.Name)
2508		}
2509	}
2510	if len(expectedParams) > 0 {
2511		t.Errorf("did not find all expected parameters: %#v", expectedParams)
2512	}
2513}
2514
2515func TestGetWithOptionsRouteParams(t *testing.T) {
2516	storage := map[string]rest.Storage{}
2517	simpleStorage := GetWithOptionsRESTStorage{
2518		SimpleRESTStorage: &SimpleRESTStorage{},
2519	}
2520	storage["simple"] = &simpleStorage
2521	handler := handle(storage)
2522	ws := handler.(*defaultAPIServer).container.RegisteredWebServices()
2523	if len(ws) == 0 {
2524		t.Fatal("no web services registered")
2525	}
2526	routes := ws[0].Routes()
2527	for i := range routes {
2528		if routes[i].Method == "GET" && routes[i].Operation == "readNamespacedSimple" {
2529			validateSimpleGetOptionsParams(t, &routes[i])
2530			break
2531		}
2532	}
2533}
2534
2535func TestGetWithOptions(t *testing.T) {
2536
2537	tests := []struct {
2538		name         string
2539		rootScoped   bool
2540		requestURL   string
2541		expectedPath string
2542	}{
2543		{
2544			name:         "basic",
2545			requestURL:   "/namespaces/default/simple/id?param1=test1&param2=test2",
2546			expectedPath: "",
2547		},
2548		{
2549			name:         "with root slash",
2550			requestURL:   "/namespaces/default/simple/id/?param1=test1&param2=test2",
2551			expectedPath: "/",
2552		},
2553		{
2554			name:         "with path",
2555			requestURL:   "/namespaces/default/simple/id/a/different/path?param1=test1&param2=test2",
2556			expectedPath: "/a/different/path",
2557		},
2558		{
2559			name:         "with path with trailing slash",
2560			requestURL:   "/namespaces/default/simple/id/a/different/path/?param1=test1&param2=test2",
2561			expectedPath: "/a/different/path/",
2562		},
2563		{
2564			name:         "as subresource",
2565			requestURL:   "/namespaces/default/simple/id/subresource/another/different/path?param1=test1&param2=test2",
2566			expectedPath: "/another/different/path",
2567		},
2568		{
2569			name:         "cluster-scoped basic",
2570			rootScoped:   true,
2571			requestURL:   "/simple/id?param1=test1&param2=test2",
2572			expectedPath: "",
2573		},
2574		{
2575			name:         "cluster-scoped basic with path",
2576			rootScoped:   true,
2577			requestURL:   "/simple/id/a/cluster/path?param1=test1&param2=test2",
2578			expectedPath: "/a/cluster/path",
2579		},
2580		{
2581			name:         "cluster-scoped basic as subresource",
2582			rootScoped:   true,
2583			requestURL:   "/simple/id/subresource/another/cluster/path?param1=test1&param2=test2",
2584			expectedPath: "/another/cluster/path",
2585		},
2586	}
2587
2588	for _, test := range tests {
2589		simpleStorage := GetWithOptionsRESTStorage{
2590			SimpleRESTStorage: &SimpleRESTStorage{
2591				item: genericapitesting.Simple{
2592					Other: "foo",
2593				},
2594			},
2595			takesPath: "atAPath",
2596		}
2597		simpleRootStorage := GetWithOptionsRootRESTStorage{
2598			SimpleTypedStorage: &SimpleTypedStorage{
2599				baseType: &genericapitesting.SimpleRoot{}, // a root scoped type
2600				item: &genericapitesting.SimpleRoot{
2601					Other: "foo",
2602				},
2603			},
2604			takesPath: "atAPath",
2605		}
2606
2607		storage := map[string]rest.Storage{}
2608		if test.rootScoped {
2609			storage["simple"] = &simpleRootStorage
2610			storage["simple/subresource"] = &simpleRootStorage
2611		} else {
2612			storage["simple"] = &simpleStorage
2613			storage["simple/subresource"] = &simpleStorage
2614		}
2615		handler := handle(storage)
2616		server := httptest.NewServer(handler)
2617		defer server.Close()
2618
2619		resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + test.requestURL)
2620		if err != nil {
2621			t.Errorf("%s: %v", test.name, err)
2622			continue
2623		}
2624		if resp.StatusCode != http.StatusOK {
2625			t.Errorf("%s: unexpected response: %#v", test.name, resp)
2626			continue
2627		}
2628
2629		var itemOut runtime.Object
2630		if test.rootScoped {
2631			itemOut = &genericapitesting.SimpleRoot{}
2632		} else {
2633			itemOut = &genericapitesting.Simple{}
2634		}
2635		body, err := extractBody(resp, itemOut)
2636		if err != nil {
2637			t.Errorf("%s: %v", test.name, err)
2638			continue
2639		}
2640		if metadata, err := meta.Accessor(itemOut); err == nil {
2641			if metadata.GetName() != simpleStorage.item.Name {
2642				t.Errorf("%s: Unexpected data: %#v, expected %#v (%s)", test.name, itemOut, simpleStorage.item, string(body))
2643				continue
2644			}
2645		} else {
2646			t.Errorf("%s: Couldn't get name from %#v: %v", test.name, itemOut, err)
2647		}
2648
2649		var opts *genericapitesting.SimpleGetOptions
2650		var ok bool
2651		if test.rootScoped {
2652			opts, ok = simpleRootStorage.optionsReceived.(*genericapitesting.SimpleGetOptions)
2653		} else {
2654			opts, ok = simpleStorage.optionsReceived.(*genericapitesting.SimpleGetOptions)
2655
2656		}
2657		if !ok {
2658			t.Errorf("%s: Unexpected options object received: %#v", test.name, simpleStorage.optionsReceived)
2659			continue
2660		}
2661		if opts.Param1 != "test1" || opts.Param2 != "test2" {
2662			t.Errorf("%s: Did not receive expected options: %#v", test.name, opts)
2663			continue
2664		}
2665		if opts.Path != test.expectedPath {
2666			t.Errorf("%s: Unexpected path value. Expected: %s. Actual: %s.", test.name, test.expectedPath, opts.Path)
2667			continue
2668		}
2669	}
2670}
2671
2672func TestGetAlternateSelfLink(t *testing.T) {
2673	storage := map[string]rest.Storage{}
2674	simpleStorage := SimpleRESTStorage{
2675		item: genericapitesting.Simple{
2676			Other: "foo",
2677		},
2678	}
2679	selfLinker := &setTestSelfLinker{
2680		t:           t,
2681		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/test/simple/id",
2682		name:        "id",
2683		namespace:   "test",
2684	}
2685	storage["simple"] = &simpleStorage
2686	handler := handleLinker(storage, selfLinker)
2687	server := httptest.NewServer(handler)
2688	defer server.Close()
2689
2690	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/test/simple/id")
2691	if err != nil {
2692		t.Fatalf("unexpected error: %v", err)
2693	}
2694	if resp.StatusCode != http.StatusOK {
2695		t.Fatalf("unexpected response: %#v", resp)
2696	}
2697	var itemOut genericapitesting.Simple
2698	body, err := extractBody(resp, &itemOut)
2699	if err != nil {
2700		t.Fatalf("unexpected error: %v", err)
2701	}
2702	if itemOut.Name != simpleStorage.item.Name {
2703		t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
2704	}
2705	if utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) == selfLinker.called {
2706		t.Errorf("unexpected selfLinker.called: %v", selfLinker.called)
2707	}
2708}
2709
2710func TestGetNamespaceSelfLink(t *testing.T) {
2711	storage := map[string]rest.Storage{}
2712	simpleStorage := SimpleRESTStorage{
2713		item: genericapitesting.Simple{
2714			Other: "foo",
2715		},
2716	}
2717	selfLinker := &setTestSelfLinker{
2718		t:           t,
2719		expectedSet: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/foo/simple/id",
2720		name:        "id",
2721		namespace:   "foo",
2722	}
2723	storage["simple"] = &simpleStorage
2724	handler := handleInternal(storage, admissionControl, selfLinker, nil)
2725	server := httptest.NewServer(handler)
2726	defer server.Close()
2727
2728	resp, err := http.Get(server.URL + "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/foo/simple/id")
2729	if err != nil {
2730		t.Fatalf("unexpected error: %v", err)
2731	}
2732	if resp.StatusCode != http.StatusOK {
2733		t.Fatalf("unexpected response: %#v", resp)
2734	}
2735	var itemOut genericapitesting.Simple
2736	body, err := extractBody(resp, &itemOut)
2737	if err != nil {
2738		t.Fatalf("unexpected error: %v", err)
2739	}
2740	if itemOut.Name != simpleStorage.item.Name {
2741		t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
2742	}
2743	if utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) == selfLinker.called {
2744		t.Errorf("unexpected selfLinker.called: %v", selfLinker.called)
2745	}
2746}
2747
2748func TestGetMissing(t *testing.T) {
2749	storage := map[string]rest.Storage{}
2750	simpleStorage := SimpleRESTStorage{
2751		errors: map[string]error{"get": apierrors.NewNotFound(schema.GroupResource{Resource: "simples"}, "id")},
2752	}
2753	storage["simple"] = &simpleStorage
2754	handler := handle(storage)
2755	server := httptest.NewServer(handler)
2756	defer server.Close()
2757
2758	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id")
2759	if err != nil {
2760		t.Errorf("unexpected error: %v", err)
2761	}
2762
2763	if resp.StatusCode != http.StatusNotFound {
2764		t.Errorf("Unexpected response %#v", resp)
2765	}
2766}
2767
2768func TestGetRetryAfter(t *testing.T) {
2769	storage := map[string]rest.Storage{}
2770	simpleStorage := SimpleRESTStorage{
2771		errors: map[string]error{"get": apierrors.NewServerTimeout(schema.GroupResource{Resource: "simples"}, "id", 2)},
2772	}
2773	storage["simple"] = &simpleStorage
2774	handler := handle(storage)
2775	server := httptest.NewServer(handler)
2776	defer server.Close()
2777
2778	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id")
2779	if err != nil {
2780		t.Errorf("unexpected error: %v", err)
2781	}
2782	if resp.StatusCode != http.StatusInternalServerError {
2783		t.Errorf("Unexpected response %#v", resp)
2784	}
2785	if resp.Header.Get("Retry-After") != "2" {
2786		t.Errorf("Unexpected Retry-After header: %v", resp.Header)
2787	}
2788}
2789
2790func TestConnect(t *testing.T) {
2791	responseText := "Hello World"
2792	itemID := "theID"
2793	connectStorage := &ConnecterRESTStorage{
2794		connectHandler: &OutputConnect{
2795			response: responseText,
2796		},
2797	}
2798	storage := map[string]rest.Storage{
2799		"simple":         &SimpleRESTStorage{},
2800		"simple/connect": connectStorage,
2801	}
2802	handler := handle(storage)
2803	server := httptest.NewServer(handler)
2804	defer server.Close()
2805
2806	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect")
2807
2808	if err != nil {
2809		t.Errorf("unexpected error: %v", err)
2810	}
2811	if resp.StatusCode != http.StatusOK {
2812		t.Errorf("unexpected response: %#v", resp)
2813	}
2814	defer resp.Body.Close()
2815	body, err := ioutil.ReadAll(resp.Body)
2816	if err != nil {
2817		t.Fatalf("Unexpected error: %v", err)
2818	}
2819	if connectStorage.receivedID != itemID {
2820		t.Errorf("Unexpected item id. Expected: %s. Actual: %s.", itemID, connectStorage.receivedID)
2821	}
2822	if string(body) != responseText {
2823		t.Errorf("Unexpected response. Expected: %s. Actual: %s.", responseText, string(body))
2824	}
2825}
2826
2827func TestConnectResponderObject(t *testing.T) {
2828	itemID := "theID"
2829	simple := &genericapitesting.Simple{Other: "foo"}
2830	connectStorage := &ConnecterRESTStorage{}
2831	connectStorage.handlerFunc = func() http.Handler {
2832		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
2833			connectStorage.receivedResponder.Object(http.StatusCreated, simple)
2834		})
2835	}
2836	storage := map[string]rest.Storage{
2837		"simple":         &SimpleRESTStorage{},
2838		"simple/connect": connectStorage,
2839	}
2840	handler := handle(storage)
2841	server := httptest.NewServer(handler)
2842	defer server.Close()
2843
2844	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect")
2845
2846	if err != nil {
2847		t.Errorf("unexpected error: %v", err)
2848	}
2849	if resp.StatusCode != http.StatusCreated {
2850		t.Errorf("unexpected response: %#v", resp)
2851	}
2852	defer resp.Body.Close()
2853	body, err := ioutil.ReadAll(resp.Body)
2854	if err != nil {
2855		t.Fatalf("Unexpected error: %v", err)
2856	}
2857	if connectStorage.receivedID != itemID {
2858		t.Errorf("Unexpected item id. Expected: %s. Actual: %s.", itemID, connectStorage.receivedID)
2859	}
2860	obj, err := runtime.Decode(codec, body)
2861	if err != nil {
2862		t.Fatal(err)
2863	}
2864	if !apiequality.Semantic.DeepEqual(obj, simple) {
2865		t.Errorf("Unexpected response: %#v", obj)
2866	}
2867}
2868
2869func TestConnectResponderError(t *testing.T) {
2870	itemID := "theID"
2871	connectStorage := &ConnecterRESTStorage{}
2872	connectStorage.handlerFunc = func() http.Handler {
2873		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
2874			connectStorage.receivedResponder.Error(apierrors.NewForbidden(schema.GroupResource{Resource: "simples"}, itemID, errors.New("you are terminated")))
2875		})
2876	}
2877	storage := map[string]rest.Storage{
2878		"simple":         &SimpleRESTStorage{},
2879		"simple/connect": connectStorage,
2880	}
2881	handler := handle(storage)
2882	server := httptest.NewServer(handler)
2883	defer server.Close()
2884
2885	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect")
2886
2887	if err != nil {
2888		t.Errorf("unexpected error: %v", err)
2889	}
2890	if resp.StatusCode != http.StatusForbidden {
2891		t.Errorf("unexpected response: %#v", resp)
2892	}
2893	defer resp.Body.Close()
2894	body, err := ioutil.ReadAll(resp.Body)
2895	if err != nil {
2896		t.Fatalf("Unexpected error: %v", err)
2897	}
2898	if connectStorage.receivedID != itemID {
2899		t.Errorf("Unexpected item id. Expected: %s. Actual: %s.", itemID, connectStorage.receivedID)
2900	}
2901	obj, err := runtime.Decode(codec, body)
2902	if err != nil {
2903		t.Fatal(err)
2904	}
2905	if obj.(*metav1.Status).Code != http.StatusForbidden {
2906		t.Errorf("Unexpected response: %#v", obj)
2907	}
2908}
2909
2910func TestConnectWithOptionsRouteParams(t *testing.T) {
2911	connectStorage := &ConnecterRESTStorage{
2912		connectHandler:      &OutputConnect{},
2913		emptyConnectOptions: &genericapitesting.SimpleGetOptions{},
2914	}
2915	storage := map[string]rest.Storage{
2916		"simple":         &SimpleRESTStorage{},
2917		"simple/connect": connectStorage,
2918	}
2919	handler := handle(storage)
2920	ws := handler.(*defaultAPIServer).container.RegisteredWebServices()
2921	if len(ws) == 0 {
2922		t.Fatal("no web services registered")
2923	}
2924	routes := ws[0].Routes()
2925	for i := range routes {
2926		switch routes[i].Operation {
2927		case "connectGetNamespacedSimpleConnect":
2928		case "connectPostNamespacedSimpleConnect":
2929		case "connectPutNamespacedSimpleConnect":
2930		case "connectDeleteNamespacedSimpleConnect":
2931			validateSimpleGetOptionsParams(t, &routes[i])
2932
2933		}
2934	}
2935}
2936
2937func TestConnectWithOptions(t *testing.T) {
2938	responseText := "Hello World"
2939	itemID := "theID"
2940	connectStorage := &ConnecterRESTStorage{
2941		connectHandler: &OutputConnect{
2942			response: responseText,
2943		},
2944		emptyConnectOptions: &genericapitesting.SimpleGetOptions{},
2945	}
2946	storage := map[string]rest.Storage{
2947		"simple":         &SimpleRESTStorage{},
2948		"simple/connect": connectStorage,
2949	}
2950	handler := handle(storage)
2951	server := httptest.NewServer(handler)
2952	defer server.Close()
2953
2954	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect?param1=value1&param2=value2")
2955
2956	if err != nil {
2957		t.Errorf("unexpected error: %v", err)
2958	}
2959	if resp.StatusCode != http.StatusOK {
2960		t.Errorf("unexpected response: %#v", resp)
2961	}
2962	defer resp.Body.Close()
2963	body, err := ioutil.ReadAll(resp.Body)
2964	if err != nil {
2965		t.Fatalf("Unexpected error: %v", err)
2966	}
2967	if connectStorage.receivedID != itemID {
2968		t.Errorf("Unexpected item id. Expected: %s. Actual: %s.", itemID, connectStorage.receivedID)
2969	}
2970	if string(body) != responseText {
2971		t.Errorf("Unexpected response. Expected: %s. Actual: %s.", responseText, string(body))
2972	}
2973	if connectStorage.receivedResponder == nil {
2974		t.Errorf("Unexpected responder")
2975	}
2976	opts, ok := connectStorage.receivedConnectOptions.(*genericapitesting.SimpleGetOptions)
2977	if !ok {
2978		t.Fatalf("Unexpected options type: %#v", connectStorage.receivedConnectOptions)
2979	}
2980	if opts.Param1 != "value1" && opts.Param2 != "value2" {
2981		t.Errorf("Unexpected options value: %#v", opts)
2982	}
2983}
2984
2985func TestConnectWithOptionsAndPath(t *testing.T) {
2986	responseText := "Hello World"
2987	itemID := "theID"
2988	testPath := "/a/b/c/def"
2989	connectStorage := &ConnecterRESTStorage{
2990		connectHandler: &OutputConnect{
2991			response: responseText,
2992		},
2993		emptyConnectOptions: &genericapitesting.SimpleGetOptions{},
2994		takesPath:           "atAPath",
2995	}
2996	storage := map[string]rest.Storage{
2997		"simple":         &SimpleRESTStorage{},
2998		"simple/connect": connectStorage,
2999	}
3000	handler := handle(storage)
3001	server := httptest.NewServer(handler)
3002	defer server.Close()
3003
3004	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect" + testPath + "?param1=value1&param2=value2")
3005
3006	if err != nil {
3007		t.Errorf("unexpected error: %v", err)
3008	}
3009	if resp.StatusCode != http.StatusOK {
3010		t.Errorf("unexpected response: %#v", resp)
3011	}
3012	defer resp.Body.Close()
3013	body, err := ioutil.ReadAll(resp.Body)
3014	if err != nil {
3015		t.Fatalf("Unexpected error: %v", err)
3016	}
3017	if connectStorage.receivedID != itemID {
3018		t.Errorf("Unexpected item id. Expected: %s. Actual: %s.", itemID, connectStorage.receivedID)
3019	}
3020	if string(body) != responseText {
3021		t.Errorf("Unexpected response. Expected: %s. Actual: %s.", responseText, string(body))
3022	}
3023	opts, ok := connectStorage.receivedConnectOptions.(*genericapitesting.SimpleGetOptions)
3024	if !ok {
3025		t.Fatalf("Unexpected options type: %#v", connectStorage.receivedConnectOptions)
3026	}
3027	if opts.Param1 != "value1" && opts.Param2 != "value2" {
3028		t.Errorf("Unexpected options value: %#v", opts)
3029	}
3030	if opts.Path != testPath {
3031		t.Errorf("Unexpected path value. Expected: %s. Actual: %s.", testPath, opts.Path)
3032	}
3033}
3034
3035func TestDelete(t *testing.T) {
3036	storage := map[string]rest.Storage{}
3037	simpleStorage := SimpleRESTStorage{}
3038	ID := "id"
3039	storage["simple"] = &simpleStorage
3040	handler := handle(storage)
3041	server := httptest.NewServer(handler)
3042	defer server.Close()
3043
3044	client := http.Client{}
3045	request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, nil)
3046	if err != nil {
3047		t.Errorf("unexpected error: %v", err)
3048	}
3049	res, err := client.Do(request)
3050	if err != nil {
3051		t.Fatalf("unexpected error: %v", err)
3052	}
3053	if res.StatusCode != http.StatusOK {
3054		t.Errorf("unexpected response: %#v", res)
3055	}
3056	if simpleStorage.deleted != ID {
3057		t.Errorf("Unexpected delete: %s, expected %s", simpleStorage.deleted, ID)
3058	}
3059}
3060
3061func TestDeleteWithOptions(t *testing.T) {
3062	storage := map[string]rest.Storage{}
3063	simpleStorage := SimpleRESTStorage{}
3064	ID := "id"
3065	storage["simple"] = &simpleStorage
3066	handler := handle(storage)
3067	server := httptest.NewServer(handler)
3068	defer server.Close()
3069
3070	grace := int64(300)
3071	item := &metav1.DeleteOptions{
3072		GracePeriodSeconds: &grace,
3073	}
3074	body, err := runtime.Encode(codec, item)
3075	if err != nil {
3076		t.Fatalf("unexpected error: %v", err)
3077	}
3078
3079	client := http.Client{}
3080	request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
3081	if err != nil {
3082		t.Errorf("unexpected error: %v", err)
3083	}
3084	res, err := client.Do(request)
3085	if err != nil {
3086		t.Fatalf("unexpected error: %v", err)
3087	}
3088	if res.StatusCode != http.StatusOK {
3089		t.Errorf("unexpected response: %s %#v", request.URL, res)
3090		s, err := ioutil.ReadAll(res.Body)
3091		if err != nil {
3092			t.Fatalf("unexpected error: %v", err)
3093		}
3094		t.Logf(string(s))
3095	}
3096	if simpleStorage.deleted != ID {
3097		t.Errorf("Unexpected delete: %s, expected %s", simpleStorage.deleted, ID)
3098	}
3099	simpleStorage.deleteOptions.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
3100	if !apiequality.Semantic.DeepEqual(simpleStorage.deleteOptions, item) {
3101		t.Errorf("unexpected delete options: %s", diff.ObjectDiff(simpleStorage.deleteOptions, item))
3102	}
3103}
3104
3105func TestDeleteWithOptionsQuery(t *testing.T) {
3106	storage := map[string]rest.Storage{}
3107	simpleStorage := SimpleRESTStorage{}
3108	ID := "id"
3109	storage["simple"] = &simpleStorage
3110	handler := handle(storage)
3111	server := httptest.NewServer(handler)
3112	defer server.Close()
3113
3114	grace := int64(300)
3115	item := &metav1.DeleteOptions{
3116		GracePeriodSeconds: &grace,
3117	}
3118
3119	client := http.Client{}
3120	request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID+"?gracePeriodSeconds="+strconv.FormatInt(grace, 10), nil)
3121	if err != nil {
3122		t.Errorf("unexpected error: %v", err)
3123	}
3124	res, err := client.Do(request)
3125	if err != nil {
3126		t.Fatalf("unexpected error: %v", err)
3127	}
3128	if res.StatusCode != http.StatusOK {
3129		t.Fatalf("unexpected response: %s %#v", request.URL, res)
3130		s, err := ioutil.ReadAll(res.Body)
3131		if err != nil {
3132			t.Fatalf("unexpected error: %v", err)
3133		}
3134		t.Logf(string(s))
3135	}
3136	if simpleStorage.deleted != ID {
3137		t.Fatalf("Unexpected delete: %s, expected %s", simpleStorage.deleted, ID)
3138	}
3139	simpleStorage.deleteOptions.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
3140	if !apiequality.Semantic.DeepEqual(simpleStorage.deleteOptions, item) {
3141		t.Errorf("unexpected delete options: %s", diff.ObjectDiff(simpleStorage.deleteOptions, item))
3142	}
3143}
3144
3145func TestDeleteWithOptionsQueryAndBody(t *testing.T) {
3146	storage := map[string]rest.Storage{}
3147	simpleStorage := SimpleRESTStorage{}
3148	ID := "id"
3149	storage["simple"] = &simpleStorage
3150	handler := handle(storage)
3151	server := httptest.NewServer(handler)
3152	defer server.Close()
3153
3154	grace := int64(300)
3155	item := &metav1.DeleteOptions{
3156		GracePeriodSeconds: &grace,
3157	}
3158	body, err := runtime.Encode(codec, item)
3159	if err != nil {
3160		t.Fatalf("unexpected error: %v", err)
3161	}
3162	client := http.Client{}
3163	request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID+"?gracePeriodSeconds="+strconv.FormatInt(grace+10, 10), bytes.NewReader(body))
3164	if err != nil {
3165		t.Errorf("unexpected error: %v", err)
3166	}
3167	res, err := client.Do(request)
3168	if err != nil {
3169		t.Fatalf("unexpected error: %v", err)
3170	}
3171	if res.StatusCode != http.StatusOK {
3172		t.Errorf("unexpected response: %s %#v", request.URL, res)
3173		s, err := ioutil.ReadAll(res.Body)
3174		if err != nil {
3175			t.Fatalf("unexpected error: %v", err)
3176		}
3177		t.Logf(string(s))
3178	}
3179	if simpleStorage.deleted != ID {
3180		t.Errorf("Unexpected delete: %s, expected %s", simpleStorage.deleted, ID)
3181	}
3182	simpleStorage.deleteOptions.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
3183	if !apiequality.Semantic.DeepEqual(simpleStorage.deleteOptions, item) {
3184		t.Errorf("unexpected delete options: %s", diff.ObjectDiff(simpleStorage.deleteOptions, item))
3185	}
3186}
3187
3188func TestDeleteInvokesAdmissionControl(t *testing.T) {
3189	// TODO: remove mutating deny when we removed it from the endpoint implementation and ported all plugins
3190	for _, admit := range []admission.Interface{alwaysMutatingDeny{}, alwaysValidatingDeny{}} {
3191		t.Logf("Testing %T", admit)
3192
3193		storage := map[string]rest.Storage{}
3194		simpleStorage := SimpleRESTStorage{}
3195		ID := "id"
3196		storage["simple"] = &simpleStorage
3197		handler := handleInternal(storage, admit, selfLinker, nil)
3198		server := httptest.NewServer(handler)
3199		defer server.Close()
3200
3201		client := http.Client{}
3202		request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, nil)
3203		if err != nil {
3204			t.Errorf("unexpected error: %v", err)
3205		}
3206		response, err := client.Do(request)
3207		if err != nil {
3208			t.Errorf("unexpected error: %v", err)
3209		}
3210		if response.StatusCode != http.StatusForbidden {
3211			t.Errorf("Unexpected response %#v", response)
3212		}
3213	}
3214}
3215
3216func TestDeleteMissing(t *testing.T) {
3217	storage := map[string]rest.Storage{}
3218	ID := "id"
3219	simpleStorage := SimpleRESTStorage{
3220		errors: map[string]error{"delete": apierrors.NewNotFound(schema.GroupResource{Resource: "simples"}, ID)},
3221	}
3222	storage["simple"] = &simpleStorage
3223	handler := handle(storage)
3224	server := httptest.NewServer(handler)
3225	defer server.Close()
3226
3227	client := http.Client{}
3228	request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, nil)
3229	if err != nil {
3230		t.Errorf("unexpected error: %v", err)
3231	}
3232	response, err := client.Do(request)
3233	if err != nil {
3234		t.Errorf("unexpected error: %v", err)
3235	}
3236
3237	if response.StatusCode != http.StatusNotFound {
3238		t.Errorf("Unexpected response %#v", response)
3239	}
3240}
3241
3242func TestUpdate(t *testing.T) {
3243	storage := map[string]rest.Storage{}
3244	simpleStorage := SimpleRESTStorage{}
3245	ID := "id"
3246	storage["simple"] = &simpleStorage
3247	selfLinker := &setTestSelfLinker{
3248		t:           t,
3249		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + ID,
3250		name:        ID,
3251		namespace:   metav1.NamespaceDefault,
3252	}
3253	handler := handleLinker(storage, selfLinker)
3254	server := httptest.NewServer(handler)
3255	defer server.Close()
3256
3257	item := &genericapitesting.Simple{
3258		ObjectMeta: metav1.ObjectMeta{
3259			Name:      ID,
3260			Namespace: "", // update should allow the client to send an empty namespace
3261		},
3262		Other: "bar",
3263	}
3264	body, err := runtime.Encode(testCodec, item)
3265	if err != nil {
3266		// The following cases will fail, so die now
3267		t.Fatalf("unexpected error: %v", err)
3268	}
3269
3270	client := http.Client{}
3271	request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
3272	if err != nil {
3273		t.Errorf("unexpected error: %v", err)
3274	}
3275	response, err := client.Do(request)
3276	if err != nil {
3277		t.Errorf("unexpected error: %v", err)
3278	}
3279	dump, _ := httputil.DumpResponse(response, true)
3280	t.Log(string(dump))
3281
3282	if simpleStorage.updated == nil || simpleStorage.updated.Name != item.Name {
3283		t.Errorf("Unexpected update value %#v, expected %#v.", simpleStorage.updated, item)
3284	}
3285	if utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) == selfLinker.called {
3286		t.Errorf("unexpected selfLinker.called: %v", selfLinker.called)
3287	}
3288}
3289
3290func TestUpdateInvokesAdmissionControl(t *testing.T) {
3291	for _, admit := range []admission.Interface{alwaysMutatingDeny{}, alwaysValidatingDeny{}} {
3292		t.Logf("Testing %T", admit)
3293
3294		storage := map[string]rest.Storage{}
3295		simpleStorage := SimpleRESTStorage{}
3296		ID := "id"
3297		storage["simple"] = &simpleStorage
3298		handler := handleInternal(storage, admit, selfLinker, nil)
3299		server := httptest.NewServer(handler)
3300		defer server.Close()
3301
3302		item := &genericapitesting.Simple{
3303			ObjectMeta: metav1.ObjectMeta{
3304				Name:      ID,
3305				Namespace: metav1.NamespaceDefault,
3306			},
3307			Other: "bar",
3308		}
3309		body, err := runtime.Encode(testCodec, item)
3310		if err != nil {
3311			// The following cases will fail, so die now
3312			t.Fatalf("unexpected error: %v", err)
3313		}
3314
3315		client := http.Client{}
3316		request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
3317		if err != nil {
3318			t.Errorf("unexpected error: %v", err)
3319		}
3320		response, err := client.Do(request)
3321		if err != nil {
3322			t.Errorf("unexpected error: %v", err)
3323		}
3324		dump, _ := httputil.DumpResponse(response, true)
3325		t.Log(string(dump))
3326
3327		if response.StatusCode != http.StatusForbidden {
3328			t.Errorf("Unexpected response %#v", response)
3329		}
3330	}
3331}
3332
3333func TestUpdateRequiresMatchingName(t *testing.T) {
3334	storage := map[string]rest.Storage{}
3335	simpleStorage := SimpleRESTStorage{}
3336	ID := "id"
3337	storage["simple"] = &simpleStorage
3338	handler := handle(storage)
3339	server := httptest.NewServer(handler)
3340	defer server.Close()
3341
3342	item := &genericapitesting.Simple{
3343		Other: "bar",
3344	}
3345	body, err := runtime.Encode(testCodec, item)
3346	if err != nil {
3347		// The following cases will fail, so die now
3348		t.Fatalf("unexpected error: %v", err)
3349	}
3350
3351	client := http.Client{}
3352	request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
3353	if err != nil {
3354		t.Errorf("unexpected error: %v", err)
3355	}
3356	response, err := client.Do(request)
3357	if err != nil {
3358		t.Errorf("unexpected error: %v", err)
3359	}
3360	if response.StatusCode != http.StatusBadRequest {
3361		dump, _ := httputil.DumpResponse(response, true)
3362		t.Log(string(dump))
3363		t.Errorf("Unexpected response %#v", response)
3364	}
3365}
3366
3367func TestUpdateAllowsMissingNamespace(t *testing.T) {
3368	storage := map[string]rest.Storage{}
3369	simpleStorage := SimpleRESTStorage{}
3370	ID := "id"
3371	storage["simple"] = &simpleStorage
3372	handler := handle(storage)
3373	server := httptest.NewServer(handler)
3374	defer server.Close()
3375
3376	item := &genericapitesting.Simple{
3377		ObjectMeta: metav1.ObjectMeta{
3378			Name: ID,
3379		},
3380		Other: "bar",
3381	}
3382	body, err := runtime.Encode(testCodec, item)
3383	if err != nil {
3384		// The following cases will fail, so die now
3385		t.Fatalf("unexpected error: %v", err)
3386	}
3387
3388	client := http.Client{}
3389	request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
3390	if err != nil {
3391		t.Errorf("unexpected error: %v", err)
3392	}
3393	response, err := client.Do(request)
3394	if err != nil {
3395		t.Errorf("unexpected error: %v", err)
3396	}
3397	dump, _ := httputil.DumpResponse(response, true)
3398	t.Log(string(dump))
3399
3400	if response.StatusCode != http.StatusOK {
3401		t.Errorf("Unexpected response %#v", response)
3402	}
3403}
3404
3405// when the object name and namespace can't be retrieved, don't update.  It isn't safe.
3406func TestUpdateDisallowsMismatchedNamespaceOnError(t *testing.T) {
3407	storage := map[string]rest.Storage{}
3408	simpleStorage := SimpleRESTStorage{}
3409	ID := "id"
3410	storage["simple"] = &simpleStorage
3411	selfLinker := &setTestSelfLinker{
3412		t:   t,
3413		err: fmt.Errorf("test error"),
3414	}
3415	handler := handleLinker(storage, selfLinker)
3416	server := httptest.NewServer(handler)
3417	defer server.Close()
3418
3419	item := &genericapitesting.Simple{
3420		ObjectMeta: metav1.ObjectMeta{
3421			Name:      ID,
3422			Namespace: "other", // does not match request
3423		},
3424		Other: "bar",
3425	}
3426	body, err := runtime.Encode(testCodec, item)
3427	if err != nil {
3428		// The following cases will fail, so die now
3429		t.Fatalf("unexpected error: %v", err)
3430	}
3431
3432	client := http.Client{}
3433	request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
3434	if err != nil {
3435		t.Errorf("unexpected error: %v", err)
3436	}
3437	response, err := client.Do(request)
3438	if err != nil {
3439		t.Errorf("unexpected error: %v", err)
3440	}
3441	dump, _ := httputil.DumpResponse(response, true)
3442	t.Log(string(dump))
3443
3444	if simpleStorage.updated != nil {
3445		t.Errorf("Unexpected update value %#v.", simpleStorage.updated)
3446	}
3447	if selfLinker.called {
3448		t.Errorf("self link ignored")
3449	}
3450}
3451
3452func TestUpdatePreventsMismatchedNamespace(t *testing.T) {
3453	storage := map[string]rest.Storage{}
3454	simpleStorage := SimpleRESTStorage{}
3455	ID := "id"
3456	storage["simple"] = &simpleStorage
3457	handler := handle(storage)
3458	server := httptest.NewServer(handler)
3459	defer server.Close()
3460
3461	item := &genericapitesting.Simple{
3462		ObjectMeta: metav1.ObjectMeta{
3463			Name:      ID,
3464			Namespace: "other",
3465		},
3466		Other: "bar",
3467	}
3468	body, err := runtime.Encode(testCodec, item)
3469	if err != nil {
3470		// The following cases will fail, so die now
3471		t.Fatalf("unexpected error: %v", err)
3472	}
3473
3474	client := http.Client{}
3475	request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
3476	if err != nil {
3477		t.Errorf("unexpected error: %v", err)
3478	}
3479	response, err := client.Do(request)
3480	if err != nil {
3481		t.Errorf("unexpected error: %v", err)
3482	}
3483	if response.StatusCode != http.StatusBadRequest {
3484		t.Errorf("Unexpected response %#v", response)
3485	}
3486}
3487
3488func TestUpdateMissing(t *testing.T) {
3489	storage := map[string]rest.Storage{}
3490	ID := "id"
3491	simpleStorage := SimpleRESTStorage{
3492		errors: map[string]error{"update": apierrors.NewNotFound(schema.GroupResource{Resource: "simples"}, ID)},
3493	}
3494	storage["simple"] = &simpleStorage
3495	handler := handle(storage)
3496	server := httptest.NewServer(handler)
3497	defer server.Close()
3498
3499	item := &genericapitesting.Simple{
3500		ObjectMeta: metav1.ObjectMeta{
3501			Name:      ID,
3502			Namespace: metav1.NamespaceDefault,
3503		},
3504		Other: "bar",
3505	}
3506	body, err := runtime.Encode(testCodec, item)
3507	if err != nil {
3508		t.Errorf("unexpected error: %v", err)
3509	}
3510
3511	client := http.Client{}
3512	request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
3513	if err != nil {
3514		t.Errorf("unexpected error: %v", err)
3515	}
3516	response, err := client.Do(request)
3517	if err != nil {
3518		t.Errorf("unexpected error: %v", err)
3519	}
3520	if response.StatusCode != http.StatusNotFound {
3521		t.Errorf("Unexpected response %#v", response)
3522	}
3523}
3524
3525func TestCreateNotFound(t *testing.T) {
3526	handler := handle(map[string]rest.Storage{
3527		"simple": &SimpleRESTStorage{
3528			// storage.Create can fail with not found error in theory.
3529			// See http://pr.k8s.io/486#discussion_r15037092.
3530			errors: map[string]error{"create": apierrors.NewNotFound(schema.GroupResource{Resource: "simples"}, "id")},
3531		},
3532	})
3533	server := httptest.NewServer(handler)
3534	defer server.Close()
3535	client := http.Client{}
3536
3537	simple := &genericapitesting.Simple{Other: "foo"}
3538	data, err := runtime.Encode(testCodec, simple)
3539	if err != nil {
3540		t.Errorf("unexpected error: %v", err)
3541	}
3542	request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple", bytes.NewBuffer(data))
3543	if err != nil {
3544		t.Errorf("unexpected error: %v", err)
3545	}
3546
3547	response, err := client.Do(request)
3548	if err != nil {
3549		t.Errorf("unexpected error: %v", err)
3550	}
3551
3552	if response.StatusCode != http.StatusNotFound {
3553		t.Errorf("Unexpected response %#v", response)
3554	}
3555}
3556
3557func TestCreateChecksDecode(t *testing.T) {
3558	handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
3559	server := httptest.NewServer(handler)
3560	defer server.Close()
3561	client := http.Client{}
3562
3563	simple := &example.Pod{}
3564	data, err := runtime.Encode(testCodec, simple)
3565	if err != nil {
3566		t.Errorf("unexpected error: %v", err)
3567	}
3568	request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple", bytes.NewBuffer(data))
3569	if err != nil {
3570		t.Errorf("unexpected error: %v", err)
3571	}
3572	response, err := client.Do(request)
3573	if err != nil {
3574		t.Errorf("unexpected error: %v", err)
3575	}
3576	if response.StatusCode != http.StatusBadRequest {
3577		t.Errorf("Unexpected response %#v", response)
3578	}
3579	b, err := ioutil.ReadAll(response.Body)
3580	if err != nil {
3581		t.Errorf("unexpected error: %v", err)
3582	} else if !strings.Contains(string(b), "cannot be handled as a Simple") {
3583		t.Errorf("unexpected response: %s", string(b))
3584	}
3585}
3586
3587func TestParentResourceIsRequired(t *testing.T) {
3588	storage := &SimpleTypedStorage{
3589		baseType: &genericapitesting.SimpleRoot{}, // a root scoped type
3590		item:     &genericapitesting.SimpleRoot{},
3591	}
3592	group := &APIGroupVersion{
3593		Storage: map[string]rest.Storage{
3594			"simple/sub": storage,
3595		},
3596		Root:            "/" + prefix,
3597		Creater:         scheme,
3598		Convertor:       scheme,
3599		UnsafeConvertor: runtime.UnsafeObjectConvertor(scheme),
3600		Defaulter:       scheme,
3601		Typer:           scheme,
3602		Linker:          selfLinker,
3603		RootScopedKinds: sets.NewString("SimpleRoot"),
3604
3605		EquivalentResourceRegistry: runtime.NewEquivalentResourceRegistry(),
3606
3607		Admit: admissionControl,
3608
3609		GroupVersion:           newGroupVersion,
3610		OptionsExternalVersion: &newGroupVersion,
3611
3612		Serializer:     codecs,
3613		ParameterCodec: parameterCodec,
3614	}
3615	container := restful.NewContainer()
3616	if _, err := group.InstallREST(container); err == nil {
3617		t.Fatal("expected error")
3618	}
3619
3620	storage = &SimpleTypedStorage{
3621		baseType: &genericapitesting.SimpleRoot{}, // a root scoped type
3622		item:     &genericapitesting.SimpleRoot{},
3623	}
3624	group = &APIGroupVersion{
3625		Storage: map[string]rest.Storage{
3626			"simple":     &SimpleRESTStorage{},
3627			"simple/sub": storage,
3628		},
3629		Root:            "/" + prefix,
3630		Creater:         scheme,
3631		Convertor:       scheme,
3632		UnsafeConvertor: runtime.UnsafeObjectConvertor(scheme),
3633		Defaulter:       scheme,
3634		Typer:           scheme,
3635		Linker:          selfLinker,
3636
3637		EquivalentResourceRegistry: runtime.NewEquivalentResourceRegistry(),
3638
3639		Admit: admissionControl,
3640
3641		GroupVersion:           newGroupVersion,
3642		OptionsExternalVersion: &newGroupVersion,
3643
3644		Serializer:     codecs,
3645		ParameterCodec: parameterCodec,
3646	}
3647	container = restful.NewContainer()
3648	if _, err := group.InstallREST(container); err != nil {
3649		t.Fatal(err)
3650	}
3651
3652	handler := genericapifilters.WithRequestInfo(container, newTestRequestInfoResolver())
3653
3654	// resource is NOT registered in the root scope
3655	w := httptest.NewRecorder()
3656	handler.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/simple/test/sub"}})
3657	if w.Code != http.StatusNotFound {
3658		t.Errorf("expected not found: %#v", w)
3659	}
3660
3661	// resource is registered in the namespace scope
3662	w = httptest.NewRecorder()
3663	handler.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/test/simple/test/sub"}})
3664	if w.Code != http.StatusOK {
3665		t.Fatalf("expected OK: %#v", w)
3666	}
3667	if storage.actualNamespace != "test" {
3668		t.Errorf("namespace should be set %#v", storage)
3669	}
3670}
3671
3672func TestNamedCreaterWithName(t *testing.T) {
3673	pathName := "helloworld"
3674	storage := &NamedCreaterRESTStorage{SimpleRESTStorage: &SimpleRESTStorage{}}
3675	handler := handle(map[string]rest.Storage{
3676		"simple":     &SimpleRESTStorage{},
3677		"simple/sub": storage,
3678	})
3679	server := httptest.NewServer(handler)
3680	defer server.Close()
3681	client := http.Client{}
3682
3683	simple := &genericapitesting.Simple{Other: "foo"}
3684	data, err := runtime.Encode(testCodec, simple)
3685	if err != nil {
3686		t.Errorf("unexpected error: %v", err)
3687	}
3688	request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+pathName+"/sub", bytes.NewBuffer(data))
3689	if err != nil {
3690		t.Errorf("unexpected error: %v", err)
3691	}
3692	response, err := client.Do(request)
3693	if err != nil {
3694		t.Errorf("unexpected error: %v", err)
3695	}
3696	if response.StatusCode != http.StatusCreated {
3697		t.Errorf("Unexpected response %#v", response)
3698	}
3699	if storage.createdName != pathName {
3700		t.Errorf("Did not get expected name in create context. Got: %s, Expected: %s", storage.createdName, pathName)
3701	}
3702}
3703
3704func TestNamedCreaterWithoutName(t *testing.T) {
3705	storage := &NamedCreaterRESTStorage{
3706		SimpleRESTStorage: &SimpleRESTStorage{
3707			injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
3708				time.Sleep(5 * time.Millisecond)
3709				return obj, nil
3710			},
3711		},
3712	}
3713
3714	selfLinker := &setTestSelfLinker{
3715		t:           t,
3716		name:        "bar",
3717		namespace:   "default",
3718		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo",
3719	}
3720	handler := handleLinker(map[string]rest.Storage{"foo": storage}, selfLinker)
3721	server := httptest.NewServer(handler)
3722	defer server.Close()
3723	client := http.Client{}
3724
3725	simple := &genericapitesting.Simple{
3726		Other: "bar",
3727	}
3728	data, err := runtime.Encode(testCodec, simple)
3729	if err != nil {
3730		t.Errorf("unexpected error: %v", err)
3731	}
3732	request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo", bytes.NewBuffer(data))
3733	if err != nil {
3734		t.Errorf("unexpected error: %v", err)
3735	}
3736
3737	wg := sync.WaitGroup{}
3738	wg.Add(1)
3739	var response *http.Response
3740	go func() {
3741		response, err = client.Do(request)
3742		wg.Done()
3743	}()
3744	wg.Wait()
3745	if err != nil {
3746		t.Errorf("unexpected error: %v", err)
3747	}
3748	// empty name is not allowed for NamedCreater
3749	if response.StatusCode != http.StatusBadRequest {
3750		t.Errorf("Unexpected response %#v", response)
3751	}
3752}
3753
3754type namePopulatorAdmissionControl struct {
3755	t            *testing.T
3756	populateName string
3757}
3758
3759func (npac *namePopulatorAdmissionControl) Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error) {
3760	if a.GetName() != npac.populateName {
3761		npac.t.Errorf("Unexpected name: got %q, expected %q", a.GetName(), npac.populateName)
3762	}
3763	return nil
3764}
3765
3766func (npac *namePopulatorAdmissionControl) Handles(operation admission.Operation) bool {
3767	return true
3768}
3769
3770var _ admission.ValidationInterface = &namePopulatorAdmissionControl{}
3771
3772func TestNamedCreaterWithGenerateName(t *testing.T) {
3773	populateName := "bar"
3774	storage := &SimpleRESTStorage{
3775		injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
3776			time.Sleep(5 * time.Millisecond)
3777			if metadata, err := meta.Accessor(obj); err == nil {
3778				if len(metadata.GetName()) != 0 {
3779					t.Errorf("Unexpected name %q", metadata.GetName())
3780				}
3781				metadata.SetName(populateName)
3782			} else {
3783				return nil, err
3784			}
3785			return obj, nil
3786		},
3787	}
3788
3789	selfLinker := &setTestSelfLinker{
3790		t:           t,
3791		namespace:   "default",
3792		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo",
3793	}
3794
3795	ac := &namePopulatorAdmissionControl{
3796		t:            t,
3797		populateName: populateName,
3798	}
3799
3800	handler := handleInternal(map[string]rest.Storage{"foo": storage}, ac, selfLinker, nil)
3801	server := httptest.NewServer(handler)
3802	defer server.Close()
3803	client := http.Client{}
3804
3805	simple := &genericapitesting.Simple{
3806		Other: "bar",
3807	}
3808	data, err := runtime.Encode(testCodec, simple)
3809	if err != nil {
3810		t.Errorf("unexpected error: %v", err)
3811	}
3812	request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo", bytes.NewBuffer(data))
3813	if err != nil {
3814		t.Errorf("unexpected error: %v", err)
3815	}
3816
3817	wg := sync.WaitGroup{}
3818	wg.Add(1)
3819	var response *http.Response
3820	go func() {
3821		response, err = client.Do(request)
3822		wg.Done()
3823	}()
3824	wg.Wait()
3825	if err != nil {
3826		t.Errorf("unexpected error: %v", err)
3827	}
3828	if response.StatusCode != http.StatusCreated {
3829		t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusOK, response)
3830	}
3831
3832	var itemOut genericapitesting.Simple
3833	body, err := extractBody(response, &itemOut)
3834	if err != nil {
3835		t.Errorf("unexpected error: %v %#v", err, response)
3836	}
3837
3838	itemOut.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
3839	simple.Name = populateName
3840	if !reflect.DeepEqual(&itemOut, simple) {
3841		t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simple, string(body))
3842	}
3843}
3844
3845func TestUpdateChecksDecode(t *testing.T) {
3846	handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
3847	server := httptest.NewServer(handler)
3848	defer server.Close()
3849	client := http.Client{}
3850
3851	simple := &example.Pod{}
3852	data, err := runtime.Encode(testCodec, simple)
3853	if err != nil {
3854		t.Errorf("unexpected error: %v", err)
3855	}
3856	request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/bar", bytes.NewBuffer(data))
3857	if err != nil {
3858		t.Errorf("unexpected error: %v", err)
3859	}
3860	response, err := client.Do(request)
3861	if err != nil {
3862		t.Errorf("unexpected error: %v", err)
3863	}
3864	if response.StatusCode != http.StatusBadRequest {
3865		t.Errorf("Unexpected response %#v\n%s", response, readBodyOrDie(response.Body))
3866	}
3867	b, err := ioutil.ReadAll(response.Body)
3868	if err != nil {
3869		t.Errorf("unexpected error: %v", err)
3870	} else if !strings.Contains(string(b), "cannot be handled as a Simple") {
3871		t.Errorf("unexpected response: %s", string(b))
3872	}
3873}
3874
3875type setTestSelfLinker struct {
3876	t              *testing.T
3877	expectedSet    string
3878	alternativeSet sets.String
3879	name           string
3880	namespace      string
3881	called         bool
3882	err            error
3883}
3884
3885func (s *setTestSelfLinker) Namespace(runtime.Object) (string, error) { return s.namespace, s.err }
3886func (s *setTestSelfLinker) Name(runtime.Object) (string, error)      { return s.name, s.err }
3887func (s *setTestSelfLinker) SelfLink(runtime.Object) (string, error)  { return "", s.err }
3888func (s *setTestSelfLinker) SetSelfLink(obj runtime.Object, selfLink string) error {
3889	if e, a := s.expectedSet, selfLink; e != a {
3890		if !s.alternativeSet.Has(a) {
3891			s.t.Errorf("expected '%v', got '%v'", e, a)
3892		}
3893	}
3894	s.called = true
3895	return s.err
3896}
3897
3898func TestCreate(t *testing.T) {
3899	storage := SimpleRESTStorage{
3900		injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
3901			time.Sleep(5 * time.Millisecond)
3902			return obj, nil
3903		},
3904	}
3905	selfLinker := &setTestSelfLinker{
3906		t:           t,
3907		name:        "bar",
3908		namespace:   "default",
3909		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo/bar",
3910	}
3911	handler := handleLinker(map[string]rest.Storage{"foo": &storage}, selfLinker)
3912	server := httptest.NewServer(handler)
3913	defer server.Close()
3914	client := http.Client{}
3915
3916	simple := &genericapitesting.Simple{
3917		Other: "bar",
3918	}
3919	data, err := runtime.Encode(testCodec, simple)
3920	if err != nil {
3921		t.Errorf("unexpected error: %v", err)
3922	}
3923	request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo", bytes.NewBuffer(data))
3924	if err != nil {
3925		t.Errorf("unexpected error: %v", err)
3926	}
3927
3928	wg := sync.WaitGroup{}
3929	wg.Add(1)
3930	var response *http.Response
3931	go func() {
3932		response, err = client.Do(request)
3933		wg.Done()
3934	}()
3935	wg.Wait()
3936	if err != nil {
3937		t.Errorf("unexpected error: %v", err)
3938	}
3939
3940	var itemOut genericapitesting.Simple
3941	body, err := extractBody(response, &itemOut)
3942	if err != nil {
3943		t.Errorf("unexpected error: %v %#v", err, response)
3944	}
3945
3946	itemOut.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
3947	if !reflect.DeepEqual(&itemOut, simple) {
3948		t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simple, string(body))
3949	}
3950	if response.StatusCode != http.StatusCreated {
3951		t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusOK, response)
3952	}
3953	if utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) == selfLinker.called {
3954		t.Errorf("unexpected selfLinker.called: %v", selfLinker.called)
3955	}
3956}
3957
3958func TestCreateYAML(t *testing.T) {
3959	storage := SimpleRESTStorage{
3960		injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
3961			time.Sleep(5 * time.Millisecond)
3962			return obj, nil
3963		},
3964	}
3965	selfLinker := &setTestSelfLinker{
3966		t:           t,
3967		name:        "bar",
3968		namespace:   "default",
3969		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo/bar",
3970	}
3971	handler := handleLinker(map[string]rest.Storage{"foo": &storage}, selfLinker)
3972	server := httptest.NewServer(handler)
3973	defer server.Close()
3974	client := http.Client{}
3975
3976	// yaml encoder
3977	simple := &genericapitesting.Simple{
3978		Other: "bar",
3979	}
3980	info, ok := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), "application/yaml")
3981	if !ok {
3982		t.Fatal("No yaml serializer")
3983	}
3984	encoder := codecs.EncoderForVersion(info.Serializer, testGroupVersion)
3985	decoder := codecs.DecoderToVersion(info.Serializer, testInternalGroupVersion)
3986
3987	data, err := runtime.Encode(encoder, simple)
3988	if err != nil {
3989		t.Fatalf("unexpected error: %v", err)
3990	}
3991	request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo", bytes.NewBuffer(data))
3992	if err != nil {
3993		t.Fatalf("unexpected error: %v", err)
3994	}
3995	request.Header.Set("Accept", "application/yaml, application/json")
3996	request.Header.Set("Content-Type", "application/yaml")
3997
3998	wg := sync.WaitGroup{}
3999	wg.Add(1)
4000	var response *http.Response
4001	go func() {
4002		response, err = client.Do(request)
4003		wg.Done()
4004	}()
4005	wg.Wait()
4006	if err != nil {
4007		t.Fatalf("unexpected error: %v", err)
4008	}
4009
4010	var itemOut genericapitesting.Simple
4011	body, err := extractBodyDecoder(response, &itemOut, decoder)
4012	if err != nil {
4013		t.Fatalf("unexpected error: %v %#v", err, response)
4014	}
4015
4016	itemOut.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
4017	if !reflect.DeepEqual(&itemOut, simple) {
4018		t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simple, string(body))
4019	}
4020	if response.StatusCode != http.StatusCreated {
4021		t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusOK, response)
4022	}
4023	if utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) == selfLinker.called {
4024		t.Errorf("unexpected selfLinker.called: %v", selfLinker.called)
4025	}
4026}
4027
4028func TestCreateInNamespace(t *testing.T) {
4029	storage := SimpleRESTStorage{
4030		injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
4031			time.Sleep(5 * time.Millisecond)
4032			return obj, nil
4033		},
4034	}
4035	selfLinker := &setTestSelfLinker{
4036		t:           t,
4037		name:        "bar",
4038		namespace:   "other",
4039		expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/foo/bar",
4040	}
4041	handler := handleLinker(map[string]rest.Storage{"foo": &storage}, selfLinker)
4042	server := httptest.NewServer(handler)
4043	defer server.Close()
4044	client := http.Client{}
4045
4046	simple := &genericapitesting.Simple{
4047		Other: "bar",
4048	}
4049	data, err := runtime.Encode(testCodec, simple)
4050	if err != nil {
4051		t.Fatalf("unexpected error: %v", err)
4052	}
4053	request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/other/foo", bytes.NewBuffer(data))
4054	if err != nil {
4055		t.Fatalf("unexpected error: %v", err)
4056	}
4057
4058	wg := sync.WaitGroup{}
4059	wg.Add(1)
4060	var response *http.Response
4061	go func() {
4062		response, err = client.Do(request)
4063		wg.Done()
4064	}()
4065	wg.Wait()
4066	if err != nil {
4067		t.Fatalf("unexpected error: %v", err)
4068	}
4069
4070	var itemOut genericapitesting.Simple
4071	body, err := extractBody(response, &itemOut)
4072	if err != nil {
4073		t.Fatalf("unexpected error: %v\n%s", err, data)
4074	}
4075
4076	itemOut.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
4077	if !reflect.DeepEqual(&itemOut, simple) {
4078		t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simple, string(body))
4079	}
4080	if response.StatusCode != http.StatusCreated {
4081		t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusOK, response)
4082	}
4083	if utilfeature.DefaultFeatureGate.Enabled(features.RemoveSelfLink) == selfLinker.called {
4084		t.Errorf("unexpected selfLinker.called: %v", selfLinker.called)
4085	}
4086}
4087
4088func TestCreateInvokeAdmissionControl(t *testing.T) {
4089	for _, admit := range []admission.Interface{alwaysMutatingDeny{}, alwaysValidatingDeny{}} {
4090		t.Logf("Testing %T", admit)
4091
4092		storage := SimpleRESTStorage{
4093			injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
4094				time.Sleep(5 * time.Millisecond)
4095				return obj, nil
4096			},
4097		}
4098		selfLinker := &setTestSelfLinker{
4099			t:           t,
4100			name:        "bar",
4101			namespace:   "other",
4102			expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/foo/bar",
4103		}
4104		handler := handleInternal(map[string]rest.Storage{"foo": &storage}, admit, selfLinker, nil)
4105		server := httptest.NewServer(handler)
4106		defer server.Close()
4107		client := http.Client{}
4108
4109		simple := &genericapitesting.Simple{
4110			Other: "bar",
4111		}
4112		data, err := runtime.Encode(testCodec, simple)
4113		if err != nil {
4114			t.Errorf("unexpected error: %v", err)
4115		}
4116		request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/other/foo", bytes.NewBuffer(data))
4117		if err != nil {
4118			t.Errorf("unexpected error: %v", err)
4119		}
4120
4121		var response *http.Response
4122		response, err = client.Do(request)
4123		if err != nil {
4124			t.Errorf("unexpected error: %v", err)
4125		}
4126		if response.StatusCode != http.StatusForbidden {
4127			t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusForbidden, response)
4128		}
4129	}
4130}
4131
4132func expectAPIStatus(t *testing.T, method, url string, data []byte, code int) *metav1.Status {
4133	t.Helper()
4134	client := http.Client{}
4135	request, err := http.NewRequest(method, url, bytes.NewBuffer(data))
4136	if err != nil {
4137		t.Fatalf("unexpected error %#v", err)
4138		return nil
4139	}
4140	response, err := client.Do(request)
4141	if err != nil {
4142		t.Fatalf("unexpected error on %s %s: %v", method, url, err)
4143		return nil
4144	}
4145	var status metav1.Status
4146	body, err := extractBody(response, &status)
4147	if err != nil {
4148		t.Fatalf("unexpected error on %s %s: %v\nbody:\n%s", method, url, err, body)
4149		return nil
4150	}
4151	if code != response.StatusCode {
4152		t.Fatalf("Expected %s %s to return %d, Got %d: %v", method, url, code, response.StatusCode, body)
4153	}
4154	return &status
4155}
4156
4157func TestDelayReturnsError(t *testing.T) {
4158	storage := SimpleRESTStorage{
4159		injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
4160			return nil, apierrors.NewAlreadyExists(schema.GroupResource{Resource: "foos"}, "bar")
4161		},
4162	}
4163	handler := handle(map[string]rest.Storage{"foo": &storage})
4164	server := httptest.NewServer(handler)
4165	defer server.Close()
4166
4167	status := expectAPIStatus(t, "DELETE", fmt.Sprintf("%s/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo/bar", server.URL), nil, http.StatusConflict)
4168	if status.Status != metav1.StatusFailure || status.Message == "" || status.Details == nil || status.Reason != metav1.StatusReasonAlreadyExists {
4169		t.Errorf("Unexpected status %#v", status)
4170	}
4171}
4172
4173type UnregisteredAPIObject struct {
4174	Value string
4175}
4176
4177func (obj *UnregisteredAPIObject) GetObjectKind() schema.ObjectKind {
4178	return schema.EmptyObjectKind
4179}
4180func (obj *UnregisteredAPIObject) DeepCopyObject() runtime.Object {
4181	if obj == nil {
4182		return nil
4183	}
4184	clone := *obj
4185	return &clone
4186}
4187
4188func TestWriteJSONDecodeError(t *testing.T) {
4189	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
4190		responsewriters.WriteObjectNegotiated(codecs, negotiation.DefaultEndpointRestrictions, newGroupVersion, w, req, http.StatusOK, &UnregisteredAPIObject{"Undecodable"})
4191	}))
4192	defer server.Close()
4193	// Decode error response behavior is dictated by
4194	// apiserver/pkg/endpoints/handlers/responsewriters/status.go::ErrorToAPIStatus().
4195	// Unless specific metav1.Status() parameters are implemented for the particular error in question, such that
4196	// the status code is defined, metav1 errors where error.status == metav1.StatusFailure
4197	// will throw a '500 Internal Server Error'. Non-metav1 type errors will always throw a '500 Internal Server Error'.
4198	status := expectAPIStatus(t, "GET", server.URL, nil, http.StatusInternalServerError)
4199	if status.Reason != metav1.StatusReasonUnknown {
4200		t.Errorf("unexpected reason %#v", status)
4201	}
4202	if !strings.Contains(status.Message, "no kind is registered for the type endpoints.UnregisteredAPIObject") {
4203		t.Errorf("unexpected message %#v", status)
4204	}
4205}
4206
4207type marshalError struct {
4208	err error
4209}
4210
4211func (m *marshalError) MarshalJSON() ([]byte, error) {
4212	return []byte{}, m.err
4213}
4214
4215func TestWriteRAWJSONMarshalError(t *testing.T) {
4216	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
4217		responsewriters.WriteRawJSON(http.StatusOK, &marshalError{errors.New("Undecodable")}, w)
4218	}))
4219	defer server.Close()
4220	client := http.Client{}
4221	resp, err := client.Get(server.URL)
4222	if err != nil {
4223		t.Errorf("unexpected error: %v", err)
4224	}
4225
4226	if resp.StatusCode != http.StatusInternalServerError {
4227		t.Errorf("unexpected status code %d", resp.StatusCode)
4228	}
4229}
4230
4231func TestCreateTimeout(t *testing.T) {
4232	testOver := make(chan struct{})
4233	defer close(testOver)
4234	storage := SimpleRESTStorage{
4235		injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
4236			// Eliminate flakes by ensuring the create operation takes longer than this test.
4237			<-testOver
4238			return obj, nil
4239		},
4240	}
4241	handler := handle(map[string]rest.Storage{
4242		"foo": &storage,
4243	})
4244	server := httptest.NewServer(handler)
4245	defer server.Close()
4246
4247	simple := &genericapitesting.Simple{Other: "foo"}
4248	data, err := runtime.Encode(testCodec, simple)
4249	if err != nil {
4250		t.Errorf("unexpected error: %v", err)
4251	}
4252	itemOut := expectAPIStatus(t, "POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo?timeout=4ms", data, http.StatusGatewayTimeout)
4253	if itemOut.Status != metav1.StatusFailure || itemOut.Reason != metav1.StatusReasonTimeout {
4254		t.Errorf("Unexpected status %#v", itemOut)
4255	}
4256}
4257
4258func TestCreateChecksAPIVersion(t *testing.T) {
4259	handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
4260	server := httptest.NewServer(handler)
4261	defer server.Close()
4262	client := http.Client{}
4263
4264	simple := &genericapitesting.Simple{}
4265	//using newCodec and send the request to testVersion URL shall cause a discrepancy in apiVersion
4266	data, err := runtime.Encode(newCodec, simple)
4267	if err != nil {
4268		t.Errorf("unexpected error: %v", err)
4269	}
4270	request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple", bytes.NewBuffer(data))
4271	if err != nil {
4272		t.Errorf("unexpected error: %v", err)
4273	}
4274	response, err := client.Do(request)
4275	if err != nil {
4276		t.Errorf("unexpected error: %v", err)
4277	}
4278	if response.StatusCode != http.StatusBadRequest {
4279		t.Errorf("Unexpected response %#v", response)
4280	}
4281	b, err := ioutil.ReadAll(response.Body)
4282	if err != nil {
4283		t.Errorf("unexpected error: %v", err)
4284	} else if !strings.Contains(string(b), "does not match the expected API version") {
4285		t.Errorf("unexpected response: %s", string(b))
4286	}
4287}
4288
4289func TestCreateDefaultsAPIVersion(t *testing.T) {
4290	handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
4291	server := httptest.NewServer(handler)
4292	defer server.Close()
4293	client := http.Client{}
4294
4295	simple := &genericapitesting.Simple{}
4296	data, err := runtime.Encode(codec, simple)
4297	if err != nil {
4298		t.Errorf("unexpected error: %v", err)
4299	}
4300
4301	m := make(map[string]interface{})
4302	if err := json.Unmarshal(data, &m); err != nil {
4303		t.Errorf("unexpected error: %v", err)
4304	}
4305	delete(m, "apiVersion")
4306	data, err = json.Marshal(m)
4307	if err != nil {
4308		t.Errorf("unexpected error: %v", err)
4309	}
4310
4311	request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple", bytes.NewBuffer(data))
4312	if err != nil {
4313		t.Errorf("unexpected error: %v", err)
4314	}
4315	response, err := client.Do(request)
4316	if err != nil {
4317		t.Errorf("unexpected error: %v", err)
4318	}
4319	if response.StatusCode != http.StatusCreated {
4320		t.Errorf("unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusCreated, response)
4321	}
4322}
4323
4324func TestUpdateChecksAPIVersion(t *testing.T) {
4325	handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
4326	server := httptest.NewServer(handler)
4327	defer server.Close()
4328	client := http.Client{}
4329
4330	simple := &genericapitesting.Simple{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}
4331	data, err := runtime.Encode(newCodec, simple)
4332	if err != nil {
4333		t.Fatalf("unexpected error: %v", err)
4334	}
4335	request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/bar", bytes.NewBuffer(data))
4336	if err != nil {
4337		t.Fatalf("unexpected error: %v", err)
4338	}
4339	response, err := client.Do(request)
4340	if err != nil {
4341		t.Fatalf("unexpected error: %v", err)
4342	}
4343	if response.StatusCode != http.StatusBadRequest {
4344		t.Errorf("Unexpected response %#v", response)
4345	}
4346	b, err := ioutil.ReadAll(response.Body)
4347	if err != nil {
4348		t.Errorf("unexpected error: %v", err)
4349	} else if !strings.Contains(string(b), "does not match the expected API version") {
4350		t.Errorf("unexpected response: %s", string(b))
4351	}
4352}
4353
4354// runRequest is used by TestDryRun since it runs the test twice in a
4355// row with a slightly different URL (one has ?dryRun, one doesn't).
4356func runRequest(t *testing.T, path, verb string, data []byte, contentType string) *http.Response {
4357	request, err := http.NewRequest(verb, path, bytes.NewBuffer(data))
4358	if err != nil {
4359		t.Fatalf("unexpected error: %v", err)
4360	}
4361	if contentType != "" {
4362		request.Header.Set("Content-Type", contentType)
4363	}
4364	response, err := http.DefaultClient.Do(request)
4365	if err != nil {
4366		t.Fatalf("unexpected error: %v", err)
4367	}
4368	return response
4369}
4370
4371// encodeOrFatal is used by TestDryRun to parse an object and stop right
4372// away if it fails.
4373func encodeOrFatal(t *testing.T, obj runtime.Object) []byte {
4374	data, err := runtime.Encode(testCodec, obj)
4375	if err != nil {
4376		t.Fatalf("unexpected error: %v", err)
4377	}
4378	return data
4379}
4380
4381type SimpleRESTStorageWithDeleteCollection struct {
4382	SimpleRESTStorage
4383}
4384
4385// Delete collection doesn't do much, but let us test this path.
4386func (storage *SimpleRESTStorageWithDeleteCollection) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *metainternalversion.ListOptions) (runtime.Object, error) {
4387	storage.checkContext(ctx)
4388	return nil, nil
4389}
4390
4391func TestDryRunDisabled(t *testing.T) {
4392	defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DryRun, false)()
4393
4394	tests := []struct {
4395		path        string
4396		verb        string
4397		data        []byte
4398		contentType string
4399	}{
4400		{path: "/namespaces/default/simples", verb: "POST", data: encodeOrFatal(t, &genericapitesting.Simple{Other: "bar"})},
4401		{path: "/namespaces/default/simples/id", verb: "PUT", data: encodeOrFatal(t, &genericapitesting.Simple{ObjectMeta: metav1.ObjectMeta{Name: "id"}, Other: "bar"})},
4402		{path: "/namespaces/default/simples/id", verb: "PATCH", data: []byte(`{"labels":{"foo":"bar"}}`), contentType: "application/merge-patch+json; charset=UTF-8"},
4403		{path: "/namespaces/default/simples/id", verb: "DELETE"},
4404		{path: "/namespaces/default/simples", verb: "DELETE"},
4405		{path: "/namespaces/default/simples/id/subsimple", verb: "DELETE"},
4406	}
4407
4408	server := httptest.NewServer(handle(map[string]rest.Storage{
4409		"simples": &SimpleRESTStorageWithDeleteCollection{
4410			SimpleRESTStorage{
4411				item: genericapitesting.Simple{
4412					ObjectMeta: metav1.ObjectMeta{
4413						Name:      "id",
4414						Namespace: "",
4415						UID:       "uid",
4416					},
4417					Other: "bar",
4418				},
4419			},
4420		},
4421		"simples/subsimple": &SimpleXGSubresourceRESTStorage{
4422			item: genericapitesting.SimpleXGSubresource{
4423				SubresourceInfo: "foo",
4424			},
4425			itemGVK: testGroup2Version.WithKind("SimpleXGSubresource"),
4426		},
4427	}))
4428	defer server.Close()
4429	for _, test := range tests {
4430		baseURL := server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version
4431		response := runRequest(t, baseURL+test.path, test.verb, test.data, test.contentType)
4432		if response.StatusCode == http.StatusBadRequest {
4433			t.Fatalf("unexpected BadRequest: %#v", response)
4434		}
4435		response = runRequest(t, baseURL+test.path+"?dryRun", test.verb, test.data, test.contentType)
4436		if response.StatusCode != http.StatusBadRequest {
4437			t.Fatalf("unexpected non BadRequest: %#v", response)
4438		}
4439	}
4440}
4441
4442type SimpleXGSubresourceRESTStorage struct {
4443	item    genericapitesting.SimpleXGSubresource
4444	itemGVK schema.GroupVersionKind
4445}
4446
4447var _ = rest.GroupVersionKindProvider(&SimpleXGSubresourceRESTStorage{})
4448
4449func (storage *SimpleXGSubresourceRESTStorage) New() runtime.Object {
4450	return &genericapitesting.SimpleXGSubresource{}
4451}
4452
4453func (storage *SimpleXGSubresourceRESTStorage) Get(ctx context.Context, id string, options *metav1.GetOptions) (runtime.Object, error) {
4454	return storage.item.DeepCopyObject(), nil
4455}
4456
4457func (storage *SimpleXGSubresourceRESTStorage) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
4458	return nil, true, nil
4459}
4460
4461func (storage *SimpleXGSubresourceRESTStorage) GroupVersionKind(containingGV schema.GroupVersion) schema.GroupVersionKind {
4462	return storage.itemGVK
4463}
4464
4465func TestXGSubresource(t *testing.T) {
4466	container := restful.NewContainer()
4467	container.Router(restful.CurlyRouter{})
4468	mux := container.ServeMux
4469
4470	itemID := "theID"
4471	subresourceStorage := &SimpleXGSubresourceRESTStorage{
4472		item: genericapitesting.SimpleXGSubresource{
4473			SubresourceInfo: "foo",
4474		},
4475		itemGVK: testGroup2Version.WithKind("SimpleXGSubresource"),
4476	}
4477	storage := map[string]rest.Storage{
4478		"simple":           &SimpleRESTStorage{},
4479		"simple/subsimple": subresourceStorage,
4480	}
4481
4482	group := APIGroupVersion{
4483		Storage: storage,
4484
4485		Creater:         scheme,
4486		Convertor:       scheme,
4487		UnsafeConvertor: runtime.UnsafeObjectConvertor(scheme),
4488		Defaulter:       scheme,
4489		Typer:           scheme,
4490		Linker:          selfLinker,
4491
4492		EquivalentResourceRegistry: runtime.NewEquivalentResourceRegistry(),
4493
4494		ParameterCodec: parameterCodec,
4495
4496		Admit: admissionControl,
4497
4498		Root:                   "/" + prefix,
4499		GroupVersion:           testGroupVersion,
4500		OptionsExternalVersion: &testGroupVersion,
4501		Serializer:             codecs,
4502	}
4503
4504	if _, err := (&group).InstallREST(container); err != nil {
4505		panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err))
4506	}
4507
4508	server := newTestServer(defaultAPIServer{mux, container})
4509	defer server.Close()
4510
4511	resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/subsimple")
4512	if err != nil {
4513		t.Fatalf("unexpected error: %v", err)
4514	}
4515	if resp.StatusCode != http.StatusOK {
4516		t.Fatalf("unexpected response: %#v", resp)
4517	}
4518	var itemOut genericapitesting.SimpleXGSubresource
4519	body, err := extractBody(resp, &itemOut)
4520	if err != nil {
4521		t.Errorf("unexpected error: %v", err)
4522	}
4523
4524	// Test if the returned object has the expected group, version and kind
4525	// We are directly unmarshaling JSON here because TypeMeta cannot be decoded through the
4526	// installed decoders. TypeMeta cannot be decoded because it is added to the ignored
4527	// conversion type list in API scheme and hence cannot be converted from input type object
4528	// to output type object. So it's values don't appear in the decoded output object.
4529	decoder := json.NewDecoder(strings.NewReader(body))
4530	var itemFromBody genericapitesting.SimpleXGSubresource
4531	err = decoder.Decode(&itemFromBody)
4532	if err != nil {
4533		t.Errorf("unexpected JSON decoding error: %v", err)
4534	}
4535	if want := fmt.Sprintf("%s/%s", testGroup2Version.Group, testGroup2Version.Version); itemFromBody.APIVersion != want {
4536		t.Errorf("unexpected APIVersion got: %+v want: %+v", itemFromBody.APIVersion, want)
4537	}
4538	if itemFromBody.Kind != "SimpleXGSubresource" {
4539		t.Errorf("unexpected Kind got: %+v want: SimpleXGSubresource", itemFromBody.Kind)
4540	}
4541
4542	if itemOut.Name != subresourceStorage.item.Name {
4543		t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, subresourceStorage.item, string(body))
4544	}
4545}
4546
4547func readBodyOrDie(r io.Reader) []byte {
4548	body, err := ioutil.ReadAll(r)
4549	if err != nil {
4550		panic(err)
4551	}
4552	return body
4553}
4554
4555// BenchmarkUpdateProtobuf measures the cost of processing an update on the server in proto
4556func BenchmarkUpdateProtobuf(b *testing.B) {
4557	items := benchmarkItems(b)
4558
4559	simpleStorage := &SimpleRESTStorage{}
4560	handler := handle(map[string]rest.Storage{"simples": simpleStorage})
4561	server := httptest.NewServer(handler)
4562	defer server.Close()
4563	client := http.Client{}
4564
4565	dest, _ := url.Parse(server.URL)
4566	dest.Path = "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/foo/simples/bar"
4567	dest.RawQuery = ""
4568
4569	info, _ := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), "application/vnd.kubernetes.protobuf")
4570	e := codecs.EncoderForVersion(info.Serializer, newGroupVersion)
4571	data, err := runtime.Encode(e, &items[0])
4572	if err != nil {
4573		b.Fatal(err)
4574	}
4575
4576	b.ResetTimer()
4577	for i := 0; i < b.N; i++ {
4578		request, err := http.NewRequest("PUT", dest.String(), bytes.NewReader(data))
4579		if err != nil {
4580			b.Fatalf("unexpected error: %v", err)
4581		}
4582		request.Header.Set("Accept", "application/vnd.kubernetes.protobuf")
4583		request.Header.Set("Content-Type", "application/vnd.kubernetes.protobuf")
4584		response, err := client.Do(request)
4585		if err != nil {
4586			b.Fatalf("unexpected error: %v", err)
4587		}
4588		if response.StatusCode != http.StatusBadRequest {
4589			body, _ := ioutil.ReadAll(response.Body)
4590			b.Fatalf("Unexpected response %#v\n%s", response, body)
4591		}
4592		_, _ = ioutil.ReadAll(response.Body)
4593		response.Body.Close()
4594	}
4595	b.StopTimer()
4596}
4597
4598func newTestServer(handler http.Handler) *httptest.Server {
4599	handler = genericapifilters.WithRequestInfo(handler, newTestRequestInfoResolver())
4600	return httptest.NewServer(handler)
4601}
4602
4603func newTestRequestInfoResolver() *request.RequestInfoFactory {
4604	return &request.RequestInfoFactory{
4605		APIPrefixes:          sets.NewString("api", "apis"),
4606		GrouplessAPIPrefixes: sets.NewString("api"),
4607	}
4608}
4609
4610const benchmarkSeed = 100
4611
4612func benchmarkItems(b *testing.B) []example.Pod {
4613	clientapiObjectFuzzer := fuzzer.FuzzerFor(examplefuzzer.Funcs, rand.NewSource(benchmarkSeed), codecs)
4614	items := make([]example.Pod, 3)
4615	for i := range items {
4616		clientapiObjectFuzzer.Fuzz(&items[i])
4617	}
4618	return items
4619}
4620