Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow delete to work from directories and multiple sources #3198

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion contrib/flags2yaml/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

### Usage
```
$ flags2yaml image=dockerfile/nginx | simplegen - | cluster/kubectl.sh createall -f -
$ flags2yaml image=dockerfile/nginx | simplegen - | cluster/kubectl.sh create -f -
```
4 changes: 2 additions & 2 deletions contrib/simplegen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ portSpec: 10001:6379
```
Output:
```
$ simplegen redismaster.yaml | cluster/kubectl.sh createall -f -
$ simplegen redisslave.yaml | cluster/kubectl.sh createall -f -
$ simplegen redismaster.yaml | cluster/kubectl.sh create -f -
$ simplegen redisslave.yaml | cluster/kubectl.sh create -f -
$ cluster/kubectl.sh get services
NAME LABELS SELECTOR IP PORT
kubernetes-ro component=apiserver,provider=kubernetes 10.0.0.2 80
Expand Down
4 changes: 2 additions & 2 deletions contrib/simplegen/simplegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ limitations under the License.

// simplegen is a tool to generate simple services from a simple description
//
// $ simplegen myservice.json | kubectl createall -f -
// $ simplegen myservice.yaml | kubectl createall -f -
// $ simplegen myservice.json | kubectl create -f -
// $ simplegen myservice.yaml | kubectl create -f -
//
// This is completely separate from kubectl at the moment, until we figure out
// what the right integration approach is.
Expand Down
4 changes: 2 additions & 2 deletions contrib/srvexpand/srvexpand.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ limitations under the License.
// srvexpand is a tool to generate non-trivial but regular services
// from a description free of most boilerplate
//
// $ srvexpand myservice.json | kubectl createall -f -
// $ srvexpand myservice.yaml | kubectl createall -f -
// $ srvexpand myservice.json | kubectl create -f -
// $ srvexpand myservice.yaml | kubectl create -f -
//
// This is completely separate from kubectl at the moment, until we figure out
// what the right integration approach is.
Expand Down
26 changes: 17 additions & 9 deletions pkg/kubectl/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

