update delete, replace, run cmds

This commit is contained in:
juanvallejo 2018-04-09 19:13:50 -04:00
parent a62a157c48
commit ecaddbfc36
No known key found for this signature in database
GPG Key ID: 7D2C958002D6448D
7 changed files with 328 additions and 389 deletions

View File

@ -305,7 +305,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob
Commands: []*cobra.Command{
NewCmdApply("kubectl", f, out, err),
NewCmdPatch(f, out),
NewCmdReplace(f, out),
NewCmdReplace(f, out, err),
NewCmdConvert(f, out),
},
},

View File

@ -151,192 +151,6 @@ func stringBody(body string) io.ReadCloser {
return ioutil.NopCloser(bytes.NewReader([]byte(body)))
}
func Example_printMultiContainersReplicationControllerWithWide() {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
ctrl := &api.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar"},
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Selector: map[string]string{"foo": "bar"},
Template: &api.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "foo",
Image: "someimage",
},
{
Name: "foo2",
Image: "someimage2",
},
},
},
},
},
Status: api.ReplicationControllerStatus{
Replicas: 1,
},
}
cmd.Flags().Set("output", "wide")
err := cmdutil.PrintObject(cmd, ctrl, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
// Output:
// NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
// foo 1 1 0 10y foo,foo2 someimage,someimage2 foo=bar
}
func Example_printReplicationController() {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
ctrl := &api.ReplicationController{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Labels: map[string]string{"foo": "bar"},
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
},
Spec: api.ReplicationControllerSpec{
Replicas: 1,
Selector: map[string]string{"foo": "bar"},
Template: &api.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"foo": "bar"},
},
Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "foo",
Image: "someimage",
},
{
Name: "foo2",
Image: "someimage",
},
},
},
},
},
Status: api.ReplicationControllerStatus{
Replicas: 1,
},
}
err := cmdutil.PrintObject(cmd, ctrl, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
// Output:
// NAME DESIRED CURRENT READY AGE
// foo 1 1 0 10y
}
func Example_printPodWithWideFormat() {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
nodeName := "kubernetes-node-abcd"
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
pod := &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
},
Spec: api.PodSpec{
Containers: make([]api.Container, 2),
NodeName: nodeName,
},
Status: api.PodStatus{
Phase: "podPhase",
ContainerStatuses: []api.ContainerStatus{
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
{RestartCount: 3},
},
PodIP: "10.1.1.3",
},
}
cmd.Flags().Set("output", "wide")
err := cmdutil.PrintObject(cmd, pod, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
// Output:
// NAME READY STATUS RESTARTS AGE IP NODE
// test1 1/2 podPhase 6 10y 10.1.1.3 kubernetes-node-abcd
}
func Example_printPodWithShowLabels() {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
nodeName := "kubernetes-node-abcd"
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
pod := &api.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test1",
CreationTimestamp: metav1.Time{Time: time.Now().AddDate(-10, 0, 0)},
Labels: map[string]string{
"l1": "key",
"l2": "value",
},
},
Spec: api.PodSpec{
Containers: make([]api.Container, 2),
NodeName: nodeName,
},
Status: api.PodStatus{
Phase: "podPhase",
ContainerStatuses: []api.ContainerStatus{
{Ready: true, RestartCount: 3, State: api.ContainerState{Running: &api.ContainerStateRunning{}}},
{RestartCount: 3},
},
},
}
cmd.Flags().Set("show-labels", "true")
err := cmdutil.PrintObject(cmd, pod, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
// Output:
// NAME READY STATUS RESTARTS AGE LABELS
// test1 1/2 podPhase 6 10y l1=key,l2=value
}
func newAllPhasePodList() *api.PodList {
nodeName := "kubernetes-node-abcd"
return &api.PodList{
@ -429,37 +243,6 @@ func newAllPhasePodList() *api.PodList {
}
}
func Example_printPodShowTerminated() {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
ns := legacyscheme.Codecs
tf.Client = &fake.RESTClient{
NegotiatedSerializer: ns,
Client: nil,
}
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
podList := newAllPhasePodList()
printer, err := cmdutil.PrinterForOptions(cmdutil.ExtractCmdPrintOptions(cmd, false))
if err != nil {
fmt.Printf("Unexpected printer get error: %v\n", err)
}
for _, pod := range []runtime.Object{podList} {
err := printer.PrintObj(pod, os.Stdout)
if err != nil {
fmt.Printf("Unexpected error: %v", err)
}
}
// Output:
// NAME READY STATUS RESTARTS AGE
// test1 1/2 Pending 6 10y
// test2 1/2 Running 6 10y
// test3 1/2 Succeeded 6 10y
// test4 1/2 Failed 6 10y
// test5 1/2 Unknown 6 10y
}
func Example_printServiceWithLabels() {
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()

View File

@ -98,6 +98,8 @@ type DeleteOptions struct {
ForceDeletion bool
WaitForDeletion bool
Reaper func(mapping *meta.RESTMapping) (kubectl.Reaper, error)
GracePeriod int
Timeout time.Duration
@ -107,21 +109,23 @@ type DeleteOptions struct {
Mapper meta.RESTMapper
Result *resource.Result
f cmdutil.Factory
Out io.Writer
ErrOut io.Writer
}
func NewDeleteOptions() *DeleteOptions {
func NewDeleteOptions(out, errout io.Writer) *DeleteOptions {
return &DeleteOptions{
Cascade: true,
GracePeriod: -1,
Include3rdParty: true,
Out: out,
ErrOut: errout,
}
}
func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
options := NewDeleteOptions()
options := NewDeleteOptions(out, errOut)
validArgs := cmdutil.ValidArgList(f)
cmd := &cobra.Command{
@ -146,6 +150,7 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
ValidArgs: validArgs,
ArgAliases: kubectl.ResourceAliases(validArgs),
}
usage := "containing the resource to delete."
cmdutil.AddFilenameOptionFlags(cmd, &options.FilenameOptions, usage)
cmd.Flags().StringVarP(&options.Selector, "selector", "l", options.Selector, "Selector (label query) to filter on, not including uninitialized ones.")
@ -156,7 +161,11 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
cmd.Flags().BoolVar(&options.DeleteNow, "now", options.DeleteNow, "If true, resources are signaled for immediate shutdown (same as --grace-period=1).")
cmd.Flags().BoolVar(&options.ForceDeletion, "force", options.ForceDeletion, "Only used when grace-period=0. If true, immediately remove resources from API and bypass graceful deletion. Note that immediate deletion of some resources may result in inconsistency or data loss and requires confirmation.")
cmd.Flags().DurationVar(&options.Timeout, "timeout", options.Timeout, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object")
// we do not need to wire PrintFlags through this command,
// as it does not deal with runtime.Objects, and only accepts the "name" output format
cmdutil.AddOutputVarFlagsForMutation(cmd, &options.Output)
cmdutil.AddInclude3rdPartyVarFlags(cmd, &options.Include3rdParty)
cmdutil.AddIncludeUninitializedFlag(cmd)
return cmd
@ -168,6 +177,8 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args
return err
}
o.Reaper = f.Reaper
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
r := f.NewBuilder().
Unstructured().
@ -187,7 +198,6 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args
o.Result = r
o.Mapper = r.Mapper().RESTMapper
o.f = f
// Set up writer
o.Out = out
o.ErrOut = errOut
@ -233,17 +243,20 @@ func (o *DeleteOptions) Validate(cmd *cobra.Command) error {
}
func (o *DeleteOptions) RunDelete() error {
shortOutput := o.Output == "name"
// By default use a reaper to delete all related resources.
if o.Cascade {
return ReapResult(o.Result, o.f, o.Out, true, o.IgnoreNotFound, o.Timeout, o.GracePeriod, o.WaitForDeletion, shortOutput, false)
// TODO(juanvallejo): although o.Result can be accessed from the options
// it is also passed here so that callers of this method outside of the "delete"
// command do not have to tack it to the "delete" options as well.
// Find a cleaner way to approach this.
return o.ReapResult(o.Result, true, false)
}
return DeleteResult(o.Result, o.Out, o.IgnoreNotFound, o.GracePeriod, shortOutput)
return o.DeleteResult(o.Result)
}
func ReapResult(r *resource.Result, f cmdutil.Factory, out io.Writer, isDefaultDelete, ignoreNotFound bool, timeout time.Duration, gracePeriod int, waitForDeletion, shortOutput bool, quiet bool) error {
func (o *DeleteOptions) ReapResult(r *resource.Result, isDefaultDelete, quiet bool) error {
found := 0
if ignoreNotFound {
if o.IgnoreNotFound {
r = r.IgnoreErrors(errors.IsNotFound)
}
err := r.Visit(func(info *resource.Info, err error) error {
@ -251,29 +264,29 @@ func ReapResult(r *resource.Result, f cmdutil.Factory, out io.Writer, isDefaultD
return err
}
found++
reaper, err := f.Reaper(info.Mapping)
reaper, err := o.Reaper(info.Mapping)
if err != nil {
// If there is no reaper for this resources and the user didn't explicitly ask for stop.
if kubectl.IsNoSuchReaperError(err) && isDefaultDelete {
// No client side reaper found. Let the server do cascading deletion.
return cascadingDeleteResource(info, out, shortOutput, gracePeriod)
return o.cascadingDeleteResource(info)
}
return cmdutil.AddSourceToErr("reaping", info.Source, err)
}
var options *metav1.DeleteOptions
if gracePeriod >= 0 {
options = metav1.NewDeleteOptions(int64(gracePeriod))
if o.GracePeriod >= 0 {
options = metav1.NewDeleteOptions(int64(o.GracePeriod))
}
if err := reaper.Stop(info.Namespace, info.Name, timeout, options); err != nil {
if err := reaper.Stop(info.Namespace, info.Name, o.Timeout, options); err != nil {
return cmdutil.AddSourceToErr("stopping", info.Source, err)
}
if waitForDeletion {
if err := waitForObjectDeletion(info, timeout); err != nil {
if o.WaitForDeletion {
if err := waitForObjectDeletion(info, o.Timeout); err != nil {
return cmdutil.AddSourceToErr("stopping", info.Source, err)
}
}
if !quiet {
printDeletion(info, out, shortOutput, gracePeriod)
o.PrintObj(info)
}
return nil
})
@ -281,14 +294,14 @@ func ReapResult(r *resource.Result, f cmdutil.Factory, out io.Writer, isDefaultD
return err
}
if found == 0 {
fmt.Fprintf(out, "No resources found\n")
fmt.Fprintf(o.Out, "No resources found\n")
}
return nil
}
func DeleteResult(r *resource.Result, out io.Writer, ignoreNotFound bool, gracePeriod int, shortOutput bool) error {
func (o *DeleteOptions) DeleteResult(r *resource.Result) error {
found := 0
if ignoreNotFound {
if o.IgnoreNotFound {
r = r.IgnoreErrors(errors.IsNotFound)
}
err := r.Visit(func(info *resource.Info, err error) error {
@ -300,38 +313,38 @@ func DeleteResult(r *resource.Result, out io.Writer, ignoreNotFound bool, graceP
// if we're here, it means that cascade=false (not the default), so we should orphan as requested
orphan := true
options := &metav1.DeleteOptions{}
if gracePeriod >= 0 {
options = metav1.NewDeleteOptions(int64(gracePeriod))
if o.GracePeriod >= 0 {
options = metav1.NewDeleteOptions(int64(o.GracePeriod))
}
options.OrphanDependents = &orphan
return deleteResource(info, out, shortOutput, options, gracePeriod)
return o.deleteResource(info, options)
})
if err != nil {
return err
}
if found == 0 {
fmt.Fprintf(out, "No resources found\n")
fmt.Fprintf(o.Out, "No resources found\n")
}
return nil
}
func cascadingDeleteResource(info *resource.Info, out io.Writer, shortOutput bool, gracePeriod int) error {
func (o *DeleteOptions) cascadingDeleteResource(info *resource.Info) error {
falseVar := false
deleteOptions := &metav1.DeleteOptions{OrphanDependents: &falseVar}
return deleteResource(info, out, shortOutput, deleteOptions, gracePeriod)
return o.deleteResource(info, &metav1.DeleteOptions{OrphanDependents: &falseVar})
}
func deleteResource(info *resource.Info, out io.Writer, shortOutput bool, deleteOptions *metav1.DeleteOptions, gracePeriod int) error {
func (o *DeleteOptions) deleteResource(info *resource.Info, deleteOptions *metav1.DeleteOptions) error {
if err := resource.NewHelper(info.Client, info.Mapping).DeleteWithOptions(info.Namespace, info.Name, deleteOptions); err != nil {
return cmdutil.AddSourceToErr("deleting", info.Source, err)
}
printDeletion(info, out, shortOutput, gracePeriod)
o.PrintObj(info)
return nil
}
// deletion printing is special because they don't have an object to print. This logic mirrors PrintSuccess
func printDeletion(info *resource.Info, out io.Writer, shortOutput bool, gracePeriod int) {
// deletion printing is special because we do not have an object to print.
// This mirrors name printer behavior
func (o *DeleteOptions) PrintObj(info *resource.Info) {
operation := "deleted"
groupKind := info.Mapping.GroupVersionKind
kindString := fmt.Sprintf("%s.%s", strings.ToLower(groupKind.Kind), groupKind.Group)
@ -339,18 +352,18 @@ func printDeletion(info *resource.Info, out io.Writer, shortOutput bool, gracePe
kindString = strings.ToLower(groupKind.Kind)
}
if gracePeriod == 0 {
if o.GracePeriod == 0 {
operation = "force deleted"
}
if shortOutput {
if o.Output == "name" {
// -o name: prints resource/name
fmt.Fprintf(out, "%s/%s\n", kindString, info.Name)
fmt.Fprintf(o.Out, "%s/%s\n", kindString, info.Name)
return
}
// understandable output by default
fmt.Fprintf(out, "%s \"%s\" %s\n", kindString, info.Name, operation)
fmt.Fprintf(o.Out, "%s \"%s\" %s\n", kindString, info.Name, operation)
}
// objectDeletionWaitInterval is the interval to wait between checks for deletion.

View File

@ -26,13 +26,17 @@ import (
"github.com/spf13/cobra"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/kubernetes/pkg/kubectl"
"k8s.io/kubernetes/pkg/kubectl/cmd/templates"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/kubectl/resource"
"k8s.io/kubernetes/pkg/kubectl/util/i18n"
"k8s.io/kubernetes/pkg/kubectl/validation"
"k8s.io/kubernetes/pkg/printers"
)
var (
@ -60,8 +64,40 @@ var (
kubectl replace --force -f ./pod.json`))
)
func NewCmdReplace(f cmdutil.Factory, out io.Writer) *cobra.Command {
options := &resource.FilenameOptions{}
type ReplaceOpts struct {
PrintFlags *printers.PrintFlags
FileNameOptions *resource.FilenameOptions
DeleteOptions *DeleteOptions
PrintObj func(obj runtime.Object) error
createAnnotation bool
changeCause string
validate bool
Schema validation.Schema
Builder func() *resource.Builder
BuilderArgs []string
ShouldRecord func(info *resource.Info) bool
Namespace string
EnforceNamespace bool
Out io.Writer
ErrOut io.Writer
}
func NewCmdReplace(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
options := &ReplaceOpts{
PrintFlags: printers.NewPrintFlags("replaced"),
FileNameOptions: &resource.FilenameOptions{},
DeleteOptions: NewDeleteOptions(out, errOut),
Out: out,
ErrOut: errOut,
}
cmd := &cobra.Command{
Use: "replace -f FILENAME",
@ -71,19 +107,22 @@ func NewCmdReplace(f cmdutil.Factory, out io.Writer) *cobra.Command {
Example: replaceExample,
Run: func(cmd *cobra.Command, args []string) {
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
err := RunReplace(f, out, cmd, args, options)
cmdutil.CheckErr(err)
cmdutil.CheckErr(options.Complete(f, cmd, args))
cmdutil.CheckErr(options.Validate(cmd))
cmdutil.CheckErr(options.Run())
},
}
options.PrintFlags.AddFlags(cmd)
usage := "to use to replace the resource."
cmdutil.AddFilenameOptionFlags(cmd, options, usage)
cmdutil.AddFilenameOptionFlags(cmd, options.FileNameOptions, usage)
cmd.MarkFlagRequired("filename")
cmd.Flags().Bool("force", false, "Delete and re-create the specified resource")
cmd.Flags().Bool("cascade", false, "Only relevant during a force replace. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController).")
cmd.Flags().Int("grace-period", -1, "Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.")
cmd.Flags().Duration("timeout", 0, "Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
cmd.Flags().BoolVar(&options.DeleteOptions.ForceDeletion, "force", options.DeleteOptions.ForceDeletion, "Delete and re-create the specified resource")
cmd.Flags().BoolVar(&options.DeleteOptions.Cascade, "cascade", options.DeleteOptions.Cascade, "Only relevant during a force replace. If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController).")
cmd.Flags().IntVar(&options.DeleteOptions.GracePeriod, "grace-period", options.DeleteOptions.GracePeriod, "Only relevant during a force replace. Period of time in seconds given to the old resource to terminate gracefully. Ignored if negative.")
cmd.Flags().DurationVar(&options.DeleteOptions.Timeout, "timeout", options.DeleteOptions.Timeout, "Only relevant during a force replace. The length of time to wait before giving up on a delete of the old resource, zero means determine a timeout from the size of the object. Any other values should contain a corresponding time unit (e.g. 1s, 2m, 3h).")
cmdutil.AddValidateFlags(cmd)
cmdutil.AddOutputFlagsForMutation(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
cmdutil.AddRecordFlag(cmd)
cmdutil.AddInclude3rdPartyFlags(cmd)
@ -91,58 +130,101 @@ func NewCmdReplace(f cmdutil.Factory, out io.Writer) *cobra.Command {
return cmd
}
func RunReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *resource.FilenameOptions) error {
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
func (o *ReplaceOpts) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
o.validate = cmdutil.GetFlagBool(cmd, "validate")
o.changeCause = f.Command(cmd, false)
o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.ShouldRecord = func(info *resource.Info) bool {
return cmdutil.ShouldRecord(cmd, info)
}
printer, err := o.PrintFlags.ToPrinter()
if err != nil {
return err
}
o.PrintObj = func(obj runtime.Object) error {
return printer.PrintObj(obj, o.Out)
}
// complete delete options
// TODO(juanvallejo): Turn these fields in a DeleteFlags struct, similar to PrintFlags
//Replace will create a resource if it doesn't exist already, so ignore not found error
o.DeleteOptions.IgnoreNotFound = true
o.DeleteOptions.Reaper = f.Reaper
if o.PrintFlags.OutputFormat != nil {
o.DeleteOptions.Output = *o.PrintFlags.OutputFormat
}
if o.DeleteOptions.GracePeriod == 0 {
// To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0
// into --grace-period=1 and wait until the object is successfully deleted.
o.DeleteOptions.GracePeriod = 1
o.DeleteOptions.WaitForDeletion = true
}
schema, err := f.Validator(o.validate)
if err != nil {
return err
}
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
o.Schema = schema
o.Builder = f.NewBuilder
o.BuilderArgs = args
o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace()
if err != nil {
return err
}
force := cmdutil.GetFlagBool(cmd, "force")
if cmdutil.IsFilenameSliceEmpty(options.Filenames) {
return cmdutil.UsageErrorf(cmd, "Must specify --filename to replace")
}
return nil
}
shortOutput := cmdutil.GetFlagString(cmd, "output") == "name"
if force {
return forceReplace(f, out, cmd, args, shortOutput, options)
}
if cmdutil.GetFlagInt(cmd, "grace-period") >= 0 {
func (o *ReplaceOpts) Validate(cmd *cobra.Command) error {
if o.DeleteOptions.GracePeriod >= 0 && !o.DeleteOptions.ForceDeletion {
return fmt.Errorf("--grace-period must have --force specified")
}
if cmdutil.GetFlagDuration(cmd, "timeout") != 0 {
if o.DeleteOptions.Timeout != 0 && !o.DeleteOptions.ForceDeletion {
return fmt.Errorf("--timeout must have --force specified")
}
r := f.NewBuilder().
if cmdutil.IsFilenameSliceEmpty(o.FileNameOptions.Filenames) {
return cmdutil.UsageErrorf(cmd, "Must specify --filename to replace")
}
return nil
}
func (o *ReplaceOpts) Run() error {
if o.DeleteOptions.ForceDeletion {
return o.forceReplace()
}
r := o.Builder().
Unstructured().
Schema(schema).
Schema(o.Schema).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options).
NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, o.FileNameOptions).
Flatten().
Do()
if err := r.Err(); err != nil {
return err
}
return r.Visit(func(info *resource.Info, err error) error {
return o.Result.Visit(func(info *resource.Info, err error) error {
if err != nil {
return err
}
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, cmdutil.InternalVersionJSONEncoder()); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(o.createAnnotation, info, cmdutil.InternalVersionJSONEncoder()); err != nil {
return cmdutil.AddSourceToErr("replacing", info.Source, err)
}
if cmdutil.ShouldRecord(cmd, info) {
if err := cmdutil.RecordChangeCause(info.Object, f.Command(cmd, false)); err != nil {
if o.ShouldRecord(info) {
if err := cmdutil.RecordChangeCause(info.Object, o.changeCause); err != nil {
return cmdutil.AddSourceToErr("replacing", info.Source, err)
}
}
@ -154,23 +236,12 @@ func RunReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
}
info.Refresh(obj, true)
cmdutil.PrintSuccess(shortOutput, out, info.Object, false, "replaced")
return nil
return o.PrintObj(info.AsVersioned())
})
}
func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, shortOutput bool, options *resource.FilenameOptions) error {
schema, err := f.Validator(cmdutil.GetFlagBool(cmd, "validate"))
if err != nil {
return err
}
cmdNamespace, enforceNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
for i, filename := range options.Filenames {
func (o *ReplaceOpts) forceReplace() error {
for i, filename := range o.FileNameOptions.Filenames {
if filename == "-" {
tempDir, err := ioutil.TempDir("", "kubectl_replace_")
if err != nil {
@ -182,42 +253,30 @@ func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
if err != nil {
return err
}
options.Filenames[i] = tempFilename
o.FileNameOptions.Filenames[i] = tempFilename
}
}
r := f.NewBuilder().
r := o.Builder().
Unstructured().
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options).
ResourceTypeOrNameArgs(false, args...).RequireObject(false).
NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, o.FileNameOptions).
ResourceTypeOrNameArgs(false, o.BuilderArgs...).RequireObject(false).
Flatten().
Do()
if err := r.Err(); err != nil {
return err
}
//Replace will create a resource if it doesn't exist already, so ignore not found error
ignoreNotFound := true
timeout := cmdutil.GetFlagDuration(cmd, "timeout")
gracePeriod := cmdutil.GetFlagInt(cmd, "grace-period")
waitForDeletion := false
if gracePeriod == 0 {
// To preserve backwards compatibility, but prevent accidental data loss, we convert --grace-period=0
// into --grace-period=1 and wait until the object is successfully deleted.
gracePeriod = 1
waitForDeletion = true
}
var err error
// By default use a reaper to delete all related resources.
if cmdutil.GetFlagBool(cmd, "cascade") {
if o.DeleteOptions.Cascade {
glog.Warningf("\"cascade\" is set, kubectl will delete and re-create all resources managed by this resource (e.g. Pods created by a ReplicationController). Consider using \"kubectl rolling-update\" if you want to update a ReplicationController together with its Pods.")
err = ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"), ignoreNotFound, timeout, gracePeriod, waitForDeletion, shortOutput, false)
err = o.DeleteOptions.ReapResult(r, o.DeleteOptions.Cascade, false)
} else {
err = DeleteResult(r, out, ignoreNotFound, gracePeriod, shortOutput)
}
if err != nil {
return err
err = o.DeleteOptions.DeleteResult(r)
}
if timeout == 0 {
@ -239,12 +298,12 @@ func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
return err
}
r = f.NewBuilder().
r = o.Builder().
Unstructured().
Schema(schema).
Schema(o.Schema).
ContinueOnError().
NamespaceParam(cmdNamespace).DefaultNamespace().
FilenameParam(enforceNamespace, options).
NamespaceParam(o.Namespace).DefaultNamespace().
FilenameParam(o.EnforceNamespace, o.FileNameOptions).
Flatten().
Do()
err = r.Err()
@ -258,12 +317,12 @@ func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
return err
}
if err := kubectl.CreateOrUpdateAnnotation(cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag), info, cmdutil.InternalVersionJSONEncoder()); err != nil {
if err := kubectl.CreateOrUpdateAnnotation(o.createAnnotation, info, cmdutil.InternalVersionJSONEncoder()); err != nil {
return err
}
if cmdutil.ShouldRecord(cmd, info) {
if err := cmdutil.RecordChangeCause(info.Object, f.Command(cmd, false)); err != nil {
if o.ShouldRecord(info) {
if err := cmdutil.RecordChangeCause(info.Object, o.changeCause); err != nil {
return cmdutil.AddSourceToErr("replacing", info.Source, err)
}
}
@ -275,8 +334,7 @@ func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
count++
info.Refresh(obj, true)
cmdutil.PrintSuccess(shortOutput, out, info.Object, false, "replaced")
return nil
return o.PrintObj(info.AsVersioned())
})
if err != nil {
return err

View File

@ -65,7 +65,7 @@ func TestReplaceObject(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdReplace(tf, buf)
cmd := NewCmdReplace(tf, buf, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("output", "name")
cmd.Run(cmd, []string{})
@ -136,7 +136,7 @@ func TestReplaceMultipleObject(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdReplace(tf, buf)
cmd := NewCmdReplace(tf, buf, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.yaml")
cmd.Flags().Set("output", "name")
@ -194,7 +194,7 @@ func TestReplaceDirectory(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdReplace(tf, buf)
cmd := NewCmdReplace(tf, buf, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy")
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("output", "name")
@ -241,7 +241,7 @@ func TestForceReplaceObjectNotFound(t *testing.T) {
tf.Namespace = "test"
buf := bytes.NewBuffer([]byte{})
cmd := NewCmdReplace(tf, buf)
cmd := NewCmdReplace(tf, buf, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/legacy/redis-master-controller.yaml")
cmd.Flags().Set("force", "true")
cmd.Flags().Set("cascade", "false")

View File

@ -90,7 +90,38 @@ type RunObject struct {
Mapping *meta.RESTMapping
}
type RunOpts struct {
DeleteOptions *DeleteOptions
DryRun bool
ArgsLenAtDash int
Attach bool
Expose bool
Generator string
Image string
Interactive bool
LeaveStdinOpen bool
Port string
Quiet bool
Record bool
Schedule string
TTY bool
In io.Reader
Out io.Writer
ErrOut io.Writer
}
func NewCmdRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *cobra.Command {
options := &RunOpts{
DeleteOptions: NewDeleteOptions(cmdOut, cmdErr),
In: cmdIn,
Out: cmdOut,
ErrOut: cmdErr,
}
cmd := &cobra.Command{
Use: "run NAME --image=image [--env=\"key=value\"] [--port=port] [--replicas=replicas] [--dry-run=bool] [--overrides=inline-json] [--command] -- [COMMAND] [args...]",
DisableFlagsInUseLine: true,
@ -98,11 +129,11 @@ func NewCmdRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer) *co
Long: runLong,
Example: runExample,
Run: func(cmd *cobra.Command, args []string) {
argsLenAtDash := cmd.ArgsLenAtDash()
err := RunRun(f, cmdIn, cmdOut, cmdErr, cmd, args, argsLenAtDash)
cmdutil.CheckErr(err)
cmdutil.CheckErr(options.Complete(f, cmd))
cmdutil.CheckErr(options.Run(f, cmd, args))
},
}
cmdutil.AddPrinterFlags(cmd)
addRunFlags(cmd)
cmdutil.AddApplyAnnotationFlags(cmd)
@ -141,9 +172,40 @@ func addRunFlags(cmd *cobra.Command) {
cmd.Flags().String("schedule", "", i18n.T("A schedule in the Cron format the job should be run with."))
}
func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *cobra.Command, args []string, argsLenAtDash int) error {
func (o *RunOpts) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
o.ArgsLenAtDash = cmd.ArgsLenAtDash()
o.DryRun = cmdutil.GetFlagBool(cmd, "dry-run")
o.Expose = cmdutil.GetFlagBool(cmd, "expose")
o.Generator = cmdutil.GetFlagString(cmd, "generator")
o.Image = cmdutil.GetFlagString(cmd, "image")
o.Interactive = cmdutil.GetFlagBool(cmd, "stdin")
o.LeaveStdinOpen = cmdutil.GetFlagBool(cmd, "leave-stdin-open")
o.Port = cmdutil.GetFlagString(cmd, "port")
o.Quiet = cmdutil.GetFlagBool(cmd, "quiet")
o.Record = cmdutil.GetRecordFlag(cmd)
o.Schedule = cmdutil.GetFlagString(cmd, "schedule")
o.TTY = cmdutil.GetFlagBool(cmd, "tty")
attachFlag := cmd.Flags().Lookup("attach")
o.Attach = cmdutil.GetFlagBool(cmd, "attach")
if !attachFlag.Changed && o.Interactive {
o.Attach = true
}
// complete delete options
// TODO(juanvallejo): turn delete options into a DeleteFlags struct, similar to PrintFlags
o.DeleteOptions.IgnoreNotFound = true
o.DeleteOptions.Timeout = 0
o.DeleteOptions.GracePeriod = -1
o.DeleteOptions.WaitForDeletion = false
o.DeleteOptions.Reaper = f.Reaper
return nil
}
func (o *RunOpts) Run(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
// Let kubectl run follow rules for `--`, see #13004 issue
if len(args) == 0 || argsLenAtDash == 0 {
if len(args) == 0 || o.ArgsLenAtDash == 0 {
return cmdutil.UsageErrorf(cmd, "NAME is required for run")
}
@ -153,7 +215,7 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
}
// validate image name
imageName := cmdutil.GetFlagString(cmd, "image")
imageName := o.Image
if imageName == "" {
return fmt.Errorf("--image is required")
}
@ -162,16 +224,14 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
return fmt.Errorf("Invalid image name %q: %v", imageName, reference.ErrReferenceInvalidFormat)
}
interactive := cmdutil.GetFlagBool(cmd, "stdin")
tty := cmdutil.GetFlagBool(cmd, "tty")
if tty && !interactive {
if o.TTY && !o.Interactive {
return cmdutil.UsageErrorf(cmd, "-i/--stdin is required for containers with -t/--tty=true")
}
replicas := cmdutil.GetFlagInt(cmd, "replicas")
if interactive && replicas != 1 {
if o.Interactive && replicas != 1 {
return cmdutil.UsageErrorf(cmd, "-i/--stdin requires that replicas is 1, found %d", replicas)
}
if cmdutil.GetFlagBool(cmd, "expose") && len(cmdutil.GetFlagString(cmd, "port")) == 0 {
if o.Expose && len(o.Port) == 0 {
return cmdutil.UsageErrorf(cmd, "--port must be set when exposing a service")
}
@ -179,7 +239,7 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
if err != nil {
return err
}
restartPolicy, err := getRestartPolicy(cmd, interactive)
restartPolicy, err := getRestartPolicy(cmd, o.Interactive)
if err != nil {
return err
}
@ -187,19 +247,12 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
return cmdutil.UsageErrorf(cmd, "--restart=%s requires that --replicas=1, found %d", restartPolicy, replicas)
}
attachFlag := cmd.Flags().Lookup("attach")
attach := cmdutil.GetFlagBool(cmd, "attach")
if !attachFlag.Changed && interactive {
attach = true
}
remove := cmdutil.GetFlagBool(cmd, "rm")
if !attach && remove {
if !o.Attach && remove {
return cmdutil.UsageErrorf(cmd, "--rm should only be used for attached containers")
}
if attach && cmdutil.GetDryRunFlag(cmd) {
if o.Attach && o.DryRun {
return cmdutil.UsageErrorf(cmd, "--dry-run can't be used with attached containers options (--attach, --stdin, or --tty)")
}
@ -212,8 +265,8 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
return err
}
generatorName := cmdutil.GetFlagString(cmd, "generator")
schedule := cmdutil.GetFlagString(cmd, "schedule")
generatorName := o.Generator
schedule := o.Schedule
if len(schedule) != 0 && len(generatorName) == 0 {
generatorName = cmdutil.CronJobV1Beta1GeneratorName
}
@ -228,12 +281,12 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
}
// Falling back because the generator was not provided and the default one could be unavailable.
generatorNameTemp, err := cmdutil.FallbackGeneratorNameIfNecessary(generatorName, clientset.Discovery(), cmdErr)
generatorNameTemp, err := cmdutil.FallbackGeneratorNameIfNecessary(generatorName, clientset.Discovery(), o.ErrOut)
if err != nil {
return err
}
if generatorNameTemp != generatorName {
cmdutil.Warning(cmdErr, generatorName, generatorNameTemp)
cmdutil.Warning(o.ErrOut, generatorName, generatorNameTemp)
} else {
generatorName = generatorNameTemp
}
@ -254,19 +307,19 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
params["env"] = cmdutil.GetFlagStringArray(cmd, "env")
var createdObjects = []*RunObject{}
runObject, err := createGeneratedObject(f, cmd, generator, names, params, cmdutil.GetFlagString(cmd, "overrides"), namespace)
runObject, err := o.createGeneratedObject(f, cmd, generator, names, params, cmdutil.GetFlagString(cmd, "overrides"), namespace)
if err != nil {
return err
} else {
createdObjects = append(createdObjects, runObject)
}
allErrs := []error{}
if cmdutil.GetFlagBool(cmd, "expose") {
if o.Expose {
serviceGenerator := cmdutil.GetFlagString(cmd, "service-generator")
if len(serviceGenerator) == 0 {
return cmdutil.UsageErrorf(cmd, "No service generator specified")
}
serviceRunObject, err := generateService(f, cmd, args, serviceGenerator, params, namespace, cmdOut)
serviceRunObject, err := o.generateService(f, cmd, serviceGenerator, params, namespace)
if err != nil {
allErrs = append(allErrs, err)
} else {
@ -274,20 +327,19 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
}
}
if attach {
if o.Attach {
if remove {
defer removeCreatedObjects(f, createdObjects, cmdOut)
defer o.removeCreatedObjects(f, createdObjects)
}
quiet := cmdutil.GetFlagBool(cmd, "quiet")
opts := &AttachOptions{
StreamOptions: StreamOptions{
In: cmdIn,
Out: cmdOut,
Err: cmdErr,
Stdin: interactive,
TTY: tty,
Quiet: quiet,
In: o.In,
Out: o.Out,
Err: o.ErrOut,
Stdin: o.Interactive,
TTY: o.TTY,
Quiet: o.Quiet,
},
GetPodTimeout: timeout,
CommandName: cmd.Parent().CommandPath() + " attach",
@ -316,7 +368,7 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
}
var pod *api.Pod
leaveStdinOpen := cmdutil.GetFlagBool(cmd, "leave-stdin-open")
leaveStdinOpen := o.LeaveStdinOpen
waitForExitCode := !leaveStdinOpen && restartPolicy == api.RestartPolicyNever
if waitForExitCode {
pod, err = waitForPod(clientset.Core(), attachablePod.Namespace, attachablePod.Name, kubectl.PodCompleted)
@ -355,15 +407,15 @@ func RunRun(f cmdutil.Factory, cmdIn io.Reader, cmdOut, cmdErr io.Writer, cmd *c
if runObject != nil {
outputFormat := cmdutil.GetFlagString(cmd, "output")
if outputFormat != "" || cmdutil.GetDryRunFlag(cmd) {
return cmdutil.PrintObject(cmd, runObject.Object, cmdOut)
return cmdutil.PrintObject(cmd, runObject.Object, o.Out)
}
cmdutil.PrintSuccess(false, cmdOut, runObject.Object, cmdutil.GetDryRunFlag(cmd), "created")
cmdutil.PrintSuccess(false, o.Out, runObject.Object, cmdutil.GetDryRunFlag(cmd), "created")
}
return utilerrors.NewAggregate(allErrs)
}
func removeCreatedObjects(f cmdutil.Factory, createdObjects []*RunObject, cmdOut io.Writer) error {
func (o *RunOpts) removeCreatedObjects(f cmdutil.Factory, createdObjects []*RunObject) error {
for _, obj := range createdObjects {
namespace, err := obj.Mapping.MetadataAccessor.Namespace(obj.Object)
if err != nil {
@ -387,7 +439,7 @@ func removeCreatedObjects(f cmdutil.Factory, createdObjects []*RunObject, cmdOut
// asked for us to remove the pod (via --rm) then telling them
// its been deleted is unnecessary since that's what they asked
// for. We should only print something if the "rm" fails.
err = ReapResult(r, f, cmdOut, true, true, 0, -1, false, false, true)
err = o.DeleteOptions.ReapResult(r, true, true)
if err != nil {
return err
}
@ -502,7 +554,7 @@ func verifyImagePullPolicy(cmd *cobra.Command) error {
return cmdutil.UsageErrorf(cmd, "invalid image pull policy: %s", pullPolicy)
}
func generateService(f cmdutil.Factory, cmd *cobra.Command, args []string, serviceGenerator string, paramsIn map[string]interface{}, namespace string, out io.Writer) (*RunObject, error) {
func (o *RunOpts) generateService(f cmdutil.Factory, cmd *cobra.Command, serviceGenerator string, paramsIn map[string]interface{}, namespace string) (*RunObject, error) {
generators := f.Generators("expose")
generator, found := generators[serviceGenerator]
if !found {
@ -532,27 +584,27 @@ func generateService(f cmdutil.Factory, cmd *cobra.Command, args []string, servi
params["default-name"] = name
}
runObject, err := createGeneratedObject(f, cmd, generator, names, params, cmdutil.GetFlagString(cmd, "service-overrides"), namespace)
runObject, err := o.createGeneratedObject(f, cmd, generator, names, params, cmdutil.GetFlagString(cmd, "service-overrides"), namespace)
if err != nil {
return nil, err
}
if cmdutil.GetFlagString(cmd, "output") != "" || cmdutil.GetDryRunFlag(cmd) {
err := cmdutil.PrintObject(cmd, runObject.Object, out)
err := cmdutil.PrintObject(cmd, runObject.Object, o.Out)
if err != nil {
return nil, err
}
if cmdutil.GetFlagString(cmd, "output") == "yaml" {
fmt.Fprintln(out, "---")
fmt.Fprintln(o.Out, "---")
}
return runObject, nil
}
cmdutil.PrintSuccess(false, out, runObject.Object, cmdutil.GetDryRunFlag(cmd), "created")
cmdutil.PrintSuccess(false, o.Out, runObject.Object, cmdutil.GetDryRunFlag(cmd), "created")
return runObject, nil
}
func createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command, generator kubectl.Generator, names []kubectl.GeneratorParam, params map[string]interface{}, overrides, namespace string) (*RunObject, error) {
func (o *RunOpts) createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command, generator kubectl.Generator, names []kubectl.GeneratorParam, params map[string]interface{}, overrides, namespace string) (*RunObject, error) {
err := kubectl.ValidateParams(names, params)
if err != nil {
return nil, err
@ -592,12 +644,13 @@ func createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command, generator kube
if err != nil {
return nil, err
}
if cmdutil.GetRecordFlag(cmd) || len(annotations[kubectl.ChangeCauseAnnotation]) > 0 {
if o.Record || len(annotations[kubectl.ChangeCauseAnnotation]) > 0 {
if err := cmdutil.RecordChangeCause(obj, f.Command(cmd, false)); err != nil {
return nil, err
}
}
if !cmdutil.GetDryRunFlag(cmd) {
if !o.DryRun {
resourceMapper := &resource.Mapper{
ObjectTyper: typer,
RESTMapper: mapper,

View File

@ -188,12 +188,28 @@ func TestRunArgsFollowDashRules(t *testing.T) {
}, nil
}),
}
tf.Namespace = "test"
tf.ClientConfigVal = &restclient.Config{}
cmd := NewCmdRun(tf, os.Stdin, os.Stdout, os.Stderr)
cmd.Flags().Set("image", "nginx")
cmd.Flags().Set("generator", "run/v1")
err := RunRun(tf, os.Stdin, os.Stdout, os.Stderr, cmd, test.args, test.argsLenAtDash)
opts := &RunOpts{
DeleteOptions: NewDeleteOptions(os.Stdout, os.Stderr),
In: os.Stdin,
Out: os.Stdout,
ErrOut: os.Stderr,
Image: "nginx",
Generator: "run/v1",
ArgsLenAtDash: test.argsLenAtDash,
}
err := opts.Run(tf, cmd, test.args)
if test.expectError && err == nil {
t.Errorf("unexpected non-error (%s)", test.name)
}
@ -335,6 +351,18 @@ func TestGenerateService(t *testing.T) {
}
}),
}
buff := &bytes.Buffer{}
opts := &RunOpts{
DeleteOptions: NewDeleteOptions(os.Stdout, os.Stderr),
Out: buff,
ErrOut: buff,
Port: test.port,
Record: false,
}
cmd := &cobra.Command{}
cmd.Flags().Bool(cmdutil.ApplyAnnotationsFlag, false, "")
cmd.Flags().Bool("record", false, "Record current kubectl command in the resource annotation. If set to false, do not record the command. If set to true, record the command. If not set, default to updating the existing annotation value only if one already exists.")
@ -343,7 +371,7 @@ func TestGenerateService(t *testing.T) {
addRunFlags(cmd)
if !test.expectPOST {
cmd.Flags().Set("dry-run", "true")
opts.DryRun = true
}
if len(test.port) > 0 {
@ -351,8 +379,7 @@ func TestGenerateService(t *testing.T) {
test.params["port"] = test.port
}
buff := &bytes.Buffer{}
_, err := generateService(tf, cmd, test.args, test.serviceGenerator, test.params, "namespace", buff)
_, err := opts.generateService(tf, cmd, test.serviceGenerator, test.params, "namespace")
if test.expectErr {
if err == nil {
t.Error("unexpected non-error")
@ -473,7 +500,12 @@ func TestRunValidations(t *testing.T) {
for flagName, flagValue := range test.flags {
cmd.Flags().Set(flagName, flagValue)
}
err := RunRun(tf, inBuf, outBuf, errBuf, cmd, test.args, cmd.ArgsLenAtDash())
cmd.Run(cmd, test.args)
var err error
if errBuf.Len() > 0 {
err = fmt.Errorf("%v", errBuf.String())
}
if err != nil && len(test.expectedErr) > 0 {
if !strings.Contains(err.Error(), test.expectedErr) {
t.Errorf("unexpected error: %v", err)