Unverified Commit acbccfba authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #62820 from juanvallejo/jvallejo/wire-more-print-flags

Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. wire printflags through additional cmds **Release note**: ```release-note NONE ``` Adds PrintFlag pattern to more commands. cc @deads2k @soltysh
parents 30e811c9 ac6ca38d
......@@ -693,7 +693,7 @@ run_pod_tests() {
kube::test::get_object_assert pods "{{range.items}}{{$id_field}}:{{end}}" 'valid-pod:'
## Patch can modify a local object
kubectl patch --local -f pkg/kubectl/validation/testdata/v1/validPod.yaml --patch='{"spec": {"restartPolicy":"Never"}}' -o jsonpath='{.spec.restartPolicy}' | grep -q "Never"
kubectl patch --local -f pkg/kubectl/validation/testdata/v1/validPod.yaml --patch='{"spec": {"restartPolicy":"Never"}}' -o yaml | grep -q "Never"
## Patch fails with error message "not patched" and exit code 1
output_message=$(! kubectl patch "${kube_flags[@]}" pod valid-pod -p='{"spec":{"replicas":7}}' 2>&1)
......@@ -2139,8 +2139,7 @@ run_recursive_resources_tests() {
kube::test::get_object_assert deployment "{{range.items}}{{$id_field}}:{{end}}" 'nginx:'
kube::test::get_object_assert deployment "{{range.items}}{{$image_field0}}:{{end}}" "${IMAGE_DEPLOYMENT_R1}:"
# Command
output_message=$(kubectl convert --local -f hack/testdata/deployment-revision1.yaml --output-version=apps/v1beta1 -o go-template='{{ .apiVersion }}' "${kube_flags[@]}")
echo $output_message
output_message=$(kubectl convert --local -f hack/testdata/deployment-revision1.yaml --output-version=apps/v1beta1 -o yaml "${kube_flags[@]}")
# Post-condition: apiVersion is still extensions/v1beta1 in the live deployment, but command output is the new value
kube::test::get_object_assert 'deployment nginx' "{{ .apiVersion }}" 'extensions/v1beta1'
kube::test::if_has_string "${output_message}" "apps/v1beta1"
......@@ -2843,7 +2842,7 @@ run_rc_tests() {
# Pre-condition: default run without --name flag; should succeed by truncating the inherited name
output_message=$(kubectl expose -f hack/testdata/pod-with-large-name.yaml --port=8081 2>&1 "${kube_flags[@]}")
# Post-condition: inherited name from pod has been truncated
kube::test::if_has_string "${output_message}" '\"kubernetes-serve-hostname-testing-sixty-three-characters-in-len\" exposed'
kube::test::if_has_string "${output_message}" 'kubernetes-serve-hostname-testing-sixty-three-characters-in-len exposed'
# Clean-up
kubectl delete svc kubernetes-serve-hostname-testing-sixty-three-characters-in-len "${kube_flags[@]}"
......@@ -2851,7 +2850,7 @@ run_rc_tests() {
# Pre-condition: don't use --port flag
output_message=$(kubectl expose -f test/fixtures/doc-yaml/admin/high-availability/etcd.yaml --selector=test=etcd 2>&1 "${kube_flags[@]}")
# Post-condition: expose succeeded
kube::test::if_has_string "${output_message}" '\"etcd-server\" exposed'
kube::test::if_has_string "${output_message}" 'etcd-server exposed'
# Post-condition: generated service has both ports from the exposed pod
kube::test::get_object_assert 'service etcd-server' "{{$port_name}} {{$port_field}}" 'port-1 2380'
kube::test::get_object_assert 'service etcd-server' "{{$second_port_name}} {{$second_port_field}}" 'port-2 2379'
......@@ -4647,7 +4646,7 @@ __EOF__
kube::test::if_has_string "${response}" 'must provide one or more resources'
# test=label matches our node
response=$(kubectl cordon --selector test=label)
kube::test::if_has_string "${response}" 'node "127.0.0.1" cordoned'
kube::test::if_has_string "${response}" 'node/127.0.0.1 cordoned'
# invalid=label does not match any nodes
response=$(kubectl cordon --selector invalid=label)
kube::test::if_has_not_string "${response}" 'cordoned'
......
......@@ -19,6 +19,7 @@ package cmd
import (
"bytes"
"fmt"
"io"
jsonpatch "github.com/evanphx/json-patch"
"github.com/golang/glog"
......@@ -29,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/kubernetes/pkg/printers"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
......@@ -40,6 +42,9 @@ import (
// AnnotateOptions have the data required to perform the annotate operation
type AnnotateOptions struct {
PrintFlags *printers.PrintFlags
PrintObj printers.ResourcePrinterFunc
// Filename options
resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags
......@@ -99,10 +104,11 @@ var (
func NewAnnotateOptions(ioStreams genericclioptions.IOStreams) *AnnotateOptions {
return &AnnotateOptions{
RecordFlags: genericclioptions.NewRecordFlags(),
PrintFlags: printers.NewPrintFlags("annotated"),
Recorder: genericclioptions.NoopRecorder{},
IOStreams: ioStreams,
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
IOStreams: ioStreams,
}
}
......@@ -131,8 +137,8 @@ func NewCmdAnnotate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *c
// bind flag structs
o.RecordFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
cmdutil.AddPrinterFlags(cmd)
cmdutil.AddIncludeUninitializedFlag(cmd)
cmd.Flags().BoolVar(&o.overwrite, "overwrite", o.overwrite, "If true, allow annotations to be overwritten, otherwise reject annotation updates that overwrite existing annotations.")
cmd.Flags().BoolVar(&o.local, "local", o.local, "If true, annotation will NOT contact api-server but run locally.")
......@@ -159,6 +165,17 @@ func (o *AnnotateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [
o.outputFormat = cmdutil.GetFlagString(cmd, "output")
o.dryrun = cmdutil.GetDryRunFlag(cmd)
if o.dryrun {
o.PrintFlags.Complete("%s (dry run)")
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object, out io.Writer) error {
return printer.PrintObj(obj, out)
}
// retrieves resource and annotation args from args
// also checks args to verify that all resources are specified before annotations
resources, annotationArgs, err := cmdutil.GetResourcesAndPairs(args, "annotation")
......@@ -280,11 +297,7 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro
}
}
if len(o.outputFormat) > 0 {
return cmdutil.PrintObject(cmd, outputObj, o.Out)
}
cmdutil.PrintSuccess(false, o.Out, info.Object, o.dryrun, "annotated")
return nil
return o.PrintObj(outputObj, o.Out)
})
}
......
......@@ -21,16 +21,20 @@ import (
"io"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/kubernetes/pkg/apis/certificates"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
"github.com/spf13/cobra"
)
func NewCmdCertificate(f cmdutil.Factory, out io.Writer) *cobra.Command {
func NewCmdCertificate(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
cmd := &cobra.Command{
Use: "certificate SUBCOMMAND",
DisableFlagsInUseLine: true,
......@@ -41,33 +45,61 @@ func NewCmdCertificate(f cmdutil.Factory, out io.Writer) *cobra.Command {
},
}
cmd.AddCommand(NewCmdCertificateApprove(f, out))
cmd.AddCommand(NewCmdCertificateDeny(f, out))
cmd.AddCommand(NewCmdCertificateApprove(f, ioStreams))
cmd.AddCommand(NewCmdCertificateDeny(f, ioStreams))
return cmd
}
type CertificateOptions struct {
resource.FilenameOptions
PrintFlags *printers.PrintFlags
PrintObj printers.ResourcePrinterFunc
csrNames []string
outputStyle string
clientSet internalclientset.Interface
builder *resource.Builder
genericclioptions.IOStreams
}
func (options *CertificateOptions) Complete(cmd *cobra.Command, args []string) error {
options.csrNames = args
options.outputStyle = cmdutil.GetFlagString(cmd, "output")
func (o *CertificateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
o.csrNames = args
o.outputStyle = cmdutil.GetFlagString(cmd, "output")
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object, out io.Writer) error {
return printer.PrintObj(obj, out)
}
o.builder = f.NewBuilder()
o.clientSet, err = f.ClientSet()
if err != nil {
return err
}
return nil
}
func (options *CertificateOptions) Validate() error {
if len(options.csrNames) < 1 && cmdutil.IsFilenameSliceEmpty(options.Filenames) {
func (o *CertificateOptions) Validate() error {
if len(o.csrNames) < 1 && cmdutil.IsFilenameSliceEmpty(o.Filenames) {
return fmt.Errorf("one or more CSRs must be specified as <name> or -f <filename>")
}
return nil
}
func NewCmdCertificateApprove(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := CertificateOptions{}
func NewCmdCertificateApprove(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
options := CertificateOptions{
PrintFlags: printers.NewPrintFlags("approved"),
IOStreams: ioStreams,
}
cmd := &cobra.Command{
Use: "approve (-f FILENAME | NAME)",
DisableFlagsInUseLine: true,
......@@ -85,9 +117,9 @@ func NewCmdCertificateApprove(f cmdutil.Factory, out io.Writer) *cobra.Command {
signed certificate can do.
`),
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(cmd, args))
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.RunCertificateApprove(f, out, cmdutil.GetFlagBool(cmd, "force")))
cmdutil.CheckErr(options.RunCertificateApprove(cmdutil.GetFlagBool(cmd, "force")))
},
}
cmd.Flags().Bool("force", false, "Update the CSR even if it is already approved.")
......@@ -97,8 +129,8 @@ func NewCmdCertificateApprove(f cmdutil.Factory, out io.Writer) *cobra.Command {
return cmd
}
func (options *CertificateOptions) RunCertificateApprove(f cmdutil.Factory, out io.Writer, force bool) error {
return options.modifyCertificateCondition(f, out, force, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool, string) {
func (o *CertificateOptions) RunCertificateApprove(force bool) error {
return o.modifyCertificateCondition(o.builder, o.clientSet, force, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool) {
var alreadyApproved bool
for _, c := range csr.Status.Conditions {
if c.Type == certificates.CertificateApproved {
......@@ -106,7 +138,7 @@ func (options *CertificateOptions) RunCertificateApprove(f cmdutil.Factory, out
}
}
if alreadyApproved {
return csr, true, "approved"
return csr, true
}
csr.Status.Conditions = append(csr.Status.Conditions, certificates.CertificateSigningRequestCondition{
Type: certificates.CertificateApproved,
......@@ -114,12 +146,15 @@ func (options *CertificateOptions) RunCertificateApprove(f cmdutil.Factory, out
Message: "This CSR was approved by kubectl certificate approve.",
LastUpdateTime: metav1.Now(),
})
return csr, false, "approved"
return csr, false
})
}
func NewCmdCertificateDeny(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := CertificateOptions{}
func NewCmdCertificateDeny(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
options := CertificateOptions{
PrintFlags: printers.NewPrintFlags("denied"),
IOStreams: ioStreams,
}
cmd := &cobra.Command{
Use: "deny (-f FILENAME | NAME)",
DisableFlagsInUseLine: true,
......@@ -132,9 +167,9 @@ func NewCmdCertificateDeny(f cmdutil.Factory, out io.Writer) *cobra.Command {
not to issue a certificate to the requestor.
`),
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(cmd, args))
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.RunCertificateDeny(f, out, cmdutil.GetFlagBool(cmd, "force")))
cmdutil.CheckErr(options.RunCertificateDeny(cmdutil.GetFlagBool(cmd, "force")))
},
}
cmd.Flags().Bool("force", false, "Update the CSR even if it is already denied.")
......@@ -144,8 +179,8 @@ func NewCmdCertificateDeny(f cmdutil.Factory, out io.Writer) *cobra.Command {
return cmd
}
func (options *CertificateOptions) RunCertificateDeny(f cmdutil.Factory, out io.Writer, force bool) error {
return options.modifyCertificateCondition(f, out, force, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool, string) {
func (o *CertificateOptions) RunCertificateDeny(force bool) error {
return o.modifyCertificateCondition(o.builder, o.clientSet, force, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool) {
var alreadyDenied bool
for _, c := range csr.Status.Conditions {
if c.Type == certificates.CertificateDenied {
......@@ -153,7 +188,7 @@ func (options *CertificateOptions) RunCertificateDeny(f cmdutil.Factory, out io.
}
}
if alreadyDenied {
return csr, true, "denied"
return csr, true
}
csr.Status.Conditions = append(csr.Status.Conditions, certificates.CertificateSigningRequestCondition{
Type: certificates.CertificateDenied,
......@@ -161,17 +196,13 @@ func (options *CertificateOptions) RunCertificateDeny(f cmdutil.Factory, out io.
Message: "This CSR was approved by kubectl certificate deny.",
LastUpdateTime: metav1.Now(),
})
return csr, false, "denied"
return csr, false
})
}
func (options *CertificateOptions) modifyCertificateCondition(f cmdutil.Factory, out io.Writer, force bool, modify func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool, string)) error {
func (options *CertificateOptions) modifyCertificateCondition(builder *resource.Builder, clientSet internalclientset.Interface, force bool, modify func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool)) error {
var found int
c, err := f.ClientSet()
if err != nil {
return err
}
r := f.NewBuilder().
r := builder.
Internal().
ContinueOnError().
FilenameParam(false, &options.FilenameOptions).
......@@ -180,14 +211,14 @@ func (options *CertificateOptions) modifyCertificateCondition(f cmdutil.Factory,
Flatten().
Latest().
Do()
err = r.Visit(func(info *resource.Info, err error) error {
err := r.Visit(func(info *resource.Info, err error) error {
if err != nil {
return err
}
csr := info.Object.(*certificates.CertificateSigningRequest)
csr, hasCondition, verb := modify(csr)
csr, hasCondition := modify(csr)
if !hasCondition || force {
csr, err = c.Certificates().
csr, err = clientSet.Certificates().
CertificateSigningRequests().
UpdateApproval(csr)
if err != nil {
......@@ -195,11 +226,11 @@ func (options *CertificateOptions) modifyCertificateCondition(f cmdutil.Factory,
}
}
found++
cmdutil.PrintSuccess(options.outputStyle == "name", out, info.Object, false, verb)
return nil
return options.PrintObj(info.AsVersioned(), options.Out)
})
if found == 0 {
fmt.Fprintf(out, "No resources found\n")
fmt.Fprintf(options.Out, "No resources found\n")
}
return err
}
......@@ -23,9 +23,11 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
utilnet "k8s.io/apimachinery/pkg/util/net"
restclient "k8s.io/client-go/rest"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
......@@ -43,41 +45,62 @@ var (
kubectl cluster-info`))
)
func NewCmdClusterInfo(f cmdutil.Factory, out io.Writer) *cobra.Command {
type ClusterInfoOptions struct {
genericclioptions.IOStreams
Namespace string
Builder *resource.Builder
Client *restclient.Config
}
func NewCmdClusterInfo(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := &ClusterInfoOptions{
IOStreams: ioStreams,
}
cmd := &cobra.Command{
Use: "cluster-info",
Short: i18n.T("Display cluster info"),
Long: longDescr,
Example: clusterinfoExample,
Run: func(cmd *cobra.Command, args []string) {
err := RunClusterInfo(f, out, cmd)
cmdutil.CheckErr(err)
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.Run())
},
}
cmd.AddCommand(NewCmdClusterInfoDump(f, out))
cmd.AddCommand(NewCmdClusterInfoDump(f, ioStreams))
return cmd
}
func RunClusterInfo(f cmdutil.Factory, out io.Writer, cmd *cobra.Command) error {
client, err := f.ClientConfig()
func (o *ClusterInfoOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
var err error
o.Client, err = f.ClientConfig()
if err != nil {
return err
}
printService(out, "Kubernetes master", client.Host)
cmdNamespace := cmdutil.GetFlagString(cmd, "namespace")
if cmdNamespace == "" {
cmdNamespace = metav1.NamespaceSystem
}
o.Namespace = cmdNamespace
o.Builder = f.NewBuilder()
return nil
}
func (o *ClusterInfoOptions) Run() error {
printService(o.Out, "Kubernetes master", o.Client.Host)
// TODO use generalized labels once they are implemented (#341)
b := f.NewBuilder().
b := o.Builder.
Internal().
NamespaceParam(cmdNamespace).DefaultNamespace().
NamespaceParam(o.Namespace).DefaultNamespace().
LabelSelectorParam("kubernetes.io/cluster-service=true").
ResourceTypeOrNameArgs(false, []string{"services"}...).
Latest()
err = b.Do().Visit(func(r *resource.Info, err error) error {
err := b.Do().Visit(func(r *resource.Info, err error) error {
if err != nil {
return err
}
......@@ -109,10 +132,10 @@ func RunClusterInfo(f cmdutil.Factory, out io.Writer, cmd *cobra.Command) error
name = utilnet.JoinSchemeNamePort(scheme, service.ObjectMeta.Name, port.Name)
}
if len(client.GroupVersion.Group) == 0 {
link = client.Host + "/api/" + client.GroupVersion.Version + "/namespaces/" + service.ObjectMeta.Namespace + "/services/" + name + "/proxy"
if len(o.Client.GroupVersion.Group) == 0 {
link = o.Client.Host + "/api/" + o.Client.GroupVersion.Version + "/namespaces/" + service.ObjectMeta.Namespace + "/services/" + name + "/proxy"
} else {
link = client.Host + "/api/" + client.GroupVersion.Group + "/" + client.GroupVersion.Version + "/namespaces/" + service.ObjectMeta.Namespace + "/services/" + name + "/proxy"
link = o.Client.Host + "/api/" + o.Client.GroupVersion.Group + "/" + o.Client.GroupVersion.Version + "/namespaces/" + service.ObjectMeta.Namespace + "/services/" + name + "/proxy"
}
}
......@@ -120,11 +143,11 @@ func RunClusterInfo(f cmdutil.Factory, out io.Writer, cmd *cobra.Command) error
if len(name) == 0 {
name = service.ObjectMeta.Name
}
printService(out, name, link)
printService(o.Out, name, link)
}
return nil
})
out.Write([]byte("\nTo further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.\n"))
o.Out.Write([]byte("\nTo further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.\n"))
return err
// TODO consider printing more information about cluster
......
......@@ -28,19 +28,34 @@ import (
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
type ClusterInfoDumpOptions struct {
PrintFlags *printers.PrintFlags
PrintObj printers.ResourcePrinterFunc
genericclioptions.IOStreams
}
// NewCmdCreateSecret groups subcommands to create various types of secrets
func NewCmdClusterInfoDump(f cmdutil.Factory, cmdOut io.Writer) *cobra.Command {
func NewCmdClusterInfoDump(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := &ClusterInfoDumpOptions{
PrintFlags: printers.NewPrintFlags(""),
IOStreams: ioStreams,
}
cmd := &cobra.Command{
Use: "dump",
Short: i18n.T("Dump lots of relevant info for debugging and diagnosis"),
Long: dumpLong,
Example: dumpExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(dumpClusterInfo(f, cmd, cmdOut))
cmdutil.CheckErr(o.Complete())
cmdutil.CheckErr(o.Run(f, cmd))
},
}
cmd.Flags().String("output-directory", "", i18n.T("Where to output the files. If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory"))
......@@ -88,7 +103,20 @@ func setupOutputWriter(cmd *cobra.Command, defaultWriter io.Writer, filename str
return file
}
func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error {
func (o *ClusterInfoDumpOptions) Complete() error {
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
jsonOutputFmt := "json"
o.PrintFlags.OutputFormat = &jsonOutputFmt
o.PrintObj = printer.PrintObj
return nil
}
func (o *ClusterInfoDumpOptions) Run(f cmdutil.Factory, cmd *cobra.Command) error {
timeout, err := cmdutil.GetPodRunningTimeoutFlag(cmd)
if err != nil {
return cmdutil.UsageErrorf(cmd, err.Error())
......@@ -99,14 +127,12 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error
return err
}
printer := &printers.JSONPrinter{}
nodes, err := clientset.Core().Nodes().List(metav1.ListOptions{})
if err != nil {
return err
}
if err := printer.PrintObj(nodes, setupOutputWriter(cmd, out, "nodes.json")); err != nil {
if err := o.PrintObj(nodes, setupOutputWriter(cmd, o.Out, "nodes.json")); err != nil {
return err
}
......@@ -139,7 +165,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error
if err != nil {
return err
}
if err := printer.PrintObj(events, setupOutputWriter(cmd, out, path.Join(namespace, "events.json"))); err != nil {
if err := o.PrintObj(events, setupOutputWriter(cmd, o.Out, path.Join(namespace, "events.json"))); err != nil {
return err
}
......@@ -147,7 +173,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error
if err != nil {
return err
}
if err := printer.PrintObj(rcs, setupOutputWriter(cmd, out, path.Join(namespace, "replication-controllers.json"))); err != nil {
if err := o.PrintObj(rcs, setupOutputWriter(cmd, o.Out, path.Join(namespace, "replication-controllers.json"))); err != nil {
return err
}
......@@ -155,7 +181,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error
if err != nil {
return err
}
if err := printer.PrintObj(svcs, setupOutputWriter(cmd, out, path.Join(namespace, "services.json"))); err != nil {
if err := o.PrintObj(svcs, setupOutputWriter(cmd, o.Out, path.Join(namespace, "services.json"))); err != nil {
return err
}
......@@ -163,7 +189,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error
if err != nil {
return err
}
if err := printer.PrintObj(sets, setupOutputWriter(cmd, out, path.Join(namespace, "daemonsets.json"))); err != nil {
if err := o.PrintObj(sets, setupOutputWriter(cmd, o.Out, path.Join(namespace, "daemonsets.json"))); err != nil {
return err
}
......@@ -171,7 +197,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error
if err != nil {
return err
}
if err := printer.PrintObj(deps, setupOutputWriter(cmd, out, path.Join(namespace, "deployments.json"))); err != nil {
if err := o.PrintObj(deps, setupOutputWriter(cmd, o.Out, path.Join(namespace, "deployments.json"))); err != nil {
return err
}
......@@ -179,7 +205,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error
if err != nil {
return err
}
if err := printer.PrintObj(rps, setupOutputWriter(cmd, out, path.Join(namespace, "replicasets.json"))); err != nil {
if err := o.PrintObj(rps, setupOutputWriter(cmd, o.Out, path.Join(namespace, "replicasets.json"))); err != nil {
return err
}
......@@ -188,7 +214,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error
return err
}
if err := printer.PrintObj(pods, setupOutputWriter(cmd, out, path.Join(namespace, "pods.json"))); err != nil {
if err := o.PrintObj(pods, setupOutputWriter(cmd, o.Out, path.Join(namespace, "pods.json"))); err != nil {
return err
}
......@@ -215,7 +241,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error
for ix := range pods.Items {
pod := &pods.Items[ix]
containers := pod.Spec.Containers
writer := setupOutputWriter(cmd, out, path.Join(namespace, pod.Name, "logs.txt"))
writer := setupOutputWriter(cmd, o.Out, path.Join(namespace, pod.Name, "logs.txt"))
for i := range containers {
printContainer(writer, containers[i], pod)
......@@ -227,7 +253,7 @@ func dumpClusterInfo(f cmdutil.Factory, cmd *cobra.Command, out io.Writer) error
dir = "standard output"
}
if dir != "-" {
fmt.Fprintf(out, "Cluster info dumped to %s\n", dir)
fmt.Fprintf(o.Out, "Cluster info dumped to %s\n", dir)
}
return nil
}
......@@ -17,27 +17,27 @@ limitations under the License.
package cmd
import (
"bytes"
"io/ioutil"
"os"
"path"
"testing"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
func TestSetupOutputWriterNoOp(t *testing.T) {
tests := []string{"", "-"}
for _, test := range tests {
out := &bytes.Buffer{}
_, _, buf, _ := genericclioptions.NewTestIOStreams()
f := cmdtesting.NewTestFactory()
defer f.Cleanup()
cmd := NewCmdClusterInfoDump(f, os.Stdout)
cmd := NewCmdClusterInfoDump(f, genericclioptions.NewTestIOStreamsDiscard())
cmd.Flag("output-directory").Value.Set(test)
writer := setupOutputWriter(cmd, out, "/some/file/that/should/be/ignored")
if writer != out {
t.Errorf("expected: %v, saw: %v", out, writer)
writer := setupOutputWriter(cmd, buf, "/some/file/that/should/be/ignored")
if writer != buf {
t.Errorf("expected: %v, saw: %v", buf, writer)
}
}
}
......@@ -51,15 +51,15 @@ func TestSetupOutputWriterFile(t *testing.T) {
fullPath := path.Join(dir, file)
defer os.RemoveAll(dir)
out := &bytes.Buffer{}
_, _, buf, _ := genericclioptions.NewTestIOStreams()
f := cmdtesting.NewTestFactory()
defer f.Cleanup()
cmd := NewCmdClusterInfoDump(f, os.Stdout)
cmd := NewCmdClusterInfoDump(f, genericclioptions.NewTestIOStreamsDiscard())
cmd.Flag("output-directory").Value.Set(dir)
writer := setupOutputWriter(cmd, out, file)
if writer == out {
t.Errorf("expected: %v, saw: %v", out, writer)
writer := setupOutputWriter(cmd, buf, file)
if writer == buf {
t.Errorf("expected: %v, saw: %v", buf, writer)
}
output := "some data here"
writer.Write([]byte(output))
......
......@@ -276,18 +276,18 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
rollout.NewCmdRollout(f, out, err),
NewCmdRollingUpdate(f, out),
NewCmdScale(f, out, err),
NewCmdAutoscale(f, out),
NewCmdAutoscale(f, ioStreams),
},
},
{
Message: "Cluster Management Commands:",
Commands: []*cobra.Command{
NewCmdCertificate(f, out),
NewCmdClusterInfo(f, out),
NewCmdCertificate(f, ioStreams),
NewCmdClusterInfo(f, ioStreams),
NewCmdTop(f, out, err),
NewCmdCordon(f, out),
NewCmdUncordon(f, out),
NewCmdDrain(f, out, err),
NewCmdCordon(f, ioStreams),
NewCmdUncordon(f, ioStreams),
NewCmdDrain(f, ioStreams),
NewCmdTaint(f, out),
},
},
......@@ -300,7 +300,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
NewCmdExec(f, in, out, err),
NewCmdPortForward(f, out, err),
NewCmdProxy(f, out),
NewCmdCp(f, out, err),
NewCmdCp(f, ioStreams),
auth.NewCmdAuth(f, out, err),
},
},
......@@ -310,13 +310,13 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
NewCmdApply("kubectl", f, ioStreams),
NewCmdPatch(f, out),
NewCmdReplace(f, out, err),
NewCmdConvert(f, out),
NewCmdConvert(f, ioStreams),
},
},
{
Message: "Settings Commands:",
Commands: []*cobra.Command{
NewCmdLabel(f, out, err),
NewCmdLabel(f, ioStreams),
NewCmdAnnotate(f, ioStreams),
NewCmdCompletion(out, ""),
},
......
......@@ -18,7 +18,6 @@ package cmd
import (
"fmt"
"io"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
......@@ -27,6 +26,7 @@ import (
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
......@@ -61,8 +61,8 @@ var (
// NewCmdConvert creates a command object for the generic "convert" action, which
// translates the config file into a given version.
func NewCmdConvert(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := NewConvertOptions()
func NewCmdConvert(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
options := NewConvertOptions(ioStreams)
cmd := &cobra.Command{
Use: "convert -f FILENAME",
......@@ -71,18 +71,17 @@ func NewCmdConvert(f cmdutil.Factory, out io.Writer) *cobra.Command {
Long: convert_long,
Example: convert_example,
Run: func(cmd *cobra.Command, args []string) {
err := options.Complete(f, out, cmd)
cmdutil.CheckErr(err)
err = options.RunConvert()
cmdutil.CheckErr(err)
cmdutil.CheckErr(options.Complete(f, cmd))
cmdutil.CheckErr(options.RunConvert())
},
}
options.PrintFlags.AddFlags(cmd)
usage := "to need to get converted."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.MarkFlagRequired("filename")
cmdutil.AddValidateFlags(cmd)
cmdutil.AddNonDeprecatedPrinterFlags(cmd)
cmd.Flags().BoolVar(&options.local, "local", options.local, "If true, convert will NOT try to contact api-server but run locally.")
cmd.Flags().String("output-version", "", i18n.T("Output the formatted object with the given group version (for ex: 'extensions/v1beta1').)"))
return cmd
......@@ -90,19 +89,24 @@ func NewCmdConvert(f cmdutil.Factory, out io.Writer) *cobra.Command {
// ConvertOptions have the data required to perform the convert operation
type ConvertOptions struct {
PrintFlags *printers.PrintFlags
PrintObj printers.ResourcePrinterFunc
resource.FilenameOptions
builder *resource.Builder
local bool
out io.Writer
printer printers.ResourcePrinter
genericclioptions.IOStreams
specifiedOutputVersion schema.GroupVersion
}
func NewConvertOptions() *ConvertOptions {
return &ConvertOptions{local: true}
func NewConvertOptions(ioStreams genericclioptions.IOStreams) *ConvertOptions {
return &ConvertOptions{
PrintFlags: printers.NewPrintFlags("converted").WithDefaultOutput("yaml"),
local: true,
IOStreams: ioStreams,
}
}
// outputVersion returns the preferred output version for generic content (JSON, YAML, or templates)
......@@ -117,7 +121,7 @@ func outputVersion(cmd *cobra.Command) (schema.GroupVersion, error) {
}
// Complete collects information required to run Convert command from command line.
func (o *ConvertOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.Command) (err error) {
func (o *ConvertOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) (err error) {
o.specifiedOutputVersion, err = outputVersion(cmd)
if err != nil {
return err
......@@ -145,20 +149,12 @@ func (o *ConvertOptions) Complete(f cmdutil.Factory, out io.Writer, cmd *cobra.C
Flatten()
// build the printer
o.out = out
outputFormat := cmdutil.GetFlagString(cmd, "output")
templateFile := cmdutil.GetFlagString(cmd, "template")
if len(outputFormat) == 0 {
if len(templateFile) == 0 {
outputFormat = "yaml"
} else {
outputFormat = "template"
}
// TODO: once printing is abstracted, this should be handled at flag declaration time
cmd.Flags().Set("output", outputFormat)
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.printer, err = cmdutil.PrinterForOptions(cmdutil.ExtractCmdPrintOptions(cmd, false))
return err
o.PrintObj = printer.PrintObj
return nil
}
// RunConvert implements the generic Convert command
......@@ -189,10 +185,10 @@ func (o *ConvertOptions) RunConvert() error {
if err != nil {
return err
}
return o.printer.PrintObj(obj, o.out)
return o.PrintObj(obj, o.Out)
}
return o.printer.PrintObj(objects, o.out)
return o.PrintObj(objects, o.Out)
}
// objectListToVersionedObject receives a list of api objects and a group version
......
......@@ -20,10 +20,12 @@ import (
"bytes"
"fmt"
"net/http"
"strings"
"testing"
"k8s.io/client-go/rest/fake"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
type testcase struct {
......@@ -34,7 +36,6 @@ type testcase struct {
}
type checkField struct {
template string
expected string
}
......@@ -46,8 +47,7 @@ func TestConvertObject(t *testing.T) {
outputVersion: "extensions/v1beta1",
fields: []checkField{
{
template: "{{.apiVersion}}",
expected: "extensions/v1beta1",
expected: "apiVersion: extensions/v1beta1",
},
},
},
......@@ -57,8 +57,7 @@ func TestConvertObject(t *testing.T) {
outputVersion: "apps/v1beta2",
fields: []checkField{
{
template: "{{.apiVersion}}",
expected: "apps/v1beta2",
expected: "apiVersion: apps/v1beta2",
},
},
},
......@@ -68,16 +67,13 @@ func TestConvertObject(t *testing.T) {
outputVersion: "autoscaling/v2beta1",
fields: []checkField{
{
template: "{{.apiVersion}}",
expected: "autoscaling/v2beta1",
expected: "apiVersion: autoscaling/v2beta1",
},
{
template: "{{(index .spec.metrics 0).resource.name}}",
expected: "cpu",
expected: "name: cpu",
},
{
template: "{{(index .spec.metrics 0).resource.targetAverageUtilization}}",
expected: "50",
expected: "targetAverageUtilization: 50",
},
},
},
......@@ -87,12 +83,10 @@ func TestConvertObject(t *testing.T) {
outputVersion: "autoscaling/v1",
fields: []checkField{
{
template: "{{.apiVersion}}",
expected: "autoscaling/v1",
expected: "apiVersion: autoscaling/v1",
},
{
template: "{{.spec.targetCPUUtilizationPercentage}}",
expected: "50",
expected: "targetCPUUtilizationPercentage: 50",
},
},
},
......@@ -113,13 +107,13 @@ func TestConvertObject(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdConvert(tf, buf)
cmd := NewCmdConvert(tf, genericclioptions.IOStreams{Out: buf, ErrOut: buf})
cmd.Flags().Set("filename", tc.file)
cmd.Flags().Set("output-version", tc.outputVersion)
cmd.Flags().Set("local", "true")
cmd.Flags().Set("output", "go-template="+field.template)
cmd.Flags().Set("output", "yaml")
cmd.Run(cmd, []string{})
if buf.String() != field.expected {
if !strings.Contains(buf.String(), field.expected) {
t.Errorf("unexpected output when converting %s to %q, expected: %q, but got %q", tc.file, tc.outputVersion, field.expected, buf.String())
}
})
......
......@@ -28,8 +28,11 @@ import (
"path/filepath"
"strings"
restclient "k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"github.com/renstrom/dedent"
......@@ -61,8 +64,26 @@ var (
/file/path for a local file`)
)
type CopyOptions struct {
Container string
Namespace string
ClientConfig *restclient.Config
Clientset internalclientset.Interface
genericclioptions.IOStreams
}
func NewCopyOptions(ioStreams genericclioptions.IOStreams) *CopyOptions {
return &CopyOptions{
IOStreams: ioStreams,
}
}
// NewCmdCp creates a new Copy command.
func NewCmdCp(f cmdutil.Factory, cmdOut, cmdErr io.Writer) *cobra.Command {
func NewCmdCp(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewCopyOptions(ioStreams)
cmd := &cobra.Command{
Use: "cp <file-spec-src> <file-spec-dest>",
DisableFlagsInUseLine: true,
......@@ -70,7 +91,8 @@ func NewCmdCp(f cmdutil.Factory, cmdOut, cmdErr io.Writer) *cobra.Command {
Long: "Copy files and directories to and from containers.",
Example: cpExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(runCopy(f, cmd, cmdOut, cmdErr, args))
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.Run(args))
},
}
cmd.Flags().StringP("container", "c", "", "Container name. If omitted, the first container in the pod will be chosen")
......@@ -119,10 +141,35 @@ func extractFileSpec(arg string) (fileSpec, error) {
return fileSpec{}, errFileSpecDoesntMatchFormat
}
func runCopy(f cmdutil.Factory, cmd *cobra.Command, out, cmderr io.Writer, args []string) error {
func (o *CopyOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.Container = cmdutil.GetFlagString(cmd, "container")
var err error
o.Namespace, _, err = f.DefaultNamespace()
if err != nil {
return err
}
o.Clientset, err = f.ClientSet()
if err != nil {
return err
}
o.ClientConfig, err = f.ClientConfig()
if err != nil {
return err
}
return nil
}
func (o *CopyOptions) Validate(cmd *cobra.Command, args []string) error {
if len(args) != 2 {
return cmdutil.UsageErrorf(cmd, cpUsageStr)
}
return nil
}
func (o *CopyOptions) Run(args []string) error {
srcSpec, err := extractFileSpec(args[0])
if err != nil {
return err
......@@ -132,19 +179,19 @@ func runCopy(f cmdutil.Factory, cmd *cobra.Command, out, cmderr io.Writer, args
return err
}
if len(srcSpec.PodName) != 0 {
return copyFromPod(f, cmd, cmderr, srcSpec, destSpec)
return o.copyFromPod(srcSpec, destSpec)
}
if len(destSpec.PodName) != 0 {
return copyToPod(f, cmd, out, cmderr, srcSpec, destSpec)
return o.copyToPod(srcSpec, destSpec)
}
return cmdutil.UsageErrorf(cmd, "One of src or dest must be a remote file specification")
return fmt.Errorf("One of src or dest must be a remote file specification")
}
// checkDestinationIsDir receives a destination fileSpec and
// determines if the provided destination path exists on the
// pod. If the destination path does not exist or is _not_ a
// directory, an error is returned with the exit code received.
func checkDestinationIsDir(dest fileSpec, f cmdutil.Factory, cmd *cobra.Command) error {
func (o *CopyOptions) checkDestinationIsDir(dest fileSpec) error {
options := &ExecOptions{
StreamOptions: StreamOptions{
Out: bytes.NewBuffer([]byte{}),
......@@ -158,10 +205,10 @@ func checkDestinationIsDir(dest fileSpec, f cmdutil.Factory, cmd *cobra.Command)
Executor: &DefaultRemoteExecutor{},
}
return execute(f, cmd, options)
return o.execute(options)
}
func copyToPod(f cmdutil.Factory, cmd *cobra.Command, stdout, stderr io.Writer, src, dest fileSpec) error {
func (o *CopyOptions) copyToPod(src, dest fileSpec) error {
if len(src.File) == 0 || len(dest.File) == 0 {
return errFileCannotBeEmpty
}
......@@ -172,7 +219,7 @@ func copyToPod(f cmdutil.Factory, cmd *cobra.Command, stdout, stderr io.Writer,
dest.File = dest.File[:len(dest.File)-1]
}
if err := checkDestinationIsDir(dest, f, cmd); err == nil {
if err := o.checkDestinationIsDir(dest); err == nil {
// If no error, dest.File was found to be a directory.
// Copy specified src into it
dest.File = dest.File + "/" + path.Base(src.File)
......@@ -194,8 +241,8 @@ func copyToPod(f cmdutil.Factory, cmd *cobra.Command, stdout, stderr io.Writer,
options := &ExecOptions{
StreamOptions: StreamOptions{
In: reader,
Out: stdout,
Err: stderr,
Out: o.Out,
Err: o.ErrOut,
Stdin: true,
Namespace: dest.PodNamespace,
......@@ -205,10 +252,10 @@ func copyToPod(f cmdutil.Factory, cmd *cobra.Command, stdout, stderr io.Writer,
Command: cmdArr,
Executor: &DefaultRemoteExecutor{},
}
return execute(f, cmd, options)
return o.execute(options)
}
func copyFromPod(f cmdutil.Factory, cmd *cobra.Command, cmderr io.Writer, src, dest fileSpec) error {
func (o *CopyOptions) copyFromPod(src, dest fileSpec) error {
if len(src.File) == 0 || len(dest.File) == 0 {
return errFileCannotBeEmpty
}
......@@ -218,7 +265,7 @@ func copyFromPod(f cmdutil.Factory, cmd *cobra.Command, cmderr io.Writer, src, d
StreamOptions: StreamOptions{
In: nil,
Out: outStream,
Err: cmderr,
Err: o.Out,
Namespace: src.PodNamespace,
PodName: src.PodName,
......@@ -231,7 +278,7 @@ func copyFromPod(f cmdutil.Factory, cmd *cobra.Command, cmderr io.Writer, src, d
go func() {
defer outStream.Close()
execute(f, cmd, options)
o.execute(options)
}()
prefix := getPrefix(src.File)
prefix = path.Clean(prefix)
......@@ -389,31 +436,17 @@ func getPrefix(file string) string {
return strings.TrimLeft(file, "/")
}
func execute(f cmdutil.Factory, cmd *cobra.Command, options *ExecOptions) error {
func (o *CopyOptions) execute(options *ExecOptions) error {
if len(options.Namespace) == 0 {
namespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}
options.Namespace = namespace
options.Namespace = o.Namespace
}
container := cmdutil.GetFlagString(cmd, "container")
if len(container) > 0 {
options.ContainerName = container
if len(o.Container) > 0 {
options.ContainerName = o.Container
}
config, err := f.ClientConfig()
if err != nil {
return err
}
options.Config = config
clientset, err := f.ClientSet()
if err != nil {
return err
}
options.PodClient = clientset.Core()
options.Config = o.ClientConfig
options.PodClient = o.Clientset.Core()
if err := options.Validate(); err != nil {
return err
......
......@@ -36,6 +36,7 @@ import (
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
......@@ -524,9 +525,9 @@ func TestCopyToPod(t *testing.T) {
}
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdCp(tf, buf, errBuf)
ioStreams, _, _, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdCp(tf, ioStreams)
srcFile, err := ioutil.TempDir("", "test")
if err != nil {
......@@ -554,6 +555,7 @@ func TestCopyToPod(t *testing.T) {
}
for name, test := range tests {
opts := NewCopyOptions(ioStreams)
src := fileSpec{
File: srcFile,
}
......@@ -562,8 +564,9 @@ func TestCopyToPod(t *testing.T) {
PodName: "pod-name",
File: test.dest,
}
opts.Complete(tf, cmd)
t.Run(name, func(t *testing.T) {
err = copyToPod(tf, cmd, buf, errBuf, src, dest)
err = opts.copyToPod(src, dest)
//If error is NotFound error , it indicates that the
//request has been sent correctly.
//Treat this as no error.
......
......@@ -19,7 +19,6 @@ package cmd
import (
"errors"
"fmt"
"io"
"math"
"strings"
"time"
......@@ -42,18 +41,23 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"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/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
type DrainOptions struct {
PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Namespace string
client kubernetes.Interface
restClient *restclient.RESTClient
Factory cmdutil.Factory
Force bool
DryRun bool
GracePeriodSeconds int
......@@ -65,9 +69,9 @@ type DrainOptions struct {
PodSelector string
mapper meta.RESTMapper
nodeInfos []*resource.Info
Out io.Writer
ErrOut io.Writer
typer runtime.ObjectTyper
genericclioptions.IOStreams
}
// Takes a pod and returns a bool indicating whether or not to operate on the
......@@ -101,8 +105,12 @@ var (
kubectl cordon foo`))
)
func NewCmdCordon(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &DrainOptions{Factory: f, Out: out}
func NewCmdCordon(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
options := &DrainOptions{
PrintFlags: printers.NewPrintFlags("cordoned"),
IOStreams: ioStreams,
}
cmd := &cobra.Command{
Use: "cordon NODE",
......@@ -111,7 +119,7 @@ func NewCmdCordon(f cmdutil.Factory, out io.Writer) *cobra.Command {
Long: cordon_long,
Example: cordon_example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.SetupDrain(cmd, args))
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.RunCordonOrUncordon(true))
},
}
......@@ -129,8 +137,11 @@ var (
$ kubectl uncordon foo`))
)
func NewCmdUncordon(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &DrainOptions{Factory: f, Out: out}
func NewCmdUncordon(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
options := &DrainOptions{
PrintFlags: printers.NewPrintFlags("uncordoned"),
IOStreams: ioStreams,
}
cmd := &cobra.Command{
Use: "uncordon NODE",
......@@ -139,7 +150,7 @@ func NewCmdUncordon(f cmdutil.Factory, out io.Writer) *cobra.Command {
Long: uncordon_long,
Example: uncordon_example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.SetupDrain(cmd, args))
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.RunCordonOrUncordon(false))
},
}
......@@ -182,18 +193,18 @@ var (
$ kubectl drain foo --grace-period=900`))
)
func NewDrainOptions(f cmdutil.Factory, out, errOut io.Writer) *DrainOptions {
func NewDrainOptions(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *DrainOptions {
return &DrainOptions{
Factory: f,
Out: out,
ErrOut: errOut,
PrintFlags: printers.NewPrintFlags("drained"),
IOStreams: ioStreams,
backOff: clockwork.NewRealClock(),
GracePeriodSeconds: -1,
}
}
func NewCmdDrain(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
options := NewDrainOptions(f, out, errOut)
func NewCmdDrain(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
options := NewDrainOptions(f, ioStreams)
cmd := &cobra.Command{
Use: "drain NODE",
......@@ -202,7 +213,7 @@ func NewCmdDrain(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
Long: drain_long,
Example: drain_example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.SetupDrain(cmd, args))
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.RunDrain())
},
}
......@@ -218,9 +229,9 @@ func NewCmdDrain(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
return cmd
}
// SetupDrain populates some fields from the factory, grabs command line
// Complete populates some fields from the factory, grabs command line
// arguments and looks up the node using Builder
func (o *DrainOptions) SetupDrain(cmd *cobra.Command, args []string) error {
func (o *DrainOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
if len(args) == 0 && !cmd.Flags().Changed("selector") {
......@@ -235,7 +246,7 @@ func (o *DrainOptions) SetupDrain(cmd *cobra.Command, args []string) error {
o.DryRun = cmdutil.GetDryRunFlag(cmd)
if o.client, err = o.Factory.KubernetesClientSet(); err != nil {
if o.client, err = f.KubernetesClientSet(); err != nil {
return err
}
......@@ -245,21 +256,34 @@ func (o *DrainOptions) SetupDrain(cmd *cobra.Command, args []string) error {
}
}
o.restClient, err = o.Factory.RESTClient()
o.restClient, err = f.RESTClient()
if err != nil {
return err
}
o.nodeInfos = []*resource.Info{}
cmdNamespace, _, err := o.Factory.DefaultNamespace()
o.Namespace, _, err = f.DefaultNamespace()
if err != nil {
return err
}
builder := o.Factory.NewBuilder().
o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) {
o.PrintFlags.NamePrintFlags.Operation = operation
if o.DryRun {
o.PrintFlags.Complete("%s (dry run)")
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return nil, err
}
return printer.PrintObj, nil
}
builder := f.NewBuilder().
Internal().
NamespaceParam(cmdNamespace).DefaultNamespace().
NamespaceParam(o.Namespace).DefaultNamespace().
ResourceNames("nodes", args...).
SingleResourceType().
Flatten()
......@@ -294,6 +318,11 @@ func (o *DrainOptions) RunDrain() error {
return err
}
printer, err := o.ToPrinter("drained")
if err != nil {
return err
}
drainedNodes := sets.NewString()
var fatal error
......@@ -304,7 +333,7 @@ func (o *DrainOptions) RunDrain() error {
}
if err == nil || o.DryRun {
drainedNodes.Insert(info.Name)
cmdutil.PrintSuccess(false, o.Out, info.Object, o.DryRun, "drained")
printer.PrintObj(info.Object, o.Out)
} else {
fmt.Fprintf(o.ErrOut, "error: unable to drain node %q, aborting command...\n\n", info.Name)
remainingNodes := []string{}
......@@ -620,12 +649,17 @@ func (o *DrainOptions) waitForDelete(pods []corev1.Pod, interval, timeout time.D
} else {
verbStr = "deleted"
}
err := wait.PollImmediate(interval, timeout, func() (bool, error) {
printer, err := o.ToPrinter(verbStr)
if err != nil {
return pods, err
}
err = wait.PollImmediate(interval, timeout, func() (bool, error) {
pendingPods := []corev1.Pod{}
for i, pod := range pods {
p, err := getPodFn(pod.Namespace, pod.Name)
if apierrors.IsNotFound(err) || (p != nil && p.ObjectMeta.UID != pod.ObjectMeta.UID) {
cmdutil.PrintSuccess(false, o.Out, &pod, false, verbStr)
printer.PrintObj(&pod, o.Out)
continue
} else if err != nil {
return false, err
......@@ -677,11 +711,6 @@ func SupportEviction(clientset kubernetes.Interface) (string, error) {
// RunCordonOrUncordon runs either Cordon or Uncordon. The desired value for
// "Unschedulable" is passed as the first arg.
func (o *DrainOptions) RunCordonOrUncordon(desired bool) error {
cmdNamespace, _, err := o.Factory.DefaultNamespace()
if err != nil {
return err
}
cordonOrUncordon := "cordon"
if !desired {
cordonOrUncordon = "un" + cordonOrUncordon
......@@ -706,7 +735,12 @@ func (o *DrainOptions) RunCordonOrUncordon(desired bool) error {
}
unsched := node.Spec.Unschedulable
if unsched == desired {
cmdutil.PrintSuccess(false, o.Out, nodeInfo.Object, o.DryRun, already(desired))
printer, err := o.ToPrinter(already(desired))
if err != nil {
fmt.Printf("error: %v", err)
continue
}
printer.PrintObj(nodeInfo.AsVersioned(), o.Out)
} else {
if !o.DryRun {
helper := resource.NewHelper(o.restClient, nodeInfo.Mapping)
......@@ -721,16 +755,26 @@ func (o *DrainOptions) RunCordonOrUncordon(desired bool) error {
fmt.Printf("error: unable to %s node %q: %v", cordonOrUncordon, nodeInfo.Name, err)
continue
}
_, err = helper.Patch(cmdNamespace, nodeInfo.Name, types.StrategicMergePatchType, patchBytes)
_, err = helper.Patch(o.Namespace, nodeInfo.Name, types.StrategicMergePatchType, patchBytes)
if err != nil {
fmt.Printf("error: unable to %s node %q: %v", cordonOrUncordon, nodeInfo.Name, err)
continue
}
}
cmdutil.PrintSuccess(false, o.Out, nodeInfo.Object, o.DryRun, changed(desired))
printer, err := o.ToPrinter(changed(desired))
if err != nil {
fmt.Fprintf(o.ErrOut, "%v", err)
continue
}
printer.PrintObj(nodeInfo.AsVersioned(), o.Out)
}
} else {
cmdutil.PrintSuccess(false, o.Out, nodeInfo.Object, o.DryRun, "skipped")
printer, err := o.ToPrinter("skipped")
if err != nil {
fmt.Fprintf(o.ErrOut, "%v", err)
continue
}
printer.PrintObj(nodeInfo.AsVersioned(), o.Out)
}
}
......
......@@ -17,7 +17,6 @@ limitations under the License.
package cmd
import (
"bytes"
"errors"
"fmt"
"io"
......@@ -32,6 +31,7 @@ import (
"time"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
corev1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
......@@ -51,6 +51,7 @@ import (
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/printers"
)
const (
......@@ -84,7 +85,7 @@ func TestCordon(t *testing.T) {
description string
node *corev1.Node
expected *corev1.Node
cmd func(cmdutil.Factory, io.Writer) *cobra.Command
cmd func(cmdutil.Factory, genericclioptions.IOStreams) *cobra.Command
arg string
expectFatal bool
}{
......@@ -196,8 +197,8 @@ func TestCordon(t *testing.T) {
}
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := test.cmd(tf, buf)
ioStreams, _, _, _ := genericclioptions.NewTestIOStreams()
cmd := test.cmd(tf, ioStreams)
saw_fatal := false
func() {
......@@ -706,9 +707,8 @@ func TestDrain(t *testing.T) {
}
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdDrain(tf, buf, errBuf)
ioStreams, _, _, errBuf := genericclioptions.NewTestIOStreams()
cmd := NewCmdDrain(tf, ioStreams)
saw_fatal := false
fatal_msg := ""
......@@ -833,9 +833,18 @@ func TestDeletePods(t *testing.T) {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
o := DrainOptions{Factory: tf}
o := DrainOptions{
PrintFlags: printers.NewPrintFlags("drained"),
}
o.mapper, _ = tf.Object()
o.Out = os.Stdout
o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) {
return func(obj runtime.Object, out io.Writer) error {
return nil
}, nil
}
_, pods := createPods(false)
pendingPods, err := o.waitForDelete(pods, test.interval, test.timeout, false, test.getPodFn)
......
......@@ -94,11 +94,11 @@ func NewCmdEdit(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra
// bind flag structs
o.RecordFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
usage := "to use to edit the resource"
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmdutil.AddValidateOptionFlags(cmd, &o.ValidateOptions)
cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "Output format. One of: yaml|json.")
cmd.Flags().BoolVarP(&o.OutputPatch, "output-patch", "", o.OutputPatch, "Output the patch if the resource is edited.")
cmd.Flags().BoolVar(&o.WindowsLineEndings, "windows-line-endings", o.WindowsLineEndings,
"Defaults to the line ending native to your platform.")
......
......@@ -20,10 +20,12 @@ import (
"regexp"
"strings"
"github.com/golang/glog"
"github.com/spf13/cobra"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
......@@ -31,6 +33,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
var (
......@@ -76,17 +79,37 @@ var (
type ExposeServiceOptions struct {
FilenameOptions resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags
PrintFlags *printers.PrintFlags
PrintObj printers.ResourcePrinterFunc
DryRun bool
EnforceNamespace bool
Generators func(string) map[string]kubectl.Generator
CanBeExposed func(kind schema.GroupKind) error
ClientForMapping func(*meta.RESTMapping) (resource.RESTClient, error)
MapBasedSelectorForObject func(runtime.Object) (string, error)
PortsForObject func(runtime.Object) ([]string, error)
ProtocolsForObject func(runtime.Object) (map[string]string, error)
LabelsForObject func(runtime.Object) (map[string]string, error)
Namespace string
Mapper meta.RESTMapper
Typer runtime.ObjectTyper
Builder *resource.Builder
Recorder genericclioptions.Recorder
genericclioptions.IOStreams
}
func NewExposeServiceOptions(streams genericclioptions.IOStreams) *ExposeServiceOptions {
func NewExposeServiceOptions(ioStreams genericclioptions.IOStreams) *ExposeServiceOptions {
return &ExposeServiceOptions{
RecordFlags: genericclioptions.NewRecordFlags(),
PrintFlags: printers.NewPrintFlags("exposed"),
Recorder: genericclioptions.NoopRecorder{},
IOStreams: streams,
IOStreams: ioStreams,
}
}
......@@ -107,15 +130,15 @@ func NewCmdExposeService(f cmdutil.Factory, streams genericclioptions.IOStreams)
Example: exposeExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.RunExpose(f, cmd, args))
cmdutil.CheckErr(o.RunExpose(cmd, args))
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
o.RecordFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().String("generator", "service/v2", i18n.T("The name of the API generator to use. There are 2 generators: 'service/v1' and 'service/v2'. The only difference between them is that service port in v1 is named 'default', while it is left unnamed in v2. Default is 'service/v2'."))
cmd.Flags().String("protocol", "", i18n.T("The network protocol for the service to be created. Default is 'TCP'."))
cmd.Flags().String("port", "", i18n.T("The port that the service should serve on. Copied from the resource being exposed, if unspecified"))
......@@ -140,7 +163,16 @@ func NewCmdExposeService(f cmdutil.Factory, streams genericclioptions.IOStreams)
}
func (o *ExposeServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
var err error
o.DryRun = cmdutil.GetDryRunFlag(cmd)
if o.DryRun {
o.PrintFlags.Complete("%s (dry run)")
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = printer.PrintObj
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
......@@ -148,32 +180,41 @@ func (o *ExposeServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) e
return err
}
return err
}
func (o *ExposeServiceOptions) RunExpose(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
namespace, enforceNamespace, err := f.DefaultNamespace()
o.Generators = f.Generators
o.Builder = f.NewBuilder()
o.CanBeExposed = f.CanBeExposed
o.ClientForMapping = f.ClientForMapping
o.MapBasedSelectorForObject = f.MapBasedSelectorForObject
o.PortsForObject = f.PortsForObject
o.ProtocolsForObject = f.ProtocolsForObject
o.Mapper, o.Typer = f.Object()
o.LabelsForObject = f.LabelsForObject
o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
mapper, typer := f.Object()
r := f.NewBuilder().
return err
}
func (o *ExposeServiceOptions) RunExpose(cmd *cobra.Command, args []string) error {
r := o.Builder.
Internal().
ContinueOnError().
NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(false, args...).
Flatten().
Do()
err = r.Err()
err := r.Err()
if err != nil {
return cmdutil.UsageErrorf(cmd, err.Error())
}
// Get the generator, setup and validate all required parameters
generatorName := cmdutil.GetFlagString(cmd, "generator")
generators := f.Generators("expose")
generators := o.Generators("expose")
generator, found := generators[generatorName]
if !found {
return cmdutil.UsageErrorf(cmd, "generator %q not found.", generatorName)
......@@ -186,7 +227,7 @@ func (o *ExposeServiceOptions) RunExpose(f cmdutil.Factory, cmd *cobra.Command,
}
mapping := info.ResourceMapping()
if err := f.CanBeExposed(mapping.GroupVersionKind.GroupKind()); err != nil {
if err := o.CanBeExposed(mapping.GroupVersionKind.GroupKind()); err != nil {
return err
}
......@@ -200,7 +241,7 @@ func (o *ExposeServiceOptions) RunExpose(f cmdutil.Factory, cmd *cobra.Command,
// For objects that need a pod selector, derive it from the exposed object in case a user
// didn't explicitly specify one via --selector
if s, found := params["selector"]; found && kubectl.IsZero(s) {
s, err := f.MapBasedSelectorForObject(info.Object)
s, err := o.MapBasedSelectorForObject(info.Object)
if err != nil {
return cmdutil.UsageErrorf(cmd, "couldn't retrieve selectors via --selector flag or introspection: %v", err)
}
......@@ -212,7 +253,7 @@ func (o *ExposeServiceOptions) RunExpose(f cmdutil.Factory, cmd *cobra.Command,
// For objects that need a port, derive it from the exposed object in case a user
// didn't explicitly specify one via --port
if port, found := params["port"]; found && kubectl.IsZero(port) {
ports, err := f.PortsForObject(info.Object)
ports, err := o.PortsForObject(info.Object)
if err != nil {
return cmdutil.UsageErrorf(cmd, "couldn't find port via --port flag or introspection: %v", err)
}
......@@ -231,7 +272,7 @@ func (o *ExposeServiceOptions) RunExpose(f cmdutil.Factory, cmd *cobra.Command,
// Always try to derive protocols from the exposed object, may use
// different protocols for different ports.
if _, found := params["protocol"]; found {
protocolsMap, err := f.ProtocolsForObject(info.Object)
protocolsMap, err := o.ProtocolsForObject(info.Object)
if err != nil {
return cmdutil.UsageErrorf(cmd, "couldn't find protocol via introspection: %v", err)
}
......@@ -241,7 +282,7 @@ func (o *ExposeServiceOptions) RunExpose(f cmdutil.Factory, cmd *cobra.Command,
}
if kubectl.IsZero(params["labels"]) {
labels, err := f.LabelsForObject(info.Object)
labels, err := o.LabelsForObject(info.Object)
if err != nil {
return err
}
......@@ -270,9 +311,9 @@ func (o *ExposeServiceOptions) RunExpose(f cmdutil.Factory, cmd *cobra.Command,
}
resourceMapper := &resource.Mapper{
ObjectTyper: typer,
RESTMapper: mapper,
ClientMapper: resource.ClientMapperFunc(f.ClientForMapping),
ObjectTyper: o.Typer,
RESTMapper: o.Mapper,
ClientMapper: resource.ClientMapperFunc(o.ClientForMapping),
Decoder: cmdutil.InternalVersionDecoder(),
}
info, err = resourceMapper.InfoForObject(object, nil)
......@@ -283,29 +324,20 @@ func (o *ExposeServiceOptions) RunExpose(f cmdutil.Factory, cmd *cobra.Command,
glog.V(4).Infof("error recording current command: %v", err)
}
info.Refresh(object, true)
if cmdutil.GetDryRunFlag(cmd) {
if len(cmdutil.GetFlagString(cmd, "output")) > 0 {
return cmdutil.PrintObject(cmd, object, o.Out)
}
cmdutil.PrintSuccess(false, o.Out, info.Object, true, "exposed")
return nil
if o.DryRun {
return o.PrintObj(object, o.Out)
}
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info.Object, cmdutil.InternalVersionJSONEncoder()); err != nil {
return err
}
// Serialize the object with the annotation applied.
object, err = resource.NewHelper(info.Client, info.Mapping).Create(namespace, false, object)
object, err = resource.NewHelper(info.Client, info.Mapping).Create(o.Namespace, false, object)
if err != nil {
return err
}
if len(cmdutil.GetFlagString(cmd, "output")) > 0 {
return cmdutil.PrintObject(cmd, object, o.Out)
}
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "exposed")
return nil
return o.PrintObj(info.AsVersioned(), o.Out)
})
if err != nil {
return err
......
......@@ -79,7 +79,7 @@ func TestRunExposeService(t *testing.T) {
Selector: map[string]string{"app": "go"},
},
},
expected: "service \"foo\" exposed",
expected: "service/foo exposed",
status: 200,
},
{
......@@ -110,7 +110,7 @@ func TestRunExposeService(t *testing.T) {
Selector: map[string]string{"func": "stream"},
},
},
expected: "service \"foo\" exposed",
expected: "service/foo exposed",
status: 200,
},
{
......@@ -142,7 +142,7 @@ func TestRunExposeService(t *testing.T) {
Selector: map[string]string{"run": "this"},
},
},
expected: "service \"mayor\" exposed",
expected: "service/mayor exposed",
status: 200,
},
{
......@@ -237,7 +237,7 @@ func TestRunExposeService(t *testing.T) {
ClusterIP: "10.10.10.10",
},
},
expected: "service \"foo\" exposed",
expected: "service /foo exposed",
status: 200,
},
{
......@@ -269,7 +269,7 @@ func TestRunExposeService(t *testing.T) {
ClusterIP: api.ClusterIPNone,
},
},
expected: "service \"foo\" exposed",
expected: "service/foo exposed",
status: 200,
},
{
......@@ -295,7 +295,7 @@ func TestRunExposeService(t *testing.T) {
ClusterIP: api.ClusterIPNone,
},
},
expected: "service \"foo\" exposed",
expected: "service/foo exposed",
status: 200,
},
{
......@@ -353,7 +353,7 @@ func TestRunExposeService(t *testing.T) {
Selector: map[string]string{"svc": "frompod"},
},
},
expected: "service \"a-name-that-is-toooo-big-for-a-service-because-it-can-only-hand\" exposed",
expected: "service/a-name-that-is-toooo-big-for-a-service-because-it-can-only-hand exposed",
status: 200,
},
{
......@@ -500,7 +500,7 @@ func TestRunExposeService(t *testing.T) {
out := buf.String()
if _, ok := test.flags["dry-run"]; ok {
test.expected = fmt.Sprintf("service %q exposed (dry run)", test.flags["name"])
test.expected = fmt.Sprintf("service/%s exposed (dry run)", test.flags["name"])
}
if !strings.Contains(out, test.expected) {
......
......@@ -18,7 +18,6 @@ package cmd
import (
"fmt"
"io"
"reflect"
"strings"
......@@ -40,6 +39,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
// LabelOptions have the data required to perform the label operation
......@@ -48,6 +48,9 @@ type LabelOptions struct {
resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags
PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error)
// Common user flags
overwrite bool
list bool
......@@ -66,8 +69,7 @@ type LabelOptions struct {
Recorder genericclioptions.Recorder
// Common shared fields
out io.Writer
errout io.Writer
genericclioptions.IOStreams
}
var (
......@@ -100,19 +102,19 @@ var (
kubectl label pods foo bar-`))
)
func NewLabelOptions(out, errOut io.Writer) *LabelOptions {
func NewLabelOptions(ioStreams genericclioptions.IOStreams) *LabelOptions {
return &LabelOptions{
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
Recorder: genericclioptions.NoopRecorder{},
PrintFlags: printers.NewPrintFlags("labeled"),
out: out,
errout: errOut,
IOStreams: ioStreams,
}
}
func NewCmdLabel(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
o := NewLabelOptions(out, errOut)
func NewCmdLabel(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewLabelOptions(ioStreams)
validArgs := cmdutil.ValidArgList(f)
......@@ -136,8 +138,8 @@ func NewCmdLabel(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
}
o.RecordFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().BoolVar(&o.overwrite, "overwrite", o.overwrite, "If true, allow labels to be overwritten, otherwise reject label updates that overwrite existing labels.")
cmd.Flags().BoolVar(&o.list, "list", o.list, "If true, display the labels for a given resource.")
cmd.Flags().BoolVar(&o.local, "local", o.local, "If true, label will NOT contact api-server but run locally.")
......@@ -165,6 +167,19 @@ func (o *LabelOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st
o.outputFormat = cmdutil.GetFlagString(cmd, "output")
o.dryrun = cmdutil.GetDryRunFlag(cmd)
o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) {
o.PrintFlags.NamePrintFlags.Operation = operation
if o.dryrun {
o.PrintFlags.Complete("%s (dry run)")
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return nil, err
}
return printer.PrintObj, nil
}
resources, labelArgs, err := cmdutil.GetResourcesAndPairs(args, "label")
if err != nil {
return err
......@@ -255,7 +270,7 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error {
}
for _, label := range o.removeLabels {
if _, ok := accessor.GetLabels()[label]; !ok {
fmt.Fprintf(o.out, "label %q not found.\n", label)
fmt.Fprintf(o.Out, "label %q not found.\n", label)
}
}
......@@ -304,19 +319,20 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error {
indent := ""
if !one {
indent = " "
fmt.Fprintf(o.errout, "Listing labels for %s.%s/%s:\n", info.Mapping.GroupVersionKind.Kind, info.Mapping.GroupVersionKind.Group, info.Name)
fmt.Fprintf(o.ErrOut, "Listing labels for %s.%s/%s:\n", info.Mapping.GroupVersionKind.Kind, info.Mapping.GroupVersionKind.Group, info.Name)
}
for k, v := range accessor.GetLabels() {
fmt.Fprintf(o.out, "%s%s=%s\n", indent, k, v)
fmt.Fprintf(o.Out, "%s%s=%s\n", indent, k, v)
}
return nil
}
if len(o.outputFormat) > 0 {
return cmdutil.PrintObject(cmd, outputObj, o.out)
printer, err := o.ToPrinter(dataChangeMsg)
if err != nil {
return err
}
cmdutil.PrintSuccess(false, o.out, info.Object, o.dryrun, dataChangeMsg)
printer.PrintObj(info.AsVersioned(), o.Out)
return nil
})
}
......
......@@ -29,6 +29,7 @@ import (
"k8s.io/client-go/rest/fake"
"k8s.io/kubernetes/pkg/api/legacyscheme"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/scheme"
)
......@@ -327,14 +328,15 @@ func TestLabelErrors(t *testing.T) {
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
ioStreams, _, _, _ := genericclioptions.NewTestIOStreams()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLabel(tf, buf, buf)
cmd := NewCmdLabel(tf, ioStreams)
cmd.SetOutput(buf)
for k, v := range testCase.flags {
cmd.Flags().Set(k, v)
}
opts := NewLabelOptions(buf, buf)
opts := NewLabelOptions(ioStreams)
err := opts.Complete(tf, cmd, testCase.args)
if err == nil {
err = opts.Validate()
......@@ -389,9 +391,9 @@ func TestLabelForResourceFromFile(t *testing.T) {
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLabel(tf, buf, buf)
opts := NewLabelOptions(buf, buf)
ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdLabel(tf, ioStreams)
opts := NewLabelOptions(ioStreams)
opts.Filenames = []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}
err := opts.Complete(tf, cmd, []string{"a=b"})
if err == nil {
......@@ -422,9 +424,9 @@ func TestLabelLocal(t *testing.T) {
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLabel(tf, buf, buf)
opts := NewLabelOptions(buf, buf)
ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdLabel(tf, ioStreams)
opts := NewLabelOptions(ioStreams)
opts.Filenames = []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}
opts.local = true
err := opts.Complete(tf, cmd, []string{"a=b"})
......@@ -480,10 +482,10 @@ func TestLabelMultipleObjects(t *testing.T) {
tf.Namespace = "test"
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
opts := NewLabelOptions(buf, buf)
ioStreams, _, buf, _ := genericclioptions.NewTestIOStreams()
opts := NewLabelOptions(ioStreams)
opts.all = true
cmd := NewCmdLabel(tf, buf, buf)
cmd := NewCmdLabel(tf, ioStreams)
err := opts.Complete(tf, cmd, []string{"pods", "a=b"})
if err == nil {
err = opts.Validate()
......
......@@ -25,6 +25,7 @@ import (
"time"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
......
......@@ -40,6 +40,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
)
var patchTypes = map[string]types.PatchType{"json": types.JSONPatchType, "merge": types.MergePatchType, "strategic": types.StrategicMergePatchType}
......@@ -49,6 +50,8 @@ var patchTypes = map[string]types.PatchType{"json": types.JSONPatchType, "merge"
type PatchOptions struct {
resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags
PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Local bool
DryRun bool
......@@ -86,8 +89,8 @@ var (
func NewPatchOptions() *PatchOptions {
return &PatchOptions{
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
Recorder: genericclioptions.NoopRecorder{},
PrintFlags: printers.NewPrintFlags("patched"),
}
}
......@@ -110,11 +113,11 @@ func NewCmdPatch(f cmdutil.Factory, out io.Writer) *cobra.Command {
}
o.RecordFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
cmd.Flags().StringP("patch", "p", "", "The patch to be applied to the resource JSON file.")
cmd.MarkFlagRequired("patch")
cmd.Flags().String("type", "strategic", fmt.Sprintf("The type of patch being provided; one of %v", sets.StringKeySet(patchTypes).List()))
cmdutil.AddPrinterFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
usage := "identifying the resource to update"
......@@ -137,6 +140,19 @@ func (o *PatchOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.OutputFormat = cmdutil.GetFlagString(cmd, "output")
o.DryRun = cmdutil.GetFlagBool(cmd, "dry-run")
o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) {
o.PrintFlags.NamePrintFlags.Operation = operation
if o.DryRun {
o.PrintFlags.Complete("%s (dry run)")
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return nil, err
}
return printer.PrintObj, nil
}
return err
}
......@@ -222,10 +238,11 @@ func (o *PatchOptions) RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Com
return err
}
if len(o.OutputFormat) > 0 && o.OutputFormat != "name" {
return cmdutil.PrintObject(cmd, info.Object, out)
printer, err := o.ToPrinter(patchOperation(didPatch))
if err != nil {
return err
}
cmdutil.PrintSuccess(o.OutputFormat == "name", out, info.Object, false, patchOperation(didPatch))
printer.PrintObj(info.AsVersioned(), out)
// if object was not successfully patched, exit with error code 1
if !didPatch {
......@@ -264,12 +281,11 @@ func (o *PatchOptions) RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Com
}
}
if len(o.OutputFormat) > 0 && o.OutputFormat != "name" {
return cmdutil.PrintObject(cmd, info.Object, out)
printer, err := o.ToPrinter(patchOperation(didPatch))
if err != nil {
return err
}
cmdutil.PrintSuccess(o.OutputFormat == "name", out, info.Object, o.DryRun, patchOperation(didPatch))
return nil
return printer.PrintObj(info.AsVersioned(), out)
})
if err != nil {
return err
......
......@@ -144,7 +144,7 @@ func TestPatchNoop(t *testing.T) {
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("patch", `{"metadata":{"annotations":{"foo":"bar"}}}`)
cmd.Run(cmd, []string{"services", "frontend"})
if buf.String() != "service \"baz\" patched\n" {
if buf.String() != "service/baz patched\n" {
t.Errorf("unexpected output: %s", buf.String())
}
}
......
......@@ -5,8 +5,8 @@ args:
- service/svc1
namespace: "myproject"
expectedStdout:
- configmap "cm1" edited
- service "svc1" edited
- configmap/cm1 edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request
......
......@@ -5,8 +5,8 @@ args:
- service/svc1
namespace: "myproject"
expectedStdout:
- configmap "cm1" edited
- service "svc1" edited
- configmap/cm1 edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request
......
......@@ -4,7 +4,7 @@ args:
- service/svc1
namespace: myproject
expectedStdout:
- "service \"svc1\" edited"
- "service/svc1 edited"
expectedExitCode: 0
steps:
- type: request
......
......@@ -6,7 +6,7 @@ args:
outputFormat: yaml
namespace: myproject
expectedStdout:
- service "svc1" edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request
......
......@@ -3,7 +3,7 @@ mode: create
filename: "svc.yaml"
namespace: "edit-test"
expectedStdout:
- "service \"svc1\" created"
- "service/svc1 created"
expectedStderr:
- "\"svc2\" is invalid"
expectedExitCode: 1
......
......@@ -3,8 +3,8 @@ mode: create
filename: "svc.yaml"
namespace: "edit-test"
expectedStdout:
- service "svc1" created
- service "svc2" created
- service/svc1 created
- service/svc2 created
expectedExitCode: 0
steps:
- type: edit
......
......@@ -5,7 +5,7 @@ args:
- svc1
namespace: edit-test
expectedStdout:
- service "svc1" edited
- service/svc1 edited
expectedStderr:
- "error: services \"svc1\" is invalid"
expectedExitCode: 0
......
......@@ -11,7 +11,7 @@ outputPatch: "true"
namespace: edit-test
expectedStdout:
- 'Patch: {"metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"v1\",\"kind\":\"Service\",\"metadata\":{\"annotations\":{},\"creationTimestamp\":\"2017-02-27T19:40:53Z\",\"labels\":{\"app\":\"svc1\",\"new-label\":\"new-value\"},\"name\":\"svc1\",\"namespace\":\"edit-test\",\"resourceVersion\":\"670\",\"selfLink\":\"/api/v1/namespaces/edit-test/services/svc1\",\"uid\":\"a6c11186-fd24-11e6-b53c-480fcf4a5275\"},\"spec\":{\"clusterIP\":\"10.0.0.204\",\"ports\":[{\"name\":\"80\",\"port\":80,\"protocol\":\"TCP\",\"targetPort\":80}],\"selector\":{\"app\":\"svc1\"},\"sessionAffinity\":\"None\",\"type\":\"ClusterIP\"},\"status\":{\"loadBalancer\":{}}}\n"},"labels":{"new-label":"new-value"}}}'
- service "svc1" edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request
......
......@@ -4,8 +4,8 @@ args:
- configmaps,services
namespace: "edit-test"
expectedStdout:
- configmap "cm1" edited
- service "svc1" edited
- configmap/cm1 edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request
......
......@@ -5,8 +5,8 @@ args:
- service/svc1
namespace: "edit-test"
expectedStdout:
- configmap "cm1" edited
- service "svc1" edited
- configmap/cm1 edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request
......
......@@ -5,8 +5,8 @@ args:
- service/svc1
namespace: "edit-test"
expectedStdout:
- configmap "cm1" edited
- service "svc1" edited
- configmap/cm1 edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request
......
......@@ -9,7 +9,7 @@ args:
saveConfig: "false"
namespace: edit-test
expectedStdout:
- service "svc1" edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request
......
......@@ -6,9 +6,9 @@ args:
- bars/test2
namespace: default
expectedStdout:
- "service \"kubernetes\" edited"
- "bar.company.com \"test\" edited"
- "bar.company.com \"test2\" edited"
- "service/kubernetes edited"
- "bar.company.com/test edited"
- "bar.company.com/test2 edited"
expectedExitCode: 0
steps:
- type: request
......
......@@ -8,7 +8,7 @@ args:
- svc1
namespace: edit-test
expectedStdout:
- service "svc1" edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request
......
......@@ -4,7 +4,7 @@ args:
- service/kubernetes
namespace: default
expectedStdout:
- "service \"kubernetes\" edited"
- "service/kubernetes edited"
expectedExitCode: 0
steps:
- type: request
......
......@@ -4,7 +4,7 @@ args:
- storageclasses.v1beta1.storage.k8s.io/foo
namespace: default
expectedStdout:
- "storageclass.storage.k8s.io \"foo\" edited"
- "storageclass.storage.k8s.io/foo edited"
expectedExitCode: 0
steps:
- type: request
......
......@@ -4,7 +4,7 @@ args:
- storageclasses.v0.storage.k8s.io/foo
namespace: default
expectedStdout:
- "storageclass.storage.k8s.io \"foo\" edited"
- "storageclass.storage.k8s.io/foo edited"
expectedExitCode: 0
steps:
- type: request
......
......@@ -9,7 +9,7 @@ args:
saveConfig: "true"
namespace: edit-test
expectedStdout:
- service "svc1" edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request
......
......@@ -57,6 +57,9 @@ type EditOptions struct {
resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags
PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Output string
OutputPatch bool
WindowsLineEndings bool
......@@ -86,12 +89,14 @@ func NewEditOptions(editMode EditMode, ioStreams genericclioptions.IOStreams) *E
EditMode: editMode,
Output: "yaml",
PrintFlags: printers.NewPrintFlags("edited"),
WindowsLineEndings: goruntime.GOOS == "windows",
Recorder: genericclioptions.NoopRecorder{},
IOStreams: ioStreams,
Output: "yaml",
}
}
......@@ -159,6 +164,15 @@ func (o *EditOptions) Complete(f cmdutil.Factory, args []string, cmd *cobra.Comm
Do()
}
o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) {
o.PrintFlags.NamePrintFlags.Operation = operation
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return nil, err
}
return printer.PrintObj, nil
}
o.CmdNamespace = cmdNamespace
o.f = f
......@@ -423,14 +437,23 @@ func (o *EditOptions) visitToApplyEditPatch(originalInfos []*resource.Info, patc
}
if reflect.DeepEqual(originalJS, editedJS) {
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "skipped")
printer, err := o.ToPrinter("skipped")
if err != nil {
return err
}
printer.PrintObj(info.AsVersioned(), o.Out)
return nil
} else {
err := o.annotationPatch(info)
if err != nil {
return err
}
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "edited")
printer, err := o.ToPrinter("edited")
if err != nil {
return err
}
printer.PrintObj(info.AsVersioned(), o.Out)
return nil
}
})
......@@ -549,7 +572,11 @@ func (o *EditOptions) visitToPatch(originalInfos []*resource.Info, patchVisitor
if reflect.DeepEqual(originalJS, editedJS) {
// no edit, so just skip it.
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "skipped")
printer, err := o.ToPrinter("skipped")
if err != nil {
return err
}
printer.PrintObj(info.AsVersioned(), o.Out)
return nil
}
......@@ -603,7 +630,11 @@ func (o *EditOptions) visitToPatch(originalInfos []*resource.Info, patchVisitor
return nil
}
info.Refresh(patched, true)
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "edited")
printer, err := o.ToPrinter("edited")
if err != nil {
return err
}
printer.PrintObj(info.AsVersioned(), o.Out)
return nil
})
return err
......@@ -614,7 +645,11 @@ func (o *EditOptions) visitToCreate(createVisitor resource.Visitor) error {
if err := resource.CreateAndRefresh(info); err != nil {
return err
}
cmdutil.PrintSuccess(false, o.Out, info.Object, false, "created")
printer, err := o.ToPrinter("created")
if err != nil {
return err
}
printer.PrintObj(info.AsVersioned(), o.Out)
return nil
})
return err
......
......@@ -23,11 +23,17 @@ import (
)
type NoCompatiblePrinterError struct {
Options interface{}
OutputFormat *string
Options interface{}
}
func (e NoCompatiblePrinterError) Error() string {
return fmt.Sprintf("unable to match a printer suitable for the options specified: %#v", e.Options)
output := ""
if e.OutputFormat != nil {
output = *e.OutputFormat
}
return fmt.Sprintf("unable to match a printer suitable for the output format %q and the options specified: %#v", output, e.Options)
}
func IsNoCompatiblePrinterError(err error) bool {
......@@ -67,7 +73,7 @@ func (f *PrintFlags) ToPrinter() (ResourcePrinter, error) {
return p, err
}
return nil, NoCompatiblePrinterError{f}
return nil, NoCompatiblePrinterError{Options: f, OutputFormat: f.OutputFormat}
}
func (f *PrintFlags) AddFlags(cmd *cobra.Command) {
......@@ -79,6 +85,20 @@ func (f *PrintFlags) AddFlags(cmd *cobra.Command) {
}
}
// WithDefaultOutput sets a default output format if one is not provided through a flag value
func (f *PrintFlags) WithDefaultOutput(output string) *PrintFlags {
existingFormat := ""
if f.OutputFormat != nil {
existingFormat = *f.OutputFormat
}
if len(existingFormat) == 0 {
existingFormat = output
}
f.OutputFormat = &existingFormat
return f
}
func NewPrintFlags(operation string) *PrintFlags {
outputFormat := ""
......
......@@ -44,7 +44,7 @@ func (f *JSONYamlPrintFlags) ToPrinter(outputFormat string) (ResourcePrinter, er
case "yaml":
printer = &YAMLPrinter{}
default:
return nil, NoCompatiblePrinterError{f}
return nil, NoCompatiblePrinterError{Options: f, OutputFormat: &outputFormat}
}
// wrap the printer in a versioning printer that understands when to convert and when not to convert
......
......@@ -39,7 +39,7 @@ type JSONPathPrintFlags struct {
// Returns false if the specified templateFormat does not match a template format.
func (f *JSONPathPrintFlags) ToPrinter(templateFormat string) (ResourcePrinter, error) {
if (f.TemplateArgument == nil || len(*f.TemplateArgument) == 0) && len(templateFormat) == 0 {
return nil, NoCompatiblePrinterError{f}
return nil, NoCompatiblePrinterError{Options: f, OutputFormat: &templateFormat}
}
templateValue := ""
......@@ -66,7 +66,7 @@ func (f *JSONPathPrintFlags) ToPrinter(templateFormat string) (ResourcePrinter,
}
if _, supportedFormat := templateFormats[templateFormat]; !supportedFormat {
return nil, NoCompatiblePrinterError{f}
return nil, NoCompatiblePrinterError{Options: f, OutputFormat: &templateFormat}
}
if len(templateValue) == 0 {
......
......@@ -63,7 +63,7 @@ func (f *NamePrintFlags) ToPrinter(outputFormat string) (ResourcePrinter, error)
case "":
return namePrinter, nil
default:
return nil, NoCompatiblePrinterError{f}
return nil, NoCompatiblePrinterError{Options: f, OutputFormat: &outputFormat}
}
}
......
......@@ -39,7 +39,7 @@ type GoTemplatePrintFlags struct {
// Returns false if the specified templateFormat does not match a template format.
func (f *GoTemplatePrintFlags) ToPrinter(templateFormat string) (ResourcePrinter, error) {
if (f.TemplateArgument == nil || len(*f.TemplateArgument) == 0) && len(templateFormat) == 0 {
return nil, NoCompatiblePrinterError{f}
return nil, NoCompatiblePrinterError{Options: f, OutputFormat: &templateFormat}
}
templateValue := ""
......@@ -68,7 +68,7 @@ func (f *GoTemplatePrintFlags) ToPrinter(templateFormat string) (ResourcePrinter
}
if _, supportedFormat := supportedFormats[templateFormat]; !supportedFormat {
return nil, NoCompatiblePrinterError{f}
return nil, NoCompatiblePrinterError{Options: f, OutputFormat: &templateFormat}
}
if len(templateValue) == 0 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment