1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364 |
- /*
- Copyright 2014 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package apiserver
- import (
- "bytes"
- "encoding/json"
- "errors"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "net/http/httptest"
- "net/url"
- "reflect"
- "strings"
- "sync"
- "testing"
- "time"
- "k8s.io/kubernetes/pkg/admission"
- "k8s.io/kubernetes/pkg/api"
- apierrs "k8s.io/kubernetes/pkg/api/errors"
- "k8s.io/kubernetes/pkg/api/meta"
- "k8s.io/kubernetes/pkg/api/rest"
- "k8s.io/kubernetes/pkg/api/unversioned"
- "k8s.io/kubernetes/pkg/api/v1"
- apiservertesting "k8s.io/kubernetes/pkg/apiserver/testing"
- "k8s.io/kubernetes/pkg/fields"
- "k8s.io/kubernetes/pkg/labels"
- "k8s.io/kubernetes/pkg/runtime"
- "k8s.io/kubernetes/pkg/util"
- "k8s.io/kubernetes/pkg/util/diff"
- "k8s.io/kubernetes/pkg/util/sets"
- "k8s.io/kubernetes/pkg/version"
- "k8s.io/kubernetes/pkg/watch"
- "k8s.io/kubernetes/pkg/watch/versioned"
- "k8s.io/kubernetes/plugin/pkg/admission/admit"
- "k8s.io/kubernetes/plugin/pkg/admission/deny"
- "github.com/emicklei/go-restful"
- )
- func convert(obj runtime.Object) (runtime.Object, error) {
- return obj, nil
- }
- // This creates fake API versions, similar to api/latest.go.
- var testAPIGroup = "test.group"
- var testAPIGroup2 = "test.group2"
- var testInternalGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: runtime.APIVersionInternal}
- var testGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version"}
- var newGroupVersion = unversioned.GroupVersion{Group: testAPIGroup, Version: "version2"}
- var testGroup2Version = unversioned.GroupVersion{Group: testAPIGroup2, Version: "version"}
- var testInternalGroup2Version = unversioned.GroupVersion{Group: testAPIGroup2, Version: runtime.APIVersionInternal}
- var prefix = "apis"
- var grouplessGroupVersion = unversioned.GroupVersion{Group: "", Version: "v1"}
- var grouplessInternalGroupVersion = unversioned.GroupVersion{Group: "", Version: runtime.APIVersionInternal}
- var grouplessPrefix = "api"
- var groupVersions = []unversioned.GroupVersion{grouplessGroupVersion, testGroupVersion, newGroupVersion}
- var codec = api.Codecs.LegacyCodec(groupVersions...)
- var grouplessCodec = api.Codecs.LegacyCodec(grouplessGroupVersion)
- var testCodec = api.Codecs.LegacyCodec(testGroupVersion)
- var newCodec = api.Codecs.LegacyCodec(newGroupVersion)
- var accessor = meta.NewAccessor()
- var versioner runtime.ResourceVersioner = accessor
- var selfLinker runtime.SelfLinker = accessor
- var mapper, namespaceMapper meta.RESTMapper // The mappers with namespace and with legacy namespace scopes.
- var admissionControl admission.Interface
- var requestContextMapper api.RequestContextMapper
- func interfacesFor(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
- switch version {
- case testGroupVersion:
- return &meta.VersionInterfaces{
- ObjectConvertor: api.Scheme,
- MetadataAccessor: accessor,
- }, nil
- case newGroupVersion:
- return &meta.VersionInterfaces{
- ObjectConvertor: api.Scheme,
- MetadataAccessor: accessor,
- }, nil
- case grouplessGroupVersion:
- return &meta.VersionInterfaces{
- ObjectConvertor: api.Scheme,
- MetadataAccessor: accessor,
- }, nil
- case testGroup2Version:
- return &meta.VersionInterfaces{
- ObjectConvertor: api.Scheme,
- MetadataAccessor: accessor,
- }, nil
- default:
- return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, groupVersions)
- }
- }
- func newMapper() *meta.DefaultRESTMapper {
- return meta.NewDefaultRESTMapper([]unversioned.GroupVersion{testGroupVersion, newGroupVersion}, interfacesFor)
- }
- func addGrouplessTypes() {
- type ListOptions struct {
- Object runtime.Object
- unversioned.TypeMeta `json:",inline"`
- LabelSelector string `json:"labelSelector,omitempty"`
- FieldSelector string `json:"fieldSelector,omitempty"`
- Watch bool `json:"watch,omitempty"`
- ResourceVersion string `json:"resourceVersion,omitempty"`
- TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
- }
- api.Scheme.AddKnownTypes(grouplessGroupVersion,
- &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{},
- &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
- api.Scheme.AddKnownTypes(grouplessInternalGroupVersion,
- &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &api.ListOptions{},
- &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{})
- }
- func addTestTypes() {
- type ListOptions struct {
- Object runtime.Object
- unversioned.TypeMeta `json:",inline"`
- LabelSelector string `json:"labelSelector,omitempty"`
- FieldSelector string `json:"fieldSelector,omitempty"`
- Watch bool `json:"watch,omitempty"`
- ResourceVersion string `json:"resourceVersion,omitempty"`
- TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
- }
- api.Scheme.AddKnownTypes(testGroupVersion,
- &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{},
- &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{},
- &SimpleXGSubresource{})
- api.Scheme.AddKnownTypes(testGroupVersion, &v1.Pod{})
- api.Scheme.AddKnownTypes(testInternalGroupVersion,
- &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &api.ListOptions{},
- &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{},
- &SimpleXGSubresource{})
- api.Scheme.AddKnownTypes(testInternalGroupVersion, &api.Pod{})
- // Register SimpleXGSubresource in both testGroupVersion and testGroup2Version, and also their
- // their corresponding internal versions, to verify that the desired group version object is
- // served in the tests.
- api.Scheme.AddKnownTypes(testGroup2Version, &SimpleXGSubresource{})
- api.Scheme.AddKnownTypes(testInternalGroup2Version, &SimpleXGSubresource{})
- versioned.AddToGroupVersion(api.Scheme, testGroupVersion)
- }
- func addNewTestTypes() {
- type ListOptions struct {
- Object runtime.Object
- unversioned.TypeMeta `json:",inline"`
- LabelSelector string `json:"labelSelector,omitempty"`
- FieldSelector string `json:"fieldSelector,omitempty"`
- Watch bool `json:"watch,omitempty"`
- ResourceVersion string `json:"resourceVersion,omitempty"`
- TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty"`
- }
- api.Scheme.AddKnownTypes(newGroupVersion,
- &apiservertesting.Simple{}, &apiservertesting.SimpleList{}, &ListOptions{},
- &api.DeleteOptions{}, &apiservertesting.SimpleGetOptions{}, &apiservertesting.SimpleRoot{},
- &v1.Pod{},
- )
- versioned.AddToGroupVersion(api.Scheme, newGroupVersion)
- }
- func init() {
- // Certain API objects are returned regardless of the contents of storage:
- // api.Status is returned in errors
- addGrouplessTypes()
- addTestTypes()
- addNewTestTypes()
- nsMapper := newMapper()
- // enumerate all supported versions, get the kinds, and register with
- // the mapper how to address our resources
- for _, gv := range groupVersions {
- for kind := range api.Scheme.KnownTypes(gv) {
- gvk := gv.WithKind(kind)
- root := bool(kind == "SimpleRoot")
- if root {
- nsMapper.Add(gvk, meta.RESTScopeRoot)
- } else {
- nsMapper.Add(gvk, meta.RESTScopeNamespace)
- }
- }
- }
- mapper = nsMapper
- namespaceMapper = nsMapper
- admissionControl = admit.NewAlwaysAdmit()
- requestContextMapper = api.NewRequestContextMapper()
- api.Scheme.AddFieldLabelConversionFunc(grouplessGroupVersion.String(), "Simple",
- func(label, value string) (string, string, error) {
- return label, value, nil
- },
- )
- api.Scheme.AddFieldLabelConversionFunc(testGroupVersion.String(), "Simple",
- func(label, value string) (string, string, error) {
- return label, value, nil
- },
- )
- api.Scheme.AddFieldLabelConversionFunc(newGroupVersion.String(), "Simple",
- func(label, value string) (string, string, error) {
- return label, value, nil
- },
- )
- }
- // defaultAPIServer exposes nested objects for testability.
- type defaultAPIServer struct {
- http.Handler
- container *restful.Container
- }
- // uses the default settings
- func handle(storage map[string]rest.Storage) http.Handler {
- return handleInternal(storage, admissionControl, selfLinker)
- }
- // tests with a deny admission controller
- func handleDeny(storage map[string]rest.Storage) http.Handler {
- return handleInternal(storage, deny.NewAlwaysDeny(), selfLinker)
- }
- // tests using the new namespace scope mechanism
- func handleNamespaced(storage map[string]rest.Storage) http.Handler {
- return handleInternal(storage, admissionControl, selfLinker)
- }
- // tests using a custom self linker
- func handleLinker(storage map[string]rest.Storage, selfLinker runtime.SelfLinker) http.Handler {
- return handleInternal(storage, admissionControl, selfLinker)
- }
- func newTestRequestInfoResolver() *RequestInfoResolver {
- return &RequestInfoResolver{sets.NewString("api", "apis"), sets.NewString("api")}
- }
- func handleInternal(storage map[string]rest.Storage, admissionControl admission.Interface, selfLinker runtime.SelfLinker) http.Handler {
- container := restful.NewContainer()
- container.Router(restful.CurlyRouter{})
- mux := container.ServeMux
- template := APIGroupVersion{
- Storage: storage,
- RequestInfoResolver: newTestRequestInfoResolver(),
- Creater: api.Scheme,
- Convertor: api.Scheme,
- Copier: api.Scheme,
- Typer: api.Scheme,
- Linker: selfLinker,
- Mapper: namespaceMapper,
- ParameterCodec: api.ParameterCodec,
- Admit: admissionControl,
- Context: requestContextMapper,
- }
- // groupless v1 version
- {
- group := template
- group.Root = "/" + grouplessPrefix
- group.GroupVersion = grouplessGroupVersion
- group.OptionsExternalVersion = &grouplessGroupVersion
- group.Serializer = api.Codecs
- if err := (&group).InstallREST(container); err != nil {
- panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err))
- }
- }
- // group version 1
- {
- group := template
- group.Root = "/" + prefix
- group.GroupVersion = testGroupVersion
- group.OptionsExternalVersion = &testGroupVersion
- group.Serializer = api.Codecs
- if err := (&group).InstallREST(container); err != nil {
- panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err))
- }
- }
- // group version 2
- {
- group := template
- group.Root = "/" + prefix
- group.GroupVersion = newGroupVersion
- group.OptionsExternalVersion = &newGroupVersion
- group.Serializer = api.Codecs
- if err := (&group).InstallREST(container); err != nil {
- panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err))
- }
- }
- InstallVersionHandler(mux, container)
- return &defaultAPIServer{mux, container}
- }
- func TestSimpleSetupRight(t *testing.T) {
- s := &apiservertesting.Simple{ObjectMeta: api.ObjectMeta{Name: "aName"}}
- wire, err := runtime.Encode(codec, s)
- if err != nil {
- t.Fatal(err)
- }
- s2, err := runtime.Decode(codec, wire)
- if err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(s, s2) {
- t.Fatalf("encode/decode broken:\n%#v\n%#v\n", s, s2)
- }
- }
- func TestSimpleOptionsSetupRight(t *testing.T) {
- s := &apiservertesting.SimpleGetOptions{}
- wire, err := runtime.Encode(codec, s)
- if err != nil {
- t.Fatal(err)
- }
- s2, err := runtime.Decode(codec, wire)
- if err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(s, s2) {
- t.Fatalf("encode/decode broken:\n%#v\n%#v\n", s, s2)
- }
- }
- type SimpleRESTStorage struct {
- lock sync.Mutex
- errors map[string]error
- list []apiservertesting.Simple
- item apiservertesting.Simple
- updated *apiservertesting.Simple
- created *apiservertesting.Simple
- stream *SimpleStream
- deleted string
- deleteOptions *api.DeleteOptions
- actualNamespace string
- namespacePresent bool
- // These are set when Watch is called
- fakeWatch *watch.FakeWatcher
- requestedLabelSelector labels.Selector
- requestedFieldSelector fields.Selector
- requestedResourceVersion string
- requestedResourceNamespace string
- // The id requested, and location to return for ResourceLocation
- requestedResourceLocationID string
- resourceLocation *url.URL
- resourceLocationTransport http.RoundTripper
- expectedResourceNamespace string
- // If non-nil, called inside the WorkFunc when answering update, delete, create.
- // obj receives the original input to the update, delete, or create call.
- injectedFunction func(obj runtime.Object) (returnObj runtime.Object, err error)
- }
- func (storage *SimpleRESTStorage) Export(ctx api.Context, name string, opts unversioned.ExportOptions) (runtime.Object, error) {
- obj, err := storage.Get(ctx, name)
- if err != nil {
- return nil, err
- }
- s, ok := obj.(*apiservertesting.Simple)
- if !ok {
- return nil, fmt.Errorf("unexpected object")
- }
- // Set a marker to verify the method was called
- s.Other = "exported"
- return obj, storage.errors["export"]
- }
- func (storage *SimpleRESTStorage) List(ctx api.Context, options *api.ListOptions) (runtime.Object, error) {
- storage.checkContext(ctx)
- result := &apiservertesting.SimpleList{
- Items: storage.list,
- }
- storage.requestedLabelSelector = labels.Everything()
- if options != nil && options.LabelSelector != nil {
- storage.requestedLabelSelector = options.LabelSelector
- }
- storage.requestedFieldSelector = fields.Everything()
- if options != nil && options.FieldSelector != nil {
- storage.requestedFieldSelector = options.FieldSelector
- }
- return result, storage.errors["list"]
- }
- type SimpleStream struct {
- version string
- accept string
- contentType string
- err error
- io.Reader
- closed bool
- }
- func (s *SimpleStream) Close() error {
- s.closed = true
- return nil
- }
- func (obj *SimpleStream) GetObjectKind() unversioned.ObjectKind { return unversioned.EmptyObjectKind }
- func (s *SimpleStream) InputStream(version, accept string) (io.ReadCloser, bool, string, error) {
- s.version = version
- s.accept = accept
- return s, false, s.contentType, s.err
- }
- type OutputConnect struct {
- response string
- }
- func (h *OutputConnect) ServeHTTP(w http.ResponseWriter, req *http.Request) {
- w.Write([]byte(h.response))
- }
- func (storage *SimpleRESTStorage) Get(ctx api.Context, id string) (runtime.Object, error) {
- storage.checkContext(ctx)
- if id == "binary" {
- return storage.stream, storage.errors["get"]
- }
- copied, err := api.Scheme.Copy(&storage.item)
- if err != nil {
- panic(err)
- }
- return copied, storage.errors["get"]
- }
- func (storage *SimpleRESTStorage) checkContext(ctx api.Context) {
- storage.actualNamespace, storage.namespacePresent = api.NamespaceFrom(ctx)
- }
- func (storage *SimpleRESTStorage) Delete(ctx api.Context, id string, options *api.DeleteOptions) (runtime.Object, error) {
- storage.checkContext(ctx)
- storage.deleted = id
- storage.deleteOptions = options
- if err := storage.errors["delete"]; err != nil {
- return nil, err
- }
- var obj runtime.Object = &unversioned.Status{Status: unversioned.StatusSuccess}
- var err error
- if storage.injectedFunction != nil {
- obj, err = storage.injectedFunction(&apiservertesting.Simple{ObjectMeta: api.ObjectMeta{Name: id}})
- }
- return obj, err
- }
- func (storage *SimpleRESTStorage) New() runtime.Object {
- return &apiservertesting.Simple{}
- }
- func (storage *SimpleRESTStorage) NewList() runtime.Object {
- return &apiservertesting.SimpleList{}
- }
- func (storage *SimpleRESTStorage) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
- storage.checkContext(ctx)
- storage.created = obj.(*apiservertesting.Simple)
- if err := storage.errors["create"]; err != nil {
- return nil, err
- }
- var err error
- if storage.injectedFunction != nil {
- obj, err = storage.injectedFunction(obj)
- }
- return obj, err
- }
- func (storage *SimpleRESTStorage) Update(ctx api.Context, name string, objInfo rest.UpdatedObjectInfo) (runtime.Object, bool, error) {
- storage.checkContext(ctx)
- obj, err := objInfo.UpdatedObject(ctx, &storage.item)
- if err != nil {
- return nil, false, err
- }
- storage.updated = obj.(*apiservertesting.Simple)
- if err := storage.errors["update"]; err != nil {
- return nil, false, err
- }
- if storage.injectedFunction != nil {
- obj, err = storage.injectedFunction(obj)
- }
- return obj, false, err
- }
- // Implement ResourceWatcher.
- func (storage *SimpleRESTStorage) Watch(ctx api.Context, options *api.ListOptions) (watch.Interface, error) {
- storage.lock.Lock()
- defer storage.lock.Unlock()
- storage.checkContext(ctx)
- storage.requestedLabelSelector = labels.Everything()
- if options != nil && options.LabelSelector != nil {
- storage.requestedLabelSelector = options.LabelSelector
- }
- storage.requestedFieldSelector = fields.Everything()
- if options != nil && options.FieldSelector != nil {
- storage.requestedFieldSelector = options.FieldSelector
- }
- storage.requestedResourceVersion = ""
- if options != nil {
- storage.requestedResourceVersion = options.ResourceVersion
- }
- storage.requestedResourceNamespace = api.NamespaceValue(ctx)
- if err := storage.errors["watch"]; err != nil {
- return nil, err
- }
- storage.fakeWatch = watch.NewFake()
- return storage.fakeWatch, nil
- }
- func (storage *SimpleRESTStorage) Watcher() *watch.FakeWatcher {
- storage.lock.Lock()
- defer storage.lock.Unlock()
- return storage.fakeWatch
- }
- // Implement Redirector.
- var _ = rest.Redirector(&SimpleRESTStorage{})
- // Implement Redirector.
- func (storage *SimpleRESTStorage) ResourceLocation(ctx api.Context, id string) (*url.URL, http.RoundTripper, error) {
- storage.checkContext(ctx)
- // validate that the namespace context on the request matches the expected input
- storage.requestedResourceNamespace = api.NamespaceValue(ctx)
- if storage.expectedResourceNamespace != storage.requestedResourceNamespace {
- return nil, nil, fmt.Errorf("Expected request namespace %s, but got namespace %s", storage.expectedResourceNamespace, storage.requestedResourceNamespace)
- }
- storage.requestedResourceLocationID = id
- if err := storage.errors["resourceLocation"]; err != nil {
- return nil, nil, err
- }
- // Make a copy so the internal URL never gets mutated
- locationCopy := *storage.resourceLocation
- return &locationCopy, storage.resourceLocationTransport, nil
- }
- // Implement Connecter
- type ConnecterRESTStorage struct {
- connectHandler http.Handler
- handlerFunc func() http.Handler
- emptyConnectOptions runtime.Object
- receivedConnectOptions runtime.Object
- receivedID string
- receivedResponder rest.Responder
- takesPath string
- }
- // Implement Connecter
- var _ = rest.Connecter(&ConnecterRESTStorage{})
- func (s *ConnecterRESTStorage) New() runtime.Object {
- return &apiservertesting.Simple{}
- }
- func (s *ConnecterRESTStorage) Connect(ctx api.Context, id string, options runtime.Object, responder rest.Responder) (http.Handler, error) {
- s.receivedConnectOptions = options
- s.receivedID = id
- s.receivedResponder = responder
- if s.handlerFunc != nil {
- return s.handlerFunc(), nil
- }
- return s.connectHandler, nil
- }
- func (s *ConnecterRESTStorage) ConnectMethods() []string {
- return []string{"GET", "POST", "PUT", "DELETE"}
- }
- func (s *ConnecterRESTStorage) NewConnectOptions() (runtime.Object, bool, string) {
- if len(s.takesPath) > 0 {
- return s.emptyConnectOptions, true, s.takesPath
- }
- return s.emptyConnectOptions, false, ""
- }
- type LegacyRESTStorage struct {
- *SimpleRESTStorage
- }
- func (storage LegacyRESTStorage) Delete(ctx api.Context, id string) (runtime.Object, error) {
- return storage.SimpleRESTStorage.Delete(ctx, id, nil)
- }
- type MetadataRESTStorage struct {
- *SimpleRESTStorage
- types []string
- }
- func (m *MetadataRESTStorage) ProducesMIMETypes(method string) []string {
- return m.types
- }
- var _ rest.StorageMetadata = &MetadataRESTStorage{}
- type GetWithOptionsRESTStorage struct {
- *SimpleRESTStorage
- optionsReceived runtime.Object
- takesPath string
- }
- func (r *GetWithOptionsRESTStorage) Get(ctx api.Context, name string, options runtime.Object) (runtime.Object, error) {
- if _, ok := options.(*apiservertesting.SimpleGetOptions); !ok {
- return nil, fmt.Errorf("Unexpected options object: %#v", options)
- }
- r.optionsReceived = options
- return r.SimpleRESTStorage.Get(ctx, name)
- }
- func (r *GetWithOptionsRESTStorage) NewGetOptions() (runtime.Object, bool, string) {
- if len(r.takesPath) > 0 {
- return &apiservertesting.SimpleGetOptions{}, true, r.takesPath
- }
- return &apiservertesting.SimpleGetOptions{}, false, ""
- }
- var _ rest.GetterWithOptions = &GetWithOptionsRESTStorage{}
- type NamedCreaterRESTStorage struct {
- *SimpleRESTStorage
- createdName string
- }
- func (storage *NamedCreaterRESTStorage) Create(ctx api.Context, name string, obj runtime.Object) (runtime.Object, error) {
- storage.checkContext(ctx)
- storage.created = obj.(*apiservertesting.Simple)
- storage.createdName = name
- if err := storage.errors["create"]; err != nil {
- return nil, err
- }
- var err error
- if storage.injectedFunction != nil {
- obj, err = storage.injectedFunction(obj)
- }
- return obj, err
- }
- type SimpleTypedStorage struct {
- errors map[string]error
- item runtime.Object
- baseType runtime.Object
- actualNamespace string
- namespacePresent bool
- }
- func (storage *SimpleTypedStorage) New() runtime.Object {
- return storage.baseType
- }
- func (storage *SimpleTypedStorage) Get(ctx api.Context, id string) (runtime.Object, error) {
- storage.checkContext(ctx)
- copied, err := api.Scheme.Copy(storage.item)
- if err != nil {
- panic(err)
- }
- return copied, storage.errors["get"]
- }
- func (storage *SimpleTypedStorage) checkContext(ctx api.Context) {
- storage.actualNamespace, storage.namespacePresent = api.NamespaceFrom(ctx)
- }
- func extractBody(response *http.Response, object runtime.Object) (string, error) {
- return extractBodyDecoder(response, object, codec)
- }
- func extractBodyDecoder(response *http.Response, object runtime.Object, decoder runtime.Decoder) (string, error) {
- defer response.Body.Close()
- body, err := ioutil.ReadAll(response.Body)
- if err != nil {
- return string(body), err
- }
- return string(body), runtime.DecodeInto(decoder, body, object)
- }
- func TestNotFound(t *testing.T) {
- type T struct {
- Method string
- Path string
- Status int
- }
- cases := map[string]T{
- // Positive checks to make sure everything is wired correctly
- "groupless GET root": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusOK},
- "groupless GET namespaced": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusOK},
- "groupless GET long prefix": {"GET", "/" + grouplessPrefix + "/", http.StatusNotFound},
- "groupless root PATCH method": {"PATCH", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
- "groupless root GET missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/blah", http.StatusNotFound},
- "groupless root GET with extra segment": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
- "groupless root DELETE without extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
- "groupless root DELETE with extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
- "groupless root PUT without extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
- "groupless root PUT with extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
- "groupless root watch missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/", http.StatusNotFound},
- "groupless namespaced PATCH method": {"PATCH", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
- "groupless namespaced GET long prefix": {"GET", "/" + grouplessPrefix + "/", http.StatusNotFound},
- "groupless namespaced GET missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/blah", http.StatusNotFound},
- "groupless namespaced GET with extra segment": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
- "groupless namespaced POST with extra segment": {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
- "groupless namespaced DELETE without extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
- "groupless namespaced DELETE with extra segment": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
- "groupless namespaced PUT without extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
- "groupless namespaced PUT with extra segment": {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
- "groupless namespaced watch missing storage": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/", http.StatusNotFound},
- "groupless namespaced watch with bad method": {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
- // Positive checks to make sure everything is wired correctly
- "GET root": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots", http.StatusOK},
- // TODO: JTL: "GET root item": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar", http.StatusOK},
- "GET namespaced": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusOK},
- // TODO: JTL: "GET namespaced item": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar", http.StatusOK},
- "GET long prefix": {"GET", "/" + prefix + "/", http.StatusNotFound},
- "root PATCH method": {"PATCH", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
- "root GET missing storage": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/blah", http.StatusNotFound},
- "root GET with extra segment": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
- // TODO: JTL: "root POST with extra segment": {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar", http.StatusMethodNotAllowed},
- "root DELETE without extra segment": {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
- "root DELETE with extra segment": {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
- "root PUT without extra segment": {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots", http.StatusMethodNotAllowed},
- "root PUT with extra segment": {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simpleroots/bar/baz", http.StatusNotFound},
- "root watch missing storage": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/", http.StatusNotFound},
- // TODO: JTL: "root watch with bad method": {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/simpleroot/bar", http.StatusMethodNotAllowed},
- "namespaced PATCH method": {"PATCH", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
- "namespaced GET long prefix": {"GET", "/" + prefix + "/", http.StatusNotFound},
- "namespaced GET missing storage": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/blah", http.StatusNotFound},
- "namespaced GET with extra segment": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
- "namespaced POST with extra segment": {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
- "namespaced DELETE without extra segment": {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
- "namespaced DELETE with extra segment": {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
- "namespaced PUT without extra segment": {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples", http.StatusMethodNotAllowed},
- "namespaced PUT with extra segment": {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/ns/simples/bar/baz", http.StatusNotFound},
- "namespaced watch missing storage": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/", http.StatusNotFound},
- "namespaced watch with bad method": {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/namespaces/ns/simples/bar", http.StatusMethodNotAllowed},
- }
- handler := handle(map[string]rest.Storage{
- "simples": &SimpleRESTStorage{},
- "simpleroots": &SimpleRESTStorage{},
- })
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- for k, v := range cases {
- request, err := http.NewRequest(v.Method, server.URL+v.Path, nil)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != v.Status {
- t.Errorf("Expected %d for %s (%s), Got %#v", v.Status, v.Method, k, response)
- t.Errorf("MAPPER: %v", mapper)
- }
- }
- }
- type UnimplementedRESTStorage struct{}
- func (UnimplementedRESTStorage) New() runtime.Object {
- return &apiservertesting.Simple{}
- }
- // TestUnimplementedRESTStorage ensures that if a rest.Storage does not implement a given
- // method, that it is literally not registered with the server. In the past,
- // we registered everything, and returned method not supported if it didn't support
- // a verb. Now we literally do not register a storage if it does not implement anything.
- // TODO: in future, we should update proxy/redirect
- func TestUnimplementedRESTStorage(t *testing.T) {
- type T struct {
- Method string
- Path string
- ErrCode int
- }
- cases := map[string]T{
- "groupless GET object": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo/bar", http.StatusNotFound},
- "groupless GET list": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo", http.StatusNotFound},
- "groupless POST list": {"POST", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo", http.StatusNotFound},
- "groupless PUT object": {"PUT", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo/bar", http.StatusNotFound},
- "groupless DELETE object": {"DELETE", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/foo/bar", http.StatusNotFound},
- "groupless watch list": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/foo", http.StatusNotFound},
- "groupless watch object": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/watch/foo/bar", http.StatusNotFound},
- "groupless proxy object": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/proxy/foo/bar", http.StatusNotFound},
- "groupless redirect object": {"GET", "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/redirect/foo/bar", http.StatusNotFound},
- "GET object": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo/bar", http.StatusNotFound},
- "GET list": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo", http.StatusNotFound},
- "POST list": {"POST", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo", http.StatusNotFound},
- "PUT object": {"PUT", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo/bar", http.StatusNotFound},
- "DELETE object": {"DELETE", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/foo/bar", http.StatusNotFound},
- "watch list": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/foo", http.StatusNotFound},
- "watch object": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/watch/foo/bar", http.StatusNotFound},
- "proxy object": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/proxy/foo/bar", http.StatusNotFound},
- "redirect object": {"GET", "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/redirect/foo/bar", http.StatusNotFound},
- }
- handler := handle(map[string]rest.Storage{
- "foo": UnimplementedRESTStorage{},
- })
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- for k, v := range cases {
- request, err := http.NewRequest(v.Method, server.URL+v.Path, bytes.NewReader([]byte(`{"kind":"Simple","apiVersion":"version"}`)))
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- response, err := client.Do(request)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- defer response.Body.Close()
- data, err := ioutil.ReadAll(response.Body)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if response.StatusCode != v.ErrCode {
- t.Errorf("%s: expected %d for %s, Got %s", k, v.ErrCode, v.Method, string(data))
- continue
- }
- }
- }
- func TestVersion(t *testing.T) {
- handler := handle(map[string]rest.Storage{})
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- request, err := http.NewRequest("GET", server.URL+"/version", nil)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- var info version.Info
- err = json.NewDecoder(response.Body).Decode(&info)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if !reflect.DeepEqual(version.Get(), info) {
- t.Errorf("Expected %#v, Got %#v", version.Get(), info)
- }
- }
- func TestList(t *testing.T) {
- testCases := []struct {
- url string
- namespace string
- selfLink string
- legacy bool
- label string
- field string
- }{
- // Groupless API
- // legacy namespace param is ignored
- {
- url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple?namespace=",
- namespace: "",
- selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
- legacy: true,
- },
- {
- url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple?namespace=other",
- namespace: "",
- selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
- legacy: true,
- },
- {
- url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple?namespace=other&labelSelector=a%3Db&fieldSelector=c%3Dd",
- namespace: "",
- selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
- legacy: true,
- label: "a=b",
- field: "c=d",
- },
- // legacy api version is honored
- {
- url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
- namespace: "",
- selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
- legacy: true,
- },
- {
- url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
- namespace: "other",
- selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
- legacy: true,
- },
- {
- url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
- namespace: "other",
- selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
- legacy: true,
- label: "a=b",
- field: "c=d",
- },
- // list items across all namespaces
- {
- url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
- namespace: "",
- selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
- legacy: true,
- },
- // list items in a namespace in the path
- {
- url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/simple",
- namespace: "default",
- selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/default/simple",
- },
- {
- url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
- namespace: "other",
- selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
- },
- {
- url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
- namespace: "other",
- selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/namespaces/other/simple",
- label: "a=b",
- field: "c=d",
- },
- // list items across all namespaces
- {
- url: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
- namespace: "",
- selfLink: "/" + grouplessPrefix + "/" + grouplessGroupVersion.Version + "/simple",
- },
- // Group API
- // legacy namespace param is ignored
- {
- url: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple?namespace=",
- namespace: "",
- selfLink: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
- legacy: true,
- },
- {
- url: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple?namespace=other",
- namespace: "",
- selfLink: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
- legacy: true,
- },
- {
- url: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple?namespace=other&labelSelector=a%3Db&fieldSelector=c%3Dd",
- namespace: "",
- selfLink: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
- legacy: true,
- label: "a=b",
- field: "c=d",
- },
- // legacy api version is honored
- {
- url: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
- namespace: "",
- selfLink: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
- legacy: true,
- },
- {
- url: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/simple",
- namespace: "other",
- selfLink: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/simple",
- legacy: true,
- },
- {
- url: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
- namespace: "other",
- selfLink: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/simple",
- legacy: true,
- label: "a=b",
- field: "c=d",
- },
- // list items across all namespaces
- {
- url: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
- namespace: "",
- selfLink: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple",
- legacy: true,
- },
- // list items in a namespace in the path
- {
- url: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/default/simple",
- namespace: "default",
- selfLink: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/default/simple",
- },
- {
- url: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/other/simple",
- namespace: "other",
- selfLink: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/other/simple",
- },
- {
- url: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/other/simple?labelSelector=a%3Db&fieldSelector=c%3Dd",
- namespace: "other",
- selfLink: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/other/simple",
- label: "a=b",
- field: "c=d",
- },
- // list items across all namespaces
- {
- url: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/simple",
- namespace: "",
- selfLink: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/simple",
- },
- }
- for i, testCase := range testCases {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{expectedResourceNamespace: testCase.namespace}
- storage["simple"] = &simpleStorage
- selfLinker := &setTestSelfLinker{
- t: t,
- namespace: testCase.namespace,
- expectedSet: testCase.selfLink,
- }
- var handler = handleInternal(storage, admissionControl, selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + testCase.url)
- if err != nil {
- t.Errorf("%d: unexpected error: %v", i, err)
- continue
- }
- defer resp.Body.Close()
- if resp.StatusCode != http.StatusOK {
- t.Errorf("%d: unexpected status: %d from url %s, Expected: %d, %#v", i, resp.StatusCode, testCase.url, http.StatusOK, resp)
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Errorf("%d: unexpected error: %v", i, err)
- continue
- }
- t.Logf("%d: body: %s", i, string(body))
- continue
- }
- // TODO: future, restore get links
- if !selfLinker.called {
- t.Errorf("%d: never set self link", i)
- }
- if !simpleStorage.namespacePresent {
- t.Errorf("%d: namespace not set", i)
- } else if simpleStorage.actualNamespace != testCase.namespace {
- t.Errorf("%d: unexpected resource namespace: %s", i, simpleStorage.actualNamespace)
- }
- if simpleStorage.requestedLabelSelector == nil || simpleStorage.requestedLabelSelector.String() != testCase.label {
- t.Errorf("%d: unexpected label selector: %v", i, simpleStorage.requestedLabelSelector)
- }
- if simpleStorage.requestedFieldSelector == nil || simpleStorage.requestedFieldSelector.String() != testCase.field {
- t.Errorf("%d: unexpected field selector: %v", i, simpleStorage.requestedFieldSelector)
- }
- }
- }
- func TestLogs(t *testing.T) {
- handler := handle(map[string]rest.Storage{})
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- request, err := http.NewRequest("GET", server.URL+"/logs", nil)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- body, err := ioutil.ReadAll(response.Body)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- t.Logf("Data: %s", string(body))
- }
- func TestErrorList(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{
- errors: map[string]error{"list": fmt.Errorf("test Error")},
- }
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusInternalServerError {
- t.Errorf("Unexpected status: %d, Expected: %d, %#v", resp.StatusCode, http.StatusInternalServerError, resp)
- }
- }
- func TestNonEmptyList(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{
- list: []apiservertesting.Simple{
- {
- ObjectMeta: api.ObjectMeta{Name: "something", Namespace: "other"},
- Other: "foo",
- },
- },
- }
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("Unexpected status: %d, Expected: %d, %#v", resp.StatusCode, http.StatusOK, resp)
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- t.Logf("Data: %s", string(body))
- }
- var listOut apiservertesting.SimpleList
- body, err := extractBody(resp, &listOut)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if len(listOut.Items) != 1 {
- t.Errorf("Unexpected response: %#v", listOut)
- return
- }
- if listOut.Items[0].Other != simpleStorage.list[0].Other {
- t.Errorf("Unexpected data: %#v, %s", listOut.Items[0], string(body))
- }
- if listOut.SelfLink != "/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/simple" {
- t.Errorf("unexpected list self link: %#v", listOut)
- }
- expectedSelfLink := "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/simple/something"
- if listOut.Items[0].ObjectMeta.SelfLink != expectedSelfLink {
- t.Errorf("Unexpected data: %#v, %s", listOut.Items[0].ObjectMeta.SelfLink, expectedSelfLink)
- }
- }
- func TestSelfLinkSkipsEmptyName(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{
- list: []apiservertesting.Simple{
- {
- ObjectMeta: api.ObjectMeta{Namespace: "other"},
- Other: "foo",
- },
- },
- }
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/simple")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("Unexpected status: %d, Expected: %d, %#v", resp.StatusCode, http.StatusOK, resp)
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- t.Logf("Data: %s", string(body))
- }
- var listOut apiservertesting.SimpleList
- body, err := extractBody(resp, &listOut)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if len(listOut.Items) != 1 {
- t.Errorf("Unexpected response: %#v", listOut)
- return
- }
- if listOut.Items[0].Other != simpleStorage.list[0].Other {
- t.Errorf("Unexpected data: %#v, %s", listOut.Items[0], string(body))
- }
- if listOut.SelfLink != "/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/simple" {
- t.Errorf("unexpected list self link: %#v", listOut)
- }
- expectedSelfLink := ""
- if listOut.Items[0].ObjectMeta.SelfLink != expectedSelfLink {
- t.Errorf("Unexpected data: %#v, %s", listOut.Items[0].ObjectMeta.SelfLink, expectedSelfLink)
- }
- }
- func TestMetadata(t *testing.T) {
- simpleStorage := &MetadataRESTStorage{&SimpleRESTStorage{}, []string{"text/plain"}}
- h := handle(map[string]rest.Storage{"simple": simpleStorage})
- ws := h.(*defaultAPIServer).container.RegisteredWebServices()
- if len(ws) == 0 {
- t.Fatal("no web services registered")
- }
- matches := map[string]int{}
- for _, w := range ws {
- for _, r := range w.Routes() {
- s := strings.Join(r.Produces, ",")
- i := matches[s]
- matches[s] = i + 1
- }
- }
- if matches["text/plain,application/json,application/yaml,application/vnd.kubernetes.protobuf"] == 0 ||
- matches["application/json,application/json;stream=watch,application/vnd.kubernetes.protobuf,application/vnd.kubernetes.protobuf;stream=watch"] == 0 ||
- matches["application/json,application/yaml,application/vnd.kubernetes.protobuf"] == 0 ||
- matches["application/json"] == 0 ||
- matches["*/*"] == 0 ||
- len(matches) != 5 {
- t.Errorf("unexpected mime types: %v", matches)
- }
- }
- func TestExport(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{
- item: apiservertesting.Simple{
- ObjectMeta: api.ObjectMeta{
- ResourceVersion: "1234",
- CreationTimestamp: unversioned.NewTime(time.Unix(10, 10)),
- },
- Other: "foo",
- },
- }
- selfLinker := &setTestSelfLinker{
- t: t,
- expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id",
- name: "id",
- namespace: "default",
- }
- storage["simple"] = &simpleStorage
- handler := handleLinker(storage, selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id?export=true")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- data, _ := ioutil.ReadAll(resp.Body)
- resp.Body.Close()
- t.Fatalf("unexpected response: %#v\n%s\n", resp, string(data))
- }
- var itemOut apiservertesting.Simple
- body, err := extractBody(resp, &itemOut)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if itemOut.Name != simpleStorage.item.Name {
- t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
- }
- if itemOut.Other != "exported" {
- t.Errorf("Expected: exported, saw: %s", itemOut.Other)
- }
- if !selfLinker.called {
- t.Errorf("Never set self link")
- }
- }
- func TestGet(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{
- item: apiservertesting.Simple{
- Other: "foo",
- },
- }
- selfLinker := &setTestSelfLinker{
- t: t,
- expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id",
- name: "id",
- namespace: "default",
- }
- storage["simple"] = &simpleStorage
- handler := handleLinker(storage, selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Fatalf("unexpected response: %#v", resp)
- }
- var itemOut apiservertesting.Simple
- body, err := extractBody(resp, &itemOut)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if itemOut.Name != simpleStorage.item.Name {
- t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
- }
- if !selfLinker.called {
- t.Errorf("Never set self link")
- }
- }
- func TestGetBinary(t *testing.T) {
- simpleStorage := SimpleRESTStorage{
- stream: &SimpleStream{
- contentType: "text/plain",
- Reader: bytes.NewBufferString("response data"),
- },
- }
- stream := simpleStorage.stream
- server := httptest.NewServer(handle(map[string]rest.Storage{"simple": &simpleStorage}))
- defer server.Close()
- req, err := http.NewRequest("GET", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/binary", nil)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- req.Header.Add("Accept", "text/other, */*")
- resp, err := http.DefaultClient.Do(req)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Fatalf("unexpected response: %#v", resp)
- }
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if !stream.closed || stream.version != testGroupVersion.String() || stream.accept != "text/other, */*" ||
- resp.Header.Get("Content-Type") != stream.contentType || string(body) != "response data" {
- t.Errorf("unexpected stream: %#v", stream)
- }
- }
- func validateSimpleGetOptionsParams(t *testing.T, route *restful.Route) {
- // Validate name and description
- expectedParams := map[string]string{
- "param1": "description for param1",
- "param2": "description for param2",
- "atAPath": "",
- }
- for _, p := range route.ParameterDocs {
- data := p.Data()
- if desc, exists := expectedParams[data.Name]; exists {
- if desc != data.Description {
- t.Errorf("unexpected description for parameter %s: %s\n", data.Name, data.Description)
- }
- delete(expectedParams, data.Name)
- }
- }
- if len(expectedParams) > 0 {
- t.Errorf("did not find all expected parameters: %#v", expectedParams)
- }
- }
- func TestGetWithOptionsRouteParams(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := GetWithOptionsRESTStorage{
- SimpleRESTStorage: &SimpleRESTStorage{},
- }
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- ws := handler.(*defaultAPIServer).container.RegisteredWebServices()
- if len(ws) == 0 {
- t.Fatal("no web services registered")
- }
- routes := ws[0].Routes()
- for i := range routes {
- if routes[i].Method == "GET" && routes[i].Operation == "readNamespacedSimple" {
- validateSimpleGetOptionsParams(t, &routes[i])
- break
- }
- }
- }
- func TestGetWithOptions(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := GetWithOptionsRESTStorage{
- SimpleRESTStorage: &SimpleRESTStorage{
- item: apiservertesting.Simple{
- Other: "foo",
- },
- },
- }
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id?param1=test1¶m2=test2")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Fatalf("unexpected response: %#v", resp)
- }
- var itemOut apiservertesting.Simple
- body, err := extractBody(resp, &itemOut)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if itemOut.Name != simpleStorage.item.Name {
- t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
- }
- opts, ok := simpleStorage.optionsReceived.(*apiservertesting.SimpleGetOptions)
- if !ok {
- t.Errorf("Unexpected options object received: %#v", simpleStorage.optionsReceived)
- return
- }
- if opts.Param1 != "test1" || opts.Param2 != "test2" {
- t.Errorf("Did not receive expected options: %#v", opts)
- }
- }
- func TestGetWithOptionsAndPath(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := GetWithOptionsRESTStorage{
- SimpleRESTStorage: &SimpleRESTStorage{
- item: apiservertesting.Simple{
- Other: "foo",
- },
- },
- takesPath: "atAPath",
- }
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id/a/different/path?param1=test1¶m2=test2&atAPath=not")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Fatalf("unexpected response: %#v", resp)
- }
- var itemOut apiservertesting.Simple
- body, err := extractBody(resp, &itemOut)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if itemOut.Name != simpleStorage.item.Name {
- t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
- }
- opts, ok := simpleStorage.optionsReceived.(*apiservertesting.SimpleGetOptions)
- if !ok {
- t.Errorf("Unexpected options object received: %#v", simpleStorage.optionsReceived)
- return
- }
- if opts.Param1 != "test1" || opts.Param2 != "test2" || opts.Path != "a/different/path" {
- t.Errorf("Did not receive expected options: %#v", opts)
- }
- }
- func TestGetAlternateSelfLink(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{
- item: apiservertesting.Simple{
- Other: "foo",
- },
- }
- selfLinker := &setTestSelfLinker{
- t: t,
- expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/test/simple/id",
- name: "id",
- namespace: "test",
- }
- storage["simple"] = &simpleStorage
- handler := handleLinker(storage, selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/test/simple/id")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Fatalf("unexpected response: %#v", resp)
- }
- var itemOut apiservertesting.Simple
- body, err := extractBody(resp, &itemOut)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if itemOut.Name != simpleStorage.item.Name {
- t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
- }
- if !selfLinker.called {
- t.Errorf("Never set self link")
- }
- }
- func TestGetNamespaceSelfLink(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{
- item: apiservertesting.Simple{
- Other: "foo",
- },
- }
- selfLinker := &setTestSelfLinker{
- t: t,
- expectedSet: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/foo/simple/id",
- name: "id",
- namespace: "foo",
- }
- storage["simple"] = &simpleStorage
- handler := handleInternal(storage, admissionControl, selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/foo/simple/id")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Fatalf("unexpected response: %#v", resp)
- }
- var itemOut apiservertesting.Simple
- body, err := extractBody(resp, &itemOut)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if itemOut.Name != simpleStorage.item.Name {
- t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simpleStorage.item, string(body))
- }
- if !selfLinker.called {
- t.Errorf("Never set self link")
- }
- }
- func TestGetMissing(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{
- errors: map[string]error{"get": apierrs.NewNotFound(api.Resource("simples"), "id")},
- }
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusNotFound {
- t.Errorf("Unexpected response %#v", resp)
- }
- }
- func TestGetRetryAfter(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{
- errors: map[string]error{"get": apierrs.NewServerTimeout(api.Resource("simples"), "id", 2)},
- }
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/id")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusInternalServerError {
- t.Errorf("Unexpected response %#v", resp)
- }
- if resp.Header.Get("Retry-After") != "2" {
- t.Errorf("Unexpected Retry-After header: %v", resp.Header)
- }
- }
- func TestConnect(t *testing.T) {
- responseText := "Hello World"
- itemID := "theID"
- connectStorage := &ConnecterRESTStorage{
- connectHandler: &OutputConnect{
- response: responseText,
- },
- }
- storage := map[string]rest.Storage{
- "simple": &SimpleRESTStorage{},
- "simple/connect": connectStorage,
- }
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("unexpected response: %#v", resp)
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- if connectStorage.receivedID != itemID {
- t.Errorf("Unexpected item id. Expected: %s. Actual: %s.", itemID, connectStorage.receivedID)
- }
- if string(body) != responseText {
- t.Errorf("Unexpected response. Expected: %s. Actual: %s.", responseText, string(body))
- }
- }
- func TestConnectResponderObject(t *testing.T) {
- itemID := "theID"
- simple := &apiservertesting.Simple{Other: "foo"}
- connectStorage := &ConnecterRESTStorage{}
- connectStorage.handlerFunc = func() http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- connectStorage.receivedResponder.Object(http.StatusCreated, simple)
- })
- }
- storage := map[string]rest.Storage{
- "simple": &SimpleRESTStorage{},
- "simple/connect": connectStorage,
- }
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusCreated {
- t.Errorf("unexpected response: %#v", resp)
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- if connectStorage.receivedID != itemID {
- t.Errorf("Unexpected item id. Expected: %s. Actual: %s.", itemID, connectStorage.receivedID)
- }
- obj, err := runtime.Decode(codec, body)
- if err != nil {
- t.Fatal(err)
- }
- if !api.Semantic.DeepEqual(obj, simple) {
- t.Errorf("Unexpected response: %#v", obj)
- }
- }
- func TestConnectResponderError(t *testing.T) {
- itemID := "theID"
- connectStorage := &ConnecterRESTStorage{}
- connectStorage.handlerFunc = func() http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- connectStorage.receivedResponder.Error(apierrs.NewForbidden(api.Resource("simples"), itemID, errors.New("you are terminated")))
- })
- }
- storage := map[string]rest.Storage{
- "simple": &SimpleRESTStorage{},
- "simple/connect": connectStorage,
- }
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusForbidden {
- t.Errorf("unexpected response: %#v", resp)
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- if connectStorage.receivedID != itemID {
- t.Errorf("Unexpected item id. Expected: %s. Actual: %s.", itemID, connectStorage.receivedID)
- }
- obj, err := runtime.Decode(codec, body)
- if err != nil {
- t.Fatal(err)
- }
- if obj.(*unversioned.Status).Code != http.StatusForbidden {
- t.Errorf("Unexpected response: %#v", obj)
- }
- }
- func TestConnectWithOptionsRouteParams(t *testing.T) {
- connectStorage := &ConnecterRESTStorage{
- connectHandler: &OutputConnect{},
- emptyConnectOptions: &apiservertesting.SimpleGetOptions{},
- }
- storage := map[string]rest.Storage{
- "simple": &SimpleRESTStorage{},
- "simple/connect": connectStorage,
- }
- handler := handle(storage)
- ws := handler.(*defaultAPIServer).container.RegisteredWebServices()
- if len(ws) == 0 {
- t.Fatal("no web services registered")
- }
- routes := ws[0].Routes()
- for i := range routes {
- switch routes[i].Operation {
- case "connectGetNamespacedSimpleConnect":
- case "connectPostNamespacedSimpleConnect":
- case "connectPutNamespacedSimpleConnect":
- case "connectDeleteNamespacedSimpleConnect":
- validateSimpleGetOptionsParams(t, &routes[i])
- }
- }
- }
- func TestConnectWithOptions(t *testing.T) {
- responseText := "Hello World"
- itemID := "theID"
- connectStorage := &ConnecterRESTStorage{
- connectHandler: &OutputConnect{
- response: responseText,
- },
- emptyConnectOptions: &apiservertesting.SimpleGetOptions{},
- }
- storage := map[string]rest.Storage{
- "simple": &SimpleRESTStorage{},
- "simple/connect": connectStorage,
- }
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect?param1=value1¶m2=value2")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("unexpected response: %#v", resp)
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- if connectStorage.receivedID != itemID {
- t.Errorf("Unexpected item id. Expected: %s. Actual: %s.", itemID, connectStorage.receivedID)
- }
- if string(body) != responseText {
- t.Errorf("Unexpected response. Expected: %s. Actual: %s.", responseText, string(body))
- }
- if connectStorage.receivedResponder == nil {
- t.Errorf("Unexpected responder")
- }
- opts, ok := connectStorage.receivedConnectOptions.(*apiservertesting.SimpleGetOptions)
- if !ok {
- t.Fatalf("Unexpected options type: %#v", connectStorage.receivedConnectOptions)
- }
- if opts.Param1 != "value1" && opts.Param2 != "value2" {
- t.Errorf("Unexpected options value: %#v", opts)
- }
- }
- func TestConnectWithOptionsAndPath(t *testing.T) {
- responseText := "Hello World"
- itemID := "theID"
- testPath := "a/b/c/def"
- connectStorage := &ConnecterRESTStorage{
- connectHandler: &OutputConnect{
- response: responseText,
- },
- emptyConnectOptions: &apiservertesting.SimpleGetOptions{},
- takesPath: "atAPath",
- }
- storage := map[string]rest.Storage{
- "simple": &SimpleRESTStorage{},
- "simple/connect": connectStorage,
- }
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/connect/" + testPath + "?param1=value1¶m2=value2")
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Errorf("unexpected response: %#v", resp)
- }
- defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Fatalf("Unexpected error: %v", err)
- }
- if connectStorage.receivedID != itemID {
- t.Errorf("Unexpected item id. Expected: %s. Actual: %s.", itemID, connectStorage.receivedID)
- }
- if string(body) != responseText {
- t.Errorf("Unexpected response. Expected: %s. Actual: %s.", responseText, string(body))
- }
- opts, ok := connectStorage.receivedConnectOptions.(*apiservertesting.SimpleGetOptions)
- if !ok {
- t.Fatalf("Unexpected options type: %#v", connectStorage.receivedConnectOptions)
- }
- if opts.Param1 != "value1" && opts.Param2 != "value2" {
- t.Errorf("Unexpected options value: %#v", opts)
- }
- if opts.Path != testPath {
- t.Errorf("Unexpected path value. Expected: %s. Actual: %s.", testPath, opts.Path)
- }
- }
- func TestDelete(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{}
- ID := "id"
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, nil)
- res, err := client.Do(request)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if res.StatusCode != http.StatusOK {
- t.Errorf("unexpected response: %#v", res)
- }
- if simpleStorage.deleted != ID {
- t.Errorf("Unexpected delete: %s, expected %s", simpleStorage.deleted, ID)
- }
- }
- func TestDeleteWithOptions(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{}
- ID := "id"
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- grace := int64(300)
- item := &api.DeleteOptions{
- GracePeriodSeconds: &grace,
- }
- body, err := runtime.Encode(codec, item)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- client := http.Client{}
- request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
- res, err := client.Do(request)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if res.StatusCode != http.StatusOK {
- t.Errorf("unexpected response: %s %#v", request.URL, res)
- s, err := ioutil.ReadAll(res.Body)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- t.Logf(string(s))
- }
- if simpleStorage.deleted != ID {
- t.Errorf("Unexpected delete: %s, expected %s", simpleStorage.deleted, ID)
- }
- simpleStorage.deleteOptions.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
- if !api.Semantic.DeepEqual(simpleStorage.deleteOptions, item) {
- t.Errorf("unexpected delete options: %s", diff.ObjectDiff(simpleStorage.deleteOptions, item))
- }
- }
- func TestLegacyDelete(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{}
- ID := "id"
- storage["simple"] = LegacyRESTStorage{&simpleStorage}
- var _ rest.Deleter = storage["simple"].(LegacyRESTStorage)
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, nil)
- res, err := client.Do(request)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if res.StatusCode != http.StatusOK {
- t.Errorf("unexpected response: %#v", res)
- }
- if simpleStorage.deleted != ID {
- t.Errorf("Unexpected delete: %s, expected %s", simpleStorage.deleted, ID)
- }
- if simpleStorage.deleteOptions != nil {
- t.Errorf("unexpected delete options: %#v", simpleStorage.deleteOptions)
- }
- }
- func TestLegacyDeleteIgnoresOptions(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{}
- ID := "id"
- storage["simple"] = LegacyRESTStorage{&simpleStorage}
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- item := api.NewDeleteOptions(300)
- body, err := runtime.Encode(codec, item)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- client := http.Client{}
- request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
- res, err := client.Do(request)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if res.StatusCode != http.StatusOK {
- t.Errorf("unexpected response: %#v", res)
- }
- if simpleStorage.deleted != ID {
- t.Errorf("Unexpected delete: %s, expected %s", simpleStorage.deleted, ID)
- }
- if simpleStorage.deleteOptions != nil {
- t.Errorf("unexpected delete options: %#v", simpleStorage.deleteOptions)
- }
- }
- func TestDeleteInvokesAdmissionControl(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{}
- ID := "id"
- storage["simple"] = &simpleStorage
- handler := handleDeny(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, nil)
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusForbidden {
- t.Errorf("Unexpected response %#v", response)
- }
- }
- func TestDeleteMissing(t *testing.T) {
- storage := map[string]rest.Storage{}
- ID := "id"
- simpleStorage := SimpleRESTStorage{
- errors: map[string]error{"delete": apierrs.NewNotFound(api.Resource("simples"), ID)},
- }
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- request, err := http.NewRequest("DELETE", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, nil)
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusNotFound {
- t.Errorf("Unexpected response %#v", response)
- }
- }
- func TestPatch(t *testing.T) {
- storage := map[string]rest.Storage{}
- ID := "id"
- item := &apiservertesting.Simple{
- ObjectMeta: api.ObjectMeta{
- Name: ID,
- Namespace: "", // update should allow the client to send an empty namespace
- UID: "uid",
- },
- Other: "bar",
- }
- simpleStorage := SimpleRESTStorage{item: *item}
- storage["simple"] = &simpleStorage
- selfLinker := &setTestSelfLinker{
- t: t,
- expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + ID,
- name: ID,
- namespace: api.NamespaceDefault,
- }
- handler := handleLinker(storage, selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- request, err := http.NewRequest("PATCH", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader([]byte(`{"labels":{"foo":"bar"}}`)))
- request.Header.Set("Content-Type", "application/merge-patch+json; charset=UTF-8")
- _, err = client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if simpleStorage.updated == nil || simpleStorage.updated.Labels["foo"] != "bar" {
- t.Errorf("Unexpected update value %#v, expected %#v.", simpleStorage.updated, item)
- }
- if !selfLinker.called {
- t.Errorf("Never set self link")
- }
- }
- func TestPatchRequiresMatchingName(t *testing.T) {
- storage := map[string]rest.Storage{}
- ID := "id"
- item := &apiservertesting.Simple{
- ObjectMeta: api.ObjectMeta{
- Name: ID,
- Namespace: "", // update should allow the client to send an empty namespace
- UID: "uid",
- },
- Other: "bar",
- }
- simpleStorage := SimpleRESTStorage{item: *item}
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- request, err := http.NewRequest("PATCH", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader([]byte(`{"metadata":{"name":"idbar"}}`)))
- request.Header.Set("Content-Type", "application/merge-patch+json")
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusBadRequest {
- t.Errorf("Unexpected response %#v", response)
- }
- }
- func TestUpdate(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{}
- ID := "id"
- storage["simple"] = &simpleStorage
- selfLinker := &setTestSelfLinker{
- t: t,
- expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + ID,
- name: ID,
- namespace: api.NamespaceDefault,
- }
- handler := handleLinker(storage, selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- item := &apiservertesting.Simple{
- ObjectMeta: api.ObjectMeta{
- Name: ID,
- Namespace: "", // update should allow the client to send an empty namespace
- },
- Other: "bar",
- }
- body, err := runtime.Encode(testCodec, item)
- if err != nil {
- // The following cases will fail, so die now
- t.Fatalf("unexpected error: %v", err)
- }
- client := http.Client{}
- request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
- _, err = client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if simpleStorage.updated == nil || simpleStorage.updated.Name != item.Name {
- t.Errorf("Unexpected update value %#v, expected %#v.", simpleStorage.updated, item)
- }
- if !selfLinker.called {
- t.Errorf("Never set self link")
- }
- }
- func TestUpdateInvokesAdmissionControl(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{}
- ID := "id"
- storage["simple"] = &simpleStorage
- handler := handleDeny(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- item := &apiservertesting.Simple{
- ObjectMeta: api.ObjectMeta{
- Name: ID,
- Namespace: api.NamespaceDefault,
- },
- Other: "bar",
- }
- body, err := runtime.Encode(testCodec, item)
- if err != nil {
- // The following cases will fail, so die now
- t.Fatalf("unexpected error: %v", err)
- }
- client := http.Client{}
- request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusForbidden {
- t.Errorf("Unexpected response %#v", response)
- }
- }
- func TestUpdateRequiresMatchingName(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{}
- ID := "id"
- storage["simple"] = &simpleStorage
- handler := handleDeny(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- item := &apiservertesting.Simple{
- Other: "bar",
- }
- body, err := runtime.Encode(testCodec, item)
- if err != nil {
- // The following cases will fail, so die now
- t.Fatalf("unexpected error: %v", err)
- }
- client := http.Client{}
- request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusBadRequest {
- t.Errorf("Unexpected response %#v", response)
- }
- }
- func TestUpdateAllowsMissingNamespace(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{}
- ID := "id"
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- item := &apiservertesting.Simple{
- ObjectMeta: api.ObjectMeta{
- Name: ID,
- },
- Other: "bar",
- }
- body, err := runtime.Encode(testCodec, item)
- if err != nil {
- // The following cases will fail, so die now
- t.Fatalf("unexpected error: %v", err)
- }
- client := http.Client{}
- request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusOK {
- t.Errorf("Unexpected response %#v", response)
- }
- }
- // when the object name and namespace can't be retrieved, skip name checking
- func TestUpdateAllowsMismatchedNamespaceOnError(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{}
- ID := "id"
- storage["simple"] = &simpleStorage
- selfLinker := &setTestSelfLinker{
- t: t,
- err: fmt.Errorf("test error"),
- }
- handler := handleLinker(storage, selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- item := &apiservertesting.Simple{
- ObjectMeta: api.ObjectMeta{
- Name: ID,
- Namespace: "other", // does not match request
- },
- Other: "bar",
- }
- body, err := runtime.Encode(testCodec, item)
- if err != nil {
- // The following cases will fail, so die now
- t.Fatalf("unexpected error: %v", err)
- }
- client := http.Client{}
- request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
- _, err = client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if simpleStorage.updated == nil || simpleStorage.updated.Name != item.Name {
- t.Errorf("Unexpected update value %#v, expected %#v.", simpleStorage.updated, item)
- }
- if selfLinker.called {
- t.Errorf("self link ignored")
- }
- }
- func TestUpdatePreventsMismatchedNamespace(t *testing.T) {
- storage := map[string]rest.Storage{}
- simpleStorage := SimpleRESTStorage{}
- ID := "id"
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- item := &apiservertesting.Simple{
- ObjectMeta: api.ObjectMeta{
- Name: ID,
- Namespace: "other",
- },
- Other: "bar",
- }
- body, err := runtime.Encode(testCodec, item)
- if err != nil {
- // The following cases will fail, so die now
- t.Fatalf("unexpected error: %v", err)
- }
- client := http.Client{}
- request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusBadRequest {
- t.Errorf("Unexpected response %#v", response)
- }
- }
- func TestUpdateMissing(t *testing.T) {
- storage := map[string]rest.Storage{}
- ID := "id"
- simpleStorage := SimpleRESTStorage{
- errors: map[string]error{"update": apierrs.NewNotFound(api.Resource("simples"), ID)},
- }
- storage["simple"] = &simpleStorage
- handler := handle(storage)
- server := httptest.NewServer(handler)
- defer server.Close()
- item := &apiservertesting.Simple{
- ObjectMeta: api.ObjectMeta{
- Name: ID,
- Namespace: api.NamespaceDefault,
- },
- Other: "bar",
- }
- body, err := runtime.Encode(testCodec, item)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- client := http.Client{}
- request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+ID, bytes.NewReader(body))
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusNotFound {
- t.Errorf("Unexpected response %#v", response)
- }
- }
- func TestCreateNotFound(t *testing.T) {
- handler := handle(map[string]rest.Storage{
- "simple": &SimpleRESTStorage{
- // storage.Create can fail with not found error in theory.
- // See http://pr.k8s.io/486#discussion_r15037092.
- errors: map[string]error{"create": apierrs.NewNotFound(api.Resource("simples"), "id")},
- },
- })
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- simple := &apiservertesting.Simple{Other: "foo"}
- data, err := runtime.Encode(testCodec, simple)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple", bytes.NewBuffer(data))
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusNotFound {
- t.Errorf("Unexpected response %#v", response)
- }
- }
- func TestCreateChecksDecode(t *testing.T) {
- handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- simple := &api.Pod{}
- data, err := runtime.Encode(testCodec, simple)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple", bytes.NewBuffer(data))
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusBadRequest {
- t.Errorf("Unexpected response %#v", response)
- }
- b, err := ioutil.ReadAll(response.Body)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- } else if !strings.Contains(string(b), "cannot be handled as a Simple") {
- t.Errorf("unexpected response: %s", string(b))
- }
- }
- // TestUpdateREST tests that you can add new rest implementations to a pre-existing
- // web service.
- func TestUpdateREST(t *testing.T) {
- makeGroup := func(storage map[string]rest.Storage) *APIGroupVersion {
- return &APIGroupVersion{
- Storage: storage,
- Root: "/" + prefix,
- RequestInfoResolver: newTestRequestInfoResolver(),
- Creater: api.Scheme,
- Convertor: api.Scheme,
- Copier: api.Scheme,
- Typer: api.Scheme,
- Linker: selfLinker,
- Admit: admissionControl,
- Context: requestContextMapper,
- Mapper: namespaceMapper,
- GroupVersion: newGroupVersion,
- OptionsExternalVersion: &newGroupVersion,
- Serializer: api.Codecs,
- ParameterCodec: api.ParameterCodec,
- }
- }
- makeStorage := func(paths ...string) map[string]rest.Storage {
- storage := map[string]rest.Storage{}
- for _, s := range paths {
- storage[s] = &SimpleRESTStorage{}
- }
- return storage
- }
- testREST := func(t *testing.T, container *restful.Container, barCode int) {
- w := httptest.NewRecorder()
- container.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/test/foo/test"}})
- if w.Code != http.StatusOK {
- t.Fatalf("expected OK: %#v", w)
- }
- w = httptest.NewRecorder()
- container.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/test/bar/test"}})
- if w.Code != barCode {
- t.Errorf("expected response code %d for GET to bar but received %d", barCode, w.Code)
- }
- }
- storage1 := makeStorage("foo")
- group1 := makeGroup(storage1)
- storage2 := makeStorage("bar")
- group2 := makeGroup(storage2)
- container := restful.NewContainer()
- // install group1. Ensure that
- // 1. Foo storage is accessible
- // 2. Bar storage is not accessible
- if err := group1.InstallREST(container); err != nil {
- t.Fatal(err)
- }
- testREST(t, container, http.StatusNotFound)
- // update with group2. Ensure that
- // 1. Foo storage is still accessible
- // 2. Bar storage is now accessible
- if err := group2.UpdateREST(container); err != nil {
- t.Fatal(err)
- }
- testREST(t, container, http.StatusOK)
- // try to update a group that does not have an existing webservice with a matching prefix
- // should not affect the existing registered webservice
- invalidGroup := makeGroup(storage1)
- invalidGroup.Root = "bad"
- if err := invalidGroup.UpdateREST(container); err == nil {
- t.Fatal("expected an error from UpdateREST when updating a non-existing prefix but got none")
- }
- testREST(t, container, http.StatusOK)
- }
- func TestParentResourceIsRequired(t *testing.T) {
- storage := &SimpleTypedStorage{
- baseType: &apiservertesting.SimpleRoot{}, // a root scoped type
- item: &apiservertesting.SimpleRoot{},
- }
- group := &APIGroupVersion{
- Storage: map[string]rest.Storage{
- "simple/sub": storage,
- },
- Root: "/" + prefix,
- RequestInfoResolver: newTestRequestInfoResolver(),
- Creater: api.Scheme,
- Convertor: api.Scheme,
- Copier: api.Scheme,
- Typer: api.Scheme,
- Linker: selfLinker,
- Admit: admissionControl,
- Context: requestContextMapper,
- Mapper: namespaceMapper,
- GroupVersion: newGroupVersion,
- OptionsExternalVersion: &newGroupVersion,
- Serializer: api.Codecs,
- ParameterCodec: api.ParameterCodec,
- }
- container := restful.NewContainer()
- if err := group.InstallREST(container); err == nil {
- t.Fatal("expected error")
- }
- storage = &SimpleTypedStorage{
- baseType: &apiservertesting.SimpleRoot{}, // a root scoped type
- item: &apiservertesting.SimpleRoot{},
- }
- group = &APIGroupVersion{
- Storage: map[string]rest.Storage{
- "simple": &SimpleRESTStorage{},
- "simple/sub": storage,
- },
- Root: "/" + prefix,
- RequestInfoResolver: newTestRequestInfoResolver(),
- Creater: api.Scheme,
- Convertor: api.Scheme,
- Copier: api.Scheme,
- Typer: api.Scheme,
- Linker: selfLinker,
- Admit: admissionControl,
- Context: requestContextMapper,
- Mapper: namespaceMapper,
- GroupVersion: newGroupVersion,
- OptionsExternalVersion: &newGroupVersion,
- Serializer: api.Codecs,
- ParameterCodec: api.ParameterCodec,
- }
- container = restful.NewContainer()
- if err := group.InstallREST(container); err != nil {
- t.Fatal(err)
- }
- // resource is NOT registered in the root scope
- w := httptest.NewRecorder()
- container.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/simple/test/sub"}})
- if w.Code != http.StatusNotFound {
- t.Errorf("expected not found: %#v", w)
- }
- // resource is registered in the namespace scope
- w = httptest.NewRecorder()
- container.ServeHTTP(w, &http.Request{Method: "GET", URL: &url.URL{Path: "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/test/simple/test/sub"}})
- if w.Code != http.StatusOK {
- t.Fatalf("expected OK: %#v", w)
- }
- if storage.actualNamespace != "test" {
- t.Errorf("namespace should be set %#v", storage)
- }
- }
- func TestCreateWithName(t *testing.T) {
- pathName := "helloworld"
- storage := &NamedCreaterRESTStorage{SimpleRESTStorage: &SimpleRESTStorage{}}
- handler := handle(map[string]rest.Storage{
- "simple": &SimpleRESTStorage{},
- "simple/sub": storage,
- })
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- simple := &apiservertesting.Simple{Other: "foo"}
- data, err := runtime.Encode(testCodec, simple)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/"+pathName+"/sub", bytes.NewBuffer(data))
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusCreated {
- t.Errorf("Unexpected response %#v", response)
- }
- if storage.createdName != pathName {
- t.Errorf("Did not get expected name in create context. Got: %s, Expected: %s", storage.createdName, pathName)
- }
- }
- func TestUpdateChecksDecode(t *testing.T) {
- handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- simple := &api.Pod{}
- data, err := runtime.Encode(testCodec, simple)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/bar", bytes.NewBuffer(data))
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusBadRequest {
- t.Errorf("Unexpected response %#v\n%s", response, readBodyOrDie(response.Body))
- }
- b, err := ioutil.ReadAll(response.Body)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- } else if !strings.Contains(string(b), "cannot be handled as a Simple") {
- t.Errorf("unexpected response: %s", string(b))
- }
- }
- func TestParseTimeout(t *testing.T) {
- if d := parseTimeout(""); d != 30*time.Second {
- t.Errorf("blank timeout produces %v", d)
- }
- if d := parseTimeout("not a timeout"); d != 30*time.Second {
- t.Errorf("bad timeout produces %v", d)
- }
- if d := parseTimeout("10s"); d != 10*time.Second {
- t.Errorf("10s timeout produced: %v", d)
- }
- }
- type setTestSelfLinker struct {
- t *testing.T
- expectedSet string
- name string
- namespace string
- called bool
- err error
- }
- func (s *setTestSelfLinker) Namespace(runtime.Object) (string, error) { return s.namespace, s.err }
- func (s *setTestSelfLinker) Name(runtime.Object) (string, error) { return s.name, s.err }
- func (s *setTestSelfLinker) SelfLink(runtime.Object) (string, error) { return "", s.err }
- func (s *setTestSelfLinker) SetSelfLink(obj runtime.Object, selfLink string) error {
- if e, a := s.expectedSet, selfLink; e != a {
- s.t.Errorf("expected '%v', got '%v'", e, a)
- }
- s.called = true
- return s.err
- }
- func TestCreate(t *testing.T) {
- storage := SimpleRESTStorage{
- injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
- time.Sleep(5 * time.Millisecond)
- return obj, nil
- },
- }
- selfLinker := &setTestSelfLinker{
- t: t,
- name: "bar",
- namespace: "default",
- expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo/bar",
- }
- handler := handleLinker(map[string]rest.Storage{"foo": &storage}, selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- simple := &apiservertesting.Simple{
- Other: "bar",
- }
- data, err := runtime.Encode(testCodec, simple)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo", bytes.NewBuffer(data))
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- wg := sync.WaitGroup{}
- wg.Add(1)
- var response *http.Response
- go func() {
- response, err = client.Do(request)
- wg.Done()
- }()
- wg.Wait()
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- var itemOut apiservertesting.Simple
- body, err := extractBody(response, &itemOut)
- if err != nil {
- t.Errorf("unexpected error: %v %#v", err, response)
- }
- itemOut.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
- if !reflect.DeepEqual(&itemOut, simple) {
- t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simple, string(body))
- }
- if response.StatusCode != http.StatusCreated {
- t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusOK, response)
- }
- if !selfLinker.called {
- t.Errorf("Never set self link")
- }
- }
- func TestCreateYAML(t *testing.T) {
- storage := SimpleRESTStorage{
- injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
- time.Sleep(5 * time.Millisecond)
- return obj, nil
- },
- }
- selfLinker := &setTestSelfLinker{
- t: t,
- name: "bar",
- namespace: "default",
- expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo/bar",
- }
- handler := handleLinker(map[string]rest.Storage{"foo": &storage}, selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- // yaml encoder
- simple := &apiservertesting.Simple{
- Other: "bar",
- }
- serializer, ok := api.Codecs.SerializerForMediaType("application/yaml", nil)
- if !ok {
- t.Fatal("No yaml serializer")
- }
- encoder := api.Codecs.EncoderForVersion(serializer, testGroupVersion)
- decoder := api.Codecs.DecoderToVersion(serializer, testInternalGroupVersion)
- data, err := runtime.Encode(encoder, simple)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo", bytes.NewBuffer(data))
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- request.Header.Set("Accept", "application/yaml, application/json")
- request.Header.Set("Content-Type", "application/yaml")
- wg := sync.WaitGroup{}
- wg.Add(1)
- var response *http.Response
- go func() {
- response, err = client.Do(request)
- wg.Done()
- }()
- wg.Wait()
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- var itemOut apiservertesting.Simple
- body, err := extractBodyDecoder(response, &itemOut, decoder)
- if err != nil {
- t.Fatalf("unexpected error: %v %#v", err, response)
- }
- itemOut.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
- if !reflect.DeepEqual(&itemOut, simple) {
- t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simple, string(body))
- }
- if response.StatusCode != http.StatusCreated {
- t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusOK, response)
- }
- if !selfLinker.called {
- t.Errorf("Never set self link")
- }
- }
- func TestCreateInNamespace(t *testing.T) {
- storage := SimpleRESTStorage{
- injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
- time.Sleep(5 * time.Millisecond)
- return obj, nil
- },
- }
- selfLinker := &setTestSelfLinker{
- t: t,
- name: "bar",
- namespace: "other",
- expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/foo/bar",
- }
- handler := handleLinker(map[string]rest.Storage{"foo": &storage}, selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- simple := &apiservertesting.Simple{
- Other: "bar",
- }
- data, err := runtime.Encode(testCodec, simple)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/other/foo", bytes.NewBuffer(data))
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- wg := sync.WaitGroup{}
- wg.Add(1)
- var response *http.Response
- go func() {
- response, err = client.Do(request)
- wg.Done()
- }()
- wg.Wait()
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- var itemOut apiservertesting.Simple
- body, err := extractBody(response, &itemOut)
- if err != nil {
- t.Fatalf("unexpected error: %v\n%s", err, data)
- }
- itemOut.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{})
- if !reflect.DeepEqual(&itemOut, simple) {
- t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simple, string(body))
- }
- if response.StatusCode != http.StatusCreated {
- t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusOK, response)
- }
- if !selfLinker.called {
- t.Errorf("Never set self link")
- }
- }
- func TestCreateInvokesAdmissionControl(t *testing.T) {
- storage := SimpleRESTStorage{
- injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
- time.Sleep(5 * time.Millisecond)
- return obj, nil
- },
- }
- selfLinker := &setTestSelfLinker{
- t: t,
- name: "bar",
- namespace: "other",
- expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/other/foo/bar",
- }
- handler := handleInternal(map[string]rest.Storage{"foo": &storage}, deny.NewAlwaysDeny(), selfLinker)
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- simple := &apiservertesting.Simple{
- Other: "bar",
- }
- data, err := runtime.Encode(testCodec, simple)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/other/foo", bytes.NewBuffer(data))
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- wg := sync.WaitGroup{}
- wg.Add(1)
- var response *http.Response
- go func() {
- response, err = client.Do(request)
- wg.Done()
- }()
- wg.Wait()
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusForbidden {
- t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusForbidden, response)
- }
- }
- func expectApiStatus(t *testing.T, method, url string, data []byte, code int) *unversioned.Status {
- client := http.Client{}
- request, err := http.NewRequest(method, url, bytes.NewBuffer(data))
- if err != nil {
- t.Fatalf("unexpected error %#v", err)
- return nil
- }
- response, err := client.Do(request)
- if err != nil {
- t.Fatalf("unexpected error on %s %s: %v", method, url, err)
- return nil
- }
- var status unversioned.Status
- if body, err := extractBody(response, &status); err != nil {
- t.Fatalf("unexpected error on %s %s: %v\nbody:\n%s", method, url, err, body)
- return nil
- }
- if code != response.StatusCode {
- t.Fatalf("Expected %s %s to return %d, Got %d", method, url, code, response.StatusCode)
- }
- return &status
- }
- func TestDelayReturnsError(t *testing.T) {
- storage := SimpleRESTStorage{
- injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
- return nil, apierrs.NewAlreadyExists(api.Resource("foos"), "bar")
- },
- }
- handler := handle(map[string]rest.Storage{"foo": &storage})
- server := httptest.NewServer(handler)
- defer server.Close()
- status := expectApiStatus(t, "DELETE", fmt.Sprintf("%s/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo/bar", server.URL), nil, http.StatusConflict)
- if status.Status != unversioned.StatusFailure || status.Message == "" || status.Details == nil || status.Reason != unversioned.StatusReasonAlreadyExists {
- t.Errorf("Unexpected status %#v", status)
- }
- }
- type UnregisteredAPIObject struct {
- Value string
- }
- func (obj *UnregisteredAPIObject) GetObjectKind() unversioned.ObjectKind {
- return unversioned.EmptyObjectKind
- }
- func TestWriteJSONDecodeError(t *testing.T) {
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- writeNegotiated(api.Codecs, newGroupVersion, w, req, http.StatusOK, &UnregisteredAPIObject{"Undecodable"})
- }))
- defer server.Close()
- // We send a 200 status code before we encode the object, so we expect OK, but there will
- // still be an error object. This seems ok, the alternative is to validate the object before
- // encoding, but this really should never happen, so it's wasted compute for every API request.
- status := expectApiStatus(t, "GET", server.URL, nil, http.StatusOK)
- if status.Reason != unversioned.StatusReasonUnknown {
- t.Errorf("unexpected reason %#v", status)
- }
- if !strings.Contains(status.Message, "no kind is registered for the type apiserver.UnregisteredAPIObject") {
- t.Errorf("unexpected message %#v", status)
- }
- }
- type marshalError struct {
- err error
- }
- func (m *marshalError) MarshalJSON() ([]byte, error) {
- return []byte{}, m.err
- }
- func TestWriteRAWJSONMarshalError(t *testing.T) {
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
- writeRawJSON(http.StatusOK, &marshalError{errors.New("Undecodable")}, w)
- }))
- defer server.Close()
- client := http.Client{}
- resp, err := client.Get(server.URL)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusInternalServerError {
- t.Errorf("unexpected status code %d", resp.StatusCode)
- }
- }
- func TestCreateTimeout(t *testing.T) {
- testOver := make(chan struct{})
- defer close(testOver)
- storage := SimpleRESTStorage{
- injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
- // Eliminate flakes by ensuring the create operation takes longer than this test.
- <-testOver
- return obj, nil
- },
- }
- handler := handle(map[string]rest.Storage{
- "foo": &storage,
- })
- server := httptest.NewServer(handler)
- defer server.Close()
- simple := &apiservertesting.Simple{Other: "foo"}
- data, err := runtime.Encode(testCodec, simple)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- itemOut := expectApiStatus(t, "POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo?timeout=4ms", data, apierrs.StatusServerTimeout)
- if itemOut.Status != unversioned.StatusFailure || itemOut.Reason != unversioned.StatusReasonTimeout {
- t.Errorf("Unexpected status %#v", itemOut)
- }
- }
- func TestCORSAllowedOrigins(t *testing.T) {
- table := []struct {
- allowedOrigins []string
- origin string
- allowed bool
- }{
- {[]string{}, "example.com", false},
- {[]string{"example.com"}, "example.com", true},
- {[]string{"example.com"}, "not-allowed.com", false},
- {[]string{"not-matching.com", "example.com"}, "example.com", true},
- {[]string{".*"}, "example.com", true},
- }
- for _, item := range table {
- allowedOriginRegexps, err := util.CompileRegexps(item.allowedOrigins)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- handler := CORS(
- handle(map[string]rest.Storage{}),
- allowedOriginRegexps, nil, nil, "true",
- )
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- request, err := http.NewRequest("GET", server.URL+"/version", nil)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- request.Header.Set("Origin", item.origin)
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if item.allowed {
- if !reflect.DeepEqual(item.origin, response.Header.Get("Access-Control-Allow-Origin")) {
- t.Errorf("Expected %#v, Got %#v", item.origin, response.Header.Get("Access-Control-Allow-Origin"))
- }
- if response.Header.Get("Access-Control-Allow-Credentials") == "" {
- t.Errorf("Expected Access-Control-Allow-Credentials header to be set")
- }
- if response.Header.Get("Access-Control-Allow-Headers") == "" {
- t.Errorf("Expected Access-Control-Allow-Headers header to be set")
- }
- if response.Header.Get("Access-Control-Allow-Methods") == "" {
- t.Errorf("Expected Access-Control-Allow-Methods header to be set")
- }
- } else {
- if response.Header.Get("Access-Control-Allow-Origin") != "" {
- t.Errorf("Expected Access-Control-Allow-Origin header to not be set")
- }
- if response.Header.Get("Access-Control-Allow-Credentials") != "" {
- t.Errorf("Expected Access-Control-Allow-Credentials header to not be set")
- }
- if response.Header.Get("Access-Control-Allow-Headers") != "" {
- t.Errorf("Expected Access-Control-Allow-Headers header to not be set")
- }
- if response.Header.Get("Access-Control-Allow-Methods") != "" {
- t.Errorf("Expected Access-Control-Allow-Methods header to not be set")
- }
- }
- }
- }
- func TestCreateChecksAPIVersion(t *testing.T) {
- handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- simple := &apiservertesting.Simple{}
- //using newCodec and send the request to testVersion URL shall cause a discrepancy in apiVersion
- data, err := runtime.Encode(newCodec, simple)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple", bytes.NewBuffer(data))
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusBadRequest {
- t.Errorf("Unexpected response %#v", response)
- }
- b, err := ioutil.ReadAll(response.Body)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- } else if !strings.Contains(string(b), "does not match the expected API version") {
- t.Errorf("unexpected response: %s", string(b))
- }
- }
- func TestCreateDefaultsAPIVersion(t *testing.T) {
- handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- simple := &apiservertesting.Simple{}
- data, err := runtime.Encode(codec, simple)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- m := make(map[string]interface{})
- if err := json.Unmarshal(data, &m); err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- delete(m, "apiVersion")
- data, err = json.Marshal(m)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple", bytes.NewBuffer(data))
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- response, err := client.Do(request)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusCreated {
- t.Errorf("unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusCreated, response)
- }
- }
- func TestUpdateChecksAPIVersion(t *testing.T) {
- handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- simple := &apiservertesting.Simple{ObjectMeta: api.ObjectMeta{Name: "bar"}}
- data, err := runtime.Encode(newCodec, simple)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- request, err := http.NewRequest("PUT", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/simple/bar", bytes.NewBuffer(data))
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- response, err := client.Do(request)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusBadRequest {
- t.Errorf("Unexpected response %#v", response)
- }
- b, err := ioutil.ReadAll(response.Body)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- } else if !strings.Contains(string(b), "does not match the expected API version") {
- t.Errorf("unexpected response: %s", string(b))
- }
- }
- // SimpleXGSubresource is a cross group subresource, i.e. the subresource does not belong to the
- // same group as its parent resource.
- type SimpleXGSubresource struct {
- unversioned.TypeMeta `json:",inline"`
- api.ObjectMeta `json:"metadata"`
- SubresourceInfo string `json:"subresourceInfo,omitempty"`
- Labels map[string]string `json:"labels,omitempty"`
- }
- func (obj *SimpleXGSubresource) GetObjectKind() unversioned.ObjectKind { return &obj.TypeMeta }
- type SimpleXGSubresourceRESTStorage struct {
- item SimpleXGSubresource
- }
- func (storage *SimpleXGSubresourceRESTStorage) New() runtime.Object {
- return &SimpleXGSubresource{}
- }
- func (storage *SimpleXGSubresourceRESTStorage) Get(ctx api.Context, id string) (runtime.Object, error) {
- copied, err := api.Scheme.Copy(&storage.item)
- if err != nil {
- panic(err)
- }
- return copied, nil
- }
- func TestXGSubresource(t *testing.T) {
- container := restful.NewContainer()
- container.Router(restful.CurlyRouter{})
- mux := container.ServeMux
- itemID := "theID"
- subresourceStorage := &SimpleXGSubresourceRESTStorage{
- item: SimpleXGSubresource{
- SubresourceInfo: "foo",
- },
- }
- storage := map[string]rest.Storage{
- "simple": &SimpleRESTStorage{},
- "simple/subsimple": subresourceStorage,
- }
- group := APIGroupVersion{
- Storage: storage,
- RequestInfoResolver: newTestRequestInfoResolver(),
- Creater: api.Scheme,
- Convertor: api.Scheme,
- Copier: api.Scheme,
- Typer: api.Scheme,
- Linker: selfLinker,
- Mapper: namespaceMapper,
- ParameterCodec: api.ParameterCodec,
- Admit: admissionControl,
- Context: requestContextMapper,
- Root: "/" + prefix,
- GroupVersion: testGroupVersion,
- OptionsExternalVersion: &testGroupVersion,
- Serializer: api.Codecs,
- SubresourceGroupVersionKind: map[string]unversioned.GroupVersionKind{
- "simple/subsimple": testGroup2Version.WithKind("SimpleXGSubresource"),
- },
- }
- if err := (&group).InstallREST(container); err != nil {
- panic(fmt.Sprintf("unable to install container %s: %v", group.GroupVersion, err))
- }
- InstallVersionHandler(mux, container)
- handler := defaultAPIServer{mux, container}
- server := httptest.NewServer(handler)
- defer server.Close()
- resp, err := http.Get(server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/simple/" + itemID + "/subsimple")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- if resp.StatusCode != http.StatusOK {
- t.Fatalf("unexpected response: %#v", resp)
- }
- var itemOut SimpleXGSubresource
- body, err := extractBody(resp, &itemOut)
- if err != nil {
- t.Errorf("unexpected error: %v", err)
- }
- // Test if the returned object has the expected group, version and kind
- // We are directly unmarshaling JSON here because TypeMeta cannot be decoded through the
- // installed decoders. TypeMeta cannot be decoded because it is added to the ignored
- // conversion type list in API scheme and hence cannot be converted from input type object
- // to output type object. So it's values don't appear in the decoded output object.
- decoder := json.NewDecoder(strings.NewReader(body))
- var itemFromBody SimpleXGSubresource
- err = decoder.Decode(&itemFromBody)
- if err != nil {
- t.Errorf("unexpected JSON decoding error: %v", err)
- }
- if want := fmt.Sprintf("%s/%s", testGroup2Version.Group, testGroup2Version.Version); itemFromBody.APIVersion != want {
- t.Errorf("unexpected APIVersion got: %+v want: %+v", itemFromBody.APIVersion, want)
- }
- if itemFromBody.Kind != "SimpleXGSubresource" {
- t.Errorf("unexpected Kind got: %+v want: SimpleXGSubresource", itemFromBody.Kind)
- }
- if itemOut.Name != subresourceStorage.item.Name {
- t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, subresourceStorage.item, string(body))
- }
- }
- func readBodyOrDie(r io.Reader) []byte {
- body, err := ioutil.ReadAll(r)
- if err != nil {
- panic(err)
- }
- return body
- }
- // BenchmarkUpdateProtobuf measures the cost of processing an update on the server in proto
- func BenchmarkUpdateProtobuf(b *testing.B) {
- items := benchmarkItems()
- simpleStorage := &SimpleRESTStorage{}
- handler := handle(map[string]rest.Storage{"simples": simpleStorage})
- server := httptest.NewServer(handler)
- defer server.Close()
- client := http.Client{}
- dest, _ := url.Parse(server.URL)
- dest.Path = "/" + prefix + "/" + newGroupVersion.Group + "/" + newGroupVersion.Version + "/namespaces/foo/simples/bar"
- dest.RawQuery = ""
- info, _ := api.Codecs.SerializerForMediaType("application/vnd.kubernetes.protobuf", nil)
- e := api.Codecs.EncoderForVersion(info.Serializer, newGroupVersion)
- data, err := runtime.Encode(e, &items[0])
- if err != nil {
- b.Fatal(err)
- }
- b.ResetTimer()
- for i := 0; i < b.N; i++ {
- request, err := http.NewRequest("PUT", dest.String(), bytes.NewReader(data))
- if err != nil {
- b.Fatalf("unexpected error: %v", err)
- }
- request.Header.Set("Accept", "application/vnd.kubernetes.protobuf")
- request.Header.Set("Content-Type", "application/vnd.kubernetes.protobuf")
- response, err := client.Do(request)
- if err != nil {
- b.Fatalf("unexpected error: %v", err)
- }
- if response.StatusCode != http.StatusBadRequest {
- body, _ := ioutil.ReadAll(response.Body)
- b.Fatalf("Unexpected response %#v\n%s", response, body)
- }
- _, _ = ioutil.ReadAll(response.Body)
- response.Body.Close()
- }
- b.StopTimer()
- }
|