Merge pull request #41729 from smarterclayton/refactor_printers

Automatic merge from submit-queue (batch tested with PRs 41621, 41946, 41941, 41250, 41729)

Refactor printers and describers into their own package.

This sets the stage for using printer code from the server side (decoupled from kubectl) and loosens the coupling between kubectl and the printers. `pkg/printers` contains interfaces and has an import restriction against pulling in API specific code, while `pkg/printers/internalversion` can be used for internal types.

Add a method on `Factory` for retrieving PrinterForCommand which uses the Scheme and RESTMapper from the Factory, not the hardcoded ones.  This further separates kubectl from the core API scheme and allows better composition.

Change NamePrinter to use RESTMapper (previously it was hardcoding those conversions). This means that we now return plural resource names (`pods/foo`) but is correct once aliases and shortnames start being returned by the mapper.

This is a prerequisite for server side get, but is pure refactor (contains no new features).

@deads2k @liggitt
This commit is contained in:
Kubernetes Submit Queue
2017-02-26 06:47:03 -08:00
committed by GitHub
69 changed files with 2039 additions and 1407 deletions

View File

@@ -38,8 +38,8 @@ go_library(
"//cmd/kubeadm/app/util/kubeconfig:go_default_library",
"//cmd/kubeadm/app/util/token:go_default_library",
"//pkg/bootstrap/api:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/util/initsystem:go_default_library",
"//pkg/version:go_default_library",
"//vendor:github.com/blang/semver",

View File

@@ -37,7 +37,7 @@ import (
kubeconfigutil "k8s.io/kubernetes/cmd/kubeadm/app/util/kubeconfig"
tokenutil "k8s.io/kubernetes/cmd/kubeadm/app/util/token"
bootstrapapi "k8s.io/kubernetes/pkg/bootstrap/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/printers"
)
func NewCmdToken(out io.Writer, errW io.Writer) *cobra.Command {
@@ -207,7 +207,7 @@ func RunListTokens(out io.Writer, errW io.Writer, cmd *cobra.Command) error {
if err != nil {
return fmt.Errorf("error parsing expiration time [%v]", err)
}
expires = kubectl.ShortHumanDuration(expireTime.Sub(time.Now()))
expires = printers.ShortHumanDuration(expireTime.Sub(time.Now()))
}
fmt.Fprintf(w, "%s\t%s\t%s\n", tokenId, tokenutil.BearerToken(td), expires)
}

View File

@@ -180,6 +180,7 @@ pkg/kubelet/volumemanager/cache
pkg/kubelet/volumemanager/populator
pkg/kubelet/volumemanager/reconciler
pkg/labels
pkg/printers
pkg/proxy/config
pkg/proxy/healthcheck
pkg/quota

View File

@@ -92,6 +92,7 @@ filegroup(
"//pkg/labels:all-srcs",
"//pkg/master:all-srcs",
"//pkg/metrics:all-srcs",
"//pkg/printers:all-srcs",
"//pkg/probe:all-srcs",
"//pkg/proxy:all-srcs",
"//pkg/quota:all-srcs",

View File

@@ -13,7 +13,7 @@ go_library(
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/printers:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/validation/field",

View File

@@ -30,7 +30,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/printers"
)
// Based on: https://github.com/openshift/origin/blob/master/pkg/api/compatibility_test.go
@@ -94,7 +94,7 @@ func TestCompatibility(
}
if hasError {
printer := new(kubectl.JSONPrinter)
printer := &printers.JSONPrinter{}
printer.PrintObj(obj, os.Stdout)
t.Logf("2: Encoded value: %#v", string(output))
}

View File

@@ -63,6 +63,7 @@ go_test(
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/install:go_default_library",
"//pkg/api/testing/compat:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/api/validation:go_default_library",

View File

@@ -25,6 +25,8 @@ import (
"k8s.io/kubernetes/pkg/api/testing/compat"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/api/validation"
_ "k8s.io/kubernetes/pkg/api/install"
)
func TestCompatibility_v1_PodSecurityContext(t *testing.T) {

View File

@@ -17,9 +17,7 @@ go_library(
"cluster.go",
"clusterrolebinding.go",
"configmap.go",
"custom_column_printer.go",
"deployment.go",
"describe.go",
"doc.go",
"explain.go",
"generate.go",
@@ -31,7 +29,6 @@ go_library(
"proxy_server.go",
"quota.go",
"resource_filter.go",
"resource_printer.go",
"rolebinding.go",
"rollback.go",
"rolling_updater.go",
@@ -44,31 +41,24 @@ go_library(
"service.go",
"service_basic.go",
"serviceaccount.go",
"sorted_resource_name_list.go",
"sorting_printer.go",
"stop.go",
"versioned_client.go",
],
tags = ["automanaged"],
deps = [
"//federation/apis/federation:go_default_library",
"//federation/apis/federation/v1beta1:go_default_library",
"//federation/client/clientset_generated/federation_internalclientset:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/annotations:go_default_library",
"//pkg/api/events:go_default_library",
"//pkg/api/util:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/certificates:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/apis/rbac:go_default_library",
"//pkg/apis/storage:go_default_library",
"//pkg/apis/storage/util:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/clientset_generated/clientset/typed/core/v1:go_default_library",
"//pkg/client/clientset_generated/clientset/typed/extensions/v1beta1:go_default_library",
@@ -81,14 +71,12 @@ go_library(
"//pkg/client/unversioned:go_default_library",
"//pkg/controller/deployment/util:go_default_library",
"//pkg/credentialprovider:go_default_library",
"//pkg/fieldpath:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/kubelet/qos:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
"//pkg/util:go_default_library",
"//pkg/util/node:go_default_library",
"//pkg/util/slice:go_default_library",
"//vendor:github.com/emicklei/go-restful/swagger",
"//vendor:github.com/ghodss/yaml",
"//vendor:github.com/golang/glog",
"//vendor:github.com/spf13/cobra",
"//vendor:github.com/spf13/pflag",
@@ -104,12 +92,10 @@ go_library(
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apimachinery/pkg/util/errors",
"//vendor:k8s.io/apimachinery/pkg/util/intstr",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apimachinery/pkg/util/uuid",
"//vendor:k8s.io/apimachinery/pkg/util/validation",
"//vendor:k8s.io/apimachinery/pkg/util/wait",
"//vendor:k8s.io/apimachinery/pkg/watch",
"//vendor:k8s.io/client-go/dynamic",
"//vendor:k8s.io/client-go/rest",
"//vendor:k8s.io/client-go/util/integer",
"//vendor:k8s.io/client-go/util/jsonpath",
@@ -121,15 +107,12 @@ go_test(
srcs = [
"cluster_test.go",
"configmap_test.go",
"custom_column_printer_test.go",
"deployment_test.go",
"describe_test.go",
"generate_test.go",
"kubectl_test.go",
"namespace_test.go",
"proxy_server_test.go",
"quota_test.go",
"resource_printer_test.go",
"rolling_updater_test.go",
"rollout_status_test.go",
"run_test.go",
@@ -140,46 +123,32 @@ go_test(
"service_basic_test.go",
"service_test.go",
"serviceaccount_test.go",
"sorted_resource_name_list_test.go",
"sorting_printer_test.go",
"stop_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//federation/apis/federation:go_default_library",
"//federation/apis/federation/v1beta1:go_default_library",
"//federation/client/clientset_generated/federation_internalclientset/fake:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/apis/storage:go_default_library",
"//pkg/client/clientset_generated/clientset/fake:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/batch/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
"//pkg/controller/deployment/util:go_default_library",
"//pkg/kubectl/testing:go_default_library",
"//pkg/util:go_default_library",
"//vendor:github.com/ghodss/yaml",
"//vendor:github.com/spf13/cobra",
"//vendor:k8s.io/apimachinery/pkg/api/equality",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/api/resource",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/runtime/serializer/yaml",
"//vendor:k8s.io/apimachinery/pkg/util/diff",
"//vendor:k8s.io/apimachinery/pkg/util/intstr",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apimachinery/pkg/watch",

View File

@@ -90,6 +90,8 @@ go_library(
"//pkg/kubectl/resource:go_default_library",
"//pkg/kubelet/server/remotecommand:go_default_library",
"//pkg/kubelet/types:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
"//pkg/util/crlf:go_default_library",
"//pkg/util/exec:go_default_library",
"//pkg/util/i18n:go_default_library",
@@ -198,6 +200,8 @@ go_test(
"//pkg/kubectl/cmd/testing:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
"//pkg/util/strings:go_default_library",
"//pkg/util/term:go_default_library",
"//vendor:github.com/spf13/cobra",
@@ -208,6 +212,7 @@ go_test(
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/api/resource",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/runtime/serializer/json",

View File

@@ -36,6 +36,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util/i18n"
)
@@ -101,7 +102,7 @@ func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command {
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, kubectl.PrintOptions{
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)

View File

@@ -26,9 +26,9 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util/i18n"
)
@@ -93,10 +93,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, args []string, out i
return err
}
printer, _, err := kubectl.GetPrinter("json", "", false, true)
if err != nil {
return err
}
printer := &printers.JSONPrinter{}
nodes, err := clientset.Core().Nodes().List(metav1.ListOptions{})
if err != nil {

View File

@@ -35,9 +35,10 @@ import (
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
"k8s.io/kubernetes/pkg/kubectl"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
"k8s.io/kubernetes/pkg/util/strings"
)
@@ -97,12 +98,12 @@ func (t *testPrinter) AfterPrint(output io.Writer, res string) error {
type testDescriber struct {
Name, Namespace string
Settings kubectl.DescriberSettings
Settings printers.DescriberSettings
Output string
Err error
}
func (t *testDescriber) Describe(namespace, name string, describerSettings kubectl.DescriberSettings) (output string, err error) {
func (t *testDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (output string, err error) {
t.Namespace, t.Name = namespace, name
t.Settings = describerSettings
return t.Output, t.Err
@@ -146,10 +147,12 @@ func stringBody(body string) io.ReadCloser {
func Example_printReplicationControllerWithNamespace() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
p := printers.NewHumanReadablePrinter(printers.PrintOptions{
WithNamespace: true,
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,
@@ -197,10 +200,12 @@ func Example_printReplicationControllerWithNamespace() {
func Example_printMultiContainersReplicationControllerWithWide() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
p := printers.NewHumanReadablePrinter(printers.PrintOptions{
Wide: true,
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,
@@ -250,9 +255,11 @@ func Example_printMultiContainersReplicationControllerWithWide() {
func Example_printReplicationController() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
p := printers.NewHumanReadablePrinter(printers.PrintOptions{
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,
@@ -302,10 +309,12 @@ func Example_printReplicationController() {
func Example_printPodWithWideFormat() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
p := printers.NewHumanReadablePrinter(printers.PrintOptions{
Wide: true,
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,
@@ -343,10 +352,12 @@ func Example_printPodWithWideFormat() {
func Example_printPodWithShowLabels() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
p := printers.NewHumanReadablePrinter(printers.PrintOptions{
ShowLabels: true,
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,
@@ -479,9 +490,11 @@ func newAllPhasePodList() *api.PodList {
func Example_printPodHideTerminated() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
p := printers.NewHumanReadablePrinter(printers.PrintOptions{
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,
@@ -512,10 +525,12 @@ func Example_printPodHideTerminated() {
func Example_printPodShowAll() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
p := printers.NewHumanReadablePrinter(printers.PrintOptions{
ShowAll: true,
ColumnLabels: []string{},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,
@@ -539,10 +554,12 @@ func Example_printPodShowAll() {
func Example_printServiceWithNamespacesAndLabels() {
f, tf, _, ns := cmdtesting.NewAPIFactory()
tf.Printer = kubectl.NewHumanReadablePrinter(kubectl.PrintOptions{
p := printers.NewHumanReadablePrinter(printers.PrintOptions{
WithNamespace: true,
ColumnLabels: []string{"l1"},
})
printersinternal.AddHandlers(p)
tf.Printer = p
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,

View File

@@ -28,11 +28,13 @@ go_library(
],
tags = ["automanaged"],
deps = [
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/templates:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/util/i18n:go_default_library",
"//vendor:github.com/spf13/cobra",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/errors",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/apiserver/pkg/util/flag",

View File

@@ -52,7 +52,7 @@ func NewCmdConfig(pathOptions *clientcmd.PathOptions, out, errOut io.Writer) *co
// file paths are common to all sub commands
cmd.PersistentFlags().StringVar(&pathOptions.LoadingRules.ExplicitPath, pathOptions.ExplicitFileFlag, pathOptions.LoadingRules.ExplicitPath, "use a particular kubeconfig file")
cmd.AddCommand(NewCmdConfigView(out, pathOptions))
cmd.AddCommand(NewCmdConfigView(out, errOut, pathOptions))
cmd.AddCommand(NewCmdConfigSetCluster(out, pathOptions))
cmd.AddCommand(NewCmdConfigSetAuthInfo(out, pathOptions))
cmd.AddCommand(NewCmdConfigSetContext(out, pathOptions))

View File

@@ -23,13 +23,14 @@ import (
"text/tabwriter"
"github.com/spf13/cobra"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util/i18n"
)
@@ -108,7 +109,7 @@ func (o GetContextsOptions) RunGetContexts() error {
out, found := o.out.(*tabwriter.Writer)
if !found {
out = kubectl.GetNewTabWriter(o.out)
out = printers.GetNewTabWriter(o.out)
defer out.Flush()
}

View File

@@ -23,13 +23,15 @@ import (
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/util/flag"
"k8s.io/client-go/tools/clientcmd"
clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/tools/clientcmd/api/latest"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util/i18n"
)
@@ -55,7 +57,7 @@ var (
kubectl config view -o jsonpath='{.users[?(@.name == "e2e")].user.password}'`)
)
func NewCmdConfigView(out io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra.Command {
func NewCmdConfigView(out, errOut io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra.Command {
options := &ViewOptions{ConfigAccess: ConfigAccess}
// Default to yaml
defaultOutputFormat := "yaml"
@@ -69,17 +71,19 @@ func NewCmdConfigView(out io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra
options.Complete()
outputFormat := cmdutil.GetFlagString(cmd, "output")
if outputFormat == "wide" {
fmt.Printf("--output wide is not available in kubectl config view; reset to default output format (%s)\n\n", defaultOutputFormat)
fmt.Fprintf(errOut, "--output wide is not available in kubectl config view; reset to default output format (%s)\n\n", defaultOutputFormat)
// TODO: once printing is abstracted, this should be handled at flag declaration time
cmd.Flags().Set("output", defaultOutputFormat)
}
if outputFormat == "" {
fmt.Printf("Reset to default output format (%s) as --output is empty\n", defaultOutputFormat)
fmt.Fprintf(errOut, "Reset to default output format (%s) as --output is empty\n", defaultOutputFormat)
// TODO: once printing is abstracted, this should be handled at flag declaration time
cmd.Flags().Set("output", defaultOutputFormat)
}
printer, _, err := cmdutil.PrinterForCommand(cmd)
printer, _, err := cmdutil.PrinterForCommand(cmd, meta.NewDefaultRESTMapper(nil, nil), latest.Scheme, []runtime.Decoder{latest.Codec})
cmdutil.CheckErr(err)
printer = kubectl.NewVersionedPrinter(printer, latest.Scheme, latest.ExternalVersion)
printer = printers.NewVersionedPrinter(printer, latest.Scheme, latest.ExternalVersion)
cmdutil.CheckErr(options.Run(out, printer))
},
@@ -97,7 +101,7 @@ func NewCmdConfigView(out io.Writer, ConfigAccess clientcmd.ConfigAccess) *cobra
return cmd
}
func (o ViewOptions) Run(out io.Writer, printer kubectl.ResourcePrinter) error {
func (o ViewOptions) Run(out io.Writer, printer printers.ResourcePrinter) error {
config, err := o.loadConfig()
if err != nil {
return err

View File

@@ -25,10 +25,10 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util/i18n"
"github.com/spf13/cobra"
@@ -96,7 +96,7 @@ type ConvertOptions struct {
encoder runtime.Encoder
out io.Writer
printer kubectl.ResourcePrinter
printer printers.ResourcePrinter
outputVersion schema.GroupVersion
}
@@ -160,9 +160,11 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.C
} else {
outputFormat = "template"
}
// TODO: once printing is abstracted, this should be handled at flag declaration time
cmd.Flags().Set("output", outputFormat)
}
o.encoder = f.JSONEncoder()
o.printer, _, err = kubectl.GetPrinter(outputFormat, templateFile, false, cmdutil.GetFlagBool(cmd, "allow-missing-template-keys"))
o.printer, _, err = f.PrinterForCommand(cmd)
if err != nil {
return err
}

View File

@@ -32,6 +32,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util/i18n"
)
@@ -117,7 +118,7 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, kubectl.PrintOptions{
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)

View File

@@ -33,6 +33,8 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
"k8s.io/kubernetes/pkg/util/i18n"
)
@@ -72,9 +74,11 @@ var (
func NewCmdDescribe(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command {
options := &resource.FilenameOptions{}
describerSettings := &kubectl.DescriberSettings{}
describerSettings := &printers.DescriberSettings{}
validArgs := kubectl.DescribableResources()
// TODO: this should come from the factory, and may need to be loaded from the server, and so is probably
// going to have to be removed
validArgs := printersinternal.DescribableResources()
argAliases := kubectl.ResourceAliases(validArgs)
cmd := &cobra.Command{
@@ -98,7 +102,7 @@ func NewCmdDescribe(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command {
return cmd
}
func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions, describerSettings *kubectl.DescriberSettings) error {
func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions, describerSettings *printers.DescriberSettings) error {
selector := cmdutil.GetFlagString(cmd, "selector")
allNamespaces := cmdutil.GetFlagBool(cmd, "all-namespaces")
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
@@ -172,7 +176,7 @@ func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, a
return utilerrors.NewAggregate(allErrs)
}
func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f cmdutil.Factory, namespace, rsrc, prefix string, describerSettings *kubectl.DescriberSettings, out io.Writer, originalError error) error {
func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f cmdutil.Factory, namespace, rsrc, prefix string, describerSettings *printers.DescriberSettings, out io.Writer, originalError error) error {
mapper, typer, err := f.UnstructuredObject()
if err != nil {
return err

View File

@@ -43,6 +43,7 @@ import (
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/cmd/util/editor"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util/crlf"
"k8s.io/kubernetes/pkg/util/i18n"
@@ -92,7 +93,7 @@ func NewCmdEdit(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, kubectl.PrintOptions{
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)
@@ -343,14 +344,14 @@ func getPrinter(cmd *cobra.Command) (*editPrinterOptions, error) {
switch format := cmdutil.GetFlagString(cmd, "output"); format {
case "json":
return &editPrinterOptions{
printer: &kubectl.JSONPrinter{},
printer: &printers.JSONPrinter{},
ext: ".json",
addHeader: false,
}, nil
// If flag -o is not specified, use yaml as default
case "yaml", "":
return &editPrinterOptions{
printer: &kubectl.YAMLPrinter{},
printer: &printers.YAMLPrinter{},
ext: ".yaml",
addHeader: true,
}, nil
@@ -589,7 +590,7 @@ func (h *editHeader) flush() {
}
type editPrinterOptions struct {
printer kubectl.ResourcePrinter
printer printers.ResourcePrinter
ext string
addHeader bool
}

View File

@@ -28,8 +28,8 @@ import (
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/printers"
)
func TestRunExposeService(t *testing.T) {
@@ -460,7 +460,7 @@ func TestRunExposeService(t *testing.T) {
for _, test := range tests {
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Printer = &kubectl.JSONPrinter{}
tf.Printer = &printers.JSONPrinter{}
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,

View File

@@ -34,6 +34,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util/i18n"
"k8s.io/kubernetes/pkg/util/interrupt"
)
@@ -95,7 +96,7 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, kubectl.PrintOptions{
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)
@@ -303,7 +304,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
return err
}
printer, generic, err := cmdutil.PrinterForCommand(cmd)
printer, generic, err := f.PrinterForCommand(cmd)
if err != nil {
return err
}
@@ -430,7 +431,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
// use the default printer for each object
printer = nil
var lastMapping *meta.RESTMapping
w := kubectl.GetNewTabWriter(out)
w := printers.GetNewTabWriter(out)
filteredResourceCount := 0
if resource.MultipleTypesRequested(args) || cmdutil.MustPrintWithKinds(objs, infos, sorter) {
@@ -486,7 +487,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
}
}
if resourcePrinter, found := printer.(*kubectl.HumanReadablePrinter); found {
if resourcePrinter, found := printer.(*printers.HumanReadablePrinter); found {
resourceName := resourcePrinter.GetResourceKind()
if mapping != nil {
if resourceName == "" {

View File

@@ -29,6 +29,7 @@ import (
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
@@ -506,7 +507,8 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
pods, svc, _ := testData()
f, tf, codec, _ := cmdtesting.NewAPIFactory()
tf.Printer = &testPrinter{}
tf.CommandPrinter = &testPrinter{}
tf.GenericPrinter = true
tf.UnstructuredClient = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: unstructuredSerializer,
@@ -533,34 +535,37 @@ func TestGetMultipleTypeObjectsAsList(t *testing.T) {
cmd.Flags().Set("output", "json")
cmd.Run(cmd, []string{"pods,services"})
if tf.Printer.(*testPrinter).Objects != nil {
t.Errorf("unexpected print to default printer")
actual := tf.CommandPrinter.(*testPrinter).Objects
fn := func(obj runtime.Object) *unstructured.Unstructured {
data, err := runtime.Encode(api.Codecs.LegacyCodec(schema.GroupVersion{Version: "v1"}), obj)
if err != nil {
panic(err)
}
out := &unstructured.Unstructured{Object: make(map[string]interface{})}
if err := encjson.Unmarshal(data, &out.Object); err != nil {
panic(err)
}
return out
}
out, err := runtime.Decode(codec, buf.Bytes())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
list, err := meta.ExtractList(out)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if errs := runtime.DecodeList(list, codec); len(errs) > 0 {
t.Fatalf("unexpected error: %v", errs)
}
if err := meta.SetList(out, list); err != nil {
t.Fatalf("unexpected error: %v", err)
}
expected := &api.List{
Items: []runtime.Object{
&pods.Items[0],
&pods.Items[1],
&svc.Items[0],
expected := &unstructured.UnstructuredList{
Object: map[string]interface{}{"kind": "List", "apiVersion": "v1", "metadata": map[string]interface{}{}, "selfLink": "", "resourceVersion": ""},
Items: []*unstructured.Unstructured{
fn(&pods.Items[0]),
fn(&pods.Items[1]),
fn(&svc.Items[0]),
},
}
if !reflect.DeepEqual(expected, out) {
t.Errorf("unexpected output: %#v", out)
actualBytes, err := encjson.Marshal(actual[0])
if err != nil {
t.Fatal(err)
}
expectedBytes, err := encjson.Marshal(expected)
if err != nil {
t.Fatal(err)
}
if string(actualBytes) != string(expectedBytes) {
t.Errorf("unexpected object:\n%s\n%s", expectedBytes, actualBytes)
}
}

View File

@@ -39,6 +39,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util/i18n"
)
@@ -99,7 +100,7 @@ func NewCmdLabel(f cmdutil.Factory, out io.Writer) *cobra.Command {
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, kubectl.PrintOptions{
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)

View File

@@ -39,6 +39,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util/i18n"
)
@@ -81,7 +82,7 @@ func NewCmdPatch(f cmdutil.Factory, out io.Writer) *cobra.Command {
// retrieve a list of handled resources from printer as valid args
validArgs, argAliases := []string{}, []string{}
p, err := f.Printer(nil, kubectl.PrintOptions{
p, err := f.Printer(nil, printers.PrintOptions{
ColumnLabels: []string{},
})
cmdutil.CheckErr(err)

View File

@@ -56,6 +56,7 @@ go_test(
"//pkg/kubectl/cmd/testing:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/printers:go_default_library",
"//vendor:github.com/spf13/cobra",
"//vendor:github.com/stretchr/testify/assert",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",

View File

@@ -22,16 +22,17 @@ import (
"strings"
"testing"
"k8s.io/apimachinery/pkg/runtime"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
)
func TestImageLocal(t *testing.T) {
f, tf, _, ns := cmdtesting.NewAPIFactory()
f, tf, codec, ns := cmdtesting.NewAPIFactory()
tf.Client = &fake.RESTClient{
APIRegistry: api.Registry,
NegotiatedSerializer: ns,
@@ -47,7 +48,8 @@ func TestImageLocal(t *testing.T) {
cmd := NewCmdImage(f, buf, buf)
cmd.SetOutput(buf)
cmd.Flags().Set("output", "name")
tf.Printer, _, _ = cmdutil.PrinterForCommand(cmd)
mapper, typer := f.Object()
tf.Printer = &printers.NamePrinter{Decoders: []runtime.Decoder{codec}, Typer: typer, Mapper: mapper}
opts := ImageOptions{FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../examples/storage/cassandra/cassandra-controller.yaml"}},
@@ -63,7 +65,7 @@ func TestImageLocal(t *testing.T) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !strings.Contains(buf.String(), "replicationcontroller/cassandra") {
if !strings.Contains(buf.String(), "replicationcontrollers/cassandra") {
t.Errorf("did not set image: %s", buf.String())
}
}

View File

@@ -20,6 +20,7 @@ go_library(
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/cmd/util:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/printers:go_default_library",
"//vendor:github.com/emicklei/go-restful/swagger",
"//vendor:github.com/spf13/cobra",
"//vendor:github.com/spf13/pflag",

View File

@@ -41,6 +41,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
)
type InternalType struct {
@@ -214,13 +215,15 @@ type TestFactory struct {
Typer runtime.ObjectTyper
Client kubectl.RESTClient
UnstructuredClient kubectl.RESTClient
Describer kubectl.Describer
Printer kubectl.ResourcePrinter
Describer printers.Describer
Printer printers.ResourcePrinter
CommandPrinter printers.ResourcePrinter
Validator validation.Schema
Namespace string
ClientConfig *restclient.Config
Err error
Command string
GenericPrinter bool
ClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error)
UnstructuredClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error)
@@ -331,11 +334,15 @@ func (f *FakeFactory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (r
return f.tf.UnstructuredClient, f.tf.Err
}
func (f *FakeFactory) Describer(*meta.RESTMapping) (kubectl.Describer, error) {
func (f *FakeFactory) Describer(*meta.RESTMapping) (printers.Describer, error) {
return f.tf.Describer, f.tf.Err
}
func (f *FakeFactory) Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) {
func (f *FakeFactory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) {
return f.tf.CommandPrinter, f.tf.GenericPrinter, f.tf.Err
}
func (f *FakeFactory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) {
return f.tf.Printer, f.tf.Err
}
@@ -451,7 +458,7 @@ func (f *FakeFactory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, ob
return nil
}
func (f *FakeFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) {
func (f *FakeFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) {
return f.tf.Printer, f.tf.Err
}
@@ -459,8 +466,8 @@ func (f *FakeFactory) NewBuilder() *resource.Builder {
return nil
}
func (f *FakeFactory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *kubectl.PrintOptions {
return &kubectl.PrintOptions{}
func (f *FakeFactory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *printers.PrintOptions {
return &printers.PrintOptions{}
}
func (f *FakeFactory) DefaultResourceFilterFunc() kubectl.Filters {
@@ -587,11 +594,15 @@ func (f *fakeAPIFactory) UnstructuredClientForMapping(m *meta.RESTMapping) (reso
return f.tf.UnstructuredClient, f.tf.Err
}
func (f *fakeAPIFactory) Describer(*meta.RESTMapping) (kubectl.Describer, error) {
func (f *fakeAPIFactory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) {
return f.tf.CommandPrinter, f.tf.GenericPrinter, f.tf.Err
}
func (f *fakeAPIFactory) Describer(*meta.RESTMapping) (printers.Describer, error) {
return f.tf.Describer, f.tf.Err
}
func (f *fakeAPIFactory) Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) {
func (f *fakeAPIFactory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) {
return f.tf.Printer, f.tf.Err
}
@@ -664,7 +675,7 @@ func (f *fakeAPIFactory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper,
return printer.PrintObj(obj, out)
}
func (f *fakeAPIFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) {
func (f *fakeAPIFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) {
return f.tf.Printer, f.tf.Err
}

View File

@@ -38,6 +38,8 @@ go_library(
"//pkg/controller:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/kubectl/resource:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/printers/internalversion:go_default_library",
"//pkg/util/exec:go_default_library",
"//pkg/version:go_default_library",
"//vendor:github.com/emicklei/go-restful/swagger",

View File

@@ -51,6 +51,7 @@ import (
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
)
const (
@@ -136,7 +137,8 @@ type ClientAccessFactory interface {
// BindExternalFlags adds any flags defined by external projects (not part of pflags)
BindExternalFlags(flags *pflag.FlagSet)
DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *kubectl.PrintOptions
// TODO: Break the dependency on cmd here.
DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *printers.PrintOptions
// DefaultResourceFilterFunc returns a collection of FilterFuncs suitable for filtering specific resource types.
DefaultResourceFilterFunc() kubectl.Filters
@@ -144,7 +146,7 @@ type ClientAccessFactory interface {
SuggestedPodTemplateResources() []schema.GroupResource
// Returns a Printer for formatting objects of the given type or an error.
Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error)
Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error)
// Pauser marks the object in the info as paused. Currently supported only for Deployments.
// Returns the patched object in bytes and any error that occured during the encoding or
// in case the object is already paused.
@@ -193,7 +195,7 @@ type ObjectMappingFactory interface {
// Returns a RESTClient for working with Unstructured objects.
UnstructuredClientForMapping(mapping *meta.RESTMapping) (resource.RESTClient, error)
// Returns a Describer for displaying the specified RESTMapping type or an error.
Describer(mapping *meta.RESTMapping) (kubectl.Describer, error)
Describer(mapping *meta.RESTMapping) (printers.Describer, error)
// LogsForObject returns a request for the logs associated with the provided object
LogsForObject(object, options runtime.Object) (*restclient.Request, error)
@@ -211,10 +213,6 @@ type ObjectMappingFactory interface {
// AttachablePodForObject returns the pod to which to attach given an object.
AttachablePodForObject(object runtime.Object) (*api.Pod, error)
// PrinterForMapping returns a printer suitable for displaying the provided resource type.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error)
// Returns a schema that can validate objects stored on disk.
Validator(validate bool, cacheDir string) (validation.Schema, error)
// SwaggerSchema returns the schema declaration for the provided group version kind.
@@ -224,6 +222,14 @@ type ObjectMappingFactory interface {
// BuilderFactory holds the second level of factory methods. These functions depend upon ObjectMappingFactory and ClientAccessFactory methods.
// Generally they depend upon client mapper functions
type BuilderFactory interface {
// PrinterForCommand returns the default printer for the command. It requires that certain options
// are declared on the command (see AddPrinterFlags). Returns a printer, true if the printer is
// generic (is not internal), or an error if a printer could not be found.
// TODO: Break the dependency on cmd here.
PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error)
// PrinterForMapping returns a printer suitable for displaying the provided resource type.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error)
// PrintObject prints an api object given command line flags to modify the output format
PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
// One stop shopping for a Builder

View File

@@ -19,13 +19,16 @@ limitations under the License.
package util
import (
"fmt"
"io"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
)
type ring2Factory struct {
@@ -42,6 +45,54 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac
return f
}
func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command) (printers.ResourcePrinter, bool, error) {
mapper, typer := f.objectMappingFactory.Object()
// TODO: used by the custom column implementation and the name implementation, break this dependency
decoders := []runtime.Decoder{f.clientAccessFactory.Decoder(true), unstructured.UnstructuredJSONScheme}
return PrinterForCommand(cmd, mapper, typer, decoders)
}
func (f *ring2Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) {
printer, generic, err := f.PrinterForCommand(cmd)
if err != nil {
return nil, err
}
// Make sure we output versioned data for generic printers
if generic {
if mapping == nil {
return nil, fmt.Errorf("no serialization format found")
}
version := mapping.GroupVersionKind.GroupVersion()
if version.Empty() {
return nil, fmt.Errorf("no serialization format found")
}
printer = printers.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion())
} else {
// Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper
columnLabel, err := cmd.Flags().GetStringSlice("label-columns")
if err != nil {
columnLabel = []string{}
}
printer, err = f.clientAccessFactory.Printer(mapping, printers.PrintOptions{
NoHeaders: GetFlagBool(cmd, "no-headers"),
WithNamespace: withNamespace,
Wide: GetWideFlag(cmd),
ShowAll: GetFlagBool(cmd, "show-all"),
ShowLabels: GetFlagBool(cmd, "show-labels"),
AbsoluteTimestamps: isWatch(cmd),
ColumnLabels: columnLabel,
})
if err != nil {
return nil, err
}
printer = maybeWrapSortingPrinter(cmd, printer)
}
return printer, nil
}
func (f *ring2Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error {
// try to get a typed object
_, typer := f.objectMappingFactory.Object()
@@ -66,7 +117,7 @@ func (f *ring2Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, o
return err
}
printer, err := f.objectMappingFactory.PrinterForMapping(cmd, mapping, false)
printer, err := f.PrinterForMapping(cmd, mapping, false)
if err != nil {
return err
}

View File

@@ -49,6 +49,8 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
)
type ring0Factory struct {
@@ -360,12 +362,12 @@ func (f *ring0Factory) BindExternalFlags(flags *pflag.FlagSet) {
flags.AddGoFlagSet(flag.CommandLine)
}
func (f *ring0Factory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *kubectl.PrintOptions {
func (f *ring0Factory) DefaultResourceFilterOptions(cmd *cobra.Command, withNamespace bool) *printers.PrintOptions {
columnLabel, err := cmd.Flags().GetStringSlice("label-columns")
if err != nil {
columnLabel = []string{}
}
opts := &kubectl.PrintOptions{
opts := &printers.PrintOptions{
NoHeaders: GetFlagBool(cmd, "no-headers"),
WithNamespace: withNamespace,
Wide: GetWideFlag(cmd),
@@ -392,8 +394,10 @@ func (f *ring0Factory) SuggestedPodTemplateResources() []schema.GroupResource {
}
}
func (f *ring0Factory) Printer(mapping *meta.RESTMapping, options kubectl.PrintOptions) (kubectl.ResourcePrinter, error) {
return kubectl.NewHumanReadablePrinter(options), nil
func (f *ring0Factory) Printer(mapping *meta.RESTMapping, options printers.PrintOptions) (printers.ResourcePrinter, error) {
p := printers.NewHumanReadablePrinter(options)
printersinternal.AddHandlers(p)
return p, nil
}
func (f *ring0Factory) Pauser(info *resource.Info) ([]byte, error) {

View File

@@ -27,7 +27,6 @@ import (
"time"
"github.com/emicklei/go-restful/swagger"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -48,6 +47,8 @@ import (
"k8s.io/kubernetes/pkg/controller"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
)
type ring1Factory struct {
@@ -58,10 +59,12 @@ func NewObjectMappingFactory(clientAccessFactory ClientAccessFactory) ObjectMapp
f := &ring1Factory{
clientAccessFactory: clientAccessFactory,
}
return f
}
// TODO: This method should return an error now that it can fail. Alternatively, it needs to
// return lazy implementations of mapper and typer that don't hit the wire until they are
// invoked.
func (f *ring1Factory) Object() (meta.RESTMapper, runtime.ObjectTyper) {
mapper := api.Registry.RESTMapper()
discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
@@ -143,7 +146,7 @@ func (f *ring1Factory) UnstructuredClientForMapping(mapping *meta.RESTMapping) (
return restclient.RESTClientFor(cfg)
}
func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer, error) {
func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (printers.Describer, error) {
mappingVersion := mapping.GroupVersionKind.GroupVersion()
if mapping.GroupVersionKind.Group == federation.GroupName {
fedClientSet, err := f.clientAccessFactory.FederationClientSetForVersion(&mappingVersion)
@@ -151,7 +154,7 @@ func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer,
return nil, err
}
if mapping.GroupVersionKind.Kind == "Cluster" {
return &kubectl.ClusterDescriber{Interface: fedClientSet}, nil
return &printersinternal.ClusterDescriber{Interface: fedClientSet}, nil
}
}
@@ -166,7 +169,7 @@ func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer,
}
// try to get a describer
if describer, ok := kubectl.DescriberFor(mapping.GroupVersionKind.GroupKind(), clientset); ok {
if describer, ok := printersinternal.DescriberFor(mapping.GroupVersionKind.GroupKind(), clientset); ok {
return describer, nil
}
// if this is a kind we don't have a describer for yet, go generic if possible
@@ -178,7 +181,7 @@ func (f *ring1Factory) Describer(mapping *meta.RESTMapping) (kubectl.Describer,
}
// helper function to make a generic describer, or return an error
func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RESTMapping) (kubectl.Describer, error) {
func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RESTMapping) (printers.Describer, error) {
clientConfig, err := clientAccessFactory.ClientConfig()
if err != nil {
return nil, err
@@ -202,7 +205,7 @@ func genericDescriber(clientAccessFactory ClientAccessFactory, mapping *meta.RES
}
eventsClient := clientSet.Core()
return kubectl.GenericDescriberFor(mapping, dynamicClient, eventsClient), nil
return printersinternal.GenericDescriberFor(mapping, dynamicClient, eventsClient), nil
}
func (f *ring1Factory) LogsForObject(object, options runtime.Object) (*restclient.Request, error) {
@@ -361,48 +364,6 @@ func (f *ring1Factory) AttachablePodForObject(object runtime.Object) (*api.Pod,
return pod, err
}
func (f *ring1Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (kubectl.ResourcePrinter, error) {
printer, generic, err := PrinterForCommand(cmd)
if err != nil {
return nil, err
}
// Make sure we output versioned data for generic printers
if generic {
if mapping == nil {
return nil, fmt.Errorf("no serialization format found")
}
version := mapping.GroupVersionKind.GroupVersion()
if version.Empty() {
return nil, fmt.Errorf("no serialization format found")
}
printer = kubectl.NewVersionedPrinter(printer, mapping.ObjectConvertor, version, mapping.GroupVersionKind.GroupVersion())
} else {
// Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper
columnLabel, err := cmd.Flags().GetStringSlice("label-columns")
if err != nil {
columnLabel = []string{}
}
printer, err = f.clientAccessFactory.Printer(mapping, kubectl.PrintOptions{
NoHeaders: GetFlagBool(cmd, "no-headers"),
WithNamespace: withNamespace,
Wide: GetWideFlag(cmd),
ShowAll: GetFlagBool(cmd, "show-all"),
ShowLabels: GetFlagBool(cmd, "show-labels"),
AbsoluteTimestamps: isWatch(cmd),
ColumnLabels: columnLabel,
})
if err != nil {
return nil, err
}
printer = maybeWrapSortingPrinter(cmd, printer)
}
return printer, nil
}
func (f *ring1Factory) Validator(validate bool, cacheDir string) (validation.Schema, error) {
if validate {
discovery, err := f.clientAccessFactory.DiscoveryClient()

View File

@@ -47,6 +47,7 @@ import (
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
utilexec "k8s.io/kubernetes/pkg/util/exec"
)
@@ -672,7 +673,7 @@ func MustPrintWithKinds(objs []runtime.Object, infos []*resource.Info, sorter *k
// FilterResourceList receives a list of runtime objects.
// If any objects are filtered, that number is returned along with a modified list.
func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterOpts *kubectl.PrintOptions) (int, []runtime.Object, error) {
func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterOpts *printers.PrintOptions) (int, []runtime.Object, error) {
items, err := meta.ExtractList(obj)
if err != nil {
return 0, []runtime.Object{obj}, utilerrors.NewAggregate([]error{err})
@@ -697,7 +698,7 @@ func FilterResourceList(obj runtime.Object, filterFuncs kubectl.Filters, filterO
return filterCount, list, nil
}
func PrintFilterCount(hiddenObjNum int, resource string, options *kubectl.PrintOptions) {
func PrintFilterCount(hiddenObjNum int, resource string, options *printers.PrintOptions) {
if !options.NoHeaders && !options.ShowAll && hiddenObjNum > 0 {
glog.V(2).Infof(" info: %d completed object(s) was(were) not shown in %s list. Pass --show-all to see all objects.\n\n", hiddenObjNum, resource)
}

View File

@@ -22,8 +22,10 @@ import (
"strings"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
"github.com/spf13/cobra"
)
@@ -105,7 +107,7 @@ func ValidateOutputArgs(cmd *cobra.Command) error {
// PrinterForCommand returns the default printer for this command.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error) {
func PrinterForCommand(cmd *cobra.Command, mapper meta.RESTMapper, typer runtime.ObjectTyper, decoders []runtime.Decoder) (printers.ResourcePrinter, bool, error) {
outputFormat := GetFlagString(cmd, "output")
// templates are logically optional for specifying a format.
@@ -131,7 +133,10 @@ func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error
if cmd.Flags().Lookup("allow-missing-template-keys") != nil {
allowMissingTemplateKeys = GetFlagBool(cmd, "allow-missing-template-keys")
}
printer, generic, err := kubectl.GetPrinter(outputFormat, templateFile, GetFlagBool(cmd, "no-headers"), allowMissingTemplateKeys)
printer, generic, err := printers.GetStandardPrinter(
outputFormat, templateFile, GetFlagBool(cmd, "no-headers"), allowMissingTemplateKeys,
mapper, typer, decoders,
)
if err != nil {
return nil, generic, err
}
@@ -144,7 +149,7 @@ func PrinterForCommand(cmd *cobra.Command) (kubectl.ResourcePrinter, bool, error
// object passed is non-generic, it attempts to print the object using a HumanReadablePrinter.
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Factory, out io.Writer) error {
printer, generic, err := PrinterForCommand(cmd)
printer, generic, err := f.PrinterForCommand(cmd)
if err != nil {
return err
}
@@ -157,7 +162,7 @@ func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Fact
return printer.PrintObj(info.Object, out)
}
func maybeWrapSortingPrinter(cmd *cobra.Command, printer kubectl.ResourcePrinter) kubectl.ResourcePrinter {
func maybeWrapSortingPrinter(cmd *cobra.Command, printer printers.ResourcePrinter) printers.ResourcePrinter {
sorting, err := cmd.Flags().GetString("sort-by")
if err != nil {
// error can happen on missing flag or bad flag type. In either case, this command didn't intent to sort

View File

@@ -20,6 +20,7 @@ import (
"bytes"
"fmt"
"io"
"text/tabwriter"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -30,6 +31,7 @@ import (
"k8s.io/kubernetes/pkg/apis/extensions"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
sliceutil "k8s.io/kubernetes/pkg/util/slice"
)
@@ -55,6 +57,7 @@ type DeploymentHistoryViewer struct {
}
// ViewHistory returns a revision-to-replicaset map as the revision history of a deployment
// TODO: this should be a describer
func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision int64) (string, error) {
versionedClient := versionedClientsetForDeployment(h.c)
deployment, err := versionedClient.Extensions().Deployments(namespace).Get(name, metav1.GetOptions{})
@@ -101,7 +104,7 @@ func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision i
if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(template, internalTemplate, nil); err != nil {
return "", fmt.Errorf("failed to convert podtemplate, %v", err)
}
DescribePodTemplate(internalTemplate, buf)
printersinternal.DescribePodTemplate(internalTemplate, buf)
return buf.String(), nil
}
@@ -126,6 +129,22 @@ func (h *DeploymentHistoryViewer) ViewHistory(namespace, name string, revision i
})
}
// TODO: copied here until this becomes a describer
func tabbedString(f func(io.Writer) error) (string, error) {
out := new(tabwriter.Writer)
buf := &bytes.Buffer{}
out.Init(buf, 0, 8, 1, '\t', 0)
err := f(out)
if err != nil {
return "", err
}
out.Flush()
str := string(buf.String())
return str, nil
}
// getChangeCause returns the change-cause annotation of the input object
func getChangeCause(obj runtime.Object) string {
accessor, err := meta.Accessor(obj)

View File

@@ -24,7 +24,6 @@ import (
"strings"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/api"
)
const (
@@ -35,18 +34,6 @@ type NamespaceInfo struct {
Namespace string
}
func listOfImages(spec *api.PodSpec) []string {
images := make([]string, 0, len(spec.Containers))
for _, container := range spec.Containers {
images = append(images, container.Image)
}
return images
}
func makeImageList(spec *api.PodSpec) string {
return strings.Join(listOfImages(spec), ",")
}
// ResourceShortcuts represents a structure that holds the information how to
// transition from resource's shortcut to its full name.
type ResourceShortcuts struct {

View File

@@ -18,7 +18,7 @@ go_library(
"//pkg/api:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/kubectl:go_default_library",
"//pkg/printers:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/api/resource",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/labels",

View File

@@ -23,7 +23,7 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
metricsapi "k8s.io/heapster/metrics/apis/metrics/v1alpha1"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/printers"
)
var (
@@ -55,7 +55,7 @@ func (printer *TopCmdPrinter) PrintNodeMetrics(metrics []metricsapi.NodeMetrics,
if len(metrics) == 0 {
return nil
}
w := kubectl.GetNewTabWriter(printer.out)
w := printers.GetNewTabWriter(printer.out)
defer w.Flush()
printColumnNames(w, NodeColumns)
@@ -78,7 +78,7 @@ func (printer *TopCmdPrinter) PrintPodMetrics(metrics []metricsapi.PodMetrics, p
if len(metrics) == 0 {
return nil
}
w := kubectl.GetNewTabWriter(printer.out)
w := printers.GetNewTabWriter(printer.out)
defer w.Flush()
if withNamespace {

View File

@@ -20,12 +20,13 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/printers"
)
// FilterFunc is a function that knows how to filter a specific resource kind.
// It receives a generic runtime.Object which must be type-checked by the function.
// Returns a boolean value true if a resource is filtered, or false otherwise.
type FilterFunc func(runtime.Object, PrintOptions) bool
type FilterFunc func(runtime.Object, printers.PrintOptions) bool
// Filters is a collection of filter funcs
type Filters []FilterFunc
@@ -38,7 +39,7 @@ func NewResourceFilter() Filters {
// filterPods returns true if a pod should be skipped.
// defaults to true for terminated pods
func filterPods(obj runtime.Object, options PrintOptions) bool {
func filterPods(obj runtime.Object, options printers.PrintOptions) bool {
switch p := obj.(type) {
case *v1.Pod:
reason := string(p.Status.Phase)
@@ -57,7 +58,7 @@ func filterPods(obj runtime.Object, options PrintOptions) bool {
}
// Filter loops through a collection of FilterFuncs until it finds one that can filter the given resource
func (f Filters) Filter(obj runtime.Object, opts *PrintOptions) (bool, error) {
func (f Filters) Filter(obj runtime.Object, opts *printers.PrintOptions) (bool, error) {
// check if the object is unstructured. If so, let's attempt to convert it to a type we can understand
// before apply filter func.
obj, _ = DecodeUnknownObject(obj)

View File

@@ -33,6 +33,7 @@ import (
externalextensions "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
sliceutil "k8s.io/kubernetes/pkg/util/slice"
)
@@ -170,7 +171,7 @@ func simpleDryRun(deployment *extensions.Deployment, c clientset.Interface, toRe
if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(template, internalTemplate, nil); err != nil {
return "", fmt.Errorf("failed to convert podtemplate, %v", err)
}
DescribePodTemplate(internalTemplate, buf)
printersinternal.DescribePodTemplate(internalTemplate, buf)
return buf.String(), nil
}
@@ -188,6 +189,6 @@ func simpleDryRun(deployment *extensions.Deployment, c clientset.Interface, toRe
if err := v1.Convert_v1_PodTemplateSpec_To_api_PodTemplateSpec(template, internalTemplate, nil); err != nil {
return "", fmt.Errorf("failed to convert podtemplate, %v", err)
}
DescribePodTemplate(internalTemplate, buf)
printersinternal.DescribePodTemplate(internalTemplate, buf)
return buf.String(), nil
}

View File

@@ -1,86 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package kubectl
import (
"sort"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubelet/qos"
)
type SortableResourceNames []api.ResourceName
func (list SortableResourceNames) Len() int {
return len(list)
}
func (list SortableResourceNames) Swap(i, j int) {
list[i], list[j] = list[j], list[i]
}
func (list SortableResourceNames) Less(i, j int) bool {
return list[i] < list[j]
}
// SortedResourceNames returns the sorted resource names of a resource list.
func SortedResourceNames(list api.ResourceList) []api.ResourceName {
resources := make([]api.ResourceName, 0, len(list))
for res := range list {
resources = append(resources, res)
}
sort.Sort(SortableResourceNames(resources))
return resources
}
type SortableResourceQuotas []api.ResourceQuota
func (list SortableResourceQuotas) Len() int {
return len(list)
}
func (list SortableResourceQuotas) Swap(i, j int) {
list[i], list[j] = list[j], list[i]
}
func (list SortableResourceQuotas) Less(i, j int) bool {
return list[i].Name < list[j].Name
}
type SortableVolumeMounts []api.VolumeMount
func (list SortableVolumeMounts) Len() int {
return len(list)
}
func (list SortableVolumeMounts) Swap(i, j int) {
list[i], list[j] = list[j], list[i]
}
func (list SortableVolumeMounts) Less(i, j int) bool {
return list[i].MountPath < list[j].MountPath
}
// SortedQoSResourceNames returns the sorted resource names of a QoS list.
func SortedQoSResourceNames(list qos.QOSList) []api.ResourceName {
resources := make([]api.ResourceName, 0, len(list))
for res := range list {
resources = append(resources, api.ResourceName(res))
}
sort.Sort(SortableResourceNames(resources))
return resources
}

View File

@@ -31,13 +31,14 @@ import (
"k8s.io/client-go/util/integer"
"k8s.io/client-go/util/jsonpath"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/printers"
)
// Sorting printer sorts list types before delegating to another printer.
// Non-list types are simply passed through
type SortingPrinter struct {
SortField string
Delegate ResourcePrinter
Delegate printers.ResourcePrinter
Decoder runtime.Decoder
}
@@ -101,7 +102,7 @@ func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput stri
}
}
field, err := massageJSONPath(fieldInput)
field, err := printers.RelaxedJSONPathExpression(fieldInput)
if err != nil {
return nil, err
}

View File

@@ -0,0 +1,10 @@
{
"Rules": [
{
"SelectorRegexp": "k8s[.]io/kubernetes/pkg/(api$|apis/)",
"ForbiddenPrefixes": [
"k8s.io/kubernetes/pkg/printers"
]
}
]
}

66
pkg/printers/BUILD Normal file
View File

@@ -0,0 +1,66 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_library(
name = "go_default_library",
srcs = [
"common.go",
"customcolumn.go",
"humanreadable.go",
"interface.go",
"json.go",
"jsonpath.go",
"name.go",
"printers.go",
"tabwriter.go",
"template.go",
"versioned.go",
],
tags = ["automanaged"],
deps = [
"//vendor:github.com/ghodss/yaml",
"//vendor:github.com/golang/glog",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/util/errors",
"//vendor:k8s.io/client-go/util/jsonpath",
],
)
go_test(
name = "go_default_xtest",
srcs = ["customcolumn_test.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/printers:go_default_library",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//pkg/printers/internalversion:all-srcs",
],
tags = ["automanaged"],
)

41
pkg/printers/common.go Normal file
View File

@@ -0,0 +1,41 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
import (
"fmt"
"time"
)
func ShortHumanDuration(d time.Duration) string {
// Allow deviation no more than 2 seconds(excluded) to tolerate machine time
// inconsistence, it can be considered as almost now.
if seconds := int(d.Seconds()); seconds < -1 {
return fmt.Sprintf("<invalid>")
} else if seconds < 0 {
return fmt.Sprintf("0s")
} else if seconds < 60 {
return fmt.Sprintf("%ds", seconds)
} else if minutes := int(d.Minutes()); minutes < 60 {
return fmt.Sprintf("%dm", minutes)
} else if hours := int(d.Hours()); hours < 24 {
return fmt.Sprintf("%dh", hours)
} else if hours < 24*364 {
return fmt.Sprintf("%dd", hours/24)
}
return fmt.Sprintf("%dy", int(d.Hours()/24/365))
}

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package kubectl
package printers
import (
"bufio"
@@ -41,14 +41,14 @@ const (
var jsonRegexp = regexp.MustCompile("^\\{\\.?([^{}]+)\\}$|^\\.?([^{}]+)$")
// MassageJSONPath attempts to be flexible with JSONPath expressions, it accepts:
// RelaxedJSONPathExpression attempts to be flexible with JSONPath expressions, it accepts:
// * metadata.name (no leading '.' or curly brances '{...}'
// * {metadata.name} (no leading '.')
// * .metadata.name (no curly braces '{...}')
// * {.metadata.name} (complete expression)
// And transforms them all into a valid jsonpat expression:
// And transforms them all into a valid jsonpath expression:
// {.metadata.name}
func massageJSONPath(pathExpression string) (string, error) {
func RelaxedJSONPathExpression(pathExpression string) (string, error) {
if len(pathExpression) == 0 {
return pathExpression, nil
}
@@ -84,7 +84,7 @@ func NewCustomColumnsPrinterFromSpec(spec string, decoder runtime.Decoder, noHea
if len(colSpec) != 2 {
return nil, fmt.Errorf("unexpected custom-columns spec: %s, expected <header>:<json-path-expr>", parts[ix])
}
spec, err := massageJSONPath(colSpec[1])
spec, err := RelaxedJSONPathExpression(colSpec[1])
if err != nil {
return nil, err
}
@@ -126,7 +126,7 @@ func NewCustomColumnsPrinterFromTemplate(templateReader io.Reader, decoder runti
columns := make([]Column, len(headers))
for ix := range headers {
spec, err := massageJSONPath(specs[ix])
spec, err := RelaxedJSONPathExpression(specs[ix])
if err != nil {
return nil, err
}

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package kubectl
package printers_test
import (
"bytes"
@@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/printers"
)
func TestMassageJSONPath(t *testing.T) {
@@ -45,7 +46,7 @@ func TestMassageJSONPath(t *testing.T) {
{input: "{{foo.bar}", expectErr: true},
}
for _, test := range tests {
output, err := massageJSONPath(test.input)
output, err := printers.RelaxedJSONPathExpression(test.input)
if err != nil && !test.expectErr {
t.Errorf("unexpected error: %v", err)
continue
@@ -65,7 +66,7 @@ func TestMassageJSONPath(t *testing.T) {
func TestNewColumnPrinterFromSpec(t *testing.T) {
tests := []struct {
spec string
expectedColumns []Column
expectedColumns []printers.Column
expectErr bool
name string
noHeaders bool
@@ -93,7 +94,7 @@ func TestNewColumnPrinterFromSpec(t *testing.T) {
{
spec: "NAME:metadata.name,API_VERSION:apiVersion",
name: "ok",
expectedColumns: []Column{
expectedColumns: []printers.Column{
{
Header: "NAME",
FieldSpec: "{.metadata.name}",
@@ -111,7 +112,7 @@ func TestNewColumnPrinterFromSpec(t *testing.T) {
},
}
for _, test := range tests {
printer, err := NewCustomColumnsPrinterFromSpec(test.spec, api.Codecs.UniversalDecoder(), test.noHeaders)
printer, err := printers.NewCustomColumnsPrinterFromSpec(test.spec, api.Codecs.UniversalDecoder(), test.noHeaders)
if test.expectErr {
if err == nil {
t.Errorf("[%s] unexpected non-error", test.name)
@@ -141,6 +142,15 @@ func TestNewColumnPrinterFromSpec(t *testing.T) {
}
}
func contains(arr []string, s string) bool {
for i := range arr {
if arr[i] == s {
return true
}
}
return false
}
const exampleTemplateOne = `NAME API_VERSION
{metadata.name} {apiVersion}`
@@ -150,7 +160,7 @@ const exampleTemplateTwo = `NAME API_VERSION
func TestNewColumnPrinterFromTemplate(t *testing.T) {
tests := []struct {
spec string
expectedColumns []Column
expectedColumns []printers.Column
expectErr bool
name string
}{
@@ -177,7 +187,7 @@ func TestNewColumnPrinterFromTemplate(t *testing.T) {
{
spec: exampleTemplateOne,
name: "ok",
expectedColumns: []Column{
expectedColumns: []printers.Column{
{
Header: "NAME",
FieldSpec: "{.metadata.name}",
@@ -191,7 +201,7 @@ func TestNewColumnPrinterFromTemplate(t *testing.T) {
{
spec: exampleTemplateTwo,
name: "ok-2",
expectedColumns: []Column{
expectedColumns: []printers.Column{
{
Header: "NAME",
FieldSpec: "{.metadata.name}",
@@ -205,7 +215,7 @@ func TestNewColumnPrinterFromTemplate(t *testing.T) {
}
for _, test := range tests {
reader := bytes.NewBufferString(test.spec)
printer, err := NewCustomColumnsPrinterFromTemplate(reader, api.Codecs.UniversalDecoder())
printer, err := printers.NewCustomColumnsPrinterFromTemplate(reader, api.Codecs.UniversalDecoder())
if test.expectErr {
if err == nil {
t.Errorf("[%s] unexpected non-error", test.name)
@@ -226,12 +236,12 @@ func TestNewColumnPrinterFromTemplate(t *testing.T) {
func TestColumnPrint(t *testing.T) {
tests := []struct {
columns []Column
columns []printers.Column
obj runtime.Object
expectedOutput string
}{
{
columns: []Column{
columns: []printers.Column{
{
Header: "NAME",
FieldSpec: "{.metadata.name}",
@@ -243,7 +253,7 @@ foo
`,
},
{
columns: []Column{
columns: []printers.Column{
{
Header: "NAME",
FieldSpec: "{.metadata.name}",
@@ -261,7 +271,7 @@ bar
`,
},
{
columns: []Column{
columns: []printers.Column{
{
Header: "NAME",
FieldSpec: "{.metadata.name}",
@@ -279,7 +289,7 @@ foo baz
}
for _, test := range tests {
printer := &CustomColumnsPrinter{
printer := &printers.CustomColumnsPrinter{
Columns: test.columns,
Decoder: api.Codecs.UniversalDecoder(),
}

View File

@@ -0,0 +1,329 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
import (
"bytes"
"fmt"
"io"
"reflect"
"strings"
"text/tabwriter"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
var withNamespacePrefixColumns = []string{"NAMESPACE"} // TODO(erictune): print cluster name too.
type handlerEntry struct {
columns []string
columnsWithWide []string
printFunc reflect.Value
args []reflect.Value
}
// HumanReadablePrinter is an implementation of ResourcePrinter which attempts to provide
// more elegant output. It is not threadsafe, but you may call PrintObj repeatedly; headers
// will only be printed if the object type changes. This makes it useful for printing items
// received from watches.
type HumanReadablePrinter struct {
handlerMap map[reflect.Type]*handlerEntry
options PrintOptions
lastType reflect.Type
hiddenObjNum int
}
// NewHumanReadablePrinter creates a HumanReadablePrinter.
func NewHumanReadablePrinter(options PrintOptions) *HumanReadablePrinter {
printer := &HumanReadablePrinter{
handlerMap: make(map[reflect.Type]*handlerEntry),
options: options,
}
return printer
}
// GetResourceKind returns the type currently set for a resource
func (h *HumanReadablePrinter) GetResourceKind() string {
return h.options.Kind
}
// EnsurePrintWithKind sets HumanReadablePrinter options "WithKind" to true
// and "Kind" to the string arg it receives, pre-pending this string
// to the "NAME" column in an output of resources.
func (h *HumanReadablePrinter) EnsurePrintWithKind(kind string) {
h.options.WithKind = true
h.options.Kind = kind
}
// EnsurePrintHeaders sets the HumanReadablePrinter option "NoHeaders" to false
// and removes the .lastType that was printed, which forces headers to be
// printed in cases where multiple lists of the same resource are printed
// consecutively, but are separated by non-printer related information.
func (h *HumanReadablePrinter) EnsurePrintHeaders() {
h.options.NoHeaders = false
h.lastType = nil
}
// Handler adds a print handler with a given set of columns to HumanReadablePrinter instance.
// See validatePrintHandlerFunc for required method signature.
func (h *HumanReadablePrinter) Handler(columns, columnsWithWide []string, printFunc interface{}) error {
printFuncValue := reflect.ValueOf(printFunc)
if err := h.validatePrintHandlerFunc(printFuncValue); err != nil {
glog.Errorf("Unable to add print handler: %v", err)
return err
}
objType := printFuncValue.Type().In(0)
h.handlerMap[objType] = &handlerEntry{
columns: columns,
columnsWithWide: columnsWithWide,
printFunc: printFuncValue,
}
return nil
}
// validatePrintHandlerFunc validates print handler signature.
// printFunc is the function that will be called to print an object.
// It must be of the following type:
// func printFunc(object ObjectType, w io.Writer, options PrintOptions) error
// where ObjectType is the type of the object that will be printed.
func (h *HumanReadablePrinter) validatePrintHandlerFunc(printFunc reflect.Value) error {
if printFunc.Kind() != reflect.Func {
return fmt.Errorf("invalid print handler. %#v is not a function", printFunc)
}
funcType := printFunc.Type()
if funcType.NumIn() != 3 || funcType.NumOut() != 1 {
return fmt.Errorf("invalid print handler." +
"Must accept 3 parameters and return 1 value.")
}
if funcType.In(1) != reflect.TypeOf((*io.Writer)(nil)).Elem() ||
funcType.In(2) != reflect.TypeOf((*PrintOptions)(nil)).Elem() ||
funcType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {
return fmt.Errorf("invalid print handler. The expected signature is: "+
"func handler(obj %v, w io.Writer, options PrintOptions) error", funcType.In(0))
}
return nil
}
func (h *HumanReadablePrinter) HandledResources() []string {
keys := make([]string, 0)
for k := range h.handlerMap {
// k.String looks like "*api.PodList" and we want just "pod"
api := strings.Split(k.String(), ".")
resource := api[len(api)-1]
if strings.HasSuffix(resource, "List") {
continue
}
resource = strings.ToLower(resource)
keys = append(keys, resource)
}
return keys
}
func (h *HumanReadablePrinter) AfterPrint(output io.Writer, res string) error {
return nil
}
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
_, err := fmt.Fprintf(w, "Unknown object: %s", string(data))
return err
}
func (h *HumanReadablePrinter) printHeader(columnNames []string, w io.Writer) error {
if _, err := fmt.Fprintf(w, "%s\n", strings.Join(columnNames, "\t")); err != nil {
return err
}
return nil
}
// PrintObj prints the obj in a human-friendly format according to the type of the obj.
func (h *HumanReadablePrinter) PrintObj(obj runtime.Object, output io.Writer) error {
// if output is a tabwriter (when it's called by kubectl get), we use it; create a new tabwriter otherwise
w, found := output.(*tabwriter.Writer)
if !found {
w = GetNewTabWriter(output)
defer w.Flush()
}
// check if the object is unstructured. If so, let's attempt to convert it to a type we can understand before
// trying to print, since the printers are keyed by type. This is extremely expensive.
//obj, _ = DecodeUnknownObject(obj)
t := reflect.TypeOf(obj)
if handler := h.handlerMap[t]; handler != nil {
if !h.options.NoHeaders && t != h.lastType {
headers := handler.columns
if h.options.Wide {
headers = append(headers, handler.columnsWithWide...)
}
headers = append(headers, formatLabelHeaders(h.options.ColumnLabels)...)
// LABELS is always the last column.
headers = append(headers, formatShowLabelsHeader(h.options.ShowLabels, t)...)
if h.options.WithNamespace {
headers = append(withNamespacePrefixColumns, headers...)
}
h.printHeader(headers, w)
h.lastType = t
}
args := []reflect.Value{reflect.ValueOf(obj), reflect.ValueOf(w), reflect.ValueOf(h.options)}
resultValue := handler.printFunc.Call(args)[0]
if resultValue.IsNil() {
return nil
}
return resultValue.Interface().(error)
}
if _, err := meta.Accessor(obj); err == nil {
if !h.options.NoHeaders && t != h.lastType {
headers := []string{"NAME", "KIND"}
headers = append(headers, formatLabelHeaders(h.options.ColumnLabels)...)
// LABELS is always the last column.
headers = append(headers, formatShowLabelsHeader(h.options.ShowLabels, t)...)
if h.options.WithNamespace {
headers = append(withNamespacePrefixColumns, headers...)
}
h.printHeader(headers, w)
h.lastType = t
}
// we don't recognize this type, but we can still attempt to print some reasonable information about.
unstructured, ok := obj.(runtime.Unstructured)
if !ok {
return fmt.Errorf("error: unknown type %#v", obj)
}
// if the error isn't nil, report the "I don't recognize this" error
if err := printUnstructured(unstructured, w, h.options); err != nil {
return err
}
return nil
}
// we failed all reasonable printing efforts, report failure
return fmt.Errorf("error: unknown type %#v", obj)
}
// TODO: this method assumes the meta/v1 server API, so should be refactored out of this package
func printUnstructured(unstructured runtime.Unstructured, w io.Writer, options PrintOptions) error {
metadata, err := meta.Accessor(unstructured)
if err != nil {
return err
}
if options.WithNamespace {
if _, err := fmt.Fprintf(w, "%s\t", metadata.GetNamespace()); err != nil {
return err
}
}
content := unstructured.UnstructuredContent()
kind := "<missing>"
if objKind, ok := content["kind"]; ok {
if str, ok := objKind.(string); ok {
kind = str
}
}
if objAPIVersion, ok := content["apiVersion"]; ok {
if str, ok := objAPIVersion.(string); ok {
version, err := schema.ParseGroupVersion(str)
if err != nil {
return err
}
kind = kind + "." + version.Version + "." + version.Group
}
}
name := formatResourceName(options.Kind, metadata.GetName(), options.WithKind)
if _, err := fmt.Fprintf(w, "%s\t%s", name, kind); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendLabels(metadata.GetLabels(), options.ColumnLabels)); err != nil {
return err
}
if _, err := fmt.Fprint(w, appendAllLabels(options.ShowLabels, metadata.GetLabels())); err != nil {
return err
}
return nil
}
func formatLabelHeaders(columnLabels []string) []string {
formHead := make([]string, len(columnLabels))
for i, l := range columnLabels {
p := strings.Split(l, "/")
formHead[i] = strings.ToUpper((p[len(p)-1]))
}
return formHead
}
// headers for --show-labels=true
func formatShowLabelsHeader(showLabels bool, t reflect.Type) []string {
if showLabels {
// TODO: this is all sorts of hack, fix
if t.String() != "*api.ThirdPartyResource" && t.String() != "*api.ThirdPartyResourceList" {
return []string{"LABELS"}
}
}
return nil
}
// formatResourceName receives a resource kind, name, and boolean specifying
// whether or not to update the current name to "kind/name"
// TODO: dedup this with printers/internalversions
func formatResourceName(kind, name string, withKind bool) string {
if !withKind || kind == "" {
return name
}
return kind + "/" + name
}
// TODO: dedup this with printers/internalversions
func appendLabels(itemLabels map[string]string, columnLabels []string) string {
var buffer bytes.Buffer
for _, cl := range columnLabels {
buffer.WriteString(fmt.Sprint("\t"))
if il, ok := itemLabels[cl]; ok {
buffer.WriteString(fmt.Sprint(il))
} else {
buffer.WriteString("<none>")
}
}
return buffer.String()
}
// Append all labels to a single column. We need this even when show-labels flag* is
// false, since this adds newline delimiter to the end of each row.
// TODO: dedup this with printers/internalversions
func appendAllLabels(showLabels bool, itemLabels map[string]string) string {
var buffer bytes.Buffer
if showLabels {
buffer.WriteString(fmt.Sprint("\t"))
buffer.WriteString(labels.FormatLabels(itemLabels))
}
buffer.WriteString("\n")
return buffer.String()
}

96
pkg/printers/interface.go Normal file
View File

@@ -0,0 +1,96 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
import (
"fmt"
"io"
"k8s.io/apimachinery/pkg/runtime"
)
// ResourcePrinter is an interface that knows how to print runtime objects.
type ResourcePrinter interface {
// Print receives a runtime object, formats it and prints it to a writer.
PrintObj(runtime.Object, io.Writer) error
HandledResources() []string
//Can be used to print out warning/clarifications if needed
//after all objects were printed
AfterPrint(io.Writer, string) error
}
// ResourcePrinterFunc is a function that can print objects
type ResourcePrinterFunc func(runtime.Object, io.Writer) error
// PrintObj implements ResourcePrinter
func (fn ResourcePrinterFunc) PrintObj(obj runtime.Object, w io.Writer) error {
return fn(obj, w)
}
// TODO: implement HandledResources()
func (fn ResourcePrinterFunc) HandledResources() []string {
return []string{}
}
func (fn ResourcePrinterFunc) AfterPrint(io.Writer, string) error {
return nil
}
type PrintOptions struct {
NoHeaders bool
WithNamespace bool
WithKind bool
Wide bool
ShowAll bool
ShowLabels bool
AbsoluteTimestamps bool
Kind string
ColumnLabels []string
}
// Describer generates output for the named resource or an error
// if the output could not be generated. Implementers typically
// abstract the retrieval of the named object from a remote server.
type Describer interface {
Describe(namespace, name string, describerSettings DescriberSettings) (output string, err error)
}
// DescriberSettings holds display configuration for each object
// describer to control what is printed.
type DescriberSettings struct {
ShowEvents bool
}
// ObjectDescriber is an interface for displaying arbitrary objects with extra
// information. Use when an object is in hand (on disk, or already retrieved).
// Implementers may ignore the additional information passed on extra, or use it
// by default. ObjectDescribers may return ErrNoDescriber if no suitable describer
// is found.
type ObjectDescriber interface {
DescribeObject(object interface{}, extra ...interface{}) (output string, err error)
}
// ErrNoDescriber is a structured error indicating the provided object or objects
// cannot be described.
type ErrNoDescriber struct {
Types []string
}
// Error implements the error interface.
func (e ErrNoDescriber) Error() string {
return fmt.Sprintf("no describer has been defined for %v", e.Types)
}

View File

@@ -0,0 +1,4 @@
{
"Rules": [
]
}

View File

@@ -0,0 +1,112 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
srcs = [
"describe_test.go",
"printers_test.go",
"sorted_resource_name_list_test.go",
],
library = ":go_default_library",
tags = ["automanaged"],
deps = [
"//federation/apis/federation:go_default_library",
"//federation/client/clientset_generated/federation_internalclientset/fake:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/apis/storage:go_default_library",
"//pkg/client/clientset_generated/clientset/fake:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/fake:go_default_library",
"//pkg/kubectl/testing:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/util:go_default_library",
"//vendor:github.com/ghodss/yaml",
"//vendor:k8s.io/apimachinery/pkg/api/equality",
"//vendor:k8s.io/apimachinery/pkg/api/resource",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/runtime/serializer/yaml",
"//vendor:k8s.io/apimachinery/pkg/util/diff",
"//vendor:k8s.io/apimachinery/pkg/util/intstr",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
],
)
go_library(
name = "go_default_library",
srcs = [
"describe.go",
"printers.go",
],
tags = ["automanaged"],
deps = [
"//federation/apis/federation:go_default_library",
"//federation/client/clientset_generated/federation_internalclientset:go_default_library",
"//pkg/api:go_default_library",
"//pkg/api/annotations:go_default_library",
"//pkg/api/events:go_default_library",
"//pkg/apis/apps:go_default_library",
"//pkg/apis/autoscaling:go_default_library",
"//pkg/apis/batch:go_default_library",
"//pkg/apis/certificates:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/apis/policy:go_default_library",
"//pkg/apis/rbac:go_default_library",
"//pkg/apis/storage:go_default_library",
"//pkg/apis/storage/util:go_default_library",
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/client/clientset_generated/clientset/typed/core/v1:go_default_library",
"//pkg/client/clientset_generated/clientset/typed/extensions/v1beta1:go_default_library",
"//pkg/client/clientset_generated/internalclientset:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/core/internalversion:go_default_library",
"//pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion:go_default_library",
"//pkg/controller/deployment/util:go_default_library",
"//pkg/fieldpath:go_default_library",
"//pkg/kubelet/qos:go_default_library",
"//pkg/printers:go_default_library",
"//pkg/util/node:go_default_library",
"//vendor:github.com/golang/glog",
"//vendor:k8s.io/apimachinery/pkg/api/errors",
"//vendor:k8s.io/apimachinery/pkg/api/meta",
"//vendor:k8s.io/apimachinery/pkg/api/resource",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/fields",
"//vendor:k8s.io/apimachinery/pkg/labels",
"//vendor:k8s.io/apimachinery/pkg/runtime/schema",
"//vendor:k8s.io/apimachinery/pkg/types",
"//vendor:k8s.io/apimachinery/pkg/util/intstr",
"//vendor:k8s.io/apimachinery/pkg/util/sets",
"//vendor:k8s.io/client-go/dynamic",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package kubectl
package internalversion
import (
"bytes"
@@ -26,8 +26,11 @@ import (
"reflect"
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/api/resource"
@@ -42,7 +45,6 @@ import (
"k8s.io/kubernetes/federation/apis/federation"
fedclientset "k8s.io/kubernetes/federation/client/clientset_generated/federation_internalclientset"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/annotations"
"k8s.io/kubernetes/pkg/api/events"
"k8s.io/kubernetes/pkg/apis/apps"
@@ -55,48 +57,17 @@ import (
"k8s.io/kubernetes/pkg/apis/storage"
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
versionedclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
coreclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1"
extensionsclientset "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1"
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
coreclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion"
extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion"
deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util"
"k8s.io/kubernetes/pkg/fieldpath"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/kubelet/qos"
"k8s.io/kubernetes/pkg/printers"
)
// Describer generates output for the named resource or an error
// if the output could not be generated. Implementers typically
// abstract the retrieval of the named object from a remote server.
type Describer interface {
Describe(namespace, name string, describerSettings DescriberSettings) (output string, err error)
}
// DescriberSettings holds display configuration for each object
// describer to control what is printed.
type DescriberSettings struct {
ShowEvents bool
}
// ObjectDescriber is an interface for displaying arbitrary objects with extra
// information. Use when an object is in hand (on disk, or already retrieved).
// Implementers may ignore the additional information passed on extra, or use it
// by default. ObjectDescribers may return ErrNoDescriber if no suitable describer
// is found.
type ObjectDescriber interface {
DescribeObject(object interface{}, extra ...interface{}) (output string, err error)
}
// ErrNoDescriber is a structured error indicating the provided object or objects
// cannot be described.
type ErrNoDescriber struct {
Types []string
}
// Error implements the error interface.
func (e ErrNoDescriber) Error() string {
return fmt.Sprintf("no describer has been defined for %v", e.Types)
}
// Each level has 2 spaces for PrefixWriter
const (
LEVEL_0 = iota
@@ -122,8 +93,8 @@ func (pw *PrefixWriter) WriteLine(a ...interface{}) {
fmt.Fprintln(pw.out, a...)
}
func describerMap(c clientset.Interface) map[schema.GroupKind]Describer {
m := map[schema.GroupKind]Describer{
func describerMap(c clientset.Interface) map[schema.GroupKind]printers.Describer {
m := map[schema.GroupKind]printers.Describer{
api.Kind("Pod"): &PodDescriber{c},
api.Kind("ReplicationController"): &ReplicationControllerDescriber{c},
api.Kind("Secret"): &SecretDescriber{c},
@@ -155,7 +126,7 @@ func describerMap(c clientset.Interface) map[schema.GroupKind]Describer {
return m
}
// List of all resource types we can describe
// DescribableResources lists all resource types we can describe.
func DescribableResources() []string {
keys := make([]string, 0)
@@ -166,16 +137,16 @@ func DescribableResources() []string {
return keys
}
// Describer returns the default describe functions for each of the standard
// DescriberFor returns the default describe functions for each of the standard
// Kubernetes types.
func DescriberFor(kind schema.GroupKind, c clientset.Interface) (Describer, bool) {
func DescriberFor(kind schema.GroupKind, c clientset.Interface) (printers.Describer, bool) {
f, ok := describerMap(c)[kind]
return f, ok
}
// GenericDescriberFor returns a generic describer for the specified mapping
// that uses only information available from runtime.Unstructured
func GenericDescriberFor(mapping *meta.RESTMapping, dynamic *dynamic.Client, events coreclient.EventsGetter) Describer {
func GenericDescriberFor(mapping *meta.RESTMapping, dynamic *dynamic.Client, events coreclient.EventsGetter) printers.Describer {
return &genericDescriber{mapping, dynamic, events}
}
@@ -185,7 +156,7 @@ type genericDescriber struct {
events coreclient.EventsGetter
}
func (g *genericDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (output string, err error) {
func (g *genericDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (output string, err error) {
apiResource := &metav1.APIResource{
Name: g.mapping.Resource,
Namespaced: g.mapping.Scope.Name() == meta.RESTScopeNameNamespace,
@@ -214,7 +185,7 @@ func (g *genericDescriber) Describe(namespace, name string, describerSettings De
}
// DefaultObjectDescriber can describe the default Kubernetes objects.
var DefaultObjectDescriber ObjectDescriber
var DefaultObjectDescriber printers.ObjectDescriber
func init() {
d := &Describers{}
@@ -239,7 +210,7 @@ type NamespaceDescriber struct {
clientset.Interface
}
func (d *NamespaceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *NamespaceDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
ns, err := d.Core().Namespaces().Get(name, metav1.GetOptions{})
if err != nil {
return "", err
@@ -411,7 +382,7 @@ type LimitRangeDescriber struct {
clientset.Interface
}
func (d *LimitRangeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *LimitRangeDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
lr := d.Core().LimitRanges(namespace)
limitRange, err := lr.Get(name, metav1.GetOptions{})
@@ -438,7 +409,7 @@ type ResourceQuotaDescriber struct {
clientset.Interface
}
func (d *ResourceQuotaDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *ResourceQuotaDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
rq := d.Core().ResourceQuotas(namespace)
resourceQuota, err := rq.Get(name, metav1.GetOptions{})
@@ -508,7 +479,7 @@ type PodDescriber struct {
clientset.Interface
}
func (d *PodDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *PodDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
pod, err := d.Core().Pods(namespace).Get(name, metav1.GetOptions{})
if err != nil {
if describerSettings.ShowEvents {
@@ -810,7 +781,7 @@ type PersistentVolumeDescriber struct {
clientset.Interface
}
func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
c := d.Core().PersistentVolumes()
pv, err := c.Get(name, metav1.GetOptions{})
@@ -881,7 +852,7 @@ type PersistentVolumeClaimDescriber struct {
clientset.Interface
}
func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
c := d.Core().PersistentVolumeClaims(namespace)
pvc, err := c.Get(name, metav1.GetOptions{})
@@ -1214,7 +1185,7 @@ type ReplicationControllerDescriber struct {
clientset.Interface
}
func (d *ReplicationControllerDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *ReplicationControllerDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
rc := d.Core().ReplicationControllers(namespace)
pc := d.Core().Pods(namespace)
@@ -1286,7 +1257,7 @@ type ReplicaSetDescriber struct {
clientset.Interface
}
func (d *ReplicaSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *ReplicaSetDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
rsc := d.Extensions().ReplicaSets(namespace)
pc := d.Core().Pods(namespace)
@@ -1338,7 +1309,7 @@ type JobDescriber struct {
clientset.Interface
}
func (d *JobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *JobDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
job, err := d.Batch().Jobs(namespace).Get(name, metav1.GetOptions{})
if err != nil {
return "", err
@@ -1387,7 +1358,7 @@ type CronJobDescriber struct {
clientset.Interface
}
func (d *CronJobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *CronJobDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
scheduledJob, err := d.Batch().CronJobs(namespace).Get(name, metav1.GetOptions{})
if err != nil {
return "", err
@@ -1474,7 +1445,7 @@ type DaemonSetDescriber struct {
clientset.Interface
}
func (d *DaemonSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *DaemonSetDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
dc := d.Extensions().DaemonSets(namespace)
pc := d.Core().Pods(namespace)
@@ -1529,7 +1500,7 @@ type SecretDescriber struct {
clientset.Interface
}
func (d *SecretDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *SecretDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
c := d.Core().Secrets(namespace)
secret, err := c.Get(name, metav1.GetOptions{})
@@ -1569,7 +1540,7 @@ type IngressDescriber struct {
clientset.Interface
}
func (i *IngressDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (i *IngressDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
c := i.Extensions().Ingresses(namespace)
ing, err := c.Get(name, metav1.GetOptions{})
if err != nil {
@@ -1598,7 +1569,7 @@ func (i *IngressDescriber) describeBackend(ns string, backend *extensions.Ingres
return formatEndpoints(endpoints, sets.NewString(spName))
}
func (i *IngressDescriber) describeIngress(ing *extensions.Ingress, describerSettings DescriberSettings) (string, error) {
func (i *IngressDescriber) describeIngress(ing *extensions.Ingress, describerSettings printers.DescriberSettings) (string, error) {
return tabbedString(func(out io.Writer) error {
w := &PrefixWriter{out}
w.Write(LEVEL_0, "Name:\t%v\n", ing.Name)
@@ -1682,7 +1653,7 @@ type ServiceDescriber struct {
clientset.Interface
}
func (d *ServiceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *ServiceDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
c := d.Core().Services(namespace)
service, err := c.Get(name, metav1.GetOptions{})
@@ -1762,7 +1733,7 @@ type EndpointsDescriber struct {
clientset.Interface
}
func (d *EndpointsDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *EndpointsDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
c := d.Core().Endpoints(namespace)
ep, err := c.Get(name, metav1.GetOptions{})
@@ -1836,7 +1807,7 @@ type ServiceAccountDescriber struct {
clientset.Interface
}
func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
c := d.Core().ServiceAccounts(namespace)
serviceAccount, err := c.Get(name, metav1.GetOptions{})
@@ -1944,7 +1915,7 @@ type NodeDescriber struct {
clientset.Interface
}
func (d *NodeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *NodeDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
mc := d.Core().Nodes()
node, err := mc.Get(name, metav1.GetOptions{})
if err != nil {
@@ -2065,7 +2036,7 @@ type StatefulSetDescriber struct {
client clientset.Interface
}
func (p *StatefulSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (p *StatefulSetDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
ps, err := p.client.Apps().StatefulSets(namespace).Get(name, metav1.GetOptions{})
if err != nil {
return "", err
@@ -2108,7 +2079,7 @@ type CertificateSigningRequestDescriber struct {
client clientset.Interface
}
func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (p *CertificateSigningRequestDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
csr, err := p.client.Certificates().CertificateSigningRequests().Get(name, metav1.GetOptions{})
if err != nil {
return "", err
@@ -2178,7 +2149,7 @@ type HorizontalPodAutoscalerDescriber struct {
client clientset.Interface
}
func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
hpa, err := d.client.Autoscaling().HorizontalPodAutoscalers(namespace).Get(name, metav1.GetOptions{})
if err != nil {
return "", err
@@ -2369,7 +2340,7 @@ type DeploymentDescriber struct {
versionedClient versionedclientset.Interface
}
func (dd *DeploymentDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (dd *DeploymentDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
d, err := dd.versionedClient.Extensions().Deployments(namespace).Get(name, metav1.GetOptions{})
if err != nil {
return "", err
@@ -2504,7 +2475,7 @@ type ConfigMapDescriber struct {
clientset.Interface
}
func (d *ConfigMapDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *ConfigMapDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
c := d.Core().ConfigMaps(namespace)
configMap, err := c.Get(name, metav1.GetOptions{})
@@ -2537,7 +2508,7 @@ type ClusterDescriber struct {
fedclientset.Interface
}
func (d *ClusterDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *ClusterDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
cluster, err := d.Federation().Clusters().Get(name, metav1.GetOptions{})
if err != nil {
return "", err
@@ -2579,7 +2550,7 @@ type NetworkPolicyDescriber struct {
clientset.Interface
}
func (d *NetworkPolicyDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (d *NetworkPolicyDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
c := d.Extensions().NetworkPolicies(namespace)
networkPolicy, err := c.Get(name, metav1.GetOptions{})
@@ -2606,7 +2577,7 @@ type StorageClassDescriber struct {
clientset.Interface
}
func (s *StorageClassDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (s *StorageClassDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
sc, err := s.Storage().StorageClasses().Get(name, metav1.GetOptions{})
if err != nil {
return "", err
@@ -2635,7 +2606,7 @@ type PodDisruptionBudgetDescriber struct {
clientset.Interface
}
func (p *PodDisruptionBudgetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) {
func (p *PodDisruptionBudgetDescriber) Describe(namespace, name string, describerSettings printers.DescriberSettings) (string, error) {
pdb, err := p.Policy().PodDisruptionBudgets(namespace).Get(name, metav1.GetOptions{})
if err != nil {
return "", err
@@ -2673,7 +2644,7 @@ func newErrNoDescriber(types ...reflect.Type) error {
for _, t := range types {
names = append(names, t.String())
}
return ErrNoDescriber{Types: names}
return printers.ErrNoDescriber{Types: names}
}
// Describers implements ObjectDescriber against functions registered via Add. Those functions can
@@ -2720,7 +2691,7 @@ func (d *Describers) DescribeObject(exact interface{}, extra ...interface{}) (st
return "", newErrNoDescriber(append([]reflect.Type{exactType}, types...)...)
}
// Add adds one or more describer functions to the Describer. The passed function must
// Add adds one or more describer functions to the printers.Describer. The passed function must
// match the signature:
//
// func(...) (string, error)
@@ -2928,3 +2899,102 @@ func printTolerationsMultilineWithIndent(w *PrefixWriter, initialIndent, title,
}
}
}
func tabbedString(f func(io.Writer) error) (string, error) {
out := new(tabwriter.Writer)
buf := &bytes.Buffer{}
out.Init(buf, 0, 8, 1, '\t', 0)
err := f(out)
if err != nil {
return "", err
}
out.Flush()
str := string(buf.String())
return str, nil
}
type SortableResourceNames []api.ResourceName
func (list SortableResourceNames) Len() int {
return len(list)
}
func (list SortableResourceNames) Swap(i, j int) {
list[i], list[j] = list[j], list[i]
}
func (list SortableResourceNames) Less(i, j int) bool {
return list[i] < list[j]
}
// SortedResourceNames returns the sorted resource names of a resource list.
func SortedResourceNames(list api.ResourceList) []api.ResourceName {
resources := make([]api.ResourceName, 0, len(list))
for res := range list {
resources = append(resources, res)
}
sort.Sort(SortableResourceNames(resources))
return resources
}
type SortableResourceQuotas []api.ResourceQuota
func (list SortableResourceQuotas) Len() int {
return len(list)
}
func (list SortableResourceQuotas) Swap(i, j int) {
list[i], list[j] = list[j], list[i]
}
func (list SortableResourceQuotas) Less(i, j int) bool {
return list[i].Name < list[j].Name
}
type SortableVolumeMounts []api.VolumeMount
func (list SortableVolumeMounts) Len() int {
return len(list)
}
func (list SortableVolumeMounts) Swap(i, j int) {
list[i], list[j] = list[j], list[i]
}
func (list SortableVolumeMounts) Less(i, j int) bool {
return list[i].MountPath < list[j].MountPath
}
// SortedQoSResourceNames returns the sorted resource names of a QoS list.
func SortedQoSResourceNames(list qos.QOSList) []api.ResourceName {
resources := make([]api.ResourceName, 0, len(list))
for res := range list {
resources = append(resources, api.ResourceName(res))
}
sort.Sort(SortableResourceNames(resources))
return resources
}
func listOfImages(spec *api.PodSpec) []string {
images := make([]string, 0, len(spec.Containers))
for _, container := range spec.Containers {
images = append(images, container.Image)
}
return images
}
func makeImageList(spec *api.PodSpec) string {
return strings.Join(listOfImages(spec), ",")
}
func versionedClientsetForDeployment(internalClient clientset.Interface) versionedclientset.Interface {
if internalClient == nil {
return &versionedclientset.Clientset{}
}
return &versionedclientset.Clientset{
CoreV1Client: coreclientset.New(internalClient.Core().RESTClient()),
ExtensionsV1beta1Client: extensionsclientset.New(internalClient.Extensions().RESTClient()),
}
}

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package kubectl
package internalversion
import (
"bytes"
@@ -40,6 +40,7 @@ import (
versionedfake "k8s.io/kubernetes/pkg/client/clientset_generated/clientset/fake"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/util"
)
@@ -59,7 +60,7 @@ func TestDescribePod(t *testing.T) {
})
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
d := PodDescriber{c}
out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
out, err := d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -83,7 +84,7 @@ func TestDescribePodTolerations(t *testing.T) {
})
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
d := PodDescriber{c}
out, err := d.Describe("foo", "bar", DescriberSettings{})
out, err := d.Describe("foo", "bar", printers.DescriberSettings{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -100,7 +101,7 @@ func TestDescribeNamespace(t *testing.T) {
})
c := &describeClient{T: t, Namespace: "", Interface: fake}
d := NamespaceDescriber{c}
out, err := d.Describe("", "myns", DescriberSettings{ShowEvents: true})
out, err := d.Describe("", "myns", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -118,7 +119,7 @@ func TestDescribeService(t *testing.T) {
})
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
d := ServiceDescriber{c}
out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
out, err := d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -167,7 +168,7 @@ func TestPodDescribeResultsSorted(t *testing.T) {
d := PodDescriber{c}
// Act
out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
out, err := d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true})
// Assert
if err != nil {
@@ -436,7 +437,7 @@ func TestDescribers(t *testing.T) {
if out, err := d.DescribeObject(first, second, third); out != "" || err == nil {
t.Errorf("unexpected result: %s %v", out, err)
} else {
if noDescriber, ok := err.(ErrNoDescriber); ok {
if noDescriber, ok := err.(printers.ErrNoDescriber); ok {
if !reflect.DeepEqual(noDescriber.Types, []string{"*api.Event", "*api.Pod", "*api.Pod"}) {
t.Errorf("unexpected describer: %v", err)
}
@@ -649,7 +650,7 @@ func TestPersistentVolumeDescriber(t *testing.T) {
for name, pv := range tests {
fake := fake.NewSimpleClientset(pv)
c := PersistentVolumeDescriber{fake}
str, err := c.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
str, err := c.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("Unexpected error for test %s: %v", name, err)
}
@@ -673,7 +674,7 @@ func TestDescribeDeployment(t *testing.T) {
},
})
d := DeploymentDescriber{fake, versionedFake}
out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
out, err := d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -707,7 +708,7 @@ func TestDescribeCluster(t *testing.T) {
}
fake := fedfake.NewSimpleClientset(&cluster)
d := ClusterDescriber{Interface: fake}
out, err := d.Describe("any", "foo", DescriberSettings{ShowEvents: true})
out, err := d.Describe("any", "foo", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -732,7 +733,7 @@ func TestDescribeStorageClass(t *testing.T) {
},
})
s := StorageClassDescriber{f}
out, err := s.Describe("", "foo", DescriberSettings{ShowEvents: true})
out, err := s.Describe("", "foo", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -756,7 +757,7 @@ func TestDescribePodDisruptionBudget(t *testing.T) {
},
})
s := PodDisruptionBudgetDescriber{f}
out, err := s.Describe("ns1", "pdb1", DescriberSettings{ShowEvents: true})
out, err := s.Describe("ns1", "pdb1", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
@@ -1112,7 +1113,7 @@ func TestDescribeHorizontalPodAutoscaler(t *testing.T) {
}
fake := fake.NewSimpleClientset(&test.hpa)
desc := HorizontalPodAutoscalerDescriber{fake}
str, err := desc.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
str, err := desc.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("Unexpected error for test %s: %v", test.name, err)
}
@@ -1141,7 +1142,7 @@ func TestDescribeEvents(t *testing.T) {
},
}
m := map[string]Describer{
m := map[string]printers.Describer{
"DaemonSetDescriber": &DaemonSetDescriber{
fake.NewSimpleClientset(&extensions.DaemonSet{
ObjectMeta: metav1.ObjectMeta{
@@ -1241,7 +1242,7 @@ func TestDescribeEvents(t *testing.T) {
}
for name, d := range m {
out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
out, err := d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error for %q: %v", name, err)
}
@@ -1252,7 +1253,7 @@ func TestDescribeEvents(t *testing.T) {
t.Errorf("events not found for %q when ShowEvents=true: %s", name, out)
}
out, err = d.Describe("foo", "bar", DescriberSettings{ShowEvents: false})
out, err = d.Describe("foo", "bar", printers.DescriberSettings{ShowEvents: false})
if err != nil {
t.Errorf("unexpected error for %q: %s", name, err)
}

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package kubectl
package internalversion
import (
"bytes"
@@ -26,8 +26,11 @@ import (
"testing"
"time"
"github.com/ghodss/yaml"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
yamlserializer "k8s.io/apimachinery/pkg/runtime/serializer/yaml"
@@ -42,8 +45,7 @@ import (
"k8s.io/kubernetes/pkg/apis/extensions"
"k8s.io/kubernetes/pkg/apis/policy"
kubectltesting "k8s.io/kubernetes/pkg/kubectl/testing"
"github.com/ghodss/yaml"
"k8s.io/kubernetes/pkg/printers"
)
func init() {
@@ -60,8 +62,8 @@ var testData = kubectltesting.TestStruct{
func TestVersionedPrinter(t *testing.T) {
original := &kubectltesting.TestStruct{Key: "value"}
p := NewVersionedPrinter(
ResourcePrinterFunc(func(obj runtime.Object, w io.Writer) error {
p := printers.NewVersionedPrinter(
printers.ResourcePrinterFunc(func(obj runtime.Object, w io.Writer) error {
if obj == original {
t.Fatalf("object should not be identical: %#v", obj)
}
@@ -79,7 +81,7 @@ func TestVersionedPrinter(t *testing.T) {
}
func TestPrintDefault(t *testing.T) {
printer, found, err := GetPrinter("", "", false, false)
printer, found, err := printers.GetStandardPrinter("", "", false, false, nil, nil, nil)
if err != nil {
t.Fatalf("unexpected error: %#v", err)
}
@@ -129,17 +131,17 @@ func TestPrinter(t *testing.T) {
{"test jsonpath", "jsonpath", "{.metadata.name}", podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"},
{"test jsonpath list", "jsonpath", "{.items[*].metadata.name}", podListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo bar"},
{"test jsonpath empty list", "jsonpath", "{.items[*].metadata.name}", emptyListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, ""},
{"test name", "name", "", podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "pod/foo\n"},
{"test name", "name", "", podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "pods/foo\n"},
{"emits versioned objects", "template", "{{.kind}}", testapi, []schema.GroupVersion{v1.SchemeGroupVersion}, "Pod"},
}
for _, test := range printerTests {
buf := bytes.NewBuffer([]byte{})
printer, generic, err := GetPrinter(test.Format, test.FormatArgument, false, true)
printer, generic, err := printers.GetStandardPrinter(test.Format, test.FormatArgument, false, true, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme})
if err != nil {
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
}
if generic && len(test.OutputVersions) > 0 {
printer = NewVersionedPrinter(printer, api.Scheme, test.OutputVersions...)
printer = printers.NewVersionedPrinter(printer, api.Scheme, test.OutputVersions...)
}
if err := printer.PrintObj(test.Input, buf); err != nil {
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
@@ -164,14 +166,14 @@ func TestBadPrinter(t *testing.T) {
{"bad jsonpath", "jsonpath", "{.Name", fmt.Errorf("error parsing jsonpath {.Name, unclosed action\n")},
}
for _, test := range badPrinterTests {
_, _, err := GetPrinter(test.Format, test.FormatArgument, false, false)
_, _, err := printers.GetStandardPrinter(test.Format, test.FormatArgument, false, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme})
if err == nil || err.Error() != test.Error.Error() {
t.Errorf("in %s, expect %s, got %s", test.Name, test.Error, err)
}
}
}
func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data []byte, v interface{}) error) {
func testPrinter(t *testing.T, printer printers.ResourcePrinter, unmarshalFunc func(data []byte, v interface{}) error) {
buf := bytes.NewBuffer([]byte{})
err := printer.PrintObj(&testData, buf)
@@ -216,11 +218,11 @@ func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data
}
func TestYAMLPrinter(t *testing.T) {
testPrinter(t, &YAMLPrinter{}, yaml.Unmarshal)
testPrinter(t, &printers.YAMLPrinter{}, yaml.Unmarshal)
}
func TestJSONPrinter(t *testing.T) {
testPrinter(t, &JSONPrinter{}, json.Unmarshal)
testPrinter(t, &printers.JSONPrinter{}, json.Unmarshal)
}
func TestFormatResourceName(t *testing.T) {
@@ -240,7 +242,7 @@ func TestFormatResourceName(t *testing.T) {
}
}
func PrintCustomType(obj *TestPrintType, w io.Writer, options PrintOptions) error {
func PrintCustomType(obj *TestPrintType, w io.Writer, options printers.PrintOptions) error {
data := obj.Data
kind := options.Kind
if options.WithKind {
@@ -250,13 +252,13 @@ func PrintCustomType(obj *TestPrintType, w io.Writer, options PrintOptions) erro
return err
}
func ErrorPrintHandler(obj *TestPrintType, w io.Writer, options PrintOptions) error {
func ErrorPrintHandler(obj *TestPrintType, w io.Writer, options printers.PrintOptions) error {
return fmt.Errorf("ErrorPrintHandler error")
}
func TestCustomTypePrinting(t *testing.T) {
columns := []string{"Data"}
printer := NewHumanReadablePrinter(PrintOptions{})
printer := printers.NewHumanReadablePrinter(printers.PrintOptions{})
printer.Handler(columns, nil, PrintCustomType)
obj := TestPrintType{"test object"}
@@ -273,7 +275,7 @@ func TestCustomTypePrinting(t *testing.T) {
func TestCustomTypePrintingWithKind(t *testing.T) {
columns := []string{"Data"}
printer := NewHumanReadablePrinter(PrintOptions{})
printer := printers.NewHumanReadablePrinter(printers.PrintOptions{})
printer.Handler(columns, nil, PrintCustomType)
printer.EnsurePrintWithKind("test")
@@ -291,7 +293,7 @@ func TestCustomTypePrintingWithKind(t *testing.T) {
func TestPrintHandlerError(t *testing.T) {
columns := []string{"Data"}
printer := NewHumanReadablePrinter(PrintOptions{})
printer := printers.NewHumanReadablePrinter(printers.PrintOptions{})
printer.Handler(columns, nil, ErrorPrintHandler)
obj := TestPrintType{"test object"}
buffer := &bytes.Buffer{}
@@ -302,7 +304,7 @@ func TestPrintHandlerError(t *testing.T) {
}
func TestUnknownTypePrinting(t *testing.T) {
printer := NewHumanReadablePrinter(PrintOptions{})
printer := printers.NewHumanReadablePrinter(printers.PrintOptions{})
buffer := &bytes.Buffer{}
err := printer.PrintObj(&TestUnknownType{}, buffer)
if err == nil {
@@ -312,7 +314,7 @@ func TestUnknownTypePrinting(t *testing.T) {
func TestTemplatePanic(t *testing.T) {
tmpl := `{{and ((index .currentState.info "foo").state.running.startedAt) .currentState.info.net.state.running.startedAt}}`
printer, err := NewTemplatePrinter([]byte(tmpl))
printer, err := printers.NewTemplatePrinter([]byte(tmpl))
if err != nil {
t.Fatalf("tmpl fail: %v", err)
}
@@ -340,7 +342,7 @@ func TestNamePrinter(t *testing.T) {
Name: "foo",
},
},
"pod/foo\n"},
"pods/foo\n"},
"List": {
&v1.List{
TypeMeta: metav1.TypeMeta{
@@ -355,9 +357,9 @@ func TestNamePrinter(t *testing.T) {
},
},
},
"pod/foo\npod/bar\n"},
"pods/foo\npods/bar\n"},
}
printer, _, _ := GetPrinter("name", "", false, false)
printer, _, _ := printers.GetStandardPrinter("name", "", false, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme})
for name, item := range tests {
buff := &bytes.Buffer{}
err := printer.PrintObj(item.obj, buff)
@@ -467,12 +469,12 @@ func TestTemplateStrings(t *testing.T) {
}
// The point of this test is to verify that the below template works.
tmpl := `{{if (exists . "status" "containerStatuses")}}{{range .status.containerStatuses}}{{if (and (eq .name "foo") (exists . "state" "running"))}}true{{end}}{{end}}{{end}}`
p, err := NewTemplatePrinter([]byte(tmpl))
p, err := printers.NewTemplatePrinter([]byte(tmpl))
if err != nil {
t.Fatalf("tmpl fail: %v", err)
}
printer := NewVersionedPrinter(p, api.Scheme, api.Registry.GroupOrDie(api.GroupName).GroupVersion)
printer := printers.NewVersionedPrinter(p, api.Scheme, api.Registry.GroupOrDie(api.GroupName).GroupVersion)
for name, item := range table {
buffer := &bytes.Buffer{}
@@ -496,44 +498,47 @@ func TestPrinters(t *testing.T) {
var (
err error
templatePrinter ResourcePrinter
templatePrinter2 ResourcePrinter
jsonpathPrinter ResourcePrinter
templatePrinter printers.ResourcePrinter
templatePrinter2 printers.ResourcePrinter
jsonpathPrinter printers.ResourcePrinter
)
templatePrinter, err = NewTemplatePrinter([]byte("{{.name}}"))
templatePrinter, err = printers.NewTemplatePrinter([]byte("{{.name}}"))
if err != nil {
t.Fatal(err)
}
templatePrinter = NewVersionedPrinter(templatePrinter, api.Scheme, v1.SchemeGroupVersion)
templatePrinter = printers.NewVersionedPrinter(templatePrinter, api.Scheme, v1.SchemeGroupVersion)
templatePrinter2, err = NewTemplatePrinter([]byte("{{len .items}}"))
templatePrinter2, err = printers.NewTemplatePrinter([]byte("{{len .items}}"))
if err != nil {
t.Fatal(err)
}
templatePrinter2 = NewVersionedPrinter(templatePrinter2, api.Scheme, v1.SchemeGroupVersion)
templatePrinter2 = printers.NewVersionedPrinter(templatePrinter2, api.Scheme, v1.SchemeGroupVersion)
jsonpathPrinter, err = NewJSONPathPrinter("{.metadata.name}")
jsonpathPrinter, err = printers.NewJSONPathPrinter("{.metadata.name}")
if err != nil {
t.Fatal(err)
}
jsonpathPrinter = NewVersionedPrinter(jsonpathPrinter, api.Scheme, v1.SchemeGroupVersion)
jsonpathPrinter = printers.NewVersionedPrinter(jsonpathPrinter, api.Scheme, v1.SchemeGroupVersion)
printers := map[string]ResourcePrinter{
"humanReadable": NewHumanReadablePrinter(PrintOptions{
allPrinters := map[string]printers.ResourcePrinter{
"humanReadable": printers.NewHumanReadablePrinter(printers.PrintOptions{
NoHeaders: true,
}),
"humanReadableHeaders": NewHumanReadablePrinter(PrintOptions{}),
"json": &JSONPrinter{},
"yaml": &YAMLPrinter{},
"humanReadableHeaders": printers.NewHumanReadablePrinter(printers.PrintOptions{}),
"json": &printers.JSONPrinter{},
"yaml": &printers.YAMLPrinter{},
"template": templatePrinter,
"template2": templatePrinter2,
"jsonpath": jsonpathPrinter,
"name": &NamePrinter{
Typer: api.Scheme,
Decoder: api.Codecs.UniversalDecoder(),
"name": &printers.NamePrinter{
Typer: api.Scheme,
Decoders: []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme},
Mapper: api.Registry.RESTMapper(api.Registry.EnabledVersions()...),
},
}
AddHandlers((allPrinters["humanReadable"]).(*printers.HumanReadablePrinter))
AddHandlers((allPrinters["humanReadableHeaders"]).(*printers.HumanReadablePrinter))
objects := map[string]runtime.Object{
"pod": &api.Pod{ObjectMeta: om("pod")},
"emptyPodList": &api.PodList{},
@@ -550,7 +555,7 @@ func TestPrinters(t *testing.T) {
"jsonpath": sets.NewString("emptyPodList", "nonEmptyPodList", "endpoints"),
}
for pName, p := range printers {
for pName, p := range allPrinters {
for oName, obj := range objects {
b := &bytes.Buffer{}
if err := p.PrintObj(obj, b); err != nil {
@@ -566,7 +571,8 @@ func TestPrinters(t *testing.T) {
func TestPrintEventsResultSorted(t *testing.T) {
// Arrange
printer := NewHumanReadablePrinter(PrintOptions{})
printer := printers.NewHumanReadablePrinter(printers.PrintOptions{})
AddHandlers(printer)
obj := api.EventList{
Items: []api.Event{
@@ -610,7 +616,8 @@ func TestPrintEventsResultSorted(t *testing.T) {
}
func TestPrintNodeStatus(t *testing.T) {
printer := NewHumanReadablePrinter(PrintOptions{})
printer := printers.NewHumanReadablePrinter(printers.PrintOptions{})
AddHandlers(printer)
table := []struct {
node api.Node
status string
@@ -729,10 +736,11 @@ func TestPrintNodeStatus(t *testing.T) {
}
func TestPrintNodeOSImage(t *testing.T) {
printer := NewHumanReadablePrinter(PrintOptions{
printer := printers.NewHumanReadablePrinter(printers.PrintOptions{
ColumnLabels: []string{},
Wide: true,
})
AddHandlers(printer)
table := []struct {
node api.Node
@@ -773,10 +781,11 @@ func TestPrintNodeOSImage(t *testing.T) {
}
func TestPrintNodeKernelVersion(t *testing.T) {
printer := NewHumanReadablePrinter(PrintOptions{
printer := printers.NewHumanReadablePrinter(printers.PrintOptions{
ColumnLabels: []string{},
Wide: true,
})
AddHandlers(printer)
table := []struct {
node api.Node
@@ -817,9 +826,10 @@ func TestPrintNodeKernelVersion(t *testing.T) {
}
func TestPrintNodeExternalIP(t *testing.T) {
printer := NewHumanReadablePrinter(PrintOptions{
printer := printers.NewHumanReadablePrinter(printers.PrintOptions{
Wide: true,
})
AddHandlers(printer)
table := []struct {
node api.Node
externalIP string
@@ -900,7 +910,7 @@ func TestPrintHunmanReadableIngressWithColumnLabels(t *testing.T) {
},
}
buff := bytes.Buffer{}
printIngress(&ingress, &buff, PrintOptions{
printIngress(&ingress, &buff, printers.PrintOptions{
ColumnLabels: []string{"app_name"},
})
output := string(buff.Bytes())
@@ -1025,7 +1035,7 @@ func TestPrintHumanReadableService(t *testing.T) {
for _, svc := range tests {
for _, wide := range []bool{false, true} {
buff := bytes.Buffer{}
printService(&svc, &buff, PrintOptions{Wide: wide})
printService(&svc, &buff, printers.PrintOptions{Wide: wide})
output := string(buff.Bytes())
ip := svc.Spec.ClusterIP
if !strings.Contains(output, ip) {
@@ -1209,9 +1219,10 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) {
for _, test := range table {
if test.isNamespaced {
// Expect output to include namespace when requested.
printer := NewHumanReadablePrinter(PrintOptions{
printer := printers.NewHumanReadablePrinter(printers.PrintOptions{
WithNamespace: true,
})
AddHandlers(printer)
buffer := &bytes.Buffer{}
err := printer.PrintObj(test.obj, buffer)
if err != nil {
@@ -1223,7 +1234,7 @@ func TestPrintHumanReadableWithNamespace(t *testing.T) {
}
} else {
// Expect error when trying to get all namespaces for un-namespaced object.
printer := NewHumanReadablePrinter(PrintOptions{
printer := printers.NewHumanReadablePrinter(printers.PrintOptions{
WithNamespace: true,
})
buffer := &bytes.Buffer{}
@@ -1319,9 +1330,8 @@ func TestPrintPod(t *testing.T) {
}
buf := bytes.NewBuffer([]byte{})
printer := HumanReadablePrinter{}
for _, test := range tests {
printer.printPod(&test.pod, buf, PrintOptions{ShowAll: true})
printPod(&test.pod, buf, printers.PrintOptions{ShowAll: true})
// We ignore time
if !strings.HasPrefix(buf.String(), test.expect) {
t.Fatalf("Expected: %s, got: %s", test.expect, buf.String())
@@ -1413,9 +1423,8 @@ func TestPrintNonTerminatedPod(t *testing.T) {
}
buf := bytes.NewBuffer([]byte{})
printer := HumanReadablePrinter{}
for _, test := range tests {
printer.printPod(&test.pod, buf, PrintOptions{})
printPod(&test.pod, buf, printers.PrintOptions{})
// We ignore time
if !strings.HasPrefix(buf.String(), test.expect) {
t.Fatalf("Expected: %s, got: %s", test.expect, buf.String())
@@ -1474,9 +1483,8 @@ func TestPrintPodWithLabels(t *testing.T) {
}
buf := bytes.NewBuffer([]byte{})
printer := HumanReadablePrinter{}
for _, test := range tests {
printer.printPod(&test.pod, buf, PrintOptions{ColumnLabels: test.labelColumns})
printPod(&test.pod, buf, printers.PrintOptions{ColumnLabels: test.labelColumns})
// We ignore time
if !strings.HasPrefix(buf.String(), test.startsWith) || !strings.HasSuffix(buf.String(), test.endsWith) {
t.Fatalf("Expected to start with: %s and end with: %s, but got: %s", test.startsWith, test.endsWith, buf.String())
@@ -1554,13 +1562,13 @@ func TestPrintDeployment(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
for _, test := range tests {
printDeployment(&test.deployment, buf, PrintOptions{})
printDeployment(&test.deployment, buf, printers.PrintOptions{})
if buf.String() != test.expect {
t.Fatalf("Expected: %s, got: %s", test.expect, buf.String())
}
buf.Reset()
// print deployment with '-o wide' option
printDeployment(&test.deployment, buf, PrintOptions{Wide: true})
printDeployment(&test.deployment, buf, printers.PrintOptions{Wide: true})
if buf.String() != test.wideExpect {
t.Fatalf("Expected: %s, got: %s", test.wideExpect, buf.String())
}
@@ -1596,7 +1604,7 @@ func TestPrintDaemonSet(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
for _, test := range tests {
printDaemonSet(&test.ds, buf, PrintOptions{})
printDaemonSet(&test.ds, buf, printers.PrintOptions{})
if !strings.HasPrefix(buf.String(), test.startsWith) {
t.Fatalf("Expected to start with %s but got %s", test.startsWith, buf.String())
}
@@ -1644,7 +1652,7 @@ func TestPrintJob(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
for _, test := range tests {
printJob(&test.job, buf, PrintOptions{})
printJob(&test.job, buf, printers.PrintOptions{})
if buf.String() != test.expect {
t.Fatalf("Expected: %s, got: %s", test.expect, buf.String())
}
@@ -2014,7 +2022,7 @@ func TestPrintHPA(t *testing.T) {
buff := bytes.NewBuffer([]byte{})
for _, test := range tests {
err := printHorizontalPodAutoscaler(&test.hpa, buff, PrintOptions{})
err := printHorizontalPodAutoscaler(&test.hpa, buff, printers.PrintOptions{})
if err != nil {
t.Errorf("expected %q, got error: %v", test.expected, err)
buff.Reset()
@@ -2079,10 +2087,8 @@ func TestPrintPodShowLabels(t *testing.T) {
}
buf := bytes.NewBuffer([]byte{})
printer := HumanReadablePrinter{}
for _, test := range tests {
printer.printPod(&test.pod, buf, PrintOptions{ShowLabels: test.showLabels})
printPod(&test.pod, buf, printers.PrintOptions{ShowLabels: test.showLabels})
// We ignore time
if !strings.HasPrefix(buf.String(), test.startsWith) || !strings.HasSuffix(buf.String(), test.endsWith) {
t.Fatalf("Expected to start with: %s and end with: %s, but got: %s", test.startsWith, test.endsWith, buf.String())
@@ -2132,7 +2138,7 @@ func TestPrintService(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
for _, test := range tests {
printService(&test.service, buf, PrintOptions{})
printService(&test.service, buf, printers.PrintOptions{})
// We ignore time
if buf.String() != test.expect {
t.Fatalf("Expected: %s, got: %s %d", test.expect, buf.String(), strings.Compare(test.expect, buf.String()))
@@ -2165,7 +2171,7 @@ func TestPrintPodDisruptionBudget(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
for _, test := range tests {
printPodDisruptionBudget(&test.pdb, buf, PrintOptions{})
printPodDisruptionBudget(&test.pdb, buf, printers.PrintOptions{})
if buf.String() != test.expect {
t.Fatalf("Expected: %s, got: %s", test.expect, buf.String())
}
@@ -2190,7 +2196,7 @@ func TestAllowMissingKeys(t *testing.T) {
}
for _, test := range tests {
buf := bytes.NewBuffer([]byte{})
printer, _, err := GetPrinter(test.Format, test.Template, false, test.AllowMissingTemplateKeys)
printer, _, err := printers.GetStandardPrinter(test.Format, test.Template, false, test.AllowMissingTemplateKeys, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme})
if err != nil {
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
}

View File

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

101
pkg/printers/json.go Normal file
View File

@@ -0,0 +1,101 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
import (
"bytes"
"encoding/json"
"fmt"
"io"
"k8s.io/apimachinery/pkg/runtime"
"github.com/ghodss/yaml"
)
// JSONPrinter is an implementation of ResourcePrinter which outputs an object as JSON.
type JSONPrinter struct {
}
func (p *JSONPrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
// PrintObj is an implementation of ResourcePrinter.PrintObj which simply writes the object to the Writer.
func (p *JSONPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
switch obj := obj.(type) {
case *runtime.Unknown:
var buf bytes.Buffer
err := json.Indent(&buf, obj.Raw, "", " ")
if err != nil {
return err
}
buf.WriteRune('\n')
_, err = buf.WriteTo(w)
return err
}
data, err := json.MarshalIndent(obj, "", " ")
if err != nil {
return err
}
data = append(data, '\n')
_, err = w.Write(data)
return err
}
// TODO: implement HandledResources()
func (p *JSONPrinter) HandledResources() []string {
return []string{}
}
// YAMLPrinter is an implementation of ResourcePrinter which outputs an object as YAML.
// The input object is assumed to be in the internal version of an API and is converted
// to the given version first.
type YAMLPrinter struct {
version string
converter runtime.ObjectConvertor
}
func (p *YAMLPrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
// PrintObj prints the data as YAML.
func (p *YAMLPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
switch obj := obj.(type) {
case *runtime.Unknown:
data, err := yaml.JSONToYAML(obj.Raw)
if err != nil {
return err
}
_, err = w.Write(data)
return err
}
output, err := yaml.Marshal(obj)
if err != nil {
return err
}
_, err = fmt.Fprint(w, string(output))
return err
}
// TODO: implement HandledResources()
func (p *YAMLPrinter) HandledResources() []string {
return []string{}
}

157
pkg/printers/jsonpath.go Normal file
View File

@@ -0,0 +1,157 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
import (
"encoding/json"
"fmt"
"io"
"reflect"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/util/jsonpath"
)
// exists returns true if it would be possible to call the index function
// with these arguments.
//
// TODO: how to document this for users?
//
// index returns the result of indexing its first argument by the following
// arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
// indexed item must be a map, slice, or array.
func exists(item interface{}, indices ...interface{}) bool {
v := reflect.ValueOf(item)
for _, i := range indices {
index := reflect.ValueOf(i)
var isNil bool
if v, isNil = indirect(v); isNil {
return false
}
switch v.Kind() {
case reflect.Array, reflect.Slice, reflect.String:
var x int64
switch index.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
x = index.Int()
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
x = int64(index.Uint())
default:
return false
}
if x < 0 || x >= int64(v.Len()) {
return false
}
v = v.Index(int(x))
case reflect.Map:
if !index.IsValid() {
index = reflect.Zero(v.Type().Key())
}
if !index.Type().AssignableTo(v.Type().Key()) {
return false
}
if x := v.MapIndex(index); x.IsValid() {
v = x
} else {
v = reflect.Zero(v.Type().Elem())
}
default:
return false
}
}
if _, isNil := indirect(v); isNil {
return false
}
return true
}
// stolen from text/template
// indirect returns the item at the end of indirection, and a bool to indicate if it's nil.
// We indirect through pointers and empty interfaces (only) because
// non-empty interfaces have methods we might need.
func indirect(v reflect.Value) (rv reflect.Value, isNil bool) {
for ; v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface; v = v.Elem() {
if v.IsNil() {
return v, true
}
if v.Kind() == reflect.Interface && v.NumMethod() > 0 {
break
}
}
return v, false
}
// JSONPathPrinter is an implementation of ResourcePrinter which formats data with jsonpath expression.
type JSONPathPrinter struct {
rawTemplate string
*jsonpath.JSONPath
}
func NewJSONPathPrinter(tmpl string) (*JSONPathPrinter, error) {
j := jsonpath.New("out")
if err := j.Parse(tmpl); err != nil {
return nil, err
}
return &JSONPathPrinter{tmpl, j}, nil
}
func (j *JSONPathPrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
// PrintObj formats the obj with the JSONPath Template.
func (j *JSONPathPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
var queryObj interface{} = obj
if meta.IsListType(obj) {
data, err := json.Marshal(obj)
if err != nil {
return err
}
queryObj = map[string]interface{}{}
if err := json.Unmarshal(data, &queryObj); err != nil {
return err
}
}
if unknown, ok := obj.(*runtime.Unknown); ok {
data, err := json.Marshal(unknown)
if err != nil {
return err
}
queryObj = map[string]interface{}{}
if err := json.Unmarshal(data, &queryObj); err != nil {
return err
}
}
if unstructured, ok := obj.(runtime.Unstructured); ok {
queryObj = unstructured.UnstructuredContent()
}
if err := j.JSONPath.Execute(w, queryObj); err != nil {
fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n", err)
fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", j.rawTemplate)
fmt.Fprintf(w, "\tobject given to jsonpath engine was:\n\t\t%#v\n\n", queryObj)
return fmt.Errorf("error executing jsonpath %q: %v\n", j.rawTemplate, err)
}
return nil
}
// TODO: implement HandledResources()
func (p *JSONPathPrinter) HandledResources() []string {
return []string{}
}

91
pkg/printers/name.go Normal file
View File

@@ -0,0 +1,91 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
import (
"fmt"
"io"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
)
// NamePrinter is an implementation of ResourcePrinter which outputs "resource/name" pair of an object.
type NamePrinter struct {
Decoders []runtime.Decoder
Typer runtime.ObjectTyper
Mapper meta.RESTMapper
}
func (p *NamePrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
// PrintObj is an implementation of ResourcePrinter.PrintObj which decodes the object
// and print "resource/name" pair. If the object is a List, print all items in it.
func (p *NamePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
if meta.IsListType(obj) {
items, err := meta.ExtractList(obj)
if err != nil {
return err
}
if errs := runtime.DecodeList(items, p.Decoders...); len(errs) > 0 {
return utilerrors.NewAggregate(errs)
}
for _, obj := range items {
if err := p.PrintObj(obj, w); err != nil {
return err
}
}
return nil
}
name := "<unknown>"
if acc, err := meta.Accessor(obj); err == nil {
if n := acc.GetName(); len(n) > 0 {
name = n
}
}
kind := obj.GetObjectKind().GroupVersionKind()
if len(kind.Kind) == 0 {
if gvks, _, err := p.Typer.ObjectKinds(obj); err == nil {
for _, gvk := range gvks {
if mappings, err := p.Mapper.RESTMappings(gvk.GroupKind(), gvk.Version); err == nil && len(mappings) > 0 {
fmt.Fprintf(w, "%s/%s\n", mappings[0].Resource, name)
}
}
} else {
fmt.Fprintf(w, "<unknown>/%s\n", name)
}
} else {
if mappings, err := p.Mapper.RESTMappings(kind.GroupKind(), kind.Version); err == nil && len(mappings) > 0 {
fmt.Fprintf(w, "%s/%s\n", mappings[0].Resource, name)
} else {
fmt.Fprintf(w, "<unknown>/%s\n", name)
}
}
return nil
}
// TODO: implement HandledResources()
func (p *NamePrinter) HandledResources() []string {
return []string{}
}

116
pkg/printers/printers.go Normal file
View File

@@ -0,0 +1,116 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
import (
"fmt"
"io/ioutil"
"os"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
)
// GetStandardPrinter takes a format type, an optional format argument. It will return true
// if the format is generic (untyped), otherwise it will return false. The printer
// is agnostic to schema versions, so you must send arguments to PrintObj in the
// version you wish them to be shown using a VersionedPrinter (typically when
// generic is true).
func GetStandardPrinter(format, formatArgument string, noHeaders, allowMissingTemplateKeys bool, mapper meta.RESTMapper, typer runtime.ObjectTyper, decoders []runtime.Decoder) (ResourcePrinter, bool, error) {
var printer ResourcePrinter
switch format {
case "json":
printer = &JSONPrinter{}
case "yaml":
printer = &YAMLPrinter{}
case "name":
printer = &NamePrinter{
Typer: typer,
Decoders: decoders,
Mapper: mapper,
}
case "template", "go-template":
if len(formatArgument) == 0 {
return nil, false, fmt.Errorf("template format specified but no template given")
}
templatePrinter, err := NewTemplatePrinter([]byte(formatArgument))
if err != nil {
return nil, false, fmt.Errorf("error parsing template %s, %v\n", formatArgument, err)
}
templatePrinter.AllowMissingKeys(allowMissingTemplateKeys)
printer = templatePrinter
case "templatefile", "go-template-file":
if len(formatArgument) == 0 {
return nil, false, fmt.Errorf("templatefile format specified but no template file given")
}
data, err := ioutil.ReadFile(formatArgument)
if err != nil {
return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err)
}
templatePrinter, err := NewTemplatePrinter(data)
if err != nil {
return nil, false, fmt.Errorf("error parsing template %s, %v\n", string(data), err)
}
templatePrinter.AllowMissingKeys(allowMissingTemplateKeys)
printer = templatePrinter
case "jsonpath":
if len(formatArgument) == 0 {
return nil, false, fmt.Errorf("jsonpath template format specified but no template given")
}
jsonpathPrinter, err := NewJSONPathPrinter(formatArgument)
if err != nil {
return nil, false, fmt.Errorf("error parsing jsonpath %s, %v\n", formatArgument, err)
}
jsonpathPrinter.AllowMissingKeys(allowMissingTemplateKeys)
printer = jsonpathPrinter
case "jsonpath-file":
if len(formatArgument) == 0 {
return nil, false, fmt.Errorf("jsonpath file format specified but no template file file given")
}
data, err := ioutil.ReadFile(formatArgument)
if err != nil {
return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err)
}
jsonpathPrinter, err := NewJSONPathPrinter(string(data))
if err != nil {
return nil, false, fmt.Errorf("error parsing template %s, %v\n", string(data), err)
}
jsonpathPrinter.AllowMissingKeys(allowMissingTemplateKeys)
printer = jsonpathPrinter
case "custom-columns":
var err error
if printer, err = NewCustomColumnsPrinterFromSpec(formatArgument, decoders[0], noHeaders); err != nil {
return nil, false, err
}
case "custom-columns-file":
file, err := os.Open(formatArgument)
if err != nil {
return nil, false, fmt.Errorf("error reading template %s, %v\n", formatArgument, err)
}
defer file.Close()
if printer, err = NewCustomColumnsPrinterFromTemplate(file, decoders[0]); err != nil {
return nil, false, err
}
case "wide":
fallthrough
case "":
return nil, false, nil
default:
return nil, false, fmt.Errorf("output format %q not recognized", format)
}
return printer, true, nil
}

35
pkg/printers/tabwriter.go Normal file
View File

@@ -0,0 +1,35 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
import (
"io"
"text/tabwriter"
)
const (
tabwriterMinWidth = 10
tabwriterWidth = 4
tabwriterPadding = 3
tabwriterPadChar = ' '
tabwriterFlags = 0
)
// GetNewTabWriter returns a tabwriter that translates tabbed columns in input into properly aligned text.
func GetNewTabWriter(output io.Writer) *tabwriter.Writer {
return tabwriter.NewWriter(output, tabwriterMinWidth, tabwriterWidth, tabwriterPadding, tabwriterPadChar, tabwriterFlags)
}

110
pkg/printers/template.go Normal file
View File

@@ -0,0 +1,110 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
import (
"encoding/json"
"fmt"
"io"
"text/template"
"k8s.io/apimachinery/pkg/runtime"
)
// TemplatePrinter is an implementation of ResourcePrinter which formats data with a Go Template.
type TemplatePrinter struct {
rawTemplate string
template *template.Template
}
func NewTemplatePrinter(tmpl []byte) (*TemplatePrinter, error) {
t, err := template.New("output").
Funcs(template.FuncMap{"exists": exists}).
Parse(string(tmpl))
if err != nil {
return nil, err
}
return &TemplatePrinter{
rawTemplate: string(tmpl),
template: t,
}, nil
}
// AllowMissingKeys tells the template engine if missing keys are allowed.
func (p *TemplatePrinter) AllowMissingKeys(allow bool) {
if allow {
p.template.Option("missingkey=default")
} else {
p.template.Option("missingkey=error")
}
}
func (p *TemplatePrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
// PrintObj formats the obj with the Go Template.
func (p *TemplatePrinter) PrintObj(obj runtime.Object, w io.Writer) error {
var data []byte
var err error
data, err = json.Marshal(obj)
if err != nil {
return err
}
out := map[string]interface{}{}
if err := json.Unmarshal(data, &out); err != nil {
return err
}
if err = p.safeExecute(w, out); err != nil {
// It is way easier to debug this stuff when it shows up in
// stdout instead of just stdin. So in addition to returning
// a nice error, also print useful stuff with the writer.
fmt.Fprintf(w, "Error executing template: %v. Printing more information for debugging the template:\n", err)
fmt.Fprintf(w, "\ttemplate was:\n\t\t%v\n", p.rawTemplate)
fmt.Fprintf(w, "\traw data was:\n\t\t%v\n", string(data))
fmt.Fprintf(w, "\tobject given to template engine was:\n\t\t%+v\n\n", out)
return fmt.Errorf("error executing template %q: %v", p.rawTemplate, err)
}
return nil
}
// TODO: implement HandledResources()
func (p *TemplatePrinter) HandledResources() []string {
return []string{}
}
// safeExecute tries to execute the template, but catches panics and returns an error
// should the template engine panic.
func (p *TemplatePrinter) safeExecute(w io.Writer, obj interface{}) error {
var panicErr error
// Sorry for the double anonymous function. There's probably a clever way
// to do this that has the defer'd func setting the value to be returned, but
// that would be even less obvious.
retErr := func() error {
defer func() {
if x := recover(); x != nil {
panicErr = fmt.Errorf("caught panic: %+v", x)
}
}()
return p.template.Execute(w, obj)
}()
if panicErr != nil {
return panicErr
}
return retErr
}

63
pkg/printers/versioned.go Normal file
View File

@@ -0,0 +1,63 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package printers
import (
"fmt"
"io"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// VersionedPrinter takes runtime objects and ensures they are converted to a given API version
// prior to being passed to a nested printer.
type VersionedPrinter struct {
printer ResourcePrinter
converter runtime.ObjectConvertor
versions []schema.GroupVersion
}
// NewVersionedPrinter wraps a printer to convert objects to a known API version prior to printing.
func NewVersionedPrinter(printer ResourcePrinter, converter runtime.ObjectConvertor, versions ...schema.GroupVersion) ResourcePrinter {
return &VersionedPrinter{
printer: printer,
converter: converter,
versions: versions,
}
}
func (p *VersionedPrinter) AfterPrint(w io.Writer, res string) error {
return nil
}
// PrintObj implements ResourcePrinter
func (p *VersionedPrinter) PrintObj(obj runtime.Object, w io.Writer) error {
if len(p.versions) == 0 {
return fmt.Errorf("no version specified, object cannot be converted")
}
converted, err := p.converter.ConvertToVersion(obj, schema.GroupVersions(p.versions))
if err != nil {
return err
}
return p.printer.PrintObj(converted, w)
}
// TODO: implement HandledResources()
func (p *VersionedPrinter) HandledResources() []string {
return []string{}
}

View File

@@ -73,11 +73,24 @@ func (obj *Unstructured) UnstructuredContent() map[string]interface{} {
}
return obj.Object
}
// UnstructuredContent returns a map contain an overlay of the Items field onto
// the Object field. Items always overwrites overlay. Changing "items" in the
// returned object will affect items in the underlying Items field, but changing
// the "items" slice itself will have no effect.
// TODO: expose SetUnstructuredContent on runtime.Unstructured that allows
// items to be changed.
func (obj *UnstructuredList) UnstructuredContent() map[string]interface{} {
if obj.Object == nil {
obj.Object = make(map[string]interface{})
out := obj.Object
if out == nil {
out = make(map[string]interface{})
}
return obj.Object
items := make([]interface{}, len(obj.Items))
for i, item := range obj.Items {
items[i] = item.Object
}
out["items"] = items
return out
}
// MarshalJSON ensures that the unstructured object produces proper

View File

@@ -0,0 +1,36 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package unstructured
import "testing"
func TestUnstructuredList(t *testing.T) {
list := &UnstructuredList{
Object: map[string]interface{}{"kind": "List", "apiVersion": "v1"},
Items: []*Unstructured{
{Object: map[string]interface{}{"kind": "Pod", "apiVersion": "v1", "metadata": map[string]interface{}{"name": "test"}}},
},
}
content := list.UnstructuredContent()
items := content["items"].([]interface{})
if len(items) != 1 {
t.Fatalf("unexpected items: %#v", items)
}
if getNestedField(items[0].(map[string]interface{}), "metadata", "name") != "test" {
t.Fatalf("unexpected fields: %#v", items[0])
}
}

7
vendor/BUILD vendored
View File

@@ -17116,3 +17116,10 @@ go_library(
"//vendor:k8s.io/kube-aggregator/pkg/apis/apiregistration/install",
],
)
go_test(
name = "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured_test",
srcs = ["k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured_test.go"],
library = ":k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
tags = ["automanaged"],
)