Break kubectl from assuming details of codecs

Most of the logic related to type and kind retrieval belongs in the
codec, not in the various classes. Make it explicit that the codec
should handle these details.

Factory now returns a universal Decoder and a JSONEncoder to assist code
in kubectl that needs to specifically deal with JSON serialization
(apply, merge, patch, edit, jsonpath). Add comments to indicate the
serialization is explicit in those places. These methods decode to
internal and encode to the preferred API version as previous, although
in the future they may be changed.

React to removing Codec from version interfaces and RESTMapping by
passing it in to all the places that it is needed.
pull/6/head
Clayton Coleman 2015-12-21 00:37:49 -05:00
parent 181dc7c64c
commit 2fd38a7dc0
40 changed files with 245 additions and 232 deletions

View File

@ -21,6 +21,7 @@ import (
"k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
) )
type debugError interface { type debugError interface {
@ -80,7 +81,7 @@ func SetOriginalConfiguration(info *resource.Info, original []byte) error {
// If annotate is true, it embeds the result as an anotation in the modified // If annotate is true, it embeds the result as an anotation in the modified
// configuration. If an object was read from the command input, it will use that // configuration. If an object was read from the command input, it will use that
// version of the object. Otherwise, it will use the version from the server. // version of the object. Otherwise, it will use the version from the server.
func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error) { func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.Encoder) ([]byte, error) {
// First serialize the object without the annotation to prevent recursion, // First serialize the object without the annotation to prevent recursion,
// then add that serialization to it as the annotation and serialize it again. // then add that serialization to it as the annotation and serialize it again.
var modified []byte var modified []byte
@ -100,6 +101,8 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error
original := annotations[LastAppliedConfigAnnotation] original := annotations[LastAppliedConfigAnnotation]
delete(annotations, LastAppliedConfigAnnotation) delete(annotations, LastAppliedConfigAnnotation)
accessor.SetAnnotations(annotations) accessor.SetAnnotations(annotations)
// TODO: this needs to be abstracted - there should be no assumption that versioned object
// can be marshalled to JSON.
modified, err = json.Marshal(info.VersionedObject) modified, err = json.Marshal(info.VersionedObject)
if err != nil { if err != nil {
return nil, err return nil, err
@ -108,6 +111,8 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error
if annotate { if annotate {
annotations[LastAppliedConfigAnnotation] = string(modified) annotations[LastAppliedConfigAnnotation] = string(modified)
accessor.SetAnnotations(annotations) accessor.SetAnnotations(annotations)
// TODO: this needs to be abstracted - there should be no assumption that versioned object
// can be marshalled to JSON.
modified, err = json.Marshal(info.VersionedObject) modified, err = json.Marshal(info.VersionedObject)
if err != nil { if err != nil {
return nil, err return nil, err
@ -136,7 +141,7 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error
return nil, err return nil, err
} }
modified, err = info.Mapping.Codec.Encode(info.Object) modified, err = runtime.Encode(codec, info.Object)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -147,7 +152,7 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error
return nil, err return nil, err
} }
modified, err = info.Mapping.Codec.Encode(info.Object) modified, err = runtime.Encode(codec, info.Object)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -165,17 +170,17 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool) ([]byte, error
// UpdateApplyAnnotation calls CreateApplyAnnotation if the last applied // UpdateApplyAnnotation calls CreateApplyAnnotation if the last applied
// configuration annotation is already present. Otherwise, it does nothing. // configuration annotation is already present. Otherwise, it does nothing.
func UpdateApplyAnnotation(info *resource.Info) error { func UpdateApplyAnnotation(info *resource.Info, codec runtime.Encoder) error {
if original, err := GetOriginalConfiguration(info); err != nil || len(original) <= 0 { if original, err := GetOriginalConfiguration(info); err != nil || len(original) <= 0 {
return err return err
} }
return CreateApplyAnnotation(info) return CreateApplyAnnotation(info, codec)
} }
// CreateApplyAnnotation gets the modified configuration of the object, // CreateApplyAnnotation gets the modified configuration of the object,
// without embedding it again, and then sets it on the object as the annotation. // without embedding it again, and then sets it on the object as the annotation.
func CreateApplyAnnotation(info *resource.Info) error { func CreateApplyAnnotation(info *resource.Info, codec runtime.Encoder) error {
modified, err := GetModifiedConfiguration(info, false) modified, err := GetModifiedConfiguration(info, false, codec)
if err != nil { if err != nil {
return err return err
} }
@ -184,9 +189,9 @@ func CreateApplyAnnotation(info *resource.Info) error {
// Create the annotation used by kubectl apply only when createAnnotation is true // Create the annotation used by kubectl apply only when createAnnotation is true
// Otherwise, only update the annotation when it already exists // Otherwise, only update the annotation when it already exists
func CreateOrUpdateAnnotation(createAnnotation bool, info *resource.Info) error { func CreateOrUpdateAnnotation(createAnnotation bool, info *resource.Info, codec runtime.Encoder) error {
if createAnnotation { if createAnnotation {
return CreateApplyAnnotation(info) return CreateApplyAnnotation(info, codec)
} }
return UpdateApplyAnnotation(info) return UpdateApplyAnnotation(info, codec)
} }

View File

@ -151,7 +151,7 @@ func (o *AnnotateOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra
} }
mapper, typer := f.Object() mapper, typer := f.Object()
o.builder = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). o.builder = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError(). ContinueOnError().
NamespaceParam(namespace).DefaultNamespace(). NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, o.filenames...). FilenameParam(enforceNamespace, o.filenames...).
@ -211,7 +211,7 @@ func (o AnnotateOptions) RunAnnotate() error {
} }
mapping := info.ResourceMapping() mapping := info.ResourceMapping()
client, err := o.f.RESTClient(mapping) client, err := o.f.ClientForMapping(mapping)
if err != nil { if err != nil {
return err return err
} }

View File

@ -27,6 +27,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/strategicpatch" "k8s.io/kubernetes/pkg/util/strategicpatch"
) )
@ -92,7 +93,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap
} }
mapper, typer := f.Object() mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
Schema(schema). Schema(schema).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
@ -104,6 +105,8 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap
return err return err
} }
encoder := f.JSONEncoder()
count := 0 count := 0
err = r.Visit(func(info *resource.Info, err error) error { err = r.Visit(func(info *resource.Info, err error) error {
// In this method, info.Object contains the object retrieved from the server // In this method, info.Object contains the object retrieved from the server
@ -115,7 +118,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap
// Get the modified configuration of the object. Embed the result // Get the modified configuration of the object. Embed the result
// as an annotation in the modified configuration, so that it will appear // as an annotation in the modified configuration, so that it will appear
// in the patch sent to the server. // in the patch sent to the server.
modified, err := kubectl.GetModifiedConfiguration(info, true) modified, err := kubectl.GetModifiedConfiguration(info, true, encoder)
if err != nil { if err != nil {
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%v\nfor:", info), info.Source, err) return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving modified configuration from:\n%v\nfor:", info), info.Source, err)
} }
@ -126,7 +129,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap
} }
// Create the resource if it doesn't exist // Create the resource if it doesn't exist
// First, update the annotation used by kubectl apply // First, update the annotation used by kubectl apply
if err := kubectl.CreateApplyAnnotation(info); err != nil { if err := kubectl.CreateApplyAnnotation(info, encoder); err != nil {
return cmdutil.AddSourceToErr("creating", info.Source, err) return cmdutil.AddSourceToErr("creating", info.Source, err)
} }
// Then create the resource and skip the three-way merge // Then create the resource and skip the three-way merge
@ -139,7 +142,7 @@ func RunApply(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *Ap
} }
// Serialize the current configuration of the object from the server. // Serialize the current configuration of the object from the server.
current, err := info.Mapping.Codec.Encode(info.Object) current, err := runtime.Encode(encoder, info.Object)
if err != nil { if err != nil {
return cmdutil.AddSourceToErr(fmt.Sprintf("serializing current configuration from:\n%v\nfor:", info), info.Source, err) return cmdutil.AddSourceToErr(fmt.Sprintf("serializing current configuration from:\n%v\nfor:", info), info.Source, err)
} }

View File

@ -79,7 +79,7 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
} }
mapper, typer := f.Object() mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError(). ContinueOnError().
NamespaceParam(namespace).DefaultNamespace(). NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, filenames...). FilenameParam(enforceNamespace, filenames...).
@ -129,7 +129,12 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
return err return err
} }
resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} resourceMapper := &resource.Mapper{
ObjectTyper: typer,
RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
Decoder: f.Decoder(true),
}
hpa, err := resourceMapper.InfoForObject(object) hpa, err := resourceMapper.InfoForObject(object)
if err != nil { if err != nil {
return err return err
@ -139,7 +144,7 @@ func RunAutoscale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
return f.PrintObject(cmd, object, out) return f.PrintObject(cmd, object, out)
} }
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa); err != nil { if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa, f.JSONEncoder()); err != nil {
return err return err
} }

View File

