diff --git a/pkg/api/install/install.go b/pkg/api/install/install.go index 05433889c47..bed5f0791ab 100644 --- a/pkg/api/install/install.go +++ b/pkg/api/install/install.go @@ -32,6 +32,7 @@ import ( "k8s.io/kubernetes/pkg/conversion" "k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/util/sets" + "k8s.io/kubernetes/pkg/watch/versioned" ) const importPrefix = "k8s.io/kubernetes/pkg/api" @@ -233,6 +234,17 @@ func addVersionsToScheme(externalVersions ...unversioned.GroupVersion) { case *v1.Endpoints: return true, v1.Convert_api_Endpoints_To_v1_Endpoints(a, b, s) } + + case *versioned.Event: + switch b := objB.(type) { + case *versioned.InternalEvent: + return true, versioned.Convert_versioned_Event_to_versioned_InternalEvent(a, b, s) + } + case *versioned.InternalEvent: + switch b := objB.(type) { + case *versioned.Event: + return true, versioned.Convert_versioned_InternalEvent_to_versioned_Event(a, b, s) + } } return false, nil }) diff --git a/pkg/api/ref.go b/pkg/api/ref.go index a6d1d627dc6..08dede0711f 100644 --- a/pkg/api/ref.go +++ b/pkg/api/ref.go @@ -57,11 +57,11 @@ func GetReference(obj runtime.Object) (*ObjectReference, error) { kind := gvk.Kind if len(kind) == 0 { // TODO: this is wrong - gvk, err := Scheme.ObjectKind(obj) + gvks, _, err := Scheme.ObjectKinds(obj) if err != nil { return nil, err } - kind = gvk.Kind + kind = gvks[0].Kind } // if the object referenced is actually persisted, we can also get version from meta diff --git a/pkg/api/ref_test.go b/pkg/api/ref_test.go index dbde7a01f9c..ad4df02b3e1 100644 --- a/pkg/api/ref_test.go +++ b/pkg/api/ref_test.go @@ -40,7 +40,7 @@ func TestGetReference(t *testing.T) { // when vendoring kube, if you don't force the set of registered versions (like this hack/test-go.sh does) // then you run into trouble because the types aren't registered in the scheme by anything. This does the // register manually to allow unit test execution - if _, err := Scheme.ObjectKind(&Pod{}); err != nil { + if _, _, err := Scheme.ObjectKinds(&Pod{}); err != nil { AddToScheme(Scheme) } diff --git a/pkg/api/rest/create.go b/pkg/api/rest/create.go index 4e3a8938b0d..fa95b7f93f2 100644 --- a/pkg/api/rest/create.go +++ b/pkg/api/rest/create.go @@ -112,11 +112,11 @@ func objectMetaAndKind(typer runtime.ObjectTyper, obj runtime.Object) (*api.Obje if err != nil { return nil, unversioned.GroupVersionKind{}, errors.NewInternalError(err) } - kind, err := typer.ObjectKind(obj) + kinds, _, err := typer.ObjectKinds(obj) if err != nil { return nil, unversioned.GroupVersionKind{}, errors.NewInternalError(err) } - return objectMeta, kind, nil + return objectMeta, kinds[0], nil } // NamespaceScopedStrategy has a method to tell if the object must be in a namespace. diff --git a/pkg/api/serialization_proto_test.go b/pkg/api/serialization_proto_test.go index a108a98308c..36fce27a1c0 100644 --- a/pkg/api/serialization_proto_test.go +++ b/pkg/api/serialization_proto_test.go @@ -37,7 +37,7 @@ import ( func init() { codecsToTest = append(codecsToTest, func(version unversioned.GroupVersion, item runtime.Object) (runtime.Codec, error) { - s := protobuf.NewSerializer(api.Scheme, runtime.ObjectTyperToTyper(api.Scheme), "application/arbitrary.content.type") + s := protobuf.NewSerializer(api.Scheme, api.Scheme, "application/arbitrary.content.type") return api.Codecs.CodecForVersions(s, s, testapi.ExternalGroupVersions(), nil), nil }) } @@ -138,7 +138,7 @@ func BenchmarkEncodeProtobufGeneratedMarshal(b *testing.B) { func BenchmarkDecodeCodecToInternalProtobuf(b *testing.B) { items := benchmarkItems() width := len(items) - s := protobuf.NewSerializer(api.Scheme, runtime.ObjectTyperToTyper(api.Scheme), "application/arbitrary.content.type") + s := protobuf.NewSerializer(api.Scheme, api.Scheme, "application/arbitrary.content.type") encoder := api.Codecs.EncoderForVersion(s, v1.SchemeGroupVersion) var encoded [][]byte for i := range items { diff --git a/pkg/api/testapi/testapi.go b/pkg/api/testapi/testapi.go index fd1fa69e01a..f738acc5889 100644 --- a/pkg/api/testapi/testapi.go +++ b/pkg/api/testapi/testapi.go @@ -365,10 +365,11 @@ func ExternalGroupVersions() []unversioned.GroupVersion { // Get codec based on runtime.Object func GetCodecForObject(obj runtime.Object) (runtime.Codec, error) { - kind, err := api.Scheme.ObjectKind(obj) + kinds, _, err := api.Scheme.ObjectKinds(obj) if err != nil { return nil, fmt.Errorf("unexpected encoding error: %v", err) } + kind := kinds[0] for _, group := range Groups { if group.GroupVersion().Group != kind.Group { diff --git a/pkg/api/testing/fuzzer.go b/pkg/api/testing/fuzzer.go index 2b88e50f97e..5c52bbb77e2 100644 --- a/pkg/api/testing/fuzzer.go +++ b/pkg/api/testing/fuzzer.go @@ -195,6 +195,8 @@ func FuzzerFor(t *testing.T, version unversioned.GroupVersion, src rand.Source) randomQuantity := func() resource.Quantity { var q resource.Quantity c.Fuzz(&q) + // precalc the string for benchmarking purposes + _ = q.String() return q } q.Limits = make(api.ResourceList) diff --git a/pkg/apiserver/api_installer.go b/pkg/apiserver/api_installer.go index 75ab8f69e86..30690dd7c0e 100644 --- a/pkg/apiserver/api_installer.go +++ b/pkg/apiserver/api_installer.go @@ -118,7 +118,7 @@ func (a *APIInstaller) getResourceKind(path string, storage rest.Storage) (unver } object := storage.New() - fqKinds, err := a.group.Typer.ObjectKinds(object) + fqKinds, _, err := a.group.Typer.ObjectKinds(object) if err != nil { return unversioned.GroupVersionKind{}, err } @@ -233,8 +233,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag var versionedList interface{} if isLister { list := lister.NewList() - listGVK, err := a.group.Typer.ObjectKind(list) - versionedListPtr, err := a.group.Creater.New(a.group.GroupVersion.WithKind(listGVK.Kind)) + listGVKs, _, err := a.group.Typer.ObjectKinds(list) + if err != nil { + return nil, err + } + versionedListPtr, err := a.group.Creater.New(a.group.GroupVersion.WithKind(listGVKs[0].Kind)) if err != nil { return nil, err } @@ -272,10 +275,11 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag ) if isGetterWithOptions { getOptions, getSubpath, _ = getterWithOptions.NewGetOptions() - getOptionsInternalKind, err = a.group.Typer.ObjectKind(getOptions) + getOptionsInternalKinds, _, err := a.group.Typer.ObjectKinds(getOptions) if err != nil { return nil, err } + getOptionsInternalKind = getOptionsInternalKinds[0] versionedGetOptions, err = a.group.Creater.New(optionsExternalVersion.WithKind(getOptionsInternalKind.Kind)) if err != nil { return nil, err @@ -300,12 +304,16 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag if isConnecter { connectOptions, connectSubpath, _ = connecter.NewConnectOptions() if connectOptions != nil { - connectOptionsInternalKind, err = a.group.Typer.ObjectKind(connectOptions) + connectOptionsInternalKinds, _, err := a.group.Typer.ObjectKinds(connectOptions) if err != nil { return nil, err } + connectOptionsInternalKind = connectOptionsInternalKinds[0] versionedConnectOptions, err = a.group.Creater.New(optionsExternalVersion.WithKind(connectOptionsInternalKind.Kind)) + if err != nil { + return nil, err + } } } @@ -390,18 +398,26 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag resourcePath := namespacedPath resourceParams := namespaceParams + itemPathPrefix := gpath.Join(a.prefix, scope.ParamName()) + "/" itemPath := namespacedPath + "/{name}" + itemPathMiddle := "/" + resource + "/" nameParams := append(namespaceParams, nameParam) proxyParams := append(nameParams, pathParam) + itemPathSuffix := "" if hasSubresource { - itemPath = itemPath + "/" + subresource + itemPathSuffix = "/" + subresource + itemPath = itemPath + itemPathSuffix resourcePath = itemPath resourceParams = nameParams } apiResource.Name = path apiResource.Namespaced = true apiResource.Kind = resourceKind - namer := scopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath), false} + + itemPathFn := func(name, namespace string) string { + return itemPathPrefix + namespace + itemPathMiddle + name + itemPathSuffix + } + namer := scopeNaming{scope, a.group.Linker, itemPathFn, false} actions = appendIf(actions, action{"LIST", resourcePath, resourceParams, namer}, isLister) actions = appendIf(actions, action{"POST", resourcePath, resourceParams, namer}, isCreater) @@ -430,7 +446,7 @@ func (a *APIInstaller) registerResourceHandlers(path string, storage rest.Storag // For ex: LIST all pods in all namespaces by sending a LIST request at /api/apiVersion/pods. // TODO: more strongly type whether a resource allows these actions on "all namespaces" (bulk delete) if !hasSubresource { - namer = scopeNaming{scope, a.group.Linker, gpath.Join(a.prefix, itemPath), true} + namer = scopeNaming{scope, a.group.Linker, itemPathFn, true} actions = appendIf(actions, action{"LIST", resource, params, namer}, isLister) actions = appendIf(actions, action{"WATCHLIST", "watch/" + resource, params, namer}, allowWatchList) } @@ -776,7 +792,7 @@ func (n rootScopeNaming) ObjectName(obj runtime.Object) (namespace, name string, type scopeNaming struct { scope meta.RESTScope runtime.SelfLinker - itemPath string + itemPathFn func(name, namespace string) string allNamespaces bool } @@ -823,9 +839,8 @@ func (n scopeNaming) GenerateLink(req *restful.Request, obj runtime.Object) (pat if len(name) == 0 { return "", "", errEmptyName } - path = strings.Replace(n.itemPath, "{name}", name, 1) - path = strings.Replace(path, "{"+n.scope.ArgumentName()+"}", namespace, 1) - return path, "", nil + + return n.itemPathFn(name, namespace), "", nil } // GenerateListLink returns the appropriate path and query to locate a list by its canonical path. diff --git a/pkg/apiserver/api_installer_test.go b/pkg/apiserver/api_installer_test.go index 57df10e94c4..3d5f2b869a4 100644 --- a/pkg/apiserver/api_installer_test.go +++ b/pkg/apiserver/api_installer_test.go @@ -36,7 +36,9 @@ func TestScopeNamingGenerateLink(t *testing.T) { s := scopeNaming{ meta.RESTScopeNamespace, selfLinker, - "/api/v1/namespaces/{namespace}/services/{name}", + func(name, namespace string) string { + return "/api/v1/namespaces/" + namespace + "/services/" + name + }, true, } service := &api.Service{ diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index 325b5641919..c5918373560 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -23,7 +23,6 @@ import ( "math/rand" "net/http" "net/url" - gpath "path" "strings" "time" @@ -975,10 +974,11 @@ func finishRequest(timeout time.Duration, fn resultFunc) (result runtime.Object, // transformDecodeError adds additional information when a decode fails. func transformDecodeError(typer runtime.ObjectTyper, baseErr error, into runtime.Object, gvk *unversioned.GroupVersionKind, body []byte) error { - objGVK, err := typer.ObjectKind(into) + objGVKs, _, err := typer.ObjectKinds(into) if err != nil { return err } + objGVK := objGVKs[0] if gvk != nil && len(gvk.Kind) > 0 { return errors.NewBadRequest(fmt.Sprintf("%s in version %q cannot be handled as a %s: %v", gvk.Kind, gvk.Version, objGVK.Kind, baseErr)) } @@ -997,7 +997,7 @@ func setSelfLink(obj runtime.Object, req *restful.Request, namer ScopeNamer) err newURL := *req.Request.URL // use only canonical paths - newURL.Path = gpath.Clean(path) + newURL.Path = path newURL.RawQuery = query newURL.Fragment = "" diff --git a/pkg/apiserver/watch.go b/pkg/apiserver/watch.go index 8094ac9c006..b62693822f7 100644 --- a/pkg/apiserver/watch.go +++ b/pkg/apiserver/watch.go @@ -167,18 +167,22 @@ func (s *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusOK) flusher.Flush() + var unknown runtime.Unknown + internalEvent := &versioned.InternalEvent{} buf := &bytes.Buffer{} + ch := s.watching.ResultChan() for { select { case <-cn.CloseNotify(): return case <-timeoutCh: return - case event, ok := <-s.watching.ResultChan(): + case event, ok := <-ch: if !ok { // End of results. return } + obj := event.Object s.fixup(obj) if err := s.embeddedEncoder.EncodeToStream(obj, buf); err != nil { @@ -186,12 +190,15 @@ func (s *WatchServer) ServeHTTP(w http.ResponseWriter, req *http.Request) { utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v", err)) return } - event.Object = &runtime.Unknown{ - Raw: buf.Bytes(), - // ContentType is not required here because we are defaulting to the serializer - // type - } - if err := e.Encode((*versioned.InternalEvent)(&event)); err != nil { + + // ContentType is not required here because we are defaulting to the serializer + // type + unknown.Raw = buf.Bytes() + event.Object = &unknown + + // the internal event will be versioned by the encoder + *internalEvent = versioned.InternalEvent(event) + if err := e.Encode(internalEvent); err != nil { utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v (%#v)", err, e)) // client disconnect. return @@ -208,14 +215,18 @@ func (s *WatchServer) HandleWS(ws *websocket.Conn) { defer ws.Close() done := make(chan struct{}) go wsstream.IgnoreReceives(ws, 0) + + var unknown runtime.Unknown + internalEvent := &versioned.InternalEvent{} buf := &bytes.Buffer{} streamBuf := &bytes.Buffer{} + ch := s.watching.ResultChan() for { select { case <-done: s.watching.Stop() return - case event, ok := <-s.watching.ResultChan(): + case event, ok := <-ch: if !ok { // End of results. return @@ -227,14 +238,15 @@ func (s *WatchServer) HandleWS(ws *websocket.Conn) { utilruntime.HandleError(fmt.Errorf("unable to encode watch object: %v", err)) return } - event.Object = &runtime.Unknown{ - Raw: buf.Bytes(), - // ContentType is not required here because we are defaulting to the serializer - // type - } + + // ContentType is not required here because we are defaulting to the serializer + // type + unknown.Raw = buf.Bytes() + event.Object = &unknown + // the internal event will be versioned by the encoder - internalEvent := versioned.InternalEvent(event) - if err := s.encoder.EncodeToStream(&internalEvent, streamBuf); err != nil { + *internalEvent = versioned.InternalEvent(event) + if err := s.encoder.EncodeToStream(internalEvent, streamBuf); err != nil { // encoding error utilruntime.HandleError(fmt.Errorf("unable to encode event: %v", err)) s.watching.Stop() diff --git a/pkg/apiserver/watch_test.go b/pkg/apiserver/watch_test.go index 1b25771373f..8386008c851 100644 --- a/pkg/apiserver/watch_test.go +++ b/pkg/apiserver/watch_test.go @@ -561,6 +561,7 @@ func benchmarkItems() []api.Pod { items := make([]api.Pod, 3) for i := range items { apiObjectFuzzer.Fuzz(&items[i]) + items[i].Spec.InitContainers, items[i].Status.InitContainerStatuses = nil, nil } return items } diff --git a/pkg/client/testing/core/fixture.go b/pkg/client/testing/core/fixture.go index 94732304276..bd4e560064e 100644 --- a/pkg/client/testing/core/fixture.go +++ b/pkg/client/testing/core/fixture.go @@ -210,11 +210,11 @@ func (o objects) Kind(kind unversioned.GroupVersionKind, name string) (runtime.O } func (o objects) Add(obj runtime.Object) error { - gvk, err := o.scheme.ObjectKind(obj) + gvks, _, err := o.scheme.ObjectKinds(obj) if err != nil { return err } - kind := gvk.Kind + kind := gvks[0].Kind switch { case meta.IsListType(obj): diff --git a/pkg/client/typed/dynamic/dynamic_util.go b/pkg/client/typed/dynamic/dynamic_util.go index bcd18b2c6d2..094f838115b 100644 --- a/pkg/client/typed/dynamic/dynamic_util.go +++ b/pkg/client/typed/dynamic/dynamic_util.go @@ -76,28 +76,15 @@ func NewObjectTyper(resources []*unversioned.APIResourceList) (runtime.ObjectTyp return ot, nil } -// ObjectKind returns the group,version,kind of the provided object, -// or an error if the object in not *runtime.Unstructured or has no -// group,version,kind information. -func (ot *ObjectTyper) ObjectKind(obj runtime.Object) (unversioned.GroupVersionKind, error) { - if _, ok := obj.(*runtime.Unstructured); !ok { - return unversioned.GroupVersionKind{}, fmt.Errorf("type %T is invalid for dynamic object typer", obj) - } - - return obj.GetObjectKind().GroupVersionKind(), nil -} - // ObjectKinds returns a slice of one element with the // group,version,kind of the provided object, or an error if the // object is not *runtime.Unstructured or has no group,version,kind // information. -func (ot *ObjectTyper) ObjectKinds(obj runtime.Object) ([]unversioned.GroupVersionKind, error) { - gvk, err := ot.ObjectKind(obj) - if err != nil { - return nil, err +func (ot *ObjectTyper) ObjectKinds(obj runtime.Object) ([]unversioned.GroupVersionKind, bool, error) { + if _, ok := obj.(*runtime.Unstructured); !ok { + return nil, false, fmt.Errorf("type %T is invalid for dynamic object typer", obj) } - - return []unversioned.GroupVersionKind{gvk}, nil + return []unversioned.GroupVersionKind{obj.GetObjectKind().GroupVersionKind()}, false, nil } // Recognizes returns true if the provided group,version,kind was in @@ -105,15 +92,3 @@ func (ot *ObjectTyper) ObjectKinds(obj runtime.Object) ([]unversioned.GroupVersi func (ot *ObjectTyper) Recognizes(gvk unversioned.GroupVersionKind) bool { return ot.registered[gvk] } - -// IsUnversioned returns false always because *runtime.Unstructured -// objects should always have group,version,kind information set. ok -// will be true if the object's group,version,kind is registered. -func (ot *ObjectTyper) IsUnversioned(obj runtime.Object) (unversioned bool, ok bool) { - gvk, err := ot.ObjectKind(obj) - if err != nil { - return false, false - } - - return false, ot.registered[gvk] -} diff --git a/pkg/client/unversioned/clientcmd/api/latest/latest.go b/pkg/client/unversioned/clientcmd/api/latest/latest.go index d974aa9a908..48cedb82ea5 100644 --- a/pkg/client/unversioned/clientcmd/api/latest/latest.go +++ b/pkg/client/unversioned/clientcmd/api/latest/latest.go @@ -43,7 +43,7 @@ var Versions = []string{"v1"} var Codec runtime.Codec func init() { - yamlSerializer := json.NewYAMLSerializer(json.DefaultMetaFactory, api.Scheme, runtime.ObjectTyperToTyper(api.Scheme)) + yamlSerializer := json.NewYAMLSerializer(json.DefaultMetaFactory, api.Scheme, api.Scheme) Codec = versioning.NewCodecForScheme( api.Scheme, yamlSerializer, diff --git a/pkg/client/unversioned/testclient/fixture.go b/pkg/client/unversioned/testclient/fixture.go index bb02e96d1dc..0fdbeac9f66 100644 --- a/pkg/client/unversioned/testclient/fixture.go +++ b/pkg/client/unversioned/testclient/fixture.go @@ -204,11 +204,11 @@ func (o objects) Kind(kind unversioned.GroupVersionKind, name string) (runtime.O } func (o objects) Add(obj runtime.Object) error { - gvk, err := o.scheme.ObjectKind(obj) + gvks, _, err := o.scheme.ObjectKinds(obj) if err != nil { return err } - kind := gvk.Kind + kind := gvks[0].Kind switch { case meta.IsListType(obj): diff --git a/pkg/client/unversioned/testclient/simple/simple_testclient.go b/pkg/client/unversioned/testclient/simple/simple_testclient.go index 9f11721cd55..546fb7c47b7 100644 --- a/pkg/client/unversioned/testclient/simple/simple_testclient.go +++ b/pkg/client/unversioned/testclient/simple/simple_testclient.go @@ -224,11 +224,11 @@ func validateFields(a, b string) bool { func (c *Client) body(t *testing.T, obj runtime.Object, raw *string) *string { if obj != nil { - fqKind, err := api.Scheme.ObjectKind(obj) + fqKinds, _, err := api.Scheme.ObjectKinds(obj) if err != nil { t.Errorf("unexpected encoding error: %v", err) } - groupName := fqKind.GroupVersion().Group + groupName := fqKinds[0].GroupVersion().Group if c.ResourceGroup != "" { groupName = c.ResourceGroup } diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 60a2ba2e4b2..48b5e8d9155 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -320,11 +320,11 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) { } return c.Pods(t.Namespace).GetLogs(t.Name, opts), nil default: - fqKind, err := api.Scheme.ObjectKind(object) + fqKinds, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } - return nil, fmt.Errorf("cannot get the logs from %v", fqKind) + return nil, fmt.Errorf("cannot get the logs from %v", fqKinds[0]) } }, } diff --git a/pkg/kubectl/cmd/create.go b/pkg/kubectl/cmd/create.go index 24dfc74bddf..3db768c4c1d 100644 --- a/pkg/kubectl/cmd/create.go +++ b/pkg/kubectl/cmd/create.go @@ -238,7 +238,11 @@ func RunCreateSubcommand(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, return err } mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd)) - gvk, err := typer.ObjectKind(obj) + gvks, _, err := typer.ObjectKinds(obj) + if err != nil { + return err + } + gvk := gvks[0] mapping, err := mapper.RESTMapping(unversioned.GroupKind{Group: gvk.Group, Kind: gvk.Kind}, gvk.Version) if err != nil { return err diff --git a/pkg/kubectl/cmd/rollingupdate.go b/pkg/kubectl/cmd/rollingupdate.go index 1170aea7d63..d47bfaf0aef 100644 --- a/pkg/kubectl/cmd/rollingupdate.go +++ b/pkg/kubectl/cmd/rollingupdate.go @@ -221,8 +221,8 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg } newRc, ok = obj.(*api.ReplicationController) if !ok { - if gvk, err := typer.ObjectKind(obj); err == nil { - return cmdutil.UsageError(cmd, "%s contains a %v not a ReplicationController", filename, gvk) + if gvks, _, err := typer.ObjectKinds(obj); err == nil { + return cmdutil.UsageError(cmd, "%s contains a %v not a ReplicationController", filename, gvks[0]) } glog.V(4).Infof("Object %#v is not a ReplicationController", obj) return cmdutil.UsageError(cmd, "%s does not specify a valid ReplicationController", filename) @@ -377,11 +377,11 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg if outputFormat != "" { return f.PrintObject(cmd, mapper, newRc, out) } - kind, err := api.Scheme.ObjectKind(newRc) + kinds, _, err := api.Scheme.ObjectKinds(newRc) if err != nil { return err } - _, res := meta.KindToResource(kind) + _, res := meta.KindToResource(kinds[0]) cmdutil.PrintSuccess(mapper, false, out, res.Resource, oldName, message) return nil } diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index 236e758dce7..548cb76256a 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -443,10 +443,11 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub } mapper, typer := f.Object(cmdutil.GetIncludeThirdPartyAPIs(cmd)) - groupVersionKind, err := typer.ObjectKind(obj) + groupVersionKinds, _, err := typer.ObjectKinds(obj) if err != nil { return nil, "", nil, nil, err } + groupVersionKind := groupVersionKinds[0] if len(overrides) > 0 { codec := runtime.NewCodec(f.JSONEncoder(), f.Decoder(true)) diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 4078b46819f..a3afd44ab6e 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -427,11 +427,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil default: - gvk, err := api.Scheme.ObjectKind(object) + gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return "", err } - return "", fmt.Errorf("cannot extract pod selector from %v", gvk) + return "", fmt.Errorf("cannot extract pod selector from %v", gvks[0]) } }, PortsForObject: func(object runtime.Object) ([]string, error) { @@ -448,11 +448,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { case *extensions.ReplicaSet: return getPorts(t.Spec.Template.Spec), nil default: - gvk, err := api.Scheme.ObjectKind(object) + gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } - return nil, fmt.Errorf("cannot extract ports from %v", gvk) + return nil, fmt.Errorf("cannot extract ports from %v", gvks[0]) } }, ProtocolsForObject: func(object runtime.Object) (map[string]string, error) { @@ -469,11 +469,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { case *extensions.ReplicaSet: return getProtocols(t.Spec.Template.Spec), nil default: - gvk, err := api.Scheme.ObjectKind(object) + gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } - return nil, fmt.Errorf("cannot extract protocols from %v", gvk) + return nil, fmt.Errorf("cannot extract protocols from %v", gvks[0]) } }, LabelsForObject: func(object runtime.Object) (map[string]string, error) { @@ -531,11 +531,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { return c.Pods(pod.Namespace).GetLogs(pod.Name, opts), nil default: - gvk, err := api.Scheme.ObjectKind(object) + gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } - return nil, fmt.Errorf("cannot get the logs from %v", gvk) + return nil, fmt.Errorf("cannot get the logs from %v", gvks[0]) } }, PauseObject: func(object runtime.Object) (bool, error) { @@ -553,11 +553,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { _, err := c.Extensions().Deployments(t.Namespace).Update(t) return false, err default: - gvk, err := api.Scheme.ObjectKind(object) + gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return false, err } - return false, fmt.Errorf("cannot pause %v", gvk) + return false, fmt.Errorf("cannot pause %v", gvks[0]) } }, ResumeObject: func(object runtime.Object) (bool, error) { @@ -575,11 +575,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { _, err := c.Extensions().Deployments(t.Namespace).Update(t) return false, err default: - gvk, err := api.Scheme.ObjectKind(object) + gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return false, err } - return false, fmt.Errorf("cannot resume %v", gvk) + return false, fmt.Errorf("cannot resume %v", gvks[0]) } }, Scaler: func(mapping *meta.RESTMapping) (kubectl.Scaler, error) { @@ -712,11 +712,11 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { case *api.Pod: return t, nil default: - gvk, err := api.Scheme.ObjectKind(object) + gvks, _, err := api.Scheme.ObjectKinds(object) if err != nil { return nil, err } - return nil, fmt.Errorf("cannot attach to %v: not implemented", gvk) + return nil, fmt.Errorf("cannot attach to %v: not implemented", gvks[0]) } }, // UpdatePodSpecForObject update the pod specification for the provided object @@ -1101,12 +1101,12 @@ func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig { // PrintObject prints an api object given command line flags to modify the output format func (f *Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error { - gvk, err := api.Scheme.ObjectKind(obj) + gvks, _, err := api.Scheme.ObjectKinds(obj) if err != nil { return err } - mapping, err := mapper.RESTMapping(gvk.GroupKind()) + mapping, err := mapper.RESTMapping(gvks[0].GroupKind()) if err != nil { return err } diff --git a/pkg/kubectl/resource/mapper.go b/pkg/kubectl/resource/mapper.go index 9dece8e94ab..7f9eec28f7c 100644 --- a/pkg/kubectl/resource/mapper.go +++ b/pkg/kubectl/resource/mapper.go @@ -96,7 +96,7 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) { // if the object cannot be introspected. Name and namespace will be set into Info // if the mapping's MetadataAccessor can retrieve them. func (m *Mapper) InfoForObject(obj runtime.Object, preferredGVKs []unversioned.GroupVersionKind) (*Info, error) { - groupVersionKinds, err := m.ObjectKinds(obj) + groupVersionKinds, _, err := m.ObjectKinds(obj) if err != nil { return nil, fmt.Errorf("unable to get type info from the object %q: %v", reflect.TypeOf(obj), err) } diff --git a/pkg/kubectl/resource/result.go b/pkg/kubectl/resource/result.go index f37fcf1c6ae..562fc0cc359 100644 --- a/pkg/kubectl/resource/result.go +++ b/pkg/kubectl/resource/result.go @@ -251,7 +251,7 @@ func AsVersionedObjects(infos []*Info, version unversioned.GroupVersion, encoder // objects that are not part of api.Scheme must be converted to JSON // TODO: convert to map[string]interface{}, attach to runtime.Unknown? if !version.IsEmpty() { - if _, err := api.Scheme.ObjectKind(info.Object); runtime.IsNotRegisteredError(err) { + if _, _, err := api.Scheme.ObjectKinds(info.Object); runtime.IsNotRegisteredError(err) { // TODO: ideally this would encode to version, but we don't expose multiple codecs here. data, err := runtime.Encode(encoder, info.Object) if err != nil { diff --git a/pkg/kubectl/resource_printer.go b/pkg/kubectl/resource_printer.go index ebf3db20413..b97267a4983 100644 --- a/pkg/kubectl/resource_printer.go +++ b/pkg/kubectl/resource_printer.go @@ -69,7 +69,8 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) { printer = &YAMLPrinter{} case "name": printer = &NamePrinter{ - Typer: runtime.ObjectTyperToTyper(api.Scheme), + // TODO: this is wrong, these should be provided as an argument to GetPrinter + Typer: api.Scheme, Decoder: api.Codecs.UniversalDecoder(), } case "template", "go-template": @@ -203,14 +204,12 @@ func (p *VersionedPrinter) HandledResources() []string { // NamePrinter is an implementation of ResourcePrinter which outputs "resource/name" pair of an object. type NamePrinter struct { Decoder runtime.Decoder - Typer runtime.Typer + Typer runtime.ObjectTyper } // PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object // and print "resource/name" pair. If the object is a List, print all items in it. func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error { - gvk, _, _ := p.Typer.ObjectKind(obj) - if meta.IsListType(obj) { items, err := meta.ExtractList(obj) if err != nil { @@ -236,10 +235,9 @@ func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error { } } - if gvk != nil { + if gvks, _, err := p.Typer.ObjectKinds(obj); err == nil { // TODO: this is wrong, it assumes that meta knows about all Kinds - should take a RESTMapper - _, resource := meta.KindToResource(*gvk) - + _, resource := meta.KindToResource(gvks[0]) fmt.Fprintf(w, "%s/%s\n", resource.Resource, name) } else { fmt.Fprintf(w, "/%s\n", name) diff --git a/pkg/kubectl/resource_printer_test.go b/pkg/kubectl/resource_printer_test.go index c6c709085fe..e9e1f1cd74a 100644 --- a/pkg/kubectl/resource_printer_test.go +++ b/pkg/kubectl/resource_printer_test.go @@ -466,7 +466,7 @@ func TestPrinters(t *testing.T) { "template2": templatePrinter2, "jsonpath": jsonpathPrinter, "name": &NamePrinter{ - Typer: runtime.ObjectTyperToTyper(api.Scheme), + Typer: api.Scheme, Decoder: api.Codecs.UniversalDecoder(), }, } diff --git a/pkg/registry/registrytest/etcd.go b/pkg/registry/registrytest/etcd.go index 887eeaa672c..f64b4b3ee43 100644 --- a/pkg/registry/registrytest/etcd.go +++ b/pkg/registry/registrytest/etcd.go @@ -152,10 +152,11 @@ func (t *Tester) TestWatch(valid runtime.Object, labelsPass, labelsFail []labels // ============================================================================= // get codec based on runtime.Object func getCodec(obj runtime.Object) (runtime.Codec, error) { - fqKind, err := api.Scheme.ObjectKind(obj) + fqKinds, _, err := api.Scheme.ObjectKinds(obj) if err != nil { return nil, fmt.Errorf("unexpected encoding error: %v", err) } + fqKind := fqKinds[0] // TODO: caesarxuchao: we should detect which group an object belongs to // by using the version returned by Schem.ObjectVersionAndKind() once we // split the schemes for internal objects. diff --git a/pkg/runtime/codec.go b/pkg/runtime/codec.go index c9dee29123d..e86c0861c73 100644 --- a/pkg/runtime/codec.go +++ b/pkg/runtime/codec.go @@ -78,13 +78,13 @@ func EncodeOrDie(e Encoder, obj Object) string { // UseOrCreateObject returns obj if the canonical ObjectKind returned by the provided typer matches gvk, or // invokes the ObjectCreator to instantiate a new gvk. Returns an error if the typer cannot find the object. -func UseOrCreateObject(t Typer, c ObjectCreater, gvk unversioned.GroupVersionKind, obj Object) (Object, error) { +func UseOrCreateObject(t ObjectTyper, c ObjectCreater, gvk unversioned.GroupVersionKind, obj Object) (Object, error) { if obj != nil { - into, _, err := t.ObjectKind(obj) + into, _, err := t.ObjectKinds(obj) if err != nil { return nil, err } - if gvk == *into { + if gvk == into[0] { return obj, nil } } @@ -116,7 +116,7 @@ func (n NoopDecoder) Decode(data []byte, gvk *unversioned.GroupVersionKind, into // NewParameterCodec creates a ParameterCodec capable of transforming url values into versioned objects and back. func NewParameterCodec(scheme *Scheme) ParameterCodec { return ¶meterCodec{ - typer: ObjectTyperToTyper(scheme), + typer: scheme, convertor: scheme, creator: scheme, } @@ -124,7 +124,7 @@ func NewParameterCodec(scheme *Scheme) ParameterCodec { // parameterCodec implements conversion to and from query parameters and objects. type parameterCodec struct { - typer Typer + typer ObjectTyper convertor ObjectConvertor creator ObjectCreater } @@ -137,10 +137,11 @@ func (c *parameterCodec) DecodeParameters(parameters url.Values, from unversione if len(parameters) == 0 { return nil } - targetGVK, _, err := c.typer.ObjectKind(into) + targetGVKs, _, err := c.typer.ObjectKinds(into) if err != nil { return err } + targetGVK := targetGVKs[0] if targetGVK.GroupVersion() == from { return c.convertor.Convert(¶meters, into) } @@ -157,10 +158,11 @@ func (c *parameterCodec) DecodeParameters(parameters url.Values, from unversione // EncodeParameters converts the provided object into the to version, then converts that object to url.Values. // Returns an error if conversion is not possible. func (c *parameterCodec) EncodeParameters(obj Object, to unversioned.GroupVersion) (url.Values, error) { - gvk, _, err := c.typer.ObjectKind(obj) + gvks, _, err := c.typer.ObjectKinds(obj) if err != nil { return nil, err } + gvk := gvks[0] if to != gvk.GroupVersion() { out, err := c.convertor.ConvertToVersion(obj, to) if err != nil { diff --git a/pkg/runtime/helper.go b/pkg/runtime/helper.go index dca37b8f4eb..3181ddf7e7e 100644 --- a/pkg/runtime/helper.go +++ b/pkg/runtime/helper.go @@ -26,28 +26,6 @@ import ( "k8s.io/kubernetes/pkg/util/errors" ) -type objectTyperToTyper struct { - typer ObjectTyper -} - -func (t objectTyperToTyper) ObjectKind(obj Object) (*unversioned.GroupVersionKind, bool, error) { - gvk, err := t.typer.ObjectKind(obj) - if err != nil { - return nil, false, err - } - unversionedType, ok := t.typer.IsUnversioned(obj) - if !ok { - // ObjectTyper violates its contract - return nil, false, fmt.Errorf("typer returned a kind for %v, but then reported it was not in the scheme with IsUnversioned", reflect.TypeOf(obj)) - } - return &gvk, unversionedType, nil -} - -// ObjectTyperToTyper casts the old typer interface to the new typer interface -func ObjectTyperToTyper(typer ObjectTyper) Typer { - return objectTyperToTyper{typer: typer} -} - // unsafeObjectConvertor implements ObjectConvertor using the unsafe conversion path. type unsafeObjectConvertor struct { *Scheme @@ -195,19 +173,9 @@ type MultiObjectTyper []ObjectTyper var _ ObjectTyper = MultiObjectTyper{} -func (m MultiObjectTyper) ObjectKind(obj Object) (gvk unversioned.GroupVersionKind, err error) { +func (m MultiObjectTyper) ObjectKinds(obj Object) (gvks []unversioned.GroupVersionKind, unversionedType bool, err error) { for _, t := range m { - gvk, err = t.ObjectKind(obj) - if err == nil { - return - } - } - return -} - -func (m MultiObjectTyper) ObjectKinds(obj Object) (gvks []unversioned.GroupVersionKind, err error) { - for _, t := range m { - gvks, err = t.ObjectKinds(obj) + gvks, unversionedType, err = t.ObjectKinds(obj) if err == nil { return } @@ -224,15 +192,6 @@ func (m MultiObjectTyper) Recognizes(gvk unversioned.GroupVersionKind) bool { return false } -func (m MultiObjectTyper) IsUnversioned(obj Object) (bool, bool) { - for _, t := range m { - if unversioned, ok := t.IsUnversioned(obj); ok { - return unversioned, true - } - } - return false, false -} - // SetZeroValue would set the object of objPtr to zero value of its type. func SetZeroValue(objPtr Object) error { v, err := conversion.EnforcePtr(objPtr) diff --git a/pkg/runtime/interfaces.go b/pkg/runtime/interfaces.go index 04f0af29715..0fbc930319c 100644 --- a/pkg/runtime/interfaces.go +++ b/pkg/runtime/interfaces.go @@ -30,15 +30,6 @@ const ( APIVersionInternal = "__internal" ) -// Typer retrieves information about an object's group, version, and kind. -type Typer interface { - // ObjectKind returns the version and kind of the provided object, or an - // error if the object is not recognized (IsNotRegisteredError will return true). - // It returns whether the object is considered unversioned at the same time. - // TODO: align the signature of ObjectTyper with this interface - ObjectKind(Object) (*unversioned.GroupVersionKind, bool, error) -} - type Encoder interface { // EncodeToStream writes an object to a stream. Override versions may be provided for each group // that enforce a certain versioning. Implementations may return errors if the versions are incompatible, @@ -178,20 +169,14 @@ type ObjectConvertor interface { // ObjectTyper contains methods for extracting the APIVersion and Kind // of objects. type ObjectTyper interface { - // ObjectKind returns the default group,version,kind of the provided object, or an - // error if the object is not recognized (IsNotRegisteredError will return true). - ObjectKind(Object) (unversioned.GroupVersionKind, error) - // ObjectKinds returns the all possible group,version,kind of the provided object, or an - // error if the object is not recognized (IsNotRegisteredError will return true). - ObjectKinds(Object) ([]unversioned.GroupVersionKind, error) + // ObjectKinds returns the all possible group,version,kind of the provided object, true if + // the object is unversioned, or an error if the object is not recognized + // (IsNotRegisteredError will return true). + ObjectKinds(Object) ([]unversioned.GroupVersionKind, bool, error) // Recognizes returns true if the scheme is able to handle the provided version and kind, // or more precisely that the provided version is a possible conversion or decoding // target. Recognizes(gvk unversioned.GroupVersionKind) bool - // IsUnversioned returns true if the provided object is considered unversioned and thus - // should have Version and Group suppressed in the output. If the object is not recognized - // in the scheme, ok is false. - IsUnversioned(Object) (unversioned bool, ok bool) } // ObjectCreater contains methods for instantiating an object by kind and version. diff --git a/pkg/runtime/scheme.go b/pkg/runtime/scheme.go index 203d5fa43b7..c49c43e93ff 100644 --- a/pkg/runtime/scheme.go +++ b/pkg/runtime/scheme.go @@ -211,31 +211,32 @@ func (s *Scheme) KnownTypes(gv unversioned.GroupVersion) map[string]reflect.Type return types } -// ObjectKind returns the group,version,kind of the go object, -// or an error if it's not a pointer or is unregistered. -func (s *Scheme) ObjectKind(obj Object) (unversioned.GroupVersionKind, error) { - gvks, err := s.ObjectKinds(obj) +// 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) { + gvks, unversionedType, err := s.ObjectKinds(obj) if err != nil { - return unversioned.GroupVersionKind{}, err + return unversioned.GroupVersionKind{}, false, err } - return gvks[0], nil + return gvks[0], unversionedType, nil } -// ObjectKinds returns all possible group,version,kind of the go object, -// or an error if it's not a pointer or is unregistered. -func (s *Scheme) ObjectKinds(obj Object) ([]unversioned.GroupVersionKind, error) { +// ObjectKinds returns all possible group,version,kind of the go object, true if the +// object is considered unversioned, or an error if it's not a pointer or is unregistered. +func (s *Scheme) ObjectKinds(obj Object) ([]unversioned.GroupVersionKind, bool, error) { v, err := conversion.EnforcePtr(obj) if err != nil { - return nil, err + return nil, false, err } t := v.Type() gvks, ok := s.typeToGVK[t] if !ok { - return nil, ¬RegisteredErr{t: t} + return nil, false, ¬RegisteredErr{t: t} } + _, unversionedType := s.unversionedTypes[t] - return gvks, nil + return gvks, unversionedType, nil } // Recognizes returns true if the scheme is able to handle the provided group,version,kind @@ -439,13 +440,13 @@ func (s *Scheme) Convert(in, out interface{}) error { inVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"} outVersion := unversioned.GroupVersion{Group: "unknown", Version: "unknown"} if inObj, ok := in.(Object); ok { - if gvk, err := s.ObjectKind(inObj); err == nil { - inVersion = gvk.GroupVersion() + if gvks, _, err := s.ObjectKinds(inObj); err == nil { + inVersion = gvks[0].GroupVersion() } } if outObj, ok := out.(Object); ok { - if gvk, err := s.ObjectKind(outObj); err == nil { - outVersion = gvk.GroupVersion() + if gvks, _, err := s.ObjectKinds(outObj); err == nil { + outVersion = gvks[0].GroupVersion() } } flags, meta := s.generateConvertMeta(inVersion, outVersion, in) @@ -504,7 +505,7 @@ func (s *Scheme) ConvertToVersion(in Object, outVersion unversioned.GroupVersion outKind := outVersion.WithKind(kind.Kind) - inKind, err := s.ObjectKind(in) + inKinds, _, err := s.ObjectKinds(in) if err != nil { return nil, err } @@ -514,7 +515,7 @@ func (s *Scheme) ConvertToVersion(in Object, outVersion unversioned.GroupVersion return nil, err } - flags, meta := s.generateConvertMeta(inKind.GroupVersion(), outVersion, in) + flags, meta := s.generateConvertMeta(inKinds[0].GroupVersion(), outVersion, in) if err := s.converter.Convert(in, out, flags, meta); err != nil { return nil, err } @@ -603,8 +604,11 @@ func setTargetVersion(obj Object, raw *Scheme, gv unversioned.GroupVersion) { obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{}) return } - gvk, _ := raw.ObjectKind(obj) - obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: gvk.Kind}) + if gvks, _, _ := raw.ObjectKinds(obj); len(gvks) > 0 { + obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: gvks[0].Kind}) + } else { + obj.GetObjectKind().SetGroupVersionKind(unversioned.GroupVersionKind{Group: gv.Group, Version: gv.Version}) + } } // setTargetKind sets the kind on an object, taking into account whether the target kind is the internal version. diff --git a/pkg/runtime/scheme_test.go b/pkg/runtime/scheme_test.go index 8f922f34a4c..61d6bb1315e 100644 --- a/pkg/runtime/scheme_test.go +++ b/pkg/runtime/scheme_test.go @@ -354,10 +354,11 @@ func TestUnversionedTypes(t *testing.T) { t.Fatalf("type not unversioned and in scheme: %t %t", unv, ok) } - kind, err := scheme.ObjectKind(&InternalSimple{}) + kinds, _, err := scheme.ObjectKinds(&InternalSimple{}) if err != nil { t.Fatal(err) } + kind := kinds[0] if kind != externalGV.WithKind("InternalSimple") { t.Fatalf("unexpected: %#v", kind) } diff --git a/pkg/runtime/serializer/codec_factory.go b/pkg/runtime/serializer/codec_factory.go index 573ec70bb39..96af0cfd354 100644 --- a/pkg/runtime/serializer/codec_factory.go +++ b/pkg/runtime/serializer/codec_factory.go @@ -58,9 +58,9 @@ type serializerType struct { } func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory) []serializerType { - jsonSerializer := json.NewSerializer(mf, scheme, runtime.ObjectTyperToTyper(scheme), false) - jsonPrettySerializer := json.NewSerializer(mf, scheme, runtime.ObjectTyperToTyper(scheme), true) - yamlSerializer := json.NewYAMLSerializer(mf, scheme, runtime.ObjectTyperToTyper(scheme)) + jsonSerializer := json.NewSerializer(mf, scheme, scheme, false) + jsonPrettySerializer := json.NewSerializer(mf, scheme, scheme, true) + yamlSerializer := json.NewYAMLSerializer(mf, scheme, scheme) serializers := []serializerType{ { diff --git a/pkg/runtime/serializer/json/json.go b/pkg/runtime/serializer/json/json.go index 4b9b2b61436..c4a2987a9c7 100644 --- a/pkg/runtime/serializer/json/json.go +++ b/pkg/runtime/serializer/json/json.go @@ -31,7 +31,7 @@ import ( // NewSerializer creates a JSON serializer that handles encoding versioned objects into the proper JSON form. If typer // is not nil, the object has the group, version, and kind fields set. -func NewSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.Typer, pretty bool) *Serializer { +func NewSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, pretty bool) *Serializer { return &Serializer{ meta: meta, creater: creater, @@ -44,7 +44,7 @@ func NewSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtim // NewYAMLSerializer creates a YAML serializer that handles encoding versioned objects into the proper YAML form. If typer // is not nil, the object has the group, version, and kind fields set. This serializer supports only the subset of YAML that // matches JSON, and will error if constructs are used that do not serialize to JSON. -func NewYAMLSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.Typer) *Serializer { +func NewYAMLSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper) *Serializer { return &Serializer{ meta: meta, creater: creater, @@ -56,7 +56,7 @@ func NewYAMLSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer ru type Serializer struct { meta MetaFactory creater runtime.ObjectCreater - typer runtime.Typer + typer runtime.ObjectTyper yaml bool pretty bool } @@ -116,7 +116,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKi } if into != nil { - typed, _, err := s.typer.ObjectKind(into) + types, _, err := s.typer.ObjectKinds(into) switch { case runtime.IsNotRegisteredError(err): if err := codec.NewDecoderBytes(data, new(codec.JsonHandle)).Decode(into); err != nil { @@ -126,6 +126,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKi case err != nil: return nil, actual, err default: + typed := types[0] if len(actual.Kind) == 0 { actual.Kind = typed.Kind } diff --git a/pkg/runtime/serializer/json/json_test.go b/pkg/runtime/serializer/json/json_test.go index 1202f74cad7..8b0fcac67c4 100644 --- a/pkg/runtime/serializer/json/json_test.go +++ b/pkg/runtime/serializer/json/json_test.go @@ -41,7 +41,7 @@ func (d *testDecodable) GroupVersionKind() unversioned.GroupVersionKind { func TestDecode(t *testing.T) { testCases := []struct { creater runtime.ObjectCreater - typer runtime.Typer + typer runtime.ObjectTyper yaml bool pretty bool @@ -260,6 +260,13 @@ type mockTyper struct { err error } -func (t *mockTyper) ObjectKind(obj runtime.Object) (*unversioned.GroupVersionKind, bool, error) { - return t.gvk, false, t.err +func (t *mockTyper) ObjectKinds(obj runtime.Object) ([]unversioned.GroupVersionKind, bool, error) { + if t.gvk == nil { + return nil, false, t.err + } + return []unversioned.GroupVersionKind{*t.gvk}, false, t.err +} + +func (t *mockTyper) Recognizes(_ unversioned.GroupVersionKind) bool { + return false } diff --git a/pkg/runtime/serializer/protobuf/protobuf.go b/pkg/runtime/serializer/protobuf/protobuf.go index 8190809271c..a4e4f2cdd77 100644 --- a/pkg/runtime/serializer/protobuf/protobuf.go +++ b/pkg/runtime/serializer/protobuf/protobuf.go @@ -59,7 +59,7 @@ func IsNotMarshalable(err error) bool { // as-is (any type info passed with the object will be used). // // This encoding scheme is experimental, and is subject to change at any time. -func NewSerializer(creater runtime.ObjectCreater, typer runtime.Typer, defaultContentType string) *Serializer { +func NewSerializer(creater runtime.ObjectCreater, typer runtime.ObjectTyper, defaultContentType string) *Serializer { return &Serializer{ prefix: protoEncodingPrefix, creater: creater, @@ -71,7 +71,7 @@ func NewSerializer(creater runtime.ObjectCreater, typer runtime.Typer, defaultCo type Serializer struct { prefix []byte creater runtime.ObjectCreater - typer runtime.Typer + typer runtime.ObjectTyper contentType string } @@ -131,7 +131,7 @@ func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKi } if into != nil { - typed, _, err := s.typer.ObjectKind(into) + types, _, err := s.typer.ObjectKinds(into) switch { case runtime.IsNotRegisteredError(err): pb, ok := into.(proto.Message) @@ -145,12 +145,12 @@ func (s *Serializer) Decode(originalData []byte, gvk *unversioned.GroupVersionKi case err != nil: return nil, &actual, err default: - copyKindDefaults(&actual, typed) + copyKindDefaults(&actual, &types[0]) // if the result of defaulting did not set a version or group, ensure that at least group is set // (copyKindDefaults will not assign Group if version is already set). This guarantees that the group // of into is set if there is no better information from the caller or object. if len(actual.Version) == 0 && len(actual.Group) == 0 { - actual.Group = typed.Group + actual.Group = types[0].Group } } } @@ -277,7 +277,7 @@ func estimateUnknownSize(unk *runtime.Unknown, byteSize uint64) uint64 { // encoded object, and thus is not self describing (callers must know what type is being described in order to decode). // // This encoding scheme is experimental, and is subject to change at any time. -func NewRawSerializer(creater runtime.ObjectCreater, typer runtime.Typer, defaultContentType string) *RawSerializer { +func NewRawSerializer(creater runtime.ObjectCreater, typer runtime.ObjectTyper, defaultContentType string) *RawSerializer { return &RawSerializer{ creater: creater, typer: typer, @@ -289,7 +289,7 @@ func NewRawSerializer(creater runtime.ObjectCreater, typer runtime.Typer, defaul // type). type RawSerializer struct { creater runtime.ObjectCreater - typer runtime.Typer + typer runtime.ObjectTyper contentType string } @@ -337,7 +337,7 @@ func (s *RawSerializer) Decode(originalData []byte, gvk *unversioned.GroupVersio return intoUnknown, actual, nil } - typed, _, err := s.typer.ObjectKind(into) + types, _, err := s.typer.ObjectKinds(into) switch { case runtime.IsNotRegisteredError(err): pb, ok := into.(proto.Message) @@ -351,12 +351,12 @@ func (s *RawSerializer) Decode(originalData []byte, gvk *unversioned.GroupVersio case err != nil: return nil, actual, err default: - copyKindDefaults(actual, typed) + copyKindDefaults(actual, &types[0]) // if the result of defaulting did not set a version or group, ensure that at least group is set // (copyKindDefaults will not assign Group if version is already set). This guarantees that the group // of into is set if there is no better information from the caller or object. if len(actual.Version) == 0 && len(actual.Group) == 0 { - actual.Group = typed.Group + actual.Group = types[0].Group } } @@ -371,7 +371,7 @@ func (s *RawSerializer) Decode(originalData []byte, gvk *unversioned.GroupVersio } // unmarshalToObject is the common code between decode in the raw and normal serializer. -func unmarshalToObject(typer runtime.Typer, creater runtime.ObjectCreater, actual *unversioned.GroupVersionKind, into runtime.Object, data []byte) (runtime.Object, *unversioned.GroupVersionKind, error) { +func unmarshalToObject(typer runtime.ObjectTyper, creater runtime.ObjectCreater, actual *unversioned.GroupVersionKind, into runtime.Object, data []byte) (runtime.Object, *unversioned.GroupVersionKind, error) { // use the target if necessary obj, err := runtime.UseOrCreateObject(typer, creater, *actual, into) if err != nil { diff --git a/pkg/runtime/serializer/protobuf/protobuf_test.go b/pkg/runtime/serializer/protobuf/protobuf_test.go index 8474f712254..03bf2b25417 100644 --- a/pkg/runtime/serializer/protobuf/protobuf_test.go +++ b/pkg/runtime/serializer/protobuf/protobuf_test.go @@ -323,7 +323,7 @@ func TestDecodeObjects(t *testing.T) { } for i, test := range testCases { - s := protobuf.NewSerializer(api.Scheme, runtime.ObjectTyperToTyper(api.Scheme), "application/protobuf") + s := protobuf.NewSerializer(api.Scheme, api.Scheme, "application/protobuf") obj, err := runtime.Decode(s, test.data) switch { diff --git a/pkg/runtime/serializer/protobuf_extension.go b/pkg/runtime/serializer/protobuf_extension.go index 2c95e01f9ce..a93708c45d0 100644 --- a/pkg/runtime/serializer/protobuf_extension.go +++ b/pkg/runtime/serializer/protobuf_extension.go @@ -31,8 +31,8 @@ const ( ) func protobufSerializer(scheme *runtime.Scheme) (serializerType, bool) { - serializer := protobuf.NewSerializer(scheme, runtime.ObjectTyperToTyper(scheme), contentTypeProtobuf) - raw := protobuf.NewRawSerializer(scheme, runtime.ObjectTyperToTyper(scheme), contentTypeProtobuf) + serializer := protobuf.NewSerializer(scheme, scheme, contentTypeProtobuf) + raw := protobuf.NewRawSerializer(scheme, scheme, contentTypeProtobuf) return serializerType{ AcceptContentTypes: []string{contentTypeProtobuf}, ContentType: contentTypeProtobuf, diff --git a/pkg/runtime/serializer/recognizer/recognizer_test.go b/pkg/runtime/serializer/recognizer/recognizer_test.go index c83b87aa4dd..9998f942ccf 100644 --- a/pkg/runtime/serializer/recognizer/recognizer_test.go +++ b/pkg/runtime/serializer/recognizer/recognizer_test.go @@ -32,8 +32,8 @@ func TestRecognizer(t *testing.T) { s := runtime.NewScheme() s.AddKnownTypes(unversioned.GroupVersion{Version: "v1"}, &A{}) d := NewDecoder( - json.NewSerializer(json.DefaultMetaFactory, s, runtime.ObjectTyperToTyper(s), false), - json.NewYAMLSerializer(json.DefaultMetaFactory, s, runtime.ObjectTyperToTyper(s)), + json.NewSerializer(json.DefaultMetaFactory, s, s, false), + json.NewYAMLSerializer(json.DefaultMetaFactory, s, s), ) out, _, err := d.Decode([]byte(` kind: A diff --git a/pkg/runtime/serializer/versioning/versioning.go b/pkg/runtime/serializer/versioning/versioning.go index 389c31e4dd3..d7c5cbe9db7 100644 --- a/pkg/runtime/serializer/versioning/versioning.go +++ b/pkg/runtime/serializer/versioning/versioning.go @@ -71,7 +71,7 @@ func NewCodecForScheme( encodeVersion []unversioned.GroupVersion, decodeVersion []unversioned.GroupVersion, ) runtime.Codec { - return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, runtime.ObjectTyperToTyper(scheme), encodeVersion, decodeVersion) + return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, scheme, encodeVersion, decodeVersion) } // NewCodec takes objects in their internal versions and converts them to external versions before @@ -83,7 +83,7 @@ func NewCodec( convertor runtime.ObjectConvertor, creater runtime.ObjectCreater, copier runtime.ObjectCopier, - typer runtime.Typer, + typer runtime.ObjectTyper, encodeVersion []unversioned.GroupVersion, decodeVersion []unversioned.GroupVersion, ) runtime.Codec { @@ -104,6 +104,11 @@ func NewCodec( } internal.encodeVersion[v.Group] = v } + if len(internal.encodeVersion) == 1 { + for _, v := range internal.encodeVersion { + internal.preferredEncodeVersion = []unversioned.GroupVersion{v} + } + } } if decodeVersion != nil { internal.decodeVersion = make(map[string]unversioned.GroupVersion) @@ -125,10 +130,12 @@ type codec struct { convertor runtime.ObjectConvertor creater runtime.ObjectCreater copier runtime.ObjectCopier - typer runtime.Typer + typer runtime.ObjectTyper encodeVersion map[string]unversioned.GroupVersion decodeVersion map[string]unversioned.GroupVersion + + preferredEncodeVersion []unversioned.GroupVersion } // Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is @@ -221,15 +228,16 @@ func (c *codec) EncodeToStream(obj runtime.Object, w io.Writer, overrides ...unv if _, ok := obj.(*runtime.Unknown); ok { return c.encoder.EncodeToStream(obj, w, overrides...) } - gvk, isUnversioned, err := c.typer.ObjectKind(obj) + gvks, isUnversioned, err := c.typer.ObjectKinds(obj) if err != nil { return err } + gvk := gvks[0] if (c.encodeVersion == nil && len(overrides) == 0) || isUnversioned { objectKind := obj.GetObjectKind() old := objectKind.GroupVersionKind() - objectKind.SetGroupVersionKind(*gvk) + objectKind.SetGroupVersionKind(gvk) err = c.encoder.EncodeToStream(obj, w, overrides...) objectKind.SetGroupVersionKind(old) return err @@ -248,13 +256,16 @@ func (c *codec) EncodeToStream(obj runtime.Object, w io.Writer, overrides ...unv } // attempt a conversion to the sole encode version - if !ok && len(c.encodeVersion) == 1 { + if !ok && c.preferredEncodeVersion != nil { ok = true - for _, v := range c.encodeVersion { - targetGV = v + targetGV = c.preferredEncodeVersion[0] + if len(overrides) > 0 { + // ensure the target override is first + overrides = promoteOrPrependGroupVersion(targetGV, overrides) + } else { + // avoids allocating a new array for each call to EncodeToVersion + overrides = c.preferredEncodeVersion } - // ensure the target override is first - overrides = promoteOrPrependGroupVersion(targetGV, overrides) } // if no fallback is available, error diff --git a/pkg/runtime/serializer/versioning/versioning_test.go b/pkg/runtime/serializer/versioning/versioning_test.go index 4bb6469bf29..5ca6c2a9afc 100644 --- a/pkg/runtime/serializer/versioning/versioning_test.go +++ b/pkg/runtime/serializer/versioning/versioning_test.go @@ -49,7 +49,7 @@ func TestDecode(t *testing.T) { convertor runtime.ObjectConvertor creater runtime.ObjectCreater copier runtime.ObjectCopier - typer runtime.Typer + typer runtime.ObjectTyper yaml bool pretty bool diff --git a/pkg/util/framer/framer.go b/pkg/util/framer/framer.go index 615d07de1cc..7ca806fa054 100644 --- a/pkg/util/framer/framer.go +++ b/pkg/util/framer/framer.go @@ -25,6 +25,7 @@ import ( type lengthDelimitedFrameWriter struct { w io.Writer + h [4]byte } func NewLengthDelimitedFrameWriter(w io.Writer) io.Writer { @@ -34,13 +35,12 @@ func NewLengthDelimitedFrameWriter(w io.Writer) io.Writer { // Write writes a single frame to the nested writer, prepending it with the length in // in bytes of data (as a 4 byte, bigendian uint32). func (w *lengthDelimitedFrameWriter) Write(data []byte) (int, error) { - header := [4]byte{} - binary.BigEndian.PutUint32(header[:], uint32(len(data))) - n, err := w.w.Write(header[:]) + binary.BigEndian.PutUint32(w.h[:], uint32(len(data))) + n, err := w.w.Write(w.h[:]) if err != nil { return 0, err } - if n != len(header) { + if n != len(w.h) { return 0, io.ErrShortWrite } return w.w.Write(data) diff --git a/plugin/pkg/scheduler/api/latest/latest.go b/plugin/pkg/scheduler/api/latest/latest.go index 44ec05858f5..e28ae26b9fc 100644 --- a/plugin/pkg/scheduler/api/latest/latest.go +++ b/plugin/pkg/scheduler/api/latest/latest.go @@ -42,7 +42,7 @@ var Versions = []string{"v1"} var Codec runtime.Codec func init() { - jsonSerializer := json.NewSerializer(json.DefaultMetaFactory, api.Scheme, runtime.ObjectTyperToTyper(api.Scheme), true) + jsonSerializer := json.NewSerializer(json.DefaultMetaFactory, api.Scheme, api.Scheme, true) Codec = versioning.NewCodecForScheme( api.Scheme, jsonSerializer, diff --git a/test/integration/framework/serializer.go b/test/integration/framework/serializer.go index 5d6d0e77a1b..c958999cd8c 100644 --- a/test/integration/framework/serializer.go +++ b/test/integration/framework/serializer.go @@ -59,9 +59,9 @@ func (s *wrappedSerializer) UniversalDeserializer() runtime.Decoder { } func (s *wrappedSerializer) EncoderForVersion(encoder runtime.Encoder, gv unversioned.GroupVersion) runtime.Encoder { - return versioning.NewCodec(encoder, nil, s.scheme, s.scheme, s.scheme, runtime.ObjectTyperToTyper(s.scheme), []unversioned.GroupVersion{gv}, nil) + return versioning.NewCodec(encoder, nil, s.scheme, s.scheme, s.scheme, s.scheme, []unversioned.GroupVersion{gv}, nil) } func (s *wrappedSerializer) DecoderToVersion(decoder runtime.Decoder, gv unversioned.GroupVersion) runtime.Decoder { - return versioning.NewCodec(nil, decoder, s.scheme, s.scheme, s.scheme, runtime.ObjectTyperToTyper(s.scheme), nil, []unversioned.GroupVersion{gv}) + return versioning.NewCodec(nil, decoder, s.scheme, s.scheme, s.scheme, s.scheme, nil, []unversioned.GroupVersion{gv}) }