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 # Set env of deployments for all container
kubectl set env deployment nginx-deployment env=prod "${kube_flags[@]}" kubectl set env deployment nginx-deployment env=prod "${kube_flags[@]}"
# Set env of deployments for specific container # 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 # Set env of deployments by configmap
kubectl set env deployment nginx-deployment --from=configmap/test-set-env-config "${kube_flags[@]}" kubectl set env deployment nginx-deployment --from=configmap/test-set-env-config "${kube_flags[@]}"
# Set env of deployments by secret # Set env of deployments by secret

View File

@ -59,10 +59,15 @@ type AnnotateOptions struct {
outputFormat string outputFormat string
// results of arg parsing // results of arg parsing
resources []string resources []string
newAnnotations map[string]string newAnnotations map[string]string
removeAnnotations []string removeAnnotations []string
Recorder genericclioptions.Recorder Recorder genericclioptions.Recorder
namespace string
enforceNamespace bool
builder *resource.Builder
unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
includeUninitialized bool
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -123,13 +128,9 @@ func NewCmdAnnotate(parent string, f cmdutil.Factory, ioStreams genericclioption
Long: annotateLong + "\n\n" + cmdutil.SuggestApiResources(parent), Long: annotateLong + "\n\n" + cmdutil.SuggestApiResources(parent),
Example: annotateExample, Example: annotateExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := o.Complete(f, cmd, args); err != nil { cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, "%v", err)) cmdutil.CheckErr(o.Validate())
} cmdutil.CheckErr(o.RunAnnotate())
if err := o.Validate(); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, "%v", err))
}
cmdutil.CheckErr(o.RunAnnotate(f, cmd))
}, },
ValidArgs: validArgs, ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(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) 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 // retrieves resource and annotation args from args
// also checks args to verify that all resources are specified before annotations // also checks args to verify that all resources are specified before annotations
resources, annotationArgs, err := cmdutil.GetResourcesAndPairs(args, "annotation") 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.resources = resources
o.newAnnotations, o.removeAnnotations, err = parseAnnotations(annotationArgs) 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. // 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 // RunAnnotate does the work
func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) error { func (o AnnotateOptions) RunAnnotate() error {
namespace, enforceNamespace, err := f.DefaultNamespace() b := o.builder.
if err != nil {
return err
}
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
b := f.NewBuilder().
Unstructured(). Unstructured().
LocalParam(o.local). LocalParam(o.local).
ContinueOnError(). ContinueOnError().
NamespaceParam(namespace).DefaultNamespace(). NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(o.enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(includeUninitialized). IncludeUninitialized(o.includeUninitialized).
Flatten() Flatten()
if !o.local { if !o.local {
@ -276,7 +283,7 @@ func (o AnnotateOptions) RunAnnotate(f cmdutil.Factory, cmd *cobra.Command) erro
} }
mapping := info.ResourceMapping() mapping := info.ResourceMapping()
client, err := f.UnstructuredClientForMapping(mapping) client, err := o.unstructuredClientForMapping(mapping)
if err != nil { if err != nil {
return err return err
} }

View File

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

View File

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

View File

@ -18,19 +18,14 @@ package cmd
import ( import (
"bytes" "bytes"
"encoding/json"
"fmt" "fmt"
"io"
"github.com/ghodss/yaml"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
apijson "k8s.io/apimachinery/pkg/util/json" "k8s.io/kubernetes/pkg/api/legacyscheme"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
@ -39,21 +34,27 @@ import (
"k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/scheme" "k8s.io/kubernetes/pkg/kubectl/scheme"
"k8s.io/kubernetes/pkg/kubectl/util/i18n" "k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/printers"
) )
type SetLastAppliedOptions struct { type SetLastAppliedOptions struct {
FilenameOptions resource.FilenameOptions
Selector string
InfoList []*resource.Info
Mapper meta.RESTMapper
Namespace string
EnforceNamespace bool
DryRun bool
ShortOutput bool
CreateAnnotation bool CreateAnnotation bool
Output string
PatchBufferList []PatchBuffer PrintFlags *printers.PrintFlags
Factory cmdutil.Factory 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 genericclioptions.IOStreams
} }
@ -83,12 +84,13 @@ var (
func NewSetLastAppliedOptions(ioStreams genericclioptions.IOStreams) *SetLastAppliedOptions { func NewSetLastAppliedOptions(ioStreams genericclioptions.IOStreams) *SetLastAppliedOptions {
return &SetLastAppliedOptions{ return &SetLastAppliedOptions{
IOStreams: ioStreams, PrintFlags: printers.NewPrintFlags("configured"),
IOStreams: ioStreams,
} }
} }
func NewCmdApplySetLastApplied(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command { func NewCmdApplySetLastApplied(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
options := NewSetLastAppliedOptions(ioStreams) o := NewSetLastAppliedOptions(ioStreams)
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "set-last-applied -f FILENAME", Use: "set-last-applied -f FILENAME",
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
@ -96,38 +98,55 @@ func NewCmdApplySetLastApplied(f cmdutil.Factory, ioStreams genericclioptions.IO
Long: applySetLastAppliedLong, Long: applySetLastAppliedLong,
Example: applySetLastAppliedExample, Example: applySetLastAppliedExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd)) cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(options.Validate(f, cmd)) cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(options.RunSetLastApplied(f, cmd)) cmdutil.CheckErr(o.RunSetLastApplied())
}, },
} }
cmdutil.AddDryRunFlag(cmd) cmdutil.AddDryRunFlag(cmd)
cmdutil.AddPrinterFlags(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") cmd.Flags().BoolVar(&o.CreateAnnotation, "create-annotation", o.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, &o.FilenameOptions.Filenames, "Filename, directory, or URL to files that contains the last-applied-configuration annotations")
kubectl.AddJsonFilenameFlag(cmd, &options.FilenameOptions.Filenames, "Filename, directory, or URL to files "+usage)
return cmd return cmd
} }
func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.DryRun = cmdutil.GetDryRunFlag(cmd) o.dryRun = cmdutil.GetDryRunFlag(cmd)
o.Output = cmdutil.GetFlagString(cmd, "output") o.output = cmdutil.GetFlagString(cmd, "output")
o.ShortOutput = o.Output == "name" o.shortOutput = o.output == "name"
o.Mapper = f.RESTMapper()
o.mapper = f.RESTMapper()
var err error var err error
o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() o.namespace, o.enforceNamespace, err = f.DefaultNamespace()
return err 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 { func (o *SetLastAppliedOptions) Validate() error {
r := f.NewBuilder(). r := o.builder.
Unstructured(). Unstructured().
NamespaceParam(o.Namespace).DefaultNamespace(). NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, &o.FilenameOptions). FilenameParam(o.enforceNamespace, &o.FilenameOptions).
Flatten(). Flatten().
Do() 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) return cmdutil.AddSourceToErr(fmt.Sprintf("retrieving current configuration of:\n%s\nfrom server for:", info.String()), info.Source, err)
} }
if originalBuf == nil && !o.CreateAnnotation { 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 //only add to PatchBufferList when changed
if !bytes.Equal(cmdutil.StripComments(originalBuf), cmdutil.StripComments(diffBuf)) { if !bytes.Equal(cmdutil.StripComments(originalBuf), cmdutil.StripComments(diffBuf)) {
p := PatchBuffer{Patch: patchBuf, PatchType: patchType} p := PatchBuffer{Patch: patchBuf, PatchType: patchType}
o.PatchBufferList = append(o.PatchBufferList, p) o.patchBufferList = append(o.patchBufferList, p)
o.InfoList = append(o.InfoList, info) o.infoList = append(o.infoList, info)
} else { } else {
fmt.Fprintf(o.Out, "set-last-applied %s: no changes required.\n", info.Name) 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 return err
} }
func (o *SetLastAppliedOptions) RunSetLastApplied(f cmdutil.Factory, cmd *cobra.Command) error { func (o *SetLastAppliedOptions) RunSetLastApplied() error {
for i, patch := range o.PatchBufferList { for i, patch := range o.patchBufferList {
info := o.InfoList[i] info := o.infoList[i]
if !o.DryRun { if !o.dryRun {
mapping := info.ResourceMapping() mapping := info.ResourceMapping()
client, err := f.UnstructuredClientForMapping(mapping) client, err := o.unstructuredClientForMapping(mapping)
if err != nil { if err != nil {
return err return err
} }
helper := resource.NewHelper(client, mapping) 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 { if err != nil {
return err return err
} }
info.Refresh(patchedObj, false)
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")
} }
} if err := o.PrintObj(info.AsVersioned(legacyscheme.Scheme), o.Out); err != nil {
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 {
return err return err
} }
fmt.Fprintf(w, "%s\n", jsonBuffer.String())
case "yaml":
fmt.Fprintf(w, "%s\n", string(yamlOutput))
} }
return nil 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", name: "set with exist object",
filePath: filenameRC, filePath: filenameRC,
expectedErr: "", expectedErr: "",
expectedOut: "replicationcontroller/test-rc\n", expectedOut: "replicationcontroller/test-rc configured\n",
output: "name", output: "name",
}, },
{ {
@ -1134,7 +1134,7 @@ func TestRunApplySetLastApplied(t *testing.T) {
{ {
name: "set for the annotation does not exist on the live object", name: "set for the annotation does not exist on the live object",
filePath: filenameRCNoAnnotation, 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: "", expectedOut: "",
output: "name", output: "name",
}, },
@ -1142,14 +1142,14 @@ func TestRunApplySetLastApplied(t *testing.T) {
name: "set with exist object output json", name: "set with exist object output json",
filePath: filenameRCJSON, filePath: filenameRCJSON,
expectedErr: "", expectedErr: "",
expectedOut: "replicationcontroller/test-rc\n", expectedOut: "replicationcontroller/test-rc configured\n",
output: "name", output: "name",
}, },
{ {
name: "set test for a directory of files", name: "set test for a directory of files",
filePath: dirName, filePath: dirName,
expectedErr: "", expectedErr: "",
expectedOut: "replicationcontroller/test-rc\nreplicationcontroller/test-rc\n", expectedOut: "replicationcontroller/test-rc configured\nreplicationcontroller/test-rc configured\n",
output: "name", output: "name",
}, },
} }

