Unverified Commit 40e71018 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #55660 from smarterclayton/get_clean

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Stop using VersionedObject in resource.Builder, use unstructured Remove the need for kubectl callers to distinguish between unstructured and versioned / type aware builders. The factory should create a single builder than can be set to return unstructured objects. Callers can then use one of the new helpers on `resource.Info` to convert the objects into the desired form - `Internal()` for printers, `Typed()` for external versions, and `Unstructured()` to ensure the object is in the right state. Leverages the new scheme support for unstructured conversion so that higher level callers can perform best effort conversion (get typed versions if you have them, otherwise use default behavior). `get.go` demonstrates this by removing the previous logic that depended on the underlying scheme. Other commands are updated to be consistent. Includes #55650 and #55647.
parents d0301aa6 09b64d36
...@@ -72,59 +72,33 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime. ...@@ -72,59 +72,33 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.
// 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
if info.VersionedObject != nil {
// If an object was read from input, use that version.
accessor, err := meta.Accessor(info.VersionedObject)
if err != nil {
return nil, err
}
// Get the current annotations from the object.
annots := accessor.GetAnnotations()
if annots == nil {
annots = map[string]string{}
}
original := annots[api.LastAppliedConfigAnnotation] // Otherwise, use the server side version of the object.
delete(annots, api.LastAppliedConfigAnnotation) accessor := info.Mapping.MetadataAccessor
accessor.SetAnnotations(annots) // Get the current annotations from the object.
// TODO: this needs to be abstracted - there should be no assumption that versioned object annots, err := accessor.Annotations(info.Object)
// can be marshalled to JSON. if err != nil {
modified, err = runtime.Encode(codec, info.VersionedObject) return nil, err
if err != nil { }
return nil, err
}
if annotate { if annots == nil {
annots[api.LastAppliedConfigAnnotation] = string(modified) annots = map[string]string{}
accessor.SetAnnotations(annots) }
// TODO: this needs to be abstracted - there should be no assumption that versioned object
// can be marshalled to JSON.
modified, err = runtime.Encode(codec, info.VersionedObject)
if err != nil {
return nil, err
}
}
// Restore the object to its original condition. original := annots[api.LastAppliedConfigAnnotation]
annots[api.LastAppliedConfigAnnotation] = original delete(annots, api.LastAppliedConfigAnnotation)
accessor.SetAnnotations(annots) if err := accessor.SetAnnotations(info.Object, annots); err != nil {
} else { return nil, err
// Otherwise, use the server side version of the object. }
accessor := info.Mapping.MetadataAccessor
// Get the current annotations from the object.
annots, err := accessor.Annotations(info.Object)
if err != nil {
return nil, err
}
if annots == nil { modified, err = runtime.Encode(codec, info.Object)
annots = map[string]string{} if err != nil {
} return nil, err
}
original := annots[api.LastAppliedConfigAnnotation] if annotate {
delete(annots, api.LastAppliedConfigAnnotation) annots[api.LastAppliedConfigAnnotation] = string(modified)
if err := accessor.SetAnnotations(info.Object, annots); err != nil { if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
return nil, err return nil, err
} }
...@@ -132,24 +106,12 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime. ...@@ -132,24 +106,12 @@ func GetModifiedConfiguration(info *resource.Info, annotate bool, codec runtime.
if err != nil { if err != nil {
return nil, err return nil, err
} }
}
if annotate { // Restore the object to its original condition.
annots[api.LastAppliedConfigAnnotation] = string(modified) annots[api.LastAppliedConfigAnnotation] = original
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil { if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
return nil, err return nil, err
}
modified, err = runtime.Encode(codec, info.Object)
if err != nil {
return nil, err
}
}
// Restore the object to its original condition.
annots[api.LastAppliedConfigAnnotation] = original
if err := info.Mapping.MetadataAccessor.SetAnnotations(info.Object, annots); err != nil {
return nil, err
}
} }
return modified, nil return modified, nil
......
...@@ -189,23 +189,22 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro ...@@ -189,23 +189,22 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro
changeCause := f.Command(cmd, false) changeCause := f.Command(cmd, false)
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
b := f.NewBuilder().
Unstructured().
LocalParam(o.local).
ContinueOnError().
NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(includeUninitialized).
Flatten()
var b *resource.Builder if !o.local {
if o.local { b = b.LabelSelectorParam(o.selector).
b = f.NewBuilder().
Local(f.ClientForMapping)
} else {
b = f.NewUnstructuredBuilder().
LabelSelectorParam(o.selector).
ResourceTypeOrNameArgs(o.all, o.resources...). ResourceTypeOrNameArgs(o.all, o.resources...).
Latest() Latest()
} }
r := b.ContinueOnError().
NamespaceParam(namespace).DefaultNamespace(). r := b.Do()
FilenameParam(enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(includeUninitialized).
Flatten().
Do()
if err := r.Err(); err != nil { if err := r.Err(); err != nil {
return err return err
} }
...@@ -279,15 +278,7 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro ...@@ -279,15 +278,7 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro
} }
} }
var mapper meta.RESTMapper mapper := r.Mapper().RESTMapper
if o.local {
mapper, _ = f.Object()
} else {
mapper, _, err = f.UnstructuredObject()
if err != nil {
return err
}
}
if len(o.outputFormat) > 0 { if len(o.outputFormat) > 0 {
return f.PrintObject(cmd, o.local, mapper, outputObj, o.out) return f.PrintObject(cmd, o.local, mapper, outputObj, o.out)
} }
......
...@@ -198,22 +198,11 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti ...@@ -198,22 +198,11 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti
return err return err
} }
mapper, _, err := f.UnstructuredObject()
if err != nil {
return err
}
if options.Prune {
options.PruneResources, err = parsePruneResources(mapper, cmdutil.GetFlagStringArray(cmd, "prune-whitelist"))
if err != nil {
return err
}
}
// include the uninitialized objects by default if --prune is true // include the uninitialized objects by default if --prune is true
// unless explicitly set --include-uninitialized=false // unless explicitly set --include-uninitialized=false
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, options.Prune) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, options.Prune)
r := f.NewUnstructuredBuilder(). r := f.NewBuilder().
Unstructured().
Schema(schema). Schema(schema).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
...@@ -222,25 +211,30 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti ...@@ -222,25 +211,30 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti
IncludeUninitialized(includeUninitialized). IncludeUninitialized(includeUninitialized).
Flatten(). Flatten().
Do() Do()
err = r.Err() if err := r.Err(); err != nil {
if err != nil {
return err return err
} }
if options.Prune {
options.PruneResources, err = parsePruneResources(r.Mapper().RESTMapper, cmdutil.GetFlagStringArray(cmd, "prune-whitelist"))
if err != nil {
return err
}
}
dryRun := cmdutil.GetFlagBool(cmd, "dry-run") dryRun := cmdutil.GetFlagBool(cmd, "dry-run")
output := cmdutil.GetFlagString(cmd, "output") output := cmdutil.GetFlagString(cmd, "output")
shortOutput := output == "name" shortOutput := output == "name"
encoder := f.JSONEncoder() encoder := f.JSONEncoder()
decoder := f.Decoder(false) decoder := f.Decoder(false)
mapper := r.Mapper().RESTMapper
visitedUids := sets.NewString() visitedUids := sets.NewString()
visitedNamespaces := sets.NewString() visitedNamespaces := sets.NewString()
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
// and info.VersionedObject contains the object decoded from the input source.
if err != nil { if err != nil {
return err return err
} }
...@@ -251,10 +245,7 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti ...@@ -251,10 +245,7 @@ func RunApply(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opti
// Add change-cause annotation to resource info if it should be recorded // Add change-cause annotation to resource info if it should be recorded
if cmdutil.ShouldRecord(cmd, info) { if cmdutil.ShouldRecord(cmd, info) {
recordInObj := info.VersionedObject recordInObj := info.Object
if info.VersionedObject == nil {
recordInObj = info.Object
}
if err := cmdutil.RecordChangeCause(recordInObj, f.Command(cmd, false)); err != nil { if err := cmdutil.RecordChangeCause(recordInObj, f.Command(cmd, false)); err != nil {
glog.V(4).Infof("error recording current command: %v", err) glog.V(4).Infof("error recording current command: %v", err)
} }
......
...@@ -112,7 +112,7 @@ func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) ...@@ -112,7 +112,7 @@ func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command)
o.Codec = f.JSONEncoder() o.Codec = f.JSONEncoder()
var err error var err error
o.Mapper, o.Typer, err = f.UnstructuredObject() o.Mapper, o.Typer = f.Object()
if err != nil { if err != nil {
return err return err
} }
...@@ -122,23 +122,18 @@ func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) ...@@ -122,23 +122,18 @@ func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command)
} }
func (o *SetLastAppliedOptions) Validate(f cmdutil.Factory, cmd *cobra.Command) error { func (o *SetLastAppliedOptions) Validate(f cmdutil.Factory, cmd *cobra.Command) error {
r := f.NewUnstructuredBuilder(). r := f.NewBuilder().
Unstructured().
NamespaceParam(o.Namespace).DefaultNamespace(). NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, &o.FilenameOptions). FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
Latest().
Flatten(). Flatten().
Do() Do()
err := r.Err()
if err != nil {
return err
}
err = r.Visit(func(info *resource.Info, err error) error { err := r.Visit(func(info *resource.Info, err error) error {
if err != nil { if err != nil {
return err return err
} }
patchBuf, diffBuf, patchType, err := editor.GetApplyPatch(info.Object, o.Codec)
patchBuf, diffBuf, patchType, err := editor.GetApplyPatch(info.VersionedObject, o.Codec)
if err != nil { if err != nil {
return err return err
} }
...@@ -151,16 +146,16 @@ func (o *SetLastAppliedOptions) Validate(f cmdutil.Factory, cmd *cobra.Command) ...@@ -151,16 +146,16 @@ func (o *SetLastAppliedOptions) Validate(f cmdutil.Factory, cmd *cobra.Command)
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%v\nfrom server for:", info), info.Source, err) return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%v\nfrom server for:", info), info.Source, err)
} }
} }
oringalBuf, err := kubectl.GetOriginalConfiguration(info.Mapping, info.Object) originalBuf, err := kubectl.GetOriginalConfiguration(info.Mapping, info.Object)
if err != nil { if err != nil {
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%v\nfrom server for:", info), info.Source, err) return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%v\nfrom server for:", info), info.Source, err)
} }
if oringalBuf == nil && !o.CreateAnnotation { if originalBuf == nil && !o.CreateAnnotation {
return cmdutil.UsageErrorf(cmd, "no last-applied-configuration annotation found on resource: %s, to create the annotation, run the command with --create-annotation", info.Name) return cmdutil.UsageErrorf(cmd, "no last-applied-configuration annotation found on resource: %s, to create the annotation, run the command with --create-annotation", info.Name)
} }
//only add to PatchBufferList when changed //only add to PatchBufferList when changed
if !bytes.Equal(cmdutil.StripComments(oringalBuf), cmdutil.StripComments(diffBuf)) { if !bytes.Equal(cmdutil.StripComments(originalBuf), cmdutil.StripComments(diffBuf)) {
p := PatchBuffer{Patch: patchBuf, PatchType: patchType} p := PatchBuffer{Patch: patchBuf, PatchType: patchType}
o.PatchBufferList = append(o.PatchBufferList, p) o.PatchBufferList = append(o.PatchBufferList, p)
o.InfoList = append(o.InfoList, info) o.InfoList = append(o.InfoList, info)
...@@ -228,7 +223,7 @@ func (o *SetLastAppliedOptions) getPatch(info *resource.Info) ([]byte, []byte, e ...@@ -228,7 +223,7 @@ func (o *SetLastAppliedOptions) getPatch(info *resource.Info) ([]byte, []byte, e
objMap := map[string]map[string]map[string]string{} objMap := map[string]map[string]map[string]string{}
metadataMap := map[string]map[string]string{} metadataMap := map[string]map[string]string{}
annotationsMap := map[string]string{} annotationsMap := map[string]string{}
localFile, err := runtime.Encode(o.Codec, info.VersionedObject) localFile, err := runtime.Encode(o.Codec, info.Object)
if err != nil { if err != nil {
return nil, localFile, err return nil, localFile, err
} }
......
...@@ -1014,51 +1014,53 @@ func TestRunApplySetLastApplied(t *testing.T) { ...@@ -1014,51 +1014,53 @@ func TestRunApplySetLastApplied(t *testing.T) {
}, },
} }
for _, test := range tests { for _, test := range tests {
f, tf, codec, _ := cmdtesting.NewAPIFactory() t.Run(test.name, func(t *testing.T) {
tf.Printer = &testPrinter{} f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.UnstructuredClient = &fake.RESTClient{ tf.Printer = &testPrinter{}
GroupVersion: schema.GroupVersion{Version: "v1"}, tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer, GroupVersion: schema.GroupVersion{Version: "v1"},
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { NegotiatedSerializer: unstructuredSerializer,
switch p, m := req.URL.Path, req.Method; { Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
case p == pathRC && m == "GET": switch p, m := req.URL.Path, req.Method; {
bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) case p == pathRC && m == "GET":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC))
case p == noAnnotationPath && m == "GET": return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil
bodyRC := ioutil.NopCloser(bytes.NewReader(noAnnotationRC)) case p == noAnnotationPath && m == "GET":
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil bodyRC := ioutil.NopCloser(bytes.NewReader(noAnnotationRC))
case p == noExistPath && m == "GET": return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil
return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: objBody(codec, &api.Pod{})}, nil case p == noExistPath && m == "GET":
case p == pathRC && m == "PATCH": return &http.Response{StatusCode: 404, Header: defaultHeader(), Body: objBody(codec, &api.Pod{})}, nil
checkPatchString(t, req) case p == pathRC && m == "PATCH":
bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC)) checkPatchString(t, req)
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil bodyRC := ioutil.NopCloser(bytes.NewReader(currentRC))
case p == "/api/v1/namespaces/test" && m == "GET": return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: bodyRC}, nil
return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &api.Namespace{})}, nil case p == "/api/v1/namespaces/test" && m == "GET":
default: return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &api.Namespace{})}, nil
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req) default:
return nil, nil t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil
}
}),
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmdutil.BehaviorOnFatal(func(str string, code int) {
if str != test.expectedErr {
t.Errorf("%s: unexpected error: %s\nexpected: %s", test.name, str, test.expectedErr)
} }
}), })
}
tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig()
buf, errBuf := bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{})
cmdutil.BehaviorOnFatal(func(str string, code int) { cmd := NewCmdApplySetLastApplied(f, buf, errBuf)
if str != test.expectedErr { cmd.Flags().Set("filename", test.filePath)
t.Errorf("%s: unexpected error: %s\nexpected: %s", test.name, str, test.expectedErr) cmd.Flags().Set("output", test.output)
cmd.Run(cmd, []string{})
if buf.String() != test.expectedOut {
t.Fatalf("%s: unexpected output: %s\nexpected: %s", test.name, buf.String(), test.expectedOut)
} }
}) })
cmd := NewCmdApplySetLastApplied(f, buf, errBuf)
cmd.Flags().Set("filename", test.filePath)
cmd.Flags().Set("output", test.output)
cmd.Run(cmd, []string{})
if buf.String() != test.expectedOut {
t.Fatalf("%s: unexpected output: %s\nexpected: %s", test.name, buf.String(), test.expectedOut)
}
} }
cmdutil.BehaviorOnFatal(func(str string, code int) {}) cmdutil.BehaviorOnFatal(func(str string, code int) {})
} }
......
...@@ -87,7 +87,8 @@ func (o *ViewLastAppliedOptions) Complete(f cmdutil.Factory, args []string) erro ...@@ -87,7 +87,8 @@ func (o *ViewLastAppliedOptions) Complete(f cmdutil.Factory, args []string) erro
return err return err
} }
r := f.NewUnstructuredBuilder(). r := f.NewBuilder().
Unstructured().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(enforceNamespace, args...). ResourceTypeOrNameArgs(enforceNamespace, args...).
......
...@@ -144,6 +144,7 @@ func (p *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn [ ...@@ -144,6 +144,7 @@ func (p *AttachOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, argsIn [
} }
builder := f.NewBuilder(). builder := f.NewBuilder().
Internal().
NamespaceParam(namespace).DefaultNamespace() NamespaceParam(namespace).DefaultNamespace()
switch len(argsIn) { switch len(argsIn) {
......
...@@ -94,11 +94,13 @@ func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args ...@@ -94,11 +94,13 @@ func (o *ReconcileOptions) Complete(cmd *cobra.Command, f cmdutil.Factory, args
} }
r := f.NewBuilder(). r := f.NewBuilder().
Internal().
ContinueOnError(). ContinueOnError().
NamespaceParam(namespace).DefaultNamespace(). NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, options). FilenameParam(enforceNamespace, options).
Flatten(). Flatten().
Do() Do()
if err := r.Err(); err != nil { if err := r.Err(); err != nil {
return err return err
} }
......
...@@ -91,6 +91,7 @@ func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s ...@@ -91,6 +91,7 @@ func RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
} }
r := f.NewBuilder(). r := f.NewBuilder().
Internal().
ContinueOnError(). ContinueOnError().
NamespaceParam(namespace).DefaultNamespace(). NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, options). FilenameParam(enforceNamespace, options).
......
...@@ -168,6 +168,7 @@ func (options *CertificateOptions) modifyCertificateCondition(f cmdutil.Factory, ...@@ -168,6 +168,7 @@ func (options *CertificateOptions) modifyCertificateCondition(f cmdutil.Factory,
return err return err
} }
r := f.NewBuilder(). r := f.NewBuilder().
Internal().
ContinueOnError(). ContinueOnError().
FilenameParam(false, &options.FilenameOptions). FilenameParam(false, &options.FilenameOptions).
ResourceNames("certificatesigningrequest", options.csrNames...). ResourceNames("certificatesigningrequest", options.csrNames...).
......
...@@ -73,6 +73,7 @@ func RunClusterInfo(f cmdutil.Factory, out io.Writer, cmd *cobra.Command) error ...@@ -73,6 +73,7 @@ func RunClusterInfo(f cmdutil.Factory, out io.Writer, cmd *cobra.Command) error
// TODO use generalized labels once they are implemented (#341) // TODO use generalized labels once they are implemented (#341)
b := f.NewBuilder(). b := f.NewBuilder().
Internal().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
LabelSelectorParam("kubernetes.io/cluster-service=true"). LabelSelectorParam("kubernetes.io/cluster-service=true").
ResourceTypeOrNameArgs(false, []string{"services"}...). ResourceTypeOrNameArgs(false, []string{"services"}...).
......
...@@ -128,22 +128,22 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.C ...@@ -128,22 +128,22 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.C
} }
// build the builder // build the builder
o.builder = f.NewBuilder() o.builder = f.NewBuilder().
if o.local { Internal().
o.builder = o.builder.Local(f.ClientForMapping) LocalParam(o.local)
} else { if !o.local {
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate")) schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
if err != nil { if err != nil {
return err return err
} }
o.builder = o.builder.Schema(schema) o.builder.Schema(schema)
} }
cmdNamespace, _, err := f.DefaultNamespace() cmdNamespace, _, err := f.DefaultNamespace()
if err != nil { if err != nil {
return err return err
} }
o.builder = o.builder.NamespaceParam(cmdNamespace). o.builder.NamespaceParam(cmdNamespace).
ContinueOnError(). ContinueOnError().
FilenameParam(false, &o.FilenameOptions). FilenameParam(false, &o.FilenameOptions).
Flatten() Flatten()
......
...@@ -18,6 +18,7 @@ package cmd ...@@ -18,6 +18,7 @@ package cmd
import ( import (
"bytes" "bytes"
"fmt"
"net/http" "net/http"
"testing" "testing"
...@@ -98,30 +99,30 @@ func TestConvertObject(t *testing.T) { ...@@ -98,30 +99,30 @@ func TestConvertObject(t *testing.T) {
}, },
} }
f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.UnstructuredClient = &fake.RESTClient{
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil
}),
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
for _, tc := range testcases { for _, tc := range testcases {
cmd := NewCmdConvert(f, buf)
cmd.Flags().Set("filename", tc.file)
cmd.Flags().Set("output-version", tc.outputVersion)
cmd.Flags().Set("local", "true")
cmd.Flags().Set("output", "go-template")
for _, field := range tc.fields { for _, field := range tc.fields {
buf.Reset() t.Run(fmt.Sprintf("%s %s", tc.name, field), func(t *testing.T) {
tf.Printer, _ = printers.NewTemplatePrinter([]byte(field.template)) f, tf, _, _ := cmdtesting.NewAPIFactory()
cmd.Run(cmd, []string{}) tf.UnstructuredClient = &fake.RESTClient{
if buf.String() != field.expected { Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
t.Errorf("unexpected output when converting %s to %q, expected: %q, but got %q", tc.file, tc.outputVersion, field.expected, buf.String()) t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
} return nil, nil
}),
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdConvert(f, buf)
cmd.Flags().Set("filename", tc.file)
cmd.Flags().Set("output-version", tc.outputVersion)
cmd.Flags().Set("local", "true")
cmd.Flags().Set("output", "go-template")
tf.Printer, _ = printers.NewTemplatePrinter([]byte(field.template))
cmd.Run(cmd, []string{})
if buf.String() != field.expected {
t.Errorf("unexpected output when converting %s to %q, expected: %q, but got %q", tc.file, tc.outputVersion, field.expected, buf.String())
}
})
} }
} }
} }
...@@ -184,12 +184,8 @@ func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opt ...@@ -184,12 +184,8 @@ func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opt
return err return err
} }
mapper, _, err := f.UnstructuredObject() r := f.NewBuilder().
if err != nil { Unstructured().
return err
}
r := f.NewUnstructuredBuilder().
Schema(schema). Schema(schema).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
...@@ -204,6 +200,7 @@ func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opt ...@@ -204,6 +200,7 @@ func RunCreate(f cmdutil.Factory, cmd *cobra.Command, out, errOut io.Writer, opt
dryRun := cmdutil.GetFlagBool(cmd, "dry-run") dryRun := cmdutil.GetFlagBool(cmd, "dry-run")
output := cmdutil.GetFlagString(cmd, "output") output := cmdutil.GetFlagString(cmd, "output")
mapper := r.Mapper().RESTMapper
count := 0 count := 0
err = r.Visit(func(info *resource.Info, err error) error { err = r.Visit(func(info *resource.Info, err error) error {
......
...@@ -444,13 +444,15 @@ func TestClusterRoleValidate(t *testing.T) { ...@@ -444,13 +444,15 @@ func TestClusterRoleValidate(t *testing.T) {
} }
for name, test := range tests { for name, test := range tests {
test.clusterRoleOptions.Mapper, _ = f.Object() t.Run(name, func(t *testing.T) {
err := test.clusterRoleOptions.Validate() test.clusterRoleOptions.Mapper, _ = f.Object()
if test.expectErr && err == nil { err := test.clusterRoleOptions.Validate()
t.Errorf("%s: expect error happens, but validate passes.", name) if test.expectErr && err == nil {
} t.Errorf("%s: expect error happens, but validate passes.", name)
if !test.expectErr && err != nil { }
t.Errorf("%s: unexpected error: %v", name, err) if !test.expectErr && err != nil {
} t.Errorf("%s: unexpected error: %v", name, err)
}
})
} }
} }
...@@ -25,6 +25,7 @@ import ( ...@@ -25,6 +25,7 @@ import (
rbac "k8s.io/api/rbac/v1" rbac "k8s.io/api/rbac/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/client-go/rest/fake" "k8s.io/client-go/rest/fake"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
) )
...@@ -143,19 +144,21 @@ func TestCreateRole(t *testing.T) { ...@@ -143,19 +144,21 @@ func TestCreateRole(t *testing.T) {
} }
for name, test := range tests { for name, test := range tests {
buf := bytes.NewBuffer([]byte{}) t.Run(name, func(t *testing.T) {
cmd := NewCmdCreateRole(f, buf) buf := bytes.NewBuffer([]byte{})
cmd.Flags().Set("dry-run", "true") cmd := NewCmdCreateRole(f, buf)
cmd.Flags().Set("output", "object") cmd.Flags().Set("dry-run", "true")
cmd.Flags().Set("verb", test.verbs) cmd.Flags().Set("output", "object")
cmd.Flags().Set("resource", test.resources) cmd.Flags().Set("verb", test.verbs)
if test.resourceNames != "" { cmd.Flags().Set("resource", test.resources)
cmd.Flags().Set("resource-name", test.resourceNames) if test.resourceNames != "" {
} cmd.Flags().Set("resource-name", test.resourceNames)
cmd.Run(cmd, []string{roleName}) }
if !reflect.DeepEqual(test.expectedRole, printer.CachedRole) { cmd.Run(cmd, []string{roleName})
t.Errorf("%s:\nexpected:\n%#v\nsaw:\n%#v", name, test.expectedRole, printer.CachedRole) if !reflect.DeepEqual(test.expectedRole, printer.CachedRole) {
} t.Errorf("%s", diff.ObjectReflectDiff(test.expectedRole, printer.CachedRole))
}
})
} }
} }
......
...@@ -169,15 +169,9 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args ...@@ -169,15 +169,9 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args
return err return err
} }
// Set up client based support.
mapper, _, err := f.UnstructuredObject()
if err != nil {
return err
}
o.Mapper = mapper
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
r := f.NewUnstructuredBuilder(). r := f.NewBuilder().
Unstructured().
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(enforceNamespace, &o.FilenameOptions).
...@@ -192,6 +186,7 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args ...@@ -192,6 +186,7 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args
return err return err
} }
o.Result = r o.Result = r
o.Mapper = r.Mapper().RESTMapper
o.f = f o.f = f
// Set up writer // Set up writer
......
...@@ -24,7 +24,6 @@ import ( ...@@ -24,7 +24,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
...@@ -119,7 +118,8 @@ func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, a ...@@ -119,7 +118,8 @@ func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, a
// include the uninitialized objects by default // include the uninitialized objects by default
// unless user explicitly set --include-uninitialized=false // unless user explicitly set --include-uninitialized=false
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, true) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, true)
r := f.NewUnstructuredBuilder(). r := f.NewBuilder().
Unstructured().
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces). NamespaceParam(cmdNamespace).DefaultNamespace().AllNamespaces(allNamespaces).
FilenameParam(enforceNamespace, options). FilenameParam(enforceNamespace, options).
...@@ -176,11 +176,8 @@ func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, a ...@@ -176,11 +176,8 @@ func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, a
} }
func DescribeMatchingResources(f cmdutil.Factory, namespace, rsrc, prefix string, describerSettings *printers.DescriberSettings, out io.Writer, originalError error) error { func DescribeMatchingResources(f cmdutil.Factory, namespace, rsrc, prefix string, describerSettings *printers.DescriberSettings, out io.Writer, originalError error) error {
mapper, typer, err := f.UnstructuredObject() r := f.NewBuilder().
if err != nil { Unstructured().
return err
}
r := resource.NewBuilder(mapper, f.CategoryExpander(), typer, resource.ClientMapperFunc(f.UnstructuredClientForMapping), unstructured.UnstructuredJSONScheme).
NamespaceParam(namespace).DefaultNamespace(). NamespaceParam(namespace).DefaultNamespace().
ResourceTypeOrNameArgs(true, rsrc). ResourceTypeOrNameArgs(true, rsrc).
SingleResourceType(). SingleResourceType().
......
...@@ -280,7 +280,7 @@ func (obj InfoObject) toMap(data []byte) (map[string]interface{}, error) { ...@@ -280,7 +280,7 @@ func (obj InfoObject) toMap(data []byte) (map[string]interface{}, error) {
} }
func (obj InfoObject) Local() (map[string]interface{}, error) { func (obj InfoObject) Local() (map[string]interface{}, error) {
data, err := runtime.Encode(obj.Encoder, obj.Info.VersionedObject) data, err := runtime.Encode(obj.Encoder, obj.Info.Object)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -413,13 +413,13 @@ func RunDiff(f cmdutil.Factory, diff *DiffProgram, options *DiffOptions, from, t ...@@ -413,13 +413,13 @@ func RunDiff(f cmdutil.Factory, diff *DiffProgram, options *DiffOptions, from, t
return err return err
} }
r := f.NewUnstructuredBuilder(). r := f.NewBuilder().
Unstructured().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &options.FilenameOptions). FilenameParam(enforceNamespace, &options.FilenameOptions).
Flatten(). Flatten().
Do() Do()
err = r.Err() if err := r.Err(); err != nil {
if err != nil {
return err return err
} }
......
...@@ -237,6 +237,7 @@ func (o *DrainOptions) SetupDrain(cmd *cobra.Command, args []string) error { ...@@ -237,6 +237,7 @@ func (o *DrainOptions) SetupDrain(cmd *cobra.Command, args []string) error {
} }
builder := o.Factory.NewBuilder(). builder := o.Factory.NewBuilder().
Internal().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
ResourceNames("nodes", args...). ResourceNames("nodes", args...).
SingleResourceType(). SingleResourceType().
......
...@@ -127,6 +127,7 @@ func RunExpose(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri ...@@ -127,6 +127,7 @@ func RunExpose(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []stri
mapper, typer := f.Object() mapper, typer := f.Object()
r := f.NewBuilder(). r := f.NewBuilder().
Internal().
ContinueOnError(). ContinueOnError().
NamespaceParam(namespace).DefaultNamespace(). NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, options). FilenameParam(enforceNamespace, options).
......
...@@ -191,25 +191,23 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error { ...@@ -191,25 +191,23 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error {
changeCause := f.Command(cmd, false) changeCause := f.Command(cmd, false)
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
var b *resource.Builder b := f.NewBuilder().
if o.local { Unstructured().
b = f.NewBuilder(). LocalParam(o.local).
Local(f.ClientForMapping) ContinueOnError().
} else { NamespaceParam(cmdNamespace).DefaultNamespace().
b = f.NewUnstructuredBuilder(). FilenameParam(enforceNamespace, &o.FilenameOptions).
LabelSelectorParam(o.selector). IncludeUninitialized(includeUninitialized).
Flatten()
if !o.local {
b = b.LabelSelectorParam(o.selector).
ResourceTypeOrNameArgs(o.all, o.resources...). ResourceTypeOrNameArgs(o.all, o.resources...).
Latest() Latest()
} }
one := false one := false
r := b.ContinueOnError(). r := b.Do().IntoSingleItemImplied(&one)
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(includeUninitialized).
Flatten().
Do().
IntoSingleItemImplied(&one)
if err := r.Err(); err != nil { if err := r.Err(); err != nil {
return err return err
} }
...@@ -302,19 +300,10 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error { ...@@ -302,19 +300,10 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error {
return nil return nil
} }
var mapper meta.RESTMapper
if o.local {
mapper, _ = f.Object()
} else {
mapper, _, err = f.UnstructuredObject()
if err != nil {
return err
}
}
if o.outputFormat != "" { if o.outputFormat != "" {
return f.PrintObject(cmd, o.local, mapper, outputObj, o.out) return f.PrintObject(cmd, o.local, r.Mapper().RESTMapper, outputObj, o.out)
} }
f.PrintSuccess(mapper, false, o.out, info.Mapping.Resource, info.Name, o.dryrun, dataChangeMsg) f.PrintSuccess(r.Mapper().RESTMapper, false, o.out, info.Mapping.Resource, info.Name, o.dryrun, dataChangeMsg)
return nil return nil
}) })
} }
......
...@@ -193,6 +193,7 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Comm ...@@ -193,6 +193,7 @@ func (o *LogsOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Comm
if o.Object == nil { if o.Object == nil {
builder := f.NewBuilder(). builder := f.NewBuilder().
Internal().
NamespaceParam(o.Namespace).DefaultNamespace(). NamespaceParam(o.Namespace).DefaultNamespace().
SingleResourceType() SingleResourceType()
if o.ResourceArg != "" { if o.ResourceArg != "" {
......
...@@ -153,7 +153,8 @@ func RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin ...@@ -153,7 +153,8 @@ func RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin
return fmt.Errorf("unable to parse %q: %v", patch, err) return fmt.Errorf("unable to parse %q: %v", patch, err)
} }
r := f.NewUnstructuredBuilder(). r := f.NewBuilder().
Unstructured().
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &options.FilenameOptions). FilenameParam(enforceNamespace, &options.FilenameOptions).
...@@ -190,7 +191,6 @@ func RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin ...@@ -190,7 +191,6 @@ func RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin
// Copy the resource info and update with the result of applying the user's patch // Copy the resource info and update with the result of applying the user's patch
infoCopy := *info infoCopy := *info
infoCopy.Object = patchedObj infoCopy.Object = patchedObj
infoCopy.VersionedObject = patchedObj
if patch, patchType, err := cmdutil.ChangeResourcePatch(&infoCopy, f.Command(cmd, true)); err == nil { if patch, patchType, err := cmdutil.ChangeResourcePatch(&infoCopy, f.Command(cmd, true)); err == nil {
if recordedObj, err := helper.Patch(info.Namespace, info.Name, patchType, patch); err != nil { if recordedObj, err := helper.Patch(info.Namespace, info.Name, patchType, patch); err != nil {
glog.V(4).Infof("error recording reason: %v", err) glog.V(4).Infof("error recording reason: %v", err)
...@@ -222,11 +222,7 @@ func RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin ...@@ -222,11 +222,7 @@ func RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin
if len(options.OutputFormat) > 0 && options.OutputFormat != "name" { if len(options.OutputFormat) > 0 && options.OutputFormat != "name" {
return f.PrintResourceInfoForCommand(cmd, info, out) return f.PrintResourceInfoForCommand(cmd, info, out)
} }
mapper, _, err := f.UnstructuredObject() f.PrintSuccess(r.Mapper().RESTMapper, options.OutputFormat == "name", out, info.Mapping.Resource, info.Name, false, dataChangedMsg)
if err != nil {
return err
}
f.PrintSuccess(mapper, options.OutputFormat == "name", out, info.Mapping.Resource, info.Name, false, dataChangedMsg)
// if object was not successfully patched, exit with error code 1 // if object was not successfully patched, exit with error code 1
if !didPatch { if !didPatch {
...@@ -238,7 +234,7 @@ func RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin ...@@ -238,7 +234,7 @@ func RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin
count++ count++
originalObjJS, err := runtime.Encode(unstructured.UnstructuredJSONScheme, info.VersionedObject) originalObjJS, err := runtime.Encode(unstructured.UnstructuredJSONScheme, info.Object)
if err != nil { if err != nil {
return err return err
} }
......
...@@ -27,7 +27,6 @@ import ( ...@@ -27,7 +27,6 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/wait" "k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
...@@ -120,23 +119,20 @@ func RunReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str ...@@ -120,23 +119,20 @@ func RunReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
return fmt.Errorf("--timeout must have --force specified") return fmt.Errorf("--timeout must have --force specified")
} }
mapper, _, err := f.UnstructuredObject() r := f.NewBuilder().
if err != nil { Unstructured().
return err
}
r := f.NewUnstructuredBuilder().
Schema(schema). Schema(schema).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options). FilenameParam(enforceNamespace, options).
Flatten(). Flatten().
Do() Do()
err = r.Err() if err := r.Err(); err != nil {
if err != nil {
return err return err
} }
mapper := r.Mapper().RESTMapper
return r.Visit(func(info *resource.Info, err error) error { return r.Visit(func(info *resource.Info, err error) error {
if err != nil { if err != nil {
return err return err
...@@ -192,21 +188,20 @@ func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s ...@@ -192,21 +188,20 @@ func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
} }
} }
mapper, typer, err := f.UnstructuredObject() r := f.NewBuilder().
if err != nil { Unstructured().
return err
}
r := resource.NewBuilder(mapper, f.CategoryExpander(), typer, resource.ClientMapperFunc(f.UnstructuredClientForMapping), unstructured.UnstructuredJSONScheme).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options). FilenameParam(enforceNamespace, options).
ResourceTypeOrNameArgs(false, args...).RequireObject(false). ResourceTypeOrNameArgs(false, args...).RequireObject(false).
Flatten(). Flatten().
Do() Do()
err = r.Err() if err := r.Err(); err != nil {
if err != nil {
return err return err
} }
mapper := r.Mapper().RESTMapper
//Replace will create a resource if it doesn't exist already, so ignore not found error //Replace will create a resource if it doesn't exist already, so ignore not found error
ignoreNotFound := true ignoreNotFound := true
timeout := cmdutil.GetFlagDuration(cmd, "timeout") timeout := cmdutil.GetFlagDuration(cmd, "timeout")
...@@ -248,7 +243,8 @@ func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s ...@@ -248,7 +243,8 @@ func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
return err return err
} }
r = f.NewUnstructuredBuilder(). r = f.NewBuilder().
Unstructured().
Schema(schema). Schema(schema).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
......
...@@ -56,6 +56,7 @@ go_test( ...@@ -56,6 +56,7 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library",
"//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/dynamic:go_default_library",
"//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library",
......
...@@ -232,7 +232,8 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str ...@@ -232,7 +232,8 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
return options.watch(f, cmd, args) return options.watch(f, cmd, args)
} }
r := f.NewUnstructuredBuilder(). r := f.NewBuilder().
Unstructured().
NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces). NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
FilenameParam(options.ExplicitNamespace, &options.FilenameOptions). FilenameParam(options.ExplicitNamespace, &options.FilenameOptions).
LabelSelectorParam(options.LabelSelector). LabelSelectorParam(options.LabelSelector).
...@@ -309,13 +310,15 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str ...@@ -309,13 +310,15 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
for ix := range objs { for ix := range objs {
var mapping *meta.RESTMapping var mapping *meta.RESTMapping
var original runtime.Object var original runtime.Object
var info *resource.Info
if sorter != nil { if sorter != nil {
mapping = infos[sorter.OriginalPosition(ix)].Mapping info = infos[sorter.OriginalPosition(ix)]
original = infos[sorter.OriginalPosition(ix)].Object mapping = info.Mapping
original = info.Object
} else { } else {
mapping = infos[ix].Mapping info = infos[ix]
original = infos[ix].Object mapping = info.Mapping
original = info.Object
} }
if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) { if shouldGetNewPrinterForMapping(printer, lastMapping, mapping) {
if printer != nil { if printer != nil {
...@@ -354,11 +357,10 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str ...@@ -354,11 +357,10 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
lastMapping = mapping lastMapping = mapping
} }
// try to convert before apply filter func typedObj := info.AsInternal()
decodedObj, _ := kubectl.DecodeUnknownObject(original)
// filter objects if filter has been defined for current object // filter objects if filter has been defined for current object
if isFiltered, err := filterFuncs.Filter(decodedObj, filterOpts); isFiltered { if isFiltered, err := filterFuncs.Filter(typedObj, filterOpts); isFiltered {
if err == nil { if err == nil {
filteredResourceCount++ filteredResourceCount++
continue continue
...@@ -388,7 +390,7 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str ...@@ -388,7 +390,7 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
resourcePrinter.EnsurePrintWithKind(resourceName) resourcePrinter.EnsurePrintWithKind(resourceName)
} }
if err := printer.PrintObj(decodedObj, w); err != nil { if err := printer.PrintObj(typedObj, w); err != nil {
if !errs.Has(err.Error()) { if !errs.Has(err.Error()) {
errs.Insert(err.Error()) errs.Insert(err.Error())
allErrs = append(allErrs, err) allErrs = append(allErrs, err)
...@@ -396,7 +398,7 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str ...@@ -396,7 +398,7 @@ func (options *GetOptions) Run(f cmdutil.Factory, cmd *cobra.Command, args []str
} }
continue continue
} }
objToPrint := decodedObj objToPrint := typedObj
if printer.IsGeneric() { if printer.IsGeneric() {
// use raw object as recieved from the builder when using generic // use raw object as recieved from the builder when using generic
// printer instead of decodedObj // printer instead of decodedObj
...@@ -444,7 +446,8 @@ func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []s ...@@ -444,7 +446,8 @@ func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []s
// unless explicitly set --include-uninitialized=false // unless explicitly set --include-uninitialized=false
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, len(args) == 2) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, len(args) == 2)
r := f.NewUnstructuredBuilder(). r := f.NewBuilder().
Unstructured().
NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces). NamespaceParam(options.Namespace).DefaultNamespace().AllNamespaces(options.AllNamespaces).
FilenameParam(options.ExplicitNamespace, &options.FilenameOptions). FilenameParam(options.ExplicitNamespace, &options.FilenameOptions).
LabelSelectorParam(options.LabelSelector). LabelSelectorParam(options.LabelSelector).
...@@ -456,8 +459,7 @@ func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []s ...@@ -456,8 +459,7 @@ func (options *GetOptions) watch(f cmdutil.Factory, cmd *cobra.Command, args []s
SingleResourceType(). SingleResourceType().
Latest(). Latest().
Do() Do()
err := r.Err() if err := r.Err(); err != nil {
if err != nil {
return err return err
} }
infos, err := r.Infos() infos, err := r.Infos()
......
...@@ -36,6 +36,7 @@ import ( ...@@ -36,6 +36,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/runtime/serializer/streaming" "k8s.io/apimachinery/pkg/runtime/serializer/streaming"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
"k8s.io/client-go/dynamic" "k8s.io/client-go/dynamic"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
...@@ -208,15 +209,46 @@ func testComponentStatusData() *api.ComponentStatusList { ...@@ -208,15 +209,46 @@ func testComponentStatusData() *api.ComponentStatusList {
// Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get. // Verifies that schemas that are not in the master tree of Kubernetes can be retrieved via Get.
func TestGetUnknownSchemaObject(t *testing.T) { func TestGetUnknownSchemaObject(t *testing.T) {
f, tf, _, _ := cmdtesting.NewAPIFactory() f, tf, _, _ := cmdtesting.NewAPIFactory()
tf.WithCustomScheme()
_, _, codec, _ := cmdtesting.NewTestFactory() _, _, codec, _ := cmdtesting.NewTestFactory()
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
tf.OpenAPISchemaFunc = openapitesting.CreateOpenAPISchemaFunc(openapiSchemaPath) tf.OpenAPISchemaFunc = openapitesting.CreateOpenAPISchemaFunc(openapiSchemaPath)
obj := &cmdtesting.ExternalType{
Kind: "Type",
APIVersion: "apitest/unlikelyversion",
Name: "foo",
}
tf.UnstructuredClient = &fake.RESTClient{ tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer, NegotiatedSerializer: unstructuredSerializer,
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, cmdtesting.NewInternalType("", "", "foo"))}, Resp: &http.Response{
StatusCode: 200, Header: defaultHeader(),
Body: objBody(codec, obj),
},
} }
tf.Namespace = "test" tf.Namespace = "test"
tf.ClientConfig = defaultClientConfig() tf.ClientConfig = defaultClientConfig()
mapper, _ := f.Object()
m, err := mapper.RESTMapping(schema.GroupKind{Group: "apitest", Kind: "Type"})
if err != nil {
t.Fatal(err)
}
convertedObj, err := m.ConvertToVersion(&unstructured.Unstructured{
Object: map[string]interface{}{
"kind": "Type",
"apiVersion": "apitest/unlikelyversion",
"name": "foo",
},
}, schema.GroupVersion{Group: "apitest", Version: "unlikelyversion"})
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(convertedObj, obj) {
t.Fatalf("unexpected conversion of unstructured object to structured: %s", diff.ObjectReflectDiff(convertedObj, obj))
}
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{}) errBuf := bytes.NewBuffer([]byte{})
...@@ -229,6 +261,7 @@ func TestGetUnknownSchemaObject(t *testing.T) { ...@@ -229,6 +261,7 @@ func TestGetUnknownSchemaObject(t *testing.T) {
if len(actual) != len(expected) { if len(actual) != len(expected) {
t.Fatalf("expected: %#v, but actual: %#v", expected, actual) t.Fatalf("expected: %#v, but actual: %#v", expected, actual)
} }
t.Logf("actual: %#v", actual[0])
for i, obj := range actual { for i, obj := range actual {
expectedJSON := runtime.EncodeOrDie(codec, expected[i]) expectedJSON := runtime.EncodeOrDie(codec, expected[i])
expectedMap := map[string]interface{}{} expectedMap := map[string]interface{}{}
...@@ -236,7 +269,7 @@ func TestGetUnknownSchemaObject(t *testing.T) { ...@@ -236,7 +269,7 @@ func TestGetUnknownSchemaObject(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
actualJSON := runtime.EncodeOrDie(scheme.Codecs.LegacyCodec(), obj) actualJSON := runtime.EncodeOrDie(codec, obj)
actualMap := map[string]interface{}{} actualMap := map[string]interface{}{}
if err := encjson.Unmarshal([]byte(actualJSON), &actualMap); err != nil { if err := encjson.Unmarshal([]byte(actualJSON), &actualMap); err != nil {
t.Fatal(err) t.Fatal(err)
...@@ -381,30 +414,32 @@ func TestGetObjectsFiltered(t *testing.T) { ...@@ -381,30 +414,32 @@ func TestGetObjectsFiltered(t *testing.T) {
} }
for i, test := range testCases { for i, test := range testCases {
t.Logf("%d", i) t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
f, tf, codec, _ := cmdtesting.NewAPIFactory() f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{GenericPrinter: test.genericPrinter} tf.WithLegacyScheme()
tf.UnstructuredClient = &fake.RESTClient{ tf.Printer = &testPrinter{GenericPrinter: test.genericPrinter}
GroupVersion: schema.GroupVersion{Version: "v1"}, tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer, GroupVersion: schema.GroupVersion{Version: "v1"},
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, test.resp)}, NegotiatedSerializer: unstructuredSerializer,
} Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, test.resp)},
tf.Namespace = "test" }
buf := bytes.NewBuffer([]byte{}) tf.Namespace = "test"
errBuf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdGet(f, buf, errBuf)
cmd.SetOutput(buf) cmd := NewCmdGet(f, buf, errBuf)
for k, v := range test.flags { cmd.SetOutput(buf)
cmd.Flags().Lookup(k).Value.Set(v) for k, v := range test.flags {
} cmd.Flags().Lookup(k).Value.Set(v)
cmd.Run(cmd, test.args) }
cmd.Run(cmd, test.args)
verifyObjects(t, test.expect, tf.Printer.(*testPrinter).Objects) verifyObjects(t, test.expect, tf.Printer.(*testPrinter).Objects)
if len(buf.String()) == 0 { if len(buf.String()) == 0 {
t.Errorf("%d: unexpected empty output", i) t.Errorf("%d: unexpected empty output", i)
} }
})
} }
} }
......
...@@ -192,7 +192,7 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args ...@@ -192,7 +192,7 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args
var keepOldName bool var keepOldName bool
var replicasDefaulted bool var replicasDefaulted bool
mapper, typer := f.Object() mapper, _ := f.Object()
if len(filename) != 0 { if len(filename) != 0 {
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate")) schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
...@@ -201,41 +201,35 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args ...@@ -201,41 +201,35 @@ func RunRollingUpdate(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args
} }
request := f.NewBuilder(). request := f.NewBuilder().
Unstructured().
Schema(schema). Schema(schema).
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &resource.FilenameOptions{Recursive: false, Filenames: []string{filename}}). FilenameParam(enforceNamespace, &resource.FilenameOptions{Recursive: false, Filenames: []string{filename}}).
Flatten().
Do() Do()
obj, err := request.Object() infos, err := request.Infos()
if err != nil { if err != nil {
return err return err
} }
var ok bool // Handle filename input from stdin.
// Handle filename input from stdin. The resource builder always returns an api.List if len(infos) > 1 {
// when creating resource(s) from a stream. return cmdutil.UsageErrorf(cmd, "%s specifies multiple items", filename)
if list, ok := obj.(*v1.List); ok {
if len(list.Items) > 1 {
return cmdutil.UsageErrorf(cmd, "%s specifies multiple items", filename)
}
if len(list.Items) == 0 {
return cmdutil.UsageErrorf(cmd, "please make sure %s exists and is not empty", filename)
}
obj = list.Items[0].Object
} }
newRc, ok = obj.(*api.ReplicationController) if len(infos) == 0 {
if !ok { return cmdutil.UsageErrorf(cmd, "please make sure %s exists and is not empty", filename)
if gvks, _, err := typer.ObjectKinds(obj); err == nil {
return cmdutil.UsageErrorf(cmd, "%s contains a %v not a ReplicationController", filename, gvks[0])
}
glog.V(4).Infof("Object %#v is not a ReplicationController", obj)
return cmdutil.UsageErrorf(cmd, "%s does not specify a valid ReplicationController", filename)
} }
infos, err := request.Infos()
if err != nil || len(infos) != 1 { switch t := infos[0].AsVersioned().(type) {
glog.V(2).Infof("was not able to recover adequate information to discover if .spec.replicas was defaulted") case *v1.ReplicationController:
} else { replicasDefaulted = t.Spec.Replicas == nil
replicasDefaulted = isReplicasDefaulted(infos[0]) newRc, _ = infos[0].AsInternal().(*api.ReplicationController)
}
if newRc == nil {
glog.V(4).Infof("Object %T is not a ReplicationController", infos[0].Object)
return cmdutil.UsageErrorf(cmd, "%s contains a %v not a ReplicationController", filename, infos[0].Object.GetObjectKind().GroupVersionKind())
} }
} }
// If the --image option is specified, we need to create a new rc with at least one different selector // If the --image option is specified, we need to create a new rc with at least one different selector
// than the old rc. This selector is the hash of the rc, with a suffix to provide uniqueness for // than the old rc. This selector is the hash of the rc, with a suffix to provide uniqueness for
// same-image updates. // same-image updates.
...@@ -393,15 +387,3 @@ func findNewName(args []string, oldRc *api.ReplicationController) string { ...@@ -393,15 +387,3 @@ func findNewName(args []string, oldRc *api.ReplicationController) string {
} }
return "" return ""
} }
func isReplicasDefaulted(info *resource.Info) bool {
if info == nil || info.VersionedObject == nil {
// was unable to recover versioned info
return false
}
switch t := info.VersionedObject.(type) {
case *v1.ReplicationController:
return t.Spec.Replicas == nil
}
return false
}
...@@ -80,6 +80,7 @@ func RunHistory(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []str ...@@ -80,6 +80,7 @@ func RunHistory(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []str
} }
r := f.NewBuilder(). r := f.NewBuilder().
Internal().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options). FilenameParam(enforceNamespace, options).
ResourceTypeOrNameArgs(true, args...). ResourceTypeOrNameArgs(true, args...).
......
...@@ -114,6 +114,7 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, out i ...@@ -114,6 +114,7 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, out i
} }
r := f.NewBuilder(). r := f.NewBuilder().
Internal().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(true, args...). ResourceTypeOrNameArgs(true, args...).
......
...@@ -112,6 +112,7 @@ func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, out ...@@ -112,6 +112,7 @@ func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, out
} }
r := f.NewBuilder(). r := f.NewBuilder().
Internal().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(true, args...). ResourceTypeOrNameArgs(true, args...).
......
...@@ -83,6 +83,7 @@ func RunStatus(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []stri ...@@ -83,6 +83,7 @@ func RunStatus(f cmdutil.Factory, cmd *cobra.Command, out io.Writer, args []stri
} }
r := f.NewBuilder(). r := f.NewBuilder().
Internal().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options). FilenameParam(enforceNamespace, options).
ResourceTypeOrNameArgs(true, args...). ResourceTypeOrNameArgs(true, args...).
......
...@@ -113,6 +113,7 @@ func (o *UndoOptions) CompleteUndo(f cmdutil.Factory, cmd *cobra.Command, out io ...@@ -113,6 +113,7 @@ func (o *UndoOptions) CompleteUndo(f cmdutil.Factory, cmd *cobra.Command, out io
} }
r := f.NewBuilder(). r := f.NewBuilder().
Internal().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(true, args...). ResourceTypeOrNameArgs(true, args...).
......
...@@ -354,6 +354,7 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c ...@@ -354,6 +354,7 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
return err return err
} }
r := f.NewBuilder(). r := f.NewBuilder().
Internal().
ContinueOnError(). ContinueOnError().
NamespaceParam(namespace).DefaultNamespace(). NamespaceParam(namespace).DefaultNamespace().
ResourceNames(obj.Mapping.Resource, name). ResourceNames(obj.Mapping.Resource, name).
......
...@@ -105,6 +105,7 @@ func RunScale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin ...@@ -105,6 +105,7 @@ func RunScale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []strin
mapper, _ := f.Object() mapper, _ := f.Object()
r := f.NewBuilder(). r := f.NewBuilder().
Internal().
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options). FilenameParam(enforceNamespace, options).
......
...@@ -19,7 +19,6 @@ go_library( ...@@ -19,7 +19,6 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/set", importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/set",
visibility = ["//build/visible_to:pkg_kubectl_cmd_set_CONSUMERS"], visibility = ["//build/visible_to:pkg_kubectl_cmd_set_CONSUMERS"],
deps = [ deps = [
"//pkg/api/legacyscheme:go_default_library",
"//pkg/apis/rbac:go_default_library", "//pkg/apis/rbac:go_default_library",
"//pkg/kubectl:go_default_library", "//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/templates:go_default_library", "//pkg/kubectl/cmd/templates:go_default_library",
......
...@@ -126,14 +126,7 @@ type patchFn func(*resource.Info) ([]byte, error) ...@@ -126,14 +126,7 @@ type patchFn func(*resource.Info) ([]byte, error)
// the changes in the object. Encoder must be able to encode the info into the appropriate destination type. // the changes in the object. Encoder must be able to encode the info into the appropriate destination type.
// This function returns whether the mutation function made any change in the original object. // This function returns whether the mutation function made any change in the original object.
func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) bool { func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) bool {
versioned, err := patch.Info.Mapping.ConvertToVersion(patch.Info.Object, patch.Info.Mapping.GroupVersionKind.GroupVersion()) patch.Before, patch.Err = runtime.Encode(encoder, patch.Info.Object)
if err != nil {
patch.Err = err
return true
}
patch.Info.VersionedObject = versioned
patch.Before, patch.Err = runtime.Encode(encoder, patch.Info.VersionedObject)
patch.After, patch.Err = mutateFn(patch.Info) patch.After, patch.Err = mutateFn(patch.Info)
if patch.Err != nil { if patch.Err != nil {
return true return true
...@@ -142,7 +135,7 @@ func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) boo ...@@ -142,7 +135,7 @@ func CalculatePatch(patch *Patch, encoder runtime.Encoder, mutateFn patchFn) boo
return false return false
} }
patch.Patch, patch.Err = strategicpatch.CreateTwoWayMergePatch(patch.Before, patch.After, patch.Info.VersionedObject) patch.Patch, patch.Err = strategicpatch.CreateTwoWayMergePatch(patch.Before, patch.After, patch.Info.Object)
return true return true
} }
......
...@@ -236,14 +236,14 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error { ...@@ -236,14 +236,14 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
if len(o.From) != 0 { if len(o.From) != 0 {
b := f.NewBuilder(). b := f.NewBuilder().
Internal().
LocalParam(o.Local).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(enforceNamespace, &o.FilenameOptions).
Flatten() Flatten()
if o.Local { if !o.Local {
b = b.Local(f.ClientForMapping)
} else {
b = b. b = b.
LabelSelectorParam(o.Selector). LabelSelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, o.From). ResourceTypeOrNameArgs(o.All, o.From).
...@@ -304,16 +304,15 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error { ...@@ -304,16 +304,15 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
} }
b := f.NewBuilder(). b := f.NewBuilder().
Internal().
LocalParam(o.Local).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(enforceNamespace, &o.FilenameOptions).
Flatten() Flatten()
if o.Local { if !o.Local {
b = b.Local(f.ClientForMapping) b.LabelSelectorParam(o.Selector).
} else {
b = b.
LabelSelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, o.Resources...). ResourceTypeOrNameArgs(o.All, o.Resources...).
Latest() Latest()
} }
...@@ -323,7 +322,8 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error { ...@@ -323,7 +322,8 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
return err return err
} }
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) { patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) {
_, err := o.UpdatePodSpecForObject(info.VersionedObject, func(spec *v1.PodSpec) error { info.Object = info.AsVersioned()
_, err := o.UpdatePodSpecForObject(info.Object, func(spec *v1.PodSpec) error {
resolutionErrorsEncountered := false resolutionErrorsEncountered := false
containers, _ := selectContainers(spec.Containers, o.ContainerSelector) containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
if len(containers) == 0 { if len(containers) == 0 {
...@@ -389,7 +389,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error { ...@@ -389,7 +389,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
}) })
if err == nil { if err == nil {
return runtime.Encode(o.Encoder, info.VersionedObject) return runtime.Encode(o.Encoder, info.Object)
} }
return nil, err return nil, err
}) })
...@@ -413,7 +413,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error { ...@@ -413,7 +413,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
} }
if o.PrintObject != nil && (o.Local || o.DryRun) { if o.PrintObject != nil && (o.Local || o.DryRun) {
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, patch.Info.VersionedObject, o.Out); err != nil { if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, patch.Info.AsVersioned(), o.Out); err != nil {
return err return err
} }
continue continue
...@@ -433,11 +433,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error { ...@@ -433,11 +433,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
} }
if len(o.Output) > 0 { if len(o.Output) > 0 {
versionedObject, err := patch.Info.Mapping.ConvertToVersion(obj, patch.Info.Mapping.GroupVersionKind.GroupVersion()) if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, info.AsVersioned(), o.Out); err != nil {
if err != nil {
return err
}
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, versionedObject, o.Out); err != nil {
return err return err
} }
continue continue
......
...@@ -143,26 +143,25 @@ func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st ...@@ -143,26 +143,25 @@ func (o *ImageOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
builder := f.NewBuilder(). builder := f.NewBuilder().
Internal().
LocalParam(o.Local).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(includeUninitialized). IncludeUninitialized(includeUninitialized).
Flatten() Flatten()
if o.Local { if !o.Local {
builder.LabelSelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, o.Resources...).
Latest()
} else {
// if a --local flag was provided, and a resource was specified in the form // if a --local flag was provided, and a resource was specified in the form
// <resource>/<name>, fail immediately as --local cannot query the api server // <resource>/<name>, fail immediately as --local cannot query the api server
// for the specified resource. // for the specified resource.
if len(o.Resources) > 0 { if len(o.Resources) > 0 {
return resource.LocalResourceError return resource.LocalResourceError
} }
builder = builder.Local(f.ClientForMapping)
} else {
builder = builder.
LabelSelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, o.Resources...).
Latest()
} }
o.Infos, err = builder.Do().Infos() o.Infos, err = builder.Do().Infos()
...@@ -191,7 +190,8 @@ func (o *ImageOptions) Run() error { ...@@ -191,7 +190,8 @@ func (o *ImageOptions) Run() error {
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) { patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) {
transformed := false transformed := false
_, err := o.UpdatePodSpecForObject(info.VersionedObject, func(spec *v1.PodSpec) error { info.Object = info.AsVersioned()
_, err := o.UpdatePodSpecForObject(info.Object, func(spec *v1.PodSpec) error {
for name, image := range o.ContainerImages { for name, image := range o.ContainerImages {
var ( var (
containerFound bool containerFound bool
...@@ -228,7 +228,7 @@ func (o *ImageOptions) Run() error { ...@@ -228,7 +228,7 @@ func (o *ImageOptions) Run() error {
return nil return nil
}) })
if transformed && err == nil { if transformed && err == nil {
return runtime.Encode(o.Encoder, info.VersionedObject) return runtime.Encode(o.Encoder, info.Object)
} }
return nil, err return nil, err
}) })
...@@ -246,7 +246,7 @@ func (o *ImageOptions) Run() error { ...@@ -246,7 +246,7 @@ func (o *ImageOptions) Run() error {
} }
if o.PrintObject != nil && (o.Local || o.DryRun) { if o.PrintObject != nil && (o.Local || o.DryRun) {
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, patch.Info.VersionedObject, o.Out); err != nil { if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, patch.Info.AsVersioned(), o.Out); err != nil {
return err return err
} }
continue continue
...@@ -272,11 +272,7 @@ func (o *ImageOptions) Run() error { ...@@ -272,11 +272,7 @@ func (o *ImageOptions) Run() error {
info.Refresh(obj, true) info.Refresh(obj, true)
if len(o.Output) > 0 { if len(o.Output) > 0 {
versionedObject, err := patch.Info.Mapping.ConvertToVersion(obj, patch.Info.Mapping.GroupVersionKind.GroupVersion()) if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, info.AsVersioned(), o.Out); err != nil {
if err != nil {
return err
}
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, versionedObject, o.Out); err != nil {
return err return err
} }
continue continue
......
...@@ -145,26 +145,27 @@ func (o *ResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args ...@@ -145,26 +145,27 @@ func (o *ResourcesOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
builder := f.NewBuilder(). builder := f.NewBuilder().
Internal().
LocalParam(o.Local).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(includeUninitialized). IncludeUninitialized(includeUninitialized).
Flatten() Flatten()
if o.Local { if !o.Local {
builder.LabelSelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, args...).
Latest()
} else {
// if a --local flag was provided, and a resource was specified in the form // if a --local flag was provided, and a resource was specified in the form
// <resource>/<name>, fail immediately as --local cannot query the api server // <resource>/<name>, fail immediately as --local cannot query the api server
// for the specified resource. // for the specified resource.
// TODO: this should be in the builder - if someone specifies tuples, fail when
// local is true
if len(args) > 0 { if len(args) > 0 {
return resource.LocalResourceError return resource.LocalResourceError
} }
builder = builder.Local(f.ClientForMapping)
} else {
builder = builder.
LabelSelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, args...).
Latest()
} }
o.Infos, err = builder.Do().Infos() o.Infos, err = builder.Do().Infos()
...@@ -192,7 +193,8 @@ func (o *ResourcesOptions) Run() error { ...@@ -192,7 +193,8 @@ func (o *ResourcesOptions) Run() error {
allErrs := []error{} allErrs := []error{}
patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) { patches := CalculatePatches(o.Infos, o.Encoder, func(info *resource.Info) ([]byte, error) {
transformed := false transformed := false
_, err := o.UpdatePodSpecForObject(info.VersionedObject, func(spec *v1.PodSpec) error { info.Object = info.AsVersioned()
_, err := o.UpdatePodSpecForObject(info.Object, func(spec *v1.PodSpec) error {
containers, _ := selectContainers(spec.Containers, o.ContainerSelector) containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
if len(containers) != 0 { if len(containers) != 0 {
for i := range containers { for i := range containers {
...@@ -217,7 +219,7 @@ func (o *ResourcesOptions) Run() error { ...@@ -217,7 +219,7 @@ func (o *ResourcesOptions) Run() error {
return nil return nil
}) })
if transformed && err == nil { if transformed && err == nil {
return runtime.Encode(o.Encoder, info.VersionedObject) return runtime.Encode(o.Encoder, info.Object)
} }
return nil, err return nil, err
}) })
...@@ -236,7 +238,7 @@ func (o *ResourcesOptions) Run() error { ...@@ -236,7 +238,7 @@ func (o *ResourcesOptions) Run() error {
} }
if o.Local || cmdutil.GetDryRunFlag(o.Cmd) { if o.Local || cmdutil.GetDryRunFlag(o.Cmd) {
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, patch.Info.VersionedObject, o.Out); err != nil { if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, patch.Info.AsVersioned(), o.Out); err != nil {
return err return err
} }
continue continue
...@@ -261,11 +263,7 @@ func (o *ResourcesOptions) Run() error { ...@@ -261,11 +263,7 @@ func (o *ResourcesOptions) Run() error {
shortOutput := o.Output == "name" shortOutput := o.Output == "name"
if len(o.Output) > 0 && !shortOutput { if len(o.Output) > 0 && !shortOutput {
versionedObject, err := patch.Info.Mapping.ConvertToVersion(obj, patch.Info.Mapping.GroupVersionKind.GroupVersion()) if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, info.AsVersioned(), o.Out); err != nil {
if err != nil {
return err
}
if err := o.PrintObject(o.Cmd, o.Local, o.Mapper, versionedObject, o.Out); err != nil {
return err return err
} }
continue continue
......
...@@ -442,57 +442,59 @@ func TestSetResourcesRemote(t *testing.T) { ...@@ -442,57 +442,59 @@ func TestSetResourcesRemote(t *testing.T) {
args: []string{"replicationcontroller", "nginx"}, args: []string{"replicationcontroller", "nginx"},
}, },
} }
for _, input := range inputs { for i, input := range inputs {
groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion} t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
testapi.Default = testapi.Groups[input.testAPIGroup] groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion}
f, tf, _, ns := cmdtesting.NewAPIFactory() testapi.Default = testapi.Groups[input.testAPIGroup]
codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion) f, tf, _, ns := cmdtesting.NewAPIFactory()
mapper, typer := f.Object() codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion)
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{testapi.Default.Codec()}, Typer: typer, Mapper: mapper} mapper, typer := f.Object()
tf.Namespace = "test" tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{testapi.Default.Codec()}, Typer: typer, Mapper: mapper}
tf.CategoryExpander = categories.LegacyCategoryExpander tf.Namespace = "test"
tf.Client = &fake.RESTClient{ tf.CategoryExpander = categories.LegacyCategoryExpander
GroupVersion: groupVersion, tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns, GroupVersion: groupVersion,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { NegotiatedSerializer: ns,
resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1]) Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; { resourcePath := testapi.Default.ResourcePath(input.args[0]+"s", tf.Namespace, input.args[1])
case p == resourcePath && m == http.MethodGet: switch p, m := req.URL.Path, req.Method; {
return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, input.object)}, nil case p == resourcePath && m == http.MethodGet:
case p == resourcePath && m == http.MethodPatch: return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, input.object)}, nil
stream, err := req.GetBody() case p == resourcePath && m == http.MethodPatch:
if err != nil { stream, err := req.GetBody()
return nil, err if err != nil {
return nil, err
}
bytes, err := ioutil.ReadAll(stream)
if err != nil {
return nil, err
}
assert.Contains(t, string(bytes), "200m", fmt.Sprintf("resources not updated for %#v", input.object))
return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, input.object)}, nil
default:
t.Errorf("%s: unexpected request: %s %#v\n%#v", "resources", req.Method, req.URL, req)
return nil, fmt.Errorf("unexpected request")
} }
bytes, err := ioutil.ReadAll(stream) }),
if err != nil { VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()),
return nil, err }
} buf := new(bytes.Buffer)
assert.Contains(t, string(bytes), "200m", fmt.Sprintf("resources not updated for %#v", input.object)) cmd := NewCmdResources(f, buf, buf)
return &http.Response{StatusCode: http.StatusOK, Header: defaultHeader(), Body: objBody(codec, input.object)}, nil cmd.SetOutput(buf)
default: cmd.Flags().Set("output", "yaml")
t.Errorf("%s: unexpected request: %s %#v\n%#v", "resources", req.Method, req.URL, req) opts := ResourcesOptions{
return nil, fmt.Errorf("unexpected request") Out: buf,
} Local: true,
}), Limits: "cpu=200m,memory=512Mi",
VersionedAPIPath: path.Join(input.apiPrefix, testapi.Default.GroupVersion().String()), ContainerSelector: "*"}
} err := opts.Complete(f, cmd, input.args)
buf := new(bytes.Buffer) if err == nil {
cmd := NewCmdResources(f, buf, buf) err = opts.Validate()
cmd.SetOutput(buf) }
cmd.Flags().Set("output", "yaml") if err == nil {
opts := ResourcesOptions{ err = opts.Run()
Out: buf, }
Local: true, assert.NoError(t, err)
Limits: "cpu=200m,memory=512Mi", })
ContainerSelector: "*"}
err := opts.Complete(f, cmd, input.args)
if err == nil {
err = opts.Validate()
}
if err == nil {
err = opts.Run()
}
assert.NoError(t, err)
} }
} }
...@@ -129,25 +129,25 @@ func (o *SelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [ ...@@ -129,25 +129,25 @@ func (o *SelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
o.builder = f.NewBuilder(). o.builder = f.NewBuilder().
Internal().
LocalParam(o.local).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.fileOptions). FilenameParam(enforceNamespace, &o.fileOptions).
IncludeUninitialized(includeUninitialized). IncludeUninitialized(includeUninitialized).
Flatten() Flatten()
if o.local { if !o.local {
o.builder.
ResourceTypeOrNameArgs(o.all, o.resources...).
Latest()
} else {
// if a --local flag was provided, and a resource was specified in the form // if a --local flag was provided, and a resource was specified in the form
// <resource>/<name>, fail immediately as --local cannot query the api server // <resource>/<name>, fail immediately as --local cannot query the api server
// for the specified resource. // for the specified resource.
if len(o.resources) > 0 { if len(o.resources) > 0 {
return resource.LocalResourceError return resource.LocalResourceError
} }
o.builder = o.builder.Local(f.ClientForMapping)
} else {
o.builder = o.builder.
ResourceTypeOrNameArgs(o.all, o.resources...).
Latest()
} }
o.PrintObject = func(obj runtime.Object) error { o.PrintObject = func(obj runtime.Object) error {
...@@ -181,15 +181,12 @@ func (o *SelectorOptions) RunSelector() error { ...@@ -181,15 +181,12 @@ func (o *SelectorOptions) RunSelector() error {
return r.Visit(func(info *resource.Info, err error) error { return r.Visit(func(info *resource.Info, err error) error {
patch := &Patch{Info: info} patch := &Patch{Info: info}
CalculatePatch(patch, o.encoder, func(info *resource.Info) ([]byte, error) { CalculatePatch(patch, o.encoder, func(info *resource.Info) ([]byte, error) {
versioned, err := info.Mapping.ConvertToVersion(info.Object, info.Mapping.GroupVersionKind.GroupVersion()) versioned := info.AsVersioned()
if err != nil { patch.Info.Object = versioned
return nil, err selectErr := updateSelectorForObject(info.Object, *o.selector)
}
patch.Info.VersionedObject = versioned
selectErr := updateSelectorForObject(info.VersionedObject, *o.selector)
if selectErr == nil { if selectErr == nil {
return runtime.Encode(o.encoder, info.VersionedObject) return runtime.Encode(o.encoder, info.Object)
} }
return nil, selectErr return nil, selectErr
}) })
...@@ -198,7 +195,7 @@ func (o *SelectorOptions) RunSelector() error { ...@@ -198,7 +195,7 @@ func (o *SelectorOptions) RunSelector() error {
return patch.Err return patch.Err
} }
if o.local || o.dryrun { if o.local || o.dryrun {
return o.PrintObject(info.VersionedObject) return o.PrintObject(info.Object)
} }
patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch) patched, err := resource.NewHelper(info.Client, info.Mapping).Patch(info.Namespace, info.Name, types.StrategicMergePatchType, patch.Patch)
......
...@@ -129,7 +129,10 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com ...@@ -129,7 +129,10 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com
saConfig.serviceAccountName = args[len(args)-1] saConfig.serviceAccountName = args[len(args)-1]
resources := args[:len(args)-1] resources := args[:len(args)-1]
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
builder := f.NewBuilder().ContinueOnError(). builder := f.NewBuilder().
Internal().
LocalParam(saConfig.local).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &saConfig.fileNameOptions). FilenameParam(enforceNamespace, &saConfig.fileNameOptions).
IncludeUninitialized(includeUninitialized). IncludeUninitialized(includeUninitialized).
...@@ -137,8 +140,6 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com ...@@ -137,8 +140,6 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com
if !saConfig.local { if !saConfig.local {
builder.ResourceTypeOrNameArgs(saConfig.all, resources...). builder.ResourceTypeOrNameArgs(saConfig.all, resources...).
Latest() Latest()
} else {
builder = builder.Local(f.ClientForMapping)
} }
saConfig.infos, err = builder.Do().Infos() saConfig.infos, err = builder.Do().Infos()
if err != nil { if err != nil {
...@@ -151,11 +152,12 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com ...@@ -151,11 +152,12 @@ func (saConfig *serviceAccountConfig) Complete(f cmdutil.Factory, cmd *cobra.Com
func (saConfig *serviceAccountConfig) Run() error { func (saConfig *serviceAccountConfig) Run() error {
patchErrs := []error{} patchErrs := []error{}
patchFn := func(info *resource.Info) ([]byte, error) { patchFn := func(info *resource.Info) ([]byte, error) {
saConfig.updatePodSpecForObject(info.VersionedObject, func(podSpec *v1.PodSpec) error { info.Object = info.AsVersioned()
saConfig.updatePodSpecForObject(info.Object, func(podSpec *v1.PodSpec) error {
podSpec.ServiceAccountName = saConfig.serviceAccountName podSpec.ServiceAccountName = saConfig.serviceAccountName
return nil return nil
}) })
return runtime.Encode(saConfig.encoder, info.VersionedObject) return runtime.Encode(saConfig.encoder, info.Object)
} }
patches := CalculatePatches(saConfig.infos, saConfig.encoder, patchFn) patches := CalculatePatches(saConfig.infos, saConfig.encoder, patchFn)
for _, patch := range patches { for _, patch := range patches {
...@@ -165,7 +167,7 @@ func (saConfig *serviceAccountConfig) Run() error { ...@@ -165,7 +167,7 @@ func (saConfig *serviceAccountConfig) Run() error {
continue continue
} }
if saConfig.local || saConfig.dryRun { if saConfig.local || saConfig.dryRun {
if err := saConfig.PrintObject(saConfig.cmd, saConfig.local, saConfig.mapper, patch.Info.VersionedObject, saConfig.out); err != nil { if err := saConfig.PrintObject(saConfig.cmd, saConfig.local, saConfig.mapper, patch.Info.AsVersioned(), saConfig.out); err != nil {
return err return err
} }
continue continue
...@@ -184,11 +186,7 @@ func (saConfig *serviceAccountConfig) Run() error { ...@@ -184,11 +186,7 @@ func (saConfig *serviceAccountConfig) Run() error {
} }
} }
if len(saConfig.output) > 0 { if len(saConfig.output) > 0 {
versionedObject, err := patch.Info.Mapping.ConvertToVersion(patched, patch.Info.Mapping.GroupVersionKind.GroupVersion()) if err := saConfig.PrintObject(saConfig.cmd, saConfig.local, saConfig.mapper, info.AsVersioned(), saConfig.out); err != nil {
if err != nil {
return err
}
if err := saConfig.PrintObject(saConfig.cmd, saConfig.local, saConfig.mapper, versionedObject, saConfig.out); err != nil {
return err return err
} }
continue continue
......
...@@ -66,32 +66,34 @@ func TestSetServiceAccountLocal(t *testing.T) { ...@@ -66,32 +66,34 @@ func TestSetServiceAccountLocal(t *testing.T) {
{yaml: "../../../../test/fixtures/doc-yaml/user-guide/deployment.yaml", apiGroup: "extensions"}, {yaml: "../../../../test/fixtures/doc-yaml/user-guide/deployment.yaml", apiGroup: "extensions"},
} }
f, tf, _, _ := cmdtesting.NewAPIFactory() for i, input := range inputs {
tf.Client = &fake.RESTClient{ t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
GroupVersion: schema.GroupVersion{Version: "v1"}, f, tf, _, _ := cmdtesting.NewAPIFactory()
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { tf.Client = &fake.RESTClient{
t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req) GroupVersion: schema.GroupVersion{Version: "v1"},
return nil, nil Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
}), t.Fatalf("unexpected request: %s %#v\n%#v", req.Method, req.URL, req)
} return nil, nil
tf.Namespace = "test" }),
out := new(bytes.Buffer) }
cmd := NewCmdServiceAccount(f, out, out) tf.Namespace = "test"
cmd.SetOutput(out) out := new(bytes.Buffer)
cmd.Flags().Set("output", "yaml") cmd := NewCmdServiceAccount(f, out, out)
cmd.Flags().Set("local", "true") cmd.SetOutput(out)
for _, input := range inputs { cmd.Flags().Set("output", "yaml")
testapi.Default = testapi.Groups[input.apiGroup] cmd.Flags().Set("local", "true")
tf.Printer = printers.NewVersionedPrinter(&printers.YAMLPrinter{}, testapi.Default.Converter(), *testapi.Default.GroupVersion()) testapi.Default = testapi.Groups[input.apiGroup]
saConfig := serviceAccountConfig{fileNameOptions: resource.FilenameOptions{ tf.Printer = printers.NewVersionedPrinter(&printers.YAMLPrinter{}, testapi.Default.Converter(), *testapi.Default.GroupVersion())
Filenames: []string{input.yaml}}, saConfig := serviceAccountConfig{fileNameOptions: resource.FilenameOptions{
out: out, Filenames: []string{input.yaml}},
local: true} out: out,
err := saConfig.Complete(f, cmd, []string{serviceAccount}) local: true}
assert.NoError(t, err) err := saConfig.Complete(f, cmd, []string{serviceAccount})
err = saConfig.Run() assert.NoError(t, err)
assert.NoError(t, err) err = saConfig.Run()
assert.Contains(t, out.String(), "serviceAccountName: "+serviceAccount, fmt.Sprintf("serviceaccount not updated for %s", input.yaml)) assert.NoError(t, err)
assert.Contains(t, out.String(), "serviceAccountName: "+serviceAccount, fmt.Sprintf("serviceaccount not updated for %s", input.yaml))
})
} }
} }
......
...@@ -28,7 +28,6 @@ import ( ...@@ -28,7 +28,6 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/rbac" "k8s.io/kubernetes/pkg/apis/rbac"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
...@@ -127,6 +126,8 @@ func (o *SubjectOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [] ...@@ -127,6 +126,8 @@ func (o *SubjectOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false) includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
builder := f.NewBuilder(). builder := f.NewBuilder().
Internal().
LocalParam(o.Local).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(enforceNamespace, &o.FilenameOptions).
...@@ -140,8 +141,6 @@ func (o *SubjectOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [] ...@@ -140,8 +141,6 @@ func (o *SubjectOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
if len(args) > 0 { if len(args) > 0 {
return resource.LocalResourceError return resource.LocalResourceError
} }
builder = builder.Local(f.ClientForMapping)
} else { } else {
builder = builder. builder = builder.
LabelSelectorParam(o.Selector). LabelSelectorParam(o.Selector).
...@@ -219,9 +218,8 @@ func (o *SubjectOptions) Run(f cmdutil.Factory, fn updateSubjects) error { ...@@ -219,9 +218,8 @@ func (o *SubjectOptions) Run(f cmdutil.Factory, fn updateSubjects) error {
transformed, err := updateSubjectForObject(info.Object, subjects, fn) transformed, err := updateSubjectForObject(info.Object, subjects, fn)
if transformed && err == nil { if transformed && err == nil {
// TODO: switch UpdatePodSpecForObject to work on v1.PodSpec, use info.VersionedObject, and avoid conversion completely // TODO: switch UpdatePodSpecForObject to work on v1.PodSpec
versionedEncoder := legacyscheme.Codecs.EncoderForVersion(o.Encoder, info.Mapping.GroupVersionKind.GroupVersion()) return runtime.Encode(o.Encoder, info.AsVersioned())
return runtime.Encode(versionedEncoder, info.Object)
} }
return nil, err return nil, err
}) })
...@@ -256,7 +254,7 @@ func (o *SubjectOptions) Run(f cmdutil.Factory, fn updateSubjects) error { ...@@ -256,7 +254,7 @@ func (o *SubjectOptions) Run(f cmdutil.Factory, fn updateSubjects) error {
shortOutput := o.Output == "name" shortOutput := o.Output == "name"
if len(o.Output) > 0 && !shortOutput { if len(o.Output) > 0 && !shortOutput {
return o.PrintObject(o.Mapper, info.Object, o.Out) return o.PrintObject(o.Mapper, info.AsVersioned(), o.Out)
} }
f.PrintSuccess(o.Mapper, shortOutput, o.Out, info.Mapping.Resource, info.Name, false, "subjects updated") f.PrintSuccess(o.Mapper, shortOutput, o.Out, info.Mapping.Resource, info.Name, false, "subjects updated")
} }
......
...@@ -150,6 +150,7 @@ func (o *TaintOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Com ...@@ -150,6 +150,7 @@ func (o *TaintOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Com
return cmdutil.UsageErrorf(cmd, err.Error()) return cmdutil.UsageErrorf(cmd, err.Error())
} }
o.builder = f.NewBuilder(). o.builder = f.NewBuilder().
Internal().
ContinueOnError(). ContinueOnError().
NamespaceParam(namespace).DefaultNamespace() NamespaceParam(namespace).DefaultNamespace()
if o.selector != "" { if o.selector != "" {
......
# Please edit the 'last-applied-configuration' annotations below. # Please edit the 'last-applied-configuration' annotations below.
# Lines beginning with a '#' will be ignored, and an empty file will abort the edit. # Lines beginning with a '#' will be ignored, and an empty file will abort the edit.
# #
# The edited file had a syntax error: unable to get type info from the object "*unstructured.Unstructured": Object 'apiVersion' is missing in 'unstructured object has no version' # The edited file had a syntax error: unable to get type info from the object "*unstructured.Unstructured": Object 'apiVersion' is missing in 'object has no apiVersion field'
# #
apiVersion: v1 apiVersion: v1
items: items:
......
...@@ -107,12 +107,9 @@ func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args [] ...@@ -107,12 +107,9 @@ func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []
if err != nil { if err != nil {
return err return err
} }
mapper, _, err := f.UnstructuredObject() mapper, _ := f.Object()
if err != nil { b := f.NewBuilder().
return err Unstructured()
}
b := f.NewUnstructuredBuilder()
if o.EditMode == NormalEditMode || o.EditMode == ApplyEditMode { if o.EditMode == NormalEditMode || o.EditMode == ApplyEditMode {
// when do normal edit or apply edit we need to always retrieve the latest resource from server // when do normal edit or apply edit we need to always retrieve the latest resource from server
b = b.ResourceTypeOrNameArgs(true, args...).Latest() b = b.ResourceTypeOrNameArgs(true, args...).Latest()
...@@ -132,7 +129,8 @@ func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args [] ...@@ -132,7 +129,8 @@ func (o *EditOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args []
o.updatedResultGetter = func(data []byte) *resource.Result { o.updatedResultGetter = func(data []byte) *resource.Result {
// resource builder to read objects from edited data // resource builder to read objects from edited data
return f.NewUnstructuredBuilder(). return f.NewBuilder().
Unstructured().
Stream(bytes.NewReader(data), "edited-file"). Stream(bytes.NewReader(data), "edited-file").
IncludeUninitialized(includeUninitialized). IncludeUninitialized(includeUninitialized).
ContinueOnError(). ContinueOnError().
......
...@@ -189,9 +189,6 @@ type ClientAccessFactory interface { ...@@ -189,9 +189,6 @@ type ClientAccessFactory interface {
type ObjectMappingFactory interface { type ObjectMappingFactory interface {
// Returns interfaces for dealing with arbitrary runtime.Objects. // Returns interfaces for dealing with arbitrary runtime.Objects.
Object() (meta.RESTMapper, runtime.ObjectTyper) Object() (meta.RESTMapper, runtime.ObjectTyper)
// Returns interfaces for dealing with arbitrary
// runtime.Unstructured. This performs API calls to discover types.
UnstructuredObject() (meta.RESTMapper, runtime.ObjectTyper, error)
// Returns interface for expanding categories like `all`. // Returns interface for expanding categories like `all`.
CategoryExpander() categories.CategoryExpander CategoryExpander() categories.CategoryExpander
// 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
...@@ -251,10 +248,9 @@ type BuilderFactory interface { ...@@ -251,10 +248,9 @@ type BuilderFactory interface {
PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, out io.Writer) error PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, out io.Writer) error
// PrintSuccess prints message after finishing mutating operations // PrintSuccess prints message after finishing mutating operations
PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource, name string, dryRun bool, operation string) PrintSuccess(mapper meta.RESTMapper, shortOutput bool, out io.Writer, resource, name string, dryRun bool, operation string)
// One stop shopping for a structured Builder // NewBuilder returns an object that assists in loading objects from both disk and the server
// and which implements the common patterns for CLI interactions with generic resources.
NewBuilder() *resource.Builder NewBuilder() *resource.Builder
// One stop shopping for a unstructured Builder
NewUnstructuredBuilder() *resource.Builder
// PluginLoader provides the implementation to be used to load cli plugins. // PluginLoader provides the implementation to be used to load cli plugins.
PluginLoader() plugins.PluginLoader PluginLoader() plugins.PluginLoader
// PluginRunner provides the implementation to be used to run cli plugins. // PluginRunner provides the implementation to be used to run cli plugins.
......
...@@ -28,7 +28,6 @@ import ( ...@@ -28,7 +28,6 @@ import (
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl/plugins" "k8s.io/kubernetes/pkg/kubectl/plugins"
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/printers"
...@@ -51,17 +50,9 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac ...@@ -51,17 +50,9 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac
func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command, isLocal bool, outputOpts *printers.OutputOptions, options printers.PrintOptions) (printers.ResourcePrinter, error) { func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command, isLocal bool, outputOpts *printers.OutputOptions, options printers.PrintOptions) (printers.ResourcePrinter, error) {
var mapper meta.RESTMapper var mapper meta.RESTMapper
var typer runtime.ObjectTyper var typer runtime.ObjectTyper
var err error
if isLocal { mapper, typer = f.objectMappingFactory.Object()
mapper = legacyscheme.Registry.RESTMapper()
typer = legacyscheme.Scheme
} else {
mapper, typer, err = f.objectMappingFactory.UnstructuredObject()
if err != nil {
return nil, err
}
}
// TODO: used by the custom column implementation and the name implementation, break this dependency // TODO: used by the custom column implementation and the name implementation, break this dependency
decoders := []runtime.Decoder{f.clientAccessFactory.Decoder(true), unstructured.UnstructuredJSONScheme} decoders := []runtime.Decoder{f.clientAccessFactory.Decoder(true), unstructured.UnstructuredJSONScheme}
encoder := f.clientAccessFactory.JSONEncoder() encoder := f.clientAccessFactory.JSONEncoder()
...@@ -135,16 +126,6 @@ func (f *ring2Factory) PrintObject(cmd *cobra.Command, isLocal bool, mapper meta ...@@ -135,16 +126,6 @@ func (f *ring2Factory) PrintObject(cmd *cobra.Command, isLocal bool, mapper meta
_, typer := f.objectMappingFactory.Object() _, typer := f.objectMappingFactory.Object()
gvks, _, err := typer.ObjectKinds(obj) gvks, _, err := typer.ObjectKinds(obj)
// fall back to an unstructured object if we get something unregistered
if runtime.IsNotRegisteredError(err) {
_, typer, unstructuredErr := f.objectMappingFactory.UnstructuredObject()
if unstructuredErr != nil {
// if we can't get an unstructured typer, return the original error
return err
}
gvks, _, err = typer.ObjectKinds(obj)
}
if err != nil { if err != nil {
return err return err
} }
...@@ -183,23 +164,27 @@ func (f *ring2Factory) PrintResourceInfoForCommand(cmd *cobra.Command, info *res ...@@ -183,23 +164,27 @@ func (f *ring2Factory) PrintResourceInfoForCommand(cmd *cobra.Command, info *res
// NewBuilder returns a new resource builder for structured api objects. // NewBuilder returns a new resource builder for structured api objects.
func (f *ring2Factory) NewBuilder() *resource.Builder { func (f *ring2Factory) NewBuilder() *resource.Builder {
clientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.ClientForMapping) clientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.ClientForMapping)
mapper, typer := f.objectMappingFactory.Object() mapper, typer := f.objectMappingFactory.Object()
categoryExpander := f.objectMappingFactory.CategoryExpander()
return resource.NewBuilder(mapper, categoryExpander, typer, clientMapperFunc, f.clientAccessFactory.Decoder(true)) unstructuredClientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.UnstructuredClientForMapping)
}
// NewUnstructuredBuilder returns a new resource builder for unstructured api objects.
func (f *ring2Factory) NewUnstructuredBuilder() *resource.Builder {
clientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.UnstructuredClientForMapping)
mapper, typer, err := f.objectMappingFactory.UnstructuredObject()
if err != nil {
CheckErr(err)
}
categoryExpander := f.objectMappingFactory.CategoryExpander() categoryExpander := f.objectMappingFactory.CategoryExpander()
return resource.NewBuilder(mapper, categoryExpander, typer, clientMapperFunc, unstructured.UnstructuredJSONScheme) return resource.NewBuilder(
&resource.Mapper{
RESTMapper: mapper,
ObjectTyper: typer,
ClientMapper: clientMapperFunc,
Decoder: f.clientAccessFactory.Decoder(true),
},
&resource.Mapper{
RESTMapper: mapper,
ObjectTyper: typer,
ClientMapper: unstructuredClientMapperFunc,
Decoder: unstructured.UnstructuredJSONScheme,
},
categoryExpander,
)
} }
// PluginLoader loads plugins from a path set by the KUBECTL_PLUGINS_PATH env var. // PluginLoader loads plugins from a path set by the KUBECTL_PLUGINS_PATH env var.
......
...@@ -310,11 +310,7 @@ func (f *ring0Factory) MapBasedSelectorForObject(object runtime.Object) (string, ...@@ -310,11 +310,7 @@ func (f *ring0Factory) MapBasedSelectorForObject(object runtime.Object) (string,
} }
return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil return kubectl.MakeLabels(t.Spec.Selector.MatchLabels), nil
default: default:
gvks, _, err := legacyscheme.Scheme.ObjectKinds(object) return "", fmt.Errorf("cannot extract pod selector from %T", object)
if err != nil {
return "", err
}
return "", fmt.Errorf("cannot extract pod selector from %v", gvks[0])
} }
} }
...@@ -332,11 +328,7 @@ func (f *ring0Factory) PortsForObject(object runtime.Object) ([]string, error) { ...@@ -332,11 +328,7 @@ func (f *ring0Factory) PortsForObject(object runtime.Object) ([]string, error) {
case *extensions.ReplicaSet: case *extensions.ReplicaSet:
return getPorts(t.Spec.Template.Spec), nil return getPorts(t.Spec.Template.Spec), nil
default: default:
gvks, _, err := legacyscheme.Scheme.ObjectKinds(object) return nil, fmt.Errorf("cannot extract ports from %T", object)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("cannot extract ports from %v", gvks[0])
} }
} }
...@@ -354,11 +346,7 @@ func (f *ring0Factory) ProtocolsForObject(object runtime.Object) (map[string]str ...@@ -354,11 +346,7 @@ func (f *ring0Factory) ProtocolsForObject(object runtime.Object) (map[string]str
case *extensions.ReplicaSet: case *extensions.ReplicaSet:
return getProtocols(t.Spec.Template.Spec), nil return getProtocols(t.Spec.Template.Spec), nil
default: default:
gvks, _, err := legacyscheme.Scheme.ObjectKinds(object) return nil, fmt.Errorf("cannot extract protocols from %T", object)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("cannot extract protocols from %v", gvks[0])
} }
} }
......
...@@ -27,6 +27,8 @@ import ( ...@@ -27,6 +27,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/golang/glog"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
...@@ -71,49 +73,40 @@ func NewObjectMappingFactory(clientAccessFactory ClientAccessFactory) ObjectMapp ...@@ -71,49 +73,40 @@ func NewObjectMappingFactory(clientAccessFactory ClientAccessFactory) ObjectMapp
return f return f
} }
// TODO: This method should return an error now that it can fail. Alternatively, it needs to // objectLoader attempts to perform discovery against the server, and will fall back to
// return lazy implementations of mapper and typer that don't hit the wire until they are // the built in mapper if necessary. It supports unstructured objects either way, since
// invoked. // the underlying Scheme supports Unstructured. The mapper will return converters that can
func (f *ring1Factory) Object() (meta.RESTMapper, runtime.ObjectTyper) { // convert versioned types to unstructured and back.
mapper := legacyscheme.Registry.RESTMapper() func (f *ring1Factory) objectLoader() (meta.RESTMapper, runtime.ObjectTyper, error) {
discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
if err == nil {
mapper = meta.FirstHitRESTMapper{
MultiRESTMapper: meta.MultiRESTMapper{
discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, legacyscheme.Registry.InterfacesFor),
legacyscheme.Registry.RESTMapper(), // hardcoded fall back
},
}
// wrap with shortcuts, they require a discoveryClient
mapper, err = NewShortcutExpander(mapper, discoveryClient)
// you only have an error on missing discoveryClient, so this shouldn't fail. Check anyway.
CheckErr(err)
}
return mapper, legacyscheme.Scheme
}
func (f *ring1Factory) UnstructuredObject() (meta.RESTMapper, runtime.ObjectTyper, error) {
discoveryClient, err := f.clientAccessFactory.DiscoveryClient() discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
if err != nil { if err != nil {
return nil, nil, err glog.V(3).Infof("Unable to get a discovery client to find server resources, falling back to hardcoded types: %v", err)
return legacyscheme.Registry.RESTMapper(), legacyscheme.Scheme, nil
} }
groupResources, err := discovery.GetAPIGroupResources(discoveryClient) groupResources, err := discovery.GetAPIGroupResources(discoveryClient)
if err != nil && !discoveryClient.Fresh() { if err != nil && !discoveryClient.Fresh() {
discoveryClient.Invalidate() discoveryClient.Invalidate()
groupResources, err = discovery.GetAPIGroupResources(discoveryClient) groupResources, err = discovery.GetAPIGroupResources(discoveryClient)
} }
if err != nil { if err != nil {
return nil, nil, err glog.V(3).Infof("Unable to retrieve API resources, falling back to hardcoded types: %v", err)
return legacyscheme.Registry.RESTMapper(), legacyscheme.Scheme, nil
} }
mapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, meta.InterfacesForUnstructured) // allow conversion between typed and unstructured objects
typer := discovery.NewUnstructuredObjectTyper(groupResources) interfaces := meta.InterfacesForUnstructuredConversion(legacyscheme.Registry.InterfacesFor)
expander, err := NewShortcutExpander(mapper, discoveryClient) mapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, meta.VersionInterfacesFunc(interfaces))
// TODO: should this also indicate it recognizes typed objects?
typer := discovery.NewUnstructuredObjectTyper(groupResources, legacyscheme.Scheme)
expander := NewShortcutExpander(mapper, discoveryClient)
return expander, typer, err return expander, typer, err
} }
func (f *ring1Factory) Object() (meta.RESTMapper, runtime.ObjectTyper) {
return meta.NewLazyObjectLoader(f.objectLoader)
}
func (f *ring1Factory) CategoryExpander() categories.CategoryExpander { func (f *ring1Factory) CategoryExpander() categories.CategoryExpander {
legacyExpander := categories.LegacyCategoryExpander legacyExpander := categories.LegacyCategoryExpander
...@@ -273,11 +266,7 @@ func (f *ring1Factory) LogsForObject(object, options runtime.Object, timeout tim ...@@ -273,11 +266,7 @@ func (f *ring1Factory) LogsForObject(object, options runtime.Object, timeout tim
} }
default: default:
gvks, _, err := legacyscheme.Scheme.ObjectKinds(object) return nil, fmt.Errorf("cannot get the logs from %T", object)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("cannot get the logs from %v", gvks[0])
} }
sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) } sortBy := func(pods []*v1.Pod) sort.Interface { return controller.ByLogging(pods) }
...@@ -401,11 +390,7 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object, timeout tim ...@@ -401,11 +390,7 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object, timeout tim
return t, nil return t, nil
default: default:
gvks, _, err := legacyscheme.Scheme.ObjectKinds(object) return nil, fmt.Errorf("cannot attach to %T: not implemented", object)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("cannot attach to %v: not implemented", gvks[0])
} }
sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) } sortBy := func(pods []*v1.Pod) sort.Interface { return sort.Reverse(controller.ActivePods(pods)) }
......
...@@ -539,11 +539,17 @@ func TestDiscoveryReplaceAliases(t *testing.T) { ...@@ -539,11 +539,17 @@ func TestDiscoveryReplaceAliases(t *testing.T) {
} }
ds := &fakeDiscoveryClient{} ds := &fakeDiscoveryClient{}
mapper, err := NewShortcutExpander(testapi.Default.RESTMapper(), ds) mapper := NewShortcutExpander(testapi.Default.RESTMapper(), ds)
if err != nil { b := resource.NewBuilder(
t.Fatalf("Unable to create shortcut expander, err = %s", err.Error()) &resource.Mapper{
} RESTMapper: mapper,
b := resource.NewBuilder(mapper, categories.LegacyCategoryExpander, legacyscheme.Scheme, fakeClient(), testapi.Default.Codec()) ObjectTyper: legacyscheme.Scheme,
ClientMapper: fakeClient(),
Decoder: testapi.Default.Codec(),
},
nil,
categories.LegacyCategoryExpander,
)
for _, test := range tests { for _, test := range tests {
replaced := b.ReplaceAliases(test.arg) replaced := b.ReplaceAliases(test.arg)
......
...@@ -17,7 +17,6 @@ limitations under the License. ...@@ -17,7 +17,6 @@ limitations under the License.
package util package util
import ( import (
"errors"
"strings" "strings"
"github.com/golang/glog" "github.com/golang/glog"
...@@ -37,11 +36,8 @@ type shortcutExpander struct { ...@@ -37,11 +36,8 @@ type shortcutExpander struct {
var _ meta.RESTMapper = &shortcutExpander{} var _ meta.RESTMapper = &shortcutExpander{}
func NewShortcutExpander(delegate meta.RESTMapper, client discovery.DiscoveryInterface) (shortcutExpander, error) { func NewShortcutExpander(delegate meta.RESTMapper, client discovery.DiscoveryInterface) shortcutExpander {
if client == nil { return shortcutExpander{RESTMapper: delegate, discoveryClient: client}
return shortcutExpander{}, errors.New("Please provide discovery client to shortcut expander")
}
return shortcutExpander{RESTMapper: delegate, discoveryClient: client}, nil
} }
func (e shortcutExpander) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) { func (e shortcutExpander) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
......
...@@ -71,10 +71,7 @@ func TestReplaceAliases(t *testing.T) { ...@@ -71,10 +71,7 @@ func TestReplaceAliases(t *testing.T) {
} }
ds := &fakeDiscoveryClient{} ds := &fakeDiscoveryClient{}
mapper, err := NewShortcutExpander(testapi.Default.RESTMapper(), ds) mapper := NewShortcutExpander(testapi.Default.RESTMapper(), ds)
if err != nil {
t.Fatalf("Unable to create shortcut expander, err %s", err.Error())
}
for _, test := range tests { for _, test := range tests {
ds.serverResourcesHandler = func() ([]*metav1.APIResourceList, error) { ds.serverResourcesHandler = func() ([]*metav1.APIResourceList, error) {
...@@ -126,10 +123,7 @@ func TestKindFor(t *testing.T) { ...@@ -126,10 +123,7 @@ func TestKindFor(t *testing.T) {
} }
ds := &fakeDiscoveryClient{} ds := &fakeDiscoveryClient{}
mapper, err := NewShortcutExpander(testapi.Default.RESTMapper(), ds) mapper := NewShortcutExpander(testapi.Default.RESTMapper(), ds)
if err != nil {
t.Fatalf("Unable to create shortcut expander, err %s", err.Error())
}
for i, test := range tests { for i, test := range tests {
ds.serverResourcesHandler = func() ([]*metav1.APIResourceList, error) { ds.serverResourcesHandler = func() ([]*metav1.APIResourceList, error) {
......
...@@ -29,6 +29,7 @@ go_library( ...@@ -29,6 +29,7 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library", "//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
......
...@@ -27,7 +27,6 @@ import ( ...@@ -27,7 +27,6 @@ import (
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
...@@ -44,9 +43,13 @@ const defaultHttpGetAttempts int = 3 ...@@ -44,9 +43,13 @@ const defaultHttpGetAttempts int = 3
// from the command line and converting them to a list of resources to iterate // from the command line and converting them to a list of resources to iterate
// over using the Visitor interface. // over using the Visitor interface.
type Builder struct { type Builder struct {
mapper *Mapper
categoryExpander categories.CategoryExpander categoryExpander categories.CategoryExpander
// mapper is set explicitly by resource builders
mapper *Mapper
internal *Mapper
unstructured *Mapper
errs []error errs []error
paths []Visitor paths []Visitor
...@@ -116,10 +119,13 @@ type resourceTuple struct { ...@@ -116,10 +119,13 @@ type resourceTuple struct {
Name string Name string
} }
// NewBuilder creates a builder that operates on generic objects. // NewBuilder creates a builder that operates on generic objects. At least one of
func NewBuilder(mapper meta.RESTMapper, categoryExpander categories.CategoryExpander, typer runtime.ObjectTyper, clientMapper ClientMapper, decoder runtime.Decoder) *Builder { // internal or unstructured must be specified.
// TODO: Add versioned client (although versioned is still lossy)
func NewBuilder(internal, unstructured *Mapper, categoryExpander categories.CategoryExpander) *Builder {
return &Builder{ return &Builder{
mapper: &Mapper{typer, mapper, clientMapper, decoder}, internal: internal,
unstructured: unstructured,
categoryExpander: categoryExpander, categoryExpander: categoryExpander,
requireObject: true, requireObject: true,
} }
...@@ -165,12 +171,64 @@ func (b *Builder) FilenameParam(enforceNamespace bool, filenameOptions *Filename ...@@ -165,12 +171,64 @@ func (b *Builder) FilenameParam(enforceNamespace bool, filenameOptions *Filename
return b return b
} }
// Local wraps the builder's clientMapper in a DisabledClientMapperForMapping // Unstructured updates the builder so that it will request and send unstructured
func (b *Builder) Local(mapperFunc ClientMapperFunc) *Builder { // objects. Unstructured objects preserve all fields sent by the server in a map format
b.mapper.ClientMapper = DisabledClientForMapping{ClientMapper: ClientMapperFunc(mapperFunc)} // based on the object's JSON structure which means no data is lost when the client
// reads and then writes an object. Use this mode in preference to Internal unless you
// are working with Go types directly.
func (b *Builder) Unstructured() *Builder {
if b.unstructured == nil {
b.errs = append(b.errs, fmt.Errorf("no unstructured mapper provided"))
return b
}
if b.mapper != nil {
b.errs = append(b.errs, fmt.Errorf("another mapper was already selected, cannot use unstructured types"))
return b
}
b.mapper = b.unstructured
return b
}
// Internal updates the builder so that it will convert objects off the wire
// into the internal form if necessary. Using internal types is lossy - fields added
// to the server will not be seen by the client code and may result in failure. Only
// use this mode when working offline, or when generating patches to send to the server.
// Use Unstructured if you are reading an object and performing a POST or PUT.
func (b *Builder) Internal() *Builder {
if b.internal == nil {
b.errs = append(b.errs, fmt.Errorf("no internal mapper provided"))
return b
}
if b.mapper != nil {
b.errs = append(b.errs, fmt.Errorf("another mapper was already selected, cannot use internal types"))
return b
}
b.mapper = b.internal
return b return b
} }
// LocalParam calls Local() if local is true.
func (b *Builder) LocalParam(local bool) *Builder {
if local {
b.Local()
}
return b
}
// Local will avoid asking the server for results.
func (b *Builder) Local() *Builder {
mapper := *b.mapper
mapper.ClientMapper = DisabledClientForMapping{ClientMapper: mapper.ClientMapper}
b.mapper = &mapper
return b
}
// Mapper returns a copy of the current mapper.
func (b *Builder) Mapper() *Mapper {
mapper := *b.mapper
return &mapper
}
// URL accepts a number of URLs directly. // URL accepts a number of URLs directly.
func (b *Builder) URL(httpAttemptCount int, urls ...*url.URL) *Builder { func (b *Builder) URL(httpAttemptCount int, urls ...*url.URL) *Builder {
for _, u := range urls { for _, u := range urls {
...@@ -743,7 +801,13 @@ func (b *Builder) visitByResource() *Result { ...@@ -743,7 +801,13 @@ func (b *Builder) visitByResource() *Result {
} }
} }
info := NewInfo(client, mapping, selectorNamespace, tuple.Name, b.export) info := &Info{
Client: client,
Mapping: mapping,
Namespace: selectorNamespace,
Name: tuple.Name,
Export: b.export,
}
items = append(items, info) items = append(items, info)
} }
...@@ -802,7 +866,13 @@ func (b *Builder) visitByName() *Result { ...@@ -802,7 +866,13 @@ func (b *Builder) visitByName() *Result {
visitors := []Visitor{} visitors := []Visitor{}
for _, name := range b.names { for _, name := range b.names {
info := NewInfo(client, mapping, selectorNamespace, name, b.export) info := &Info{
Client: client,
Mapping: mapping,
Namespace: selectorNamespace,
Name: name,
Export: b.export,
}
visitors = append(visitors, info) visitors = append(visitors, info)
} }
result.visitor = VisitorList(visitors) result.visitor = VisitorList(visitors)
...@@ -863,6 +933,7 @@ func (b *Builder) visitByPaths() *Result { ...@@ -863,6 +933,7 @@ func (b *Builder) visitByPaths() *Result {
// for further iteration. // for further iteration.
func (b *Builder) Do() *Result { func (b *Builder) Do() *Result {
r := b.visitorResult() r := b.visitorResult()
r.mapper = b.Mapper()
if r.err != nil { if r.err != nil {
return r return r
} }
......
...@@ -25,17 +25,7 @@ import ( ...@@ -25,17 +25,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
// DisabledClientForMapping allows callers to avoid allowing remote calls when handling // Mapper is a convenience struct for holding references to the interfaces
// 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
// needed to create Info for arbitrary objects. // needed to create Info for arbitrary objects.
type Mapper struct { type Mapper struct {
runtime.ObjectTyper runtime.ObjectTyper
...@@ -44,17 +34,27 @@ type Mapper struct { ...@@ -44,17 +34,27 @@ type Mapper struct {
runtime.Decoder runtime.Decoder
} }
// AcceptUnrecognizedObjects will return a mapper that will tolerate objects
// that are not recognized by the RESTMapper, returning mappings that can
// perform minimal transformation. Allows working in disconnected mode, or with
// objects that the server does not recognize. Returned resource.Info objects
// may have empty resource fields and nil clients.
func (m *Mapper) AcceptUnrecognizedObjects() *Mapper {
copied := *m
copied.RESTMapper = NewRelaxedRESTMapper(m.RESTMapper)
copied.ClientMapper = NewRelaxedClientMapper(m.ClientMapper)
return &copied
}
// 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) {
versions := &runtime.VersionedObjects{} obj, gvk, err := m.Decode(data, nil, nil)
_, gvk, err := m.Decode(data, nil, versions)
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to decode %q: %v", source, err) return nil, fmt.Errorf("unable to decode %q: %v", source, err)
} }
obj, versioned := versions.Last(), versions.First()
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)
...@@ -70,14 +70,15 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) { ...@@ -70,14 +70,15 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj) resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj)
return &Info{ return &Info{
Mapping: mapping, Client: client,
Client: client, Mapping: mapping,
Source: source,
Namespace: namespace, Namespace: namespace,
Name: name, Name: name,
Source: source,
VersionedObject: versioned,
Object: obj,
ResourceVersion: resourceVersion, ResourceVersion: resourceVersion,
Object: obj,
}, nil }, nil
} }
...@@ -99,6 +100,7 @@ func (m *Mapper) InfoForObject(obj runtime.Object, preferredGVKs []schema.GroupV ...@@ -99,6 +100,7 @@ func (m *Mapper) InfoForObject(obj runtime.Object, preferredGVKs []schema.GroupV
if err != nil { if err != nil {
return nil, fmt.Errorf("unable to recognize %v: %v", groupVersionKind, err) return nil, fmt.Errorf("unable to recognize %v: %v", groupVersionKind, 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)
...@@ -107,13 +109,14 @@ func (m *Mapper) InfoForObject(obj runtime.Object, preferredGVKs []schema.GroupV ...@@ -107,13 +109,14 @@ func (m *Mapper) InfoForObject(obj runtime.Object, preferredGVKs []schema.GroupV
namespace, _ := mapping.MetadataAccessor.Namespace(obj) namespace, _ := mapping.MetadataAccessor.Namespace(obj)
resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj) resourceVersion, _ := mapping.MetadataAccessor.ResourceVersion(obj)
return &Info{ return &Info{
Mapping: mapping, Client: client,
Client: client, Mapping: mapping,
Namespace: namespace,
Name: name,
Object: obj, Namespace: namespace,
Name: name,
ResourceVersion: resourceVersion, ResourceVersion: resourceVersion,
Object: obj,
}, nil }, nil
} }
...@@ -152,3 +155,83 @@ func preferredObjectKind(possibilities []schema.GroupVersionKind, preferences [] ...@@ -152,3 +155,83 @@ func preferredObjectKind(possibilities []schema.GroupVersionKind, preferences []
// Just pick the first // Just pick the first
return possibilities[0] return possibilities[0]
} }
// 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
}
// NewRelaxedClientMapper will return a nil mapping if the object is not a recognized resource.
func NewRelaxedClientMapper(mapper ClientMapper) ClientMapper {
return relaxedClientMapper{mapper}
}
type relaxedClientMapper struct {
ClientMapper
}
func (f relaxedClientMapper) ClientForMapping(mapping *meta.RESTMapping) (RESTClient, error) {
if len(mapping.Resource) == 0 {
return nil, nil
}
return f.ClientMapper.ClientForMapping(mapping)
}
// NewRelaxedRESTMapper returns a RESTMapper that will tolerate mappings that don't exist in provided
// RESTMapper, returning a mapping that is a best effort against the current server. This allows objects
// that the server does not recognize to still be loaded.
func NewRelaxedRESTMapper(mapper meta.RESTMapper) meta.RESTMapper {
return relaxedMapper{mapper}
}
type relaxedMapper struct {
meta.RESTMapper
}
func (m relaxedMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) {
mapping, err := m.RESTMapper.RESTMapping(gk, versions...)
if err != nil && meta.IsNoMatchError(err) && len(versions) > 0 {
return &meta.RESTMapping{
GroupVersionKind: gk.WithVersion(versions[0]),
MetadataAccessor: meta.NewAccessor(),
Scope: meta.RESTScopeRoot,
ObjectConvertor: identityConvertor{},
}, nil
}
return mapping, err
}
func (m relaxedMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) {
mappings, err := m.RESTMapper.RESTMappings(gk, versions...)
if err != nil && meta.IsNoMatchError(err) && len(versions) > 0 {
return []*meta.RESTMapping{
{
GroupVersionKind: gk.WithVersion(versions[0]),
MetadataAccessor: meta.NewAccessor(),
Scope: meta.RESTScopeRoot,
ObjectConvertor: identityConvertor{},
},
}, nil
}
return mappings, err
}
type identityConvertor struct{}
var _ runtime.ObjectConvertor = identityConvertor{}
func (c identityConvertor) Convert(in interface{}, out interface{}, context interface{}) error {
return fmt.Errorf("unable to convert objects across pointers")
}
func (c identityConvertor) ConvertToVersion(in runtime.Object, gv runtime.GroupVersioner) (out runtime.Object, err error) {
return in, nil
}
func (c identityConvertor) ConvertFieldLabel(version string, kind string, label string, value string) (string, string, error) {
return "", "", fmt.Errorf("unable to convert field labels")
}
...@@ -41,6 +41,7 @@ type Result struct { ...@@ -41,6 +41,7 @@ type Result struct {
singleItemImplied bool singleItemImplied bool
targetsSingleItems bool targetsSingleItems bool
mapper *Mapper
ignoreErrors []utilerrors.Matcher ignoreErrors []utilerrors.Matcher
// populated by a call to Infos // populated by a call to Infos
...@@ -74,6 +75,11 @@ func (r *Result) IgnoreErrors(fns ...ErrMatchFunc) *Result { ...@@ -74,6 +75,11 @@ func (r *Result) IgnoreErrors(fns ...ErrMatchFunc) *Result {
return r return r
} }
// Mapper returns a copy of the builder's mapper.
func (r *Result) Mapper() *Mapper {
return r.mapper
}
// Err returns one or more errors (via a util.ErrorList) that occurred prior // Err returns one or more errors (via a util.ErrorList) that occurred prior
// to visiting the elements in the visitor. To see all errors including those // to visiting the elements in the visitor. To see all errors including those
// that occur during visitation, invoke Infos(). // that occur during visitation, invoke Infos().
......
...@@ -92,13 +92,15 @@ func (r *Selector) Visit(fn VisitorFunc) error { ...@@ -92,13 +92,15 @@ func (r *Selector) Visit(fn VisitorFunc) error {
resourceVersion, _ := accessor.ResourceVersion(list) resourceVersion, _ := accessor.ResourceVersion(list)
nextContinueToken, _ := accessor.Continue(list) nextContinueToken, _ := accessor.Continue(list)
info := &Info{ info := &Info{
Client: r.Client, Client: r.Client,
Mapping: r.Mapping, Mapping: r.Mapping,
Namespace: r.Namespace,
Object: list, Namespace: r.Namespace,
ResourceVersion: resourceVersion, ResourceVersion: resourceVersion,
Object: list,
} }
if err := fn(info, nil); err != nil { if err := fn(info, nil); err != nil {
return err return err
} }
......
...@@ -32,6 +32,7 @@ import ( ...@@ -32,6 +32,7 @@ import (
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
...@@ -73,19 +74,22 @@ type ResourceMapping interface { ...@@ -73,19 +74,22 @@ type ResourceMapping interface {
// Info contains temporary info to execute a REST call, or show the results // Info contains temporary info to execute a REST call, or show the results
// of an already completed REST call. // of an already completed REST call.
type Info struct { type Info struct {
Client RESTClient Client RESTClient
Mapping *meta.RESTMapping // Mapping may be nil if the object has no available metadata, but is still parseable
// from disk.
Mapping *meta.RESTMapping
// Namespace will be set if the object is namespaced and has a specified value.
Namespace string Namespace string
Name string Name string
// Optional, Source is the filename or URL to template file (.json or .yaml), // Optional, Source is the filename or URL to template file (.json or .yaml),
// or stdin to use to handle the resource // or stdin to use to handle the resource
Source string Source string
// Optional, this is the provided object in a versioned type before defaulting // Optional, this is the most recent value returned by the server if available. It will
// and conversions into its corresponding internal type. This is useful for // typically be in unstructured or internal forms, depending on how the Builder was
// reflecting on user intent which may be lost after defaulting and conversions. // defined. If retrieved from the server, the Builder expects the mapping client to
VersionedObject runtime.Object // decide the final form. Use the AsVersioned, AsUnstructured, and AsInternal helpers
// Optional, this is the most recent value returned by the server if available // to alter the object versions.
Object runtime.Object Object runtime.Object
// Optional, this is the most recent resource version the server knows about for // Optional, this is the most recent resource version the server knows about for
// this type of resource. It may not match the resource version of the object, // this type of resource. It may not match the resource version of the object,
...@@ -96,17 +100,6 @@ type Info struct { ...@@ -96,17 +100,6 @@ type Info struct {
Export bool Export bool
} }
// NewInfo returns a new info object
func NewInfo(client RESTClient, mapping *meta.RESTMapping, namespace, name string, export bool) *Info {
return &Info{
Client: client,
Mapping: mapping,
Namespace: namespace,
Name: name,
Export: export,
}
}
// Visit implements Visitor // Visit implements Visitor
func (i *Info) Visit(fn VisitorFunc) error { func (i *Info) Visit(fn VisitorFunc) error {
return fn(i, nil) return fn(i, nil)
...@@ -176,6 +169,61 @@ func (i *Info) ResourceMapping() *meta.RESTMapping { ...@@ -176,6 +169,61 @@ func (i *Info) ResourceMapping() *meta.RESTMapping {
return i.Mapping return i.Mapping
} }
// Internal attempts to convert the provided object to an internal type or returns an error.
func (i *Info) Internal() (runtime.Object, error) {
return i.Mapping.ConvertToVersion(i.Object, i.Mapping.GroupVersionKind.GroupKind().WithVersion(runtime.APIVersionInternal).GroupVersion())
}
// AsInternal returns the object in internal form if possible, or i.Object if it cannot be
// converted.
func (i *Info) AsInternal() runtime.Object {
if obj, err := i.Internal(); err == nil {
return obj
}
return i.Object
}
// Versioned returns the object as a Go type in the mapping's version or returns an error.
func (i *Info) Versioned() (runtime.Object, error) {
return i.Mapping.ConvertToVersion(i.Object, i.Mapping.GroupVersionKind.GroupVersion())
}
// AsVersioned returns the object as a Go object in the external form if possible (matching the
// group version kind of the mapping, or i.Object if it cannot be converted.
func (i *Info) AsVersioned() runtime.Object {
if obj, err := i.Versioned(); err == nil {
return obj
}
return i.Object
}
// Unstructured returns the current object in unstructured form (as a runtime.Unstructured)
func (i *Info) Unstructured() (runtime.Unstructured, error) {
switch t := i.Object.(type) {
case runtime.Unstructured:
return t, nil
case *runtime.Unknown:
gvk := i.Mapping.GroupVersionKind
out, _, err := unstructured.UnstructuredJSONScheme.Decode(t.Raw, &gvk, nil)
return out.(runtime.Unstructured), err
default:
out := &unstructured.Unstructured{}
if err := i.Mapping.Convert(i.Object, out, nil); err != nil {
return nil, err
}
return out, nil
}
}
// AsUnstructured returns the object as a Go object in external form as a runtime.Unstructured
// (map of JSON equivalent values) or as i.Object if it cannot be converted.
func (i *Info) AsUnstructured() runtime.Object {
if out, err := i.Unstructured(); err == nil {
return out
}
return i.Object
}
// VisitorList implements Visit for the sub visitors it contains. The first error // VisitorList implements Visit for the sub visitors it contains. The first error
// returned from a child Visitor will terminate iteration. // returned from a child Visitor will terminate iteration.
type VisitorList []Visitor type VisitorList []Visitor
...@@ -388,10 +436,7 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error { ...@@ -388,10 +436,7 @@ func (v FlattenListVisitor) Visit(fn VisitorFunc) error {
if err != nil { if err != nil {
return fn(info, nil) return fn(info, nil)
} }
if errs := runtime.DecodeList(items, struct { if errs := runtime.DecodeList(items, v.Mapper.Decoder); len(errs) > 0 {
runtime.ObjectTyper
runtime.Decoder
}{v.Mapper, v.Mapper.Decoder}); len(errs) > 0 {
return utilerrors.NewAggregate(errs) return utilerrors.NewAggregate(errs)
} }
......
...@@ -19,7 +19,6 @@ package kubectl ...@@ -19,7 +19,6 @@ package kubectl
import ( import (
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/printers" "k8s.io/kubernetes/pkg/printers"
) )
...@@ -57,10 +56,6 @@ func filterPods(obj runtime.Object, options printers.PrintOptions) bool { ...@@ -57,10 +56,6 @@ func filterPods(obj runtime.Object, options printers.PrintOptions) bool {
// Filter loops through a collection of FilterFuncs until it finds one that can filter the given resource // Filter loops through a collection of FilterFuncs until it finds one that can filter the given resource
func (f Filters) Filter(obj runtime.Object, opts *printers.PrintOptions) (bool, error) { func (f Filters) Filter(obj runtime.Object, opts *printers.PrintOptions) (bool, error) {
// check if the object is unstructured. If so, let's attempt to convert it to a type we can understand
// before apply filter func.
obj, _ = DecodeUnknownObject(obj)
for _, filter := range f { for _, filter := range f {
if ok := filter(obj, *opts); ok { if ok := filter(obj, *opts); ok {
return true, nil return true, nil
...@@ -68,19 +63,3 @@ func (f Filters) Filter(obj runtime.Object, opts *printers.PrintOptions) (bool, ...@@ -68,19 +63,3 @@ func (f Filters) Filter(obj runtime.Object, opts *printers.PrintOptions) (bool,
} }
return false, nil return false, nil
} }
// check if the object is unstructured. If so, let's attempt to convert it to a type we can understand.
func DecodeUnknownObject(obj runtime.Object) (runtime.Object, error) {
var err error
switch obj.(type) {
case runtime.Unstructured, *runtime.Unknown:
if objBytes, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(), obj); err == nil {
if decodedObj, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), objBytes); err == nil {
obj = decodedObj
}
}
}
return obj, err
}
...@@ -34,6 +34,7 @@ go_library( ...@@ -34,6 +34,7 @@ go_library(
"firsthit_restmapper.go", "firsthit_restmapper.go",
"help.go", "help.go",
"interfaces.go", "interfaces.go",
"lazy.go",
"meta.go", "meta.go",
"multirestmapper.go", "multirestmapper.go",
"priority.go", "priority.go",
......
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package meta
import (
"sync"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// lazyObject defers loading the mapper and typer until necessary.
type lazyObject struct {
loader func() (RESTMapper, runtime.ObjectTyper, error)
lock sync.Mutex
loaded bool
err error
mapper RESTMapper
typer runtime.ObjectTyper
}
// NewLazyObjectLoader handles unrecoverable errors when creating a RESTMapper / ObjectTyper by
// returning those initialization errors when the interface methods are invoked. This defers the
// initialization and any server calls until a client actually needs to perform the action.
func NewLazyObjectLoader(fn func() (RESTMapper, runtime.ObjectTyper, error)) (RESTMapper, runtime.ObjectTyper) {
obj := &lazyObject{loader: fn}
return obj, obj
}
// init lazily loads the mapper and typer, returning an error if initialization has failed.
func (o *lazyObject) init() error {
o.lock.Lock()
defer o.lock.Unlock()
if o.loaded {
return o.err
}
o.mapper, o.typer, o.err = o.loader()
o.loaded = true
return o.err
}
var _ RESTMapper = &lazyObject{}
var _ runtime.ObjectTyper = &lazyObject{}
func (o *lazyObject) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
if err := o.init(); err != nil {
return schema.GroupVersionKind{}, err
}
return o.mapper.KindFor(resource)
}
func (o *lazyObject) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
if err := o.init(); err != nil {
return []schema.GroupVersionKind{}, err
}
return o.mapper.KindsFor(resource)
}
func (o *lazyObject) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) {
if err := o.init(); err != nil {
return schema.GroupVersionResource{}, err
}
return o.mapper.ResourceFor(input)
}
func (o *lazyObject) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
if err := o.init(); err != nil {
return []schema.GroupVersionResource{}, err
}
return o.mapper.ResourcesFor(input)
}
func (o *lazyObject) RESTMapping(gk schema.GroupKind, versions ...string) (*RESTMapping, error) {
if err := o.init(); err != nil {
return nil, err
}
return o.mapper.RESTMapping(gk, versions...)
}
func (o *lazyObject) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
if err := o.init(); err != nil {
return nil, err
}
return o.mapper.RESTMappings(gk, versions...)
}
func (o *lazyObject) ResourceSingularizer(resource string) (singular string, err error) {
if err := o.init(); err != nil {
return "", err
}
return o.mapper.ResourceSingularizer(resource)
}
func (o *lazyObject) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
if err := o.init(); err != nil {
return nil, false, err
}
return o.typer.ObjectKinds(obj)
}
func (o *lazyObject) Recognizes(gvk schema.GroupVersionKind) bool {
if err := o.init(); err != nil {
return false
}
return o.typer.Recognizes(gvk)
}
...@@ -21,8 +21,24 @@ import ( ...@@ -21,8 +21,24 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
// InterfacesForUnstructuredConversion returns VersionInterfaces suitable for
// dealing with unstructured.Unstructured objects and supports conversion
// from typed objects (provided by parent) to untyped objects.
func InterfacesForUnstructuredConversion(parent VersionInterfacesFunc) VersionInterfacesFunc {
return func(version schema.GroupVersion) (*VersionInterfaces, error) {
if i, err := parent(version); err == nil {
return &VersionInterfaces{
ObjectConvertor: i.ObjectConvertor,
MetadataAccessor: NewAccessor(),
}, nil
}
return InterfacesForUnstructured(version)
}
}
// InterfacesForUnstructured returns VersionInterfaces suitable for // InterfacesForUnstructured returns VersionInterfaces suitable for
// dealing with unstructured.Unstructured objects. // dealing with unstructured.Unstructured objects. It will return errors for
// other conversions.
func InterfacesForUnstructured(schema.GroupVersion) (*VersionInterfaces, error) { func InterfacesForUnstructured(schema.GroupVersion) (*VersionInterfaces, error) {
return &VersionInterfaces{ return &VersionInterfaces{
ObjectConvertor: &unstructured.UnstructuredObjectConverter{}, ObjectConvertor: &unstructured.UnstructuredObjectConverter{},
......
...@@ -17,22 +17,28 @@ limitations under the License. ...@@ -17,22 +17,28 @@ limitations under the License.
package discovery package discovery
import ( import (
"fmt" "reflect"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
// UnstructuredObjectTyper provides a runtime.ObjectTyper implmentation for // UnstructuredObjectTyper provides a runtime.ObjectTyper implementation for
// runtime.Unstructured object based on discovery information. // runtime.Unstructured object based on discovery information.
type UnstructuredObjectTyper struct { type UnstructuredObjectTyper struct {
registered map[schema.GroupVersionKind]bool registered map[schema.GroupVersionKind]bool
typers []runtime.ObjectTyper
} }
// NewUnstructuredObjectTyper returns a runtime.ObjectTyper for // NewUnstructuredObjectTyper returns a runtime.ObjectTyper for
// unstructred objects based on discovery information. // unstructured objects based on discovery information. It accepts a list of fallback typers
func NewUnstructuredObjectTyper(groupResources []*APIGroupResources) *UnstructuredObjectTyper { // for handling objects that are not runtime.Unstructured. It does not delegate the Recognizes
dot := &UnstructuredObjectTyper{registered: make(map[schema.GroupVersionKind]bool)} // check, only ObjectKinds.
func NewUnstructuredObjectTyper(groupResources []*APIGroupResources, typers ...runtime.ObjectTyper) *UnstructuredObjectTyper {
dot := &UnstructuredObjectTyper{
registered: make(map[schema.GroupVersionKind]bool),
typers: typers,
}
for _, group := range groupResources { for _, group := range groupResources {
for _, discoveryVersion := range group.Group.Versions { for _, discoveryVersion := range group.Group.Versions {
resources, ok := group.VersionedResources[discoveryVersion.Version] resources, ok := group.VersionedResources[discoveryVersion.Version]
...@@ -55,17 +61,29 @@ func NewUnstructuredObjectTyper(groupResources []*APIGroupResources) *Unstructur ...@@ -55,17 +61,29 @@ func NewUnstructuredObjectTyper(groupResources []*APIGroupResources) *Unstructur
// because runtime.Unstructured object should always have group,version,kind // because runtime.Unstructured object should always have group,version,kind
// information set. // information set.
func (d *UnstructuredObjectTyper) ObjectKinds(obj runtime.Object) (gvks []schema.GroupVersionKind, unversionedType bool, err error) { func (d *UnstructuredObjectTyper) ObjectKinds(obj runtime.Object) (gvks []schema.GroupVersionKind, unversionedType bool, err error) {
if _, ok := obj.(runtime.Unstructured); !ok { if _, ok := obj.(runtime.Unstructured); ok {
return nil, false, fmt.Errorf("type %T is invalid for dynamic object typer", obj) gvk := obj.GetObjectKind().GroupVersionKind()
if len(gvk.Kind) == 0 {
return nil, false, runtime.NewMissingKindErr("object has no kind field ")
}
if len(gvk.Version) == 0 {
return nil, false, runtime.NewMissingVersionErr("object has no apiVersion field")
}
return []schema.GroupVersionKind{gvk}, false, nil
} }
gvk := obj.GetObjectKind().GroupVersionKind() var lastErr error
if len(gvk.Kind) == 0 { for _, typer := range d.typers {
return nil, false, runtime.NewMissingKindErr("unstructured object has no kind") gvks, unversioned, err := typer.ObjectKinds(obj)
if err != nil {
lastErr = err
continue
}
return gvks, unversioned, nil
} }
if len(gvk.Version) == 0 { if lastErr == nil {
return nil, false, runtime.NewMissingVersionErr("unstructured object has no version") lastErr = runtime.NewNotRegisteredErrForType(reflect.TypeOf(obj))
} }
return []schema.GroupVersionKind{gvk}, false, nil return nil, false, lastErr
} }
// Recognizes returns true if the provided group,version,kind was in the // Recognizes returns true if the provided group,version,kind was in the
......
...@@ -84,7 +84,7 @@ func NewObjectTyper(resources []*metav1.APIResourceList) (runtime.ObjectTyper, e ...@@ -84,7 +84,7 @@ func NewObjectTyper(resources []*metav1.APIResourceList) (runtime.ObjectTyper, e
// information. // information.
func (ot *ObjectTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) { func (ot *ObjectTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
if _, ok := obj.(*unstructured.Unstructured); !ok { if _, ok := obj.(*unstructured.Unstructured); !ok {
return nil, false, fmt.Errorf("type %T is invalid for dynamic object typer", obj) return nil, false, fmt.Errorf("type %T is invalid for determining dynamic object types", obj)
} }
return []schema.GroupVersionKind{obj.GetObjectKind().GroupVersionKind()}, false, nil return []schema.GroupVersionKind{obj.GetObjectKind().GroupVersionKind()}, false, nil
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment