From d3208be7e7f12c4cc2fe383327e3c7e5f7b7c895 Mon Sep 17 00:00:00 2001 From: Mohsen Mirzakhani Date: Fri, 30 Sep 2022 22:52:54 +0200 Subject: [PATCH] feat: down command to remove containers --- internal/cmd/down.go | 52 +++++++++++++++++++++++++++ internal/cmd/up.go | 11 +----- internal/cmd/{context.go => utils.go} | 9 +++++ internal/container/container.go | 31 ++++++++++++++-- internal/pg/pg.go | 2 +- internal/redis/redis.go | 2 +- main.go | 1 + 7 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 internal/cmd/down.go rename internal/cmd/{context.go => utils.go} (79%) diff --git a/internal/cmd/down.go b/internal/cmd/down.go new file mode 100644 index 0000000..c12fec5 --- /dev/null +++ b/internal/cmd/down.go @@ -0,0 +1,52 @@ +package cmd + +import ( + "errors" + "fmt" + "strings" + + "github.com/mirzakhany/dbctl/internal/container" + "github.com/spf13/cobra" +) + +// GetDownCmd represents the down command +func GetDownCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "down", + Short: "stop one or more detached databases", + RunE: runDown, + } + return cmd +} + +func runDown(cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return errors.New("invalid args, can be postgres(pg) and/or redis(rs)") + } + + ctx := contextWithOsSignal() + r, err := container.List(ctx) + if err != nil { + return err + } + + toRemove := make(map[string]struct{}) + if contain(args, "postgres", "pg") { + toRemove["pg"] = struct{}{} + } + + if contain(args, "redis", "rs") { + toRemove["rs"] = struct{}{} + } + + for _, c := range r { + t := strings.Split(c.Name, "_")[1] + if _, ok := toRemove[t]; ok { + if err := container.Remove(ctx, c); err != nil { + return fmt.Errorf("stop database %s failed", c.Name) + } + } + } + + return nil +} diff --git a/internal/cmd/up.go b/internal/cmd/up.go index f14058a..5d09198 100644 --- a/internal/cmd/up.go +++ b/internal/cmd/up.go @@ -6,7 +6,7 @@ import ( "github.com/spf13/cobra" ) -// GetUpCmd represents the start command +// GetUpCmd represents the up command func GetUpCmd() *cobra.Command { cmd := &cobra.Command{ Use: "up", @@ -33,12 +33,3 @@ func runUp(cmd *cobra.Command, args []string) error { return errors.New("invalid args, can be postgres(pg) and/or redis(rs)") } - -func contain(src []string, target, alias string) bool { - for _, s := range src { - if s == target || s == alias { - return true - } - } - return false -} diff --git a/internal/cmd/context.go b/internal/cmd/utils.go similarity index 79% rename from internal/cmd/context.go rename to internal/cmd/utils.go index 1880633..f4f20d2 100644 --- a/internal/cmd/context.go +++ b/internal/cmd/utils.go @@ -25,3 +25,12 @@ func contextWithOsSignal(sig ...os.Signal) context.Context { }() return ctx } + +func contain(src []string, target, alias string) bool { + for _, s := range src { + if s == target || s == alias { + return true + } + } + return false +} diff --git a/internal/container/container.go b/internal/container/container.go index b84b578..3f09636 100644 --- a/internal/container/container.go +++ b/internal/container/container.go @@ -3,6 +3,7 @@ package container import ( "context" "io" + "strings" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -19,7 +20,8 @@ type Request struct { } type Container struct { - ID string + ID string + Name string } func Run(ctx context.Context, req Request) (*Container, error) { @@ -58,7 +60,7 @@ func Run(ctx context.Context, req Request) (*Container, error) { return nil, err } - cn := &Container{ID: resp.ID} + cn := &Container{ID: resp.ID, Name: req.Name} if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil { return cn, err } @@ -79,3 +81,28 @@ func (c *Container) Terminate(ctx context.Context) error { }) return err } + +func List(ctx context.Context) ([]*Container, error) { + cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if err != nil { + return nil, err + } + + res, err := cli.ContainerList(ctx, types.ContainerListOptions{}) + if err != nil { + return nil, err + } + + out := make([]*Container, 0) + for _, c := range res { + n := c.Names[0] + if strings.HasPrefix(n, "/dbctl") { + out = append(out, &Container{ID: c.ID, Name: c.Names[0]}) + } + } + return out, nil +} + +func Remove(ctx context.Context, container *Container) error { + return container.Terminate(ctx) +} diff --git a/internal/pg/pg.go b/internal/pg/pg.go index 9970066..591b626 100644 --- a/internal/pg/pg.go +++ b/internal/pg/pg.go @@ -128,7 +128,7 @@ func (p *Postgres) startUsingDocker(ctx context.Context) (func(ctx context.Conte }, Cmd: []string{"postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"}, ExposedPorts: []string{fmt.Sprintf("%s:5432/tcp", port)}, - Name: fmt.Sprintf("dbctl_%d_%d", time.Now().Unix(), rnd.Uint64()), + Name: fmt.Sprintf("dbctl_pg_%d_%d", time.Now().Unix(), rnd.Uint64()), }) if err != nil { return nil, err diff --git a/internal/redis/redis.go b/internal/redis/redis.go index f9b0843..2bec4b2 100644 --- a/internal/redis/redis.go +++ b/internal/redis/redis.go @@ -79,7 +79,7 @@ func (p *Redis) startUsingDocker(ctx context.Context) (func(ctx context.Context) "--databases", "2000", }, ExposedPorts: []string{fmt.Sprintf("%s:6379/tcp", port)}, - Name: fmt.Sprintf("dbctl_%d_%d", time.Now().Unix(), rnd.Uint64()), + Name: fmt.Sprintf("dbctl_rs_%d_%d", time.Now().Unix(), rnd.Uint64()), }) if err != nil { return nil, err diff --git a/main.go b/main.go index ea92d0c..12cb6cf 100644 --- a/main.go +++ b/main.go @@ -19,6 +19,7 @@ func main() { root.AddCommand(cmd.GetStartCmd()) root.AddCommand(cmd.GetUpCmd()) + root.AddCommand(cmd.GetDownCmd()) root.AddCommand(cmd.GetSelfUpdateCmd(version)) if err := root.Execute(); err != nil {