@ -45,25 +45,25 @@ func NewCmdClusterInfo(f *cmdutil.Factory, out io.Writer) *cobra.Command {
return cmd return cmd
} }
func RunClusterInfo(factory *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error { func RunClusterInfo(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error {
if len(os.Args) > 1 && os.Args[1] == "clusterinfo" { if len(os.Args) > 1 && os.Args[1] == "clusterinfo" {
printDeprecationWarning("cluster-info", "clusterinfo") printDeprecationWarning("cluster-info", "clusterinfo")
} }
client, err := factory.ClientConfig() client, err := f.ClientConfig()
if err != nil { if err != nil {
return err return err
} }
printService(out, "Kubernetes master", client.Host) printService(out, "Kubernetes master", client.Host)
mapper, typer := factory.Object() mapper, typer := f.Object()
cmdNamespace := cmdutil.GetFlagString(cmd, "namespace") cmdNamespace := cmdutil.GetFlagString(cmd, "namespace")
if cmdNamespace == "" { if cmdNamespace == "" {
cmdNamespace = api.NamespaceSystem cmdNamespace = api.NamespaceSystem
} }
// TODO use generalized labels once they are implemented (#341) // TODO use generalized labels once they are implemented (#341)
b := resource.NewBuilder(mapper, typer, factory.ClientMapperForCommand()). b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
SelectorParam("kubernetes.io/cluster-service=true"). SelectorParam("kubernetes.io/cluster-service=true").
ResourceTypeOrNameArgs(false, []string{"services"}...). ResourceTypeOrNameArgs(false, []string{"services"}...).

View File

@ -38,6 +38,7 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/serializer"
"k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util"
) )
@ -100,22 +101,21 @@ func versionErrIfFalse(b bool) error {
} }
var validVersion = testapi.Default.GroupVersion().Version var validVersion = testapi.Default.GroupVersion().Version
var internalGV = unversioned.GroupVersion{Group: "apitest", Version: ""} var internalGV = unversioned.GroupVersion{Group: "apitest", Version: runtime.APIVersionInternal}
var unlikelyGV = unversioned.GroupVersion{Group: "apitest", Version: "unlikelyversion"} var unlikelyGV = unversioned.GroupVersion{Group: "apitest", Version: "unlikelyversion"}
var validVersionGV = unversioned.GroupVersion{Group: "apitest", Version: validVersion} var validVersionGV = unversioned.GroupVersion{Group: "apitest", Version: validVersion}
func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) { func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
scheme := runtime.NewScheme() scheme := runtime.NewScheme()
scheme.AddInternalGroupVersion(internalGV)
scheme.AddKnownTypeWithName(internalGV.WithKind("Type"), &internalType{}) scheme.AddKnownTypeWithName(internalGV.WithKind("Type"), &internalType{})
scheme.AddKnownTypeWithName(unlikelyGV.WithKind("Type"), &externalType{}) scheme.AddKnownTypeWithName(unlikelyGV.WithKind("Type"), &externalType{})
//This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name. //This tests that kubectl will not confuse the external scheme with the internal scheme, even when they accidentally have versions of the same name.
scheme.AddKnownTypeWithName(validVersionGV.WithKind("Type"), &ExternalType2{}) scheme.AddKnownTypeWithName(validVersionGV.WithKind("Type"), &ExternalType2{})
codec := runtime.CodecFor(scheme, unlikelyGV) codecs := serializer.NewCodecFactory(scheme)
codec := codecs.LegacyCodec(unlikelyGV)
mapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{unlikelyGV, validVersionGV}, func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) { mapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{unlikelyGV, validVersionGV}, func(version unversioned.GroupVersion) (*meta.VersionInterfaces, error) {
return &meta.VersionInterfaces{ return &meta.VersionInterfaces{
Codec: runtime.CodecFor(scheme, version),
ObjectConvertor: scheme, ObjectConvertor: scheme,
MetadataAccessor: meta.NewAccessor(), MetadataAccessor: meta.NewAccessor(),
}, versionErrIfFalse(version == validVersionGV || version == unlikelyGV) }, versionErrIfFalse(version == validVersionGV || version == unlikelyGV)
@ -183,9 +183,15 @@ func NewTestFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) {
Object: func() (meta.RESTMapper, runtime.ObjectTyper) { Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
return t.Mapper, t.Typer return t.Mapper, t.Typer
}, },
RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) { ClientForMapping: func(*meta.RESTMapping) (resource.RESTClient, error) {
return t.Client, t.Err return t.Client, t.Err
}, },
Decoder: func(bool) runtime.Decoder {
return codec
},
JSONEncoder: func() runtime.Encoder {
return codec
},
Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { Describer: func(*meta.RESTMapping) (kubectl.Describer, error) {
return t.Describer, t.Err return t.Describer, t.Err
}, },
@ -209,7 +215,7 @@ func NewMixedFactory(apiClient resource.RESTClient) (*cmdutil.Factory, *testFact
f.Object = func() (meta.RESTMapper, runtime.ObjectTyper) { f.Object = func() (meta.RESTMapper, runtime.ObjectTyper) {
return meta.MultiRESTMapper{t.Mapper, testapi.Default.RESTMapper()}, runtime.MultiObjectTyper{t.Typer, api.Scheme} return meta.MultiRESTMapper{t.Mapper, testapi.Default.RESTMapper()}, runtime.MultiObjectTyper{t.Typer, api.Scheme}
} }
f.RESTClient = func(m *meta.RESTMapping) (resource.RESTClient, error) { f.ClientForMapping = func(m *meta.RESTMapping) (resource.RESTClient, error) {
if m.ObjectConvertor == api.Scheme { if m.ObjectConvertor == api.Scheme {
return apiClient, t.Err return apiClient, t.Err
} }
@ -235,9 +241,15 @@ func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) {
c.ExtensionsClient.Client = fakeClient.Client c.ExtensionsClient.Client = fakeClient.Client
return c, t.Err return c, t.Err
}, },
RESTClient: func(*meta.RESTMapping) (resource.RESTClient, error) { ClientForMapping: func(*meta.RESTMapping) (resource.RESTClient, error) {
return t.Client, t.Err return t.Client, t.Err
}, },
Decoder: func(bool) runtime.Decoder {
return testapi.Default.Codec()
},
JSONEncoder: func() runtime.Encoder {
return testapi.Default.Codec()
},
Describer: func(*meta.RESTMapping) (kubectl.Describer, error) { Describer: func(*meta.RESTMapping) (kubectl.Describer, error) {
return t.Describer, t.Err return t.Describer, t.Err
}, },
@ -303,7 +315,7 @@ func stringBody(body string) io.ReadCloser {
// mapping := &meta.RESTMapping{ // mapping := &meta.RESTMapping{
// APIVersion: version, // APIVersion: version,
// } // }
// c, err := f.RESTClient(mapping) // c, err := f.ClientForMapping(mapping)
// if err != nil { // if err != nil {
// t.Errorf("unexpected error: %v", err) // t.Errorf("unexpected error: %v", err)
// } // }

View File

@ -26,6 +26,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -87,6 +88,7 @@ type ConvertOptions struct {
filenames []string filenames []string
local bool local bool
encoder runtime.Encoder
out io.Writer out io.Writer
printer kubectl.ResourcePrinter printer kubectl.ResourcePrinter
@ -105,11 +107,12 @@ func (o *ConvertOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra.
// build the builder // build the builder
mapper, typer := f.Object() mapper, typer := f.Object()
clientMapper := resource.ClientMapperFunc(f.ClientForMapping)
if o.local { if o.local {
fmt.Fprintln(out, "running in local mode...") fmt.Fprintln(out, "running in local mode...")
o.builder = resource.NewBuilder(mapper, typer, f.NilClientMapperForCommand()) o.builder = resource.NewBuilder(mapper, typer, resource.DisabledClientForMapping{clientMapper}, f.Decoder(true))
} else { } else {
o.builder = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()) o.builder = resource.NewBuilder(mapper, typer, clientMapper, f.Decoder(true))
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir")) schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"), cmdutil.GetFlagString(cmd, "schema-cache-dir"))
if err != nil { if err != nil {
return err return err
@ -136,6 +139,7 @@ func (o *ConvertOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra.
outputFormat = "template" outputFormat = "template"
} }
} }
o.encoder = f.JSONEncoder()
o.printer, _, err = kubectl.GetPrinter(outputFormat, templateFile) o.printer, _, err = kubectl.GetPrinter(outputFormat, templateFile)
if err != nil { if err != nil {
return err return err
@ -151,7 +155,7 @@ func (o *ConvertOptions) RunConvert() error {
return err return err
} }
objects, err := resource.AsVersionedObject(infos, false, o.outputVersion.String()) objects, err := resource.AsVersionedObject(infos, false, o.outputVersion.String(), o.encoder)
if err != nil { if err != nil {
return err return err
} }

View File

@ -99,7 +99,7 @@ func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *C
} }
mapper, typer := f.Object() mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
Schema(schema). Schema(schema).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
@ -116,7 +116,7 @@ func RunCreate(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer, options *C
if err != nil { if err != nil {
return err return err
} }
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
return cmdutil.AddSourceToErr("creating", info.Source, err) return cmdutil.AddSourceToErr("creating", info.Source, err)
} }
@ -218,16 +218,20 @@ func RunCreateSubcommand(f *cmdutil.Factory, cmd *cobra.Command, out io.Writer,
if err != nil { if err != nil {
return err return err
} }
client, err := f.RESTClient(mapping) client, err := f.ClientForMapping(mapping)
if err != nil { if err != nil {
return err return err
} }
resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} resourceMapper := &resource.Mapper{
ObjectTyper: typer,
RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
}
info, err := resourceMapper.InfoForObject(obj) info, err := resourceMapper.InfoForObject(obj)
if err != nil { if err != nil {
return err return err
} }
if err := kubectl.UpdateApplyAnnotation(info); err != nil { if err := kubectl.UpdateApplyAnnotation(info, f.JSONEncoder()); err != nil {
return err return err
} }
if !options.DryRun { if !options.DryRun {

View File

@ -108,7 +108,7 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
} }
deleteAll := cmdutil.GetFlagBool(cmd, "all") deleteAll := cmdutil.GetFlagBool(cmd, "all")
mapper, typer := f.Object() mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options.Filenames...). FilenameParam(enforceNamespace, options.Filenames...).