View File

@ -24,7 +24,6 @@ import (
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
@ -59,20 +58,22 @@ type AutoscaleOptions struct {
PrintFlags *printers.PrintFlags PrintFlags *printers.PrintFlags
ToPrinter func(string) (printers.ResourcePrinterFunc, error) ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Builder *resource.Builder Name string
CanBeAutoscaled func(kind schema.GroupKind) error Generator string
Min int32
Max int32
CpuPercent int32
CreateAnnotation bool createAnnotation bool
DryRun bool args []string
EnforceNamespace bool enforceNamespace bool
namespace string
Mapper meta.RESTMapper dryRun bool
ClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error) builder *resource.Builder
mapper meta.RESTMapper
GeneratorFunc func(string, *meta.RESTMapping) (kubectl.StructuredGenerator, error) canBeAutoscaled func(kind schema.GroupKind) error
clientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
Namespace string generatorFunc func(string, *meta.RESTMapping) (kubectl.StructuredGenerator, error)
BuilderArgs []string
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -102,7 +103,7 @@ func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *
Example: autoscaleExample, Example: autoscaleExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(o.Validate(cmd)) cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.Run()) cmdutil.CheckErr(o.Run())
}, },
ValidArgs: validArgs, ValidArgs: validArgs,
@ -113,27 +114,26 @@ func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *
o.RecordFlags.AddFlags(cmd) o.RecordFlags.AddFlags(cmd)
o.PrintFlags.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().StringVar(&o.Generator, "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().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().Int32("max", -1, "The upper limit for the number of pods that can be set by the autoscaler. Required.") 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.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().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().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().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) cmdutil.AddDryRunFlag(cmd)
usage := "identifying the resource to autoscale." cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, "identifying the resource to autoscale.")
cmdutil.AddFilenameOptionFlags(cmd, o.FilenameOptions, usage)
cmdutil.AddApplyAnnotationFlags(cmd) cmdutil.AddApplyAnnotationFlags(cmd)
return cmd return cmd
} }
func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
o.DryRun = cmdutil.GetFlagBool(cmd, "dry-run") o.dryRun = cmdutil.GetFlagBool(cmd, "dry-run")
o.CreateAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag) o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.Builder = f.NewBuilder() o.builder = f.NewBuilder()
o.CanBeAutoscaled = f.CanBeAutoscaled o.canBeAutoscaled = f.CanBeAutoscaled
o.Mapper = f.RESTMapper() o.mapper = f.RESTMapper()
o.ClientForMapping = f.ClientForMapping o.clientForMapping = f.ClientForMapping
o.BuilderArgs = args o.args = args
o.RecordFlags.Complete(f.Command(cmd, false)) o.RecordFlags.Complete(f.Command(cmd, false))
var err error var err error
@ -143,34 +143,31 @@ func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
} }
// get the generator // get the generator
o.GeneratorFunc = func(name string, mapping *meta.RESTMapping) (kubectl.StructuredGenerator, error) { o.generatorFunc = func(name string, mapping *meta.RESTMapping) (kubectl.StructuredGenerator, error) {
var generator kubectl.StructuredGenerator switch o.Generator {
switch generatorName := cmdutil.GetFlagString(cmd, "generator"); generatorName {
case cmdutil.HorizontalPodAutoscalerV1GeneratorName: case cmdutil.HorizontalPodAutoscalerV1GeneratorName:
generator = &kubectl.HorizontalPodAutoscalerGeneratorV1{ return &kubectl.HorizontalPodAutoscalerGeneratorV1{
Name: name, Name: name,
MinReplicas: cmdutil.GetFlagInt32(cmd, "min"), MinReplicas: o.Min,
MaxReplicas: cmdutil.GetFlagInt32(cmd, "max"), MaxReplicas: o.Max,
CPUPercent: cmdutil.GetFlagInt32(cmd, "cpu-percent"), CPUPercent: o.CpuPercent,
ScaleRefName: name, ScaleRefName: name,
ScaleRefKind: mapping.GroupVersionKind.Kind, ScaleRefKind: mapping.GroupVersionKind.Kind,
ScaleRefApiVersion: mapping.GroupVersionKind.GroupVersion().String(), ScaleRefApiVersion: mapping.GroupVersionKind.GroupVersion().String(),
} }, nil
default: 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 { if err != nil {
return err return err
} }
o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) { o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) {
o.PrintFlags.NamePrintFlags.Operation = operation o.PrintFlags.NamePrintFlags.Operation = operation
if o.DryRun { if o.dryRun {
o.PrintFlags.Complete("%s (dry run)") o.PrintFlags.Complete("%s (dry run)")
} }
@ -185,21 +182,24 @@ func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
return nil return nil
} }
func (o *AutoscaleOptions) Validate(cmd *cobra.Command) error { func (o *AutoscaleOptions) Validate() error {
if err := validateFlags(cmd); err != nil { if o.Max < 1 {
return err 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 return nil
} }
func (o *AutoscaleOptions) Run() error { func (o *AutoscaleOptions) Run() error {
r := o.Builder. r := o.builder.
Internal(legacyscheme.Scheme). Internal(legacyscheme.Scheme).
ContinueOnError(). ContinueOnError().
NamespaceParam(o.Namespace).DefaultNamespace(). NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, o.FilenameOptions). FilenameParam(o.enforceNamespace, o.FilenameOptions).
ResourceTypeOrNameArgs(false, o.BuilderArgs...). ResourceTypeOrNameArgs(false, o.args...).
Flatten(). Flatten().
Do() Do()
if err := r.Err(); err != nil { if err := r.Err(); err != nil {
@ -213,11 +213,11 @@ func (o *AutoscaleOptions) Run() error {
} }
mapping := info.ResourceMapping() mapping := info.ResourceMapping()
if err := o.CanBeAutoscaled(mapping.GroupVersionKind.GroupKind()); err != nil { if err := o.canBeAutoscaled(mapping.GroupVersionKind.GroupKind()); err != nil {
return err return err
} }
generator, err := o.GeneratorFunc(info.Name, mapping) generator, err := o.generatorFunc(info.Name, mapping)
if err != nil { if err != nil {
return err return err
} }
@ -229,8 +229,8 @@ func (o *AutoscaleOptions) Run() error {
} }
resourceMapper := &resource.Mapper{ resourceMapper := &resource.Mapper{
RESTMapper: o.Mapper, RESTMapper: o.mapper,
ClientMapper: resource.ClientMapperFunc(o.ClientForMapping), ClientMapper: resource.ClientMapperFunc(o.clientForMapping),
Decoder: cmdutil.InternalVersionDecoder(), Decoder: cmdutil.InternalVersionDecoder(),
} }
hpa, err := resourceMapper.InfoForObject(object, legacyscheme.Scheme, nil) hpa, err := resourceMapper.InfoForObject(object, legacyscheme.Scheme, nil)
@ -242,7 +242,7 @@ func (o *AutoscaleOptions) Run() error {
} }
object = hpa.Object object = hpa.Object
if o.DryRun { if o.dryRun {
count++ count++
printer, err := o.ToPrinter("created") printer, err := o.ToPrinter("created")
@ -252,11 +252,11 @@ func (o *AutoscaleOptions) Run() error {
return printer.PrintObj(hpa.AsVersioned(legacyscheme.Scheme), o.Out) 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 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 { if err != nil {
return err return err
} }
@ -276,15 +276,3 @@ func (o *AutoscaleOptions) Run() error {
} }
return nil 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" "io"
"os" "os"
"path" "path"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 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" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/genericclioptions" "k8s.io/kubernetes/pkg/kubectl/genericclioptions"
@ -37,6 +41,15 @@ type ClusterInfoDumpOptions struct {
PrintFlags *printers.PrintFlags PrintFlags *printers.PrintFlags
PrintObj printers.ResourcePrinterFunc 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 genericclioptions.IOStreams
} }
@ -54,13 +67,13 @@ func NewCmdClusterInfoDump(f cmdutil.Factory, ioStreams genericclioptions.IOStre
Long: dumpLong, Long: dumpLong,
Example: dumpExample, Example: dumpExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete()) cmdutil.CheckErr(o.Complete(f, cmd))
cmdutil.CheckErr(o.Run(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().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().StringSlice("namespaces", []string{}, "A comma separated list of namespaces to dump.") cmd.Flags().StringSliceVar(&o.Namespaces, "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().BoolVar(&o.AllNamespaces, "all-namespaces", false, "If true, dump all namespaces. If true, --namespaces is ignored.")
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout) cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodLogsTimeout)
return cmd return cmd
} }
@ -89,8 +102,7 @@ var (
kubectl cluster-info dump --namespaces default,kube-system --output-directory=/path/to/cluster-state`)) 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 { func setupOutputWriter(dir string, defaultWriter io.Writer, filename string) io.Writer {
dir := cmdutil.GetFlagString(cmd, "output-directory")
if len(dir) == 0 || dir == "-" { if len(dir) == 0 || dir == "-" {
return defaultWriter return defaultWriter
} }
@ -103,7 +115,7 @@ func setupOutputWriter(cmd *cobra.Command, defaultWriter io.Writer, filename str
return file return file
} }
func (o *ClusterInfoDumpOptions) Complete() error { func (o *ClusterInfoDumpOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
printer, err := o.PrintFlags.ToPrinter() printer, err := o.PrintFlags.ToPrinter()
if err != nil { if err != nil {
return err return err
@ -113,32 +125,36 @@ func (o *ClusterInfoDumpOptions) Complete() error {
o.PrintFlags.OutputFormat = &jsonOutputFmt o.PrintFlags.OutputFormat = &jsonOutputFmt
o.PrintObj = printer.PrintObj 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 return nil
} }
func (o *ClusterInfoDumpOptions) Run(f cmdutil.Factory, cmd *cobra.Command) error { func (o *ClusterInfoDumpOptions) Run() error {
timeout, err := cmdutil.GetPodRunningTimeoutFlag(cmd) nodes, err := o.clientset.Core().Nodes().List(metav1.ListOptions{})
if err != nil {
return cmdutil.UsageErrorf(cmd, err.Error())
}
clientset, err := f.ClientSet()
if err != nil { if err != nil {
return err return err
} }
nodes, err := clientset.Core().Nodes().List(metav1.ListOptions{}) if err := o.PrintObj(nodes, setupOutputWriter(o.OutputDir, o.Out, "nodes.json")); err != nil {
if err != nil {
return err
}
if err := o.PrintObj(nodes, setupOutputWriter(cmd, o.Out, "nodes.json")); err != nil {
return err return err
} }
var namespaces []string var namespaces []string
if cmdutil.GetFlagBool(cmd, "all-namespaces") { if o.AllNamespaces {
namespaceList, err := clientset.Core().Namespaces().List(metav1.ListOptions{}) namespaceList, err := o.clientset.Core().Namespaces().List(metav1.ListOptions{})
if err != nil { if err != nil {
return err return err
} }
@ -146,75 +162,70 @@ func (o *ClusterInfoDumpOptions) Run(f cmdutil.Factory, cmd *cobra.Command) erro
namespaces = append(namespaces, namespaceList.Items[ix].Name) namespaces = append(namespaces, namespaceList.Items[ix].Name)
} }
} else { } else {
namespaces = cmdutil.GetFlagStringSlice(cmd, "namespaces") if len(o.Namespaces) == 0 {
if len(namespaces) == 0 {
cmdNamespace, _, err := f.DefaultNamespace()
if err != nil {
return err
}
namespaces = []string{ namespaces = []string{
metav1.NamespaceSystem, metav1.NamespaceSystem,
cmdNamespace, o.namespace,
} }
} }
} }
for _, namespace := range namespaces { for _, namespace := range namespaces {
// TODO: this is repetitive in the extreme. Use reflection or // TODO: this is repetitive in the extreme. Use reflection or
// something to make this a for loop. // 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 { if err != nil {
return err 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 return err
} }
rcs, err := clientset.Core().ReplicationControllers(namespace).List(metav1.ListOptions{}) rcs, err := o.clientset.Core().ReplicationControllers(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err 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 return err
} }
svcs, err := clientset.Core().Services(namespace).List(metav1.ListOptions{}) svcs, err := o.clientset.Core().Services(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err 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 return err
} }
sets, err := clientset.Extensions().DaemonSets(namespace).List(metav1.ListOptions{}) sets, err := o.clientset.Extensions().DaemonSets(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err 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 return err
} }
deps, err := clientset.Extensions().Deployments(namespace).List(metav1.ListOptions{}) deps, err := o.clientset.Extensions().Deployments(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err 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 return err
} }
rps, err := clientset.Extensions().ReplicaSets(namespace).List(metav1.ListOptions{}) rps, err := o.clientset.Extensions().ReplicaSets(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err 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 return err
} }
pods, err := clientset.Core().Pods(namespace).List(metav1.ListOptions{}) pods, err := o.clientset.Core().Pods(namespace).List(metav1.ListOptions{})
if err != nil { if err != nil {
return err 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 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))) 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))) 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 { if err != nil {
// Print error and return. // Print error and return.
writer.Write([]byte(fmt.Sprintf("Create log request error: %s\n", err.Error()))) 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 { for ix := range pods.Items {
pod := &pods.Items[ix] pod := &pods.Items[ix]
containers := pod.Spec.Containers 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 { for i := range containers {
printContainer(writer, containers[i], pod) printContainer(writer, containers[i], pod)
} }
} }
} }
dir := cmdutil.GetFlagString(cmd, "output-directory") if o.OutputDir != "-" {
if len(dir) == 0 { fmt.Fprintf(o.Out, "Cluster info dumped to %s\n", o.OutputDir)
dir = "standard output"
}
if dir != "-" {
fmt.Fprintf(o.Out, "Cluster info dumped to %s\n", dir)
} }
return nil return nil
} }

View File

@ -33,9 +33,7 @@ func TestSetupOutputWriterNoOp(t *testing.T) {
f := cmdtesting.NewTestFactory() f := cmdtesting.NewTestFactory()
defer f.Cleanup() defer f.Cleanup()
cmd := NewCmdClusterInfoDump(f, genericclioptions.NewTestIOStreamsDiscard()) writer := setupOutputWriter(test, buf, "/some/file/that/should/be/ignored")
cmd.Flag("output-directory").Value.Set(test)
writer := setupOutputWriter(cmd, buf, "/some/file/that/should/be/ignored")
if writer != buf { if writer != buf {
t.Errorf("expected: %v, saw: %v", buf, writer) t.Errorf("expected: %v, saw: %v", buf, writer)
} }
@ -55,9 +53,7 @@ func TestSetupOutputWriterFile(t *testing.T) {
f := cmdtesting.NewTestFactory() f := cmdtesting.NewTestFactory()
defer f.Cleanup() defer f.Cleanup()
cmd := NewCmdClusterInfoDump(f, genericclioptions.NewTestIOStreamsDiscard()) writer := setupOutputWriter(dir, buf, file)
cmd.Flag("output-directory").Value.Set(dir)
writer := setupOutputWriter(cmd, buf, file)
if writer == buf { if writer == buf {
t.Errorf("expected: %v, saw: %v", buf, writer) 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:", Message: "Advanced Commands:",
Commands: []*cobra.Command{ Commands: []*cobra.Command{
NewCmdApply("kubectl", f, ioStreams), NewCmdApply("kubectl", f, ioStreams),
NewCmdPatch(f, out), NewCmdPatch(f, ioStreams),
NewCmdReplace(f, out, err), NewCmdReplace(f, out, err),
NewCmdConvert(f, ioStreams), 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(alpha)
cmds.AddCommand(cmdconfig.NewCmdConfig(f, clientcmd.NewDefaultPathOptions(), out, err)) cmds.AddCommand(cmdconfig.NewCmdConfig(f, clientcmd.NewDefaultPathOptions(), out, err))
cmds.AddCommand(NewCmdPlugin(f, in, out, err)) cmds.AddCommand(NewCmdPlugin(f, in, out, err))
cmds.AddCommand(NewCmdVersion(f, out)) cmds.AddCommand(NewCmdVersion(f, ioStreams))
cmds.AddCommand(NewCmdApiVersions(f, out)) cmds.AddCommand(NewCmdApiVersions(f, ioStreams))
cmds.AddCommand(NewCmdApiResources(f, ioStreams)) cmds.AddCommand(NewCmdApiResources(f, ioStreams))
cmds.AddCommand(NewCmdOptions(out)) cmds.AddCommand(NewCmdOptions(out))

View File

@ -68,6 +68,12 @@ type LabelOptions struct {
Recorder genericclioptions.Recorder Recorder genericclioptions.Recorder
namespace string
enforceNamespace bool
includeUninitialized bool
builder *resource.Builder
unstructuredClientForMapping func(mapping *meta.RESTMapping) (resource.RESTClient, error)
// Common shared fields // Common shared fields
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -125,13 +131,9 @@ func NewCmdLabel(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobr
Long: fmt.Sprintf(labelLong, validation.LabelValueMaxLength), Long: fmt.Sprintf(labelLong, validation.LabelValueMaxLength),
Example: labelExample, Example: labelExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
if err := o.Complete(f, cmd, args); err != nil { cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, err.Error())) cmdutil.CheckErr(o.Validate())
} cmdutil.CheckErr(o.RunLabel())
if err := o.Validate(); err != nil {
cmdutil.CheckErr(cmdutil.UsageErrorf(cmd, err.Error()))
}
cmdutil.CheckErr(o.RunLabel(f, cmd))
}, },
ValidArgs: validArgs, ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(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) o.newLabels, o.removeLabels, err = parseLabels(labelArgs)
if o.list && len(o.outputFormat) > 0 { 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. // 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 // RunLabel does the work
func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error { func (o *LabelOptions) RunLabel() error {
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() b := o.builder.
if err != nil {
return err
}
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
b := f.NewBuilder().
Unstructured(). Unstructured().
LocalParam(o.local). LocalParam(o.local).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(o.enforceNamespace, &o.FilenameOptions).
IncludeUninitialized(includeUninitialized). IncludeUninitialized(o.includeUninitialized).
Flatten() Flatten()
if !o.local { if !o.local {
@ -294,7 +298,7 @@ func (o *LabelOptions) RunLabel(f cmdutil.Factory, cmd *cobra.Command) error {
} }
mapping := info.ResourceMapping() mapping := info.ResourceMapping()
client, err := f.UnstructuredClientForMapping(mapping) client, err := o.unstructuredClientForMapping(mapping)
if err != nil { if err != nil {
return err return err
} }

View File

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

View File

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

View File

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

View File

@ -70,46 +70,40 @@ type ScaleOptions struct {
PrintFlags *printers.PrintFlags PrintFlags *printers.PrintFlags
PrintObj printers.ResourcePrinterFunc PrintObj printers.ResourcePrinterFunc
BuilderArgs []string Selector string
Namespace string All bool
EnforceNamespace bool Replicas int
Builder *resource.Builder
ClientSet internalclientset.Interface
Scaler kubectl.Scaler
All bool
Selector string
CmdParent string
ResourceVersion string ResourceVersion string
CurrentReplicas int CurrentReplicas int
Replicas int Timeout time.Duration
Duration time.Duration
ClientForMapping func(*meta.RESTMapping) (resource.RESTClient, error) Recorder genericclioptions.Recorder
builder *resource.Builder
Recorder genericclioptions.Recorder 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 genericclioptions.IOStreams
} }
func NewScaleOptions(ioStreams genericclioptions.IOStreams) *ScaleOptions { func NewScaleOptions(ioStreams genericclioptions.IOStreams) *ScaleOptions {
return &ScaleOptions{ return &ScaleOptions{
RecordFlags: genericclioptions.NewRecordFlags(), RecordFlags: genericclioptions.NewRecordFlags(),
PrintFlags: printers.NewPrintFlags("scaled"), PrintFlags: printers.NewPrintFlags("scaled"),
CurrentReplicas: -1, CurrentReplicas: -1,
Recorder: genericclioptions.NoopRecorder{},
Recorder: genericclioptions.NoopRecorder{}, IOStreams: ioStreams,
IOStreams: ioStreams,
} }
} }
// NewCmdScale returns a cobra command with the appropriate configuration and flags to run scale // NewCmdScale returns a cobra command with the appropriate configuration and flags to run scale
func NewCmdScale(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { func NewCmdScale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *cobra.Command {
o := NewScaleOptions(streams) o := NewScaleOptions(ioStreams)
validArgs := []string{"deployment", "replicaset", "replicationcontroller", "statefulset"} validArgs := []string{"deployment", "replicaset", "replicationcontroller", "statefulset"}
argAliases := kubectl.ResourceAliases(validArgs) argAliases := kubectl.ResourceAliases(validArgs)
@ -122,7 +116,7 @@ func NewCmdScale(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.
Example: scaleExample, Example: scaleExample,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(o.Complete(f, cmd, args)) cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd)) cmdutil.CheckErr(o.Validate(cmd))
cmdutil.CheckErr(o.RunScale()) cmdutil.CheckErr(o.RunScale())
}, },
ValidArgs: validArgs, 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.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.Flags().IntVar(&o.Replicas, "replicas", o.Replicas, "The new desired number of replicas. Required.")
cmd.MarkFlagRequired("replicas") 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).") 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")
usage := "identifying the resource to set a new size"
cmdutil.AddFilenameOptionFlags(cmd, &o.FilenameOptions, usage)
return cmd return cmd
} }
func (o *ScaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { func (o *ScaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err 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 { if err != nil {
return err 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() printer, err := o.PrintFlags.ToPrinter()
if err != nil { if err != nil {
return err return err
} }
o.PrintObj = printer.PrintObj o.PrintObj = printer.PrintObj
o.RecordFlags.Complete(f.Command(cmd, false)) o.namespace, o.enforceNamespace, err = f.DefaultNamespace()
o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil { if err != nil {
return err 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 return nil
} }
// RunScale executes the scaling // RunScale executes the scaling
func (o *ScaleOptions) RunScale() error { func (o *ScaleOptions) RunScale() error {
r := o.builder.
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.
Unstructured(). Unstructured().
ContinueOnError(). ContinueOnError().
NamespaceParam(o.Namespace).DefaultNamespace(). NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, &o.FilenameOptions). FilenameParam(o.enforceNamespace, &o.FilenameOptions).
ResourceTypeOrNameArgs(o.All, o.BuilderArgs...). ResourceTypeOrNameArgs(o.All, o.args...).
Flatten(). Flatten().
LabelSelectorParam(o.Selector). LabelSelectorParam(o.Selector).
Do() Do()
err := r.Err() err := r.Err()
if resource.IsUsageError(err) {
return fmt.Errorf("%v", err)
}
if err != nil { if err != nil {
return err return err
} }
@ -219,12 +210,11 @@ func (o *ScaleOptions) RunScale() error {
return fmt.Errorf("cannot use --resource-version with multiple resources") return fmt.Errorf("cannot use --resource-version with multiple resources")
} }
currentSize := o.CurrentReplicas precondition := &kubectl.ScalePrecondition{Size: o.CurrentReplicas, ResourceVersion: o.ResourceVersion}
precondition := &kubectl.ScalePrecondition{Size: currentSize, ResourceVersion: o.ResourceVersion}
retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout) retry := kubectl.NewRetryParams(kubectl.Interval, kubectl.Timeout)
var waitForReplicas *kubectl.RetryParams var waitForReplicas *kubectl.RetryParams
if timeout := o.Duration; timeout != 0 { if o.Timeout != 0 {
waitForReplicas = kubectl.NewRetryParams(kubectl.Interval, timeout) waitForReplicas = kubectl.NewRetryParams(kubectl.Interval, timeout)
} }
@ -237,15 +227,15 @@ func (o *ScaleOptions) RunScale() error {
mapping := info.ResourceMapping() mapping := info.ResourceMapping()
if mapping.Resource == "jobs" { if mapping.Resource == "jobs" {
// go down the legacy jobs path. This can be removed in 3.14 For now, contain it. // 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 return err
} }
} else { } else {
gvk := mapping.GroupVersionKind.GroupVersion().WithResource(mapping.Resource) 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 return err
} }
} }
@ -254,7 +244,7 @@ func (o *ScaleOptions) RunScale() error {
if mergePatch, err := o.Recorder.MakeRecordMergePatch(info.Object); err != nil { if mergePatch, err := o.Recorder.MakeRecordMergePatch(info.Object); err != nil {
glog.V(4).Infof("error recording current command: %v", err) glog.V(4).Infof("error recording current command: %v", err)
} else if len(mergePatch) > 0 { } else if len(mergePatch) > 0 {
client, err := o.ClientForMapping(mapping) client, err := o.unstructuredClientForMapping(mapping)
if err != nil { if err != nil {
return err return err
} }

View File

@ -24,19 +24,19 @@ import (
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
envutil "k8s.io/kubernetes/pkg/kubectl/cmd/util/env" 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/kubectl/resource"
"k8s.io/kubernetes/pkg/printers" "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 ( var (
@ -94,32 +94,30 @@ var (
type EnvOptions struct { type EnvOptions struct {
PrintFlags *printers.PrintFlags PrintFlags *printers.PrintFlags
resource.FilenameOptions resource.FilenameOptions
EnvParams []string
EnvArgs []string
Resources []string
All bool EnvParams []string
Resolve bool All bool
List bool Resolve bool
Local bool List bool
Overwrite bool Local bool
DryRun bool Overwrite bool
ResourceVersion string
ContainerSelector string ContainerSelector string
Selector string Selector string
Output string
From string From string
Prefix string Prefix string
PrintObj printers.ResourcePrinterFunc PrintObj printers.ResourcePrinterFunc
Builder *resource.Builder envArgs []string
Infos []*resource.Info resources []string
output string
UpdatePodSpecForObject func(obj runtime.Object, fn func(*v1.PodSpec) error) (bool, error) 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 genericclioptions.IOStreams
} }
@ -139,7 +137,7 @@ func NewEnvOptions(streams genericclioptions.IOStreams) *EnvOptions {
// NewCmdEnv implements the OpenShift cli env command // NewCmdEnv implements the OpenShift cli env command
func NewCmdEnv(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command { func NewCmdEnv(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Command {
options := NewEnvOptions(streams) o := NewEnvOptions(streams)
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "env RESOURCE/NAME KEY_1=VAL_1 ... KEY_N=VAL_N", Use: "env RESOURCE/NAME KEY_1=VAL_1 ... KEY_N=VAL_N",
DisableFlagsInUseLine: true, DisableFlagsInUseLine: true,
@ -147,24 +145,25 @@ func NewCmdEnv(f cmdutil.Factory, streams genericclioptions.IOStreams) *cobra.Co
Long: envLong, Long: envLong,
Example: fmt.Sprintf(envExample), Example: fmt.Sprintf(envExample),
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(options.Complete(f, cmd, args)) cmdutil.CheckErr(o.Complete(f, cmd, args))
cmdutil.CheckErr(options.RunEnv(f)) cmdutil.CheckErr(o.Validate())
cmdutil.CheckErr(o.RunEnv())
}, },
} }
usage := "the resource to update the env" usage := "the resource to update the env"
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage) cmdutil.AddFilenameOptionFlags(cmd, &o.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().StringVarP(&o.ContainerSelector, "containers", "c", o.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().StringVarP(&o.From, "from", "", "", "The name of a resource from which to inject environment variables")
cmd.Flags().StringP("prefix", "", "", "Prefix to append to variable names") cmd.Flags().StringVarP(&o.Prefix, "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().StringArrayVarP(&o.EnvParams, "env", "e", o.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(&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(&options.Resolve, "resolve", options.Resolve, "If true, show secret or configmap references when listing variables") cmd.Flags().BoolVar(&o.Resolve, "resolve", o.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().StringVarP(&o.Selector, "selector", "l", o.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(&o.Local, "local", o.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(&o.All, "all", o.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.") 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) cmdutil.AddDryRunFlag(cmd)
return 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 { if o.All && len(o.Selector) > 0 {
return fmt.Errorf("cannot set --all and --selector at the same time") 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 { if !ok {
return cmdutil.UsageErrorf(cmd, "all resources must be specified before environment changes: %s", strings.Join(args, " ")) return fmt.Errorf("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>")
} }
o.UpdatePodSpecForObject = f.UpdatePodSpecForObject o.updatePodSpecForObject = f.UpdatePodSpecForObject
o.ContainerSelector = cmdutil.GetFlagString(cmd, "containers") o.output = cmdutil.GetFlagString(cmd, "output")
o.List = cmdutil.GetFlagBool(cmd, "list") o.dryRun = cmdutil.GetDryRunFlag(cmd)
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.EnvArgs = envArgs if o.dryRun {
o.Resources = resources
if o.DryRun {
// TODO(juanvallejo): This can be cleaned up even further by creating // TODO(juanvallejo): This can be cleaned up even further by creating
// a PrintFlags struct that binds the --dry-run flag, and whose // a PrintFlags struct that binds the --dry-run flag, and whose
// ToPrinter method returns a printer that understands how to print // 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 o.PrintObj = printer.PrintObj
if o.List && len(o.Output) > 0 { o.clientset, err = f.KubernetesClientSet()
return cmdutil.UsageErrorf(cmd, "--list and --output may not be specified together") if err != nil {
return err
} }
o.namespace, o.enforceNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
o.builder = f.NewBuilder
return nil 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 // RunEnv contains all the necessary functionality for the OpenShift cli env command
func (o *EnvOptions) RunEnv(f cmdutil.Factory) error { func (o *EnvOptions) RunEnv() error {
var kubeClient *kubernetes.Clientset env, remove, err := envutil.ParseEnv(append(o.EnvParams, o.envArgs...), o.In)
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)
if err != nil { if err != nil {
return err return err
} }
if len(o.From) != 0 { if len(o.From) != 0 {
b := f.NewBuilder(). b := o.builder().
Internal(legacyscheme.Scheme). Internal(legacyscheme.Scheme).
LocalParam(o.Local). LocalParam(o.Local).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(o.enforceNamespace, &o.FilenameOptions).
Flatten() Flatten()
if !o.Local { if !o.Local {
@ -320,27 +308,27 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
} }
} }
b := f.NewBuilder(). b := o.builder().
Internal(legacyscheme.Scheme). Internal(legacyscheme.Scheme).
LocalParam(o.Local). LocalParam(o.Local).
ContinueOnError(). ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace(). NamespaceParam(o.namespace).DefaultNamespace().
FilenameParam(enforceNamespace, &o.FilenameOptions). FilenameParam(o.enforceNamespace, &o.FilenameOptions).
Flatten() Flatten()
if !o.Local { if !o.Local {
b.LabelSelectorParam(o.Selector). b.LabelSelectorParam(o.Selector).
ResourceTypeOrNameArgs(o.All, o.Resources...). ResourceTypeOrNameArgs(o.All, o.resources...).
Latest() Latest()
} }
o.Infos, err = b.Do().Infos() infos, err := b.Do().Infos()
if err != nil { if err != nil {
return err 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) 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 resolutionErrorsEncountered := false
containers, _ := selectContainers(spec.Containers, o.ContainerSelector) containers, _ := selectContainers(spec.Containers, o.ContainerSelector)
if len(containers) == 0 { if len(containers) == 0 {
@ -373,7 +361,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
continue 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 // Print the resolved value
if err == nil { if err == nil {
fmt.Fprintf(o.Out, "%s=%s\n", env.Name, value) fmt.Fprintf(o.Out, "%s=%s\n", env.Name, value)
@ -429,7 +417,7 @@ func (o *EnvOptions) RunEnv(f cmdutil.Factory) error {
continue continue
} }
if o.Local || o.DryRun { if o.Local || o.dryRun {
if err := o.PrintObj(patch.Info.AsVersioned(legacyscheme.Scheme), o.Out); err != nil { if err := o.PrintObj(patch.Info.AsVersioned(legacyscheme.Scheme), o.Out); err != nil {
return err 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 // make sure arguments to set or replace environment variables are set
// before returning a successful message // 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") 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.Namespace = "test"
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
outputFormat := "name" outputFormat := "name"
streams, _, buf, _ := genericclioptions.NewTestIOStreams() streams, _, buf, bufErr := genericclioptions.NewTestIOStreams()
cmd := NewCmdEnv(tf, streams) opts := NewEnvOptions(streams)
cmd.SetOutput(buf) opts.PrintFlags = &printers.PrintFlags{
cmd.Flags().Set("output", outputFormat) JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
cmd.Flags().Set("local", "true") NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
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,
} }
err := opts.Complete(tf, cmd, []string{"env=prod"}) opts.FilenameOptions = resource.FilenameOptions{
if err == nil { Filenames: []string{"../../../../test/e2e/testing-manifests/statefulset/cassandra/controller.yaml"},
err = opts.RunEnv(tf)
} }
if err != nil { opts.Local = true
t.Fatalf("unexpected error: %v", err)
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") { if !strings.Contains(buf.String(), "replicationcontroller/cassandra") {
t.Errorf("did not set env: %s", buf.String()) t.Errorf("did not set env: %s", buf.String())
@ -99,7 +94,6 @@ func TestSetMultiResourcesEnvLocal(t *testing.T) {
defer tf.Cleanup() defer tf.Cleanup()
ns := legacyscheme.Codecs ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{ tf.Client = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: ""}, GroupVersion: schema.GroupVersion{Version: ""},
NegotiatedSerializer: ns, NegotiatedSerializer: ns,
@ -112,33 +106,27 @@ func TestSetMultiResourcesEnvLocal(t *testing.T) {
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}} tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
outputFormat := "name" outputFormat := "name"
streams, _, buf, bufErr := genericclioptions.NewTestIOStreams()
streams, _, buf, _ := genericclioptions.NewTestIOStreams() opts := NewEnvOptions(streams)
cmd := NewCmdEnv(tf, streams) opts.PrintFlags = &printers.PrintFlags{
cmd.SetOutput(buf) JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
cmd.Flags().Set("output", outputFormat) NamePrintFlags: printers.NewNamePrintFlags(""),
cmd.Flags().Set("local", "true") OutputFormat: &outputFormat,
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,
} }
err := opts.Complete(tf, cmd, []string{"env=prod"}) opts.FilenameOptions = resource.FilenameOptions{
if err == nil { Filenames: []string{"../../../../test/fixtures/pkg/kubectl/cmd/set/multi-resource-yaml.yaml"},
err = opts.RunEnv(tf)
}
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()))
}
expectedOut := "replicationcontroller/first-rc\nreplicationcontroller/second-rc\n" expectedOut := "replicationcontroller/first-rc\nreplicationcontroller/second-rc\n"
if buf.String() != expectedOut { if buf.String() != expectedOut {
t.Errorf("expected out:\n%s\nbut got:\n%s", expectedOut, buf.String()) 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} groupVersion := schema.GroupVersion{Group: input.apiGroup, Version: input.apiVersion}
testapi.Default = testapi.Groups[input.testAPIGroup] testapi.Default = testapi.Groups[input.testAPIGroup]
tf := cmdtesting.NewTestFactory() tf := cmdtesting.NewTestFactory()
tf.ClientConfigVal = &restclient.Config{ContentConfig: restclient.ContentConfig{GroupVersion: &schema.GroupVersion{Version: ""}}}
defer tf.Cleanup() defer tf.Cleanup()
codec := scheme.Codecs.CodecForVersions(scheme.Codecs.LegacyCodec(groupVersion), scheme.Codecs.UniversalDecoder(groupVersion), groupVersion, groupVersion) 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" outputFormat := "yaml"
streams := genericclioptions.NewTestIOStreamsDiscard() streams := genericclioptions.NewTestIOStreamsDiscard()
cmd := NewCmdEnv(tf, streams) opts := NewEnvOptions(streams)
cmd.Flags().Set("output", outputFormat) opts.PrintFlags = &printers.PrintFlags{
opts := EnvOptions{ JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(),
PrintFlags: &printers.PrintFlags{ NamePrintFlags: printers.NewNamePrintFlags(""),
JSONYamlPrintFlags: printers.NewJSONYamlPrintFlags(), OutputFormat: &outputFormat,
NamePrintFlags: printers.NewNamePrintFlags(""),
OutputFormat: &outputFormat,
},
Local: false,
IOStreams: streams,
} }
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) assert.NoError(t, err)
err = opts.RunEnv(tf) err = opts.RunEnv()
assert.NoError(t, err) assert.NoError(t, err)
}) })
} }

View File

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

View File

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