Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Allow output formatting for kubectl config view #4147

Merged
merged 2 commits into from
Feb 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 13 additions & 3 deletions docs/kubectl.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ Usage:
kubectl config [command]

Available Commands:
view displays the specified .kubeconfig file or a merged result
view displays merged .kubeconfig settings or a specified .kubeconfig file.
set-cluster name [--server=server] [--certificate-authority=path/to/certficate/authority] [--api-version=apiversion] [--insecure-skip-tls-verify=true] Sets a cluster entry in .kubeconfig
set-credentials name [--auth-path=path/to/auth/file] [--client-certificate=path/to/certficate/file] [--client-key=path/to/key/file] [--token=bearer_token_string] Sets a user entry in .kubeconfig
set-context name [--cluster=cluster-nickname] [--user=user-nickname] [--namespace=namespace] Sets a context entry in .kubeconfig
Expand Down Expand Up @@ -395,7 +395,13 @@ Use "kubectl help [command]" for more information about that command.
```

#### config view
displays the specified .kubeconfig file or a merged result
displays merged .kubeconfig settings or a specified .kubeconfig file.
Examples:
// Show merged .kubeconfig settings.
$ kubectl config view

// Show only local ./.kubeconfig settings
$ kubectl config view --local

Usage:
```
Expand All @@ -421,10 +427,14 @@ Usage:
--log_flush_frequency=5s: Maximum number of seconds between log flushes
--logtostderr=true: log to standard error instead of files
--match-server-version=false: Require server version to match client version
--merge=false: merge together the full hierarchy of .kubeconfig files
--merge=true: merge together the full hierarchy of .kubeconfig files
--namespace="": If present, the namespace scope for this CLI request.
--no-headers=false: When using the default output, don't print headers
-o, --output="": Output format: json|yaml|template|templatefile
--output-version="": Output the formatted object with the given version (default api-version)
-s, --server="": The address of the Kubernetes API server
--stderrthreshold=2: logs at or above this threshold go to stderr
-t, --template="": Template string or path to template file to use when -o=template or -o=templatefile.
--token="": Bearer token for authentication to the API server.
--user="": The name of the kubeconfig user to use
--v=0: log level for V logs
Expand Down
66 changes: 58 additions & 8 deletions pkg/kubectl/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
cmdconfig "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/config"
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"
Expand Down Expand Up @@ -143,7 +144,7 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory {
return kubectl.ReaperFor(mapping.Kind, client)
},
Validator: func(cmd *cobra.Command) (validation.Schema, error) {
if GetFlagBool(cmd, "validate") {
if cmdutil.GetFlagBool(cmd, "validate") {
client, err := clients.ClientForVersion("")
if err != nil {
return nil, err
Expand Down Expand Up @@ -217,6 +218,62 @@ Find more information at https://github.com/GoogleCloudPlatform/kubernetes.`,
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(cmd)
_, 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(cmd)
checkErr(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(cmd, mapping, cmdutil.GetFlagBool(cmd, "no-headers"))
if err != nil {
return nil, err
}
}
return printer, nil
}

// ClientMapperForCommand returns a ClientMapper for the given command and factory.
func (f *Factory) ClientMapperForCommand(cmd *cobra.Command) resource.ClientMapper {
return resource.ClientMapperFunc(func(mapping *meta.RESTMapping) (resource.RESTClient, error) {
return f.RESTClient(cmd, 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:
Expand Down Expand Up @@ -266,13 +323,6 @@ func DefaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig {
return clientConfig
}

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

func checkErr(err error) {
if err != nil {
glog.FatalDepth(1, err)
Expand Down
41 changes: 41 additions & 0 deletions pkg/kubectl/cmd/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"fmt"
"io"
"io/ioutil"
"os"
"testing"

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
Expand Down Expand Up @@ -203,3 +204,43 @@ func TestClientVersions(t *testing.T) {
}
}
}

func ExamplePrintReplicationController() {
f, tf, codec := NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(false)
tf.Client = &client.FakeRESTClient{
Codec: codec,
Client: nil,
}
cmd := f.NewCmdRunContainer(os.Stdout)
ctrl := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar"},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Selector: map[string]string{"foo": "bar"},
Template: &api.PodTemplateSpec{
ObjectMeta: api.ObjectMeta{
Labels: map[string]string{"foo": "bar"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "foo",
Image: "someimage",
},
},
},
},
},
}
err := f.PrintObject(cmd, ctrl, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
// Output:
// CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS
// foo foo someimage foo=bar 1
}
48 changes: 27 additions & 21 deletions pkg/kubectl/cmd/config/view.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ import (
"io"
"os"

"github.com/ghodss/yaml"
"github.com/golang/glog"
"github.com/spf13/cobra"

"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
)

type viewOptions struct {
Expand All @@ -38,40 +39,45 @@ func NewCmdConfigView(out io.Writer, pathOptions *pathOptions) *cobra.Command {

cmd := &cobra.Command{
Use: "view",
Short: "displays the specified .kubeconfig file or a merged result",
Long: `displays the specified .kubeconfig file or a merged result`,
Short: "displays merged .kubeconfig settings or a specified .kubeconfig file.",
Long: `displays merged .kubeconfig settings or a specified .kubeconfig file.
Examples:
// Show merged .kubeconfig settings.
$ kubectl config view

// Show only local ./.kubeconfig settings
$ kubectl config view --local`,
Run: func(cmd *cobra.Command, args []string) {
err := options.run()
printer, _, err := util.PrinterForCommand(cmd)
if err != nil {
fmt.Printf("%v\n", err)
glog.FatalDepth(1, err)
}
config, err := options.loadConfig()
if err != nil {
glog.FatalDepth(1, err)
}
err = printer.PrintObj(config, out)
if err != nil {
glog.FatalDepth(1, err)
}
},
}

cmd.Flags().BoolVar(&options.merge, "merge", false, "merge together the full hierarchy of .kubeconfig files")

util.AddPrinterFlags(cmd)
// Default to yaml
cmd.Flags().Set("output", "yaml")
cmd.Flags().BoolVar(&options.merge, "merge", true, "merge together the full hierarchy of .kubeconfig files")
return cmd
}

func (o viewOptions) run() error {
func (o viewOptions) loadConfig() (*clientcmdapi.Config, error) {
err := o.validate()
if err != nil {
return err
return nil, err
}

config, _, err := o.getStartingConfig()
if err != nil {
return err
}

content, err := yaml.Marshal(config)
if err != nil {
return err
}

fmt.Printf("%v", string(content))

return nil
return config, err
}

func (o viewOptions) validate() error {
Expand Down
2 changes: 1 addition & 1 deletion pkg/kubectl/cmd/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Examples:
checkErr(err)

mapper, typer := f.Object(cmd)
r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)).
ContinueOnError().
NamespaceParam(cmdNamespace).RequireNamespace().
FilenameParam(flags.Filenames...).
Expand Down
5 changes: 3 additions & 2 deletions pkg/kubectl/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/spf13/cobra"

"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
cmdutil "github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
Expand Down Expand Up @@ -62,11 +63,11 @@ Examples:
checkErr(err)

mapper, typer := f.Object(cmd)
r := resource.NewBuilder(mapper, typer, ClientMapperForCommand(cmd, f)).
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand(cmd)).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(flags.Filenames...).
SelectorParam(GetFlagString(cmd, "selector")).
SelectorParam(cmdutil.GetFlagString(cmd, "selector")).
ResourceTypeOrNameArgs(args...).
Flatten().
Do()
Expand Down
3 changes: 2 additions & 1 deletion pkg/kubectl/cmd/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"
"io"

"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra"
)

Expand All @@ -36,7 +37,7 @@ given resource.`,
checkErr(err)

mapper, _ := f.Object(cmd)
mapping, namespace, name := ResourceFromArgs(cmd, args, mapper, cmdNamespace)
mapping, namespace, name := util.ResourceFromArgs(cmd, args, mapper, cmdNamespace)

describer, err := f.Describer(cmd, mapping)
checkErr(err)
Expand Down
22 changes: 12 additions & 10 deletions pkg/kubectl/cmd/expose.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl/cmd/util"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -51,27 +52,28 @@ $ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream
client, err := f.Client(cmd)
checkErr(err)

generatorName := GetFlagString(cmd, "generator")
generatorName := util.GetFlagString(cmd, "generator")

generator, found := kubectl.Generators[generatorName]
if !found {
usageError(cmd, fmt.Sprintf("Generator: %s not found.", generator))
}
if GetFlagInt(cmd, "port") < 1 {
if util.GetFlagInt(cmd, "port") < 1 {
usageError(cmd, "--port is required and must be a positive integer.")
}
names := generator.ParamNames()
params := kubectl.MakeParams(cmd, names)
if len(GetFlagString(cmd, "service-name")) == 0 {
if len(util.GetFlagString(cmd, "service-name")) == 0 {
params["name"] = args[0]
} else {
params["name"] = GetFlagString(cmd, "service-name")
params["name"] = util.GetFlagString(cmd, "service-name")
}
if _, found := params["selector"]; !found {
rc, err := client.ReplicationControllers(namespace).Get(args[0])
checkErr(err)
params["selector"] = kubectl.MakeLabels(rc.Spec.Selector)
}
if GetFlagBool(cmd, "create-external-load-balancer") {
if util.GetFlagBool(cmd, "create-external-load-balancer") {
params["create-external-load-balancer"] = "true"
}

Expand All @@ -81,22 +83,22 @@ $ kubectl expose streamer --port=4100 --protocol=udp --service-name=video-stream
service, err := generator.Generate(params)
checkErr(err)

inline := GetFlagString(cmd, "overrides")
inline := util.GetFlagString(cmd, "overrides")
if len(inline) > 0 {
Merge(service, inline, "Service")
util.Merge(service, inline, "Service")
}

// TODO: extract this flag to a central location, when such a location exists.
if !GetFlagBool(cmd, "dry-run") {
if !util.GetFlagBool(cmd, "dry-run") {
service, err = client.Services(namespace).Create(service.(*api.Service))
checkErr(err)
}

err = PrintObject(cmd, service, f, out)
err = f.PrintObject(cmd, service, out)
checkErr(err)
},
}
AddPrinterFlags(cmd)
util.AddPrinterFlags(cmd)
cmd.Flags().String("generator", "service/v1", "The name of the api generator that you want to use. Default 'service/v1'")
cmd.Flags().String("protocol", "TCP", "The network protocol for the service you want to be created. Default 'tcp'")
cmd.Flags().Int("port", -1, "The port that the service should serve on. Required.")
Expand Down