Forráskód Böngészése

ACI Stop implementation

Guillaume Tardif 5 éve
szülő
commit
ee062e8333
5 módosított fájl, 118 hozzáadás és 4 törlés
  1. 15 0
      aci/aci.go
  2. 10 2
      aci/backend.go
  3. 77 0
      cli/cmd/stop.go
  4. 1 0
      cli/main.go
  5. 15 2
      tests/aci-e2e/e2e-aci_test.go

+ 15 - 0
aci/aci.go

@@ -24,6 +24,8 @@ import (
 	"strings"
 	"time"
 
+	"github.com/docker/api/errdefs"
+
 	"github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance"
 	"github.com/Azure/go-autorest/autorest"
 	"github.com/Azure/go-autorest/autorest/to"
@@ -139,6 +141,19 @@ func deleteACIContainerGroup(ctx context.Context, aciContext store.AciContext, c
 	return containerGroupsClient.Delete(ctx, aciContext.ResourceGroup, containerGroupName)
 }
 
+func stopACIContainerGroup(ctx context.Context, aciContext store.AciContext, containerGroupName string) error {
+	containerGroupsClient, err := getContainerGroupsClient(aciContext.SubscriptionID)
+	if err != nil {
+		return fmt.Errorf("cannot get container group client: %v", err)
+	}
+
+	result, err := containerGroupsClient.Stop(ctx, aciContext.ResourceGroup, containerGroupName)
+	if result.StatusCode == http.StatusNotFound {
+		return errdefs.ErrNotFound
+	}
+	return err
+}
+
 func execACIContainer(ctx context.Context, aciContext store.AciContext, command, containerGroup string, containerName string) (c containerinstance.ContainerExecResponse, err error) {
 	containerClient, err := getContainerClient(aciContext.SubscriptionID)
 	if err != nil {

+ 10 - 2
aci/backend.go

@@ -221,8 +221,16 @@ func addTag(groupDefinition *containerinstance.ContainerGroup, tagName string) {
 	groupDefinition.Tags[tagName] = to.StringPtr(tagName)
 }
 
-func (cs *aciContainerService) Stop(ctx context.Context, containerName string, timeout *uint32) error {
-	return errdefs.ErrNotImplemented
+func (cs *aciContainerService) Stop(ctx context.Context, containerID string, timeout *uint32) error {
+	if timeout != nil && *timeout != uint32(0) {
+		return errors.Errorf("ACI integration does not support setting a timeout to stop a container before killing it.")
+	}
+	groupName, containerName := getGroupAndContainerName(containerID)
+	if groupName != containerID {
+		msg := "cannot stop service %q from compose application %q, you can stop the entire compose app with docker stop %s"
+		return errors.New(fmt.Sprintf(msg, containerName, groupName, groupName))
+	}
+	return stopACIContainerGroup(ctx, cs.ctx, groupName)
 }
 
 func getGroupAndContainerName(containerID string) (string, string) {

+ 77 - 0
cli/cmd/stop.go

@@ -0,0 +1,77 @@
+/*
+   Copyright 2020 Docker, Inc.
+
+   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/docker/api/errdefs"
+
+	"github.com/pkg/errors"
+	"github.com/spf13/cobra"
+
+	"github.com/hashicorp/go-multierror"
+
+	"github.com/docker/api/client"
+)
+
+type stopOpts struct {
+	timeout uint32
+}
+
+// StopCommand deletes containers
+func StopCommand() *cobra.Command {
+	var opts stopOpts
+	cmd := &cobra.Command{
+		Use:   "stop",
+		Short: "Stop one or more running containers",
+		Args:  cobra.MinimumNArgs(1),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return runStop(cmd.Context(), args, opts)
+		},
+	}
+
+	cmd.Flags().Uint32Var(&opts.timeout, "timeout", 0, "Seconds to wait for stop before killing it (default 0, no timeout)")
+
+	return cmd
+}
+
+func runStop(ctx context.Context, args []string, opts stopOpts) error {
+	c, err := client.New(ctx)
+	if err != nil {
+		return errors.Wrap(err, "cannot connect to backend")
+	}
+
+	var errs *multierror.Error
+	for _, id := range args {
+		err := c.ContainerService().Stop(ctx, id, &opts.timeout)
+		if err != nil {
+			if errdefs.IsNotFoundError(err) {
+				errs = multierror.Append(errs, fmt.Errorf("container %s not found", id))
+			} else {
+				errs = multierror.Append(errs, err)
+			}
+			continue
+		}
+		fmt.Println(id)
+	}
+	if errs != nil {
+		errs.ErrorFormat = formatErrors
+	}
+	return errs.ErrorOrNil()
+}

+ 1 - 0
cli/main.go

@@ -124,6 +124,7 @@ func main() {
 		login.Command(),
 		logout.Command(),
 		cmd.VersionCommand(version),
+		cmd.StopCommand(),
 	)
 
 	helpFunc := root.HelpFunc()

+ 15 - 2
tests/aci-e2e/e2e-aci_test.go

@@ -360,8 +360,21 @@ func TestContainerRunAttached(t *testing.T) {
 		poll.WaitOn(t, checkLog, poll.WithDelay(1*time.Second), poll.WithTimeout(20*time.Second))
 	})
 
-	t.Run("rm attached", func(t *testing.T) {
-		res := c.RunDockerCmd("rm", "-f", container)
+	t.Run("stop wrong container", func(t *testing.T) {
+		res := c.RunDockerCmd("stop", "unknown-container")
+		res.Assert(t, icmd.Expected{
+			Err:      "Error: container unknown-container not found",
+			ExitCode: 1,
+		})
+	})
+
+	t.Run("stop container", func(t *testing.T) {
+		res := c.RunDockerCmd("stop", container)
+		res.Assert(t, icmd.Expected{Out: container})
+	})
+
+	t.Run("rm stopped container", func(t *testing.T) {
+		res := c.RunDockerCmd("rm", container)
 		res.Assert(t, icmd.Expected{Out: container})
 	})
 }