View File

@ -106,7 +106,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
} }
mapper, typer := f.Object() mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options.Filenames...). FilenameParam(enforceNamespace, options.Filenames...).
@ -147,7 +147,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
} }
func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f *cmdutil.Factory, namespace, rsrc, prefix string, out io.Writer, originalError error) error { func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f *cmdutil.Factory, namespace, rsrc, prefix string, out io.Writer, originalError error) error {
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
NamespaceParam(namespace).DefaultNamespace(). NamespaceParam(namespace).DefaultNamespace().
ResourceTypeOrNameArgs(true, rsrc). ResourceTypeOrNameArgs(true, rsrc).
SingleResourceType(). SingleResourceType().

View File

@ -209,8 +209,7 @@ func (o *DrainOptions) getPodsForDeletion() ([]api.Pod, error) {
if found { if found {
// Now verify that the specified creator actually exists. // Now verify that the specified creator actually exists.
var sr api.SerializedReference var sr api.SerializedReference
err := api.Scheme.DecodeInto([]byte(creatorRef), &sr) if err := runtime.DecodeInto(o.factory.Decoder(true), []byte(creatorRef), &sr); err != nil {
if err != nil {
return pods, err return pods, err
} }
if sr.Reference.Kind == "ReplicationController" { if sr.Reference.Kind == "ReplicationController" {

View File

@ -23,7 +23,7 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"runtime" gruntime "runtime"
"strings" "strings"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor" "k8s.io/kubernetes/pkg/kubectl/cmd/util/editor"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge" "k8s.io/kubernetes/pkg/kubectl/cmd/util/jsonmerge"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util"
"k8s.io/kubernetes/pkg/util/strategicpatch" "k8s.io/kubernetes/pkg/util/strategicpatch"
"k8s.io/kubernetes/pkg/util/yaml" "k8s.io/kubernetes/pkg/util/yaml"
@ -94,7 +95,7 @@ func NewCmdEdit(f *cmdutil.Factory, out io.Writer) *cobra.Command {
kubectl.AddJsonFilenameFlag(cmd, &filenames, usage) kubectl.AddJsonFilenameFlag(cmd, &filenames, usage)
cmd.Flags().StringP("output", "o", "yaml", "Output format. One of: yaml|json.") cmd.Flags().StringP("output", "o", "yaml", "Output format. One of: yaml|json.")
cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version).") cmd.Flags().String("output-version", "", "Output the formatted object with the given version (default api-version).")
cmd.Flags().Bool("windows-line-endings", runtime.GOOS == "windows", "Use Windows line-endings (default Unix line-endings)") cmd.Flags().Bool("windows-line-endings", gruntime.GOOS == "windows", "Use Windows line-endings (default Unix line-endings)")
cmdutil.AddApplyAnnotationFlags(cmd) cmdutil.AddApplyAnnotationFlags(cmd)
return cmd return cmd
} }
@ -119,13 +120,14 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin
} }
mapper, typer := f.Object() mapper, typer := f.Object()
rmap := &resource.Mapper{ resourceMapper := &resource.Mapper{
ObjectTyper: typer, ObjectTyper: typer,
RESTMapper: mapper, RESTMapper: mapper,
ClientMapper: f.ClientMapperForCommand(), ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
Decoder: f.Decoder(true),
} }
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, filenames...). FilenameParam(enforceNamespace, filenames...).
ResourceTypeOrNameArgs(true, args...). ResourceTypeOrNameArgs(true, args...).
@ -147,6 +149,8 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin
return err return err
} }
encoder := f.JSONEncoder()
windowsLineEndings := cmdutil.GetFlagBool(cmd, "windows-line-endings") windowsLineEndings := cmdutil.GetFlagBool(cmd, "windows-line-endings")
edit := editor.NewDefaultEditor(f.EditorEnvs()) edit := editor.NewDefaultEditor(f.EditorEnvs())
defaultVersion, err := cmdutil.OutputVersion(cmd, clientConfig.GroupVersion) defaultVersion, err := cmdutil.OutputVersion(cmd, clientConfig.GroupVersion)
@ -155,7 +159,7 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin
} }
results := editResults{} results := editResults{}
for { for {
objs, err := resource.AsVersionedObjects(infos, defaultVersion.String()) objs, err := resource.AsVersionedObjects(infos, defaultVersion.String(), encoder)
if err != nil { if err != nil {
return preservedFile(err, results.file, out) return preservedFile(err, results.file, out)
} }
@ -219,21 +223,21 @@ func RunEdit(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin
} }
// parse the edited file // parse the edited file
updates, err := rmap.InfoForData(edited, "edited-file") updates, err := resourceMapper.InfoForData(edited, "edited-file")
if err != nil { if err != nil {
return fmt.Errorf("The edited file had a syntax error: %v", err) return fmt.Errorf("The edited file had a syntax error: %v", err)
} }
// put configuration annotation in "updates" // put configuration annotation in "updates"
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), updates); err != nil { if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), updates, encoder); err != nil {
return preservedFile(err, file, out) return preservedFile(err, file, out)
} }
// encode updates back to "edited" since we'll only generate patch from "edited" // encode updates back to "edited" since we'll only generate patch from "edited"
if edited, err = updates.Mapping.Codec.Encode(updates.Object); err != nil { if edited, err = runtime.Encode(encoder, updates.Object); err != nil {
return preservedFile(err, file, out) return preservedFile(err, file, out)
} }
visitor := resource.NewFlattenListVisitor(updates, rmap) visitor := resource.NewFlattenListVisitor(updates, resourceMapper)
// need to make sure the original namespace wasn't changed while editing // need to make sure the original namespace wasn't changed while editing
if err = visitor.Visit(resource.RequireNamespace(cmdNamespace)); err != nil { if err = visitor.Visit(resource.RequireNamespace(cmdNamespace)); err != nil {

View File

@ -26,6 +26,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/validation" "k8s.io/kubernetes/pkg/util/validation"
) )
@ -104,7 +105,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
} }
mapper, typer := f.Object() mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError(). ContinueOnError().
NamespaceParam(namespace).DefaultNamespace(). NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, options.Filenames...). FilenameParam(enforceNamespace, options.Filenames...).
@ -192,13 +193,19 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
} }
if inline := cmdutil.GetFlagString(cmd, "overrides"); len(inline) > 0 { if inline := cmdutil.GetFlagString(cmd, "overrides"); len(inline) > 0 {
object, err = cmdutil.Merge(object, inline, mapping.GroupVersionKind.Kind) codec := runtime.NewCodec(f.JSONEncoder(), f.Decoder(true))
object, err = cmdutil.Merge(codec, object, inline, mapping.GroupVersionKind.Kind)
if err != nil { if err != nil {
return err return err
} }
} }
resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} resourceMapper := &resource.Mapper{
ObjectTyper: typer,
RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
Decoder: f.Decoder(true),
}
info, err = resourceMapper.InfoForObject(object) info, err = resourceMapper.InfoForObject(object)
if err != nil { if err != nil {
return err return err
@ -207,7 +214,7 @@ func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
if cmdutil.GetFlagBool(cmd, "dry-run") { if cmdutil.GetFlagBool(cmd, "dry-run") {
return f.PrintObject(cmd, object, out) return f.PrintObject(cmd, object, out)
} }
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
return err return err
} }

View File

