mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-31 07:20:13 +00:00
Get cmd uses print-column extn from Openapi schema
Get command now uses metadata x-kubernetes-print-columns, if present, in Openapi schema to format output for a resource. This functionality is guarded by a boolean flag 'use-openapi-print-columns'.
This commit is contained in:
parent
4a01f44b73
commit
f768a63fb0
@ -92,6 +92,7 @@ go_library(
|
|||||||
"//pkg/kubectl/cmd/templates:go_default_library",
|
"//pkg/kubectl/cmd/templates:go_default_library",
|
||||||
"//pkg/kubectl/cmd/util:go_default_library",
|
"//pkg/kubectl/cmd/util:go_default_library",
|
||||||
"//pkg/kubectl/cmd/util/editor:go_default_library",
|
"//pkg/kubectl/cmd/util/editor:go_default_library",
|
||||||
|
"//pkg/kubectl/cmd/util/openapi:go_default_library",
|
||||||
"//pkg/kubectl/metricsutil:go_default_library",
|
"//pkg/kubectl/metricsutil:go_default_library",
|
||||||
"//pkg/kubectl/plugins:go_default_library",
|
"//pkg/kubectl/plugins:go_default_library",
|
||||||
"//pkg/kubectl/resource:go_default_library",
|
"//pkg/kubectl/resource:go_default_library",
|
||||||
@ -208,6 +209,7 @@ go_test(
|
|||||||
"//pkg/kubectl:go_default_library",
|
"//pkg/kubectl:go_default_library",
|
||||||
"//pkg/kubectl/cmd/testing:go_default_library",
|
"//pkg/kubectl/cmd/testing:go_default_library",
|
||||||
"//pkg/kubectl/cmd/util:go_default_library",
|
"//pkg/kubectl/cmd/util:go_default_library",
|
||||||
|
"//pkg/kubectl/cmd/util/openapi:go_default_library",
|
||||||
"//pkg/kubectl/plugins:go_default_library",
|
"//pkg/kubectl/plugins:go_default_library",
|
||||||
"//pkg/kubectl/resource:go_default_library",
|
"//pkg/kubectl/resource:go_default_library",
|
||||||
"//pkg/printers:go_default_library",
|
"//pkg/printers:go_default_library",
|
||||||
@ -215,6 +217,7 @@ go_test(
|
|||||||
"//pkg/util/i18n:go_default_library",
|
"//pkg/util/i18n:go_default_library",
|
||||||
"//pkg/util/strings:go_default_library",
|
"//pkg/util/strings:go_default_library",
|
||||||
"//pkg/util/term:go_default_library",
|
"//pkg/util/term:go_default_library",
|
||||||
|
"//vendor/github.com/go-openapi/spec:go_default_library",
|
||||||
"//vendor/github.com/spf13/cobra:go_default_library",
|
"//vendor/github.com/spf13/cobra:go_default_library",
|
||||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||||
"//vendor/gopkg.in/yaml.v2:go_default_library",
|
"//vendor/gopkg.in/yaml.v2:go_default_library",
|
||||||
|
@ -81,7 +81,7 @@ func NewCmdConfigView(out, errOut io.Writer, ConfigAccess clientcmd.ConfigAccess
|
|||||||
cmd.Flags().Set("output", defaultOutputFormat)
|
cmd.Flags().Set("output", defaultOutputFormat)
|
||||||
}
|
}
|
||||||
|
|
||||||
printer, err := cmdutil.PrinterForCommand(cmd, meta.NewDefaultRESTMapper(nil, nil), latest.Scheme, nil, []runtime.Decoder{latest.Codec}, printers.PrintOptions{})
|
printer, err := cmdutil.PrinterForCommand(cmd, nil, meta.NewDefaultRESTMapper(nil, nil), latest.Scheme, nil, []runtime.Decoder{latest.Codec}, printers.PrintOptions{})
|
||||||
cmdutil.CheckErr(err)
|
cmdutil.CheckErr(err)
|
||||||
printer = printers.NewVersionedPrinter(printer, latest.Scheme, latest.ExternalVersion)
|
printer = printers.NewVersionedPrinter(printer, latest.Scheme, latest.ExternalVersion)
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.C
|
|||||||
cmd.Flags().Set("output", outputFormat)
|
cmd.Flags().Set("output", outputFormat)
|
||||||
}
|
}
|
||||||
o.encoder = f.JSONEncoder()
|
o.encoder = f.JSONEncoder()
|
||||||
o.printer, err = f.PrinterForCommand(cmd, printers.PrintOptions{})
|
o.printer, err = f.PrinterForCommand(cmd, nil, printers.PrintOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ package cmd
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -33,6 +34,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/kubectl"
|
"k8s.io/kubernetes/pkg/kubectl"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
|
||||||
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||||
"k8s.io/kubernetes/pkg/printers"
|
"k8s.io/kubernetes/pkg/printers"
|
||||||
"k8s.io/kubernetes/pkg/util/i18n"
|
"k8s.io/kubernetes/pkg/util/i18n"
|
||||||
@ -90,6 +92,10 @@ var (
|
|||||||
kubectl get all`))
|
kubectl get all`))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
useOpenAPIPrintColumnFlagLabel = "experimental-use-openapi-print-columns"
|
||||||
|
)
|
||||||
|
|
||||||
// NewCmdGet creates a command object for the generic "get" action, which
|
// NewCmdGet creates a command object for the generic "get" action, which
|
||||||
// retrieves one or more resources from a server.
|
// retrieves one or more resources from a server.
|
||||||
func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
|
func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Command {
|
||||||
@ -128,6 +134,7 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman
|
|||||||
cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", false, "Treat \"resource not found\" as a successful retrieval.")
|
cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", false, "Treat \"resource not found\" as a successful retrieval.")
|
||||||
cmd.Flags().StringSliceP("label-columns", "L", []string{}, "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag options like -L label1 -L label2...")
|
cmd.Flags().StringSliceP("label-columns", "L", []string{}, "Accepts a comma separated list of labels that are going to be presented as columns. Names are case-sensitive. You can also use multiple flag options like -L label1 -L label2...")
|
||||||
cmd.Flags().Bool("export", false, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.")
|
cmd.Flags().Bool("export", false, "If true, use 'export' for the resources. Exported resources are stripped of cluster-specific information.")
|
||||||
|
addOpenAPIPrintColumnFlags(cmd)
|
||||||
usage := "identifying the resource to get from a server."
|
usage := "identifying the resource to get from a server."
|
||||||
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
|
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
|
||||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||||
@ -219,7 +226,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||||||
}
|
}
|
||||||
info := infos[0]
|
info := infos[0]
|
||||||
mapping := info.ResourceMapping()
|
mapping := info.ResourceMapping()
|
||||||
printer, err := f.PrinterForMapping(cmd, mapping, allNamespaces)
|
printer, err := f.PrinterForMapping(cmd, nil, mapping, allNamespaces)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -299,7 +306,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
printer, err := f.PrinterForCommand(cmd, printers.PrintOptions{})
|
printer, err := f.PrinterForCommand(cmd, nil, printers.PrintOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -418,6 +425,8 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||||||
var lastMapping *meta.RESTMapping
|
var lastMapping *meta.RESTMapping
|
||||||
w := printers.GetNewTabWriter(out)
|
w := printers.GetNewTabWriter(out)
|
||||||
|
|
||||||
|
useOpenAPIPrintColumns := cmdutil.GetFlagBool(cmd, useOpenAPIPrintColumnFlagLabel)
|
||||||
|
|
||||||
if resource.MultipleTypesRequested(args) || cmdutil.MustPrintWithKinds(objs, infos, sorter) {
|
if resource.MultipleTypesRequested(args) || cmdutil.MustPrintWithKinds(objs, infos, sorter) {
|
||||||
showKind = true
|
showKind = true
|
||||||
}
|
}
|
||||||
@ -426,6 +435,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||||||
for ix := range objs {
|
for ix := range objs {
|
||||||
var mapping *meta.RESTMapping
|
var mapping *meta.RESTMapping
|
||||||
var original runtime.Object
|
var original runtime.Object
|
||||||
|
|
||||||
if sorter != nil {
|
if sorter != nil {
|
||||||
mapping = infos[sorter.OriginalPosition(ix)].Mapping
|
mapping = infos[sorter.OriginalPosition(ix)].Mapping
|
||||||
original = infos[sorter.OriginalPosition(ix)].Object
|
original = infos[sorter.OriginalPosition(ix)].Object
|
||||||
@ -437,7 +447,15 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||||||
if printer != nil {
|
if printer != nil {
|
||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
}
|
||||||
printer, err = f.PrinterForMapping(cmd, mapping, allNamespaces)
|
|
||||||
|
var outputOpts *printers.OutputOptions
|
||||||
|
// if cmd does not specify output format and useOpenAPIPrintColumnFlagLabel flag is true,
|
||||||
|
// then get the default output options for this mapping from OpenAPI schema.
|
||||||
|
if !cmdSpecifiesOutputFmt(cmd) && useOpenAPIPrintColumns {
|
||||||
|
outputOpts, _ = outputOptsForMappingFromOpenAPI(f, cmdutil.GetOpenAPICacheDir(cmd), mapping)
|
||||||
|
}
|
||||||
|
|
||||||
|
printer, err = f.PrinterForMapping(cmd, outputOpts, mapping, allNamespaces)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !errs.Has(err.Error()) {
|
if !errs.Has(err.Error()) {
|
||||||
errs.Insert(err.Error())
|
errs.Insert(err.Error())
|
||||||
@ -498,7 +516,13 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := printer.PrintObj(decodedObj, w); err != nil {
|
objToPrint := decodedObj
|
||||||
|
if printer.IsGeneric() {
|
||||||
|
// use raw object as recieved from the builder when using generic
|
||||||
|
// printer instead of decodedObj
|
||||||
|
objToPrint = original
|
||||||
|
}
|
||||||
|
if err := printer.PrintObj(objToPrint, w); err != nil {
|
||||||
if !errs.Has(err.Error()) {
|
if !errs.Has(err.Error()) {
|
||||||
errs.Insert(err.Error())
|
errs.Insert(err.Error())
|
||||||
allErrs = append(allErrs, err)
|
allErrs = append(allErrs, err)
|
||||||
@ -511,6 +535,59 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [
|
|||||||
return utilerrors.NewAggregate(allErrs)
|
return utilerrors.NewAggregate(allErrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func addOpenAPIPrintColumnFlags(cmd *cobra.Command) {
|
||||||
|
cmd.Flags().Bool(useOpenAPIPrintColumnFlagLabel, false, "If true, use x-kubernetes-print-column metadata (if present) from openapi schema for displaying a resource.")
|
||||||
|
// marking it deprecated so that it is hidden from usage/help text.
|
||||||
|
cmd.Flags().MarkDeprecated(useOpenAPIPrintColumnFlagLabel, "its an experimental feature.")
|
||||||
|
}
|
||||||
|
|
||||||
func shouldGetNewPrinterForMapping(printer printers.ResourcePrinter, lastMapping, mapping *meta.RESTMapping) bool {
|
func shouldGetNewPrinterForMapping(printer printers.ResourcePrinter, lastMapping, mapping *meta.RESTMapping) bool {
|
||||||
return printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource
|
return printer == nil || lastMapping == nil || mapping == nil || mapping.Resource != lastMapping.Resource
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cmdSpecifiesOutputFmt(cmd *cobra.Command) bool {
|
||||||
|
return cmdutil.GetFlagString(cmd, "output") != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// outputOptsForMappingFromOpenAPI looks for the output format metatadata in the
|
||||||
|
// openapi schema and returns the output options for the mapping if found.
|
||||||
|
func outputOptsForMappingFromOpenAPI(f cmdutil.Factory, openAPIcacheDir string, mapping *meta.RESTMapping) (*printers.OutputOptions, bool) {
|
||||||
|
|
||||||
|
// user has not specified any output format, check if OpenAPI has
|
||||||
|
// default specification to print this resource type
|
||||||
|
api, err := f.OpenAPISchema(openAPIcacheDir)
|
||||||
|
if err != nil {
|
||||||
|
// Error getting schema
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
// Found openapi metadata for this resource
|
||||||
|
kind, found := api.LookupResource(mapping.GroupVersionKind)
|
||||||
|
if !found {
|
||||||
|
// Kind not found, return empty columns
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
columns, found := openapi.GetPrintColumns(kind.Extensions)
|
||||||
|
if !found {
|
||||||
|
// Extension not found, return empty columns
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputOptsFromStr(columns)
|
||||||
|
}
|
||||||
|
|
||||||
|
// outputOptsFromStr parses the print-column metadata and generates printer.OutputOptions object.
|
||||||
|
func outputOptsFromStr(columnStr string) (*printers.OutputOptions, bool) {
|
||||||
|
if columnStr == "" {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(columnStr, "=", 2)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return &printers.OutputOptions{
|
||||||
|
FmtType: parts[0],
|
||||||
|
FmtArg: parts[1],
|
||||||
|
AllowMissingKeys: true,
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
@ -26,6 +26,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-openapi/spec"
|
||||||
|
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@ -42,6 +44,7 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/api/testapi"
|
"k8s.io/kubernetes/pkg/api/testapi"
|
||||||
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
||||||
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
|
||||||
|
"k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) {
|
func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) {
|
||||||
@ -185,6 +188,56 @@ func TestGetSchemaObject(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetObjectsWithOpenAPIOutputFormatPresent(t *testing.T) {
|
||||||
|
pods, _, _ := testData()
|
||||||
|
|
||||||
|
f, tf, codec, _ := cmdtesting.NewAPIFactory()
|
||||||
|
tf.Printer = &testPrinter{}
|
||||||
|
// overide the openAPISchema function to return custom output
|
||||||
|
// for Pod type.
|
||||||
|
tf.OpenAPISchemaFunc = testOpenAPISchemaData
|
||||||
|
tf.UnstructuredClient = &fake.RESTClient{
|
||||||
|
APIRegistry: api.Registry,
|
||||||
|
NegotiatedSerializer: unstructuredSerializer,
|
||||||
|
Resp: &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, &pods.Items[0])},
|
||||||
|
}
|
||||||
|
tf.Namespace = "test"
|
||||||
|
buf := bytes.NewBuffer([]byte{})
|
||||||
|
errBuf := bytes.NewBuffer([]byte{})
|
||||||
|
|
||||||
|
cmd := NewCmdGet(f, buf, errBuf)
|
||||||
|
cmd.SetOutput(buf)
|
||||||
|
cmd.Flags().Set(useOpenAPIPrintColumnFlagLabel, "true")
|
||||||
|
cmd.Run(cmd, []string{"pods", "foo"})
|
||||||
|
|
||||||
|
expected := []runtime.Object{&pods.Items[0]}
|
||||||
|
verifyObjects(t, expected, tf.Printer.(*testPrinter).Objects)
|
||||||
|
|
||||||
|
if len(buf.String()) == 0 {
|
||||||
|
t.Errorf("unexpected empty output")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOpenAPISchemaData() (*openapi.Resources, error) {
|
||||||
|
return &openapi.Resources{
|
||||||
|
GroupVersionKindToName: map[schema.GroupVersionKind]string{
|
||||||
|
{
|
||||||
|
Version: "v1",
|
||||||
|
Kind: "Pod",
|
||||||
|
}: "io.k8s.kubernetes.pkg.api.v1.Pod",
|
||||||
|
},
|
||||||
|
NameToDefinition: map[string]openapi.Kind{
|
||||||
|
"io.k8s.kubernetes.pkg.api.v1.Pod": {
|
||||||
|
Name: "io.k8s.kubernetes.pkg.api.v1.Pod",
|
||||||
|
IsResource: false,
|
||||||
|
Extensions: spec.Extensions{
|
||||||
|
"x-kubernetes-print-columns": "custom-columns=NAME:.metadata.name,RSRC:.metadata.resourceVersion",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetObjects(t *testing.T) {
|
func TestGetObjects(t *testing.T) {
|
||||||
pods, _, _ := testData()
|
pods, _, _ := testData()
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/emicklei/go-restful-swagger12"
|
swagger "github.com/emicklei/go-restful-swagger12"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/pflag"
|
"github.com/spf13/pflag"
|
||||||
|
|
||||||
@ -230,6 +230,7 @@ type TestFactory struct {
|
|||||||
|
|
||||||
ClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error)
|
ClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error)
|
||||||
UnstructuredClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error)
|
UnstructuredClientForMappingFunc func(mapping *meta.RESTMapping) (resource.RESTClient, error)
|
||||||
|
OpenAPISchemaFunc func() (*openapi.Resources, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type FakeFactory struct {
|
type FakeFactory struct {
|
||||||
@ -336,7 +337,7 @@ func (f *FakeFactory) Describer(*meta.RESTMapping) (printers.Describer, error) {
|
|||||||
return f.tf.Describer, f.tf.Err
|
return f.tf.Describer, f.tf.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeFactory) PrinterForCommand(cmd *cobra.Command, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
func (f *FakeFactory) PrinterForCommand(cmd *cobra.Command, outputOpts *printers.OutputOptions, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
||||||
return f.tf.Printer, f.tf.Err
|
return f.tf.Printer, f.tf.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,7 +461,7 @@ func (f *FakeFactory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, ob
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *FakeFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) {
|
func (f *FakeFactory) PrinterForMapping(cmd *cobra.Command, outputOpts *printers.OutputOptions, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) {
|
||||||
return f.tf.Printer, f.tf.Err
|
return f.tf.Printer, f.tf.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -617,7 +618,7 @@ func (f *fakeAPIFactory) UnstructuredClientForMapping(m *meta.RESTMapping) (reso
|
|||||||
return f.tf.UnstructuredClient, f.tf.Err
|
return f.tf.UnstructuredClient, f.tf.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeAPIFactory) PrinterForCommand(cmd *cobra.Command, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
func (f *fakeAPIFactory) PrinterForCommand(cmd *cobra.Command, outputOpts *printers.OutputOptions, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
||||||
return f.tf.Printer, f.tf.Err
|
return f.tf.Printer, f.tf.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,14 +692,14 @@ func (f *fakeAPIFactory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper,
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
printer, err := f.PrinterForMapping(cmd, mapping, false)
|
printer, err := f.PrinterForMapping(cmd, nil, mapping, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return printer.PrintObj(obj, out)
|
return printer.PrintObj(obj, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakeAPIFactory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) {
|
func (f *fakeAPIFactory) PrinterForMapping(cmd *cobra.Command, outputOpts *printers.OutputOptions, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) {
|
||||||
return f.tf.Printer, f.tf.Err
|
return f.tf.Printer, f.tf.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -712,6 +713,17 @@ func (f *fakeAPIFactory) SuggestedPodTemplateResources() []schema.GroupResource
|
|||||||
return []schema.GroupResource{}
|
return []schema.GroupResource{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fakeAPIFactory) SwaggerSchema(schema.GroupVersionKind) (*swagger.ApiDeclaration, error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeAPIFactory) OpenAPISchema(cacheDir string) (*openapi.Resources, error) {
|
||||||
|
if f.tf.OpenAPISchemaFunc != nil {
|
||||||
|
return f.tf.OpenAPISchemaFunc()
|
||||||
|
}
|
||||||
|
return &openapi.Resources{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func NewAPIFactory() (cmdutil.Factory, *TestFactory, runtime.Codec, runtime.NegotiatedSerializer) {
|
func NewAPIFactory() (cmdutil.Factory, *TestFactory, runtime.Codec, runtime.NegotiatedSerializer) {
|
||||||
t := &TestFactory{
|
t := &TestFactory{
|
||||||
Validator: validation.NullSchema{},
|
Validator: validation.NullSchema{},
|
||||||
|
@ -233,10 +233,12 @@ type BuilderFactory interface {
|
|||||||
// are declared on the command (see AddPrinterFlags). Returns a printer, or an error if a printer
|
// are declared on the command (see AddPrinterFlags). Returns a printer, or an error if a printer
|
||||||
// could not be found.
|
// could not be found.
|
||||||
// TODO: Break the dependency on cmd here.
|
// TODO: Break the dependency on cmd here.
|
||||||
PrinterForCommand(cmd *cobra.Command, options printers.PrintOptions) (printers.ResourcePrinter, error)
|
PrinterForCommand(cmd *cobra.Command, outputOpts *printers.OutputOptions, options printers.PrintOptions) (printers.ResourcePrinter, error)
|
||||||
// PrinterForMapping returns a printer suitable for displaying the provided resource type.
|
// PrinterForMapping returns a printer suitable for displaying the provided resource type.
|
||||||
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
|
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
|
||||||
PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error)
|
// Returns a printer, true if the printer is generic (is not internal), or
|
||||||
|
// an error if a printer could not be found.
|
||||||
|
PrinterForMapping(cmd *cobra.Command, outputOpts *printers.OutputOptions, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error)
|
||||||
// PrintObject prints an api object given command line flags to modify the output format
|
// 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
|
PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, obj runtime.Object, out io.Writer) error
|
||||||
// One stop shopping for a Builder
|
// One stop shopping for a Builder
|
||||||
|
@ -48,7 +48,7 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac
|
|||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command, outputOpts *printers.OutputOptions, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
||||||
mapper, typer, err := f.objectMappingFactory.UnstructuredObject()
|
mapper, typer, err := f.objectMappingFactory.UnstructuredObject()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -56,10 +56,10 @@ func (f *ring2Factory) PrinterForCommand(cmd *cobra.Command, options printers.Pr
|
|||||||
// TODO: used by the custom column implementation and the name implementation, break this dependency
|
// TODO: used by the custom column implementation and the name implementation, break this dependency
|
||||||
decoders := []runtime.Decoder{f.clientAccessFactory.Decoder(true), unstructured.UnstructuredJSONScheme}
|
decoders := []runtime.Decoder{f.clientAccessFactory.Decoder(true), unstructured.UnstructuredJSONScheme}
|
||||||
encoder := f.clientAccessFactory.JSONEncoder()
|
encoder := f.clientAccessFactory.JSONEncoder()
|
||||||
return PrinterForCommand(cmd, mapper, typer, encoder, decoders, options)
|
return PrinterForCommand(cmd, outputOpts, mapper, typer, encoder, decoders, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *ring2Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) {
|
func (f *ring2Factory) PrinterForMapping(cmd *cobra.Command, outputOpts *printers.OutputOptions, mapping *meta.RESTMapping, withNamespace bool) (printers.ResourcePrinter, error) {
|
||||||
// Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper
|
// Some callers do not have "label-columns" so we can't use the GetFlagStringSlice() helper
|
||||||
columnLabel, err := cmd.Flags().GetStringSlice("label-columns")
|
columnLabel, err := cmd.Flags().GetStringSlice("label-columns")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -76,7 +76,7 @@ func (f *ring2Factory) PrinterForMapping(cmd *cobra.Command, mapping *meta.RESTM
|
|||||||
ColumnLabels: columnLabel,
|
ColumnLabels: columnLabel,
|
||||||
}
|
}
|
||||||
|
|
||||||
printer, err := f.PrinterForCommand(cmd, options)
|
printer, err := f.PrinterForCommand(cmd, outputOpts, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -132,7 +132,7 @@ func (f *ring2Factory) PrintObject(cmd *cobra.Command, mapper meta.RESTMapper, o
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
printer, err := f.PrinterForMapping(cmd, mapping, false)
|
printer, err := f.PrinterForMapping(cmd, nil, mapping, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/emicklei/go-restful-swagger12"
|
swagger "github.com/emicklei/go-restful-swagger12"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
@ -417,6 +417,10 @@ func AddOpenAPIFlags(cmd *cobra.Command) {
|
|||||||
cmd.MarkFlagFilename("schema-cache-dir")
|
cmd.MarkFlagFilename("schema-cache-dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetOpenAPICacheDir(cmd *cobra.Command) string {
|
||||||
|
return GetFlagString(cmd, "schema-cache-dir")
|
||||||
|
}
|
||||||
|
|
||||||
func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) {
|
func AddFilenameOptionFlags(cmd *cobra.Command, options *resource.FilenameOptions, usage string) {
|
||||||
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, "Filename, directory, or URL to files "+usage)
|
kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, "Filename, directory, or URL to files "+usage)
|
||||||
cmd.Flags().BoolVarP(&options.Recursive, "recursive", "R", options.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.")
|
cmd.Flags().BoolVarP(&options.Recursive, "recursive", "R", options.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.")
|
||||||
|
@ -12,6 +12,7 @@ go_library(
|
|||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
"doc.go",
|
"doc.go",
|
||||||
|
"extensions.go",
|
||||||
"openapi.go",
|
"openapi.go",
|
||||||
"openapi_cache.go",
|
"openapi_cache.go",
|
||||||
"openapi_getter.go",
|
"openapi_getter.go",
|
||||||
|
26
pkg/kubectl/cmd/util/openapi/extensions.go
Normal file
26
pkg/kubectl/cmd/util/openapi/extensions.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
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 openapi
|
||||||
|
|
||||||
|
import "github.com/go-openapi/spec"
|
||||||
|
|
||||||
|
const PrintColumnsKey = "x-kubernetes-print-columns"
|
||||||
|
|
||||||
|
// GetPrintColumns looks for the open API extension for the display columns.
|
||||||
|
func GetPrintColumns(extensions spec.Extensions) (string, bool) {
|
||||||
|
return extensions.GetString(PrintColumnsKey)
|
||||||
|
}
|
@ -194,7 +194,7 @@ func (o *Resources) parseDefinition(name string, s spec.Schema) Kind {
|
|||||||
Fields: map[string]Type{},
|
Fields: map[string]Type{},
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Warning(err)
|
glog.V(2).Info(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Definition represents a primitive type - e.g.
|
// Definition represents a primitive type - e.g.
|
||||||
|
@ -105,38 +105,18 @@ func ValidateOutputArgs(cmd *cobra.Command) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrinterForCommand returns the default printer for this command.
|
// PrinterForCommand returns the printer for the outputOptions (if given) or
|
||||||
// Requires that printer flags have been added to cmd (see AddPrinterFlags).
|
// returns the default printer for the command. Requires that printer flags have
|
||||||
func PrinterForCommand(cmd *cobra.Command, mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
// been added to cmd (see AddPrinterFlags).
|
||||||
outputFormat := GetFlagString(cmd, "output")
|
// TODO: remove the dependency on cmd object
|
||||||
|
func PrinterForCommand(cmd *cobra.Command, outputOpts *printers.OutputOptions, mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options printers.PrintOptions) (printers.ResourcePrinter, error) {
|
||||||
|
|
||||||
// templates are logically optional for specifying a format.
|
if outputOpts == nil {
|
||||||
// TODO once https://github.com/kubernetes/kubernetes/issues/12668 is fixed, this should fall back to GetFlagString
|
outputOpts = extractOutputOptions(cmd)
|
||||||
templateFile, _ := cmd.Flags().GetString("template")
|
|
||||||
if len(outputFormat) == 0 && len(templateFile) != 0 {
|
|
||||||
outputFormat = "template"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
templateFormat := []string{
|
printer, err := printers.GetStandardPrinter(outputOpts,
|
||||||
"go-template=", "go-template-file=", "jsonpath=", "jsonpath-file=", "custom-columns=", "custom-columns-file=",
|
GetFlagBool(cmd, "no-headers"), mapper, typer, encoder, decoders, options)
|
||||||
}
|
|
||||||
for _, format := range templateFormat {
|
|
||||||
if strings.HasPrefix(outputFormat, format) {
|
|
||||||
templateFile = outputFormat[len(format):]
|
|
||||||
outputFormat = format[:len(format)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// this function may be invoked by a command that did not call AddPrinterFlags first, so we need
|
|
||||||
// to be safe about how we access the allow-missing-template-keys flag
|
|
||||||
allowMissingTemplateKeys := false
|
|
||||||
if cmd.Flags().Lookup("allow-missing-template-keys") != nil {
|
|
||||||
allowMissingTemplateKeys = GetFlagBool(cmd, "allow-missing-template-keys")
|
|
||||||
}
|
|
||||||
printer, err := printers.GetStandardPrinter(
|
|
||||||
outputFormat, templateFile, GetFlagBool(cmd, "no-headers"), allowMissingTemplateKeys,
|
|
||||||
mapper, typer, encoder, decoders, options,
|
|
||||||
)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -149,12 +129,12 @@ func PrinterForCommand(cmd *cobra.Command, mapper meta.RESTMapper, typer runtime
|
|||||||
// object passed is non-generic, it attempts to print the object using a HumanReadablePrinter.
|
// 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).
|
// 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 {
|
func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Factory, out io.Writer) error {
|
||||||
printer, err := f.PrinterForCommand(cmd, printers.PrintOptions{})
|
printer, err := f.PrinterForCommand(cmd, nil, printers.PrintOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !printer.IsGeneric() {
|
if !printer.IsGeneric() {
|
||||||
printer, err = f.PrinterForMapping(cmd, nil, false)
|
printer, err = f.PrinterForMapping(cmd, nil, nil, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -162,6 +142,50 @@ func PrintResourceInfoForCommand(cmd *cobra.Command, info *resource.Info, f Fact
|
|||||||
return printer.PrintObj(info.Object, out)
|
return printer.PrintObj(info.Object, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// extractOutputOptions parses printer specific commandline args and returns
|
||||||
|
// printers.OutputsOptions object.
|
||||||
|
func extractOutputOptions(cmd *cobra.Command) *printers.OutputOptions {
|
||||||
|
flags := cmd.Flags()
|
||||||
|
|
||||||
|
var outputFormat string
|
||||||
|
if flags.Lookup("output") != nil {
|
||||||
|
outputFormat = GetFlagString(cmd, "output")
|
||||||
|
}
|
||||||
|
|
||||||
|
// templates are logically optional for specifying a format.
|
||||||
|
// TODO once https://github.com/kubernetes/kubernetes/issues/12668 is fixed, this should fall back to GetFlagString
|
||||||
|
var templateFile string
|
||||||
|
if flags.Lookup("template") != nil {
|
||||||
|
templateFile = GetFlagString(cmd, "template")
|
||||||
|
}
|
||||||
|
if len(outputFormat) == 0 && len(templateFile) != 0 {
|
||||||
|
outputFormat = "template"
|
||||||
|
}
|
||||||
|
|
||||||
|
templateFormats := []string{
|
||||||
|
"go-template=", "go-template-file=", "jsonpath=", "jsonpath-file=", "custom-columns=", "custom-columns-file=",
|
||||||
|
}
|
||||||
|
for _, format := range templateFormats {
|
||||||
|
if strings.HasPrefix(outputFormat, format) {
|
||||||
|
templateFile = outputFormat[len(format):]
|
||||||
|
outputFormat = format[:len(format)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this function may be invoked by a command that did not call AddPrinterFlags first, so we need
|
||||||
|
// to be safe about how we access the allow-missing-template-keys flag
|
||||||
|
allowMissingTemplateKeys := false
|
||||||
|
if flags.Lookup("allow-missing-template-keys") != nil {
|
||||||
|
allowMissingTemplateKeys = GetFlagBool(cmd, "allow-missing-template-keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &printers.OutputOptions{
|
||||||
|
FmtType: outputFormat,
|
||||||
|
FmtArg: templateFile,
|
||||||
|
AllowMissingKeys: allowMissingTemplateKeys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func maybeWrapSortingPrinter(cmd *cobra.Command, printer printers.ResourcePrinter) printers.ResourcePrinter {
|
func maybeWrapSortingPrinter(cmd *cobra.Command, printer printers.ResourcePrinter) printers.ResourcePrinter {
|
||||||
sorting, err := cmd.Flags().GetString("sort-by")
|
sorting, err := cmd.Flags().GetString("sort-by")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -153,6 +153,9 @@ type CustomColumnsPrinter struct {
|
|||||||
Columns []Column
|
Columns []Column
|
||||||
Decoder runtime.Decoder
|
Decoder runtime.Decoder
|
||||||
NoHeaders bool
|
NoHeaders bool
|
||||||
|
// lastType records type of resource printed last so that we don't repeat
|
||||||
|
// header while printing same type of resources.
|
||||||
|
lastType reflect.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *CustomColumnsPrinter) AfterPrint(w io.Writer, res string) error {
|
func (s *CustomColumnsPrinter) AfterPrint(w io.Writer, res string) error {
|
||||||
@ -162,12 +165,14 @@ func (s *CustomColumnsPrinter) AfterPrint(w io.Writer, res string) error {
|
|||||||
func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
func (s *CustomColumnsPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
|
||||||
w := tabwriter.NewWriter(out, columnwidth, tabwidth, padding, padding_character, flags)
|
w := tabwriter.NewWriter(out, columnwidth, tabwidth, padding, padding_character, flags)
|
||||||
|
|
||||||
if !s.NoHeaders {
|
t := reflect.TypeOf(obj)
|
||||||
|
if !s.NoHeaders && t != s.lastType {
|
||||||
headers := make([]string, len(s.Columns))
|
headers := make([]string, len(s.Columns))
|
||||||
for ix := range s.Columns {
|
for ix := range s.Columns {
|
||||||
headers[ix] = s.Columns[ix].Header
|
headers[ix] = s.Columns[ix].Header
|
||||||
}
|
}
|
||||||
fmt.Fprintln(w, strings.Join(headers, "\t"))
|
fmt.Fprintln(w, strings.Join(headers, "\t"))
|
||||||
|
s.lastType = t
|
||||||
}
|
}
|
||||||
parsers := make([]*jsonpath.JSONPath, len(s.Columns))
|
parsers := make([]*jsonpath.JSONPath, len(s.Columns))
|
||||||
for ix := range s.Columns {
|
for ix := range s.Columns {
|
||||||
|
@ -100,3 +100,13 @@ type ErrNoDescriber struct {
|
|||||||
func (e ErrNoDescriber) Error() string {
|
func (e ErrNoDescriber) Error() string {
|
||||||
return fmt.Sprintf("no describer has been defined for %v", e.Types)
|
return fmt.Sprintf("no describer has been defined for %v", e.Types)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OutputOptions represents resource output options which is used to generate a resource printer.
|
||||||
|
type OutputOptions struct {
|
||||||
|
// supported Format types can be found in pkg/printers/printers.go
|
||||||
|
FmtType string
|
||||||
|
FmtArg string
|
||||||
|
|
||||||
|
// indicates if it is OK to ignore missing keys for rendering an output template.
|
||||||
|
AllowMissingKeys bool
|
||||||
|
}
|
||||||
|
@ -93,7 +93,7 @@ func TestPrintDefault(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range printerTests {
|
for _, test := range printerTests {
|
||||||
printer, err := printers.GetStandardPrinter(test.Format, "", false, false, nil, nil, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
printer, err := printers.GetStandardPrinter(&printers.OutputOptions{AllowMissingKeys: false}, false, nil, nil, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
||||||
}
|
}
|
||||||
@ -131,25 +131,24 @@ func TestPrinter(t *testing.T) {
|
|||||||
|
|
||||||
printerTests := []struct {
|
printerTests := []struct {
|
||||||
Name string
|
Name string
|
||||||
Format string
|
OutputOpts *printers.OutputOptions
|
||||||
FormatArgument string
|
|
||||||
Input runtime.Object
|
Input runtime.Object
|
||||||
OutputVersions []schema.GroupVersion
|
OutputVersions []schema.GroupVersion
|
||||||
Expect string
|
Expect string
|
||||||
}{
|
}{
|
||||||
{"test json", "json", "", simpleTest, nil, "{\n \"Data\": \"foo\"\n}\n"},
|
{"test json", &printers.OutputOptions{FmtType: "json", AllowMissingKeys: true}, simpleTest, nil, "{\n \"Data\": \"foo\"\n}\n"},
|
||||||
{"test yaml", "yaml", "", simpleTest, nil, "Data: foo\n"},
|
{"test yaml", &printers.OutputOptions{FmtType: "yaml", AllowMissingKeys: true}, simpleTest, nil, "Data: foo\n"},
|
||||||
{"test template", "template", "{{if .id}}{{.id}}{{end}}{{if .metadata.name}}{{.metadata.name}}{{end}}",
|
{"test template", &printers.OutputOptions{FmtType: "template", FmtArg: "{{if .id}}{{.id}}{{end}}{{if .metadata.name}}{{.metadata.name}}{{end}}", AllowMissingKeys: true},
|
||||||
podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"},
|
podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"},
|
||||||
{"test jsonpath", "jsonpath", "{.metadata.name}", podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"},
|
{"test jsonpath", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.metadata.name}", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo"},
|
||||||
{"test jsonpath list", "jsonpath", "{.items[*].metadata.name}", podListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo bar"},
|
{"test jsonpath list", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.items[*].metadata.name}", AllowMissingKeys: true}, podListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "foo bar"},
|
||||||
{"test jsonpath empty list", "jsonpath", "{.items[*].metadata.name}", emptyListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, ""},
|
{"test jsonpath empty list", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.items[*].metadata.name}", AllowMissingKeys: true}, emptyListTest, []schema.GroupVersion{v1.SchemeGroupVersion}, ""},
|
||||||
{"test name", "name", "", podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "pods/foo\n"},
|
{"test name", &printers.OutputOptions{FmtType: "name", AllowMissingKeys: true}, podTest, []schema.GroupVersion{v1.SchemeGroupVersion}, "pods/foo\n"},
|
||||||
{"emits versioned objects", "template", "{{.kind}}", testapi, []schema.GroupVersion{v1.SchemeGroupVersion}, "Pod"},
|
{"emits versioned objects", &printers.OutputOptions{FmtType: "template", FmtArg: "{{.kind}}", AllowMissingKeys: true}, testapi, []schema.GroupVersion{v1.SchemeGroupVersion}, "Pod"},
|
||||||
}
|
}
|
||||||
for _, test := range printerTests {
|
for _, test := range printerTests {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
printer, err := printers.GetStandardPrinter(test.Format, test.FormatArgument, false, true, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
printer, err := printers.GetStandardPrinter(test.OutputOpts, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
||||||
}
|
}
|
||||||
@ -168,19 +167,18 @@ func TestPrinter(t *testing.T) {
|
|||||||
|
|
||||||
func TestBadPrinter(t *testing.T) {
|
func TestBadPrinter(t *testing.T) {
|
||||||
badPrinterTests := []struct {
|
badPrinterTests := []struct {
|
||||||
Name string
|
Name string
|
||||||
Format string
|
OutputOpts *printers.OutputOptions
|
||||||
FormatArgument string
|
Error error
|
||||||
Error error
|
|
||||||
}{
|
}{
|
||||||
{"empty template", "template", "", fmt.Errorf("template format specified but no template given")},
|
{"empty template", &printers.OutputOptions{FmtType: "template", AllowMissingKeys: false}, fmt.Errorf("template format specified but no template given")},
|
||||||
{"bad template", "template", "{{ .Name", fmt.Errorf("error parsing template {{ .Name, template: output:1: unclosed action\n")},
|
{"bad template", &printers.OutputOptions{FmtType: "template", FmtArg: "{{ .Name", AllowMissingKeys: false}, fmt.Errorf("error parsing template {{ .Name, template: output:1: unclosed action\n")},
|
||||||
{"bad templatefile", "templatefile", "", fmt.Errorf("templatefile format specified but no template file given")},
|
{"bad templatefile", &printers.OutputOptions{FmtType: "templatefile", AllowMissingKeys: false}, fmt.Errorf("templatefile format specified but no template file given")},
|
||||||
{"bad jsonpath", "jsonpath", "{.Name", fmt.Errorf("error parsing jsonpath {.Name, unclosed action\n")},
|
{"bad jsonpath", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.Name", AllowMissingKeys: false}, fmt.Errorf("error parsing jsonpath {.Name, unclosed action\n")},
|
||||||
{"unknown format", "anUnknownFormat", "", fmt.Errorf("output format \"anUnknownFormat\" not recognized")},
|
{"unknown format", &printers.OutputOptions{FmtType: "anUnknownFormat", FmtArg: "", AllowMissingKeys: false}, fmt.Errorf("output format \"anUnknownFormat\" not recognized")},
|
||||||
}
|
}
|
||||||
for _, test := range badPrinterTests {
|
for _, test := range badPrinterTests {
|
||||||
_, err := printers.GetStandardPrinter(test.Format, test.FormatArgument, false, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
_, err := printers.GetStandardPrinter(test.OutputOpts, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
||||||
if err == nil || err.Error() != test.Error.Error() {
|
if err == nil || err.Error() != test.Error.Error() {
|
||||||
t.Errorf("in %s, expect %s, got %s", test.Name, test.Error, err)
|
t.Errorf("in %s, expect %s, got %s", test.Name, test.Error, err)
|
||||||
}
|
}
|
||||||
@ -373,7 +371,8 @@ func TestNamePrinter(t *testing.T) {
|
|||||||
},
|
},
|
||||||
"pods/foo\npods/bar\n"},
|
"pods/foo\npods/bar\n"},
|
||||||
}
|
}
|
||||||
printer, _ := printers.GetStandardPrinter("name", "", false, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
outputOpts := &printers.OutputOptions{FmtType: "name", AllowMissingKeys: false}
|
||||||
|
printer, _ := printers.GetStandardPrinter(outputOpts, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
||||||
for name, item := range tests {
|
for name, item := range tests {
|
||||||
buff := &bytes.Buffer{}
|
buff := &bytes.Buffer{}
|
||||||
err := printer.PrintObj(item.obj, buff)
|
err := printer.PrintObj(item.obj, buff)
|
||||||
@ -2309,22 +2308,20 @@ func TestPrintPodDisruptionBudget(t *testing.T) {
|
|||||||
|
|
||||||
func TestAllowMissingKeys(t *testing.T) {
|
func TestAllowMissingKeys(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
Name string
|
Name string
|
||||||
AllowMissingTemplateKeys bool
|
OutputOpts *printers.OutputOptions
|
||||||
Format string
|
Input runtime.Object
|
||||||
Template string
|
Expect string
|
||||||
Input runtime.Object
|
Error string
|
||||||
Expect string
|
|
||||||
Error string
|
|
||||||
}{
|
}{
|
||||||
{"test template, allow missing keys", true, "template", "{{.blarg}}", &api.Pod{}, "<no value>", ""},
|
{"test template, allow missing keys", &printers.OutputOptions{FmtType: "template", FmtArg: "{{.blarg}}", AllowMissingKeys: true}, &api.Pod{}, "<no value>", ""},
|
||||||
{"test template, strict", false, "template", "{{.blarg}}", &api.Pod{}, "", `error executing template "{{.blarg}}": template: output:1:2: executing "output" at <.blarg>: map has no entry for key "blarg"`},
|
{"test template, strict", &printers.OutputOptions{FmtType: "template", FmtArg: "{{.blarg}}", AllowMissingKeys: false}, &api.Pod{}, "", `error executing template "{{.blarg}}": template: output:1:2: executing "output" at <.blarg>: map has no entry for key "blarg"`},
|
||||||
{"test jsonpath, allow missing keys", true, "jsonpath", "{.blarg}", &api.Pod{}, "", ""},
|
{"test jsonpath, allow missing keys", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.blarg}", AllowMissingKeys: true}, &api.Pod{}, "", ""},
|
||||||
{"test jsonpath, strict", false, "jsonpath", "{.blarg}", &api.Pod{}, "", "error executing jsonpath \"{.blarg}\": blarg is not found\n"},
|
{"test jsonpath, strict", &printers.OutputOptions{FmtType: "jsonpath", FmtArg: "{.blarg}", AllowMissingKeys: false}, &api.Pod{}, "", "error executing jsonpath \"{.blarg}\": blarg is not found\n"},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
buf := bytes.NewBuffer([]byte{})
|
buf := bytes.NewBuffer([]byte{})
|
||||||
printer, err := printers.GetStandardPrinter(test.Format, test.Template, false, test.AllowMissingTemplateKeys, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
printer, err := printers.GetStandardPrinter(test.OutputOpts, false, api.Registry.RESTMapper(api.Registry.EnabledVersions()...), api.Scheme, api.Codecs.LegacyCodec(api.Registry.EnabledVersions()...), []runtime.Decoder{api.Codecs.UniversalDecoder(), unstructured.UnstructuredJSONScheme}, printers.PrintOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
t.Errorf("in %s, unexpected error: %#v", test.Name, err)
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,13 @@ import (
|
|||||||
// a printer or an error. The printer is agnostic to schema versions, so you must
|
// a printer or an error. 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
|
// send arguments to PrintObj in the version you wish them to be shown using a
|
||||||
// VersionedPrinter (typically when generic is true).
|
// VersionedPrinter (typically when generic is true).
|
||||||
func GetStandardPrinter(format, formatArgument string, noHeaders, allowMissingTemplateKeys bool, mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options PrintOptions) (ResourcePrinter, error) {
|
func GetStandardPrinter(outputOpts *OutputOptions, noHeaders bool, mapper meta.RESTMapper, typer runtime.ObjectTyper, encoder runtime.Encoder, decoders []runtime.Decoder, options PrintOptions) (ResourcePrinter, error) {
|
||||||
|
if outputOpts == nil {
|
||||||
|
return nil, fmt.Errorf("no output options specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
format, formatArgument, allowMissingTemplateKeys := outputOpts.FmtType, outputOpts.FmtArg, outputOpts.AllowMissingKeys
|
||||||
|
|
||||||
var printer ResourcePrinter
|
var printer ResourcePrinter
|
||||||
switch format {
|
switch format {
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user