Просмотр исходного кода

Add `docker prune` command and ACI implementation

Signed-off-by: Guillaume Tardif <[email protected]>
Guillaume Tardif 5 лет назад
Родитель
Сommit
a5e34323e2
9 измененных файлов с 169 добавлено и 14 удалено
  1. 3 0
      aci/backend.go
  2. 9 4
      aci/convert/convert.go
  3. 23 3
      aci/resources.go
  4. 10 0
      api/client/client.go
  5. 32 0
      api/client/resources.go
  6. 2 1
      api/resources/api.go
  7. 69 0
      cli/cmd/prune.go
  8. 1 0
      cli/main.go
  9. 20 6
      tests/aci-e2e/e2e-aci_test.go

+ 3 - 0
aci/backend.go

@@ -97,6 +97,9 @@ func getAciAPIService(aciCtx store.AciContext) *aciAPIService {
 		aciVolumeService: &aciVolumeService{
 			aciContext: aciCtx,
 		},
+		aciResourceService: &aciResourceService{
+			aciContext: aciCtx,
+		},
 	}
 }
 

+ 9 - 4
aci/convert/convert.go

@@ -542,12 +542,17 @@ func ContainerGroupToContainer(containerID string, cg containerinstance.Containe
 
 // GetStatus returns status for the specified container
 func GetStatus(container containerinstance.Container, group containerinstance.ContainerGroup) string {
-	status := compose.UNKNOWN
-	if group.InstanceView != nil && group.InstanceView.State != nil {
-		status = "Node " + *group.InstanceView.State
-	}
+	status := GetGroupStatus(group)
 	if container.InstanceView != nil && container.InstanceView.CurrentState != nil {
 		status = *container.InstanceView.CurrentState.State
 	}
 	return status
 }
+
+// GetGroupStatus returns status for the container group
+func GetGroupStatus(group containerinstance.ContainerGroup) string {
+	if group.InstanceView != nil && group.InstanceView.State != nil {
+		return "Node " + *group.InstanceView.State
+	}
+	return compose.UNKNOWN
+}

+ 23 - 3
aci/resources.go

@@ -18,8 +18,10 @@ package aci
 
 import (
 	"context"
-	"fmt"
 
+	"github.com/hashicorp/go-multierror"
+
+	"github.com/docker/compose-cli/aci/convert"
 	"github.com/docker/compose-cli/api/resources"
 	"github.com/docker/compose-cli/context/store"
 )
@@ -29,6 +31,24 @@ type aciResourceService struct {
 }
 
 func (cs *aciResourceService) Prune(ctx context.Context, request resources.PruneRequest) ([]string, error) {
-	fmt.Println("PRUNE " + cs.aciContext.ResourceGroup)
-	return nil, nil
+	res, err := getACIContainerGroups(ctx, cs.aciContext.SubscriptionID, cs.aciContext.ResourceGroup)
+	if err != nil {
+		return nil, err
+	}
+	multierr := &multierror.Error{}
+	deleted := []string{}
+	for _, containerGroup := range res {
+		if !request.Force && convert.GetGroupStatus(containerGroup) == "Node "+convert.StatusRunning {
+			continue
+		}
+
+		if !request.DryRun {
+			_, err := deleteACIContainerGroup(ctx, cs.aciContext, *containerGroup.Name)
+			multierr = multierror.Append(multierr, err)
+		}
+		if err == nil {
+			deleted = append(deleted, *containerGroup.Name)
+		}
+	}
+	return deleted, multierr.ErrorOrNil()
 }

+ 10 - 0
api/client/client.go

@@ -21,6 +21,7 @@ import (
 
 	"github.com/docker/compose-cli/api/compose"
 	"github.com/docker/compose-cli/api/containers"
+	"github.com/docker/compose-cli/api/resources"
 	"github.com/docker/compose-cli/api/secrets"
 	"github.com/docker/compose-cli/api/volumes"
 	"github.com/docker/compose-cli/backend"
@@ -107,3 +108,12 @@ func (c *Client) VolumeService() volumes.Service {
 
 	return &volumeService{}
 }
+
+// ResourceService returns the backend service for the current context
+func (c *Client) ResourceService() resources.Service {
+	if vs := c.bs.ResourceService(); vs != nil {
+		return vs
+	}
+
+	return &resourceService{}
+}

+ 32 - 0
api/client/resources.go

@@ -0,0 +1,32 @@
+/*
+   Copyright 2020 Docker Compose CLI 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 client
+
+import (
+	"context"
+
+	"github.com/docker/compose-cli/api/resources"
+	"github.com/docker/compose-cli/errdefs"
+)
+
+type resourceService struct {
+}
+
+// Prune prune resources
+func (c *resourceService) Prune(ctx context.Context, request resources.PruneRequest) ([]string, error) {
+	return nil, errdefs.ErrNotImplemented
+}

+ 2 - 1
api/resources/api.go

@@ -22,7 +22,8 @@ import (
 
 // PruneRequest options on what to prune
 type PruneRequest struct {
-	Force bool
+	Force  bool
+	DryRun bool
 }
 
 // Service interacts with the underlying container backend

+ 69 - 0
cli/cmd/prune.go

@@ -0,0 +1,69 @@
+/*
+   Copyright 2020 Docker Compose CLI 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 (
+	"context"
+	"fmt"
+
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+
+	"github.com/docker/compose-cli/api/client"
+	"github.com/docker/compose-cli/api/resources"
+)
+
+type pruneOpts struct {
+	force  bool
+	dryRun bool
+}
+
+// PruneCommand deletes backend resources
+func PruneCommand() *cobra.Command {
+	var opts pruneOpts
+	cmd := &cobra.Command{
+		Use:   "prune",
+		Short: "prune existing resources in current context",
+		Args:  cobra.MaximumNArgs(0),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runPrune(cmd.Context(), opts)
+		},
+	}
+
+	cmd.Flags().BoolVar(&opts.force, "force", false, "Also prune running containers and Compose applications")
+	cmd.Flags().BoolVar(&opts.dryRun, "dry-run", false, "List resources to be deleted, but do not delete them")
+
+	return cmd
+}
+
+func runPrune(ctx context.Context, opts pruneOpts) error {
+	c, err := client.New(ctx)
+	if err != nil {
+		return errors.Wrap(err, "cannot connect to backend")
+	}
+
+	ids, err := c.ResourceService().Prune(ctx, resources.PruneRequest{Force: opts.force, DryRun: opts.dryRun})
+	if opts.dryRun {
+		fmt.Println("resources that would be deleted:")
+	} else {
+		fmt.Println("deleted resources:")
+	}
+	for _, id := range ids {
+		fmt.Println(id)
+	}
+	return err
+}

+ 1 - 0
cli/main.go

@@ -122,6 +122,7 @@ func main() {
 		cmd.StopCommand(),
 		cmd.KillCommand(),
 		cmd.SecretCommand(),
+		cmd.PruneCommand(),
 
 		// Place holders
 		cmd.EcsCommand(),

+ 20 - 6
tests/aci-e2e/e2e-aci_test.go

@@ -488,13 +488,27 @@ func TestContainerRunAttached(t *testing.T) {
 		waitForStatus(t, c, container, convert.StatusRunning)
 	})
 
-	t.Run("kill & rm stopped container", func(t *testing.T) {
-		res := c.RunDockerCmd("kill", container)
-		res.Assert(t, icmd.Expected{Out: container})
-		waitForStatus(t, c, container, "Terminated", "Node Stopped")
+	t.Run("prune dry run", func(t *testing.T) {
+		res := c.RunDockerCmd("prune", "--dry-run")
+		fmt.Println("prune output:")
+		assert.Equal(t, "resources that would be deleted:\n", res.Stdout())
+		res = c.RunDockerCmd("prune", "--dry-run", "--force")
+		assert.Equal(t, "resources that would be deleted:\n"+container+"\n", res.Stdout())
+	})
+
+	t.Run("prune", func(t *testing.T) {
+		res := c.RunDockerCmd("prune")
+		assert.Equal(t, "deleted resources:\n", res.Stdout())
+		res = c.RunDockerCmd("ps")
+		l := lines(res.Stdout())
+		assert.Equal(t, 2, len(l))
 
-		res = c.RunDockerCmd("rm", container)
-		res.Assert(t, icmd.Expected{Out: container})
+		res = c.RunDockerCmd("prune", "--force")
+		assert.Equal(t, "deleted resources:\n"+container+"\n", res.Stdout())
+
+		res = c.RunDockerCmd("ps", "--all")
+		l = lines(res.Stdout())
+		assert.Equal(t, 1, len(l))
 	})
 }