@ -137,7 +137,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
// handle watch separately since we cannot watch multiple resource types // handle watch separately since we cannot watch multiple resource types
isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only") isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only")
if isWatch || isWatchOnly { if isWatch || isWatchOnly {
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces).
FilenameParam(enforceNamespace, options.Filenames...). FilenameParam(enforceNamespace, options.Filenames...).
SelectorParam(selector). SelectorParam(selector).
@ -192,7 +192,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
return nil return nil
} }
b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces).
FilenameParam(enforceNamespace, options.Filenames...). FilenameParam(enforceNamespace, options.Filenames...).
SelectorParam(selector). SelectorParam(selector).
@ -224,7 +224,7 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
if err != nil { if err != nil {
return err return err
} }
obj, err := resource.AsVersionedObject(infos, !singular, version.String()) obj, err := resource.AsVersionedObject(infos, !singular, version.String(), f.JSONEncoder())
if err != nil { if err != nil {
return err return err
} }
@ -244,7 +244,8 @@ func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string
sorting, err := cmd.Flags().GetString("sort-by") sorting, err := cmd.Flags().GetString("sort-by")
var sorter *kubectl.RuntimeSort var sorter *kubectl.RuntimeSort
if err == nil && len(sorting) > 0 { if err == nil && len(sorting) > 0 {
if sorter, err = kubectl.SortObjects(objs, sorting); err != nil { // TODO: questionable
if sorter, err = kubectl.SortObjects(f.Decoder(true), objs, sorting); err != nil {
return err return err
} }
} }

View File

@ -177,7 +177,7 @@ func TestGetUnknownSchemaObjectListGeneric(t *testing.T) {
}, },
} }
for k, test := range testCases { for k, test := range testCases {
apiCodec := runtime.CodecFor(api.Scheme, *testapi.Default.GroupVersion()) apiCodec := testapi.Default.Codec()
regularClient := &fake.RESTClient{ regularClient := &fake.RESTClient{
Codec: apiCodec, Codec: apiCodec,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
@ -482,7 +482,7 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("unexpected error: %v", err) t.Fatalf("unexpected error: %v", err)
} }
if errs := runtime.DecodeList(list, api.Scheme); len(errs) > 0 { if errs := runtime.DecodeList(list, codec); len(errs) > 0 {
t.Fatalf("unexpected error: %v", errs) t.Fatalf("unexpected error: %v", errs)
} }
if err := meta.SetList(out, list); err != nil { if err := meta.SetList(out, list); err != nil {

View File

@ -201,7 +201,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
return cmdutil.UsageError(cmd, err.Error()) return cmdutil.UsageError(cmd, err.Error())
} }
mapper, typer := f.Object() mapper, typer := f.Object()
b := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). b := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options.Filenames...). FilenameParam(enforceNamespace, options.Filenames...).
@ -264,7 +264,7 @@ func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
} }
mapping := info.ResourceMapping() mapping := info.ResourceMapping()
client, err := f.RESTClient(mapping) client, err := f.ClientForMapping(mapping)
if err != nil { if err != nil {
return err return err
} }

View File

@ -59,6 +59,7 @@ type LogsOptions struct {
Mapper meta.RESTMapper Mapper meta.RESTMapper
Typer runtime.ObjectTyper Typer runtime.ObjectTyper
ClientMapper resource.ClientMapper ClientMapper resource.ClientMapper
Decoder runtime.Decoder
LogsForObject func(object, options runtime.Object) (*client.Request, error) LogsForObject func(object, options runtime.Object) (*client.Request, error)
@ -151,7 +152,8 @@ func (o *LogsOptions) Complete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Com
o.Options = logOptions o.Options = logOptions
o.Mapper, o.Typer = f.Object() o.Mapper, o.Typer = f.Object()
o.ClientMapper = f.ClientMapperForCommand() o.Decoder = f.Decoder(true)
o.ClientMapper = resource.ClientMapperFunc(f.ClientForMapping)
o.LogsForObject = f.LogsForObject o.LogsForObject = f.LogsForObject
o.Out = out o.Out = out
@ -176,7 +178,7 @@ func (o LogsOptions) Validate() error {
// RunLogs retrieves a pod log // RunLogs retrieves a pod log
func (o LogsOptions) RunLogs() (int64, error) { func (o LogsOptions) RunLogs() (int64, error) {
infos, err := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper). infos, err := resource.NewBuilder(o.Mapper, o.Typer, o.ClientMapper, o.Decoder).
NamespaceParam(o.Namespace).DefaultNamespace(). NamespaceParam(o.Namespace).DefaultNamespace().
ResourceNames("pods", o.ResourceArg). ResourceNames("pods", o.ResourceArg).
SingleResourceType(). SingleResourceType().

View File

@ -92,7 +92,7 @@ func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
} }
mapper, typer := f.Object() mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options.Filenames...). FilenameParam(enforceNamespace, options.Filenames...).
@ -114,7 +114,7 @@ func RunPatch(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
info := infos[0] info := infos[0]
name, namespace := info.Name, info.Namespace name, namespace := info.Name, info.Namespace
mapping := info.ResourceMapping() mapping := info.ResourceMapping()
client, err := f.RESTClient(mapping) client, err := f.ClientForMapping(mapping)
if err != nil { if err != nil {
return err return err
} }

View File

@ -112,7 +112,7 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st
} }
mapper, typer := f.Object() mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
Schema(schema). Schema(schema).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
@ -129,7 +129,7 @@ func RunReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st
return err return err
} }
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
return cmdutil.AddSourceToErr("replacing", info.Source, err) return cmdutil.AddSourceToErr("replacing", info.Source, err)
} }
@ -174,7 +174,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
} }
mapper, typer := f.Object() mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options.Filenames...). FilenameParam(enforceNamespace, options.Filenames...).
@ -198,7 +198,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
return err return err
} }
r = resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r = resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
Schema(schema). Schema(schema).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
@ -216,7 +216,7 @@ func forceReplace(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []
return err return err
} }
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
return err return err
} }

View File

@ -29,7 +29,6 @@ import (
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -198,7 +197,7 @@ func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, arg
return err return err
} }
request := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). request := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
Schema(schema). Schema(schema).
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, filename). FilenameParam(enforceNamespace, filename).
@ -385,12 +384,9 @@ func isReplicasDefaulted(info *resource.Info) bool {
// was unable to recover versioned info // was unable to recover versioned info
return false return false
} }
switch t := info.VersionedObject.(type) {
switch info.Mapping.GroupVersionKind.GroupVersion() { case *v1.ReplicationController:
case unversioned.GroupVersion{Version: "v1"}: return t.Spec.Replicas == nil
if rc, ok := info.VersionedObject.(*v1.ReplicationController); ok {
return rc.Spec.Replicas == nil
}
} }
return false return false
} }

View File

@ -236,7 +236,7 @@ func Run(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cob
return err return err
} }
_, typer := f.Object() _, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError(). ContinueOnError().
NamespaceParam(namespace).DefaultNamespace(). NamespaceParam(namespace).DefaultNamespace().
ResourceNames(mapping.Resource, name). ResourceNames(mapping.Resource, name).
@ -409,7 +409,8 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub
} }
if len(overrides) > 0 { if len(overrides) > 0 {
obj, err = cmdutil.Merge(obj, overrides, groupVersionKind.Kind) codec := runtime.NewCodec(f.JSONEncoder(), f.Decoder(true))
obj, err = cmdutil.Merge(codec, obj, overrides, groupVersionKind.Kind)
if err != nil { if err != nil {
return nil, "", nil, nil, err return nil, "", nil, nil, err
} }
@ -419,20 +420,25 @@ func createGeneratedObject(f *cmdutil.Factory, cmd *cobra.Command, generator kub
if err != nil { if err != nil {
return nil, "", nil, nil, err return nil, "", nil, nil, err
} }
client, err := f.RESTClient(mapping) client, err := f.ClientForMapping(mapping)
if err != nil { if err != nil {
return nil, "", nil, nil, err return nil, "", nil, nil, err
} }
// TODO: extract this flag to a central location, when such a location exists. // TODO: extract this flag to a central location, when such a location exists.
if !cmdutil.GetFlagBool(cmd, "dry-run") { if !cmdutil.GetFlagBool(cmd, "dry-run") {
resourceMapper := &resource.Mapper{ObjectTyper: typer, RESTMapper: mapper, ClientMapper: f.ClientMapperForCommand()} resourceMapper := &resource.Mapper{
ObjectTyper: typer,
RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
Decoder: f.Decoder(true),
}
info, err := resourceMapper.InfoForObject(obj) info, err := resourceMapper.InfoForObject(obj)
if err != nil { if err != nil {
return nil, "", nil, nil, err return nil, "", nil, nil, err
} }
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info); err != nil { if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, f.JSONEncoder()); err != nil {
return nil, "", nil, nil, err return nil, "", nil, nil, err
} }

View File

@ -105,7 +105,7 @@ func RunScale(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
} }
mapper, typer := f.Object() mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options.Filenames...). FilenameParam(enforceNamespace, options.Filenames...).

View File

@ -85,7 +85,7 @@ func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, out io.Write
} }
mapper, typer := f.Object() mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
ResourceTypeOrNameArgs(false, args...). ResourceTypeOrNameArgs(false, args...).

View File

