ACI Stop implementation

This commit is contained in:
Guillaume Tardif 2020-08-11 17:49:02 +02:00
parent f3aeb318f6
commit ee062e8333
5 changed files with 118 additions and 4 deletions

View File

@ -24,6 +24,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/docker/api/errdefs"
"github.com/Azure/azure-sdk-for-go/services/containerinstance/mgmt/2018-10-01/containerinstance" "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"
"github.com/Azure/go-autorest/autorest/to" "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) 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) { func execACIContainer(ctx context.Context, aciContext store.AciContext, command, containerGroup string, containerName string) (c containerinstance.ContainerExecResponse, err error) {
containerClient, err := getContainerClient(aciContext.SubscriptionID) containerClient, err := getContainerClient(aciContext.SubscriptionID)
if err != nil { if err != nil {

View File

@ -221,8 +221,16 @@ func addTag(groupDefinition *containerinstance.ContainerGroup, tagName string) {
groupDefinition.Tags[tagName] = to.StringPtr(tagName) groupDefinition.Tags[tagName] = to.StringPtr(tagName)
} }
func (cs *aciContainerService) Stop(ctx context.Context, containerName string, timeout *uint32) error { func (cs *aciContainerService) Stop(ctx context.Context, containerID string, timeout *uint32) error {
return errdefs.ErrNotImplemented 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) { func getGroupAndContainerName(containerID string) (string, string) {

77
cli/cmd/stop.go Normal file
View File

@ -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()
}

View File

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

View File

@ -360,8 +360,21 @@ func TestContainerRunAttached(t *testing.T) {
poll.WaitOn(t, checkLog, poll.WithDelay(1*time.Second), poll.WithTimeout(20*time.Second)) poll.WaitOn(t, checkLog, poll.WithDelay(1*time.Second), poll.WithTimeout(20*time.Second))
}) })
t.Run("rm attached", func(t *testing.T) { t.Run("stop wrong container", func(t *testing.T) {
res := c.RunDockerCmd("rm", "-f", container) 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}) res.Assert(t, icmd.Expected{Out: container})
}) })
} }