Make delete actually stop resources by default.

Refactor for shared code.
This commit is contained in:
Brendan Burns
2015-04-22 21:15:15 -07:00
parent 01f201945d
commit e1256c0802
34 changed files with 122 additions and 57 deletions

View File

@@ -72,6 +72,7 @@ func NewCmdDelete(f *cmdutil.Factory, out io.Writer) *cobra.Command {
kubectl.AddJsonFilenameFlag(cmd, &filenames, usage)
cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on")
cmd.Flags().Bool("all", false, "[-all] to select all the specified resources")
cmd.Flags().Bool("cascade", true, "If true, cascade the delete resources managed by this resource (e.g. Pods created by a ReplicationController). Default true.")
return cmd
}
@@ -95,20 +96,59 @@ func RunDelete(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []str
return err
}
// By default use a reaper to delete all related resources.
if cmdutil.GetFlagBool(cmd, "cascade") {
return ReapResult(r, f, out, cmdutil.GetFlagBool(cmd, "cascade"))
}
return DeleteResult(r, out)
}
func ReapResult(r *resource.Result, f *cmdutil.Factory, out io.Writer, isDefaultDelete bool) error {
found := 0
err = r.IgnoreErrors(errors.IsNotFound).Visit(func(r *resource.Info) error {
err := r.IgnoreErrors(errors.IsNotFound).Visit(func(info *resource.Info) error {
found++
if err := resource.NewHelper(r.Client, r.Mapping).Delete(r.Namespace, r.Name); err != nil {
reaper, err := f.Reaper(info.Mapping)
if err != nil {
// If the error is "not found" and the user didn't explicitly ask for stop.
if kubectl.IsNoSuchReaperError(err) && isDefaultDelete {
return deleteResource(info, out)
}
return err
}
fmt.Fprintf(out, "%s/%s\n", r.Mapping.Resource, r.Name)
if _, err := reaper.Stop(info.Namespace, info.Name); err != nil {
return err
}
fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name)
return nil
})
if err != nil {
return err
}
if found == 0 {
fmt.Fprintf(cmd.Out(), "No resources found\n")
fmt.Fprintf(out, "No resources found\n")
}
return nil
}
func DeleteResult(r *resource.Result, out io.Writer) error {
found := 0
err := r.IgnoreErrors(errors.IsNotFound).Visit(func(info *resource.Info) error {
found++
return deleteResource(info, out)
})
if err != nil {
return err
}
if found == 0 {
fmt.Fprintf(out, "No resources found\n")
}
return nil
}
func deleteResource(info *resource.Info, out io.Writer) error {
if err := resource.NewHelper(info.Client, info.Mapping).Delete(info.Namespace, info.Name); err != nil {
return err
}
fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name)
return nil
}

View File

@@ -50,6 +50,7 @@ func TestDeleteObjectByTuple(t *testing.T) {
cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Run(cmd, []string{"replicationcontrollers/redis-master-controller"})
if buf.String() != "replicationControllers/redis-master-controller\n" {
@@ -80,6 +81,7 @@ func TestDeleteNamedObject(t *testing.T) {
cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("namespace", "test")
cmd.Flags().Set("cascade", "false")
cmd.Run(cmd, []string{"replicationcontrollers", "redis-master-controller"})
if buf.String() != "replicationControllers/redis-master-controller\n" {
@@ -109,6 +111,7 @@ func TestDeleteObject(t *testing.T) {
cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("cascade", "false")
cmd.Run(cmd, []string{})
// uses the name from the file, not the response
@@ -137,6 +140,7 @@ func TestDeleteObjectIgnoreNotFound(t *testing.T) {
cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("cascade", "false")
cmd.Run(cmd, []string{})
if buf.String() != "" {
@@ -169,6 +173,7 @@ func TestDeleteMultipleObject(t *testing.T) {
cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Flags().Set("cascade", "false")
cmd.Run(cmd, []string{})
if buf.String() != "replicationcontrollers/redis-master\nservices/frontend\n" {
@@ -201,6 +206,7 @@ func TestDeleteMultipleObjectIgnoreMissing(t *testing.T) {
cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook/redis-master-controller.json")
cmd.Flags().Set("filename", "../../../examples/guestbook/frontend-service.json")
cmd.Flags().Set("cascade", "false")
cmd.Run(cmd, []string{})
if buf.String() != "services/frontend\n" {
@@ -232,6 +238,7 @@ func TestDeleteDirectory(t *testing.T) {
cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("filename", "../../../examples/guestbook")
cmd.Flags().Set("cascade", "false")
cmd.Run(cmd, []string{})
if buf.String() != "replicationcontrollers/frontend\nservices/frontend\nreplicationcontrollers/redis-master\nservices/redis-master\nreplicationcontrollers/redis-slave\nservices/redis-slave\n" {
@@ -273,6 +280,7 @@ func TestDeleteMultipleSelector(t *testing.T) {
cmd := NewCmdDelete(f, buf)
cmd.Flags().Set("selector", "a=b")
cmd.Flags().Set("cascade", "false")
cmd.Run(cmd, []string{"pods,services"})
if buf.String() != "pods/foo\npods/bar\nservices/baz\n" {

View File

@@ -17,7 +17,6 @@ limitations under the License.
package cmd
import (
"fmt"
"io"
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
@@ -55,29 +54,7 @@ func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command {
Long: stop_long,
Example: stop_example,
Run: func(cmd *cobra.Command, args []string) {
cmdNamespace, err := f.DefaultNamespace()
cmdutil.CheckErr(err)
mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
ContinueOnError().
NamespaceParam(cmdNamespace).RequireNamespace().
ResourceTypeOrNameArgs(false, args...).
FilenameParam(flags.Filenames...).
SelectorParam(cmdutil.GetFlagString(cmd, "selector")).
SelectAllParam(cmdutil.GetFlagBool(cmd, "all")).
Flatten().
Do()
cmdutil.CheckErr(r.Err())
r.Visit(func(info *resource.Info) error {
reaper, err := f.Reaper(info.Mapping)
cmdutil.CheckErr(err)
if _, err := reaper.Stop(info.Namespace, info.Name); err != nil {
return err
}
fmt.Fprintf(out, "%s/%s\n", info.Mapping.Resource, info.Name)
return nil
})
cmdutil.CheckErr(RunStop(f, cmd, args, flags.Filenames, out))
},
}
usage := "Filename, directory, or URL to file of resource(s) to be stopped"
@@ -86,3 +63,24 @@ func NewCmdStop(f *cmdutil.Factory, out io.Writer) *cobra.Command {
cmd.Flags().Bool("all", false, "[-all] to select all the specified resources")
return cmd
}
func RunStop(f *cmdutil.Factory, cmd *cobra.Command, args []string, filenames util.StringList, out io.Writer) error {
cmdNamespace, err := f.DefaultNamespace()
if err != nil {
return err
}
mapper, typer := f.Object()
r := resource.NewBuilder(mapper, typer, f.ClientMapperForCommand()).
ContinueOnError().
NamespaceParam(cmdNamespace).RequireNamespace().
ResourceTypeOrNameArgs(false, args...).
FilenameParam(filenames...).
SelectorParam(cmdutil.GetFlagString(cmd, "selector")).
SelectAllParam(cmdutil.GetFlagBool(cmd, "all")).
Flatten().
Do()
if r.Err() != nil {
return r.Err()
}
return ReapResult(r, f, out, false)
}

View File

@@ -35,6 +35,19 @@ type Reaper interface {
Stop(namespace, name string) (string, error)
}
type NoSuchReaperError struct {
kind string
}
func (n *NoSuchReaperError) Error() string {
return fmt.Sprintf("no reaper has been implemented for %q", n.kind)
}
func IsNoSuchReaperError(err error) bool {
_, ok := err.(*NoSuchReaperError)
return ok
}
func ReaperFor(kind string, c client.Interface) (Reaper, error) {
switch kind {
case "ReplicationController":
@@ -44,7 +57,7 @@ func ReaperFor(kind string, c client.Interface) (Reaper, error) {
case "Service":
return &ServiceReaper{c}, nil
}
return nil, fmt.Errorf("no reaper has been implemented for %q", kind)
return nil, &NoSuchReaperError{kind}
}
type ReplicationControllerReaper struct {