From 4d2350632c4c025e1a42495ddbf4bc804bc82607 Mon Sep 17 00:00:00 2001 From: Chao Xu Date: Fri, 1 Jul 2016 23:46:00 -0700 Subject: [PATCH] only store typeMeta and objectMeta in the gc store --- .../app/controllermanager.go | 10 +- pkg/apiserver/apiserver_test.go | 4 + pkg/client/typed/dynamic/client.go | 16 +- pkg/client/typed/dynamic/client_pool.go | 9 + .../garbagecollector/garbagecollector.go | 68 +- .../garbagecollector/garbagecollector_test.go | 23 +- .../garbagecollector/metaonly/metaonly.go | 64 ++ .../metaonly/metaonly_test.go | 163 ++++ .../metaonly/types.generated.go | 794 ++++++++++++++++++ .../garbagecollector/metaonly/types.go | 40 + .../namespace/namespace_controller_test.go | 2 +- .../namespace/namespace_controller_utils.go | 6 +- pkg/runtime/codec.go | 8 +- pkg/runtime/scheme.go | 5 + pkg/runtime/unstructured.go | 1 + .../integration/client/dynamic_client_test.go | 6 +- .../garbage_collector_test.go | 12 +- 17 files changed, 1193 insertions(+), 38 deletions(-) create mode 100644 pkg/controller/garbagecollector/metaonly/metaonly.go create mode 100644 pkg/controller/garbagecollector/metaonly/metaonly_test.go create mode 100644 pkg/controller/garbagecollector/metaonly/types.generated.go create mode 100644 pkg/controller/garbagecollector/metaonly/types.go diff --git a/cmd/kube-controller-manager/app/controllermanager.go b/cmd/kube-controller-manager/app/controllermanager.go index 300396a7095..76fa7013bfb 100644 --- a/cmd/kube-controller-manager/app/controllermanager.go +++ b/cmd/kube-controller-manager/app/controllermanager.go @@ -51,6 +51,7 @@ import ( endpointcontroller "k8s.io/kubernetes/pkg/controller/endpoint" "k8s.io/kubernetes/pkg/controller/framework/informers" "k8s.io/kubernetes/pkg/controller/garbagecollector" + "k8s.io/kubernetes/pkg/controller/garbagecollector/metaonly" "k8s.io/kubernetes/pkg/controller/job" namespacecontroller "k8s.io/kubernetes/pkg/controller/namespace" nodecontroller "k8s.io/kubernetes/pkg/controller/node" @@ -69,6 +70,7 @@ import ( persistentvolumecontroller "k8s.io/kubernetes/pkg/controller/volume/persistentvolume" "k8s.io/kubernetes/pkg/healthz" quotainstall "k8s.io/kubernetes/pkg/quota/install" + "k8s.io/kubernetes/pkg/runtime/serializer" "k8s.io/kubernetes/pkg/serviceaccount" "k8s.io/kubernetes/pkg/util/configz" "k8s.io/kubernetes/pkg/util/crypto" @@ -495,8 +497,12 @@ func StartControllers(s *options.CMServer, kubeClient *client.Client, kubeconfig if err != nil { glog.Fatalf("Failed to get supported resources from server: %v", err) } - clientPool := dynamic.NewClientPool(restclient.AddUserAgent(kubeconfig, "generic-garbage-collector"), dynamic.LegacyAPIPathResolverFunc) - garbageCollector, err := garbagecollector.NewGarbageCollector(clientPool, groupVersionResources) + config := restclient.AddUserAgent(kubeconfig, "generic-garbage-collector") + config.ContentConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: metaonly.NewMetadataCodecFactory()} + metaOnlyClientPool := dynamic.NewClientPool(config, dynamic.LegacyAPIPathResolverFunc) + config.ContentConfig.NegotiatedSerializer = nil + clientPool := dynamic.NewClientPool(config, dynamic.LegacyAPIPathResolverFunc) + garbageCollector, err := garbagecollector.NewGarbageCollector(metaOnlyClientPool, clientPool, groupVersionResources) if err != nil { glog.Errorf("Failed to start the generic garbage collector: %v", err) } else { diff --git a/pkg/apiserver/apiserver_test.go b/pkg/apiserver/apiserver_test.go index ca7b89ba2a2..84fa532f988 100644 --- a/pkg/apiserver/apiserver_test.go +++ b/pkg/apiserver/apiserver_test.go @@ -1911,6 +1911,7 @@ func TestDeleteWithOptions(t *testing.T) { 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)) } @@ -2700,6 +2701,7 @@ func TestCreate(t *testing.T) { 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)) } @@ -2769,6 +2771,7 @@ func TestCreateYAML(t *testing.T) { 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)) } @@ -2827,6 +2830,7 @@ func TestCreateInNamespace(t *testing.T) { 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)) } diff --git a/pkg/client/typed/dynamic/client.go b/pkg/client/typed/dynamic/client.go index 5b8e1481ebc..dba2af02621 100644 --- a/pkg/client/typed/dynamic/client.go +++ b/pkg/client/typed/dynamic/client.go @@ -54,13 +54,9 @@ func NewClient(conf *restclient.Config) (*Client, error) { confCopy := *conf conf = &confCopy - codec := dynamicCodec{} - // TODO: it's questionable that this should be using anything other than unstructured schema and JSON conf.ContentType = runtime.ContentTypeJSON conf.AcceptContentTypes = runtime.ContentTypeJSON - streamingInfo, _ := api.Codecs.StreamingSerializerForMediaType("application/json;stream=watch", nil) - conf.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: codec}, streamingInfo) if conf.APIPath == "" { conf.APIPath = "/api" @@ -69,6 +65,10 @@ func NewClient(conf *restclient.Config) (*Client, error) { if len(conf.UserAgent) == 0 { conf.UserAgent = restclient.DefaultKubernetesUserAgent() } + if conf.NegotiatedSerializer == nil { + streamingInfo, _ := api.Codecs.StreamingSerializerForMediaType("application/json;stream=watch", nil) + conf.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: dynamicCodec{}}, streamingInfo) + } cl, err := restclient.RESTClientFor(conf) if err != nil { @@ -119,19 +119,17 @@ type ResourceClient struct { } // List returns a list of objects for this resource. -func (rc *ResourceClient) List(opts runtime.Object) (*runtime.UnstructuredList, error) { - result := new(runtime.UnstructuredList) +func (rc *ResourceClient) List(opts runtime.Object) (runtime.Object, error) { parameterEncoder := rc.parameterCodec if parameterEncoder == nil { parameterEncoder = defaultParameterEncoder } - err := rc.cl.Get(). + return rc.cl.Get(). NamespaceIfScoped(rc.ns, rc.resource.Namespaced). Resource(rc.resource.Name). VersionedParams(opts, parameterEncoder). Do(). - Into(result) - return result, err + Get() } // Get gets the resource with the specified name. diff --git a/pkg/client/typed/dynamic/client_pool.go b/pkg/client/typed/dynamic/client_pool.go index 5723ed708f6..66a50e0bbd6 100644 --- a/pkg/client/typed/dynamic/client_pool.go +++ b/pkg/client/typed/dynamic/client_pool.go @@ -19,8 +19,11 @@ package dynamic import ( "sync" + "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/client/restclient" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" ) // ClientPool manages a pool of dynamic clients. @@ -77,6 +80,12 @@ func (c *clientPoolImpl) ClientForGroupVersion(groupVersion unversioned.GroupVer // we need to make a client conf.GroupVersion = &groupVersion + + if conf.NegotiatedSerializer == nil { + streamingInfo, _ := api.Codecs.StreamingSerializerForMediaType("application/json;stream=watch", nil) + conf.NegotiatedSerializer = serializer.NegotiatedSerializerWrapper(runtime.SerializerInfo{Serializer: dynamicCodec{}}, streamingInfo) + } + dynamicClient, err := NewClient(conf) if err != nil { return nil, err diff --git a/pkg/controller/garbagecollector/garbagecollector.go b/pkg/controller/garbagecollector/garbagecollector.go index 55a3bf3b513..4cc4a854d4a 100644 --- a/pkg/controller/garbagecollector/garbagecollector.go +++ b/pkg/controller/garbagecollector/garbagecollector.go @@ -33,6 +33,7 @@ import ( "k8s.io/kubernetes/pkg/client/cache" "k8s.io/kubernetes/pkg/client/typed/dynamic" "k8s.io/kubernetes/pkg/controller/framework" + "k8s.io/kubernetes/pkg/controller/garbagecollector/metaonly" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/types" utilerrors "k8s.io/kubernetes/pkg/util/errors" @@ -154,6 +155,7 @@ func (p *Propagator) addDependentToOwners(n *node, owners []metatypes.OwnerRefer dependentsLock: &sync.RWMutex{}, dependents: make(map[*node]struct{}), } + glog.V(6).Infof("add virtual node.identity: %s\n\n", ownerNode.identity) p.uidToNode.Write(ownerNode) p.gc.dirtyQueue.Add(ownerNode) } @@ -426,7 +428,12 @@ func (p *Propagator) processEvent() { // removing ownerReferences from the dependents if the owner is deleted with // DeleteOptions.OrphanDependents=true. type GarbageCollector struct { - restMapper meta.RESTMapper + restMapper meta.RESTMapper + // metaOnlyClientPool uses a special codec, which removes fields except for + // apiVersion, kind, and metadata during decoding. + metaOnlyClientPool dynamic.ClientPool + // clientPool uses the regular dynamicCodec. We need it to update + // finalizers. It can be removed if we support patching finalizers. clientPool dynamic.ClientPool dirtyQueue *workqueue.Type orphanQueue *workqueue.Type @@ -434,8 +441,6 @@ type GarbageCollector struct { propagator *Propagator } -// TODO: make special List and Watch function that removes fields other than -// ObjectMeta. func gcListWatcher(client *dynamic.Client, resource unversioned.GroupVersionResource) *cache.ListWatch { return &cache.ListWatch{ ListFunc: func(options api.ListOptions) (runtime.Object, error) { @@ -461,14 +466,21 @@ func gcListWatcher(client *dynamic.Client, resource unversioned.GroupVersionReso } } -func monitorFor(p *Propagator, clientPool dynamic.ClientPool, resource unversioned.GroupVersionResource) (monitor, error) { +func monitorFor(p *Propagator, clientPool dynamic.ClientPool, resource unversioned.GroupVersionResource, kind unversioned.GroupVersionKind) (monitor, error) { // TODO: consider store in one storage. glog.V(6).Infof("create storage for resource %s", resource) var monitor monitor - client, err := clientPool.ClientForGroupVersion(resource.GroupVersion()) + client, err := p.gc.metaOnlyClientPool.ClientForGroupVersion(resource.GroupVersion()) if err != nil { return monitor, err } + setObjectTypeMeta := func(obj interface{}) { + runtimeObject, ok := obj.(runtime.Object) + if !ok { + utilruntime.HandleError(fmt.Errorf("expected runtime.Object, got %#v", obj)) + } + runtimeObject.GetObjectKind().SetGroupVersionKind(kind) + } monitor.store, monitor.controller = framework.NewInformer( gcListWatcher(client, resource), nil, @@ -476,6 +488,7 @@ func monitorFor(p *Propagator, clientPool dynamic.ClientPool, resource unversion framework.ResourceEventHandlerFuncs{ // add the event to the propagator's eventQueue. AddFunc: func(obj interface{}) { + setObjectTypeMeta(obj) event := event{ eventType: addEvent, obj: obj, @@ -483,6 +496,8 @@ func monitorFor(p *Propagator, clientPool dynamic.ClientPool, resource unversion p.eventQueue.Add(event) }, UpdateFunc: func(oldObj, newObj interface{}) { + setObjectTypeMeta(newObj) + setObjectTypeMeta(oldObj) event := event{updateEvent, newObj, oldObj} p.eventQueue.Add(event) }, @@ -491,6 +506,7 @@ func monitorFor(p *Propagator, clientPool dynamic.ClientPool, resource unversion if deletedFinalStateUnknown, ok := obj.(cache.DeletedFinalStateUnknown); ok { obj = deletedFinalStateUnknown.Obj } + setObjectTypeMeta(obj) event := event{ eventType: deleteEvent, obj: obj, @@ -511,11 +527,12 @@ var ignoredResources = map[unversioned.GroupVersionResource]struct{}{ unversioned.GroupVersionResource{Group: "authorization.k8s.io", Version: "v1beta1", Resource: "subjectaccessreviews"}: {}, } -func NewGarbageCollector(clientPool dynamic.ClientPool, resources []unversioned.GroupVersionResource) (*GarbageCollector, error) { +func NewGarbageCollector(metaOnlyClientPool dynamic.ClientPool, clientPool dynamic.ClientPool, resources []unversioned.GroupVersionResource) (*GarbageCollector, error) { gc := &GarbageCollector{ - clientPool: clientPool, - dirtyQueue: workqueue.New(), - orphanQueue: workqueue.New(), + metaOnlyClientPool: metaOnlyClientPool, + clientPool: clientPool, + dirtyQueue: workqueue.New(), + orphanQueue: workqueue.New(), // TODO: should use a dynamic RESTMapper built from the discovery results. restMapper: registered.RESTMapper(), } @@ -532,7 +549,11 @@ func NewGarbageCollector(clientPool dynamic.ClientPool, resources []unversioned. glog.V(6).Infof("ignore resource %#v", resource) continue } - monitor, err := monitorFor(gc.propagator, gc.clientPool, resource) + kind, err := gc.restMapper.KindFor(resource) + if err != nil { + return nil, err + } + monitor, err := monitorFor(gc.propagator, gc.clientPool, resource, kind) if err != nil { return nil, err } @@ -549,7 +570,7 @@ func (gc *GarbageCollector) worker() { defer gc.dirtyQueue.Done(key) err := gc.processItem(key.(*node)) if err != nil { - utilruntime.HandleError(fmt.Errorf("Error syncing item %v: %v", key, err)) + utilruntime.HandleError(fmt.Errorf("Error syncing item %#v: %v", key, err)) } } @@ -623,6 +644,20 @@ func objectReferenceToUnstructured(ref objectReference) *runtime.Unstructured { return ret } +func objectReferenceToMetadataOnlyObject(ref objectReference) *metaonly.MetadataOnlyObject { + return &metaonly.MetadataOnlyObject{ + TypeMeta: unversioned.TypeMeta{ + APIVersion: ref.APIVersion, + Kind: ref.Kind, + }, + ObjectMeta: v1.ObjectMeta{ + Namespace: ref.Namespace, + UID: ref.UID, + Name: ref.Name, + }, + } +} + func (gc *GarbageCollector) processItem(item *node) error { // Get the latest item from the API server latest, err := gc.getObject(item.identity) @@ -634,15 +669,22 @@ func (gc *GarbageCollector) processItem(item *node) error { glog.V(6).Infof("item %v not found, generating a virtual delete event", item.identity) event := event{ eventType: deleteEvent, - obj: objectReferenceToUnstructured(item.identity), + obj: objectReferenceToMetadataOnlyObject(item.identity), } + glog.V(6).Infof("generating virtual delete event for %s\n\n", event.obj) gc.propagator.eventQueue.Add(event) return nil } return err } if latest.GetUID() != item.identity.UID { - glog.V(6).Infof("UID doesn't match, item %v not found, ignore it", item.identity) + glog.V(6).Infof("UID doesn't match, item %v not found, generating a virtual delete event", item.identity) + event := event{ + eventType: deleteEvent, + obj: objectReferenceToMetadataOnlyObject(item.identity), + } + glog.V(6).Infof("generating virtual delete event for %s\n\n", event.obj) + gc.propagator.eventQueue.Add(event) return nil } ownerReferences := latest.GetOwnerReferences() diff --git a/pkg/controller/garbagecollector/garbagecollector_test.go b/pkg/controller/garbagecollector/garbagecollector_test.go index 58fc5fccae1..009fa5a5628 100644 --- a/pkg/controller/garbagecollector/garbagecollector_test.go +++ b/pkg/controller/garbagecollector/garbagecollector_test.go @@ -24,6 +24,8 @@ import ( "testing" _ "k8s.io/kubernetes/pkg/api/install" + "k8s.io/kubernetes/pkg/controller/garbagecollector/metaonly" + "k8s.io/kubernetes/pkg/runtime/serializer" "github.com/stretchr/testify/assert" "k8s.io/kubernetes/pkg/api" @@ -39,9 +41,13 @@ import ( ) func TestNewGarbageCollector(t *testing.T) { - clientPool := dynamic.NewClientPool(&restclient.Config{}, dynamic.LegacyAPIPathResolverFunc) + config := &restclient.Config{} + config.ContentConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: metaonly.NewMetadataCodecFactory()} + metaOnlyClientPool := dynamic.NewClientPool(config, dynamic.LegacyAPIPathResolverFunc) + config.ContentConfig.NegotiatedSerializer = nil + clientPool := dynamic.NewClientPool(config, dynamic.LegacyAPIPathResolverFunc) podResource := []unversioned.GroupVersionResource{{Version: "v1", Resource: "pods"}} - gc, err := NewGarbageCollector(clientPool, podResource) + gc, err := NewGarbageCollector(metaOnlyClientPool, clientPool, podResource) if err != nil { t.Fatal(err) } @@ -141,8 +147,11 @@ func TestProcessItem(t *testing.T) { podResource := []unversioned.GroupVersionResource{{Version: "v1", Resource: "pods"}} srv, clientConfig := testServerAndClientConfig(testHandler.ServeHTTP) defer srv.Close() + clientConfig.ContentConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: metaonly.NewMetadataCodecFactory()} + metaOnlyClientPool := dynamic.NewClientPool(clientConfig, dynamic.LegacyAPIPathResolverFunc) + clientConfig.ContentConfig.NegotiatedSerializer = nil clientPool := dynamic.NewClientPool(clientConfig, dynamic.LegacyAPIPathResolverFunc) - gc, err := NewGarbageCollector(clientPool, podResource) + gc, err := NewGarbageCollector(metaOnlyClientPool, clientPool, podResource) if err != nil { t.Fatal(err) } @@ -293,9 +302,13 @@ func TestProcessEvent(t *testing.T) { // TestDependentsRace relies on golang's data race detector to check if there is // data race among in the dependents field. func TestDependentsRace(t *testing.T) { - clientPool := dynamic.NewClientPool(&restclient.Config{}, dynamic.LegacyAPIPathResolverFunc) + config := &restclient.Config{} + config.ContentConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: metaonly.NewMetadataCodecFactory()} + metaOnlyClientPool := dynamic.NewClientPool(config, dynamic.LegacyAPIPathResolverFunc) + config.ContentConfig.NegotiatedSerializer = nil + clientPool := dynamic.NewClientPool(config, dynamic.LegacyAPIPathResolverFunc) podResource := []unversioned.GroupVersionResource{{Version: "v1", Resource: "pods"}} - gc, err := NewGarbageCollector(clientPool, podResource) + gc, err := NewGarbageCollector(metaOnlyClientPool, clientPool, podResource) if err != nil { t.Fatal(err) } diff --git a/pkg/controller/garbagecollector/metaonly/metaonly.go b/pkg/controller/garbagecollector/metaonly/metaonly.go new file mode 100644 index 00000000000..bce9c7b4b4a --- /dev/null +++ b/pkg/controller/garbagecollector/metaonly/metaonly.go @@ -0,0 +1,64 @@ +/* +Copyright 2016 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 metaonly + +import ( + "fmt" + "strings" + + "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" +) + +func (obj *MetadataOnlyObject) GetObjectKind() unversioned.ObjectKind { return obj } +func (obj *MetadataOnlyObjectList) GetObjectKind() unversioned.ObjectKind { return obj } + +type metaOnlyJSONScheme struct{} + +// This function can be extended to mapping different gvk to different MetadataOnlyObject, +// which embedded with different version of ObjectMeta. Currently the system +// only supports v1.ObjectMeta. +func gvkToMetadataOnlyObject(gvk unversioned.GroupVersionKind) runtime.Object { + if strings.HasSuffix(gvk.Kind, "List") { + return &MetadataOnlyObjectList{} + } else { + return &MetadataOnlyObject{} + } +} + +func NewMetadataCodecFactory() serializer.CodecFactory { + // populating another scheme from api.Scheme, registering every kind with + // MetadataOnlyObject (or MetadataOnlyObjectList). + scheme := runtime.NewScheme() + allTypes := api.Scheme.AllKnownTypes() + for kind := range allTypes { + if kind.Version == runtime.APIVersionInternal { + continue + } + metaOnlyObject := gvkToMetadataOnlyObject(kind) + scheme.AddKnownTypeWithName(kind, metaOnlyObject) + } + scheme.AddUnversionedTypes(api.Unversioned, &unversioned.Status{}) + return serializer.NewCodecFactory(scheme) +} + +// String converts a MetadataOnlyObject to a human-readable string. +func (metaOnly MetadataOnlyObject) String() string { + return fmt.Sprintf("%s/%s, name: %s, DeletionTimestamp:%v", metaOnly.TypeMeta.APIVersion, metaOnly.TypeMeta.Kind, metaOnly.ObjectMeta.Name, metaOnly.ObjectMeta.DeletionTimestamp) +} diff --git a/pkg/controller/garbagecollector/metaonly/metaonly_test.go b/pkg/controller/garbagecollector/metaonly/metaonly_test.go new file mode 100644 index 00000000000..aad13ff57a7 --- /dev/null +++ b/pkg/controller/garbagecollector/metaonly/metaonly_test.go @@ -0,0 +1,163 @@ +/* +Copyright 2016 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 metaonly + +import ( + "encoding/json" + "reflect" + "testing" + + _ "k8s.io/kubernetes/pkg/api/install" + "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/runtime/serializer" +) + +func getPod() *v1.Pod { + return &v1.Pod{ + TypeMeta: unversioned.TypeMeta{ + Kind: "Pod", + APIVersion: "v1", + }, + ObjectMeta: v1.ObjectMeta{ + Name: "pod", + OwnerReferences: []v1.OwnerReference{ + {UID: "1234"}, + }, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "fake-name", + Image: "fakeimage", + }, + }, + }, + } +} + +func getPodJson(t *testing.T) []byte { + data, err := json.Marshal(getPod()) + if err != nil { + t.Fatal(err) + } + return data +} + +func getPodListJson(t *testing.T) []byte { + data, err := json.Marshal(&v1.PodList{ + TypeMeta: unversioned.TypeMeta{ + Kind: "PodList", + APIVersion: "v1", + }, + Items: []v1.Pod{ + *getPod(), + *getPod(), + }, + }) + if err != nil { + t.Fatal(err) + } + return data +} + +func verfiyMetadata(description string, t *testing.T, in *MetadataOnlyObject) { + pod := getPod() + if e, a := pod.ObjectMeta, in.ObjectMeta; !reflect.DeepEqual(e, a) { + t.Errorf("%s: expected %#v, got %#v", description, e, a) + } +} + +func TestDecodeToMetadataOnlyObject(t *testing.T) { + data := getPodJson(t) + cf := serializer.DirectCodecFactory{CodecFactory: NewMetadataCodecFactory()} + serializer, ok := cf.SerializerForMediaType(runtime.ContentTypeJSON, nil) + if !ok { + t.Fatalf("expected to get a JSON serializer") + } + codec := cf.DecoderToVersion(serializer, unversioned.GroupVersion{Group: "SOMEGROUP", Version: "SOMEVERSION"}) + // decode with into + into := &MetadataOnlyObject{} + ret, _, err := codec.Decode(data, nil, into) + if err != nil { + t.Fatal(err) + } + metaOnly, ok := ret.(*MetadataOnlyObject) + if !ok { + t.Fatalf("expected ret to be *runtime.MetadataOnlyObject") + } + verfiyMetadata("check returned metaonly with into", t, metaOnly) + verfiyMetadata("check into", t, into) + // decode without into + ret, _, err = codec.Decode(data, nil, nil) + if err != nil { + t.Fatal(err) + } + metaOnly, ok = ret.(*MetadataOnlyObject) + if !ok { + t.Fatalf("expected ret to be *runtime.MetadataOnlyObject") + } + verfiyMetadata("check returned metaonly without into", t, metaOnly) +} + +func verifyListMetadata(t *testing.T, metaOnlyList *MetadataOnlyObjectList) { + items, err := meta.ExtractList(metaOnlyList) + if err != nil { + t.Fatal(err) + } + for _, item := range items { + metaOnly, ok := item.(*MetadataOnlyObject) + if !ok { + t.Fatalf("expected item to be *MetadataOnlyObject") + } + verfiyMetadata("check list", t, metaOnly) + } +} + +func TestDecodeToMetadataOnlyObjectList(t *testing.T) { + data := getPodListJson(t) + cf := serializer.DirectCodecFactory{CodecFactory: NewMetadataCodecFactory()} + serializer, ok := cf.SerializerForMediaType(runtime.ContentTypeJSON, nil) + if !ok { + t.Fatalf("expected to get a JSON serializer") + } + codec := cf.DecoderToVersion(serializer, unversioned.GroupVersion{Group: "SOMEGROUP", Version: "SOMEVERSION"}) + // decode with into + into := &MetadataOnlyObjectList{} + ret, _, err := codec.Decode(data, nil, into) + if err != nil { + t.Fatal(err) + } + metaOnlyList, ok := ret.(*MetadataOnlyObjectList) + if !ok { + t.Fatalf("expected ret to be *runtime.UnstructuredList") + } + verifyListMetadata(t, metaOnlyList) + verifyListMetadata(t, into) + // decode without into + ret, _, err = codec.Decode(data, nil, nil) + if err != nil { + t.Fatal(err) + } + metaOnlyList, ok = ret.(*MetadataOnlyObjectList) + if !ok { + t.Fatalf("expected ret to be *runtime.UnstructuredList") + } + verifyListMetadata(t, metaOnlyList) +} diff --git a/pkg/controller/garbagecollector/metaonly/types.generated.go b/pkg/controller/garbagecollector/metaonly/types.generated.go new file mode 100644 index 00000000000..a36a4d11494 --- /dev/null +++ b/pkg/controller/garbagecollector/metaonly/types.generated.go @@ -0,0 +1,794 @@ +/* +Copyright 2016 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. +*/ + +// ************************************************************ +// DO NOT EDIT. +// THIS FILE IS AUTO-GENERATED BY codecgen. +// ************************************************************ + +package metaonly + +import ( + "errors" + "fmt" + codec1978 "github.com/ugorji/go/codec" + pkg1_unversioned "k8s.io/kubernetes/pkg/api/unversioned" + pkg2_v1 "k8s.io/kubernetes/pkg/api/v1" + pkg3_types "k8s.io/kubernetes/pkg/types" + "reflect" + "runtime" + time "time" +) + +const ( + // ----- content types ---- + codecSelferC_UTF81234 = 1 + codecSelferC_RAW1234 = 0 + // ----- value types used ---- + codecSelferValueTypeArray1234 = 10 + codecSelferValueTypeMap1234 = 9 + // ----- containerStateValues ---- + codecSelfer_containerMapKey1234 = 2 + codecSelfer_containerMapValue1234 = 3 + codecSelfer_containerMapEnd1234 = 4 + codecSelfer_containerArrayElem1234 = 6 + codecSelfer_containerArrayEnd1234 = 7 +) + +var ( + codecSelferBitsize1234 = uint8(reflect.TypeOf(uint(0)).Bits()) + codecSelferOnlyMapOrArrayEncodeToStructErr1234 = errors.New(`only encoded map or array can be decoded into a struct`) +) + +type codecSelfer1234 struct{} + +func init() { + if codec1978.GenVersion != 5 { + _, file, _, _ := runtime.Caller(0) + err := fmt.Errorf("codecgen version mismatch: current: %v, need %v. Re-generate file: %v", + 5, codec1978.GenVersion, file) + panic(err) + } + if false { // reference the types, but skip this branch at build/run time + var v0 pkg1_unversioned.TypeMeta + var v1 pkg2_v1.ObjectMeta + var v2 pkg3_types.UID + var v3 time.Time + _, _, _, _ = v0, v1, v2, v3 + } +} + +func (x *MetadataOnlyObject) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [3]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = true + yyq2[1] = x.Kind != "" + yyq2[2] = x.APIVersion != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(3) + } else { + yynn2 = 0 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + yy4 := &x.ObjectMeta + yy4.CodecEncodeSelf(e) + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("metadata")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy6 := &x.ObjectMeta + yy6.CodecEncodeSelf(e) + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[1] { + yym9 := z.EncBinary() + _ = yym9 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[1] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("kind")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym12 := z.EncBinary() + _ = yym12 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym13 := z.EncBinary() + _ = yym13 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *MetadataOnlyObject) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *MetadataOnlyObject) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "metadata": + if r.TryDecodeAsNil() { + x.ObjectMeta = pkg2_v1.ObjectMeta{} + } else { + yyv4 := &x.ObjectMeta + yyv4.CodecDecodeSelf(d) + } + case "kind": + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + case "apiVersion": + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *MetadataOnlyObject) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj7 int + var yyb7 bool + var yyhl7 bool = l >= 0 + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ObjectMeta = pkg2_v1.ObjectMeta{} + } else { + yyv8 := &x.ObjectMeta + yyv8.CodecDecodeSelf(d) + } + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + for { + yyj7++ + if yyhl7 { + yyb7 = yyj7 > l + } else { + yyb7 = r.CheckBreak() + } + if yyb7 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj7-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x *MetadataOnlyObjectList) CodecEncodeSelf(e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + if x == nil { + r.EncodeNil() + } else { + yym1 := z.EncBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.EncExt(x) { + } else { + yysep2 := !z.EncBinary() + yy2arr2 := z.EncBasicHandle().StructToArray + var yyq2 [4]bool + _, _, _ = yysep2, yyq2, yy2arr2 + const yyr2 bool = false + yyq2[0] = true + yyq2[2] = x.Kind != "" + yyq2[3] = x.APIVersion != "" + var yynn2 int + if yyr2 || yy2arr2 { + r.EncodeArrayStart(4) + } else { + yynn2 = 1 + for _, b := range yyq2 { + if b { + yynn2++ + } + } + r.EncodeMapStart(yynn2) + yynn2 = 0 + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[0] { + yy4 := &x.ListMeta + yym5 := z.EncBinary() + _ = yym5 + if false { + } else if z.HasExtensions() && z.EncExt(yy4) { + } else { + z.EncFallback(yy4) + } + } else { + r.EncodeNil() + } + } else { + if yyq2[0] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("metadata")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yy6 := &x.ListMeta + yym7 := z.EncBinary() + _ = yym7 + if false { + } else if z.HasExtensions() && z.EncExt(yy6) { + } else { + z.EncFallback(yy6) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if x.Items == nil { + r.EncodeNil() + } else { + yym9 := z.EncBinary() + _ = yym9 + if false { + } else { + h.encSliceMetadataOnlyObject(([]MetadataOnlyObject)(x.Items), e) + } + } + } else { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("items")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + if x.Items == nil { + r.EncodeNil() + } else { + yym10 := z.EncBinary() + _ = yym10 + if false { + } else { + h.encSliceMetadataOnlyObject(([]MetadataOnlyObject)(x.Items), e) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[2] { + yym12 := z.EncBinary() + _ = yym12 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[2] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("kind")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym13 := z.EncBinary() + _ = yym13 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.Kind)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + if yyq2[3] { + yym15 := z.EncBinary() + _ = yym15 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } else { + r.EncodeString(codecSelferC_UTF81234, "") + } + } else { + if yyq2[3] { + z.EncSendContainerState(codecSelfer_containerMapKey1234) + r.EncodeString(codecSelferC_UTF81234, string("apiVersion")) + z.EncSendContainerState(codecSelfer_containerMapValue1234) + yym16 := z.EncBinary() + _ = yym16 + if false { + } else { + r.EncodeString(codecSelferC_UTF81234, string(x.APIVersion)) + } + } + } + if yyr2 || yy2arr2 { + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + z.EncSendContainerState(codecSelfer_containerMapEnd1234) + } + } + } +} + +func (x *MetadataOnlyObjectList) CodecDecodeSelf(d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + yym1 := z.DecBinary() + _ = yym1 + if false { + } else if z.HasExtensions() && z.DecExt(x) { + } else { + yyct2 := r.ContainerType() + if yyct2 == codecSelferValueTypeMap1234 { + yyl2 := r.ReadMapStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerMapEnd1234) + } else { + x.codecDecodeSelfFromMap(yyl2, d) + } + } else if yyct2 == codecSelferValueTypeArray1234 { + yyl2 := r.ReadArrayStart() + if yyl2 == 0 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + } else { + x.codecDecodeSelfFromArray(yyl2, d) + } + } else { + panic(codecSelferOnlyMapOrArrayEncodeToStructErr1234) + } + } +} + +func (x *MetadataOnlyObjectList) codecDecodeSelfFromMap(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yys3Slc = z.DecScratchBuffer() // default slice to decode into + _ = yys3Slc + var yyhl3 bool = l >= 0 + for yyj3 := 0; ; yyj3++ { + if yyhl3 { + if yyj3 >= l { + break + } + } else { + if r.CheckBreak() { + break + } + } + z.DecSendContainerState(codecSelfer_containerMapKey1234) + yys3Slc = r.DecodeBytes(yys3Slc, true, true) + yys3 := string(yys3Slc) + z.DecSendContainerState(codecSelfer_containerMapValue1234) + switch yys3 { + case "metadata": + if r.TryDecodeAsNil() { + x.ListMeta = pkg1_unversioned.ListMeta{} + } else { + yyv4 := &x.ListMeta + yym5 := z.DecBinary() + _ = yym5 + if false { + } else if z.HasExtensions() && z.DecExt(yyv4) { + } else { + z.DecFallback(yyv4, false) + } + } + case "items": + if r.TryDecodeAsNil() { + x.Items = nil + } else { + yyv6 := &x.Items + yym7 := z.DecBinary() + _ = yym7 + if false { + } else { + h.decSliceMetadataOnlyObject((*[]MetadataOnlyObject)(yyv6), d) + } + } + case "kind": + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + case "apiVersion": + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + default: + z.DecStructFieldNotFound(-1, yys3) + } // end switch yys3 + } // end for yyj3 + z.DecSendContainerState(codecSelfer_containerMapEnd1234) +} + +func (x *MetadataOnlyObjectList) codecDecodeSelfFromArray(l int, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + var yyj10 int + var yyb10 bool + var yyhl10 bool = l >= 0 + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.ListMeta = pkg1_unversioned.ListMeta{} + } else { + yyv11 := &x.ListMeta + yym12 := z.DecBinary() + _ = yym12 + if false { + } else if z.HasExtensions() && z.DecExt(yyv11) { + } else { + z.DecFallback(yyv11, false) + } + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Items = nil + } else { + yyv13 := &x.Items + yym14 := z.DecBinary() + _ = yym14 + if false { + } else { + h.decSliceMetadataOnlyObject((*[]MetadataOnlyObject)(yyv13), d) + } + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.Kind = "" + } else { + x.Kind = string(r.DecodeString()) + } + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) + return + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + if r.TryDecodeAsNil() { + x.APIVersion = "" + } else { + x.APIVersion = string(r.DecodeString()) + } + for { + yyj10++ + if yyhl10 { + yyb10 = yyj10 > l + } else { + yyb10 = r.CheckBreak() + } + if yyb10 { + break + } + z.DecSendContainerState(codecSelfer_containerArrayElem1234) + z.DecStructFieldNotFound(yyj10-1, "") + } + z.DecSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x codecSelfer1234) encSliceMetadataOnlyObject(v []MetadataOnlyObject, e *codec1978.Encoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperEncoder(e) + _, _, _ = h, z, r + r.EncodeArrayStart(len(v)) + for _, yyv1 := range v { + z.EncSendContainerState(codecSelfer_containerArrayElem1234) + yy2 := &yyv1 + yy2.CodecEncodeSelf(e) + } + z.EncSendContainerState(codecSelfer_containerArrayEnd1234) +} + +func (x codecSelfer1234) decSliceMetadataOnlyObject(v *[]MetadataOnlyObject, d *codec1978.Decoder) { + var h codecSelfer1234 + z, r := codec1978.GenHelperDecoder(d) + _, _, _ = h, z, r + + yyv1 := *v + yyh1, yyl1 := z.DecSliceHelperStart() + var yyc1 bool + _ = yyc1 + if yyl1 == 0 { + if yyv1 == nil { + yyv1 = []MetadataOnlyObject{} + yyc1 = true + } else if len(yyv1) != 0 { + yyv1 = yyv1[:0] + yyc1 = true + } + } else if yyl1 > 0 { + var yyrr1, yyrl1 int + var yyrt1 bool + _, _ = yyrl1, yyrt1 + yyrr1 = yyl1 // len(yyv1) + if yyl1 > cap(yyv1) { + + yyrg1 := len(yyv1) > 0 + yyv21 := yyv1 + yyrl1, yyrt1 = z.DecInferLen(yyl1, z.DecBasicHandle().MaxInitLen, 240) + if yyrt1 { + if yyrl1 <= cap(yyv1) { + yyv1 = yyv1[:yyrl1] + } else { + yyv1 = make([]MetadataOnlyObject, yyrl1) + } + } else { + yyv1 = make([]MetadataOnlyObject, yyrl1) + } + yyc1 = true + yyrr1 = len(yyv1) + if yyrg1 { + copy(yyv1, yyv21) + } + } else if yyl1 != len(yyv1) { + yyv1 = yyv1[:yyl1] + yyc1 = true + } + yyj1 := 0 + for ; yyj1 < yyrr1; yyj1++ { + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = MetadataOnlyObject{} + } else { + yyv2 := &yyv1[yyj1] + yyv2.CodecDecodeSelf(d) + } + + } + if yyrt1 { + for ; yyj1 < yyl1; yyj1++ { + yyv1 = append(yyv1, MetadataOnlyObject{}) + yyh1.ElemContainerState(yyj1) + if r.TryDecodeAsNil() { + yyv1[yyj1] = MetadataOnlyObject{} + } else { + yyv3 := &yyv1[yyj1] + yyv3.CodecDecodeSelf(d) + } + + } + } + + } else { + yyj1 := 0 + for ; !r.CheckBreak(); yyj1++ { + + if yyj1 >= len(yyv1) { + yyv1 = append(yyv1, MetadataOnlyObject{}) // var yyz1 MetadataOnlyObject + yyc1 = true + } + yyh1.ElemContainerState(yyj1) + if yyj1 < len(yyv1) { + if r.TryDecodeAsNil() { + yyv1[yyj1] = MetadataOnlyObject{} + } else { + yyv4 := &yyv1[yyj1] + yyv4.CodecDecodeSelf(d) + } + + } else { + z.DecSwallow() + } + + } + if yyj1 < len(yyv1) { + yyv1 = yyv1[:yyj1] + yyc1 = true + } else if yyj1 == 0 && yyv1 == nil { + yyv1 = []MetadataOnlyObject{} + yyc1 = true + } + } + yyh1.End() + if yyc1 { + *v = yyv1 + } +} diff --git a/pkg/controller/garbagecollector/metaonly/types.go b/pkg/controller/garbagecollector/metaonly/types.go new file mode 100644 index 00000000000..a07f389f2cf --- /dev/null +++ b/pkg/controller/garbagecollector/metaonly/types.go @@ -0,0 +1,40 @@ +/* +Copyright 2016 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 metaonly + +import ( + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/api/v1" +) + +// MetadataOnlyObject allows decoding only the apiVersion, kind, and metadata fields of +// JSON data. +// TODO: enable meta-only decoding for protobuf. +type MetadataOnlyObject struct { + unversioned.TypeMeta `json:",inline"` + v1.ObjectMeta `json:"metadata,omitempty"` +} + +// MetadataOnlyObjectList allows decoding from JSON data only the typemeta and metadata of +// a list, and those of the enclosing objects. +// TODO: enable meta-only decoding for protobuf. +type MetadataOnlyObjectList struct { + unversioned.TypeMeta `json:",inline"` + unversioned.ListMeta `json:"metadata,omitempty"` + + Items []MetadataOnlyObject `json:"items"` +} diff --git a/pkg/controller/namespace/namespace_controller_test.go b/pkg/controller/namespace/namespace_controller_test.go index 056204d86e9..dcee9a696e3 100644 --- a/pkg/controller/namespace/namespace_controller_test.go +++ b/pkg/controller/namespace/namespace_controller_test.go @@ -272,7 +272,7 @@ func (f *fakeActionHandler) ServeHTTP(response http.ResponseWriter, request *htt f.actions = append(f.actions, fakeAction{method: request.Method, path: request.URL.Path}) response.Header().Set("Content-Type", runtime.ContentTypeJSON) response.WriteHeader(f.statusCode) - response.Write([]byte("{\"kind\": \"List\"}")) + response.Write([]byte("{\"kind\": \"List\",\"items\":null}")) } // testGroupVersionResources returns a mocked up set of resources across different api groups for testing namespace controller. diff --git a/pkg/controller/namespace/namespace_controller_utils.go b/pkg/controller/namespace/namespace_controller_utils.go index f6ed3f33995..88e7f9a76a6 100644 --- a/pkg/controller/namespace/namespace_controller_utils.go +++ b/pkg/controller/namespace/namespace_controller_utils.go @@ -195,8 +195,12 @@ func listCollection( } apiResource := unversioned.APIResource{Name: gvr.Resource, Namespaced: true} - unstructuredList, err := dynamicClient.Resource(&apiResource, namespace).List(&v1.ListOptions{}) + obj, err := dynamicClient.Resource(&apiResource, namespace).List(&v1.ListOptions{}) if err == nil { + unstructuredList, ok := obj.(*runtime.UnstructuredList) + if !ok { + return nil, false, fmt.Errorf("resource: %s, expected *runtime.UnstructuredList, got %#v", apiResource.Name, obj) + } return unstructuredList, true, nil } diff --git a/pkg/runtime/codec.go b/pkg/runtime/codec.go index 3f7681c0024..dc70ea55903 100644 --- a/pkg/runtime/codec.go +++ b/pkg/runtime/codec.go @@ -80,12 +80,14 @@ func EncodeOrDie(e Encoder, obj Object) string { // invokes the ObjectCreator to instantiate a new gvk. Returns an error if the typer cannot find the object. func UseOrCreateObject(t ObjectTyper, c ObjectCreater, gvk unversioned.GroupVersionKind, obj Object) (Object, error) { if obj != nil { - into, _, err := t.ObjectKinds(obj) + kinds, _, err := t.ObjectKinds(obj) if err != nil { return nil, err } - if gvk == into[0] { - return obj, nil + for _, kind := range kinds { + if gvk == kind { + return obj, nil + } } } return c.New(gvk) diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index 71b01073c4c..3da67978b97 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -211,6 +211,11 @@ func (s *Scheme) KnownTypes(gv unversioned.GroupVersion) map[string]reflect.Type return types } +// AllKnownTypes returns the all known types. +func (s *Scheme) AllKnownTypes() map[unversioned.GroupVersionKind]reflect.Type { + return s.gvkToType +} + // ObjectKind returns the group,version,kind of the go object and true if this object // is considered unversioned, or an error if it's not a pointer or is unregistered. func (s *Scheme) ObjectKind(obj Object) (unversioned.GroupVersionKind, bool, error) { diff --git a/pkg/runtime/unstructured.go b/pkg/runtime/unstructured.go index 048e6dc148c..6cc34432c05 100644 --- a/pkg/runtime/unstructured.go +++ b/pkg/runtime/unstructured.go @@ -95,6 +95,7 @@ func (s unstructuredJSONScheme) decode(data []byte) (Object, error) { err := s.decodeToUnstructured(data, unstruct) return unstruct, err } + func (s unstructuredJSONScheme) decodeInto(data []byte, obj Object) error { switch x := obj.(type) { case *Unstructured: diff --git a/test/integration/client/dynamic_client_test.go b/test/integration/client/dynamic_client_test.go index e65b87be2d3..e05a2831f16 100644 --- a/test/integration/client/dynamic_client_test.go +++ b/test/integration/client/dynamic_client_test.go @@ -92,7 +92,11 @@ func TestDynamicClient(t *testing.T) { } // check dynamic list - unstructuredList, err := dynamicClient.Resource(&resource, ns.Name).List(&v1.ListOptions{}) + obj, err := dynamicClient.Resource(&resource, ns.Name).List(&v1.ListOptions{}) + unstructuredList, ok := obj.(*runtime.UnstructuredList) + if !ok { + t.Fatalf("expected *runtime.UnstructuredList, got %#v", obj) + } if err != nil { t.Fatalf("unexpected error when listing pods: %v", err) } diff --git a/test/integration/garbagecollector/garbage_collector_test.go b/test/integration/garbagecollector/garbage_collector_test.go index 282651eea06..d42cbede372 100644 --- a/test/integration/garbagecollector/garbage_collector_test.go +++ b/test/integration/garbagecollector/garbage_collector_test.go @@ -35,7 +35,9 @@ import ( "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/client/typed/dynamic" "k8s.io/kubernetes/pkg/controller/garbagecollector" + "k8s.io/kubernetes/pkg/controller/garbagecollector/metaonly" "k8s.io/kubernetes/pkg/registry/generic/registry" + "k8s.io/kubernetes/pkg/runtime/serializer" "k8s.io/kubernetes/pkg/types" "k8s.io/kubernetes/pkg/util/wait" "k8s.io/kubernetes/test/integration/framework" @@ -127,8 +129,12 @@ func setup(t *testing.T) (*httptest.Server, *garbagecollector.GarbageCollector, if err != nil { t.Fatalf("Failed to get supported resources from server: %v", err) } - clientPool := dynamic.NewClientPool(&restclient.Config{Host: s.URL}, dynamic.LegacyAPIPathResolverFunc) - gc, err := garbagecollector.NewGarbageCollector(clientPool, groupVersionResources) + config := &restclient.Config{Host: s.URL} + config.ContentConfig.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: metaonly.NewMetadataCodecFactory()} + metaOnlyClientPool := dynamic.NewClientPool(config, dynamic.LegacyAPIPathResolverFunc) + config.ContentConfig.NegotiatedSerializer = nil + clientPool := dynamic.NewClientPool(config, dynamic.LegacyAPIPathResolverFunc) + gc, err := garbagecollector.NewGarbageCollector(metaOnlyClientPool, clientPool, groupVersionResources) if err != nil { t.Fatalf("Failed to create garbage collector") } @@ -215,7 +221,7 @@ func TestCascadingDeletion(t *testing.T) { // sometimes the deletion of the RC takes long time to be observed by // the gc, so wait for the garbage collector to observe the deletion of // the toBeDeletedRC - if err := wait.Poll(10*time.Second, 120*time.Second, func() (bool, error) { + if err := wait.Poll(10*time.Second, 60*time.Second, func() (bool, error) { return !gc.GraphHasUID([]types.UID{toBeDeletedRC.ObjectMeta.UID}), nil }); err != nil { t.Fatal(err)