Merge pull request #3196 from smarterclayton/allow_create_to_span_resources

Allow create to handle multiple resources, remove createall
This commit is contained in:
bgrant0607 2015-01-13 12:23:50 -08:00
commit de2e298fa9
18 changed files with 220 additions and 172 deletions

View File

@ -4,5 +4,5 @@
### Usage ### Usage
``` ```
$ flags2yaml image=dockerfile/nginx | simplegen - | cluster/kubectl.sh createall -f - $ flags2yaml image=dockerfile/nginx | simplegen - | cluster/kubectl.sh create -f -
``` ```

View File

@ -41,8 +41,8 @@ portSpec: 10001:6379
``` ```
Output: Output:
``` ```
$ simplegen redismaster.yaml | cluster/kubectl.sh createall -f - $ simplegen redismaster.yaml | cluster/kubectl.sh create -f -
$ simplegen redisslave.yaml | cluster/kubectl.sh createall -f - $ simplegen redisslave.yaml | cluster/kubectl.sh create -f -
$ cluster/kubectl.sh get services $ cluster/kubectl.sh get services
NAME LABELS SELECTOR IP PORT NAME LABELS SELECTOR IP PORT
kubernetes-ro component=apiserver,provider=kubernetes 10.0.0.2 80 kubernetes-ro component=apiserver,provider=kubernetes 10.0.0.2 80

View File

@ -16,8 +16,8 @@ limitations under the License.
// simplegen is a tool to generate simple services from a simple description // simplegen is a tool to generate simple services from a simple description
// //
// $ simplegen myservice.json | kubectl createall -f - // $ simplegen myservice.json | kubectl create -f -
// $ simplegen myservice.yaml | kubectl createall -f - // $ simplegen myservice.yaml | kubectl create -f -
// //
// This is completely separate from kubectl at the moment, until we figure out // This is completely separate from kubectl at the moment, until we figure out
// what the right integration approach is. // what the right integration approach is.

View File

@ -17,8 +17,8 @@ limitations under the License.
// srvexpand is a tool to generate non-trivial but regular services // srvexpand is a tool to generate non-trivial but regular services
// from a description free of most boilerplate // from a description free of most boilerplate
// //
// $ srvexpand myservice.json | kubectl createall -f - // $ srvexpand myservice.json | kubectl create -f -
// $ srvexpand myservice.yaml | kubectl createall -f - // $ srvexpand myservice.yaml | kubectl create -f -
// //
// This is completely separate from kubectl at the moment, until we figure out // This is completely separate from kubectl at the moment, until we figure out
// what the right integration approach is. // what the right integration approach is.

View File