@ -45,6 +45,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/labels" "k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/serializer/json"
"k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util"
) )
@ -63,13 +64,19 @@ type Factory struct {
// Returns interfaces for dealing with arbitrary runtime.Objects. // Returns interfaces for dealing with arbitrary runtime.Objects.
Object func() (meta.RESTMapper, runtime.ObjectTyper) Object func() (meta.RESTMapper, runtime.ObjectTyper)
// Returns interfaces for decoding objects - if toInternal is set, decoded objects will be converted
// into their internal form (if possible). Eventually the internal form will be removed as an option,
// and only versioned objects will be returned.
Decoder func(toInternal bool) runtime.Decoder
// Returns an encoder capable of encoding a provided object into JSON in the default desired version.
JSONEncoder func() runtime.Encoder
// Returns a client for accessing Kubernetes resources or an error. // Returns a client for accessing Kubernetes resources or an error.
Client func() (*client.Client, error) Client func() (*client.Client, error)
// Returns a client.Config for accessing the Kubernetes server. // Returns a client.Config for accessing the Kubernetes server.
ClientConfig func() (*client.Config, error) ClientConfig func() (*client.Config, error)
// Returns a RESTClient for working with the specified RESTMapping or an error. This is intended // Returns a RESTClient for working with the specified RESTMapping or an error. This is intended
// for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer. // for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer.
RESTClient func(mapping *meta.RESTMapping) (resource.RESTClient, error) ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
// Returns a Describer for displaying the specified RESTMapping type or an error. // Returns a Describer for displaying the specified RESTMapping type or an error.
Describer func(mapping *meta.RESTMapping) (kubectl.Describer, error) Describer func(mapping *meta.RESTMapping) (kubectl.Describer, error)
// Returns a Printer for formatting objects of the given type or an error. // Returns a Printer for formatting objects of the given type or an error.
@ -185,7 +192,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
ClientConfig: func() (*client.Config, error) { ClientConfig: func() (*client.Config, error) {
return clients.ClientConfigForVersion(nil) return clients.ClientConfigForVersion(nil)
}, },
RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) { ClientForMapping: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
mappingVersion := mapping.GroupVersionKind.GroupVersion() mappingVersion := mapping.GroupVersionKind.GroupVersion()
client, err := clients.ClientForVersion(&mappingVersion) client, err := clients.ClientForVersion(&mappingVersion)
if err != nil { if err != nil {
@ -210,6 +217,15 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
} }
return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind) return nil, fmt.Errorf("no description has been implemented for %q", mapping.GroupVersionKind.Kind)
}, },
Decoder: func(toInternal bool) runtime.Decoder {
if toInternal {
return api.Codecs.UniversalDecoder()
}
return api.Codecs.UniversalDeserializer()
},
JSONEncoder: func() runtime.Encoder {
return api.Codecs.LegacyCodec(registered.EnabledVersions()...)
},
Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) { Printer: func(mapping *meta.RESTMapping, noHeaders, withNamespace bool, wide bool, showAll bool, absoluteTimestamps bool, columnLabels []string) (kubectl.ResourcePrinter, error) {
return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, absoluteTimestamps, columnLabels), nil return kubectl.NewHumanReadablePrinter(noHeaders, withNamespace, wide, showAll, absoluteTimestamps, columnLabels), nil
}, },
@ -531,7 +547,7 @@ func getSchemaAndValidate(c schemaClient, data []byte, prefix, groupVersion, cac
} }
func (c *clientSwaggerSchema) ValidateBytes(data []byte) error { func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
gvk, err := runtime.UnstructuredJSONScheme.DataKind(data) gvk, err := json.DefaultMetaFactory.Interpret(data)
if err != nil { if err != nil {
return err return err
} }
@ -663,24 +679,9 @@ func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMappin
return printer, nil return printer, nil
} }
// ClientMapperForCommand returns a ClientMapper for the factory.
func (f *Factory) ClientMapperForCommand() resource.ClientMapper {
return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
return f.RESTClient(mapping)
})
}
// NilClientMapperForCommand returns a ClientMapper which always returns nil.
// When command is running locally and client isn't needed, this mapper can be parsed to NewBuilder.
func (f *Factory) NilClientMapperForCommand() resource.ClientMapper {
return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
return nil, nil
})
}
// One stop shopping for a Builder // One stop shopping for a Builder
func (f *Factory) NewBuilder() *resource.Builder { func (f *Factory) NewBuilder() *resource.Builder {
mapper, typer := f.Object() mapper, typer := f.Object()
return resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()) return resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true))
} }

View File

