refactor to move kubectl.cmd.Factory to kubect/cmd/util

pull/6/head
deads2k 2015-04-07 14:21:25 -04:00
parent 13b805fe1e
commit 6344cf3c3a
34 changed files with 664 additions and 611 deletions

View File

@ -25,6 +25,7 @@ import (
"strings" "strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -139,7 +140,7 @@ func main() {
// regardless of where we run. // regardless of where we run.
os.Setenv("HOME", "/home/username") os.Setenv("HOME", "/home/username")
//TODO os.Stdin should really be something like ioutil.Discard, but a Reader //TODO os.Stdin should really be something like ioutil.Discard, but a Reader
kubectl := cmd.NewFactory(nil).NewKubectlCommand(os.Stdin, ioutil.Discard, ioutil.Discard) kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
genMarkdown(kubectl, "", docsDir) genMarkdown(kubectl, "", docsDir)
for _, c := range kubectl.Commands() { for _, c := range kubectl.Commands() {
genMarkdown(c, "kubectl", docsDir) genMarkdown(c, "kubectl", docsDir)

View File

@ -25,6 +25,7 @@ import (
"strings" "strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/cpuguy83/go-md2man/mangen" "github.com/cpuguy83/go-md2man/mangen"
"github.com/russross/blackfriday" "github.com/russross/blackfriday"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -63,7 +64,7 @@ func main() {
// regardless of where we run. // regardless of where we run.
os.Setenv("HOME", "/home/username") os.Setenv("HOME", "/home/username")
//TODO os.Stdin should really be something like ioutil.Discard, but a Reader //TODO os.Stdin should really be something like ioutil.Discard, but a Reader
kubectl := cmd.NewFactory(nil).NewKubectlCommand(os.Stdin, ioutil.Discard, ioutil.Discard) kubectl := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, ioutil.Discard, ioutil.Discard)
genMarkdown(kubectl, "", docsDir) genMarkdown(kubectl, "", docsDir)
for _, c := range kubectl.Commands() { for _, c := range kubectl.Commands() {
genMarkdown(c, "kubectl", docsDir) genMarkdown(c, "kubectl", docsDir)

View File

@ -21,11 +21,12 @@ import (
"runtime" "runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
) )
func main() { func main() {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
cmd := cmd.NewFactory(nil).NewKubectlCommand(os.Stdin, os.Stdout, os.Stderr) cmd := cmd.NewKubectlCommand(cmdutil.NewFactory(nil), os.Stdin, os.Stdout, os.Stderr)
if err := cmd.Execute(); err != nil { if err := cmd.Execute(); err != nil {
os.Exit(1) os.Exit(1)
} }

View File

@ -23,10 +23,10 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
) )
func (f *Factory) NewCmdApiVersions(out io.Writer) *cobra.Command { func NewCmdApiVersions(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "api-versions", Use: "api-versions",
// apiversions is deprecated. // apiversions is deprecated.
@ -34,13 +34,13 @@ func (f *Factory) NewCmdApiVersions(out io.Writer) *cobra.Command {
Short: "Print available API versions.", Short: "Print available API versions.",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunApiVersions(f, out) err := RunApiVersions(f, out)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
return cmd return cmd
} }
func RunApiVersions(f *Factory, out io.Writer) error { func RunApiVersions(f *cmdutil.Factory, out io.Writer) error {
if os.Args[1] == "apiversions" { if os.Args[1] == "apiversions" {
printDeprecationWarning("api-versions", "apiversions") printDeprecationWarning("api-versions", "apiversions")
} }

View File

@ -23,14 +23,14 @@ import (
"strings" "strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/daviddengcn/go-colortext" "github.com/daviddengcn/go-colortext"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func (f *Factory) NewCmdClusterInfo(out io.Writer) *cobra.Command { func NewCmdClusterInfo(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "cluster-info", Use: "cluster-info",
// clusterinfo is deprecated. // clusterinfo is deprecated.
@ -39,13 +39,13 @@ func (f *Factory) NewCmdClusterInfo(out io.Writer) *cobra.Command {
Long: "Display addresses of the master and services with label kubernetes.io/cluster-service=true", Long: "Display addresses of the master and services with label kubernetes.io/cluster-service=true",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunClusterInfo(f, out, cmd) err := RunClusterInfo(f, out, cmd)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
return cmd return cmd
} }
func RunClusterInfo(factory *Factory, out io.Writer, cmd *cobra.Command) error { func RunClusterInfo(factory *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error {
if os.Args[1] == "clusterinfo" { if os.Args[1] == "clusterinfo" {
printDeprecationWarning("cluster-info", "clusterinfo") printDeprecationWarning("cluster-info", "clusterinfo")
} }

View File

@ -17,254 +17,17 @@ limitations under the License.
package cmd package cmd
import ( import (
"fmt"
"io" "io"
"os"
"strconv"
"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/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
cmdconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/config" cmdconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/config"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag"
) )
const (
FlagMatchBinaryVersion = "match-server-version"
)
// 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
// TODO: pass the various interfaces on the factory directly into the command constructors (so the
// commands are decoupled from the factory).
type Factory struct {
clients *clientCache
flags *pflag.FlagSet
// Returns interfaces for dealing with arbitrary runtime.Objects.
Object func() (meta.RESTMapper, runtime.ObjectTyper)
// Returns a client for accessing Kubernetes resources or an error.
Client func() (*client.Client, error)
// Returns a client.Config for accessing the Kubernetes server.
ClientConfig func() (*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(mapping *meta.RESTMapping) (resource.RESTClient, error)
// Returns a Describer for displaying the specified RESTMapping type or an error.
Describer func(mapping *meta.RESTMapping) (kubectl.Describer, error)
// Returns a Printer for formatting objects of the given type or an error.
Printer func(mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error)
// Returns a Resizer for changing the size of the specified RESTMapping type or an error
Resizer func(mapping *meta.RESTMapping) (kubectl.Resizer, error)
// Returns a Reaper for gracefully shutting down resources.
Reaper func(mapping *meta.RESTMapping) (kubectl.Reaper, error)
// PodSelectorForResource returns the pod selector associated with the provided resource name
// or an error.
PodSelectorForResource func(mapping *meta.RESTMapping, namespace, name string) (string, error)
// PortForResource returns the ports associated with the provided resource name or an error
PortsForResource func(mapping *meta.RESTMapping, namespace, name string) ([]string, error)
// Returns a schema that can validate objects stored on disk.
Validator func() (validation.Schema, error)
// Returns the default namespace to use in cases where no other namespace is specified
DefaultNamespace func() (string, error)
}
func getPorts(spec api.PodSpec) []string {
result := []string{}
for _, container := range spec.Containers {
for _, port := range container.Ports {
result = append(result, strconv.Itoa(port.ContainerPort))
}
}
return result
}
// NewFactory creates a factory with the default Kubernetes resources defined
// if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig.
// if optionalClientConfig is not nil, then this factory will make use of it.
func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
mapper := kubectl.ShortcutExpander{latest.RESTMapper}
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
clientConfig := optionalClientConfig
if optionalClientConfig == nil {
clientConfig = DefaultClientConfig(flags)
}
clients := &clientCache{
clients: make(map[string]*client.Client),
loader: clientConfig,
}
return &Factory{
clients: clients,
flags: flags,
Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
cfg, err := clientConfig.ClientConfig()
cmdutil.CheckErr(err)
cmdApiVersion := cfg.Version
return kubectl.OutputVersionMapper{mapper, cmdApiVersion}, api.Scheme
},
Client: func() (*client.Client, error) {
return clients.ClientForVersion("")
},
ClientConfig: func() (*client.Config, error) {
return clients.ClientConfigForVersion("")
},
RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
if err != nil {
return nil, err
}
return client.RESTClient, nil
},
Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
if err != nil {
return nil, err
}
describer, ok := kubectl.DescriberFor(mapping.Kind, client)
if !ok {
return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
}
return describer, nil
},
Printer: func(mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
return kubectl.NewHumanReadablePrinter(noHeaders), nil
},
PodSelectorForResource: func(mapping *meta.RESTMapping, namespace, name string) (string, error) {
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
client, err := clients.ClientForVersion("")
if err != nil {
return "", err
}
switch mapping.Kind {
case "ReplicationController":
rc, err := client.ReplicationControllers(namespace).Get(name)
if err != nil {
return "", err
}
return kubectl.MakeLabels(rc.Spec.Selector), nil
case "Pod":
rc, err := client.Pods(namespace).Get(name)
if err != nil {
return "", err
}
if len(rc.Labels) == 0 {
return "", fmt.Errorf("the pod has no labels and cannot be exposed")
}
return kubectl.MakeLabels(rc.Labels), nil
case "Service":
rc, err := client.ReplicationControllers(namespace).Get(name)
if err != nil {
return "", err
}
if rc.Spec.Selector == nil {
return "", fmt.Errorf("the service has no pod selector set")
}
return kubectl.MakeLabels(rc.Spec.Selector), nil
default:
return "", fmt.Errorf("it is not possible to get a pod selector from %s", mapping.Kind)
}
},
PortsForResource: func(mapping *meta.RESTMapping, namespace, name string) ([]string, error) {
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
client, err := clients.ClientForVersion("")
if err != nil {
return nil, err
}
switch mapping.Kind {
case "ReplicationController":
rc, err := client.ReplicationControllers(namespace).Get(name)
if err != nil {
return nil, err
}
return getPorts(rc.Spec.Template.Spec), nil
case "Pod":
pod, err := client.Pods(namespace).Get(name)
if err != nil {
return nil, err
}
return getPorts(pod.Spec), nil
default:
return nil, fmt.Errorf("it is not possible to get ports from %s", mapping.Kind)
}
},
Resizer: func(mapping *meta.RESTMapping) (kubectl.Resizer, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
if err != nil {
return nil, err
}
return kubectl.ResizerFor(mapping.Kind, client)
},
Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
if err != nil {
return nil, err
}
return kubectl.ReaperFor(mapping.Kind, client)
},
Validator: func() (validation.Schema, error) {
if flags.Lookup("validate").Value.String() == "true" {
client, err := clients.ClientForVersion("")
if err != nil {
return nil, err
}
return &clientSwaggerSchema{client, api.Scheme}, nil
}
return validation.NullSchema{}, nil
},
DefaultNamespace: func() (string, error) {
return clientConfig.Namespace()
},
}
}
// BindFlags adds any flags that are common to all kubectl sub commands.
func (f *Factory) BindFlags(flags *pflag.FlagSet) {
// any flags defined by external projects (not part of pflags)
util.AddAllFlagsToPFlagSet(flags)
// This is necessary as github.com/spf13/cobra doesn't support "global"
// pflags currently. See https://github.com/spf13/cobra/issues/44.
util.AddPFlagSetToPFlagSet(pflag.CommandLine, flags)
// Hack for global access to validation flag.
// TODO: Refactor out after configuration flag overhaul.
if f.flags.Lookup("validate") == nil {
f.flags.Bool("validate", false, "If true, use a schema to validate the input before sending it")
}
if f.flags != nil {
f.flags.VisitAll(func(flag *pflag.Flag) {
flags.AddFlag(flag)
})
}
// Globally persistent flags across all subcommands.
// TODO Change flag names to consts to allow safer lookup from subcommands.
// TODO Add a verbose flag that turns on glog logging. Probably need a way
// to do that automatically for every subcommand.
flags.BoolVar(&f.clients.matchVersion, FlagMatchBinaryVersion, false, "Require server version to match client version")
}
// NewKubectlCommand creates the `kubectl` command and its nested children. // NewKubectlCommand creates the `kubectl` command and its nested children.
func (f *Factory) NewKubectlCommand(in io.Reader, out, err io.Writer) *cobra.Command { func NewKubectlCommand(f *cmdutil.Factory, in io.Reader, out, err io.Writer) *cobra.Command {
// Parent command to which all subcommands are added. // Parent command to which all subcommands are added.
cmds := &cobra.Command{ cmds := &cobra.Command{
Use: "kubectl", Use: "kubectl",
@ -277,223 +40,39 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
f.BindFlags(cmds.PersistentFlags()) f.BindFlags(cmds.PersistentFlags())
cmds.AddCommand(f.NewCmdGet(out)) cmds.AddCommand(NewCmdGet(f, out))
cmds.AddCommand(f.NewCmdDescribe(out)) cmds.AddCommand(NewCmdDescribe(f, out))
cmds.AddCommand(f.NewCmdCreate(out)) cmds.AddCommand(NewCmdCreate(f, out))
cmds.AddCommand(f.NewCmdUpdate(out)) cmds.AddCommand(NewCmdUpdate(f, out))
cmds.AddCommand(f.NewCmdDelete(out)) cmds.AddCommand(NewCmdDelete(f, out))
cmds.AddCommand(NewCmdNamespace(out)) cmds.AddCommand(NewCmdNamespace(out))
cmds.AddCommand(f.NewCmdLog(out)) cmds.AddCommand(NewCmdLog(f, out))
cmds.AddCommand(f.NewCmdRollingUpdate(out)) cmds.AddCommand(NewCmdRollingUpdate(f, out))
cmds.AddCommand(f.NewCmdResize(out)) cmds.AddCommand(NewCmdResize(f, out))
cmds.AddCommand(f.NewCmdExec(in, out, err)) cmds.AddCommand(NewCmdExec(f, in, out, err))
cmds.AddCommand(f.NewCmdPortForward()) cmds.AddCommand(NewCmdPortForward(f))
cmds.AddCommand(f.NewCmdProxy(out)) cmds.AddCommand(NewCmdProxy(f, out))
cmds.AddCommand(f.NewCmdRunContainer(out)) cmds.AddCommand(NewCmdRunContainer(f, out))
cmds.AddCommand(f.NewCmdStop(out)) cmds.AddCommand(NewCmdStop(f, out))
cmds.AddCommand(f.NewCmdExposeService(out)) cmds.AddCommand(NewCmdExposeService(f, out))
cmds.AddCommand(f.NewCmdLabel(out)) cmds.AddCommand(NewCmdLabel(f, out))
cmds.AddCommand(cmdconfig.NewCmdConfig(out)) cmds.AddCommand(cmdconfig.NewCmdConfig(f, out))
cmds.AddCommand(f.NewCmdClusterInfo(out)) cmds.AddCommand(NewCmdClusterInfo(f, out))
cmds.AddCommand(f.NewCmdApiVersions(out)) cmds.AddCommand(NewCmdApiVersions(f, out))
cmds.AddCommand(f.NewCmdVersion(out)) cmds.AddCommand(NewCmdVersion(f, out))
return cmds return cmds
} }
// PrintObject prints an api object given command line flags to modify the output format
func (f *Factory) PrintObject(cmd *cobra.Command, obj runtime.Object, out io.Writer) error {
mapper, _ := f.Object()
_, kind, err := api.Scheme.ObjectVersionAndKind(obj)
if err != nil {
return err
}
mapping, err := mapper.RESTMapping(kind)
if err != nil {
return err
}
printer, err := f.PrinterForMapping(cmd, mapping)
if err != nil {
return err
}
return printer.PrintObj(obj, out)
}
// PrinterForMapping returns a printer suitable for displaying the provided resource type.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.ResourcePrinter, error) {
printer, ok, err := cmdutil.PrinterForCommand(cmd)
if err != nil {
return nil, err
}
if ok {
clientConfig, err := f.ClientConfig()
if err != nil {
return nil, err
}
defaultVersion := clientConfig.Version
version := cmdutil.OutputVersion(cmd, defaultVersion)
if len(version) == 0 {
version = mapping.APIVersion
}
if len(version) == 0 {
return nil, fmt.Errorf("you must specify an output-version when using this output format")
}
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version)
} else {
printer, err = f.Printer(mapping, cmdutil.GetFlagBool(cmd, "no-headers"))
if err != nil {
return nil, err
}
}
return printer, nil
}
// ClientMapperForCommand returns a ClientMapper for the factory.
func (f *Factory) ClientMapperForCommand() resource.ClientMapper {
return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
return f.RESTClient(mapping)
})
}
// DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy:
// 1. Use the kubeconfig builder. The number of merges and overrides here gets a little crazy. Stay with me.
// 1. Merge together the kubeconfig itself. This is done with the following hierarchy and merge rules:
// 1. CommandLineLocation - this parsed from the command line, so it must be late bound
// 2. EnvVarLocation
// 3. CurrentDirectoryLocation
// 4. HomeDirectoryLocation
// Empty filenames are ignored. Files with non-deserializable content produced errors.
// The first file to set a particular value or map key wins and the value or map key is never changed.
// This means that the first file to set CurrentContext will have its context preserved. It also means
// that if two files specify a "red-user", only values from the first file's red-user are used. Even
// non-conflicting entries from the second file's "red-user" are discarded.
// 2. Determine the context to use based on the first hit in this chain
// 1. command line argument - again, parsed from the command line, so it must be late bound
// 2. CurrentContext from the merged kubeconfig file
// 3. Empty is allowed at this stage
// 3. Determine the cluster info and auth info to use. At this point, we may or may not have a context. They
// are built based on the first hit in this chain. (run it twice, once for auth, once for cluster)
// 1. command line argument
// 2. If context is present, then use the context value
// 3. Empty is allowed
// 4. Determine the actual cluster info to use. At this point, we may or may not have a cluster info. Build
// each piece of the cluster info based on the chain:
// 1. command line argument
// 2. If cluster info is present and a value for the attribute is present, use it.
// 3. If you don't have a server location, bail.
// 5. Auth info is build using the same rules as cluster info, EXCEPT that you can only have one authentication
// technique per auth info. The following conditions result in an error:
// 1. If there are two conflicting techniques specified from the command line, fail.
// 2. If the command line does not specify one, and the auth info has conflicting techniques, fail.
// 3. If the command line specifies one and the auth info specifies another, honor the command line technique.
// 2. Use default values and potentially prompt for auth information
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
overrides := &clientcmd.ConfigOverrides{}
flagNames := clientcmd.RecommendedConfigOverrideFlags("")
// short flagnames are disabled by default. These are here for compatibility with existing scripts
flagNames.AuthOverrideFlags.AuthPathShort = "a"
flagNames.ClusterOverrideFlags.APIServerShort = "s"
clientcmd.BindOverrideFlags(overrides, flags, flagNames)
clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
return clientConfig
}
func runHelp(cmd *cobra.Command, args []string) { func runHelp(cmd *cobra.Command, args []string) {
cmd.Help() cmd.Help()
} }
type clientSwaggerSchema struct {
c *client.Client
t runtime.ObjectTyper
}
func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
version, _, err := c.t.DataVersionAndKind(data)
if err != nil {
return err
}
schemaData, err := c.c.RESTClient.Get().
AbsPath("/swaggerapi/api", version).
Do().
Raw()
if err != nil {
return err
}
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData)
if err != nil {
return err
}
return schema.ValidateBytes(data)
}
// clientCache caches previously loaded clients for reuse, and ensures MatchServerVersion
// is invoked only once
type clientCache struct {
loader clientcmd.ClientConfig
clients map[string]*client.Client
defaultConfig *client.Config
matchVersion bool
}
// ClientConfigForVersion returns the correct config for a server
func (c *clientCache) ClientConfigForVersion(version string) (*client.Config, error) {
if c.defaultConfig == nil {
config, err := c.loader.ClientConfig()
if err != nil {
return nil, err
}
c.defaultConfig = config
if c.matchVersion {
if err := client.MatchesServerVersion(config); err != nil {
return nil, err
}
}
}
// TODO: have a better config copy method
config := *c.defaultConfig
if len(version) != 0 {
config.Version = version
}
client.SetKubernetesDefaults(&config)
return &config, nil
}
// ClientForVersion initializes or reuses a client for the specified version, or returns an
// error if that is not possible
func (c *clientCache) ClientForVersion(version string) (*client.Client, error) {
config, err := c.ClientConfigForVersion(version)
if err != nil {
return nil, err
}
if client, ok := c.clients[config.Version]; ok {
return client, nil
}
client, err := client.New(config)
if err != nil {
return nil, err
}
c.clients[config.Version] = client
return client, nil
}
func printDeprecationWarning(command, alias string) { func printDeprecationWarning(command, alias string) {
glog.Warningf("%s is DEPRECATED and will be removed in a future version. Use %s instead.", alias, command) glog.Warningf("%s is DEPRECATED and will be removed in a future version. Use %s instead.", alias, command)
} }

View File

@ -30,6 +30,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
) )
@ -109,14 +110,14 @@ type testFactory struct {
Err error Err error
} }
func NewTestFactory() (*Factory, *testFactory, runtime.Codec) { func NewTestFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) {
scheme, mapper, codec := newExternalScheme() scheme, mapper, codec := newExternalScheme()
t := &testFactory{ t := &testFactory{
Validator: validation.NullSchema{}, Validator: validation.NullSchema{},
Mapper: mapper, Mapper: mapper,
Typer: scheme, Typer: scheme,
} }
return &Factory{ return &cmdutil.Factory{
Object: func() (meta.RESTMapper, runtime.ObjectTyper) { Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
return t.Mapper, t.Typer return t.Mapper, t.Typer
}, },
@ -141,11 +142,11 @@ func NewTestFactory() (*Factory, *testFactory, runtime.Codec) {
}, t, codec }, t, codec
} }
func NewAPIFactory() (*Factory, *testFactory, runtime.Codec) { func NewAPIFactory() (*cmdutil.Factory, *testFactory, runtime.Codec) {
t := &testFactory{ t := &testFactory{
Validator: validation.NullSchema{}, Validator: validation.NullSchema{},
} }
return &Factory{ return &cmdutil.Factory{
Object: func() (meta.RESTMapper, runtime.ObjectTyper) { Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
return latest.RESTMapper, api.Scheme return latest.RESTMapper, api.Scheme
}, },
@ -180,7 +181,7 @@ func stringBody(body string) io.ReadCloser {
// Verify that resource.RESTClients constructed from a factory respect mapping.APIVersion // Verify that resource.RESTClients constructed from a factory respect mapping.APIVersion
func TestClientVersions(t *testing.T) { func TestClientVersions(t *testing.T) {
f := NewFactory(nil) f := cmdutil.NewFactory(nil)
versions := []string{ versions := []string{
"v1beta1", "v1beta1",
@ -209,7 +210,7 @@ func ExamplePrintReplicationController() {
Codec: codec, Codec: codec,
Client: nil, Client: nil,
} }
cmd := f.NewCmdRunContainer(os.Stdout) cmd := NewCmdRunContainer(f, os.Stdout)
ctrl := &api.ReplicationController{ ctrl := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: "foo", Name: "foo",

View File

@ -27,6 +27,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
) )
type pathOptions struct { type pathOptions struct {
@ -36,7 +37,7 @@ type pathOptions struct {
specifiedFile string specifiedFile string
} }
func NewCmdConfig(out io.Writer) *cobra.Command { func NewCmdConfig(f *cmdutil.Factory, out io.Writer) *cobra.Command {
pathOptions := &pathOptions{} pathOptions := &pathOptions{}
cmd := &cobra.Command{ cmd := &cobra.Command{

View File

@ -26,6 +26,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
) )
@ -618,8 +619,9 @@ func testConfigCommand(args []string, startingConfig clientcmdapi.Config) (strin
argsToUse = append(argsToUse, args...) argsToUse = append(argsToUse, args...)
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
f := cmdutil.NewFactory(nil)
cmd := NewCmdConfig(buf) cmd := NewCmdConfig(f, buf)
cmd.SetArgs(argsToUse) cmd.SetArgs(argsToUse)
cmd.Execute() cmd.Execute()

View File

@ -38,7 +38,7 @@ $ kubectl create -f pod.json
$ cat pod.json | kubectl create -f -` $ cat pod.json | kubectl create -f -`
) )
func (f *Factory) NewCmdCreate(out io.Writer) *cobra.Command { func NewCmdCreate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
var filenames util.StringList var filenames util.StringList
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "create -f FILENAME", Use: "create -f FILENAME",
@ -61,7 +61,7 @@ func ValidateArgs(cmd *cobra.Command, args []string) error {
return nil return nil
} }
func RunCreate(f *Factory, out io.Writer, filenames util.StringList) error { func RunCreate(f *cmdutil.Factory, out io.Writer, filenames util.StringList) error {
schema, err := f.Validator() schema, err := f.Validator()
if err != nil { if err != nil {
return err return err

View File

@ -28,7 +28,7 @@ func TestExtraArgsFail(t *testing.T) {
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
f, _, _ := NewAPIFactory() f, _, _ := NewAPIFactory()
c := f.NewCmdCreate(buf) c := NewCmdCreate(f, buf)
if ValidateArgs(c, []string{"rc"}) == nil { if ValidateArgs(c, []string{"rc"}) == nil {
t.Errorf("unexpected non-error") t.Errorf("unexpected non-error")
} }
@ -55,7 +55,7 @@ func TestCreateObject(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdCreate(buf) cmd := NewCmdCreate(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -87,7 +87,7 @@ func TestCreateMultipleObject(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdCreate(buf) cmd := NewCmdCreate(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -121,7 +121,7 @@ func TestCreateDirectory(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdCreate(buf) cmd := NewCmdCreate(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook") cmd.Flags().Set("filename", "../../../examples/guestbook")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})

View File

@ -55,7 +55,7 @@ $ kubectl delete pod 1234-56-7890-234234-456456
$ kubectl delete pods --all` $ kubectl delete pods --all`
) )
func (f *Factory) NewCmdDelete(out io.Writer) *cobra.Command { func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command {
var filenames util.StringList var filenames util.StringList
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "delete ([-f FILENAME] | (RESOURCE [(ID | -l label | --all)]", Use: "delete ([-f FILENAME] | (RESOURCE [(ID | -l label | --all)]",
@ -73,7 +73,7 @@ func (f *Factory) NewCmdDelete(out io.Writer) *cobra.Command {
return cmd return cmd
} }
func RunDelete(f *Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error { func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error {
cmdNamespace, err := f.DefaultNamespace() cmdNamespace, err := f.DefaultNamespace()
if err != nil { if err != nil {
return err return err

View File

@ -47,7 +47,7 @@ func TestDeleteObject(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdDelete(buf) cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -75,7 +75,7 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdDelete(buf) cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -106,7 +106,7 @@ func TestDeleteMultipleObject(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdDelete(buf) cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -138,7 +138,7 @@ func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdDelete(buf) cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -170,7 +170,7 @@ func TestDeleteDirectory(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdDelete(buf) cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook") cmd.Flags().Set("filename", "../../../examples/guestbook")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -211,7 +211,7 @@ func TestDeleteMultipleSelector(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdDelete(buf) cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("selector", "a=b") cmd.Flags().Set("selector", "a=b")
cmd.Run(cmd, []string{"pods,services"}) cmd.Run(cmd, []string{"pods,services"})

View File

@ -20,11 +20,11 @@ import (
"fmt" "fmt"
"io" "io"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
func (f *Factory) NewCmdDescribe(out io.Writer) *cobra.Command { func NewCmdDescribe(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "describe RESOURCE ID", Use: "describe RESOURCE ID",
Short: "Show details of a specific resource", Short: "Show details of a specific resource",
@ -34,13 +34,13 @@ 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) {
err := RunDescribe(f, out, cmd, args) err := RunDescribe(f, out, cmd, args)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
return cmd return cmd
} }
func RunDescribe(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error { func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
cmdNamespace, err := f.DefaultNamespace() cmdNamespace, err := f.DefaultNamespace()
if err != nil { if err != nil {
return err return err
@ -48,7 +48,7 @@ func RunDescribe(f *Factory, out io.Writer, cmd *cobra.Command, args []string) e
mapper, _ := f.Object() mapper, _ := f.Object()
// TODO: use resource.Builder instead // TODO: use resource.Builder instead
mapping, namespace, name, err := util.ResourceFromArgs(cmd, args, mapper, cmdNamespace) mapping, namespace, name, err := cmdutil.ResourceFromArgs(cmd, args, mapper, cmdNamespace)
if err != nil { if err != nil {
return err return err
} }

View File

@ -37,7 +37,7 @@ func TestDescribeUnknownSchemaObject(t *testing.T) {
tf.Namespace = "non-default" tf.Namespace = "non-default"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdDescribe(buf) cmd := NewCmdDescribe(f, buf)
cmd.Run(cmd, []string{"type", "foo"}) cmd.Run(cmd, []string{"type", "foo"})
if d.Name != "foo" || d.Namespace != "non-default" { if d.Name != "foo" || d.Namespace != "non-default" {

View File

@ -24,7 +24,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/remotecommand" "github.com/GoogleCloudPlatform/kubernetes/pkg/client/remotecommand"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/docker/docker/pkg/term" "github.com/docker/docker/pkg/term"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -38,7 +38,7 @@ $ kubectl exec -p 123456-7890 -c ruby-container date
$ kubectl exec -p 123456-7890 -c ruby-container -i -t -- bash -il` $ kubectl exec -p 123456-7890 -c ruby-container -i -t -- bash -il`
) )
func (f *Factory) NewCmdExec(cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command { func NewCmdExec(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "exec -p POD -c CONTAINER -- COMMAND [args...]", Use: "exec -p POD -c CONTAINER -- COMMAND [args...]",
Short: "Execute a command in a container.", Short: "Execute a command in a container.",
@ -46,7 +46,7 @@ func (f *Factory) NewCmdExec(cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.C
Example: exec_example, Example: exec_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunExec(f, cmdIn, cmdOut, cmdErr, cmd, args) err := RunExec(f, cmdIn, cmdOut, cmdErr, cmd, args)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
cmd.Flags().StringP("pod", "p", "", "Pod name") cmd.Flags().StringP("pod", "p", "", "Pod name")
@ -57,14 +57,14 @@ func (f *Factory) NewCmdExec(cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.C
return cmd return cmd
} }
func RunExec(f *Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobra.Command, args []string) error { func RunExec(f *cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobra.Command, args []string) error {
podName := util.GetFlagString(cmd, "pod") podName := cmdutil.GetFlagString(cmd, "pod")
if len(podName) == 0 { if len(podName) == 0 {
return util.UsageError(cmd, "POD is required for exec") return cmdutil.UsageError(cmd, "POD is required for exec")
} }
if len(args) < 1 { if len(args) < 1 {
return util.UsageError(cmd, "COMMAND is required for exec") return cmdutil.UsageError(cmd, "COMMAND is required for exec")
} }
namespace, err := f.DefaultNamespace() namespace, err := f.DefaultNamespace()
@ -86,14 +86,14 @@ func RunExec(f *Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobra.C
glog.Fatalf("Unable to execute command because pod is not running. Current status=%v", pod.Status.Phase) glog.Fatalf("Unable to execute command because pod is not running. Current status=%v", pod.Status.Phase)
} }
containerName := util.GetFlagString(cmd, "container") containerName := cmdutil.GetFlagString(cmd, "container")
if len(containerName) == 0 { if len(containerName) == 0 {
containerName = pod.Spec.Containers[0].Name containerName = pod.Spec.Containers[0].Name
} }
var stdin io.Reader var stdin io.Reader
tty := util.GetFlagBool(cmd, "tty") tty := cmdutil.GetFlagBool(cmd, "tty")
if util.GetFlagBool(cmd, "stdin") { if cmdutil.GetFlagBool(cmd, "stdin") {
stdin = cmdIn stdin = cmdIn
if tty { if tty {
if file, ok := cmdIn.(*os.File); ok { if file, ok := cmdIn.(*os.File); ok {

View File

@ -22,7 +22,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -42,7 +42,7 @@ $ kubectl expose service nginx --port=443 --target-port=8443 --service-name=ngin
$ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream` $ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream`
) )
func (f *Factory) NewCmdExposeService(out io.Writer) *cobra.Command { func NewCmdExposeService(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "expose RESOURCE NAME --port=port [--protocol=TCP|UDP] [--target-port=number-or-name] [--service-name=name] [--public-ip=ip] [--create-external-load-balancer=bool]", Use: "expose RESOURCE NAME --port=port [--protocol=TCP|UDP] [--target-port=number-or-name] [--service-name=name] [--public-ip=ip] [--create-external-load-balancer=bool]",
Short: "Take a replicated application and expose it as Kubernetes Service", Short: "Take a replicated application and expose it as Kubernetes Service",
@ -50,10 +50,10 @@ func (f *Factory) NewCmdExposeService(out io.Writer) *cobra.Command {
Example: expose_example, Example: expose_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunExpose(f, out, cmd, args) err := RunExpose(f, out, cmd, args)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
util.AddPrinterFlags(cmd) cmdutil.AddPrinterFlags(cmd)
cmd.Flags().String("generator", "service/v1", "The name of the API generator to use. Default is 'service/v1'.") cmd.Flags().String("generator", "service/v1", "The name of the API generator to use. Default is 'service/v1'.")
cmd.Flags().String("protocol", "TCP", "The network protocol for the service to be created. Default is 'tcp'.") cmd.Flags().String("protocol", "TCP", "The network protocol for the service to be created. Default is 'tcp'.")
cmd.Flags().Int("port", -1, "The port that the service should serve on. Required.") cmd.Flags().Int("port", -1, "The port that the service should serve on. Required.")
@ -69,13 +69,13 @@ func (f *Factory) NewCmdExposeService(out io.Writer) *cobra.Command {
return cmd return cmd
} }
func RunExpose(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error { func RunExpose(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
var name, resource string var name, resource string
switch l := len(args); { switch l := len(args); {
case l == 2: case l == 2:
resource, name = args[0], args[1] resource, name = args[0], args[1]
default: default:
return util.UsageError(cmd, "the type and name of a resource to expose are required arguments") return cmdutil.UsageError(cmd, "the type and name of a resource to expose are required arguments")
} }
namespace, err := f.DefaultNamespace() namespace, err := f.DefaultNamespace()
@ -87,20 +87,20 @@ func RunExpose(f *Factory, out io.Writer, cmd *cobra.Command, args []string) err
return err return err
} }
generatorName := util.GetFlagString(cmd, "generator") generatorName := cmdutil.GetFlagString(cmd, "generator")
generator, found := kubectl.Generators[generatorName] generator, found := kubectl.Generators[generatorName]
if !found { if !found {
return util.UsageError(cmd, fmt.Sprintf("generator %q not found.", generator)) return cmdutil.UsageError(cmd, fmt.Sprintf("generator %q not found.", generator))
} }
names := generator.ParamNames() names := generator.ParamNames()
params := kubectl.MakeParams(cmd, names) params := kubectl.MakeParams(cmd, names)
if len(util.GetFlagString(cmd, "service-name")) == 0 { if len(cmdutil.GetFlagString(cmd, "service-name")) == 0 {
params["name"] = name params["name"] = name
} else { } else {
params["name"] = util.GetFlagString(cmd, "service-name") params["name"] = cmdutil.GetFlagString(cmd, "service-name")
} }
if s, found := params["selector"]; !found || len(s) == 0 || util.GetFlagInt(cmd, "port") < 1 { if s, found := params["selector"]; !found || len(s) == 0 || cmdutil.GetFlagInt(cmd, "port") < 1 {
mapper, _ := f.Object() mapper, _ := f.Object()
v, k, err := mapper.VersionAndKindForResource(resource) v, k, err := mapper.VersionAndKindForResource(resource)
if err != nil { if err != nil {
@ -117,21 +117,21 @@ func RunExpose(f *Factory, out io.Writer, cmd *cobra.Command, args []string) err
} }
params["selector"] = s params["selector"] = s
} }
if util.GetFlagInt(cmd, "port") < 0 { if cmdutil.GetFlagInt(cmd, "port") < 0 {
ports, err := f.PortsForResource(mapping, namespace, name) ports, err := f.PortsForResource(mapping, namespace, name)
if err != nil { if err != nil {
return err return err
} }
if len(ports) == 0 { if len(ports) == 0 {
return util.UsageError(cmd, "couldn't find a suitable port via --port flag or introspection") return cmdutil.UsageError(cmd, "couldn't find a suitable port via --port flag or introspection")
} }
if len(ports) > 1 { if len(ports) > 1 {
return util.UsageError(cmd, "more than one port to choose from, please explicitly specify a port using the --port flag.") return cmdutil.UsageError(cmd, "more than one port to choose from, please explicitly specify a port using the --port flag.")
} }
params["port"] = ports[0] params["port"] = ports[0]
} }
} }
if util.GetFlagBool(cmd, "create-external-load-balancer") { if cmdutil.GetFlagBool(cmd, "create-external-load-balancer") {
params["create-external-load-balancer"] = "true" params["create-external-load-balancer"] = "true"
} }
@ -145,16 +145,16 @@ func RunExpose(f *Factory, out io.Writer, cmd *cobra.Command, args []string) err
return err return err
} }
inline := util.GetFlagString(cmd, "overrides") inline := cmdutil.GetFlagString(cmd, "overrides")
if len(inline) > 0 { if len(inline) > 0 {
service, err = util.Merge(service, inline, "Service") service, err = cmdutil.Merge(service, inline, "Service")
if err != nil { if err != nil {
return err return err
} }
} }
// TODO: extract this flag to a central location, when such a location exists. // TODO: extract this flag to a central location, when such a location exists.
if !util.GetFlagBool(cmd, "dry-run") { if !cmdutil.GetFlagBool(cmd, "dry-run") {
service, err = client.Services(namespace).Create(service.(*api.Service)) service, err = client.Services(namespace).Create(service.(*api.Service))
if err != nil { if err != nil {
return err return err

View File

@ -23,7 +23,7 @@ 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/kubectl" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
@ -59,7 +59,7 @@ $ kubectl get rc/web service/frontend pods/web-pod-13je7`
// NewCmdGet creates a command object for the generic "get" action, which // NewCmdGet creates a command object for the generic "get" action, which
// retrieves one or more resources from a server. // retrieves one or more resources from a server.
func (f *Factory) NewCmdGet(out io.Writer) *cobra.Command { func NewCmdGet(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)", Use: "get [(-o|--output=)json|yaml|template|...] (RESOURCE [NAME] | RESOURCE/NAME ...)",
Short: "Display one or many resources", Short: "Display one or many resources",
@ -67,10 +67,10 @@ func (f *Factory) NewCmdGet(out io.Writer) *cobra.Command {
Example: get_example, Example: get_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunGet(f, out, cmd, args) err := RunGet(f, out, cmd, args)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
util.AddPrinterFlags(cmd) cmdutil.AddPrinterFlags(cmd)
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on")
cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.") cmd.Flags().BoolP("watch", "w", false, "After listing/getting the requested object, watch for changes.")
cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.") cmd.Flags().Bool("watch-only", false, "Watch for changes to the requested object(s), without listing/getting first.")
@ -79,8 +79,8 @@ func (f *Factory) NewCmdGet(out io.Writer) *cobra.Command {
// RunGet implements the generic Get command // RunGet implements the generic Get command
// TODO: convert all direct flag accessors to a struct and pass that instead of cmd // TODO: convert all direct flag accessors to a struct and pass that instead of cmd
func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error { func RunGet(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
selector := util.GetFlagString(cmd, "selector") selector := cmdutil.GetFlagString(cmd, "selector")
mapper, typer := f.Object() mapper, typer := f.Object()
cmdNamespace, err := f.DefaultNamespace() cmdNamespace, err := f.DefaultNamespace()
@ -89,7 +89,7 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error
} }
// handle watch separately since we cannot watch multiple resource types // handle watch separately since we cannot watch multiple resource types
isWatch, isWatchOnly := util.GetFlagBool(cmd, "watch"), util.GetFlagBool(cmd, "watch-only") isWatch, isWatchOnly := cmdutil.GetFlagBool(cmd, "watch"), cmdutil.GetFlagBool(cmd, "watch-only")
if isWatch || isWatchOnly { if isWatch || isWatchOnly {
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()). r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(cmdNamespace).DefaultNamespace().
@ -146,7 +146,7 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error
ResourceTypeOrNameArgs(true, args...). ResourceTypeOrNameArgs(true, args...).
ContinueOnError(). ContinueOnError().
Latest() Latest()
printer, generic, err := util.PrinterForCommand(cmd) printer, generic, err := cmdutil.PrinterForCommand(cmd)
if err != nil { if err != nil {
return err return err
} }
@ -159,7 +159,7 @@ func RunGet(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error
defaultVersion := clientConfig.Version defaultVersion := clientConfig.Version
// the outermost object will be converted to the output-version // the outermost object will be converted to the output-version
version := util.OutputVersion(cmd, defaultVersion) version := cmdutil.OutputVersion(cmd, defaultVersion)
r := b.Flatten().Do() r := b.Flatten().Do()
obj, err := r.Object() obj, err := r.Object()

View File

@ -99,7 +99,7 @@ func TestGetUnknownSchemaObject(t *testing.T) {
tf.ClientConfig = &client.Config{Version: latest.Version} tf.ClientConfig = &client.Config{Version: latest.Version}
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdGet(buf) cmd := NewCmdGet(f, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Run(cmd, []string{"type", "foo"}) cmd.Run(cmd, []string{"type", "foo"})
@ -128,7 +128,7 @@ func TestGetSchemaObject(t *testing.T) {
tf.ClientConfig = &client.Config{Version: "v1beta3"} tf.ClientConfig = &client.Config{Version: "v1beta3"}
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdGet(buf) cmd := NewCmdGet(f, buf)
cmd.Run(cmd, []string{"replicationcontrollers", "foo"}) cmd.Run(cmd, []string{"replicationcontrollers", "foo"})
if !strings.Contains(buf.String(), "\"foo\"") { if !strings.Contains(buf.String(), "\"foo\"") {
@ -148,7 +148,7 @@ func TestGetObjects(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdGet(buf) cmd := NewCmdGet(f, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Run(cmd, []string{"pods", "foo"}) cmd.Run(cmd, []string{"pods", "foo"})
@ -174,7 +174,7 @@ func TestGetListObjects(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdGet(buf) cmd := NewCmdGet(f, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Run(cmd, []string{"pods"}) cmd.Run(cmd, []string{"pods"})
@ -210,7 +210,7 @@ func TestGetMultipleTypeObjects(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdGet(buf) cmd := NewCmdGet(f, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Run(cmd, []string{"pods,services"}) cmd.Run(cmd, []string{"pods,services"})
@ -247,7 +247,7 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
tf.ClientConfig = &client.Config{Version: "v1beta1"} tf.ClientConfig = &client.Config{Version: "v1beta1"}
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdGet(buf) cmd := NewCmdGet(f, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("output", "json") cmd.Flags().Set("output", "json")
@ -298,7 +298,7 @@ func TestGetMultipleTypeObjectsWithSelector(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdGet(buf) cmd := NewCmdGet(f, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("selector", "a=b") cmd.Flags().Set("selector", "a=b")
@ -344,7 +344,7 @@ func TestGetMultipleTypeObjectsWithDirectReference(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdGet(buf) cmd := NewCmdGet(f, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Run(cmd, []string{"services/bar", "node/foo"}) cmd.Run(cmd, []string{"services/bar", "node/foo"})
@ -430,7 +430,7 @@ func TestWatchSelector(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdGet(buf) cmd := NewCmdGet(f, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("watch", "true") cmd.Flags().Set("watch", "true")
@ -469,7 +469,7 @@ func TestWatchResource(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdGet(buf) cmd := NewCmdGet(f, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("watch", "true") cmd.Flags().Set("watch", "true")
@ -507,7 +507,7 @@ func TestWatchOnlyResource(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdGet(buf) cmd := NewCmdGet(f, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
cmd.Flags().Set("watch-only", "true") cmd.Flags().Set("watch-only", "true")

View File

@ -22,7 +22,7 @@ import (
"strings" "strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "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"
@ -50,7 +50,7 @@ $ kubectl label pods foo status=unhealthy --resource-version=1
$ kubectl label pods foo bar-` $ kubectl label pods foo bar-`
) )
func (f *Factory) NewCmdLabel(out io.Writer) *cobra.Command { func NewCmdLabel(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "label [--overwrite] RESOURCE NAME KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]", Use: "label [--overwrite] RESOURCE NAME KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]",
Short: "Update the labels on a resource", Short: "Update the labels on a resource",
@ -58,10 +58,10 @@ func (f *Factory) NewCmdLabel(out io.Writer) *cobra.Command {
Example: label_example, Example: label_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunLabel(f, out, cmd, args) err := RunLabel(f, out, cmd, args)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
util.AddPrinterFlags(cmd) cmdutil.AddPrinterFlags(cmd)
cmd.Flags().Bool("overwrite", false, "If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels.") cmd.Flags().Bool("overwrite", false, "If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels.")
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on")
cmd.Flags().Bool("all", false, "select all resources in the namespace of the specified resource types") cmd.Flags().Bool("all", false, "select all resources in the namespace of the specified resource types")
@ -149,7 +149,7 @@ func labelFunc(obj runtime.Object, overwrite bool, resourceVersion string, label
return obj, nil return obj, nil
} }
func RunLabel(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error { func RunLabel(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
resources, labelArgs := []string{}, []string{} resources, labelArgs := []string{}, []string{}
first := true first := true
for _, s := range args { for _, s := range args {
@ -163,20 +163,20 @@ func RunLabel(f *Factory, out io.Writer, cmd *cobra.Command, args []string) erro
case first && !isLabel: case first && !isLabel:
resources = append(resources, s) resources = append(resources, s)
case !first && !isLabel: case !first && !isLabel:
return util.UsageError(cmd, "all resources must be specified before label changes: %s", s) return cmdutil.UsageError(cmd, "all resources must be specified before label changes: %s", s)
} }
} }
if len(resources) < 1 { if len(resources) < 1 {
return util.UsageError(cmd, "one or more resources must be specified as <resource> <name> or <resource>/<name>") return cmdutil.UsageError(cmd, "one or more resources must be specified as <resource> <name> or <resource>/<name>")
} }
if len(labelArgs) < 1 { if len(labelArgs) < 1 {
return util.UsageError(cmd, "at least one label update is required") return cmdutil.UsageError(cmd, "at least one label update is required")
} }
selector := util.GetFlagString(cmd, "selector") selector := cmdutil.GetFlagString(cmd, "selector")
all := util.GetFlagBool(cmd, "all") all := cmdutil.GetFlagBool(cmd, "all")
overwrite := util.GetFlagBool(cmd, "overwrite") overwrite := cmdutil.GetFlagBool(cmd, "overwrite")
resourceVersion := util.GetFlagString(cmd, "resource-version") resourceVersion := cmdutil.GetFlagString(cmd, "resource-version")
cmdNamespace, err := f.DefaultNamespace() cmdNamespace, err := f.DefaultNamespace()
if err != nil { if err != nil {
@ -204,7 +204,7 @@ func RunLabel(f *Factory, out io.Writer, cmd *cobra.Command, args []string) erro
} }
// only apply resource version locking on a single resource // only apply resource version locking on a single resource
if !one && len(resourceVersion) > 0 { if !one && len(resourceVersion) > 0 {
return util.UsageError(cmd, "--resource-version may only be used with a single resource") return cmdutil.UsageError(cmd, "--resource-version may only be used with a single resource")
} }
// TODO: support bulk generic output a la Get // TODO: support bulk generic output a la Get

View File

@ -294,7 +294,7 @@ func TestLabelErrors(t *testing.T) {
tf.ClientConfig = &client.Config{Version: "v1beta1"} tf.ClientConfig = &client.Config{Version: "v1beta1"}
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdLabel(buf) cmd := NewCmdLabel(f, buf)
cmd.SetOutput(buf) cmd.SetOutput(buf)
for k, v := range testCase.flags { for k, v := range testCase.flags {
@ -351,7 +351,7 @@ func TestLabelMultipleObjects(t *testing.T) {
tf.ClientConfig = &client.Config{Version: "v1beta1"} tf.ClientConfig = &client.Config{Version: "v1beta1"}
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdLabel(buf) cmd := NewCmdLabel(f, buf)
cmd.Flags().Set("all", "true") cmd.Flags().Set("all", "true")
if err := RunLabel(f, buf, cmd, []string{"pods", "a=b"}); err != nil { if err := RunLabel(f, buf, cmd, []string{"pods", "a=b"}); err != nil {

View File

@ -22,7 +22,7 @@ import (
"strconv" "strconv"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
libutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util" libutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -57,7 +57,7 @@ func selectContainer(pod *api.Pod, in io.Reader, out io.Writer) string {
} }
} }
func (f *Factory) NewCmdLog(out io.Writer) *cobra.Command { func NewCmdLog(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "log [-f] POD [CONTAINER]", Use: "log [-f] POD [CONTAINER]",
Short: "Print the logs for a container in a pod.", Short: "Print the logs for a container in a pod.",
@ -65,7 +65,7 @@ func (f *Factory) NewCmdLog(out io.Writer) *cobra.Command {
Example: log_example, Example: log_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunLog(f, out, cmd, args) err := RunLog(f, out, cmd, args)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
cmd.Flags().BoolP("follow", "f", false, "Specify if the logs should be streamed.") cmd.Flags().BoolP("follow", "f", false, "Specify if the logs should be streamed.")
@ -73,13 +73,13 @@ func (f *Factory) NewCmdLog(out io.Writer) *cobra.Command {
return cmd return cmd
} }
func RunLog(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error { func RunLog(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
if len(args) == 0 { if len(args) == 0 {
return util.UsageError(cmd, "POD is required for log") return cmdutil.UsageError(cmd, "POD is required for log")
} }
if len(args) > 2 { if len(args) > 2 {
return util.UsageError(cmd, "log POD [CONTAINER]") return cmdutil.UsageError(cmd, "log POD [CONTAINER]")
} }
namespace, err := f.DefaultNamespace() namespace, err := f.DefaultNamespace()
@ -109,7 +109,7 @@ func RunLog(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error
} }
follow := false follow := false
if util.GetFlagBool(cmd, "follow") { if cmdutil.GetFlagBool(cmd, "follow") {
follow = true follow = true
} }

View File

@ -22,7 +22,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/portforward" "github.com/GoogleCloudPlatform/kubernetes/pkg/client/portforward"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -42,7 +42,7 @@ $ kubectl port-forward -p mypod :5000
$ kubectl port-forward -p mypod 0:5000` $ kubectl port-forward -p mypod 0:5000`
) )
func (f *Factory) NewCmdPortForward() *cobra.Command { func NewCmdPortForward(f *cmdutil.Factory) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "port-forward -p POD [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]", Use: "port-forward -p POD [LOCAL_PORT:]REMOTE_PORT [...[LOCAL_PORT_N:]REMOTE_PORT_N]",
Short: "Forward one or more local ports to a pod.", Short: "Forward one or more local ports to a pod.",
@ -50,7 +50,7 @@ func (f *Factory) NewCmdPortForward() *cobra.Command {
Example: portforward_example, Example: portforward_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunPortForward(f, cmd, args) err := RunPortForward(f, cmd, args)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
cmd.Flags().StringP("pod", "p", "", "Pod name") cmd.Flags().StringP("pod", "p", "", "Pod name")
@ -58,14 +58,14 @@ func (f *Factory) NewCmdPortForward() *cobra.Command {
return cmd return cmd
} }
func RunPortForward(f *Factory, cmd *cobra.Command, args []string) error { func RunPortForward(f *cmdutil.Factory, cmd *cobra.Command, args []string) error {
podName := util.GetFlagString(cmd, "pod") podName := cmdutil.GetFlagString(cmd, "pod")
if len(podName) == 0 { if len(podName) == 0 {
return util.UsageError(cmd, "POD is required for exec") return cmdutil.UsageError(cmd, "POD is required for exec")
} }
if len(args) < 1 { if len(args) < 1 {
return util.UsageError(cmd, "at least 1 PORT is required for port-forward") return cmdutil.UsageError(cmd, "at least 1 PORT is required for port-forward")
} }
namespace, err := f.DefaultNamespace() namespace, err := f.DefaultNamespace()

View File

@ -22,7 +22,7 @@ import (
"strings" "strings"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -36,7 +36,7 @@ $ kubectl proxy --port=8011 --www=./local/www/
$ kubectl proxy --api-prefix=k8s-api` $ kubectl proxy --api-prefix=k8s-api`
) )
func (f *Factory) NewCmdProxy(out io.Writer) *cobra.Command { func NewCmdProxy(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix]", Use: "proxy [--port=PORT] [--www=static-dir] [--www-prefix=prefix] [--api-prefix=prefix]",
Short: "Run a proxy to the Kubernetes API server", Short: "Run a proxy to the Kubernetes API server",
@ -44,7 +44,7 @@ func (f *Factory) NewCmdProxy(out io.Writer) *cobra.Command {
Example: proxy_example, Example: proxy_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunProxy(f, out, cmd) err := RunProxy(f, out, cmd)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
cmd.Flags().StringP("www", "w", "", "Also serve static files from the given directory under the specified prefix.") cmd.Flags().StringP("www", "w", "", "Also serve static files from the given directory under the specified prefix.")
@ -54,8 +54,8 @@ func (f *Factory) NewCmdProxy(out io.Writer) *cobra.Command {
return cmd return cmd
} }
func RunProxy(f *Factory, out io.Writer, cmd *cobra.Command) error { func RunProxy(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error {
port := util.GetFlagInt(cmd, "port") port := cmdutil.GetFlagInt(cmd, "port")
fmt.Fprintf(out, "Starting to serve on localhost:%d", port) fmt.Fprintf(out, "Starting to serve on localhost:%d", port)
clientConfig, err := f.ClientConfig() clientConfig, err := f.ClientConfig()
@ -63,16 +63,16 @@ func RunProxy(f *Factory, out io.Writer, cmd *cobra.Command) error {
return err return err
} }
staticPrefix := util.GetFlagString(cmd, "www-prefix") staticPrefix := cmdutil.GetFlagString(cmd, "www-prefix")
if !strings.HasSuffix(staticPrefix, "/") { if !strings.HasSuffix(staticPrefix, "/") {
staticPrefix += "/" staticPrefix += "/"
} }
apiProxyPrefix := util.GetFlagString(cmd, "api-prefix") apiProxyPrefix := cmdutil.GetFlagString(cmd, "api-prefix")
if !strings.HasSuffix(apiProxyPrefix, "/") { if !strings.HasSuffix(apiProxyPrefix, "/") {
apiProxyPrefix += "/" apiProxyPrefix += "/"
} }
server, err := kubectl.NewProxyServer(util.GetFlagString(cmd, "www"), apiProxyPrefix, staticPrefix, clientConfig) server, err := kubectl.NewProxyServer(cmdutil.GetFlagString(cmd, "www"), apiProxyPrefix, staticPrefix, clientConfig)
if err != nil { if err != nil {
return err return err
} }

View File

@ -23,7 +23,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/controller" "github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait" "github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -45,7 +45,7 @@ $ kubectl resize --current-replicas=2 --replicas=3 replicationcontrollers foo`
retryTimeout = 10 * time.Second retryTimeout = 10 * time.Second
) )
func (f *Factory) NewCmdResize(out io.Writer) *cobra.Command { func NewCmdResize(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "resize [--resource-version=version] [--current-replicas=count] --replicas=COUNT RESOURCE ID", Use: "resize [--resource-version=version] [--current-replicas=count] --replicas=COUNT RESOURCE ID",
Short: "Set a new size for a Replication Controller.", Short: "Set a new size for a Replication Controller.",
@ -53,7 +53,7 @@ func (f *Factory) NewCmdResize(out io.Writer) *cobra.Command {
Example: resize_example, Example: resize_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunResize(f, out, cmd, args) err := RunResize(f, out, cmd, args)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
cmd.Flags().String("resource-version", "", "Precondition for resource version. Requires that the current resource version match this value in order to resize.") cmd.Flags().String("resource-version", "", "Precondition for resource version. Requires that the current resource version match this value in order to resize.")
@ -62,10 +62,10 @@ func (f *Factory) NewCmdResize(out io.Writer) *cobra.Command {
return cmd return cmd
} }
func RunResize(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error { func RunResize(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
count := util.GetFlagInt(cmd, "replicas") count := cmdutil.GetFlagInt(cmd, "replicas")
if len(args) != 2 || count < 0 { if len(args) != 2 || count < 0 {
return util.UsageError(cmd, "--replicas=COUNT RESOURCE ID") return cmdutil.UsageError(cmd, "--replicas=COUNT RESOURCE ID")
} }
cmdNamespace, err := f.DefaultNamespace() cmdNamespace, err := f.DefaultNamespace()
@ -75,7 +75,7 @@ func RunResize(f *Factory, out io.Writer, cmd *cobra.Command, args []string) err
mapper, _ := f.Object() mapper, _ := f.Object()
// TODO: use resource.Builder instead // TODO: use resource.Builder instead
mapping, namespace, name, err := util.ResourceFromArgs(cmd, args, mapper, cmdNamespace) mapping, namespace, name, err := cmdutil.ResourceFromArgs(cmd, args, mapper, cmdNamespace)
if err != nil { if err != nil {
return err return err
} }
@ -85,8 +85,8 @@ func RunResize(f *Factory, out io.Writer, cmd *cobra.Command, args []string) err
return err return err
} }
resourceVersion := util.GetFlagString(cmd, "resource-version") resourceVersion := cmdutil.GetFlagString(cmd, "resource-version")
currentSize := util.GetFlagInt(cmd, "current-replicas") currentSize := cmdutil.GetFlagInt(cmd, "current-replicas")
precondition := &kubectl.ResizePrecondition{currentSize, resourceVersion} precondition := &kubectl.ResizePrecondition{currentSize, resourceVersion}
cond := kubectl.ResizeCondition(resizer, precondition, namespace, name, uint(count)) cond := kubectl.ResizeCondition(resizer, precondition, namespace, name, uint(count))

View File

@ -23,7 +23,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -44,7 +44,7 @@ $ kubectl rolling-update frontend-v1 -f frontend-v2.json
$ cat frontend-v2.json | kubectl rolling-update frontend-v1 -f -` $ cat frontend-v2.json | kubectl rolling-update frontend-v1 -f -`
) )
func (f *Factory) NewCmdRollingUpdate(out io.Writer) *cobra.Command { func NewCmdRollingUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "rolling-update OLD_CONTROLLER_NAME -f NEW_CONTROLLER_SPEC", Use: "rolling-update OLD_CONTROLLER_NAME -f NEW_CONTROLLER_SPEC",
// rollingupdate is deprecated. // rollingupdate is deprecated.
@ -54,7 +54,7 @@ func (f *Factory) NewCmdRollingUpdate(out io.Writer) *cobra.Command {
Example: rollingUpdate_example, Example: rollingUpdate_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunRollingUpdate(f, out, cmd, args) err := RunRollingUpdate(f, out, cmd, args)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
cmd.Flags().String("update-period", updatePeriod, `Time to wait between updating pods. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) cmd.Flags().String("update-period", updatePeriod, `Time to wait between updating pods. Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`)
@ -64,20 +64,20 @@ func (f *Factory) NewCmdRollingUpdate(out io.Writer) *cobra.Command {
return cmd return cmd
} }
func RunRollingUpdate(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error { func RunRollingUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
if os.Args[1] == "rollingupdate" { if os.Args[1] == "rollingupdate" {
printDeprecationWarning("rolling-update", "rollingupdate") printDeprecationWarning("rolling-update", "rollingupdate")
} }
filename := util.GetFlagString(cmd, "filename") filename := cmdutil.GetFlagString(cmd, "filename")
if len(filename) == 0 { if len(filename) == 0 {
return util.UsageError(cmd, "Must specify filename for new controller") return cmdutil.UsageError(cmd, "Must specify filename for new controller")
} }
period := util.GetFlagDuration(cmd, "update-period") period := cmdutil.GetFlagDuration(cmd, "update-period")
interval := util.GetFlagDuration(cmd, "poll-interval") interval := cmdutil.GetFlagDuration(cmd, "poll-interval")
timeout := util.GetFlagDuration(cmd, "timeout") timeout := cmdutil.GetFlagDuration(cmd, "timeout")
if len(args) != 1 { if len(args) != 1 {
return util.UsageError(cmd, "Must specify the controller to update") return cmdutil.UsageError(cmd, "Must specify the controller to update")
} }
oldName := args[0] oldName := args[0]
@ -98,11 +98,11 @@ func RunRollingUpdate(f *Factory, out io.Writer, cmd *cobra.Command, args []stri
} }
newRc, ok := obj.(*api.ReplicationController) newRc, ok := obj.(*api.ReplicationController)
if !ok { if !ok {
return util.UsageError(cmd, "%s does not specify a valid ReplicationController", filename) return cmdutil.UsageError(cmd, "%s does not specify a valid ReplicationController", filename)
} }
newName := newRc.Name newName := newRc.Name
if oldName == newName { if oldName == newName {
return util.UsageError(cmd, "%s cannot have the same name as the existing ReplicationController %s", return cmdutil.UsageError(cmd, "%s cannot have the same name as the existing ReplicationController %s",
filename, oldName) filename, oldName)
} }
@ -127,7 +127,7 @@ func RunRollingUpdate(f *Factory, out io.Writer, cmd *cobra.Command, args []stri
} }
} }
if !hasLabel { if !hasLabel {
return util.UsageError(cmd, "%s must specify a matching key with non-equal value in Selector for %s", return cmdutil.UsageError(cmd, "%s must specify a matching key with non-equal value in Selector for %s",
filename, oldName) filename, oldName)
} }
// TODO: handle resizes during rolling update // TODO: handle resizes during rolling update

View File

@ -22,7 +22,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -42,7 +42,7 @@ $ kubectl run-container nginx --image=dockerfile/nginx --dry-run
$ kubectl run-container nginx --image=dockerfile/nginx --overrides='{ "apiVersion": "v1beta1", "desiredState": { ... } }'` $ kubectl run-container nginx --image=dockerfile/nginx --overrides='{ "apiVersion": "v1beta1", "desiredState": { ... } }'`
) )
func (f *Factory) NewCmdRunContainer(out io.Writer) *cobra.Command { func NewCmdRunContainer(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "run-container NAME --image=image [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json]", Use: "run-container NAME --image=image [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json]",
Short: "Run a particular image on the cluster.", Short: "Run a particular image on the cluster.",
@ -50,10 +50,10 @@ func (f *Factory) NewCmdRunContainer(out io.Writer) *cobra.Command {
Example: run_example, Example: run_example,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunRunContainer(f, out, cmd, args) err := RunRunContainer(f, out, cmd, args)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
util.AddPrinterFlags(cmd) cmdutil.AddPrinterFlags(cmd)
cmd.Flags().String("generator", "run-container/v1", "The name of the API generator to use. Default is 'run-container-controller/v1'.") cmd.Flags().String("generator", "run-container/v1", "The name of the API generator to use. Default is 'run-container-controller/v1'.")
cmd.Flags().String("image", "", "The image for the container to run.") cmd.Flags().String("image", "", "The image for the container to run.")
cmd.Flags().IntP("replicas", "r", 1, "Number of replicas to create for this container. Default is 1.") cmd.Flags().IntP("replicas", "r", 1, "Number of replicas to create for this container. Default is 1.")
@ -64,9 +64,9 @@ func (f *Factory) NewCmdRunContainer(out io.Writer) *cobra.Command {
return cmd return cmd
} }
func RunRunContainer(f *Factory, out io.Writer, cmd *cobra.Command, args []string) error { func RunRunContainer(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
if len(args) != 1 { if len(args) != 1 {
return util.UsageError(cmd, "NAME is required for run-container") return cmdutil.UsageError(cmd, "NAME is required for run-container")
} }
namespace, err := f.DefaultNamespace() namespace, err := f.DefaultNamespace()
@ -79,10 +79,10 @@ func RunRunContainer(f *Factory, out io.Writer, cmd *cobra.Command, args []strin
return err return err
} }
generatorName := util.GetFlagString(cmd, "generator") generatorName := cmdutil.GetFlagString(cmd, "generator")
generator, found := kubectl.Generators[generatorName] generator, found := kubectl.Generators[generatorName]
if !found { if !found {
return util.UsageError(cmd, fmt.Sprintf("Generator: %s not found.", generator)) return cmdutil.UsageError(cmd, fmt.Sprintf("Generator: %s not found.", generator))
} }
names := generator.ParamNames() names := generator.ParamNames()
params := kubectl.MakeParams(cmd, names) params := kubectl.MakeParams(cmd, names)
@ -98,16 +98,16 @@ func RunRunContainer(f *Factory, out io.Writer, cmd *cobra.Command, args []strin
return err return err
} }
inline := util.GetFlagString(cmd, "overrides") inline := cmdutil.GetFlagString(cmd, "overrides")
if len(inline) > 0 { if len(inline) > 0 {
controller, err = util.Merge(controller, inline, "ReplicationController") controller, err = cmdutil.Merge(controller, inline, "ReplicationController")
if err != nil { if err != nil {
return err return err
} }
} }
// TODO: extract this flag to a central location, when such a location exists. // TODO: extract this flag to a central location, when such a location exists.
if !util.GetFlagBool(cmd, "dry-run") { if !cmdutil.GetFlagBool(cmd, "dry-run") {
controller, err = client.ReplicationControllers(namespace).Create(controller.(*api.ReplicationController)) controller, err = client.ReplicationControllers(namespace).Create(controller.(*api.ReplicationController))
if err != nil { if err != nil {
return err return err

View File

@ -44,7 +44,7 @@ $ kubectl stop -f service.json
$ kubectl stop -f path/to/resources` $ kubectl stop -f path/to/resources`
) )
func (f *Factory) NewCmdStop(out io.Writer) *cobra.Command { func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command {
flags := &struct { flags := &struct {
Filenames util.StringList Filenames util.StringList
}{} }{}

View File

@ -40,7 +40,7 @@ $ cat pod.json | kubectl update -f -
$ kubectl update pods my-pod --patch='{ "apiVersion": "v1beta1", "desiredState": { "manifest": [{ "cpu": 100 }]}}'` $ kubectl update pods my-pod --patch='{ "apiVersion": "v1beta1", "desiredState": { "manifest": [{ "cpu": 100 }]}}'`
) )
func (f *Factory) NewCmdUpdate(out io.Writer) *cobra.Command { func NewCmdUpdate(f *cmdutil.Factory, out io.Writer) *cobra.Command {
var filenames util.StringList var filenames util.StringList
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "update -f FILENAME", Use: "update -f FILENAME",
@ -57,7 +57,7 @@ func (f *Factory) NewCmdUpdate(out io.Writer) *cobra.Command {
return cmd return cmd
} }
func RunUpdate(f *Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error { func RunUpdate(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, filenames util.StringList) error {
schema, err := f.Validator() schema, err := f.Validator()
if err != nil { if err != nil {
return err return err
@ -117,7 +117,7 @@ func RunUpdate(f *Factory, out io.Writer, cmd *cobra.Command, args []string, fil
} }
func updateWithPatch(cmd *cobra.Command, args []string, f *Factory, patch string) (string, error) { func updateWithPatch(cmd *cobra.Command, args []string, f *cmdutil.Factory, patch string) (string, error) {
cmdNamespace, err := f.DefaultNamespace() cmdNamespace, err := f.DefaultNamespace()
if err != nil { if err != nil {
return "", err return "", err

View File

@ -47,7 +47,7 @@ func TestUpdateObject(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdUpdate(buf) cmd := NewCmdUpdate(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -83,7 +83,7 @@ func TestUpdateMultipleObject(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdUpdate(buf) cmd := NewCmdUpdate(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json") cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json") cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})
@ -115,7 +115,7 @@ func TestUpdateDirectory(t *testing.T) {
tf.Namespace = "test" tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
cmd := f.NewCmdUpdate(buf) cmd := NewCmdUpdate(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook") cmd.Flags().Set("filename", "../../../examples/guestbook")
cmd.Flags().Set("namespace", "test") cmd.Flags().Set("namespace", "test")
cmd.Run(cmd, []string{}) cmd.Run(cmd, []string{})

View File

@ -0,0 +1,76 @@
/*
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 util
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
)
// clientCache caches previously loaded clients for reuse, and ensures MatchServerVersion
// is invoked only once
type clientCache struct {
loader clientcmd.ClientConfig
clients map[string]*client.Client
defaultConfig *client.Config
matchVersion bool
}
// ClientConfigForVersion returns the correct config for a server
func (c *clientCache) ClientConfigForVersion(version string) (*client.Config, error) {
if c.defaultConfig == nil {
config, err := c.loader.ClientConfig()
if err != nil {
return nil, err
}
c.defaultConfig = config
if c.matchVersion {
if err := client.MatchesServerVersion(config); err != nil {
return nil, err
}
}
}
// TODO: have a better config copy method
config := *c.defaultConfig
if len(version) != 0 {
config.Version = version
}
client.SetKubernetesDefaults(&config)
return &config, nil
}
// ClientForVersion initializes or reuses a client for the specified version, or returns an
// error if that is not possible
func (c *clientCache) ClientForVersion(version string) (*client.Client, error) {
config, err := c.ClientConfigForVersion(version)
if err != nil {
return nil, err
}
if client, ok := c.clients[config.Version]; ok {
return client, nil
}
client, err := client.New(config)
if err != nil {
return nil, err
}
c.clients[config.Version] = client
return client, nil
}

View File

@ -0,0 +1,391 @@
/*
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 util
import (
"fmt"
"io"
"os"
"strconv"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"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/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"
)
const (
FlagMatchBinaryVersion = "match-server-version"
)
// 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
// TODO: pass the various interfaces on the factory directly into the command constructors (so the
// commands are decoupled from the factory).
type Factory struct {
clients *clientCache
flags *pflag.FlagSet
// Returns interfaces for dealing with arbitrary runtime.Objects.
Object func() (meta.RESTMapper, runtime.ObjectTyper)
// Returns a client for accessing Kubernetes resources or an error.
Client func() (*client.Client, error)
// Returns a client.Config for accessing the Kubernetes server.
ClientConfig func() (*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(mapping *meta.RESTMapping) (resource.RESTClient, error)
// Returns a Describer for displaying the specified RESTMapping type or an error.
Describer func(mapping *meta.RESTMapping) (kubectl.Describer, error)
// Returns a Printer for formatting objects of the given type or an error.
Printer func(mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error)
// Returns a Resizer for changing the size of the specified RESTMapping type or an error
Resizer func(mapping *meta.RESTMapping) (kubectl.Resizer, error)
// Returns a Reaper for gracefully shutting down resources.
Reaper func(mapping *meta.RESTMapping) (kubectl.Reaper, error)
// PodSelectorForResource returns the pod selector associated with the provided resource name
// or an error.
PodSelectorForResource func(mapping *meta.RESTMapping, namespace, name string) (string, error)
// PortForResource returns the ports associated with the provided resource name or an error
PortsForResource func(mapping *meta.RESTMapping, namespace, name string) ([]string, error)
// Returns a schema that can validate objects stored on disk.
Validator func() (validation.Schema, error)
// Returns the default namespace to use in cases where no other namespace is specified
DefaultNamespace func() (string, error)
}
// NewFactory creates a factory with the default Kubernetes resources defined
// if optionalClientConfig is nil, then flags will be bound to a new clientcmd.ClientConfig.
// if optionalClientConfig is not nil, then this factory will make use of it.
func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
mapper := kubectl.ShortcutExpander{latest.RESTMapper}
flags := pflag.NewFlagSet("", pflag.ContinueOnError)
clientConfig := optionalClientConfig
if optionalClientConfig == nil {
clientConfig = DefaultClientConfig(flags)
}
clients := &clientCache{
clients: make(map[string]*client.Client),
loader: clientConfig,
}
return &Factory{
clients: clients,
flags: flags,
Object: func() (meta.RESTMapper, runtime.ObjectTyper) {
cfg, err := clientConfig.ClientConfig()
CheckErr(err)
cmdApiVersion := cfg.Version
return kubectl.OutputVersionMapper{mapper, cmdApiVersion}, api.Scheme
},
Client: func() (*client.Client, error) {
return clients.ClientForVersion("")
},
ClientConfig: func() (*client.Config, error) {
return clients.ClientConfigForVersion("")
},
RESTClient: func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
if err != nil {
return nil, err
}
return client.RESTClient, nil
},
Describer: func(mapping *meta.RESTMapping) (kubectl.Describer, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
if err != nil {
return nil, err
}
describer, ok := kubectl.DescriberFor(mapping.Kind, client)
if !ok {
return nil, fmt.Errorf("no description has been implemented for %q", mapping.Kind)
}
return describer, nil
},
Printer: func(mapping *meta.RESTMapping, noHeaders bool) (kubectl.ResourcePrinter, error) {
return kubectl.NewHumanReadablePrinter(noHeaders), nil
},
PodSelectorForResource: func(mapping *meta.RESTMapping, namespace, name string) (string, error) {
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
client, err := clients.ClientForVersion("")
if err != nil {
return "", err
}
switch mapping.Kind {
case "ReplicationController":
rc, err := client.ReplicationControllers(namespace).Get(name)
if err != nil {
return "", err
}
return kubectl.MakeLabels(rc.Spec.Selector), nil
case "Pod":
rc, err := client.Pods(namespace).Get(name)
if err != nil {
return "", err
}
if len(rc.Labels) == 0 {
return "", fmt.Errorf("the pod has no labels and cannot be exposed")
}
return kubectl.MakeLabels(rc.Labels), nil
case "Service":
rc, err := client.ReplicationControllers(namespace).Get(name)
if err != nil {
return "", err
}
if rc.Spec.Selector == nil {
return "", fmt.Errorf("the service has no pod selector set")
}
return kubectl.MakeLabels(rc.Spec.Selector), nil
default:
return "", fmt.Errorf("it is not possible to get a pod selector from %s", mapping.Kind)
}
},
PortsForResource: func(mapping *meta.RESTMapping, namespace, name string) ([]string, error) {
// TODO: replace with a swagger schema based approach (identify pod selector via schema introspection)
client, err := clients.ClientForVersion("")
if err != nil {
return nil, err
}
switch mapping.Kind {
case "ReplicationController":
rc, err := client.ReplicationControllers(namespace).Get(name)
if err != nil {
return nil, err
}
return getPorts(rc.Spec.Template.Spec), nil
case "Pod":
pod, err := client.Pods(namespace).Get(name)
if err != nil {
return nil, err
}
return getPorts(pod.Spec), nil
default:
return nil, fmt.Errorf("it is not possible to get ports from %s", mapping.Kind)
}
},
Resizer: func(mapping *meta.RESTMapping) (kubectl.Resizer, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
if err != nil {
return nil, err
}
return kubectl.ResizerFor(mapping.Kind, client)
},
Reaper: func(mapping *meta.RESTMapping) (kubectl.Reaper, error) {
client, err := clients.ClientForVersion(mapping.APIVersion)
if err != nil {
return nil, err
}
return kubectl.ReaperFor(mapping.Kind, client)
},
Validator: func() (validation.Schema, error) {
if flags.Lookup("validate").Value.String() == "true" {
client, err := clients.ClientForVersion("")
if err != nil {
return nil, err
}
return &clientSwaggerSchema{client, api.Scheme}, nil
}
return validation.NullSchema{}, nil
},
DefaultNamespace: func() (string, error) {
return clientConfig.Namespace()
},
}
}
// BindFlags adds any flags that are common to all kubectl sub commands.
func (f *Factory) BindFlags(flags *pflag.FlagSet) {
// any flags defined by external projects (not part of pflags)
util.AddAllFlagsToPFlagSet(flags)
// This is necessary as github.com/spf13/cobra doesn't support "global"
// pflags currently. See https://github.com/spf13/cobra/issues/44.
util.AddPFlagSetToPFlagSet(pflag.CommandLine, flags)
// Hack for global access to validation flag.
// TODO: Refactor out after configuration flag overhaul.
if f.flags.Lookup("validate") == nil {
f.flags.Bool("validate", false, "If true, use a schema to validate the input before sending it")
}
if f.flags != nil {
f.flags.VisitAll(func(flag *pflag.Flag) {
flags.AddFlag(flag)
})
}
// Globally persistent flags across all subcommands.
// TODO Change flag names to consts to allow safer lookup from subcommands.
// TODO Add a verbose flag that turns on glog logging. Probably need a way
// to do that automatically for every subcommand.
flags.BoolVar(&f.clients.matchVersion, FlagMatchBinaryVersion, false, "Require server version to match client version")
}
func getPorts(spec api.PodSpec) []string {
result := []string{}
for _, container := range spec.Containers {
for _, port := range container.Ports {
result = append(result, strconv.Itoa(port.ContainerPort))
}
}
return result
}
type clientSwaggerSchema struct {
c *client.Client
t runtime.ObjectTyper
}
func (c *clientSwaggerSchema) ValidateBytes(data []byte) error {
version, _, err := c.t.DataVersionAndKind(data)
if err != nil {
return err
}
schemaData, err := c.c.RESTClient.Get().
AbsPath("/swaggerapi/api", version).
Do().
Raw()
if err != nil {
return err
}
schema, err := validation.NewSwaggerSchemaFromBytes(schemaData)
if err != nil {
return err
}
return schema.ValidateBytes(data)
}
// DefaultClientConfig creates a clientcmd.ClientConfig with the following hierarchy:
// 1. Use the kubeconfig builder. The number of merges and overrides here gets a little crazy. Stay with me.
// 1. Merge together the kubeconfig itself. This is done with the following hierarchy and merge rules:
// 1. CommandLineLocation - this parsed from the command line, so it must be late bound
// 2. EnvVarLocation
// 3. CurrentDirectoryLocation
// 4. HomeDirectoryLocation
// Empty filenames are ignored. Files with non-deserializable content produced errors.
// The first file to set a particular value or map key wins and the value or map key is never changed.
// This means that the first file to set CurrentContext will have its context preserved. It also means
// that if two files specify a "red-user", only values from the first file's red-user are used. Even
// non-conflicting entries from the second file's "red-user" are discarded.
// 2. Determine the context to use based on the first hit in this chain
// 1. command line argument - again, parsed from the command line, so it must be late bound
// 2. CurrentContext from the merged kubeconfig file
// 3. Empty is allowed at this stage
// 3. Determine the cluster info and auth info to use. At this point, we may or may not have a context. They
// are built based on the first hit in this chain. (run it twice, once for auth, once for cluster)
// 1. command line argument
// 2. If context is present, then use the context value
// 3. Empty is allowed
// 4. Determine the actual cluster info to use. At this point, we may or may not have a cluster info. Build
// each piece of the cluster info based on the chain:
// 1. command line argument
// 2. If cluster info is present and a value for the attribute is present, use it.
// 3. If you don't have a server location, bail.
// 5. Auth info is build using the same rules as cluster info, EXCEPT that you can only have one authentication
// technique per auth info. The following conditions result in an error:
// 1. If there are two conflicting techniques specified from the command line, fail.
// 2. If the command line does not specify one, and the auth info has conflicting techniques, fail.
// 3. If the command line specifies one and the auth info specifies another, honor the command line technique.
// 2. Use default values and potentially prompt for auth information
func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
flags.StringVar(&loadingRules.ExplicitPath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.")
overrides := &clientcmd.ConfigOverrides{}
flagNames := clientcmd.RecommendedConfigOverrideFlags("")
// short flagnames are disabled by default. These are here for compatibility with existing scripts
flagNames.AuthOverrideFlags.AuthPathShort = "a"
flagNames.ClusterOverrideFlags.APIServerShort = "s"
clientcmd.BindOverrideFlags(overrides, flags, flagNames)
clientConfig := clientcmd.NewInteractiveDeferredLoadingClientConfig(loadingRules, overrides, os.Stdin)
return clientConfig
}
// PrintObject prints an api object given command line flags to modify the output format
func (f *Factory) PrintObject(cmd *cobra.Command, obj runtime.Object, out io.Writer) error {
mapper, _ := f.Object()
_, kind, err := api.Scheme.ObjectVersionAndKind(obj)
if err != nil {
return err
}
mapping, err := mapper.RESTMapping(kind)
if err != nil {
return err
}
printer, err := f.PrinterForMapping(cmd, mapping)
if err != nil {
return err
}
return printer.PrintObj(obj, out)
}
// PrinterForMapping returns a printer suitable for displaying the provided resource type.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
func (f *Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping) (kubectl.ResourcePrinter, error) {
printer, ok, err := PrinterForCommand(cmd)
if err != nil {
return nil, err
}
if ok {
clientConfig, err := f.ClientConfig()
if err != nil {
return nil, err
}
defaultVersion := clientConfig.Version
version := OutputVersion(cmd, defaultVersion)
if len(version) == 0 {
version = mapping.APIVersion
}
if len(version) == 0 {
return nil, fmt.Errorf("you must specify an output-version when using this output format")
}
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version)
} else {
printer, err = f.Printer(mapping, GetFlagBool(cmd, "no-headers"))
if err != nil {
return nil, err
}
}
return printer, nil
}
// ClientMapperForCommand returns a ClientMapper for the factory.
func (f *Factory) ClientMapperForCommand() resource.ClientMapper {
return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
return f.RESTClient(mapping)
})
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package cmd package util
import ( import (
"testing" "testing"

View File

@ -22,24 +22,24 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl" "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util" cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
) )
func (f *Factory) NewCmdVersion(out io.Writer) *cobra.Command { func NewCmdVersion(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "version", Use: "version",
Short: "Print the client and server version information.", Short: "Print the client and server version information.",
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
err := RunVersion(f, out, cmd) err := RunVersion(f, out, cmd)
util.CheckErr(err) cmdutil.CheckErr(err)
}, },
} }
cmd.Flags().BoolP("client", "c", false, "Client version only (no server required).") cmd.Flags().BoolP("client", "c", false, "Client version only (no server required).")
return cmd return cmd
} }
func RunVersion(f *Factory, out io.Writer, cmd *cobra.Command) error { func RunVersion(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command) error {
if util.GetFlagBool(cmd, "client") { if cmdutil.GetFlagBool(cmd, "client") {
kubectl.GetClientVersion(out) kubectl.GetClientVersion(out)
return nil return nil
} }