@ -48,16 +48,15 @@ type Factory struct {
clients *clientCache clients *clientCache
flags *pflag.FlagSet flags *pflag.FlagSet
Mapper meta.RESTMapper // Returns interfaces for dealing with arbitrary runtime.Objects.
Typer runtime.ObjectTyper Object func(cmd *cobra.Command) (meta.RESTMapper, runtime.ObjectTyper)
// Returns a client for accessing Kubernetes resources or an error. // Returns a client for accessing Kubernetes resources or an error.
Client func(cmd *cobra.Command) (*client.Client, error) Client func(cmd *cobra.Command) (*client.Client, error)
// Returns a client.Config for accessing the Kubernetes server. // Returns a client.Config for accessing the Kubernetes server.
ClientConfig func(cmd *cobra.Command) (*client.Config, error) ClientConfig func(cmd *cobra.Command) (*client.Config, error)
// Returns a RESTClient for working with the specified RESTMapping or an error. This is intended // Returns a RESTClient for working with the specified RESTMapping or an error. This is intended
// for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer. // for working with arbitrary resources and is not guaranteed to point to a Kubernetes APIServer.
RESTClient func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error) RESTClient func(cmd *cobra.Command, mapping *meta.RESTMapping) (resource.RESTClient, error)
// Returns a Describer for displaying the specified RESTMapping type or an error. // Returns a Describer for displaying the specified RESTMapping type or an error.
Describer func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Describer, error) Describer func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Describer, error)
// Returns a Printer for formatting objects of the given type or an error. // Returns a Printer for formatting objects of the given type or an error.
@ -81,16 +80,17 @@ func NewFactory() *Factory {
clients: clients, clients: clients,
flags: flags, flags: flags,
Mapper: mapper, Object: func(cmd *cobra.Command) (meta.RESTMapper, runtime.ObjectTyper) {
Typer: api.Scheme, version := GetFlagString(cmd, "api-version")
return kubectl.OutputVersionMapper{mapper, version}, api.Scheme
},
Client: func(cmd *cobra.Command) (*client.Client, error) { Client: func(cmd *cobra.Command) (*client.Client, error) {
return clients.ClientForVersion("") return clients.ClientForVersion("")
}, },
ClientConfig: func(cmd *cobra.Command) (*client.Config, error) { ClientConfig: func(cmd *cobra.Command) (*client.Config, error) {
return clients.ClientConfigForVersion("") return clients.ClientConfigForVersion("")
}, },
RESTClient: func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error) { RESTClient: func(cmd *cobra.Command, mapping *meta.RESTMapping) (resource.RESTClient, error) {
client, err := clients.ClientForVersion(mapping.APIVersion) client, err := clients.ClientForVersion(mapping.APIVersion)
if err != nil { if err != nil {
return nil, err return nil, err
@ -165,7 +165,6 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
cmds.AddCommand(f.NewCmdGet(out)) cmds.AddCommand(f.NewCmdGet(out))
cmds.AddCommand(f.NewCmdDescribe(out)) cmds.AddCommand(f.NewCmdDescribe(out))
cmds.AddCommand(f.NewCmdCreate(out)) cmds.AddCommand(f.NewCmdCreate(out))
cmds.AddCommand(f.NewCmdCreateAll(out))
cmds.AddCommand(f.NewCmdUpdate(out)) cmds.AddCommand(f.NewCmdUpdate(out))
cmds.AddCommand(f.NewCmdDelete(out)) cmds.AddCommand(f.NewCmdDelete(out))

View File

@ -25,8 +25,10 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
. "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd" . "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -90,19 +92,27 @@ func (t *testDescriber) Describe(namespace, name string) (output string, err err
} }
type testFactory struct { type testFactory struct {
Mapper meta.RESTMapper
Typer runtime.ObjectTyper
Client kubectl.RESTClient Client kubectl.RESTClient
Describer kubectl.Describer Describer kubectl.Describer
Printer kubectl.ResourcePrinter Printer kubectl.ResourcePrinter
Validator validation.Schema
Err error Err error
} }
func NewTestFactory() (*Factory, *testFactory, runtime.Codec) { func NewTestFactory() (*Factory, *testFactory, runtime.Codec) {
scheme, mapper, codec := newExternalScheme() scheme, mapper, codec := newExternalScheme()
t := &testFactory{} t := &testFactory{
Validator: validation.NullSchema{},
Mapper: mapper,
Typer: scheme,
}
return &Factory{ return &Factory{
Mapper: mapper, Object: func(*cobra.Command) (meta.RESTMapper, runtime.ObjectTyper) {
Typer: scheme, return t.Mapper, t.Typer
RESTClient: func(*cobra.Command, *meta.RESTMapping) (kubectl.RESTClient, error) { },
RESTClient: func(*cobra.Command, *meta.RESTMapping) (resource.RESTClient, error) {
return t.Client, t.Err return t.Client, t.Err
}, },
Describer: func(*cobra.Command, *meta.RESTMapping) (kubectl.Describer, error) { Describer: func(*cobra.Command, *meta.RESTMapping) (kubectl.Describer, error) {
@ -111,15 +121,21 @@ func NewTestFactory() (*Factory, *testFactory, runtime.Codec) {
Printer: func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) { Printer: func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
return t.Printer, t.Err return t.Printer, t.Err
}, },
Validator: func(cmd *cobra.Command) (validation.Schema, error) {
return t.Validator, t.Err
},
}, t, codec }, t, codec
} }
func NewAPIFactory() (*Factory, *testFactory, runtime.Codec) { func NewAPIFactory() (*Factory, *testFactory, runtime.Codec) {
t := &testFactory{} t := &testFactory{
Validator: validation.NullSchema{},
}
return &Factory{ return &Factory{
Mapper: latest.RESTMapper, Object: func(*cobra.Command) (meta.RESTMapper, runtime.ObjectTyper) {
Typer: api.Scheme, return latest.RESTMapper, api.Scheme
RESTClient: func(*cobra.Command, *meta.RESTMapping) (kubectl.RESTClient, error) { },
RESTClient: func(*cobra.Command, *meta.RESTMapping) (resource.RESTClient, error) {
return t.Client, t.Err return t.Client, t.Err
}, },
Describer: func(*cobra.Command, *meta.RESTMapping) (kubectl.Describer, error) { Describer: func(*cobra.Command, *meta.RESTMapping) (kubectl.Describer, error) {
@ -128,6 +144,9 @@ func NewAPIFactory() (*Factory, *testFactory, runtime.Codec) {
Printer: func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) { Printer: func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
return t.Printer, t.Err return t.Printer, t.Err
}, },
Validator: func(cmd *cobra.Command) (validation.Schema, error) {
return t.Validator, t.Err
},
}, t, latest.Codec }, t, latest.Codec
} }

View File

@ -20,11 +20,16 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
) )
func (f *Factory) NewCmdCreate(out io.Writer) *cobra.Command { func (f *Factory) NewCmdCreate(out io.Writer) *cobra.Command {
flags := &struct {
Filenames util.StringList
}{}
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "create -f filename", Use: "create -f filename",
Short: "Create a resource by filename or stdin", Short: "Create a resource by filename or stdin",
@ -39,29 +44,35 @@ Examples:
$ cat pod.json | kubectl create -f - $ cat pod.json | kubectl create -f -
<create a pod based on the json passed into stdin>`, <create a pod based on the json passed into stdin>`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
filename := GetFlagString(cmd, "filename")
if len(filename) == 0 {
usageError(cmd, "Must specify filename to create")
}
schema, err := f.Validator(cmd) schema, err := f.Validator(cmd)
checkErr(err) checkErr(err)
mapping, namespace, name, data := ResourceFromFile(cmd, filename, f.Typer, f.Mapper, schema)
client, err := f.RESTClient(cmd, mapping)
checkErr(err)
// use the default namespace if not specified, or check for conflict with the file's namespace mapper, typer := f.Object(cmd)
if len(namespace) == 0 { r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
namespace = GetKubeNamespace(cmd) ContinueOnError().
} else { NamespaceParam(GetKubeNamespace(cmd)).RequireNamespace().
err = CompareNamespaceFromFile(cmd, namespace) FilenameParam(flags.Filenames...).
checkErr(err) Flatten().
} Do()
err = resource.NewHelper(client, mapping).Create(namespace, true, data) err = r.Visit(func(info *resource.Info) error {
data, err := info.Mapping.Codec.Encode(info.Object)
if err != nil {
return err
}
if err := schema.ValidateBytes(data); err != nil {
return err
}
if err := resource.NewHelper(info.Client, info.Mapping).Create(info.Namespace, true, data); err != nil {
return err
}
// TODO: if generation of names added to server side, change this to use the server's name
fmt.Fprintf(out, "%s\n", info.Name)
return nil
})
checkErr(err) checkErr(err)
fmt.Fprintf(out, "%s\n", name)
}, },
} }
cmd.Flags().StringP("filename", "f", "", "Filename or URL to file to use to create the resource") cmd.Flags().VarP(&flags.Filenames, "filename", "f", "Filename, directory, or URL to file to use to create the resource")
return cmd return cmd
} }