@ -245,7 +245,7 @@ func TestValidateCachesSchema(t *testing.T) {
os.RemoveAll(dir) os.RemoveAll(dir)
obj := &api.Pod{} obj := &api.Pod{}
data, err := testapi.Default.Codec().Encode(obj) data, err := runtime.Encode(testapi.Default.Codec(), obj)
if err != nil { if err != nil {
t.Errorf("unexpected error: %v", err) t.Errorf("unexpected error: %v", err)
t.FailNow() t.FailNow()

View File

@ -18,7 +18,6 @@ package util
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -28,10 +27,8 @@ import (
"strings" "strings"
"time" "time"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors" "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
@ -371,38 +368,11 @@ func ReadConfigDataFromLocation(location string) ([]byte, error) {
} }
} }
func Merge(dst runtime.Object, fragment, kind string) (runtime.Object, error) { // Merge requires JSON serialization
// Ok, this is a little hairy, we'd rather not force the user to specify a kind for their JSON // TODO: merge assumes JSON serialization, and does not properly abstract API retrieval
// So we pull it into a map, add the Kind field, and then reserialize. func Merge(codec runtime.Codec, dst runtime.Object, fragment, kind string) (runtime.Object, error) {
// We also pull the apiVersion for proper parsing
var intermediate interface{}
if err := json.Unmarshal([]byte(fragment), &intermediate); err != nil {
return nil, err
}
dataMap, ok := intermediate.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("Expected a map, found something else: %s", fragment)
}
version, found := dataMap["apiVersion"]
if !found {
return nil, fmt.Errorf("Inline JSON requires an apiVersion field")
}
versionString, ok := version.(string)
if !ok {
return nil, fmt.Errorf("apiVersion must be a string")
}
groupVersion, err := unversioned.ParseGroupVersion(versionString)
if err != nil {
return nil, err
}
i, err := registered.GroupOrDie(api.GroupName).InterfacesFor(groupVersion)
if err != nil {
return nil, err
}
// encode dst into versioned json and apply fragment directly too it // encode dst into versioned json and apply fragment directly too it
target, err := i.Codec.Encode(dst) target, err := runtime.Encode(codec, dst)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -410,7 +380,7 @@ func Merge(dst runtime.Object, fragment, kind string) (runtime.Object, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
out, err := i.Codec.Decode(patched) out, err := runtime.Decode(codec, patched)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -443,7 +413,7 @@ func DumpReaderToFile(reader io.Reader, filename string) error {
} }
// UpdateObject updates resource object with updateFn // UpdateObject updates resource object with updateFn
func UpdateObject(info *resource.Info, updateFn func(runtime.Object) error) (runtime.Object, error) { func UpdateObject(info *resource.Info, codec runtime.Codec, updateFn func(runtime.Object) error) (runtime.Object, error) {
helper := resource.NewHelper(info.Client, info.Mapping) helper := resource.NewHelper(info.Client, info.Mapping)
if err := updateFn(info.Object); err != nil { if err := updateFn(info.Object); err != nil {
@ -451,7 +421,7 @@ func UpdateObject(info *resource.Info, updateFn func(runtime.Object) error) (run
} }
// Update the annotation used by kubectl apply // Update the annotation used by kubectl apply
if err := kubectl.UpdateApplyAnnotation(info); err != nil { if err := kubectl.UpdateApplyAnnotation(info, codec); err != nil {
return nil, err return nil, err
} }

View File

@ -183,7 +183,7 @@ func TestMerge(t *testing.T) {
} }
for i, test := range tests { for i, test := range tests {
out, err := Merge(test.obj, test.fragment, test.kind) out, err := Merge(testapi.Default.Codec(), test.obj, test.fragment, test.kind)
if !test.expectErr { if !test.expectErr {
if err != nil { if err != nil {
t.Errorf("testcase[%d], unexpected error: %v", i, err) t.Errorf("testcase[%d], unexpected error: %v", i, err)

View File

@ -26,7 +26,6 @@ import (
"strings" "strings"
"text/tabwriter" "text/tabwriter"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/jsonpath" "k8s.io/kubernetes/pkg/util/jsonpath"
@ -152,6 +151,7 @@ type Column struct {
// of data from templates specified in the `Columns` array // of data from templates specified in the `Columns` array
type CustomColumnsPrinter struct { type CustomColumnsPrinter struct {
Columns []Column Columns []Column
Decoder runtime.Decoder
} }
func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error { func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
@ -192,7 +192,7 @@ func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jso
switch u := obj.(type) { switch u := obj.(type) {
case *runtime.Unknown: case *runtime.Unknown:
var err error var err error
if obj, err = api.Codec.Decode(u.RawJSON); err != nil { if obj, _, err = s.Decoder.Decode(u.RawJSON, nil, nil); err != nil {
return err return err
} }
} }

View File

@ -80,9 +80,9 @@ type resourceTuple struct {
} }
// NewBuilder creates a builder that operates on generic objects. // NewBuilder creates a builder that operates on generic objects.
func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper ClientMapper) *Builder { func NewBuilder(mapper meta.RESTMapper, typer runtime.ObjectTyper, clientMapper ClientMapper, decoder runtime.Decoder) *Builder {
return &Builder{ return &Builder{
mapper: &Mapper{typer, mapper, clientMapper}, mapper: &Mapper{typer, mapper, clientMapper, decoder},
requireObject: true, requireObject: true,
} }
} }

View File

@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing" apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/client/unversioned/fake" "k8s.io/kubernetes/pkg/client/unversioned/fake"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
utilerrors "k8s.io/kubernetes/pkg/util/errors" utilerrors "k8s.io/kubernetes/pkg/util/errors"
@ -177,9 +178,9 @@ func (v *testVisitor) Objects() []runtime.Object {
return objects return objects
} }
func TestPathBuilder(t *testing.T) { func TestPathBuilderAndVersionedObjectNotDefaulted(t *testing.T) {
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml") FilenameParam(false, "../../../docs/user-guide/update-demo/kitten-rc.yaml")
test := &testVisitor{} test := &testVisitor{}
singular := false singular := false
@ -190,9 +191,14 @@ func TestPathBuilder(t *testing.T) {
} }
info := test.Infos[0] info := test.Infos[0]
if info.Name != "redis-master" || info.Namespace != "" || info.Object == nil { if info.Name != "update-demo-kitten" || info.Namespace != "" || info.Object == nil {
t.Errorf("unexpected info: %#v", info) t.Errorf("unexpected info: %#v", info)
} }
version, ok := info.VersionedObject.(*v1.ReplicationController)
// versioned object does not have defaulting applied
if info.VersionedObject == nil || !ok || version.Spec.Replicas != nil {
t.Errorf("unexpected versioned object: %#v", info.VersionedObject)
}
} }
func TestNodeBuilder(t *testing.T) { func TestNodeBuilder(t *testing.T) {
@ -212,7 +218,7 @@ func TestNodeBuilder(t *testing.T) {
w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), node))) w.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), node)))
}() }()
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
NamespaceParam("test").Stream(r, "STDIN") NamespaceParam("test").Stream(r, "STDIN")
test := &testVisitor{} test := &testVisitor{}
@ -228,7 +234,7 @@ func TestNodeBuilder(t *testing.T) {
} }
func TestPathBuilderWithMultiple(t *testing.T) { func TestPathBuilderWithMultiple(t *testing.T) {
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml"). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").
FilenameParam(false, "../../../examples/pod"). FilenameParam(false, "../../../examples/pod").
NamespaceParam("test").DefaultNamespace() NamespaceParam("test").DefaultNamespace()
@ -252,7 +258,7 @@ func TestPathBuilderWithMultiple(t *testing.T) {
} }
func TestDirectoryBuilder(t *testing.T) { func TestDirectoryBuilder(t *testing.T) {
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
FilenameParam(false, "../../../examples/guestbook"). FilenameParam(false, "../../../examples/guestbook").
NamespaceParam("test").DefaultNamespace() NamespaceParam("test").DefaultNamespace()
@ -283,7 +289,7 @@ func TestNamespaceOverride(t *testing.T) {
// TODO: Uncomment when fix #19254 // TODO: Uncomment when fix #19254
// defer s.Close() // defer s.Close()
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
FilenameParam(false, s.URL). FilenameParam(false, s.URL).
NamespaceParam("test") NamespaceParam("test")
@ -294,7 +300,7 @@ func TestNamespaceOverride(t *testing.T) {
t.Fatalf("unexpected response: %v %#v", err, test.Infos) t.Fatalf("unexpected response: %v %#v", err, test.Infos)
} }
b = NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b = NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
FilenameParam(true, s.URL). FilenameParam(true, s.URL).
NamespaceParam("test") NamespaceParam("test")
@ -314,7 +320,7 @@ func TestURLBuilder(t *testing.T) {
// TODO: Uncomment when fix #19254 // TODO: Uncomment when fix #19254
// defer s.Close() // defer s.Close()
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
FilenameParam(false, s.URL). FilenameParam(false, s.URL).
NamespaceParam("test") NamespaceParam("test")
@ -339,7 +345,7 @@ func TestURLBuilderRequireNamespace(t *testing.T) {
// TODO: Uncomment when fix #19254 // TODO: Uncomment when fix #19254
// defer s.Close() // defer s.Close()
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
FilenameParam(false, s.URL). FilenameParam(false, s.URL).
NamespaceParam("test").RequireNamespace() NamespaceParam("test").RequireNamespace()
@ -356,7 +362,7 @@ func TestResourceByName(t *testing.T) {
pods, _ := testData() pods, _ := testData()
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{
"/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]),
})). }), testapi.Default.Codec()).
NamespaceParam("test") NamespaceParam("test")
test := &testVisitor{} test := &testVisitor{}
@ -392,7 +398,7 @@ func TestMultipleResourceByTheSameName(t *testing.T) {
"/namespaces/test/pods/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[1]), "/namespaces/test/pods/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[1]),
"/namespaces/test/services/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &svcs.Items[0]), "/namespaces/test/services/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &svcs.Items[0]),
"/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &svcs.Items[0]), "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &svcs.Items[0]),
})). }), testapi.Default.Codec()).
NamespaceParam("test") NamespaceParam("test")
test := &testVisitor{} test := &testVisitor{}
@ -422,7 +428,7 @@ func TestResourceNames(t *testing.T) {
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{
"/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]),
"/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &svc.Items[0]), "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), &svc.Items[0]),
})). }), testapi.Default.Codec()).
NamespaceParam("test") NamespaceParam("test")
test := &testVisitor{} test := &testVisitor{}
@ -446,7 +452,7 @@ func TestResourceNames(t *testing.T) {
} }
func TestResourceByNameWithoutRequireObject(t *testing.T) { func TestResourceByNameWithoutRequireObject(t *testing.T) {
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{})). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{}), testapi.Default.Codec()).
NamespaceParam("test") NamespaceParam("test")
test := &testVisitor{} test := &testVisitor{}
@ -482,7 +488,7 @@ func TestResourceByNameAndEmptySelector(t *testing.T) {
pods, _ := testData() pods, _ := testData()
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{
"/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]), "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), &pods.Items[0]),
})). }), testapi.Default.Codec()).
NamespaceParam("test"). NamespaceParam("test").
SelectorParam(""). SelectorParam("").
ResourceTypeOrNameArgs(true, "pods", "foo") ResourceTypeOrNameArgs(true, "pods", "foo")
@ -511,7 +517,7 @@ func TestSelector(t *testing.T) {
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{
"/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods),
"/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), svc), "/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), svc),
})). }), testapi.Default.Codec()).
SelectorParam("a=b"). SelectorParam("a=b").
NamespaceParam("test"). NamespaceParam("test").
Flatten() Flatten()
@ -539,7 +545,7 @@ func TestSelector(t *testing.T) {
} }
func TestSelectorRequiresKnownTypes(t *testing.T) { func TestSelectorRequiresKnownTypes(t *testing.T) {
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
SelectorParam("a=b"). SelectorParam("a=b").
NamespaceParam("test"). NamespaceParam("test").
ResourceTypes("unknown") ResourceTypes("unknown")
@ -550,7 +556,7 @@ func TestSelectorRequiresKnownTypes(t *testing.T) {
} }
func TestSingleResourceType(t *testing.T) { func TestSingleResourceType(t *testing.T) {
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
SelectorParam("a=b"). SelectorParam("a=b").
SingleResourceType(). SingleResourceType().
ResourceTypeOrNameArgs(true, "pods,services") ResourceTypeOrNameArgs(true, "pods,services")
@ -620,7 +626,7 @@ func TestResourceTuple(t *testing.T) {
} }
} }
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith(k, t, expectedRequests)). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith(k, t, expectedRequests), testapi.Default.Codec()).
NamespaceParam("test").DefaultNamespace(). NamespaceParam("test").DefaultNamespace().
ResourceTypeOrNameArgs(true, testCase.args...).RequireObject(requireObject) ResourceTypeOrNameArgs(true, testCase.args...).RequireObject(requireObject)
@ -651,7 +657,7 @@ func TestResourceTuple(t *testing.T) {
func TestStream(t *testing.T) { func TestStream(t *testing.T) {
r, pods, rc := streamTestData() r, pods, rc := streamTestData()
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
NamespaceParam("test").Stream(r, "STDIN").Flatten() NamespaceParam("test").Stream(r, "STDIN").Flatten()
test := &testVisitor{} test := &testVisitor{}
@ -668,7 +674,7 @@ func TestStream(t *testing.T) {
func TestYAMLStream(t *testing.T) { func TestYAMLStream(t *testing.T) {
r, pods, rc := streamYAMLTestData() r, pods, rc := streamYAMLTestData()
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
NamespaceParam("test").Stream(r, "STDIN").Flatten() NamespaceParam("test").Stream(r, "STDIN").Flatten()
test := &testVisitor{} test := &testVisitor{}
@ -685,7 +691,7 @@ func TestYAMLStream(t *testing.T) {
func TestMultipleObject(t *testing.T) { func TestMultipleObject(t *testing.T) {
r, pods, svc := streamTestData() r, pods, svc := streamTestData()
obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
NamespaceParam("test").Stream(r, "STDIN").Flatten(). NamespaceParam("test").Stream(r, "STDIN").Flatten().
Do().Object() Do().Object()
@ -707,7 +713,7 @@ func TestMultipleObject(t *testing.T) {
func TestContinueOnErrorVisitor(t *testing.T) { func TestContinueOnErrorVisitor(t *testing.T) {
r, _, _ := streamTestData() r, _, _ := streamTestData()
req := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). req := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
ContinueOnError(). ContinueOnError().
NamespaceParam("test").Stream(r, "STDIN").Flatten(). NamespaceParam("test").Stream(r, "STDIN").Flatten().
Do() Do()
@ -736,7 +742,7 @@ func TestContinueOnErrorVisitor(t *testing.T) {
} }
func TestSingularObject(t *testing.T) { func TestSingularObject(t *testing.T) {
obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
NamespaceParam("test").DefaultNamespace(). NamespaceParam("test").DefaultNamespace().
FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml"). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").
Flatten(). Flatten().
@ -756,7 +762,7 @@ func TestSingularObject(t *testing.T) {
} }
func TestSingularObjectNoExtension(t *testing.T) { func TestSingularObjectNoExtension(t *testing.T) {
obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
NamespaceParam("test").DefaultNamespace(). NamespaceParam("test").DefaultNamespace().
FilenameParam(false, "../../../examples/pod"). FilenameParam(false, "../../../examples/pod").
Flatten(). Flatten().
@ -778,7 +784,7 @@ func TestSingularObjectNoExtension(t *testing.T) {
func TestSingularRootScopedObject(t *testing.T) { func TestSingularRootScopedObject(t *testing.T) {
node := &api.Node{ObjectMeta: api.ObjectMeta{Name: "test"}, Spec: api.NodeSpec{ExternalID: "test"}} node := &api.Node{ObjectMeta: api.ObjectMeta{Name: "test"}, Spec: api.NodeSpec{ExternalID: "test"}}
r := streamTestObject(node) r := streamTestObject(node)
infos, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). infos, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
NamespaceParam("test").DefaultNamespace(). NamespaceParam("test").DefaultNamespace().
Stream(r, "STDIN"). Stream(r, "STDIN").
Flatten(). Flatten().
@ -805,7 +811,7 @@ func TestListObject(t *testing.T) {
labelKey := unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String()) labelKey := unversioned.LabelSelectorQueryParam(testapi.Default.GroupVersion().String())
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{
"/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods),
})). }), testapi.Default.Codec()).
SelectorParam("a=b"). SelectorParam("a=b").
NamespaceParam("test"). NamespaceParam("test").
ResourceTypeOrNameArgs(true, "pods"). ResourceTypeOrNameArgs(true, "pods").
@ -839,7 +845,7 @@ func TestListObjectWithDifferentVersions(t *testing.T) {
obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{ obj, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClientWith("", t, map[string]string{
"/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods), "/namespaces/test/pods?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), pods),
"/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), svc), "/namespaces/test/services?" + labelKey + "=a%3Db": runtime.EncodeOrDie(testapi.Default.Codec(), svc),
})). }), testapi.Default.Codec()).
SelectorParam("a=b"). SelectorParam("a=b").
NamespaceParam("test"). NamespaceParam("test").
ResourceTypeOrNameArgs(true, "pods,services"). ResourceTypeOrNameArgs(true, "pods,services").
@ -867,7 +873,7 @@ func TestWatch(t *testing.T) {
Type: watch.Added, Type: watch.Added,
Object: &svc.Items[0], Object: &svc.Items[0],
}), }),
})). }), testapi.Default.Codec()).
NamespaceParam("test").DefaultNamespace(). NamespaceParam("test").DefaultNamespace().
FilenameParam(false, "../../../examples/guestbook/redis-master-service.yaml").Flatten(). FilenameParam(false, "../../../examples/guestbook/redis-master-service.yaml").Flatten().
Do().Watch("12") Do().Watch("12")
@ -894,7 +900,7 @@ func TestWatch(t *testing.T) {
} }
func TestWatchMultipleError(t *testing.T) { func TestWatchMultipleError(t *testing.T) {
_, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). _, err := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
NamespaceParam("test").DefaultNamespace(). NamespaceParam("test").DefaultNamespace().
FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten().
FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten(). FilenameParam(false, "../../../examples/guestbook/redis-master-controller.yaml").Flatten().
@ -921,7 +927,7 @@ func TestLatest(t *testing.T) {
"/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), newPod), "/namespaces/test/pods/foo": runtime.EncodeOrDie(testapi.Default.Codec(), newPod),
"/namespaces/test/pods/bar": runtime.EncodeOrDie(testapi.Default.Codec(), newPod2), "/namespaces/test/pods/bar": runtime.EncodeOrDie(testapi.Default.Codec(), newPod2),
"/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), newSvc), "/namespaces/test/services/baz": runtime.EncodeOrDie(testapi.Default.Codec(), newSvc),
})). }), testapi.Default.Codec()).
NamespaceParam("other").Stream(r, "STDIN").Flatten().Latest() NamespaceParam("other").Stream(r, "STDIN").Flatten().Latest()
test := &testVisitor{} test := &testVisitor{}
@ -953,7 +959,7 @@ func TestReceiveMultipleErrors(t *testing.T) {
w2.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &svc.Items[0]))) w2.Write([]byte(runtime.EncodeOrDie(testapi.Default.Codec(), &svc.Items[0])))
}() }()
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()). b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec()).
Stream(r, "1").Stream(r2, "2"). Stream(r, "1").Stream(r2, "2").
ContinueOnError() ContinueOnError()
@ -997,7 +1003,7 @@ func TestReplaceAliases(t *testing.T) {
}, },
} }
b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient()) b := NewBuilder(testapi.Default.RESTMapper(), api.Scheme, fakeClient(), testapi.Default.Codec())
for _, test := range tests { for _, test := range tests {
replaced := b.replaceAliases(test.arg) replaced := b.replaceAliases(test.arg)

View File

@ -33,8 +33,6 @@ type Helper struct {
Resource string Resource string
// A RESTClient capable of mutating this resource. // A RESTClient capable of mutating this resource.
RESTClient RESTClient RESTClient RESTClient
// A codec for decoding and encoding objects of this resource type.
Codec runtime.Codec
// An interface for reading or writing the resource version of this // An interface for reading or writing the resource version of this
// type. // type.
Versioner runtime.ResourceVersioner Versioner runtime.ResourceVersioner
@ -45,9 +43,8 @@ type Helper struct {
// NewHelper creates a Helper from a ResourceMapping // NewHelper creates a Helper from a ResourceMapping
func NewHelper(client RESTClient, mapping *meta.RESTMapping) *Helper { func NewHelper(client RESTClient, mapping *meta.RESTMapping) *Helper {
return &Helper{ return &Helper{
RESTClient: client,
Resource: mapping.Resource, Resource: mapping.Resource,
Codec: mapping.Codec, RESTClient: client,
Versioner: mapping.MetadataAccessor, Versioner: mapping.MetadataAccessor,
NamespaceScoped: mapping.Scope.Name() == meta.RESTScopeNameNamespace, NamespaceScoped: mapping.Scope.Name() == meta.RESTScopeNameNamespace,
} }

View File

@ -189,7 +189,6 @@ func TestHelperCreate(t *testing.T) {
} }
modifier := &Helper{ modifier := &Helper{
RESTClient: client, RESTClient: client,
Codec: testapi.Default.Codec(),
Versioner: testapi.Default.MetadataAccessor(), Versioner: testapi.Default.MetadataAccessor(),
NamespaceScoped: true, NamespaceScoped: true,
} }
@ -441,7 +440,6 @@ func TestHelperReplace(t *testing.T) {
} }
modifier := &Helper{ modifier := &Helper{
RESTClient: client, RESTClient: client,
Codec: testapi.Default.Codec(),
Versioner: testapi.Default.MetadataAccessor(), Versioner: testapi.Default.MetadataAccessor(),
NamespaceScoped: true, NamespaceScoped: true,
} }

View File

@ -32,7 +32,7 @@ type RESTClient interface {
Put() *client.Request Put() *client.Request
} }
// ClientMapper retrieves a client object for a given mapping // ClientMapper abstracts retrieving a Client for mapped objects.
type ClientMapper interface { type ClientMapper interface {
ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error)
} }

View File

@ -20,69 +20,63 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/yaml"
) )
// DisabledClientForMapping allows callers to avoid allowing remote calls when handling
// resources.
type DisabledClientForMapping struct {
ClientMapper
}
func (f DisabledClientForMapping) ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) {
return nil, nil
}
// Mapper is a convenience struct for holding references to the three interfaces // Mapper is a convenience struct for holding references to the three interfaces
// needed to create Info for arbitrary objects. // needed to create Info for arbitrary objects.
type Mapper struct { type Mapper struct {
runtime.ObjectTyper runtime.ObjectTyper
meta.RESTMapper meta.RESTMapper
ClientMapper ClientMapper
runtime.Decoder
} }
// InfoForData creates an Info object for the given data. An error is returned // InfoForData creates an Info object for the given data. An error is returned
// if any of the decoding or client lookup steps fail. Name and namespace will be // if any of the decoding or client lookup steps fail. Name and namespace will be
// set into Info if the mapping's MetadataAccessor can retrieve them. // set into Info if the mapping's MetadataAccessor can retrieve them.
func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) { func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
json, err := yaml.ToJSON(data) versions := &runtime.VersionedObjects{}
_, gvk, err := m.Decode(data, nil, versions)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to parse %q: %v", source, err) return nil, fmt.Errorf("unable to decode %q: %v", source, err)
}
data = json
gvk, err := runtime.UnstructuredJSONScheme.DataKind(data)
if err != nil {
return nil, fmt.Errorf("unable to get type info from %q: %v", source, err)
}
if ok := registered.IsEnabledVersion(gvk.GroupVersion()); !ok {
return nil, fmt.Errorf("API version %q in %q isn't supported, only supports API versions %q", gvk.GroupVersion().String(), source, registered.EnabledVersions())
}
if gvk.Kind == "" {
return nil, fmt.Errorf("kind not set in %q", source)
} }
mapping, err := m.RESTMapping(gvk.GroupKind(), gvk.Version) mapping, err := m.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to recognize %q: %v", source, err) return nil, fmt.Errorf("unable to recognize %q: %v", source, err)
} }
obj, err := mapping.Codec.Decode(data)
if err != nil {
return nil, fmt.Errorf("unable to load %q: %v", source, err)
}
client, err := m.ClientForMapping(mapping) client, err := m.ClientForMapping(mapping)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err) return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
} }
// TODO: decoding the version object is convenient, but questionable. This is used by apply
// and rolling-update today, but both of those cases should probably be requesting the raw
// object and performing their own decoding.
obj, versioned := versions.Last(), versions.First()
name, _ := mapping.MetadataAccessor.Name(obj) name, _ := mapping.MetadataAccessor.Name(obj)
namespace, _ := mapping.MetadataAccessor.Namespace(obj) namespace, _ := mapping.MetadataAccessor.Namespace(obj)
resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj) resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj)
var versionedObject interface{}
if vo, _, err := api.Scheme.Raw().DecodeToVersionedObject(data); err == nil {
versionedObject = vo
}
return &Info{ return &Info{
Mapping: mapping, Mapping: mapping,
Client: client, Client: client,
Namespace: namespace, Namespace: namespace,
Name: name, Name: name,
Source: source, Source: source,
VersionedObject: versionedObject, VersionedObject: versioned,
Object: obj, Object: obj,
ResourceVersion: resourceVersion, ResourceVersion: resourceVersion,
}, nil }, nil