Expand All @@ -42,20 +43,20 @@ const (

// Factory provides abstractions that allow the Kubectl command to be extended across multiple types
// of resources and different API sets.
// TODO: make the functions interfaces
type Factory struct {
clients *clientCache
flags *pflag.FlagSet

Mapper meta.RESTMapper
Typer runtime.ObjectTyper

// Returns interfaces for dealing with arbitrary runtime.Objects.
Object func(cmd *cobra.Command) (meta.RESTMapper, runtime.ObjectTyper)
// Returns a client for accessing Kubernetes resources or an error.
Client func(cmd *cobra.Command) (*client.Client, error)
// Returns a client.Config for accessing the Kubernetes server.
ClientConfig func(cmd *cobra.Command) (*client.Config, error)
// 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.
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.
Describer func(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.Describer, error)
// Returns a Printer for formatting objects of the given type or an error.
Expand All @@ -79,16 +80,17 @@ func NewFactory() *Factory {
clients: clients,
flags: flags,

Mapper: mapper,
Typer: api.Scheme,

Object: func(cmd *cobra.Command) (meta.RESTMapper, runtime.ObjectTyper) {
version := GetFlagString(cmd, "api-version")
return kubectl.OutputVersionMapper{mapper, version}, api.Scheme
},
Client: func(cmd *cobra.Command) (*client.Client, error) {
return clients.ClientForVersion("")
},
ClientConfig: func(cmd *cobra.Command) (*client.Config, error) {
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)
if err != nil {
return nil, err
Expand Down Expand Up @@ -163,7 +165,6 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
cmds.AddCommand(f.NewCmdGet(out))
cmds.AddCommand(f.NewCmdDescribe(out))
cmds.AddCommand(f.NewCmdCreate(out))
cmds.AddCommand(f.NewCmdCreateAll(out))
cmds.AddCommand(f.NewCmdUpdate(out))
cmds.AddCommand(f.NewCmdDelete(out))

Expand Down Expand Up @@ -218,6 +219,13 @@ func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
return clientConfig
}

// ClientMapperForCommand returns a ClientMapper for the given command and factory.
func ClientMapperForCommand(cmd *cobra.Command, f *Factory) resource.ClientMapper {
return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
return f.RESTClient(cmd, mapping)
})
}

func checkErr(err error) {
if err != nil {
glog.FatalDepth(1, err)
Expand Down
56 changes: 49 additions & 7 deletions pkg/kubectl/cmd/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@ import (
"io"
"io/ioutil"

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"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/cmd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -66,12 +70,12 @@ func newExternalScheme() (*runtime.Scheme, meta.RESTMapper, runtime.Codec) {
}

type testPrinter struct {
Obj runtime.Object
Err error
Objects []runtime.Object
Err error
}

func (t *testPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
t.Obj = obj
t.Objects = append(t.Objects, obj)
fmt.Fprintf(out, "%#v", obj)
return t.Err
}
Expand All @@ -88,19 +92,27 @@ func (t *testDescriber) Describe(namespace, name string) (output string, err err
}

type testFactory struct {
Mapper meta.RESTMapper
Typer runtime.ObjectTyper
Client kubectl.RESTClient
Describer kubectl.Describer
Printer kubectl.ResourcePrinter
Validator validation.Schema
Err error
}

func NewTestFactory() (*Factory, *testFactory, runtime.Codec) {
scheme, mapper, codec := newExternalScheme()
t := &testFactory{}
t := &testFactory{
Validator: validation.NullSchema{},
Mapper: mapper,
Typer: scheme,
}
return &Factory{
Mapper: mapper,
Typer: scheme,
RESTClient: func(*cobra.Command, *meta.RESTMapping) (kubectl.RESTClient, error) {
Object: func(*cobra.Command) (meta.RESTMapper, runtime.ObjectTyper) {
return t.Mapper, t.Typer
},
RESTClient: func(*cobra.Command, *meta.RESTMapping) (resource.RESTClient, error) {
return t.Client, t.Err
},
Describer: func(*cobra.Command, *meta.RESTMapping) (kubectl.Describer, error) {
Expand All @@ -109,9 +121,39 @@ func NewTestFactory() (*Factory, *testFactory, runtime.Codec) {
Printer: func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
return t.Printer, t.Err
},
Validator: func(cmd *cobra.Command) (validation.Schema, error) {
return t.Validator, t.Err
},
}, t, codec
}

func NewAPIFactory() (*Factory, *testFactory, runtime.Codec) {
t := &testFactory{
Validator: validation.NullSchema{},
}
return &Factory{
Object: func(*cobra.Command) (meta.RESTMapper, runtime.ObjectTyper) {
return latest.RESTMapper, api.Scheme
},
RESTClient: func(*cobra.Command, *meta.RESTMapping) (resource.RESTClient, error) {
return t.Client, t.Err
},
Describer: func(*cobra.Command, *meta.RESTMapping) (kubectl.Describer, error) {
return t.Describer, t.Err
},
Printer: func(cmd *cobra.Command, mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
return t.Printer, t.Err
},
Validator: func(cmd *cobra.Command) (validation.Schema, error) {
return t.Validator, t.Err
},
}, t, latest.Codec
}

func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser {
return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
}

func stringBody(body string) io.ReadCloser {
return ioutil.NopCloser(bytes.NewReader([]byte(body)))
}
47 changes: 29 additions & 18 deletions pkg/kubectl/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ import (
"fmt"
"io"

"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"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 {
flags := &struct {
Filenames util.StringList
}{}
cmd := &cobra.Command{
Use: "create -f filename",
Short: "Create a resource by filename or stdin",
Expand All @@ -39,29 +44,35 @@ Examples:
$ cat pod.json | kubectl create -f -
<create a pod based on the json passed into stdin>`,
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)
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
if len(namespace) == 0 {
namespace = GetKubeNamespace(cmd)
} else {
err = CompareNamespaceFromFile(cmd, namespace)
checkErr(err)
}
mapper, typer := f.Object(cmd)
r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
ContinueOnError().
NamespaceParam(GetKubeNamespace(cmd)).RequireNamespace().
FilenameParam(flags.Filenames...).
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)
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
}