Remove Factory from more Run commands

This commit is contained in:
Maciej Szulik 2018-04-26 15:30:21 +02:00
parent 03c5f298f3
commit b7d4f15965
No known key found for this signature in database
GPG Key ID: F15E55D276FA84C4
19 changed files with 611 additions and 642 deletions

View File

@ -3132,7 +3132,7 @@ run_deployment_tests() {
# Set env of deployments for all container
kubectl set env deployment nginx-deployment env=prod "${kube_flags[@]}"
# Set env of deployments for specific container
kubectl set env deployment nginx-deployment env=prod -c=nginx "${kube_flags[@]}"
kubectl set env deployment nginx-deployment superenv=superprod -c=nginx "${kube_flags[@]}"
# Set env of deployments by configmap
kubectl set env deployment nginx-deployment --from=configmap/test-set-env-config "${kube_flags[@]}"
# Set env of deployments by secret

View File

@ -59,10 +59,15 @@ type AnnotateOptions struct {
outputFormat string
// results of arg parsing
resources []string
newAnnotations map[string]string
removeAnnotations []string
Recorder genericclioptions.Recorder
resources []string
newAnnotations map[string]string
removeAnnotations []string
Recorder genericclioptions.Recorder
namespace string
enforceNamespace bool
builder *resource.Builder
unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
includeUninitialized bool
genericclioptions.IOStreams
}
@ -74,7 +79,7 @@ var (
All Kubernetes objects support the ability to store additional data with the object as
annotations. Annotations are key/value pairs that can be larger than labels and include
arbitrary string values such as structured JSON. Tools and system extensions may use
annotations to store their own data.
annotations to store their own data.
Attempting to set an annotation that already exists will fail unless --overwrite is set.
If --resource-version is specified and does not match the current resource version on
@ -123,13 +128,9 @@ func NewCmdAnnotate(parent string, f cmdutil.Factory, ioStreams genericclioption
Long: annotateLong + "\n\n" + cmdutil.SuggestApiResources(parent),
Example: annotateExample,
Run: func(cmd *cobra.Command, args []string) {
if err := o.Complete(f, cmd, args); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, "%v", err))
}
if err := o.Validate(); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, "%v", err))
}
cmdutil.CheckErr(o.RunAnnotate(f, cmd))
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunAnnotate())
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
@ -176,6 +177,14 @@ func (o *AnnotateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [
return printer.PrintObj(obj, out)
}
o.namespace, o.enforceNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
o.includeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, false)
o.builder = f.NewBuilder()
o.unstructuredClientForMapping = f.UnstructuredClientForMapping
// 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")
@ -184,7 +193,11 @@ func (o *AnnotateOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args [
}
o.resources = resources
o.newAnnotations, o.removeAnnotations, err = parseAnnotations(annotationArgs)
return err
if err != nil {
return err
}
return nil
}
// Validate checks to the AnnotateOptions to see if there is sufficient information run the command.
@ -202,20 +215,14 @@ func (o AnnotateOptions) Validate() error {
}
// RunAnnotate does the work
func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) error {
namespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
b := f.NewBuilder().
func (o AnnotateOptions) RunAnnotate() error {
b := o.builder.
Unstructured().
LocalParam(o.local).
ContinueOnError().
NamespaceParam(namespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(includeUninitialized).
NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(o.includeUninitialized).
Flatten()
if !o.local {
@ -276,7 +283,7 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro
}
mapping := info.ResourceMapping()
client, err := f.UnstructuredClientForMapping(mapping)
client, err := o.unstructuredClientForMapping(mapping)
if err != nil {
return err
}

View File

@ -499,7 +499,7 @@ func TestAnnotateObject(t *testing.T) {
if err := options.Validate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.RunAnnotate(tf, cmd); err != nil {
if err := options.RunAnnotate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
@ -554,7 +554,7 @@ func TestAnnotateObjectFromFile(t *testing.T) {
if err := options.Validate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.RunAnnotate(tf, cmd); err != nil {
if err := options.RunAnnotate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
@ -586,7 +586,7 @@ func TestAnnotateLocal(t *testing.T) {
if err := options.Validate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.RunAnnotate(tf, cmd); err != nil {
if err := options.RunAnnotate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}
@ -642,7 +642,7 @@ func TestAnnotateMultipleObjects(t *testing.T) {
if err := options.Validate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if err := options.RunAnnotate(tf, cmd); err != nil {
if err := options.RunAnnotate(); err != nil {
t.Fatalf("unexpected error: %v", err)
}
}

View File

@ -18,14 +18,15 @@ package cmd
import (
"fmt"
"io"
"sort"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/discovery"
"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"
)
@ -35,37 +36,54 @@ var (
kubectl api-versions`))
)
func NewCmdApiVersions(f cmdutil.Factory, out io.Writer) *cobra.Command {
type ApiVersionsOptions struct {
discoveryClient discovery.CachedDiscoveryInterface
genericclioptions.IOStreams
}
func NewApiVersionsOptions(ioStreams genericclioptions.IOStreams) *ApiVersionsOptions {
return &ApiVersionsOptions{
IOStreams: ioStreams,
}
}
func NewCmdApiVersions(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewApiVersionsOptions(ioStreams)
cmd := &cobra.Command{
Use: "api-versions",
Short: "Print the supported API versions on the server, in the form of \"group/version\"",
Long: "Print the supported API versions on the server, in the form of \"group/version\"",
Example: apiversionsExample,
Run: func(cmd *cobra.Command, args []string) {
err := RunApiVersions(f, out)
cmdutil.CheckErr(err)
cmdutil.CheckErr(o.Complete(f))
cmdutil.CheckErr(o.RunApiVersions())
},
}
return cmd
}
func RunApiVersions(f cmdutil.Factory, w io.Writer) error {
discoveryclient, err := f.DiscoveryClient()
func (o *ApiVersionsOptions) Complete(f cmdutil.Factory) error {
var err error
o.discoveryClient, err = f.DiscoveryClient()
if err != nil {
return err
}
return nil
}
func (o *ApiVersionsOptions) RunApiVersions() error {
// Always request fresh data from the server
discoveryclient.Invalidate()
o.discoveryClient.Invalidate()
groupList, err := discoveryclient.ServerGroups()
groupList, err := o.discoveryClient.ServerGroups()
if err != nil {
return fmt.Errorf("Couldn't get available api versions from server: %v\n", err)
}
apiVersions := metav1.ExtractGroupVersions(groupList)
sort.Strings(apiVersions)
for _, v := range apiVersions {
fmt.Fprintln(w, v)
fmt.Fprintln(o.Out, v)
}
return nil
}

View File

@ -18,19 +18,14 @@ package cmd
import (
"bytes"
"encoding/json"
"fmt"
"io"
"github.com/ghodss/yaml"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
apijson "k8s.io/apimachinery/pkg/util/json"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -39,21 +34,27 @@ 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"
)
type SetLastAppliedOptions struct {
FilenameOptions resource.FilenameOptions
Selector string
InfoList []*resource.Info
Mapper meta.RESTMapper
Namespace string
EnforceNamespace bool
DryRun bool
ShortOutput bool
CreateAnnotation bool
Output string
PatchBufferList []PatchBuffer
Factory cmdutil.Factory
PrintFlags *printers.PrintFlags
PrintObj printers.ResourcePrinterFunc
FilenameOptions resource.FilenameOptions
infoList []*resource.Info
mapper meta.RESTMapper
namespace string
enforceNamespace bool
dryRun bool
shortOutput bool
output string
patchBufferList []PatchBuffer
builder *resource.Builder
unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
genericclioptions.IOStreams
}
@ -83,12 +84,13 @@ var (
func NewSetLastAppliedOptions(ioStreams genericclioptions.IOStreams) *SetLastAppliedOptions {
return &SetLastAppliedOptions{
IOStreams: ioStreams,
PrintFlags: printers.NewPrintFlags("configured"),
IOStreams: ioStreams,
}
}
func NewCmdApplySetLastApplied(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
options := NewSetLastAppliedOptions(ioStreams)
o := NewSetLastAppliedOptions(ioStreams)
cmd := &cobra.Command{
Use: "set-last-applied -f FILENAME",
DisableFlagsInUseLine: true,
@ -96,38 +98,55 @@ func NewCmdApplySetLastApplied(f cmdutil.Factory, ioStreams genericclioptions.IO
Long: applySetLastAppliedLong,
Example: applySetLastAppliedExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd))
cmdutil.CheckErr(options.Validate(f, cmd))
cmdutil.CheckErr(options.RunSetLastApplied(f, cmd))
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunSetLastApplied())
},
}
cmdutil.AddDryRunFlag(cmd)
cmdutil.AddPrinterFlags(cmd)
cmd.Flags().BoolVar(&options.CreateAnnotation, "create-annotation", options.CreateAnnotation, "Will create 'last-applied-configuration' annotations if current objects doesn't have one")
usage := "that contains the last-applied-configuration annotations"
kubectl.AddJsonFilenameFlag(cmd, &options.FilenameOptions.Filenames, "Filename, directory, or URL to files "+usage)
cmd.Flags().BoolVar(&o.CreateAnnotation, "create-annotation", o.CreateAnnotation, "Will create 'last-applied-configuration' annotations if current objects doesn't have one")
kubectl.AddJsonFilenameFlag(cmd, &o.FilenameOptions.Filenames, "Filename, directory, or URL to files that contains the last-applied-configuration annotations")
return cmd
}
func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.DryRun = cmdutil.GetDryRunFlag(cmd)
o.Output = cmdutil.GetFlagString(cmd, "output")
o.ShortOutput = o.Output == "name"
o.Mapper = f.RESTMapper()
o.dryRun = cmdutil.GetDryRunFlag(cmd)
o.output = cmdutil.GetFlagString(cmd, "output")
o.shortOutput = o.output == "name"
o.mapper = f.RESTMapper()
var err error
o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace()
return err
o.namespace, o.enforceNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
o.builder = f.NewBuilder()
o.unstructuredClientForMapping = f.UnstructuredClientForMapping
if o.dryRun {
// TODO(juanvallejo): This can be cleaned up even further by creating
// a PrintFlags struct that binds the --dry-run flag, and whose
// ToPrinter method returns a printer that understands how to print
// this success message.
o.PrintFlags.Complete("%s (dry run)")
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = printer.PrintObj
return nil
}
func (o *SetLastAppliedOptions) Validate(f cmdutil.Factory, cmd *cobra.Command) error {
r := f.NewBuilder().
func (o *SetLastAppliedOptions) Validate() error {
r := o.builder.
Unstructured().
NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.enforceNamespace, &o.FilenameOptions).
Flatten().
Do()
@ -153,14 +172,14 @@ func (o *SetLastAppliedOptions) Validate(f cmdutil.Factory, cmd *cobra.Command)
return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%s\nfrom server for:", info.String()), info.Source, err)
}
if originalBuf == nil && !o.CreateAnnotation {
return cmdutil.UsageErrorf(cmd, "no last-applied-configuration annotation found on resource: %s, to create the annotation, run the command with --create-annotation", info.Name)
return fmt.Errorf("no last-applied-configuration annotation found on resource: %s, to create the annotation, run the command with --create-annotation", info.Name)
}
//only add to PatchBufferList when changed
if !bytes.Equal(cmdutil.StripComments(originalBuf), cmdutil.StripComments(diffBuf)) {
p := PatchBuffer{Patch: patchBuf, PatchType: patchType}
o.PatchBufferList = append(o.PatchBufferList, p)
o.InfoList = append(o.InfoList, info)
o.patchBufferList = append(o.patchBufferList, p)
o.infoList = append(o.infoList, info)
} else {
fmt.Fprintf(o.Out, "set-last-applied %s: no changes required.\n", info.Name)
}
@ -170,68 +189,25 @@ func (o *SetLastAppliedOptions) Validate(f cmdutil.Factory, cmd *cobra.Command)
return err
}
func (o *SetLastAppliedOptions) RunSetLastApplied(f cmdutil.Factory, cmd *cobra.Command) error {
for i, patch := range o.PatchBufferList {
info := o.InfoList[i]
if !o.DryRun {
func (o *SetLastAppliedOptions) RunSetLastApplied() error {
for i, patch := range o.patchBufferList {
info := o.infoList[i]
if !o.dryRun {
mapping := info.ResourceMapping()
client, err := f.UnstructuredClientForMapping(mapping)
client, err := o.unstructuredClientForMapping(mapping)
if err != nil {
return err
}
helper := resource.NewHelper(client, mapping)
patchedObj, err := helper.Patch(o.Namespace, info.Name, patch.PatchType, patch.Patch)
patchedObj, err := helper.Patch(o.namespace, info.Name, patch.PatchType, patch.Patch)
if err != nil {
return err
}
if len(o.Output) > 0 && !o.ShortOutput {
info.Refresh(patchedObj, false)
return cmdutil.PrintObject(cmd, info.Object, o.Out)
}
cmdutil.PrintSuccess(o.ShortOutput, o.Out, info.Object, o.DryRun, "configured")
} else {
err := o.formatPrinter(o.Output, patch.Patch, o.Out)
if err != nil {
return err
}
cmdutil.PrintSuccess(o.ShortOutput, o.Out, info.Object, o.DryRun, "configured")
info.Refresh(patchedObj, false)
}
}
return nil
}
func (o *SetLastAppliedOptions) formatPrinter(output string, buf []byte, w io.Writer) error {
yamlOutput, err := yaml.JSONToYAML(buf)
if err != nil {
return err
}
switch output {
case "json":
jsonBuffer := &bytes.Buffer{}
err = json.Indent(jsonBuffer, buf, "", " ")
if err != nil {
if err := o.PrintObj(info.AsVersioned(legacyscheme.Scheme), o.Out); err != nil {
return err
}
fmt.Fprintf(w, "%s\n", jsonBuffer.String())
case "yaml":
fmt.Fprintf(w, "%s\n", string(yamlOutput))
}
return nil
}
func (o *SetLastAppliedOptions) getPatch(info *resource.Info) ([]byte, []byte, error) {
objMap := map[string]map[string]map[string]string{}
metadataMap := map[string]map[string]string{}
annotationsMap := map[string]string{}
localFile, err := runtime.Encode(scheme.DefaultJSONEncoder(), info.Object)
if err != nil {
return nil, localFile, err
}
annotationsMap[api.LastAppliedConfigAnnotation] = string(localFile)
metadataMap["annotations"] = annotationsMap
objMap["metadata"] = metadataMap
jsonString, err := apijson.Marshal(objMap)
return jsonString, localFile, err
}

View File

@ -1121,7 +1121,7 @@ func TestRunApplySetLastApplied(t *testing.T) {
name: "set with exist object",
filePath: filenameRC,
expectedErr: "",
expectedOut: "replicationcontroller/test-rc\n",
expectedOut: "replicationcontroller/test-rc configured\n",
output: "name",
},
{
@ -1134,7 +1134,7 @@ func TestRunApplySetLastApplied(t *testing.T) {
{
name: "set for the annotation does not exist on the live object",
filePath: filenameRCNoAnnotation,
expectedErr: "error: no last-applied-configuration annotation found on resource: no-annotation, to create the annotation, run the command with --create-annotation\nSee 'set-last-applied -h' for help and examples.",
expectedErr: "error: no last-applied-configuration annotation found on resource: no-annotation, to create the annotation, run the command with --create-annotation",
expectedOut: "",
output: "name",
},
@ -1142,14 +1142,14 @@ func TestRunApplySetLastApplied(t *testing.T) {
name: "set with exist object output json",
filePath: filenameRCJSON,
expectedErr: "",
expectedOut: "replicationcontroller/test-rc\n",
expectedOut: "replicationcontroller/test-rc configured\n",
output: "name",
},
{
name: "set test for a directory of files",
filePath: dirName,
expectedErr: "",
expectedOut: "replicationcontroller/test-rc\nreplicationcontroller/test-rc\n",
expectedOut: "replicationcontroller/test-rc configured\nreplicationcontroller/test-rc configured\n",
output: "name",
},
}

View File

@ -24,7 +24,6 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
@ -59,20 +58,22 @@ type AutoscaleOptions struct {
PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Builder *resource.Builder
CanBeAutoscaled func(kind schema.GroupKind) error
Name string
Generator string
Min int32
Max int32
CpuPercent int32
CreateAnnotation bool
DryRun bool
EnforceNamespace bool
Mapper meta.RESTMapper
ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
GeneratorFunc func(string, *meta.RESTMapping) (kubectl.StructuredGenerator, error)
Namespace string
BuilderArgs []string
createAnnotation bool
args []string
enforceNamespace bool
namespace string
dryRun bool
builder *resource.Builder
mapper meta.RESTMapper
canBeAutoscaled func(kind schema.GroupKind) error
clientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
generatorFunc func(string, *meta.RESTMapping) (kubectl.StructuredGenerator, error)
genericclioptions.IOStreams
}
@ -102,7 +103,7 @@ func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *
Example: autoscaleExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
ValidArgs: validArgs,
@ -113,27 +114,26 @@ func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *
o.RecordFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(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.")
cmd.Flags().StringVar(&o.Generator, "generator", cmdutil.HorizontalPodAutoscalerV1GeneratorName, i18n.T("The name of the API generator to use. Currently there is only 1 generator."))
cmd.Flags().Int32Var(&o.Min, "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().Int32Var(&o.Max, "max", -1, "The upper limit for the number of pods that can be set by the autoscaler. Required.")
cmd.MarkFlagRequired("max")
cmd.Flags().Int32("cpu-percent", -1, fmt.Sprintf("The target average CPU utilization (represented as a percent of requested CPU) over all the pods. If it's not specified or negative, a default autoscaling policy will be used."))
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."))
cmd.Flags().Int32Var(&o.CpuPercent, "cpu-percent", -1, fmt.Sprintf("The target average CPU utilization (represented as a percent of requested CPU) over all the pods. If it's not specified or negative, a default autoscaling policy will be used."))
cmd.Flags().StringVar(&o.Name, "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, "identifying the resource to autoscale.")
cmdutil.AddApplyAnnotationFlags(cmd)
return cmd
}
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 = f.RESTMapper()
o.ClientForMapping = f.ClientForMapping
o.BuilderArgs = args
o.dryRun = cmdutil.GetFlagBool(cmd, "dry-run")
o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.builder = f.NewBuilder()
o.canBeAutoscaled = f.CanBeAutoscaled
o.mapper = f.RESTMapper()
o.clientForMapping = f.ClientForMapping
o.args = args
o.RecordFlags.Complete(f.Command(cmd, false))
var err error
@ -143,34 +143,31 @@ func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
}
// 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 {
o.generatorFunc = func(name string, mapping *meta.RESTMapping) (kubectl.StructuredGenerator, error) {
switch o.Generator {
case cmdutil.HorizontalPodAutoscalerV1GeneratorName:
generator = &kubectl.HorizontalPodAutoscalerGeneratorV1{
return &kubectl.HorizontalPodAutoscalerGeneratorV1{
Name: name,
MinReplicas: cmdutil.GetFlagInt32(cmd, "min"),
MaxReplicas: cmdutil.GetFlagInt32(cmd, "max"),
CPUPercent: cmdutil.GetFlagInt32(cmd, "cpu-percent"),
MinReplicas: o.Min,
MaxReplicas: o.Max,
CPUPercent: o.CpuPercent,
ScaleRefName: name,
ScaleRefKind: mapping.GroupVersionKind.Kind,
ScaleRefApiVersion: mapping.GroupVersionKind.GroupVersion().String(),
}
}, nil
default:
return nil, cmdutil.UsageErrorf(cmd, "Generator %s not supported. ", generatorName)
return nil, cmdutil.UsageErrorf(cmd, "Generator %s not supported. ", o.Generator)
}
return generator, nil
}
o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace()
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 {
if o.dryRun {
o.PrintFlags.Complete("%s (dry run)")
}
@ -185,21 +182,24 @@ func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
return nil
}
func (o *AutoscaleOptions) Validate(cmd *cobra.Command) error {
if err := validateFlags(cmd); err != nil {
return err
func (o *AutoscaleOptions) Validate() error {
if o.Max < 1 {
return fmt.Errorf("--max=MAXPODS is required and must be at least 1, max: %d", o.Max)
}
if o.Max < o.Min {
return fmt.Errorf("--max=MAXPODS must be larger or equal to --min=MINPODS, max: %d, min: %d", o.Max, o.Min)
}
return nil
}
func (o *AutoscaleOptions) Run() error {
r := o.Builder.
r := o.builder.
Internal(legacyscheme.Scheme).
ContinueOnError().
NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, o.FilenameOptions).
ResourceTypeOrNameArgs(false, o.BuilderArgs...).
NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.enforceNamespace, o.FilenameOptions).
ResourceTypeOrNameArgs(false, o.args...).
Flatten().
Do()
if err := r.Err(); err != nil {
@ -213,11 +213,11 @@ func (o *AutoscaleOptions) Run() error {
}
mapping := info.ResourceMapping()
if err := o.CanBeAutoscaled(mapping.GroupVersionKind.GroupKind()); err != nil {
if err := o.canBeAutoscaled(mapping.GroupVersionKind.GroupKind()); err != nil {
return err
}
generator, err := o.GeneratorFunc(info.Name, mapping)
generator, err := o.generatorFunc(info.Name, mapping)
if err != nil {
return err
}
@ -229,8 +229,8 @@ func (o *AutoscaleOptions) Run() error {
}
resourceMapper := &resource.Mapper{
RESTMapper: o.Mapper,
ClientMapper: resource.ClientMapperFunc(o.ClientForMapping),
RESTMapper: o.mapper,
ClientMapper: resource.ClientMapperFunc(o.clientForMapping),
Decoder: cmdutil.InternalVersionDecoder(),
}
hpa, err := resourceMapper.InfoForObject(object, legacyscheme.Scheme, nil)
@ -242,7 +242,7 @@ func (o *AutoscaleOptions) Run() error {
}
object = hpa.Object
if o.DryRun {
if o.dryRun {
count++
printer, err := o.ToPrinter("created")
@ -252,11 +252,11 @@ func (o *AutoscaleOptions) Run() error {
return printer.PrintObj(hpa.AsVersioned(legacyscheme.Scheme), o.Out)
}
if err := kubectl.CreateOrUpdateAnnotation(o.CreateAnnotation, hpa.Object, cmdutil.InternalVersionJSONEncoder()); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(o.createAnnotation, hpa.Object, cmdutil.InternalVersionJSONEncoder()); err != nil {
return err
}
_, err = resource.NewHelper(hpa.Client, hpa.Mapping).Create(o.Namespace, false, object)
_, err = resource.NewHelper(hpa.Client, hpa.Mapping).Create(o.namespace, false, object)
if err != nil {
return err
}
@ -276,15 +276,3 @@ func (o *AutoscaleOptions) Run() error {
}
return nil
}
func validateFlags(cmd *cobra.Command) error {
errs := []error{}
max, min := cmdutil.GetFlagInt32(cmd, "max"), cmdutil.GetFlagInt32(cmd, "min")
if max < 1 {
errs = append(errs, fmt.Errorf("--max=MAXPODS is required and must be at least 1, max: %d", max))
}
if max < min {
errs = append(errs, fmt.Errorf("--max=MAXPODS must be larger or equal to --min=MINPODS, max: %d, min: %d", max, min))
}
return utilerrors.NewAggregate(errs)
}

View File

@ -21,11 +21,15 @@ import (
"io"
"os"
"path"
"time"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
restclient "k8s.io/client-go/rest"
api "k8s.io/kubernetes/pkg/apis/core"
"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"
@ -37,6 +41,15 @@ type ClusterInfoDumpOptions struct {
PrintFlags *printers.PrintFlags
PrintObj printers.ResourcePrinterFunc
OutputDir string
AllNamespaces bool
Namespaces []string
timeout time.Duration
clientset internalclientset.Interface
namespace string
logsForObject func(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error)
genericclioptions.IOStreams
}
@ -54,13 +67,13 @@ func NewCmdClusterInfoDump(f cmdutil.Factory, ioStreams genericclioptions.IOStre
Long: dumpLong,
Example: dumpExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete())
cmdutil.CheckErr(o.Run(f, cmd))
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.Run())
},
}
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"))
cmd.Flags().StringSlice("namespaces", []string{}, "A comma separated list of namespaces to dump.")
cmd.Flags().Bool("all-namespaces", false, "If true, dump all namespaces. If true, --namespaces is ignored.")
cmd.Flags().StringVar(&o.OutputDir, "output-directory", "", i18n.T("Where to output the files. If empty or '-' uses stdout, otherwise creates a directory hierarchy in that directory"))
cmd.Flags().StringSliceVar(&o.Namespaces, "namespaces", []string{}, "A comma separated list of namespaces to dump.")
cmd.Flags().BoolVar(&o.AllNamespaces, "all-namespaces", false, "If true, dump all namespaces. If true, --namespaces is ignored.")
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout)
return cmd
}
@ -89,8 +102,7 @@ var (
kubectl cluster-info dump --namespaces default,kube-system --output-directory=/path/to/cluster-state`))
)
func setupOutputWriter(cmd *cobra.Command, defaultWriter io.Writer, filename string) io.Writer {
dir := cmdutil.GetFlagString(cmd, "output-directory")
func setupOutputWriter(dir string, defaultWriter io.Writer, filename string) io.Writer {
if len(dir) == 0 || dir == "-" {
return defaultWriter
}
@ -103,7 +115,7 @@ func setupOutputWriter(cmd *cobra.Command, defaultWriter io.Writer, filename str
return file
}
func (o *ClusterInfoDumpOptions) Complete() error {
func (o *ClusterInfoDumpOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
@ -113,32 +125,36 @@ func (o *ClusterInfoDumpOptions) Complete() error {
o.PrintFlags.OutputFormat = &jsonOutputFmt
o.PrintObj = printer.PrintObj
o.timeout, err = cmdutil.GetPodRunningTimeoutFlag(cmd)
if err != nil {
return err
}
o.clientset, err = f.ClientSet()
if err != nil {
return err
}
o.namespace, _, err = f.DefaultNamespace()
if err != nil {
return err
}
o.logsForObject = f.LogsForObject
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())
}
clientset, err := f.ClientSet()
func (o *ClusterInfoDumpOptions) Run() error {
nodes, err := o.clientset.Core().Nodes().List(metav1.ListOptions{})
if err != nil {
return err
}
nodes, err := clientset.Core().Nodes().List(metav1.ListOptions{})
if err != nil {
return err
}
if err := o.PrintObj(nodes, setupOutputWriter(cmd, o.Out, "nodes.json")); err != nil {
if err := o.PrintObj(nodes, setupOutputWriter(o.OutputDir, o.Out, "nodes.json")); err != nil {
return err
}
var namespaces []string
if cmdutil.GetFlagBool(cmd, "all-namespaces") {
namespaceList, err := clientset.Core().Namespaces().List(metav1.ListOptions{})
if o.AllNamespaces {
namespaceList, err := o.clientset.Core().Namespaces().List(metav1.ListOptions{})
if err != nil {
return err
}
@ -146,75 +162,70 @@ func (o *ClusterInfoDumpOptions) Run(f cmdutil.Factory, cmd *cobra.Command) erro
namespaces = append(namespaces, namespaceList.Items[ix].Name)
}
} else {
namespaces = cmdutil.GetFlagStringSlice(cmd, "namespaces")
if len(namespaces) == 0 {
cmdNamespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}
if len(o.Namespaces) == 0 {
namespaces = []string{
metav1.NamespaceSystem,
cmdNamespace,
o.namespace,
}
}
}
for _, namespace := range namespaces {
// TODO: this is repetitive in the extreme. Use reflection or
// something to make this a for loop.
events, err := clientset.Core().Events(namespace).List(metav1.ListOptions{})
events, err := o.clientset.Core().Events(namespace).List(metav1.ListOptions{})
if err != nil {
return err
}
if err := o.PrintObj(events, setupOutputWriter(cmd, o.Out, path.Join(namespace, "events.json"))); err != nil {
if err := o.PrintObj(events, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "events.json"))); err != nil {
return err
}
rcs, err := clientset.Core().ReplicationControllers(namespace).List(metav1.ListOptions{})
rcs, err := o.clientset.Core().ReplicationControllers(namespace).List(metav1.ListOptions{})
if err != nil {
return err
}
if err := o.PrintObj(rcs, setupOutputWriter(cmd, o.Out, path.Join(namespace, "replication-controllers.json"))); err != nil {
if err := o.PrintObj(rcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replication-controllers.json"))); err != nil {
return err
}
svcs, err := clientset.Core().Services(namespace).List(metav1.ListOptions{})
svcs, err := o.clientset.Core().Services(namespace).List(metav1.ListOptions{})
if err != nil {
return err
}
if err := o.PrintObj(svcs, setupOutputWriter(cmd, o.Out, path.Join(namespace, "services.json"))); err != nil {
if err := o.PrintObj(svcs, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "services.json"))); err != nil {
return err
}
sets, err := clientset.Extensions().DaemonSets(namespace).List(metav1.ListOptions{})
sets, err := o.clientset.Extensions().DaemonSets(namespace).List(metav1.ListOptions{})
if err != nil {
return err
}
if err := o.PrintObj(sets, setupOutputWriter(cmd, o.Out, path.Join(namespace, "daemonsets.json"))); err != nil {
if err := o.PrintObj(sets, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "daemonsets.json"))); err != nil {
return err
}
deps, err := clientset.Extensions().Deployments(namespace).List(metav1.ListOptions{})
deps, err := o.clientset.Extensions().Deployments(namespace).List(metav1.ListOptions{})
if err != nil {
return err
}
if err := o.PrintObj(deps, setupOutputWriter(cmd, o.Out, path.Join(namespace, "deployments.json"))); err != nil {
if err := o.PrintObj(deps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "deployments.json"))); err != nil {
return err
}
rps, err := clientset.Extensions().ReplicaSets(namespace).List(metav1.ListOptions{})
rps, err := o.clientset.Extensions().ReplicaSets(namespace).List(metav1.ListOptions{})
if err != nil {
return err
}
if err := o.PrintObj(rps, setupOutputWriter(cmd, o.Out, path.Join(namespace, "replicasets.json"))); err != nil {
if err := o.PrintObj(rps, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "replicasets.json"))); err != nil {
return err
}
pods, err := clientset.Core().Pods(namespace).List(metav1.ListOptions{})
pods, err := o.clientset.Core().Pods(namespace).List(metav1.ListOptions{})
if err != nil {
return err
}
if err := o.PrintObj(pods, setupOutputWriter(cmd, o.Out, path.Join(namespace, "pods.json"))); err != nil {
if err := o.PrintObj(pods, setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, "pods.json"))); err != nil {
return err
}
@ -222,7 +233,7 @@ func (o *ClusterInfoDumpOptions) Run(f cmdutil.Factory, cmd *cobra.Command) erro
writer.Write([]byte(fmt.Sprintf("==== START logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
defer writer.Write([]byte(fmt.Sprintf("==== END logs for container %s of pod %s/%s ====\n", container.Name, pod.Namespace, pod.Name)))
request, err := f.LogsForObject(pod, &api.PodLogOptions{Container: container.Name}, timeout)
request, err := o.logsForObject(pod, &api.PodLogOptions{Container: container.Name}, timeout)
if err != nil {
// Print error and return.
writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error())))
@ -241,19 +252,15 @@ func (o *ClusterInfoDumpOptions) Run(f cmdutil.Factory, cmd *cobra.Command) erro
for ix := range pods.Items {
pod := &pods.Items[ix]
containers := pod.Spec.Containers
writer := setupOutputWriter(cmd, o.Out, path.Join(namespace, pod.Name, "logs.txt"))
writer := setupOutputWriter(o.OutputDir, o.Out, path.Join(namespace, pod.Name, "logs.txt"))
for i := range containers {
printContainer(writer, containers[i], pod)
}
}
}
dir := cmdutil.GetFlagString(cmd, "output-directory")
if len(dir) == 0 {
dir = "standard output"
}
if dir != "-" {
fmt.Fprintf(o.Out, "Cluster info dumped to %s\n", dir)
if o.OutputDir != "-" {
fmt.Fprintf(o.Out, "Cluster info dumped to %s\n", o.OutputDir)
}
return nil
}

View File

@ -33,9 +33,7 @@ func TestSetupOutputWriterNoOp(t *testing.T) {
f := cmdtesting.NewTestFactory()
defer f.Cleanup()
cmd := NewCmdClusterInfoDump(f, genericclioptions.NewTestIOStreamsDiscard())
cmd.Flag("output-directory").Value.Set(test)
writer := setupOutputWriter(cmd, buf, "/some/file/that/should/be/ignored")
writer := setupOutputWriter(test, buf, "/some/file/that/should/be/ignored")
if writer != buf {
t.Errorf("expected: %v, saw: %v", buf, writer)
}
@ -55,9 +53,7 @@ func TestSetupOutputWriterFile(t *testing.T) {
f := cmdtesting.NewTestFactory()
defer f.Cleanup()
cmd := NewCmdClusterInfoDump(f, genericclioptions.NewTestIOStreamsDiscard())
cmd.Flag("output-directory").Value.Set(dir)
writer := setupOutputWriter(cmd, buf, file)
writer := setupOutputWriter(dir, buf, file)
if writer == buf {
t.Errorf("expected: %v, saw: %v", buf, writer)
}

View File

@ -308,7 +308,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
Message: "Advanced Commands:",
Commands: []*cobra.Command{
NewCmdApply("kubectl", f, ioStreams),
NewCmdPatch(f, out),
NewCmdPatch(f, ioStreams),
NewCmdReplace(f, out, err),
NewCmdConvert(f, ioStreams),
},
@ -349,8 +349,8 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
cmds.AddCommand(alpha)
cmds.AddCommand(cmdconfig.NewCmdConfig(f, clientcmd.NewDefaultPathOptions(), out, err))
cmds.AddCommand(NewCmdPlugin(f, in, out, err))
cmds.AddCommand(NewCmdVersion(f, out))
cmds.AddCommand(NewCmdApiVersions(f, out))
cmds.AddCommand(NewCmdVersion(f, ioStreams))
cmds.AddCommand(NewCmdApiVersions(f, ioStreams))
cmds.AddCommand(NewCmdApiResources(f, ioStreams))
cmds.AddCommand(NewCmdOptions(out))

View File

@ -68,6 +68,12 @@ type LabelOptions struct {
Recorder genericclioptions.Recorder
namespace string
enforceNamespace bool
includeUninitialized bool
builder *resource.Builder
unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
// Common shared fields
genericclioptions.IOStreams
}
@ -125,13 +131,9 @@ func NewCmdLabel(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr
Long: fmt.Sprintf(labelLong, validation.LabelValueMaxLength),
Example: labelExample,
Run: func(cmd *cobra.Command, args []string) {
if err := o.Complete(f, cmd, args); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, err.Error()))
}
if err := o.Validate(); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, err.Error()))
}
cmdutil.CheckErr(o.RunLabel(f, cmd))
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunLabel())
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
@ -188,10 +190,18 @@ func (o *LabelOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []st
o.newLabels, o.removeLabels, err = parseLabels(labelArgs)
if o.list && len(o.outputFormat) > 0 {
return cmdutil.UsageErrorf(cmd, "--list and --output may not be specified together")
return fmt.Errorf("--list and --output may not be specified together")
}
return err
o.namespace, o.enforceNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
o.includeUninitialized = cmdutil.ShouldIncludeUninitialized(cmd, false)
o.builder = f.NewBuilder()
o.unstructuredClientForMapping = f.UnstructuredClientForMapping
return nil
}
// Validate checks to the LabelOptions to see if there is sufficient information run the command.
@ -209,20 +219,14 @@ func (o *LabelOptions) Validate() error {
}
// RunLabel does the work
func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error {
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
b := f.NewBuilder().
func (o *LabelOptions) RunLabel() error {
b := o.builder.
Unstructured().
LocalParam(o.local).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(includeUninitialized).
NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(o.includeUninitialized).
Flatten()
if !o.local {
@ -294,7 +298,7 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error {
}
mapping := info.ResourceMapping()
client, err := f.UnstructuredClientForMapping(mapping)
client, err := o.unstructuredClientForMapping(mapping)
if err != nil {
return err
}

View File

@ -342,7 +342,7 @@ func TestLabelErrors(t *testing.T) {
err = opts.Validate()
}
if err == nil {
err = opts.RunLabel(tf, cmd)
err = opts.RunLabel()
}
if !testCase.errFn(err) {
t.Errorf("%s: unexpected error: %v", k, err)
@ -400,7 +400,7 @@ func TestLabelForResourceFromFile(t *testing.T) {
err = opts.Validate()
}
if err == nil {
err = opts.RunLabel(tf, cmd)
err = opts.RunLabel()
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
@ -434,7 +434,7 @@ func TestLabelLocal(t *testing.T) {
err = opts.Validate()
}
if err == nil {
err = opts.RunLabel(tf, cmd)
err = opts.RunLabel()
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
@ -491,7 +491,7 @@ func TestLabelMultipleObjects(t *testing.T) {
err = opts.Validate()
}
if err == nil {
err = opts.RunLabel(tf, cmd)
err = opts.RunLabel()
}
if err != nil {
t.Fatalf("unexpected error: %v", err)

View File

@ -18,7 +18,6 @@ package cmd
import (
"fmt"
"io"
"reflect"
"strings"
@ -26,6 +25,7 @@ import (
"github.com/golang/glog"
"github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -49,16 +49,25 @@ var patchTypes = map[string]types.PatchType{"json": types.JSONPatchType, "merge"
// referencing the cmd.Flags()
type PatchOptions struct {
resource.FilenameOptions
RecordFlags *genericclioptions.RecordFlags
PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Recorder genericclioptions.Recorder
Local bool
DryRun bool
Local bool
PatchType string
Patch string
Recorder genericclioptions.Recorder
namespace string
enforceNamespace bool
dryRun bool
outputFormat string
args []string
builder *resource.Builder
unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
OutputFormat string
genericclioptions.IOStreams
}
var (
@ -86,16 +95,17 @@ var (
kubectl patch pod valid-pod --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]'`))
)
func NewPatchOptions() *PatchOptions {
func NewPatchOptions(ioStreams genericclioptions.IOStreams) *PatchOptions {
return &PatchOptions{
RecordFlags: genericclioptions.NewRecordFlags(),
Recorder: genericclioptions.NoopRecorder{},
PrintFlags: printers.NewPrintFlags("patched"),
IOStreams: ioStreams,
}
}
func NewCmdPatch(f cmdutil.Factory, out io.Writer) *cobra.Command {
o := NewPatchOptions()
func NewCmdPatch(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewPatchOptions(ioStreams)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
@ -105,8 +115,9 @@ func NewCmdPatch(f cmdutil.Factory, out io.Writer) *cobra.Command {
Long: patchLong,
Example: patchExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.RunPatch(f, out, cmd, args))
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunPatch())
},
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
@ -115,34 +126,30 @@ 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.Flags().StringVarP(&o.Patch, "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()))
cmd.Flags().StringVar(&o.PatchType, "type", "strategic", fmt.Sprintf("The type of patch being provided; one of %v", sets.StringKeySet(patchTypes).List()))
cmdutil.AddDryRunFlag(cmd)
usage := "identifying the resource to update"
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "identifying the resource to update")
cmd.Flags().BoolVar(&o.Local, "local", o.Local, "If true, patch will operate on the content of the file, not the server-side resource.")
return cmd
}
func (o *PatchOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
func (o *PatchOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
o.OutputFormat = cmdutil.GetFlagString(cmd, "output")
o.DryRun = cmdutil.GetFlagBool(cmd, "dry-run")
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 {
if o.dryRun {
o.PrintFlags.Complete("%s (dry run)")
}
@ -153,46 +160,50 @@ func (o *PatchOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
return printer.PrintObj, nil
}
return err
}
func (o *PatchOptions) RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string) error {
switch {
case o.Local && len(args) != 0:
return fmt.Errorf("cannot specify --local and server resources")
}
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
o.namespace, o.enforceNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
o.args = args
o.builder = f.NewBuilder()
o.unstructuredClientForMapping = f.UnstructuredClientForMapping
patchType := types.StrategicMergePatchType
patchTypeString := strings.ToLower(cmdutil.GetFlagString(cmd, "type"))
if len(patchTypeString) != 0 {
ok := false
patchType, ok = patchTypes[patchTypeString]
if !ok {
return cmdutil.UsageErrorf(cmd, "--type must be one of %v, not %q",
sets.StringKeySet(patchTypes).List(), patchTypeString)
return nil
}
func (o *PatchOptions) Validate() error {
if o.Local && len(o.args) != 0 {
return fmt.Errorf("cannot specify --local and server resources")
}
if len(o.Patch) == 0 {
return fmt.Errorf("must specify -p to patch")
}
if len(o.PatchType) != 0 {
if _, ok := patchTypes[strings.ToLower(o.PatchType)]; !ok {
return fmt.Errorf("--type must be one of %v, not %q", sets.StringKeySet(patchTypes).List(), o.PatchType)
}
}
patch := cmdutil.GetFlagString(cmd, "patch")
if len(patch) == 0 {
return cmdutil.UsageErrorf(cmd, "Must specify -p to patch")
}
patchBytes, err := yaml.ToJSON([]byte(patch))
if err != nil {
return fmt.Errorf("unable to parse %q: %v", patch, err)
return nil
}
func (o *PatchOptions) RunPatch() error {
patchType := types.StrategicMergePatchType
if len(o.PatchType) != 0 {
patchType = patchTypes[strings.ToLower(o.PatchType)]
}
r := f.NewBuilder().
patchBytes, err := yaml.ToJSON([]byte(o.Patch))
if err != nil {
return fmt.Errorf("unable to parse %q: %v", o.Patch, err)
}
r := o.builder.
Unstructured().
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(false, args...).
NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(false, o.args...).
Flatten().
Do()
err = r.Err()
@ -207,12 +218,12 @@ func (o *PatchOptions) RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Com
}
name, namespace := info.Name, info.Namespace
mapping := info.ResourceMapping()
client, err := f.UnstructuredClientForMapping(mapping)
client, err := o.unstructuredClientForMapping(mapping)
if err != nil {
return err
}
if !o.Local && !o.DryRun {
if !o.Local && !o.dryRun {
helper := resource.NewHelper(client, mapping)
patchedObj, err := helper.Patch(namespace, name, patchType, patchBytes)
if err != nil {
@ -242,7 +253,7 @@ func (o *PatchOptions) RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Com
if err != nil {
return err
}
printer.PrintObj(info.Object, out)
printer.PrintObj(info.Object, o.Out)
// if object was not successfully patched, exit with error code 1
if !didPatch {
@ -285,7 +296,7 @@ func (o *PatchOptions) RunPatch(f cmdutil.Factory, out io.Writer, cmd *cobra.Com
if err != nil {
return err
}
return printer.PrintObj(info.Object, out)
return printer.PrintObj(info.Object, o.Out)
})
if err != nil {
return err

View File

@ -17,7 +17,6 @@ limitations under the License.
package cmd
import (
"bytes"
"net/http"
"strings"
"testing"
@ -25,6 +24,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"
)
@ -56,9 +56,9 @@ func TestPatchObject(t *testing.T) {
}),
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
stream, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdPatch(tf, buf)
cmd := NewCmdPatch(tf, stream)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`)
cmd.Flags().Set("output", "name")
@ -91,9 +91,9 @@ func TestPatchObjectFromFile(t *testing.T) {
}),
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
stream, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdPatch(tf, buf)
cmd := NewCmdPatch(tf, stream)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`)
cmd.Flags().Set("output", "name")
@ -139,8 +139,8 @@ func TestPatchNoop(t *testing.T) {
patchObject.Annotations = map[string]string{}
}
patchObject.Annotations["foo"] = "bar"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdPatch(tf, buf)
stream, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdPatch(tf, stream)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("patch", `{"metadata":{"annotations":{"foo":"bar"}}}`)
cmd.Run(cmd, []string{"services", "frontend"})
@ -179,9 +179,9 @@ func TestPatchObjectFromFileOutput(t *testing.T) {
}),
}
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
stream, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdPatch(tf, buf)
cmd := NewCmdPatch(tf, stream)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("patch", `{"spec":{"type":"NodePort"}}`)
cmd.Flags().Set("output", "yaml")

View File

@ -70,46 +70,40 @@ type ScaleOptions struct {
PrintFlags *printers.PrintFlags
PrintObj printers.ResourcePrinterFunc
BuilderArgs []string
Namespace string
EnforceNamespace bool
Builder *resource.Builder
ClientSet internalclientset.Interface
Scaler kubectl.Scaler
All bool
Selector string
CmdParent string
Selector string
All bool
Replicas int
ResourceVersion string
CurrentReplicas int
Replicas int
Duration time.Duration
Timeout time.Duration
ClientForMapping func(*meta.RESTMapping) (resource.RESTClient, error)
Recorder genericclioptions.Recorder
Recorder genericclioptions.Recorder
builder *resource.Builder
namespace string
enforceNamespace bool
args []string
shortOutput bool
clientSet internalclientset.Interface
scaler kubectl.Scaler
unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
parent string
genericclioptions.IOStreams
}
func NewScaleOptions(ioStreams genericclioptions.IOStreams) *ScaleOptions {
return &ScaleOptions{
RecordFlags: genericclioptions.NewRecordFlags(),
PrintFlags: printers.NewPrintFlags("scaled"),
RecordFlags: genericclioptions.NewRecordFlags(),
PrintFlags: printers.NewPrintFlags("scaled"),
CurrentReplicas: -1,
Recorder: genericclioptions.NoopRecorder{},
IOStreams: ioStreams,
Recorder: genericclioptions.NoopRecorder{},
IOStreams: ioStreams,
}
}
// NewCmdScale returns a cobra command with the appropriate configuration and flags to run scale
func NewCmdScale(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
o := NewScaleOptions(streams)
func NewCmdScale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewScaleOptions(ioStreams)
validArgs := []string{"deployment", "replicaset", "replicationcontroller", "statefulset"}
argAliases := kubectl.ResourceAliases(validArgs)
@ -122,7 +116,7 @@ func NewCmdScale(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.
Example: scaleExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
cmdutil.CheckErr(o.Validate(cmd))
cmdutil.CheckErr(o.RunScale())
},
ValidArgs: validArgs,
@ -138,71 +132,68 @@ func NewCmdScale(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.
cmd.Flags().IntVar(&o.CurrentReplicas, "current-replicas", o.CurrentReplicas, "Precondition for current size. Requires that the current size of the resource match this value in order to scale.")
cmd.Flags().IntVar(&o.Replicas, "replicas", o.Replicas, "The new desired number of replicas. Required.")
cmd.MarkFlagRequired("replicas")
cmd.Flags().DurationVar(&o.Duration, "timeout", o.Duration, "The length of time to wait before giving up on a scale operation, zero means don't wait. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
usage := "identifying the resource to set a new size"
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmd.Flags().DurationVar(&o.Timeout, "timeout", 0, "The length of time to wait before giving up on a scale operation, zero means don't wait. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "identifying the resource to set a new size")
return cmd
}
func (o *ScaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace()
o.RecordFlags.Complete(f.Command(cmd, false))
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil {
return err
}
o.CmdParent = cmd.Parent().Name()
o.Builder = f.NewBuilder()
o.ClientSet, err = f.ClientSet()
if err != nil {
return err
}
o.Scaler, err = f.Scaler()
if err != nil {
return err
}
o.BuilderArgs = args
o.ClientForMapping = f.UnstructuredClientForMapping
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()
o.namespace, o.enforceNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
o.builder = f.NewBuilder()
o.args = args
o.shortOutput = cmdutil.GetFlagString(cmd, "output") == "name"
o.clientSet, err = f.ClientSet()
if err != nil {
return err
}
o.scaler, err = f.Scaler()
if err != nil {
return err
}
o.unstructuredClientForMapping = f.UnstructuredClientForMapping
o.parent = cmd.Parent().Name()
return nil
}
func (o *ScaleOptions) Validate(cmd *cobra.Command) error {
if err := cmdutil.ValidateOutputArgs(cmd); err != nil {
return err
}
if o.Replicas < 0 {
return fmt.Errorf("The --replicas=COUNT flag is required, and COUNT must be greater than or equal to 0")
}
return nil
}
// RunScale executes the scaling
func (o *ScaleOptions) RunScale() error {
if o.Replicas < 0 {
return fmt.Errorf("The --replicas=COUNT flag is required, and COUNT must be greater than or equal to 0")
}
r := o.Builder.
r := o.builder.
Unstructured().
ContinueOnError().
NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(o.All, o.BuilderArgs...).
NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(o.All, o.args...).
Flatten().
LabelSelectorParam(o.Selector).
Do()
err := r.Err()
if resource.IsUsageError(err) {
return fmt.Errorf("%v", err)
}
if err != nil {
return err
}
@ -219,12 +210,11 @@ func (o *ScaleOptions) RunScale() error {
return fmt.Errorf("cannot use --resource-version with multiple resources")
}
currentSize := o.CurrentReplicas
precondition := &kubectl.ScalePrecondition{Size: currentSize, ResourceVersion: o.ResourceVersion}
precondition := &kubectl.ScalePrecondition{Size: o.CurrentReplicas, ResourceVersion: o.ResourceVersion}
retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout)
var waitForReplicas *kubectl.RetryParams
if timeout := o.Duration; timeout != 0 {
if o.Timeout != 0 {
waitForReplicas = kubectl.NewRetryParams(kubectl.Interval, timeout)
}
@ -237,15 +227,15 @@ func (o *ScaleOptions) RunScale() error {
mapping := info.ResourceMapping()
if mapping.Resource == "jobs" {
// go down the legacy jobs path. This can be removed in 3.14 For now, contain it.
fmt.Fprintf(o.ErrOut, "%s scale job is DEPRECATED and will be removed in a future version.\n", o.CmdParent)
fmt.Fprintf(o.ErrOut, "%s scale job is DEPRECATED and will be removed in a future version.\n", o.parent)
if err := ScaleJob(info, o.ClientSet.Batch(), uint(o.Replicas), precondition, retry, waitForReplicas); err != nil {
if err := ScaleJob(info, o.clientSet.Batch(), uint(o.Replicas), precondition, retry, waitForReplicas); err != nil {
return err
}
} else {
gvk := mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource)
if err := o.Scaler.Scale(info.Namespace, info.Name, uint(o.Replicas), precondition, retry, waitForReplicas, gvk.GroupResource()); err != nil {
if err := o.scaler.Scale(info.Namespace, info.Name, uint(o.Replicas), precondition, retry, waitForReplicas, gvk.GroupResource()); err != nil {
return err
}
}
@ -254,7 +244,7 @@ func (o *ScaleOptions) RunScale() error {
if mergePatch, err := o.Recorder.MakeRecordMergePatch(info.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err)
} else if len(mergePatch) > 0 {
client, err := o.ClientForMapping(mapping)
client, err := o.unstructuredClientForMapping(mapping)
if err != nil {
return err
}

View File

@ -24,19 +24,19 @@ import (
"strings"
"github.com/spf13/cobra"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
envutil "k8s.io/kubernetes/pkg/kubectl/cmd/util/env"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/printers"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions"
)
var (
@ -94,32 +94,30 @@ var (
type EnvOptions struct {
PrintFlags *printers.PrintFlags
resource.FilenameOptions
EnvParams []string
EnvArgs []string
Resources []string
All bool
Resolve bool
List bool
Local bool
Overwrite bool
DryRun bool
ResourceVersion string
EnvParams []string
All bool
Resolve bool
List bool
Local bool
Overwrite bool
ContainerSelector string
Selector string
Output string
From string
Prefix string
PrintObj printers.ResourcePrinterFunc
Builder *resource.Builder
Infos []*resource.Info
UpdatePodSpecForObject func(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error)
envArgs []string
resources []string
output string
dryRun bool
builder func() *resource.Builder
updatePodSpecForObject func(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error)
namespace string
enforceNamespace bool
clientset *kubernetes.Clientset
genericclioptions.IOStreams
}
@ -139,7 +137,7 @@ func NewEnvOptions(streams genericclioptions.IOStreams) *EnvOptions {
// NewCmdEnv implements the OpenShift cli env command
func NewCmdEnv(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
options := NewEnvOptions(streams)
o := NewEnvOptions(streams)
cmd := &cobra.Command{
Use: "env RESOURCE/NAME KEY_1=VAL_1 ... KEY_N=VAL_N",
DisableFlagsInUseLine: true,
@ -147,24 +145,25 @@ func NewCmdEnv(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Co
Long: envLong,
Example: fmt.Sprintf(envExample),
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.RunEnv(f))
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunEnv())
},
}
usage := "the resource to update the env"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().StringVarP(&options.ContainerSelector, "containers", "c", options.ContainerSelector, "The names of containers in the selected pod templates to change - may use wildcards")
cmd.Flags().StringP("from", "", "", "The name of a resource from which to inject environment variables")
cmd.Flags().StringP("prefix", "", "", "Prefix to append to variable names")
cmd.Flags().StringArrayVarP(&options.EnvParams, "env", "e", options.EnvParams, "Specify a key-value pair for an environment variable to set into each container.")
cmd.Flags().BoolVar(&options.List, "list", options.List, "If true, display the environment and any changes in the standard format. this flag will removed when we have kubectl view env.")
cmd.Flags().BoolVar(&options.Resolve, "resolve", options.Resolve, "If true, show secret or configmap references when listing variables")
cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on")
cmd.Flags().BoolVar(&options.Local, "local", options.Local, "If true, set env will NOT contact api-server but run locally.")
cmd.Flags().BoolVar(&options.All, "all", options.All, "If true, select all resources in the namespace of the specified resource types")
cmd.Flags().BoolVar(&options.Overwrite, "overwrite", options.Overwrite, "If true, allow environment to be overwritten, otherwise reject updates that overwrite existing environment.")
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
cmd.Flags().StringVarP(&o.ContainerSelector, "containers", "c", o.ContainerSelector, "The names of containers in the selected pod templates to change - may use wildcards")
cmd.Flags().StringVarP(&o.From, "from", "", "", "The name of a resource from which to inject environment variables")
cmd.Flags().StringVarP(&o.Prefix, "prefix", "", "", "Prefix to append to variable names")
cmd.Flags().StringArrayVarP(&o.EnvParams, "env", "e", o.EnvParams, "Specify a key-value pair for an environment variable to set into each container.")
cmd.Flags().BoolVar(&o.List, "list", o.List, "If true, display the environment and any changes in the standard format. this flag will removed when we have kubectl view env.")
cmd.Flags().BoolVar(&o.Resolve, "resolve", o.Resolve, "If true, show secret or configmap references when listing variables")
cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on")
cmd.Flags().BoolVar(&o.Local, "local", o.Local, "If true, set env will NOT contact api-server but run locally.")
cmd.Flags().BoolVar(&o.All, "all", o.All, "If true, select all resources in the namespace of the specified resource types")
cmd.Flags().BoolVar(&o.Overwrite, "overwrite", o.Overwrite, "If true, allow environment to be overwritten, otherwise reject updates that overwrite existing environment.")
options.PrintFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
cmdutil.AddDryRunFlag(cmd)
return cmd
@ -187,30 +186,17 @@ func (o *EnvOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
if o.All && len(o.Selector) > 0 {
return fmt.Errorf("cannot set --all and --selector at the same time")
}
resources, envArgs, ok := envutil.SplitEnvironmentFromResources(args)
ok := false
o.resources, o.envArgs, ok = envutil.SplitEnvironmentFromResources(args)
if !ok {
return cmdutil.UsageErrorf(cmd, "all resources must be specified before environment changes: %s", strings.Join(args, " "))
}
if len(o.Filenames) == 0 && len(resources) < 1 {
return cmdutil.UsageErrorf(cmd, "one or more resources must be specified as <resource> <name> or <resource>/<name>")
return fmt.Errorf("all resources must be specified before environment changes: %s", strings.Join(args, " "))
}
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject
o.ContainerSelector = cmdutil.GetFlagString(cmd, "containers")
o.List = cmdutil.GetFlagBool(cmd, "list")
o.Resolve = cmdutil.GetFlagBool(cmd, "resolve")
o.Selector = cmdutil.GetFlagString(cmd, "selector")
o.All = cmdutil.GetFlagBool(cmd, "all")
o.Overwrite = cmdutil.GetFlagBool(cmd, "overwrite")
o.Output = cmdutil.GetFlagString(cmd, "output")
o.From = cmdutil.GetFlagString(cmd, "from")
o.Prefix = cmdutil.GetFlagString(cmd, "prefix")
o.DryRun = cmdutil.GetDryRunFlag(cmd)
o.updatePodSpecForObject = f.UpdatePodSpecForObject
o.output = cmdutil.GetFlagString(cmd, "output")
o.dryRun = cmdutil.GetDryRunFlag(cmd)
o.EnvArgs = envArgs
o.Resources = resources
if o.DryRun {
if o.dryRun {
// TODO(juanvallejo): This can be cleaned up even further by creating
// a PrintFlags struct that binds the --dry-run flag, and whose
// ToPrinter method returns a printer that understands how to print
@ -223,41 +209,43 @@ func (o *EnvOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []stri
}
o.PrintObj = printer.PrintObj
if o.List && len(o.Output) > 0 {
return cmdutil.UsageErrorf(cmd, "--list and --output may not be specified together")
o.clientset, err = f.KubernetesClientSet()
if err != nil {
return err
}
o.namespace, o.enforceNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
o.builder = f.NewBuilder
return nil
}
func (o *EnvOptions) Validate() error {
if len(o.Filenames) == 0 && len(o.resources) < 1 {
return fmt.Errorf("one or more resources must be specified as <resource> <name> or <resource>/<name>")
}
if o.List && len(o.output) > 0 {
return fmt.Errorf("--list and --output may not be specified together")
}
return nil
}
// RunEnv contains all the necessary functionality for the OpenShift cli env command
func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
var kubeClient *kubernetes.Clientset
if o.List {
client, err := f.KubernetesClientSet()
if err != nil {
return err
}
kubeClient = client
}
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
env, remove, err := envutil.ParseEnv(append(o.EnvParams, o.EnvArgs...), o.In)
func (o *EnvOptions) RunEnv() error {
env, remove, err := envutil.ParseEnv(append(o.EnvParams, o.envArgs...), o.In)
if err != nil {
return err
}
if len(o.From) != 0 {
b := f.NewBuilder().
b := o.builder().
Internal(legacyscheme.Scheme).
LocalParam(o.Local).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.enforceNamespace, &o.FilenameOptions).
Flatten()
if !o.Local {
@ -320,27 +308,27 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
}
}
b := f.NewBuilder().
b := o.builder().
Internal(legacyscheme.Scheme).
LocalParam(o.Local).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.enforceNamespace, &o.FilenameOptions).
Flatten()
if !o.Local {
b.LabelSelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, o.Resources...).
ResourceTypeOrNameArgs(o.All, o.resources...).
Latest()
}
o.Infos, err = b.Do().Infos()
infos, err := b.Do().Infos()
if err != nil {
return err
}
patches := CalculatePatches(o.Infos, cmdutil.InternalVersionJSONEncoder(), func(info *resource.Info) ([]byte, error) {
patches := CalculatePatches(infos, cmdutil.InternalVersionJSONEncoder(), func(info *resource.Info) ([]byte, error) {
info.Object = info.AsVersioned(legacyscheme.Scheme)
_, err := o.UpdatePodSpecForObject(info.Object, func(spec *v1.PodSpec) error {
_, err := o.updatePodSpecForObject(info.Object, func(spec *v1.PodSpec) error {
resolutionErrorsEncountered := false
containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
if len(containers) == 0 {
@ -373,7 +361,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
continue
}
value, err := envutil.GetEnvVarRefValue(kubeClient, cmdNamespace, store, env.ValueFrom, info.Object, c)
value, err := envutil.GetEnvVarRefValue(o.clientset, o.namespace, store, env.ValueFrom, info.Object, c)
// Print the resolved value
if err == nil {
fmt.Fprintf(o.Out, "%s=%s\n", env.Name, value)
@ -429,7 +417,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
continue
}
if o.Local || o.DryRun {
if o.Local || o.dryRun {
if err := o.PrintObj(patch.Info.AsVersioned(legacyscheme.Scheme), o.Out); err != nil {
return err
}
@ -445,7 +433,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
// make sure arguments to set or replace environment variables are set
// before returning a successful message
if len(env) == 0 && len(o.EnvArgs) == 0 {
if len(env) == 0 && len(o.envArgs) == 0 {
return fmt.Errorf("at least one environment variable must be provided")
}

View File

@ -61,33 +61,28 @@ func TestSetEnvLocal(t *testing.T) {
}
tf.Namespace = "test"
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
outputFormat := "name"
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdEnv(tf, streams)
cmd.SetOutput(buf)
cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true")
opts := EnvOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"}},
Local: true,
IOStreams: streams,
streams, _, buf, bufErr := genericclioptions.NewTestIOStreams()
opts := NewEnvOptions(streams)
opts.PrintFlags = &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
}
err := opts.Complete(tf, cmd, []string{"env=prod"})
if err == nil {
err = opts.RunEnv(tf)
opts.FilenameOptions = resource.FilenameOptions{
Filenames: []string{"../../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"},
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
opts.Local = true
err := opts.Complete(tf, NewCmdEnv(tf, streams), []string{"env=prod"})
assert.NoError(t, err)
err = opts.Validate()
assert.NoError(t, err)
err = opts.RunEnv()
assert.NoError(t, err)
if bufErr.Len() > 0 {
t.Errorf("unexpected error: %s", string(bufErr.String()))
}
if !strings.Contains(buf.String(), "replicationcontroller/cassandra") {
t.Errorf("did not set env: %s", buf.String())
@ -99,7 +94,6 @@ func TestSetMultiResourcesEnvLocal(t *testing.T) {
defer tf.Cleanup()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: ""},
NegotiatedSerializer: ns,
@ -112,33 +106,27 @@ func TestSetMultiResourcesEnvLocal(t *testing.T) {
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
outputFormat := "name"
streams, _, buf, _ := genericclioptions.NewTestIOStreams()
cmd := NewCmdEnv(tf, streams)
cmd.SetOutput(buf)
cmd.Flags().Set("output", outputFormat)
cmd.Flags().Set("local", "true")
opts := EnvOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
FilenameOptions: resource.FilenameOptions{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"}},
Local: true,
IOStreams: streams,
streams, _, buf, bufErr := genericclioptions.NewTestIOStreams()
opts := NewEnvOptions(streams)
opts.PrintFlags = &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
}
err := opts.Complete(tf, cmd, []string{"env=prod"})
if err == nil {
err = opts.RunEnv(tf)
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
opts.FilenameOptions = resource.FilenameOptions{
Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"},
}
opts.Local = true
err := opts.Complete(tf, NewCmdEnv(tf, streams), []string{"env=prod"})
assert.NoError(t, err)
err = opts.Validate()
assert.NoError(t, err)
err = opts.RunEnv()
assert.NoError(t, err)
if bufErr.Len() > 0 {
t.Errorf("unexpected error: %s", string(bufErr.String()))
}
expectedOut := "replicationcontroller/first-rc\nreplicationcontroller/second-rc\n"
if buf.String() != expectedOut {
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String())
@ -472,6 +460,7 @@ func TestSetEnvRemote(t *testing.T) {
groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion}
testapi.Default = testapi.Groups[input.testAPIGroup]
tf := cmdtesting.NewTestFactory()
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
defer tf.Cleanup()
codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion)
@ -505,23 +494,18 @@ func TestSetEnvRemote(t *testing.T) {
}
outputFormat := "yaml"
streams := genericclioptions.NewTestIOStreamsDiscard()
cmd := NewCmdEnv(tf, streams)
cmd.Flags().Set("output", outputFormat)
opts := EnvOptions{
PrintFlags: &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
Local: false,
IOStreams: streams,
opts := NewEnvOptions(streams)
opts.PrintFlags = &printers.PrintFlags{
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
}
err := opts.Complete(tf, cmd, input.args)
opts.Local = false
opts.IOStreams = streams
err := opts.Complete(tf, NewCmdEnv(tf, streams), input.args)
assert.NoError(t, err)
err = opts.RunEnv(tf)
err = opts.RunEnv()
assert.NoError(t, err)
})
}

View File

@ -73,6 +73,8 @@ type SubjectOptions struct {
Groups []string
ServiceAccounts []string
namespace string
PrintObj printers.ResourcePrinterFunc
genericclioptions.IOStreams
@ -87,8 +89,7 @@ func NewSubjectOptions(streams genericclioptions.IOStreams) *SubjectOptions {
}
func NewCmdSubject(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
options := NewSubjectOptions(streams)
o := NewSubjectOptions(streams)
cmd := &cobra.Command{
Use: "subject (-f FILENAME | TYPE NAME) [--user=username] [--group=groupname] [--serviceaccount=namespace:serviceaccountname] [--dry-run]",
DisableFlagsInUseLine: true,
@ -96,23 +97,22 @@ func NewCmdSubject(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobr
Long: subject_long,
Example: subject_example,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.Run(f, addSubjects))
cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run(addSubjects))
},
}
options.PrintFlags.AddFlags(cmd)
o.PrintFlags.AddFlags(cmd)
usage := "the resource to update the subjects"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().BoolVar(&options.All, "all", options.All, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().BoolVar(&options.Local, "local", options.Local, "If true, set subject will NOT contact api-server but run locally.")
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, "the resource to update the subjects")
cmd.Flags().BoolVar(&o.All, "all", o.All, "Select all resources, including uninitialized ones, in the namespace of the specified resource types")
cmd.Flags().StringVarP(&o.Selector, "selector", "l", o.Selector, "Selector (label query) to filter on, not including uninitialized ones, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
cmd.Flags().BoolVar(&o.Local, "local", o.Local, "If true, set subject will NOT contact api-server but run locally.")
cmdutil.AddDryRunFlag(cmd)
cmd.Flags().StringArrayVar(&options.Users, "user", options.Users, "Usernames to bind to the role")
cmd.Flags().StringArrayVar(&options.Groups, "group", options.Groups, "Groups to bind to the role")
cmd.Flags().StringArrayVar(&options.ServiceAccounts, "serviceaccount", options.ServiceAccounts, "Service accounts to bind to the role")
cmd.Flags().StringArrayVar(&o.Users, "user", o.Users, "Usernames to bind to the role")
cmd.Flags().StringArrayVar(&o.Groups, "group", o.Groups, "Groups to bind to the role")
cmd.Flags().StringArrayVar(&o.ServiceAccounts, "serviceaccount", o.ServiceAccounts, "Service accounts to bind to the role")
cmdutil.AddIncludeUninitializedFlag(cmd)
return cmd
}
@ -130,7 +130,8 @@ func (o *SubjectOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
}
o.PrintObj = printer.PrintObj
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
var enforceNamespace bool
o.namespace, enforceNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
@ -140,7 +141,7 @@ func (o *SubjectOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []
Internal(legacyscheme.Scheme).
LocalParam(o.Local).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(includeUninitialized).
Flatten()
@ -192,8 +193,7 @@ func (o *SubjectOptions) Validate() error {
return nil
}
func (o *SubjectOptions) Run(f cmdutil.Factory, fn updateSubjects) error {
var err error
func (o *SubjectOptions) Run(fn updateSubjects) error {
patches := CalculatePatches(o.Infos, cmdutil.InternalVersionJSONEncoder(), func(info *resource.Info) ([]byte, error) {
subjects := []rbac.Subject{}
for _, user := range sets.NewString(o.Users...).List() {
@ -217,10 +217,7 @@ func (o *SubjectOptions) Run(f cmdutil.Factory, fn updateSubjects) error {
namespace := tokens[0]
name := tokens[1]
if len(namespace) == 0 {
namespace, _, err = f.DefaultNamespace()
if err != nil {
return nil, err
}
namespace = o.namespace
}
subject := rbac.Subject{
Kind: rbac.ServiceAccountKind,

View File

@ -20,14 +20,15 @@ import (
"encoding/json"
"errors"
"fmt"
"io"
"github.com/ghodss/yaml"
"github.com/spf13/cobra"
apimachineryversion "k8s.io/apimachinery/pkg/version"
"k8s.io/client-go/discovery"
"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/version"
)
@ -37,52 +38,67 @@ type Version struct {
ServerVersion *apimachineryversion.Info `json:"serverVersion,omitempty" yaml:"serverVersion,omitempty"`
}
// VersionOptions: describe the options available to users of the "kubectl
// version" command.
type VersionOptions struct {
clientOnly bool
short bool
output string
}
var (
versionExample = templates.Examples(i18n.T(`
# Print the client and server versions for the current context
kubectl version`))
)
func NewCmdVersion(f cmdutil.Factory, out io.Writer) *cobra.Command {
type VersionOptions struct {
ClientOnly bool
Short bool
Output string
discoveryClient discovery.CachedDiscoveryInterface
genericclioptions.IOStreams
}
func NewVersionOptions(ioStreams genericclioptions.IOStreams) *VersionOptions {
return &VersionOptions{
IOStreams: ioStreams,
}
}
func NewCmdVersion(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewVersionOptions(ioStreams)
cmd := &cobra.Command{
Use: "version",
Short: i18n.T("Print the client and server version information"),
Long: "Print the client and server version information for the current context",
Example: versionExample,
Run: func(cmd *cobra.Command, args []string) {
options := new(VersionOptions)
cmdutil.CheckErr(options.Complete(cmd))
cmdutil.CheckErr(options.Validate())
cmdutil.CheckErr(options.Run(f, out))
cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run())
},
}
cmd.Flags().BoolP("client", "c", false, "Client version only (no server required).")
cmd.Flags().BoolP("short", "", false, "Print just the version number.")
cmd.Flags().StringP("output", "o", "", "One of 'yaml' or 'json'.")
cmd.Flags().BoolVarP(&o.ClientOnly, "client", "c", o.ClientOnly, "Client version only (no server required).")
cmd.Flags().BoolVarP(&o.Short, "short", "", o.Short, "Print just the version number.")
cmd.Flags().StringVarP(&o.Output, "output", "o", o.Output, "One of 'yaml' or 'json'.")
cmd.Flags().MarkShorthandDeprecated("client", "please use --client instead.")
return cmd
}
func retrieveServerVersion(f cmdutil.Factory) (*apimachineryversion.Info, error) {
discoveryClient, err := f.DiscoveryClient()
func (o *VersionOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
var err error
o.discoveryClient, err = f.DiscoveryClient()
if err != nil {
return nil, err
return err
}
// Always request fresh data from the server
discoveryClient.Invalidate()
return discoveryClient.ServerVersion()
return nil
}
func (o *VersionOptions) Run(f cmdutil.Factory, out io.Writer) error {
func (o *VersionOptions) Validate() error {
if o.Output != "" && o.Output != "yaml" && o.Output != "json" {
return errors.New(`--output must be 'yaml' or 'json'`)
}
return nil
}
func (o *VersionOptions) Run() error {
var (
serverVersion *apimachineryversion.Info
serverErr error
@ -92,22 +108,24 @@ func (o *VersionOptions) Run(f cmdutil.Factory, out io.Writer) error {
clientVersion := version.Get()
versionInfo.ClientVersion = &clientVersion
if !o.clientOnly {
serverVersion, serverErr = retrieveServerVersion(f)
if !o.ClientOnly {
// Always request fresh data from the server
o.discoveryClient.Invalidate()
serverVersion, serverErr = o.discoveryClient.ServerVersion()
versionInfo.ServerVersion = serverVersion
}
switch o.output {
switch o.Output {
case "":
if o.short {
fmt.Fprintf(out, "Client Version: %s\n", clientVersion.GitVersion)
if o.Short {
fmt.Fprintf(o.Out, "Client Version: %s\n", clientVersion.GitVersion)
if serverVersion != nil {
fmt.Fprintf(out, "Server Version: %s\n", serverVersion.GitVersion)
fmt.Fprintf(o.Out, "Server Version: %s\n", serverVersion.GitVersion)
}
} else {
fmt.Fprintf(out, "Client Version: %s\n", fmt.Sprintf("%#v", clientVersion))
fmt.Fprintf(o.Out, "Client Version: %s\n", fmt.Sprintf("%#v", clientVersion))
if serverVersion != nil {
fmt.Fprintf(out, "Server Version: %s\n", fmt.Sprintf("%#v", *serverVersion))
fmt.Fprintf(o.Out, "Server Version: %s\n", fmt.Sprintf("%#v", *serverVersion))
}
}
case "yaml":
@ -115,33 +133,18 @@ func (o *VersionOptions) Run(f cmdutil.Factory, out io.Writer) error {
if err != nil {
return err
}
fmt.Fprintln(out, string(marshalled))
fmt.Fprintln(o.Out, string(marshalled))
case "json":
marshalled, err := json.MarshalIndent(&versionInfo, "", " ")
if err != nil {
return err
}
fmt.Fprintln(out, string(marshalled))
fmt.Fprintln(o.Out, string(marshalled))
default:
// There is a bug in the program if we hit this case.
// However, we follow a policy of never panicking.
return fmt.Errorf("VersionOptions were not validated: --output=%q should have been rejected", o.output)
return fmt.Errorf("VersionOptions were not validated: --output=%q should have been rejected", o.Output)
}
return serverErr
}
func (o *VersionOptions) Complete(cmd *cobra.Command) error {
o.clientOnly = cmdutil.GetFlagBool(cmd, "client")
o.short = cmdutil.GetFlagBool(cmd, "short")
o.output = cmdutil.GetFlagString(cmd, "output")
return nil
}
func (o *VersionOptions) Validate() error {
if o.output != "" && o.output != "yaml" && o.output != "json" {
return errors.New(`--output must be 'yaml' or 'json'`)
}
return nil
}