View File

@ -210,8 +210,8 @@ func (r *Result) Watch(resourceVersion string) (watch.Interface, error) {
// the objects as children, or if only a single Object is present, as that object. The provided // the objects as children, or if only a single Object is present, as that object. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be // version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present. // used if that version is not present.
func AsVersionedObject(infos []*Info, forceList bool, version string) (runtime.Object, error) { func AsVersionedObject(infos []*Info, forceList bool, version string, encoder runtime.Encoder) (runtime.Object, error) {
objects, err := AsVersionedObjects(infos, version) objects, err := AsVersionedObjects(infos, version, encoder)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -233,19 +233,21 @@ func AsVersionedObject(infos []*Info, forceList bool, version string) (runtime.O
// AsVersionedObjects converts a list of infos into versioned objects. The provided // AsVersionedObjects converts a list of infos into versioned objects. The provided
// version will be preferred as the conversion target, but the Object's mapping version will be // version will be preferred as the conversion target, but the Object's mapping version will be
// used if that version is not present. // used if that version is not present.
func AsVersionedObjects(infos []*Info, version string) ([]runtime.Object, error) { func AsVersionedObjects(infos []*Info, version string, encoder runtime.Encoder) ([]runtime.Object, error) {
objects := []runtime.Object{} objects := []runtime.Object{}
for _, info := range infos { for _, info := range infos {
if info.Object == nil { if info.Object == nil {
continue continue
} }
// TODO: use info.VersionedObject as the value?
// objects that are not part of api.Scheme must be converted to JSON // objects that are not part of api.Scheme must be converted to JSON
// TODO: convert to map[string]interface{}, attach to runtime.Unknown? // TODO: convert to map[string]interface{}, attach to runtime.Unknown?
if len(version) > 0 { if len(version) > 0 {
if _, err := api.Scheme.ObjectKind(info.Object); runtime.IsNotRegisteredError(err) { if _, err := api.Scheme.ObjectKind(info.Object); runtime.IsNotRegisteredError(err) {
// TODO: ideally this would encode to version, but we don't expose multiple codecs here. // TODO: ideally this would encode to version, but we don't expose multiple codecs here.
data, err := info.Mapping.Codec.Encode(info.Object) data, err := runtime.Encode(encoder, info.Object)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -345,7 +345,7 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
if errs := runtime.DecodeList(items, struct { if errs := runtime.DecodeList(items, struct {
runtime.ObjectTyper runtime.ObjectTyper
runtime.Decoder runtime.Decoder
}{v.Mapper, info.Mapping.Codec}); len(errs) > 0 { }{v.Mapper, v.Mapper.Decoder}); len(errs) > 0 {
return utilerrors.NewAggregate(errs) return utilerrors.NewAggregate(errs)
} }
for i := range items { for i := range items {

View File

@ -33,7 +33,6 @@ import (
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/latest"
"k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/apis/extensions" "k8s.io/kubernetes/pkg/apis/extensions"
@ -68,7 +67,7 @@ func GetPrinter(format, formatArgument string) (ResourcePrinter, bool, error) {
case "name": case "name":
printer = &NamePrinter{ printer = &NamePrinter{
Typer: runtime.ObjectTyperToTyper(api.Scheme), Typer: runtime.ObjectTyperToTyper(api.Scheme),
Decoder: latest.Codecs.UniversalDecoder(), Decoder: api.Codecs.UniversalDecoder(),
} }
case "template", "go-template": case "template", "go-template":
if len(formatArgument) == 0 { if len(formatArgument) == 0 {

View File

@ -27,7 +27,6 @@ import (
"time" "time"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/latest"
"k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
@ -466,7 +465,7 @@ func TestPrinters(t *testing.T) {
"jsonpath": jsonpathPrinter, "jsonpath": jsonpathPrinter,
"name": &NamePrinter{ "name": &NamePrinter{
Typer: runtime.ObjectTyperToTyper(api.Scheme), Typer: runtime.ObjectTyperToTyper(api.Scheme),
Decoder: latest.Codecs.UniversalDecoder(), Decoder: api.Codecs.UniversalDecoder(),
}, },
} }
objects := map[string]runtime.Object{ objects := map[string]runtime.Object{

View File

@ -20,13 +20,13 @@ import (
"reflect" "reflect"
"testing" "testing"
"k8s.io/kubernetes/pkg/api/latest" internal "k8s.io/kubernetes/pkg/api"
api "k8s.io/kubernetes/pkg/api/v1" api "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime" "k8s.io/kubernetes/pkg/runtime"
) )
func encodeOrDie(obj runtime.Object) []byte { func encodeOrDie(obj runtime.Object) []byte {
data, err := runtime.Encode(latest.Codecs.LegacyCodec(api.SchemeGroupVersion), obj) data, err := runtime.Encode(internal.Codecs.LegacyCodec(api.SchemeGroupVersion), obj)
if err != nil { if err != nil {
panic(err.Error()) panic(err.Error())
} }
@ -224,7 +224,7 @@ func TestSortingPrinter(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
sort := &SortingPrinter{SortField: test.field, Decoder: latest.Codecs.UniversalDecoder()} sort := &SortingPrinter{SortField: test.field, Decoder: internal.Codecs.UniversalDecoder()}
if err := sort.sortObj(test.obj); err != nil { if err := sort.sortObj(test.obj); err != nil {
t.Errorf("unexpected error: %v (%s)", err, test.name) t.Errorf("unexpected error: %v (%s)", err, test.name)
continue continue

View File

@ -25,7 +25,6 @@ import (
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta" "k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/registered"
"k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/api/unversioned"
apiutil "k8s.io/kubernetes/pkg/api/util" apiutil "k8s.io/kubernetes/pkg/api/util"
"k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apimachinery/registered"