k8s.io/apiserver@v0.31.1/pkg/endpoints/apiserver_test.go (about)

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