wire printflags through additional cmds

pull/8/head
juanvallejo 2018-04-18 20:02:37 -04:00
parent 29630b5124
commit 27bd4ded04
42 changed files with 746 additions and 388 deletions

View File

@ -2843,7 +2843,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 +2851,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 +4647,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'

View File

@ -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)
})
}

View File

@ -18,8 +18,13 @@ package cmd
import (
"fmt"
"io"
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
@ -27,9 +32,7 @@ import (
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/kubernetes/pkg/printers"
)
var (
@ -48,22 +51,46 @@ var (
)
type AutoscaleOptions struct {
FilenameOptions resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags
FilenameOptions *resource.FilenameOptions
Recorder genericclioptions.Recorder
RecordFlags *genericclioptions.RecordFlags
Recorder genericclioptions.Recorder
PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Builder *resource.Builder
CanBeAutoscaled func(kind schema.GroupKind) error
CreateAnnotation bool
DryRun bool
EnforceNamespace bool
Mapper meta.RESTMapper
Typer runtime.ObjectTyper
ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
GeneratorFunc func(string, *meta.RESTMapping) (kubectl.StructuredGenerator, error)
Namespace string
BuilderArgs []string
genericclioptions.IOStreams
}
func NewAutoscaleOptions() *AutoscaleOptions {
func NewAutoscaleOptions(ioStreams genericclioptions.IOStreams) *AutoscaleOptions {
return &AutoscaleOptions{
FilenameOptions: resource.FilenameOptions{},
PrintFlags: printers.NewPrintFlags("autoscaled"),
FilenameOptions: &resource.FilenameOptions{},
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
IOStreams: ioStreams,
}
}
func NewCmdAutoscale(f cmdutil.Factory, out io.Writer) *cobra.Command {
o := NewAutoscaleOptions()
func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewAutoscaleOptions(ioStreams)
validArgs := []string{"deployment", "replicaset", "replicationcontroller"}
argAliases := kubectl.ResourceAliases(validArgs)
@ -75,8 +102,9 @@ func NewCmdAutoscale(f cmdutil.Factory, out io.Writer) *cobra.Command {
Long: autoscaleLong,
Example: autoscaleExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.RunAutoscale(f, out, cmd, args))
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd))
cmdutil.CheckErr(o.Run())
},
ValidArgs: validArgs,
ArgAliases: argAliases,
@ -84,8 +112,8 @@ func NewCmdAutoscale(f cmdutil.Factory, out io.Writer) *cobra.Command {
// bind flag structs
o.RecordFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().String("generator", cmdutil.HorizontalPodAutoscalerV1GeneratorName, i18n.T("The name of the API generator to use. Currently there is only 1 generator."))
cmd.Flags().Int32("min", -1, "The lower limit for the number of pods that can be set by the autoscaler. If it's not specified or negative, the server will apply a default value.")
cmd.Flags().Int32("max", -1, "The upper limit for the number of pods that can be set by the autoscaler. Required.")
@ -94,73 +122,105 @@ func NewCmdAutoscale(f cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.Flags().String("name", "", i18n.T("The name for the newly created object. If not specified, the name of the input resource will be used."))
cmdutil.AddDryRunFlag(cmd)
usage := "identifying the resource to autoscale."
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, usage)
cmdutil.AddApplyAnnotationFlags(cmd)
return cmd
}
func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
var err error
func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
o.DryRun = cmdutil.GetFlagBool(cmd, "dry-run")
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.Builder = f.NewBuilder()
o.CanBeAutoscaled = f.CanBeAutoscaled
o.Mapper, o.Typer = f.Object()
o.ClientForMapping = f.ClientForMapping
o.BuilderArgs = args
o.RecordFlags.Complete(f.Command(cmd, false))
var err error
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
// get the generator
o.GeneratorFunc = func(name string, mapping *meta.RESTMapping) (kubectl.StructuredGenerator, error) {
var generator kubectl.StructuredGenerator
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
case cmdutil.HorizontalPodAutoscalerV1GeneratorName:
generator = &kubectl.HorizontalPodAutoscalerGeneratorV1{
Name: name,
MinReplicas: cmdutil.GetFlagInt32(cmd, "min"),
MaxReplicas: cmdutil.GetFlagInt32(cmd, "max"),
CPUPercent: cmdutil.GetFlagInt32(cmd, "cpu-percent"),
ScaleRefName: name,
ScaleRefKind: mapping.GroupVersionKind.Kind,
ScaleRefApiVersion: mapping.GroupVersionKind.GroupVersion().String(),
}
default:
return nil, cmdutil.UsageErrorf(cmd, "Generator %s not supported. ", generatorName)
}
return generator, nil
}
o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
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 nil
}
func (o *AutoscaleOptions) Validate(cmd *cobra.Command) error {
if err := validateFlags(cmd); err != nil {
return err
}
return nil
}
func (o *AutoscaleOptions) RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
namespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
// validate flags
if err := validateFlags(cmd); err != nil {
return err
}
r := f.NewBuilder().
func (o *AutoscaleOptions) Run() error {
r := o.Builder.
Internal().
ContinueOnError().
NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(false, args...).
NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, o.FilenameOptions).
ResourceTypeOrNameArgs(false, o.BuilderArgs...).
Flatten().
Do()
err = r.Err()
if err != nil {
if err := r.Err(); err != nil {
return err
}
count := 0
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
}
mapping := info.ResourceMapping()
if err := f.CanBeAutoscaled(mapping.GroupVersionKind.GroupKind()); err != nil {
if err := o.CanBeAutoscaled(mapping.GroupVersionKind.GroupKind()); err != nil {
return err
}
// get the generator
var generator kubectl.StructuredGenerator
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
case cmdutil.HorizontalPodAutoscalerV1GeneratorName:
generator = &kubectl.HorizontalPodAutoscalerGeneratorV1{
Name: info.Name,
MinReplicas: cmdutil.GetFlagInt32(cmd, "min"),
MaxReplicas: cmdutil.GetFlagInt32(cmd, "max"),
CPUPercent: cmdutil.GetFlagInt32(cmd, "cpu-percent"),
ScaleRefName: info.Name,
ScaleRefKind: mapping.GroupVersionKind.Kind,
ScaleRefApiVersion: mapping.GroupVersionKind.GroupVersion().String(),
}
default:
return cmdutil.UsageErrorf(cmd, "Generator %s not supported. ", generatorName)
generator, err := o.GeneratorFunc(info.Name, mapping)
if err != nil {
return err
}
// Generate new object
@ -169,11 +229,10 @@ func (o *AutoscaleOptions) RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *c
return err
}
mapper, typer := f.Object()
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(),
}
hpa, err := resourceMapper.InfoForObject(object, nil)
@ -183,26 +242,33 @@ func (o *AutoscaleOptions) RunAutoscale(f cmdutil.Factory, out io.Writer, cmd *c
if err := o.Recorder.Record(hpa.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
}
if cmdutil.GetDryRunFlag(cmd) {
return cmdutil.PrintObject(cmd, object, out)
object = hpa.Object
if o.DryRun {
count++
printer, err := o.ToPrinter("created")
if err != nil {
return err
}
return printer.PrintObj(hpa.AsVersioned(), o.Out)
}
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), hpa.Object, cmdutil.InternalVersionJSONEncoder()); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(o.CreateAnnotation, hpa.Object, cmdutil.InternalVersionJSONEncoder()); err != nil {
return err
}
object, err = resource.NewHelper(hpa.Client, hpa.Mapping).Create(namespace, false, object)
_, err = resource.NewHelper(hpa.Client, hpa.Mapping).Create(o.Namespace, false, object)
if err != nil {
return err
}
count++
if len(cmdutil.GetFlagString(cmd, "output")) > 0 {
return cmdutil.PrintObject(cmd, object, out)
printer, err := o.ToPrinter("autoscaled")
if err != nil {
return err
}
cmdutil.PrintSuccess(false, out, info.Object, cmdutil.GetDryRunFlag(cmd), "autoscaled")
return nil
return printer.PrintObj(info.AsVersioned(), o.Out)
})
if err != nil {
return err

View File

@ -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,58 @@ 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
clientSetFunc func() (internalclientset.Interface, error)
builderFunc func() *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.builderFunc = f.NewBuilder
o.clientSetFunc = f.ClientSet
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 +114,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 +126,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.builderFunc, o.clientSetFunc, force, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool) {
var alreadyApproved bool
for _, c := range csr.Status.Conditions {
if c.Type == certificates.CertificateApproved {
@ -106,7 +135,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 +143,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 +164,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 +176,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.builderFunc, o.clientSetFunc, force, func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool) {
var alreadyDenied bool
for _, c := range csr.Status.Conditions {
if c.Type == certificates.CertificateDenied {
@ -153,7 +185,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 +193,17 @@ 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(builderFunc func() *resource.Builder, clientSetFunc func() (internalclientset.Interface, error), force bool, modify func(csr *certificates.CertificateSigningRequest) (*certificates.CertificateSigningRequest, bool)) error {
var found int
c, err := f.ClientSet()
c, err := clientSetFunc()
if err != nil {
return err
}
r := f.NewBuilder().
r := builderFunc().
Internal().
ContinueOnError().
FilenameParam(false, &options.FilenameOptions).
@ -185,7 +217,7 @@ func (options *CertificateOptions) modifyCertificateCondition(f cmdutil.Factory,
return err
}
csr := info.Object.(*certificates.CertificateSigningRequest)
csr, hasCondition, verb := modify(csr)
csr, hasCondition := modify(csr)
if !hasCondition || force {
csr, err = c.Certificates().
CertificateSigningRequests().
@ -195,11 +227,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
}

View File

@ -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

View File

@ -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
}

View File

@ -24,6 +24,7 @@ import (
"testing"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
func TestSetupOutputWriterNoOp(t *testing.T) {
@ -33,7 +34,7 @@ func TestSetupOutputWriterNoOp(t *testing.T) {
f := cmdtesting.NewTestFactory()
defer f.Cleanup()
cmd := NewCmdClusterInfoDump(f, os.Stdout)
cmd := NewCmdClusterInfoDump(f, genericclioptions.IOStreams{Out: os.Stdout, ErrOut: os.Stderr})
cmd.Flag("output-directory").Value.Set(test)
writer := setupOutputWriter(cmd, out, "/some/file/that/should/be/ignored")
if writer != out {
@ -55,7 +56,7 @@ func TestSetupOutputWriterFile(t *testing.T) {
f := cmdtesting.NewTestFactory()
defer f.Cleanup()
cmd := NewCmdClusterInfoDump(f, os.Stdout)
cmd := NewCmdClusterInfoDump(f, genericclioptions.IOStreams{Out: os.Stdout, ErrOut: os.Stderr})
cmd.Flag("output-directory").Value.Set(dir)
writer := setupOutputWriter(cmd, out, file)
if writer == out {

View File

@ -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, ""),
},

View File

@ -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

View File

@ -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())
}
})

View File

@ -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

View File

@ -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"
)
@ -526,7 +527,8 @@ func TestCopyToPod(t *testing.T) {
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdCp(tf, buf, errBuf)
cmd := NewCmdCp(tf, genericclioptions.IOStreams{Out: buf, ErrOut: errBuf})
srcFile, err := ioutil.TempDir("", "test")
if err != nil {
@ -554,6 +556,7 @@ func TestCopyToPod(t *testing.T) {
}
for name, test := range tests {
opts := NewCopyOptions(genericclioptions.IOStreams{Out: buf, ErrOut: errBuf})
src := fileSpec{
File: srcFile,
}
@ -562,8 +565,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.

View File

@ -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)
}
}

View File

@ -32,6 +32,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 +52,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 +86,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
}{
@ -197,7 +199,7 @@ func TestCordon(t *testing.T) {
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := test.cmd(tf, buf)
cmd := test.cmd(tf, genericclioptions.IOStreams{Out: buf, ErrOut: buf})
saw_fatal := false
func() {
@ -708,7 +710,7 @@ func TestDrain(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
errBuf := bytes.NewBuffer([]byte{})
cmd := NewCmdDrain(tf, buf, errBuf)
cmd := NewCmdDrain(tf, genericclioptions.IOStreams{Out: buf, ErrOut: errBuf})
saw_fatal := false
fatal_msg := ""
@ -833,9 +835,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)

View File

@ -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.")

View File

@ -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
}
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
func (o *ExposeServiceOptions) RunExpose(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
namespace, enforceNamespace, err := f.DefaultNamespace()
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

View File

@ -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) {

View File

@ -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
})
}

View File

@ -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"
)
@ -328,13 +329,13 @@ func TestLabelErrors(t *testing.T) {
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLabel(tf, buf, buf)
cmd := NewCmdLabel(tf, genericclioptions.IOStreams{Out: buf, ErrOut: buf})
cmd.SetOutput(buf)
for k, v := range testCase.flags {
cmd.Flags().Set(k, v)
}
opts := NewLabelOptions(buf, buf)
opts := NewLabelOptions(genericclioptions.IOStreams{Out: buf, ErrOut: buf})
err := opts.Complete(tf, cmd, testCase.args)
if err == nil {
err = opts.Validate()
@ -390,8 +391,8 @@ func TestLabelForResourceFromFile(t *testing.T) {
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLabel(tf, buf, buf)
opts := NewLabelOptions(buf, buf)
cmd := NewCmdLabel(tf, genericclioptions.IOStreams{Out: buf, ErrOut: buf})
opts := NewLabelOptions(genericclioptions.IOStreams{Out: buf, ErrOut: buf})
opts.Filenames = []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}
err := opts.Complete(tf, cmd, []string{"a=b"})
if err == nil {
@ -423,8 +424,8 @@ func TestLabelLocal(t *testing.T) {
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdLabel(tf, buf, buf)
opts := NewLabelOptions(buf, buf)
cmd := NewCmdLabel(tf, genericclioptions.IOStreams{Out: buf, ErrOut: buf})
opts := NewLabelOptions(genericclioptions.IOStreams{Out: buf, ErrOut: buf})
opts.Filenames = []string{"../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}
opts.local = true
err := opts.Complete(tf, cmd, []string{"a=b"})
@ -481,9 +482,9 @@ func TestLabelMultipleObjects(t *testing.T) {
tf.ClientConfigVal = defaultClientConfig()
buf := bytes.NewBuffer([]byte{})
opts := NewLabelOptions(buf, buf)
opts := NewLabelOptions(genericclioptions.IOStreams{Out: buf, ErrOut: buf})
opts.all = true
cmd := NewCmdLabel(tf, buf, buf)
cmd := NewCmdLabel(tf, genericclioptions.IOStreams{Out: buf, ErrOut: buf})
err := opts.Complete(tf, cmd, []string{"pods", "a=b"})
if err == nil {
err = opts.Validate()

View File

@ -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"

View File

@ -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

View File

@ -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())
}
}

View File

@ -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

View File

@ -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

View File

@ -4,7 +4,7 @@ args:
- service/svc1
namespace: myproject
expectedStdout:
- "service \"svc1\" edited"
- "service/svc1 edited"
expectedExitCode: 0
steps:
- type: request

View File

@ -6,7 +6,7 @@ args:
outputFormat: yaml
namespace: myproject
expectedStdout:
- service "svc1" edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request

View File

@ -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

View File

@ -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

View File

@ -5,7 +5,7 @@ args:
- svc1
namespace: edit-test
expectedStdout:
- service "svc1" edited
- service/svc1 edited
expectedStderr:
- "error: services \"svc1\" is invalid"
expectedExitCode: 0

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -9,7 +9,7 @@ args:
saveConfig: "false"
namespace: edit-test
expectedStdout:
- service "svc1" edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request

View File

@ -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

View File

@ -8,7 +8,7 @@ args:
- svc1
namespace: edit-test
expectedStdout:
- service "svc1" edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request

View File

@ -4,7 +4,7 @@ args:
- service/kubernetes
namespace: default
expectedStdout:
- "service \"kubernetes\" edited"
- "service/kubernetes edited"
expectedExitCode: 0
steps:
- type: request

View File

@ -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

View File

@ -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

View File

@ -9,7 +9,7 @@ args:
saveConfig: "true"
namespace: edit-test
expectedStdout:
- service "svc1" edited
- service/svc1 edited
expectedExitCode: 0
steps:
- type: request

View File

@ -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

View File

@ -79,6 +79,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 := ""