mirror of https://github.com/k3s-io/k3s
Merge pull request #62300 from juanvallejo/jvallejo/wire-print-flags-delete-cmd
Automatic merge from submit-queue (batch tested with PRs 62748, 60536, 62300, 62661, 62731). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Wire PrintFlags through delete, replace, run commands **Release note**: ```release-note NONE ``` Wires PrintFlags through the `delete`, `replace`, and `run` commands. All three commands grouped in this patch as they depend on DeleteOptions. ~~Tagged as WIP for now, as I still need to update tests.~~ cc @soltysh @deads2kpull/8/head
commit
a5958c4e39
|
@ -332,6 +332,7 @@ package_group(
|
|||
name = "pkg_kubectl_validation_CONSUMERS",
|
||||
packages = [
|
||||
"//pkg/kubectl",
|
||||
"//pkg/kubectl/cmd",
|
||||
"//pkg/kubectl/cmd/testing",
|
||||
"//pkg/kubectl/cmd/util",
|
||||
"//pkg/kubectl/resource",
|
||||
|
|
|
@ -25,6 +25,7 @@ go_library(
|
|||
"convert.go",
|
||||
"cp.go",
|
||||
"delete.go",
|
||||
"delete_flags.go",
|
||||
"describe.go",
|
||||
"diff.go",
|
||||
"drain.go",
|
||||
|
@ -86,6 +87,7 @@ go_library(
|
|||
"//pkg/kubectl/util:go_default_library",
|
||||
"//pkg/kubectl/util/i18n:go_default_library",
|
||||
"//pkg/kubectl/util/term:go_default_library",
|
||||
"//pkg/kubectl/validation:go_default_library",
|
||||
"//pkg/printers:go_default_library",
|
||||
"//pkg/printers/internalversion:go_default_library",
|
||||
"//pkg/util/interrupt:go_default_library",
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,12 @@ type DeleteOptions struct {
|
|||
Mapper meta.RESTMapper
|
||||
Result *resource.Result
|
||||
|
||||
f cmdutil.Factory
|
||||
Out io.Writer
|
||||
ErrOut io.Writer
|
||||
}
|
||||
|
||||
func NewDeleteOptions() *DeleteOptions {
|
||||
return &DeleteOptions{
|
||||
Cascade: true,
|
||||
GracePeriod: -1,
|
||||
Include3rdParty: true,
|
||||
}
|
||||
}
|
||||
|
||||
func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
|
||||
options := NewDeleteOptions()
|
||||
deleteFlags := NewDeleteCommandFlags("containing the resource to delete.")
|
||||
validArgs := cmdutil.ValidArgList(f)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
|
@ -131,7 +124,9 @@ func NewCmdDelete(f cmdutil.Factory, out, errOut io.Writer) *cobra.Command {
|
|||
Long: delete_long,
|
||||
Example: delete_example,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
options := deleteFlags.ToOptions(out, errOut)
|
||||
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
|
||||
|
||||
if err := options.Complete(f, out, errOut, args, cmd); err != nil {
|
||||
cmdutil.CheckErr(err)
|
||||
}
|
||||
|
@ -146,18 +141,12 @@ 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.")
|
||||
cmd.Flags().BoolVar(&options.DeleteAll, "all", options.DeleteAll, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.")
|
||||
cmd.Flags().BoolVar(&options.IgnoreNotFound, "ignore-not-found", options.IgnoreNotFound, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.")
|
||||
cmd.Flags().BoolVar(&options.Cascade, "cascade", options.Cascade, "If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.")
|
||||
cmd.Flags().IntVar(&options.GracePeriod, "grace-period", options.GracePeriod, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. Can only be set to 0 when --force is true (force deletion).")
|
||||
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")
|
||||
cmdutil.AddOutputVarFlagsForMutation(cmd, &options.Output)
|
||||
cmdutil.AddInclude3rdPartyVarFlags(cmd, &options.Include3rdParty)
|
||||
|
||||
deleteFlags.AddFlags(cmd)
|
||||
|
||||
// flag-specific output flag, as this command does not depend on PrintFlags
|
||||
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, not including uninitialized ones.")
|
||||
|
||||
cmdutil.AddIncludeUninitializedFlag(cmd)
|
||||
return cmd
|
||||
}
|
||||
|
@ -168,6 +157,9 @@ func (o *DeleteOptions) Complete(f cmdutil.Factory, out, errOut io.Writer, args
|
|||
return err
|
||||
}
|
||||
|
||||
o.Selector = cmdutil.GetFlagString(cmd, "selector")
|
||||
o.Reaper = f.Reaper
|
||||
|
||||
includeUninitialized := cmdutil.ShouldIncludeUninitialized(cmd, false)
|
||||
r := f.NewBuilder().
|
||||
Unstructured().
|
||||
|
@ -187,7 +179,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 +224,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 +245,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 +275,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 +294,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 +333,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.
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"k8s.io/kubernetes/pkg/kubectl"
|
||||
"k8s.io/kubernetes/pkg/kubectl/resource"
|
||||
)
|
||||
|
||||
type FileNameFlags struct {
|
||||
Usage string
|
||||
|
||||
Filenames *[]string
|
||||
Recursive *bool
|
||||
}
|
||||
|
||||
func (o *FileNameFlags) ToOptions() resource.FilenameOptions {
|
||||
options := resource.FilenameOptions{}
|
||||
|
||||
if o.Recursive != nil {
|
||||
options.Recursive = *o.Recursive
|
||||
}
|
||||
if o.Filenames != nil {
|
||||
options.Filenames = *o.Filenames
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
func (o *FileNameFlags) AddFlags(cmd *cobra.Command) {
|
||||
if o.Recursive != nil {
|
||||
cmd.Flags().BoolVarP(o.Recursive, "recursive", "R", *o.Recursive, "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.")
|
||||
}
|
||||
if o.Filenames != nil {
|
||||
kubectl.AddJsonFilenameFlag(cmd, o.Filenames, "Filename, directory, or URL to files "+o.Usage)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintFlags composes common printer flag structs
|
||||
// used for commands requiring deletion logic.
|
||||
type DeleteFlags struct {
|
||||
FileNameFlags *FileNameFlags
|
||||
|
||||
All *bool
|
||||
Cascade *bool
|
||||
Force *bool
|
||||
GracePeriod *int
|
||||
IgnoreNotFound *bool
|
||||
Now *bool
|
||||
Timeout *time.Duration
|
||||
Output *string
|
||||
|
||||
IncludeThirdParty *bool
|
||||
}
|
||||
|
||||
func (f *DeleteFlags) ToOptions(out, errOut io.Writer) *DeleteOptions {
|
||||
options := &DeleteOptions{
|
||||
Out: out,
|
||||
ErrOut: errOut,
|
||||
}
|
||||
|
||||
// add filename options
|
||||
if f.FileNameFlags != nil {
|
||||
options.FilenameOptions = f.FileNameFlags.ToOptions()
|
||||
}
|
||||
|
||||
// add output format
|
||||
if f.Output != nil {
|
||||
options.Output = *f.Output
|
||||
}
|
||||
|
||||
if f.All != nil {
|
||||
options.DeleteAll = *f.All
|
||||
}
|
||||
if f.Cascade != nil {
|
||||
options.Cascade = *f.Cascade
|
||||
}
|
||||
if f.Force != nil {
|
||||
options.ForceDeletion = *f.Force
|
||||
}
|
||||
if f.GracePeriod != nil {
|
||||
options.GracePeriod = *f.GracePeriod
|
||||
}
|
||||
if f.IgnoreNotFound != nil {
|
||||
options.IgnoreNotFound = *f.IgnoreNotFound
|
||||
}
|
||||
if f.Now != nil {
|
||||
options.DeleteNow = *f.Now
|
||||
}
|
||||
if f.Timeout != nil {
|
||||
options.Timeout = *f.Timeout
|
||||
}
|
||||
|
||||
if f.IncludeThirdParty != nil {
|
||||
options.Include3rdParty = *f.IncludeThirdParty
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
func (f *DeleteFlags) AddFlags(cmd *cobra.Command) {
|
||||
f.FileNameFlags.AddFlags(cmd)
|
||||
|
||||
if f.All != nil {
|
||||
cmd.Flags().BoolVar(f.All, "all", *f.All, "Delete all resources, including uninitialized ones, in the namespace of the specified resource types.")
|
||||
}
|
||||
if f.Force != nil {
|
||||
cmd.Flags().BoolVar(f.Force, "force", *f.Force, "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.")
|
||||
}
|
||||
if f.Cascade != nil {
|
||||
cmd.Flags().BoolVar(f.Cascade, "cascade", *f.Cascade, "If true, cascade the deletion of the resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.")
|
||||
}
|
||||
if f.Now != nil {
|
||||
cmd.Flags().BoolVar(f.Now, "now", *f.Now, "If true, resources are signaled for immediate shutdown (same as --grace-period=1).")
|
||||
}
|
||||
if f.GracePeriod != nil {
|
||||
cmd.Flags().IntVar(f.GracePeriod, "grace-period", *f.GracePeriod, "Period of time in seconds given to the resource to terminate gracefully. Ignored if negative. Set to 1 for immediate shutdown. Can only be set to 0 when --force is true (force deletion).")
|
||||
}
|
||||
if f.Timeout != nil {
|
||||
cmd.Flags().DurationVar(f.Timeout, "timeout", *f.Timeout, "The length of time to wait before giving up on a delete, zero means determine a timeout from the size of the object")
|
||||
}
|
||||
if f.IgnoreNotFound != nil {
|
||||
cmd.Flags().BoolVar(f.IgnoreNotFound, "ignore-not-found", *f.IgnoreNotFound, "Treat \"resource not found\" as a successful delete. Defaults to \"true\" when --all is specified.")
|
||||
}
|
||||
|
||||
if f.Output != nil {
|
||||
cmd.Flags().StringVarP(f.Output, "output", "o", *f.Output, "Output mode. Use \"-o name\" for shorter output (resource/name).")
|
||||
}
|
||||
|
||||
// TODO: this is deprecated. Remove.
|
||||
if f.IncludeThirdParty != nil {
|
||||
cmd.Flags().BoolVar(f.IncludeThirdParty, "include-extended-apis", *f.IncludeThirdParty, "If true, include definitions of new APIs via calls to the API server. [default true]")
|
||||
cmd.Flags().MarkDeprecated("include-extended-apis", "No longer required.")
|
||||
}
|
||||
}
|
||||
|
||||
// NewDeleteCommandFlags provides default flags and values for use with the "delete" command
|
||||
func NewDeleteCommandFlags(usage string) *DeleteFlags {
|
||||
includeThirdParty := true
|
||||
cascade := true
|
||||
gracePeriod := -1
|
||||
|
||||
// setup command defaults
|
||||
all := false
|
||||
force := false
|
||||
ignoreNotFound := false
|
||||
now := false
|
||||
output := ""
|
||||
timeout := time.Duration(0)
|
||||
|
||||
filenames := []string{}
|
||||
recursive := false
|
||||
|
||||
return &DeleteFlags{
|
||||
FileNameFlags: &FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive},
|
||||
|
||||
Cascade: &cascade,
|
||||
GracePeriod: &gracePeriod,
|
||||
|
||||
All: &all,
|
||||
Force: &force,
|
||||
IgnoreNotFound: &ignoreNotFound,
|
||||
Now: &now,
|
||||
Timeout: &timeout,
|
||||
Output: &output,
|
||||
|
||||
IncludeThirdParty: &includeThirdParty,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDeleteFlags provides default flags and values for use in commands outside of "delete"
|
||||
func NewDeleteFlags(usage string) *DeleteFlags {
|
||||
includeThirdParty := true
|
||||
cascade := true
|
||||
gracePeriod := -1
|
||||
|
||||
force := false
|
||||
timeout := time.Duration(0)
|
||||
|
||||
filenames := []string{}
|
||||
recursive := false
|
||||
|
||||
return &DeleteFlags{
|
||||
FileNameFlags: &FileNameFlags{Usage: usage, Filenames: &filenames, Recursive: &recursive},
|
||||
|
||||
Cascade: &cascade,
|
||||
GracePeriod: &gracePeriod,
|
||||
|
||||
IncludeThirdParty: &includeThirdParty,
|
||||
|
||||
// add non-defaults
|
||||
Force: &force,
|
||||
Timeout: &timeout,
|
||||
}
|
||||
}
|
|
@ -44,12 +44,17 @@ import (
|
|||
|
||||
var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer
|
||||
|
||||
var fakecmd = &cobra.Command{
|
||||
Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
|
||||
DisableFlagsInUseLine: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
|
||||
},
|
||||
func fakecmd() *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "delete ([-f FILENAME] | TYPE [(NAME | -l label | --all)])",
|
||||
DisableFlagsInUseLine: true,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
cmdutil.CheckErr(cmdutil.ValidateOutputArgs(cmd))
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on, not including uninitialized ones.")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func TestDeleteObjectByTuple(t *testing.T) {
|
||||
|
@ -366,7 +371,7 @@ func TestDeleteObjectNotFound(t *testing.T) {
|
|||
Cascade: false,
|
||||
Output: "name",
|
||||
}
|
||||
err := options.Complete(tf, buf, errBuf, []string{}, fakecmd)
|
||||
err := options.Complete(tf, buf, errBuf, []string{}, fakecmd())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -448,7 +453,7 @@ func TestDeleteAllNotFound(t *testing.T) {
|
|||
IgnoreNotFound: false,
|
||||
Output: "name",
|
||||
}
|
||||
err := options.Complete(tf, buf, errBuf, []string{"services"}, fakecmd)
|
||||
err := options.Complete(tf, buf, errBuf, []string{"services"}, fakecmd())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -573,7 +578,7 @@ func TestDeleteMultipleObjectContinueOnMissing(t *testing.T) {
|
|||
Cascade: false,
|
||||
Output: "name",
|
||||
}
|
||||
err := options.Complete(tf, buf, errBuf, []string{}, fakecmd)
|
||||
err := options.Complete(tf, buf, errBuf, []string{}, fakecmd())
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
@ -749,7 +754,7 @@ func TestResourceErrors(t *testing.T) {
|
|||
Cascade: false,
|
||||
Output: "name",
|
||||
}
|
||||
err := options.Complete(tf, buf, errBuf, testCase.args, fakecmd)
|
||||
err := options.Complete(tf, buf, errBuf, testCase.args, fakecmd())
|
||||
if !testCase.errFn(err) {
|
||||
t.Errorf("%s: unexpected error: %v", k, err)
|
||||
return
|
||||
|
|
|
@ -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,39 @@ 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
|
||||
DeleteFlags *DeleteFlags
|
||||
|
||||
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"),
|
||||
DeleteFlags: NewDeleteFlags("to use to replace the resource."),
|
||||
|
||||
Out: out,
|
||||
ErrOut: errOut,
|
||||
}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "replace -f FILENAME",
|
||||
|
@ -71,61 +106,100 @@ 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())
|
||||
},
|
||||
}
|
||||
usage := "to use to replace the resource."
|
||||
cmdutil.AddFilenameOptionFlags(cmd, options, usage)
|
||||
|
||||
options.PrintFlags.AddFlags(cmd)
|
||||
options.DeleteFlags.AddFlags(cmd)
|
||||
|
||||
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).")
|
||||
cmdutil.AddValidateFlags(cmd)
|
||||
cmdutil.AddOutputFlagsForMutation(cmd)
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddRecordFlag(cmd)
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
deleteOpts := o.DeleteFlags.ToOptions(o.Out, o.ErrOut)
|
||||
|
||||
//Replace will create a resource if it doesn't exist already, so ignore not found error
|
||||
deleteOpts.IgnoreNotFound = true
|
||||
deleteOpts.Reaper = f.Reaper
|
||||
if o.PrintFlags.OutputFormat != nil {
|
||||
deleteOpts.Output = *o.PrintFlags.OutputFormat
|
||||
}
|
||||
if deleteOpts.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.
|
||||
deleteOpts.GracePeriod = 1
|
||||
deleteOpts.WaitForDeletion = true
|
||||
}
|
||||
o.DeleteOptions = deleteOpts
|
||||
|
||||
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.DeleteOptions.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.DeleteOptions.FilenameOptions).
|
||||
Flatten().
|
||||
Do()
|
||||
if err := r.Err(); err != nil {
|
||||
|
@ -137,12 +211,12 @@ func RunReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
|
|||
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 +228,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.DeleteOptions.FilenameOptions.Filenames {
|
||||
if filename == "-" {
|
||||
tempDir, err := ioutil.TempDir("", "kubectl_replace_")
|
||||
if err != nil {
|
||||
|
@ -182,42 +245,30 @@ func forceReplace(f cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
options.Filenames[i] = tempFilename
|
||||
o.DeleteOptions.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().
|
||||
ResourceTypeOrNameArgs(false, o.BuilderArgs...).RequireObject(false).
|
||||
FilenameParam(o.EnforceNamespace, &o.DeleteOptions.FilenameOptions).
|
||||
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 +290,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.DeleteOptions.FilenameOptions).
|
||||
Flatten().
|
||||
Do()
|
||||
err = r.Err()
|
||||
|
@ -258,12 +309,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 +326,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
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -90,7 +90,40 @@ type RunObject struct {
|
|||
Mapping *meta.RESTMapping
|
||||
}
|
||||
|
||||
type RunOpts struct {
|
||||
DeleteFlags *DeleteFlags
|
||||
|
||||
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{
|
||||
DeleteFlags: NewDeleteFlags("to use to replace the resource."),
|
||||
|
||||
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,16 +131,17 @@ 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))
|
||||
},
|
||||
}
|
||||
|
||||
options.DeleteFlags.AddFlags(cmd)
|
||||
|
||||
cmdutil.AddPrinterFlags(cmd)
|
||||
addRunFlags(cmd)
|
||||
cmdutil.AddApplyAnnotationFlags(cmd)
|
||||
cmdutil.AddRecordFlag(cmd)
|
||||
cmdutil.AddInclude3rdPartyFlags(cmd)
|
||||
cmdutil.AddPodRunningTimeoutFlag(cmd, defaultPodAttachTimeout)
|
||||
return cmd
|
||||
}
|
||||
|
@ -141,9 +175,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
|
||||
}
|
||||
|
||||
deleteOpts := o.DeleteFlags.ToOptions(o.Out, o.ErrOut)
|
||||
deleteOpts.IgnoreNotFound = true
|
||||
deleteOpts.WaitForDeletion = false
|
||||
deleteOpts.GracePeriod = -1
|
||||
deleteOpts.Reaper = f.Reaper
|
||||
|
||||
o.DeleteOptions = deleteOpts
|
||||
|
||||
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 +218,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 +227,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 +242,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 +250,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 +268,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 +284,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 +310,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 +330,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 +371,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 +410,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 +442,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 +557,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 +587,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 +647,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,
|
||||
|
|
|
@ -188,12 +188,29 @@ 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)
|
||||
|
||||
deleteFlags := NewDeleteFlags("to use to replace the resource.")
|
||||
opts := &RunOpts{
|
||||
DeleteOptions: deleteFlags.ToOptions(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 +352,19 @@ func TestGenerateService(t *testing.T) {
|
|||
}
|
||||
}),
|
||||
}
|
||||
|
||||
buff := &bytes.Buffer{}
|
||||
deleteFlags := NewDeleteFlags("to use to replace the resource.")
|
||||
opts := &RunOpts{
|
||||
DeleteOptions: deleteFlags.ToOptions(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 +373,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 +381,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 +502,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)
|
||||
|
|
Loading…
Reference in New Issue