View File

@ -0,0 +1,120 @@
/*
Copyright 2014 Google Inc. All rights reserved.
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 cmd_test
import (
"bytes"
"net/http"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
)
func TestCreateObject(t *testing.T) {
pods, _ := testData()
f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{}
tf.Client = &client.FakeRESTClient{
Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/ns/test/pods" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &pods.Items[0])}, nil
default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil
}
}),
}
buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdCreate(buf)
cmd.Flags().String("namespace", "test", "")
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json")
cmd.Run(cmd, []string{})
// uses the name from the file, not the response
if buf.String() != "redis-master\n" {
t.Errorf("unexpected output: %s", buf.String())
}
}
func TestCreateMultipleObject(t *testing.T) {
pods, svc := testData()
f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{}
tf.Client = &client.FakeRESTClient{
Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/ns/test/pods" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &pods.Items[0])}, nil
case p == "/ns/test/services" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil
default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil
}
}),
}
buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdCreate(buf)
cmd.Flags().String("namespace", "test", "")
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Run(cmd, []string{})
if buf.String() != "redis-master\nfrontend\n" {
t.Errorf("unexpected output: %s", buf.String())
}
}
func TestCreateDirectory(t *testing.T) {
pods, svc := testData()
f, tf, codec := NewAPIFactory()
tf.Printer = &testPrinter{}
tf.Client = &client.FakeRESTClient{
Codec: codec,
Client: client.HTTPClientFunc(func(req *http.Request) (*http.Response, error) {
switch p, m := req.URL.Path, req.Method; {
case p == "/ns/test/pods" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &pods.Items[0])}, nil
case p == "/ns/test/services" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil
case p == "/ns/test/replicationcontrollers" && m == "POST":
return &http.Response{StatusCode: 201, Body: objBody(codec, &svc.Items[0])}, nil
default:
t.Fatalf("unexpected request: %#v\n%#v", req.URL, req)
return nil, nil
}
}),
}
buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdCreate(buf)
cmd.Flags().String("namespace", "test", "")
cmd.Flags().Set("filename", "../../../examples/guestbook")
cmd.Run(cmd, []string{})
if buf.String() != "frontendController\nfrontend\nredis-master\nredis-master\nredisSlaveController\nredisslave\n" {
t.Errorf("unexpected output: %s", buf.String())
}
}

View File

@ -1,120 +0,0 @@
/*
Copyright 2014 Google Inc. All rights reserved.
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 cmd
import (
"fmt"
"io"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/config"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/ghodss/yaml"
"github.com/golang/glog"
"github.com/spf13/cobra"
)
// DataToObjects converts the raw JSON data into API objects
func DataToObjects(m meta.RESTMapper, t runtime.ObjectTyper, data []byte) (result []runtime.Object, errors []error) {
configObj := []runtime.RawExtension{}
if err := yaml.Unmarshal(data, &configObj); err != nil {
errors = append(errors, fmt.Errorf("config unmarshal: %v", err))
return result, errors
}
for i, in := range configObj {
version, kind, err := t.DataVersionAndKind(in.RawJSON)
if err != nil {
errors = append(errors, fmt.Errorf("item[%d] kind: %v", i, err))
continue
}
mapping, err := m.RESTMapping(kind, version)
if err != nil {
errors = append(errors, fmt.Errorf("item[%d] mapping: %v", i, err))
continue
}
obj, err := mapping.Codec.Decode(in.RawJSON)
if err != nil {
errors = append(errors, fmt.Errorf("item[%d] decode: %v", i, err))
continue
}
result = append(result, obj)
}
return
}
func (f *Factory) NewCmdCreateAll(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "createall [-d directory] [-f filename]",
Short: "Create all resources specified in a directory, filename or stdin",
Long: `Create all resources contained in JSON file specified in a directory, filename or stdin
JSON and YAML formats are accepted.
Examples:
$ kubectl createall -d configs/
<creates all resources listed in JSON or YAML files, found recursively under the configs directory>
$ kubectl createall -f config.json
<creates all resources listed in config.json>
$ cat config.json | kubectl apply -f -
<creates all resources listed in config.json>`,
Run: func(cmd *cobra.Command, args []string) {
clientFunc := func(mapper *meta.RESTMapping) (config.RESTClientPoster, error) {
client, err := f.RESTClient(cmd, mapper)
checkErr(err)
return client, nil
}
filename := GetFlagString(cmd, "filename")
directory := GetFlagString(cmd, "directory")
if (len(filename) == 0 && len(directory) == 0) || (len(filename) != 0 && len(directory) != 0) {
usageError(cmd, "Must pass a directory or filename to update")
}
files := []string{}
if len(filename) != 0 {
files = append(files, filename)
} else {
files = append(GetFilesFromDir(directory, ".json"), GetFilesFromDir(directory, ".yaml")...)
}
for _, filename := range files {
data, err := ReadConfigData(filename)
checkErr(err)
items, errs := DataToObjects(f.Mapper, f.Typer, data)
applyErrs := config.CreateObjects(f.Typer, f.Mapper, clientFunc, items)
errs = append(errs, applyErrs...)
if len(errs) > 0 {
for _, e := range errs {
glog.Error(e)
}
}
}
},
}
cmd.Flags().StringP("directory", "d", "", "Directory of JSON or YAML files to use to update the resource")
cmd.Flags().StringP("filename", "f", "", "Filename or URL to file to use to update the resource")
return cmd
}

View File

@ -59,7 +59,8 @@ Examples:
checkErr(err) checkErr(err)
selector := GetFlagString(cmd, "selector") selector := GetFlagString(cmd, "selector")
found := 0 found := 0
ResourcesFromArgsOrFile(cmd, args, filename, selector, f.Typer, f.Mapper, f.RESTClient, schema, true).Visit(func(r *resource.Info) error { mapper, typer := f.Object(cmd)
ResourcesFromArgsOrFile(cmd, args, filename, selector, typer, mapper, f.RESTClient, schema, true).Visit(func(r *resource.Info) error {
found++ found++
if err := resource.NewHelper(r.Client, r.Mapping).Delete(r.Namespace, r.Name); err != nil { if err := resource.NewHelper(r.Client, r.Mapping).Delete(r.Namespace, r.Name); err != nil {
return err return err

View File

@ -32,7 +32,8 @@ func (f *Factory) NewCmdDescribe(out io.Writer) *cobra.Command {
This command joins many API calls together to form a detailed description of a This command joins many API calls together to form a detailed description of a
given resource.`, given resource.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
mapping, namespace, name := ResourceFromArgs(cmd, args, f.Mapper) mapper, _ := f.Object(cmd)
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper)
describer, err := f.Describer(cmd, mapping) describer, err := f.Describer(cmd, mapping)
checkErr(err) checkErr(err)

View File

@ -74,11 +74,12 @@ Examples:
// TODO: return an error instead of using glog.Fatal and checkErr // TODO: return an error instead of using glog.Fatal and checkErr
func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) { func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
selector := GetFlagString(cmd, "selector") selector := GetFlagString(cmd, "selector")
mapper, typer := f.Object(cmd)
// handle watch separately since we cannot watch multiple resource types // handle watch separately since we cannot watch multiple resource types
isWatch, isWatchOnly := GetFlagBool(cmd, "watch"), GetFlagBool(cmd, "watch-only") isWatch, isWatchOnly := GetFlagBool(cmd, "watch"), GetFlagBool(cmd, "watch-only")
if isWatch || isWatchOnly { if isWatch || isWatchOnly {
r := resource.NewBuilder(f.Mapper, f.Typer, ClientMapperForCommand(cmd, f)). r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace(). NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace().
SelectorParam(selector). SelectorParam(selector).
ResourceTypeOrNameArgs(args...). ResourceTypeOrNameArgs(args...).
@ -117,7 +118,7 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) {
printer, generic, err := printerForCommand(cmd) printer, generic, err := printerForCommand(cmd)
checkErr(err) checkErr(err)
b := resource.NewBuilder(f.Mapper, f.Typer, ClientMapperForCommand(cmd, f)). b := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace(). NamespaceParam(GetKubeNamespace(cmd)).DefaultNamespace().
SelectorParam(selector). SelectorParam(selector).
ResourceTypeOrNameArgs(args...). ResourceTypeOrNameArgs(args...).

View File

@ -90,8 +90,8 @@ func TestGetUnknownSchemaObject(t *testing.T) {
// 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 TestGetSchemaObject(t *testing.T) { func TestGetSchemaObject(t *testing.T) {
f, tf, _ := NewTestFactory() f, tf, _ := NewTestFactory()
f.Mapper = latest.RESTMapper tf.Mapper = latest.RESTMapper
f.Typer = api.Scheme tf.Typer = api.Scheme
codec := latest.Codec codec := latest.Codec
tf.Printer = &testPrinter{} tf.Printer = &testPrinter{}
tf.Client = &client.FakeRESTClient{ tf.Client = &client.FakeRESTClient{

View File

@ -24,7 +24,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -39,7 +38,7 @@ func ResourcesFromArgsOrFile(
filename, selector string, filename, selector string,
typer runtime.ObjectTyper, typer runtime.ObjectTyper,
mapper meta.RESTMapper, mapper meta.RESTMapper,
clientBuilder func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.RESTClient, error), clientBuilder func(cmd *cobra.Command, mapping *meta.RESTMapping) (resource.RESTClient, error),
schema validation.Schema, schema validation.Schema,
requireNames bool, requireNames bool,
) resource.Visitor { ) resource.Visitor {

View File

@ -61,7 +61,8 @@ $ cat frontend-v2.json | kubectl rollingupdate frontend-v1 -f -
oldName := args[0] oldName := args[0]
schema, err := f.Validator(cmd) schema, err := f.Validator(cmd)
checkErr(err) checkErr(err)
mapping, namespace, newName, data := ResourceFromFile(cmd, filename, f.Typer, f.Mapper, schema) mapper, typer := f.Object(cmd)
mapping, namespace, newName, data := ResourceFromFile(cmd, filename, typer, mapper, schema)
if mapping.Kind != "ReplicationController" { if mapping.Kind != "ReplicationController" {
usageError(cmd, "%s does not specify a valid ReplicationController", filename) usageError(cmd, "%s does not specify a valid ReplicationController", filename)
} }

View File

@ -45,7 +45,8 @@ Examples:
} }
schema, err := f.Validator(cmd) schema, err := f.Validator(cmd)
checkErr(err) checkErr(err)
mapping, namespace, name, data := ResourceFromFile(cmd, filename, f.Typer, f.Mapper, schema) mapper, typer := f.Object(cmd)
mapping, namespace, name, data := ResourceFromFile(cmd, filename, typer, mapper, schema)
client, err := f.RESTClient(cmd, mapping) client, err := f.RESTClient(cmd, mapping)
checkErr(err) checkErr(err)

View File

@ -112,6 +112,19 @@ func makeImageList(spec *api.PodSpec) string {
return strings.Join(listOfImages(spec), ",") return strings.Join(listOfImages(spec), ",")
} }
// OutputVersionMapper is a RESTMapper that will prefer mappings that
// correspond to a preferred output version (if feasible)
type OutputVersionMapper struct {
meta.RESTMapper
OutputVersion string
}
// RESTMapping implements meta.RESTMapper by prepending the output version to the preferred version list.
func (m OutputVersionMapper) RESTMapping(kind string, versions ...string) (*meta.RESTMapping, error) {
preferred := append([]string{m.OutputVersion}, versions...)
return m.RESTMapper.RESTMapping(kind, preferred...)
}
// ShortcutExpander is a RESTMapper that can be used for Kubernetes // ShortcutExpander is a RESTMapper that can be used for Kubernetes
// resources. // resources.
type ShortcutExpander struct { type ShortcutExpander struct {

View File

@ -262,6 +262,8 @@ func (b *Builder) ContinueOnError() *Builder {
return b return b
} }
// SingleResourceType will cause the builder to error if the user specifies more than a single type
// of resource.
func (b *Builder) SingleResourceType() *Builder { func (b *Builder) SingleResourceType() *Builder {
b.singleResourceType = true b.singleResourceType = true
return b return b