1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00
moby--moby/hack/integration-cli-on-swarm/host/host.go
Josh Hawn 4921171587 Update ContainerWait API
This patch adds the untilRemoved option to the ContainerWait API which
allows the client to wait until the container is not only exited but
also removed.

This patch also adds some more CLI integration tests for waiting for a
created container and waiting with the new --until-removed flag.

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Handle detach sequence in CLI

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Update Container Wait Conditions

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Apply container wait changes to API 1.30

The set of changes to the containerWait API missed the cut for the
Docker 17.05 release (API version 1.29). This patch bumps the version
checks to use 1.30 instead.

This patch also makes a minor update to a testfile which was added to
the builder/dockerfile package.

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Remove wait changes from CLI

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Address minor nits on wait changes

- Changed the name of the tty Proxy wrapper to `escapeProxy`
- Removed the unnecessary Error() method on container.State
- Fixes a typo in comment (repeated word)

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Use router.WithCancel in the containerWait handler

This handler previously added this functionality manually but now uses
the existing wrapper which does it for us.

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Add WaitCondition constants to api/types/container

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Address more ContainerWait review comments

- Update ContainerWait backend interface to not return pointer values
  for container.StateStatus type.
- Updated container state's Wait() method comments to clarify that a
  context MUST be used for cancelling the request, setting timeouts,
  and to avoid goroutine leaks.
- Removed unnecessary buffering when making channels in the client's
  ContainerWait methods.
- Renamed result and error channels in client's ContainerWait methods
  to clarify that only a single result or error value would be sent
  on the channel.

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Move container.WaitCondition type to separate file

... to avoid conflict with swagger-generated code for API response

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)

Address more ContainerWait review comments

Docker-DCO-1.1-Signed-off-by: Josh Hawn <josh.hawn@docker.com> (github: jlhawn)
2017-05-16 15:11:39 -07:00

198 lines
5.9 KiB
Go

package main
import (
"context"
"flag"
"fmt"
"io"
"io/ioutil"
"os"
"strings"
"time"
"github.com/Sirupsen/logrus"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
)
const (
defaultStackName = "integration-cli-on-swarm"
defaultVolumeName = "integration-cli-on-swarm"
defaultMasterImageName = "integration-cli-master"
defaultWorkerImageName = "integration-cli-worker"
)
func main() {
rc, err := xmain()
if err != nil {
logrus.Fatalf("fatal error: %v", err)
}
os.Exit(rc)
}
func xmain() (int, error) {
// Should we use cobra maybe?
replicas := flag.Int("replicas", 1, "Number of worker service replica")
chunks := flag.Int("chunks", 0, "Number of test chunks executed in batch (0 == replicas)")
pushWorkerImage := flag.String("push-worker-image", "", "Push the worker image to the registry. Required for distribuetd execution. (empty == not to push)")
shuffle := flag.Bool("shuffle", false, "Shuffle the input so as to mitigate makespan nonuniformity")
// flags below are rarely used
randSeed := flag.Int64("rand-seed", int64(0), "Random seed used for shuffling (0 == curent time)")
filtersFile := flag.String("filters-file", "", "Path to optional file composed of `-check.f` filter strings")
dryRun := flag.Bool("dry-run", false, "Dry run")
keepExecutor := flag.Bool("keep-executor", false, "Do not auto-remove executor containers, which is used for running privileged programs on Swarm")
flag.Parse()
if *chunks == 0 {
*chunks = *replicas
}
if *randSeed == int64(0) {
*randSeed = time.Now().UnixNano()
}
cli, err := client.NewEnvClient()
if err != nil {
return 1, err
}
if hasStack(cli, defaultStackName) {
logrus.Infof("Removing stack %s", defaultStackName)
removeStack(cli, defaultStackName)
}
if hasVolume(cli, defaultVolumeName) {
logrus.Infof("Removing volume %s", defaultVolumeName)
removeVolume(cli, defaultVolumeName)
}
if err = ensureImages(cli, []string{defaultWorkerImageName, defaultMasterImageName}); err != nil {
return 1, err
}
workerImageForStack := defaultWorkerImageName
if *pushWorkerImage != "" {
logrus.Infof("Pushing %s to %s", defaultWorkerImageName, *pushWorkerImage)
if err = pushImage(cli, *pushWorkerImage, defaultWorkerImageName); err != nil {
return 1, err
}
workerImageForStack = *pushWorkerImage
}
compose, err := createCompose("", cli, composeOptions{
Replicas: *replicas,
Chunks: *chunks,
MasterImage: defaultMasterImageName,
WorkerImage: workerImageForStack,
Volume: defaultVolumeName,
Shuffle: *shuffle,
RandSeed: *randSeed,
DryRun: *dryRun,
KeepExecutor: *keepExecutor,
})
if err != nil {
return 1, err
}
filters, err := filtersBytes(*filtersFile)
if err != nil {
return 1, err
}
logrus.Infof("Creating volume %s with input data", defaultVolumeName)
if err = createVolumeWithData(cli,
defaultVolumeName,
map[string][]byte{"/input": filters},
defaultMasterImageName); err != nil {
return 1, err
}
logrus.Infof("Deploying stack %s from %s", defaultStackName, compose)
defer func() {
logrus.Infof("NOTE: You may want to inspect or clean up following resources:")
logrus.Infof(" - Stack: %s", defaultStackName)
logrus.Infof(" - Volume: %s", defaultVolumeName)
logrus.Infof(" - Compose file: %s", compose)
logrus.Infof(" - Master image: %s", defaultMasterImageName)
logrus.Infof(" - Worker image: %s", workerImageForStack)
}()
if err = deployStack(cli, defaultStackName, compose); err != nil {
return 1, err
}
logrus.Infof("The log will be displayed here after some duration."+
"You can watch the live status via `docker service logs %s_worker`",
defaultStackName)
masterContainerID, err := waitForMasterUp(cli, defaultStackName)
if err != nil {
return 1, err
}
rc, err := waitForContainerCompletion(cli, os.Stdout, os.Stderr, masterContainerID)
if err != nil {
return 1, err
}
logrus.Infof("Exit status: %d", rc)
return int(rc), nil
}
func ensureImages(cli *client.Client, images []string) error {
for _, image := range images {
_, _, err := cli.ImageInspectWithRaw(context.Background(), image)
if err != nil {
return fmt.Errorf("could not find image %s, please run `make build-integration-cli-on-swarm`: %v",
image, err)
}
}
return nil
}
func filtersBytes(optionalFiltersFile string) ([]byte, error) {
var b []byte
if optionalFiltersFile == "" {
tests, err := enumerateTests(".")
if err != nil {
return b, err
}
b = []byte(strings.Join(tests, "\n") + "\n")
} else {
var err error
b, err = ioutil.ReadFile(optionalFiltersFile)
if err != nil {
return b, err
}
}
return b, nil
}
func waitForMasterUp(cli *client.Client, stackName string) (string, error) {
// FIXME(AkihiroSuda): it should retry until master is up, rather than pre-sleeping
time.Sleep(10 * time.Second)
fil := filters.NewArgs()
fil.Add("label", "com.docker.stack.namespace="+stackName)
// FIXME(AkihiroSuda): we should not rely on internal service naming convention
fil.Add("label", "com.docker.swarm.service.name="+stackName+"_master")
masters, err := cli.ContainerList(context.Background(), types.ContainerListOptions{
All: true,
Filters: fil,
})
if err != nil {
return "", err
}
if len(masters) == 0 {
return "", fmt.Errorf("master not running in stack %s?", stackName)
}
return masters[0].ID, nil
}
func waitForContainerCompletion(cli *client.Client, stdout, stderr io.Writer, containerID string) (int64, error) {
stream, err := cli.ContainerLogs(context.Background(),
containerID,
types.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Follow: true,
})
if err != nil {
return 1, err
}
stdcopy.StdCopy(stdout, stderr, stream)
stream.Close()
resultC, errC := cli.ContainerWait(context.Background(), containerID, "")
select {
case err := <-errC:
return 1, err
case result := <-resultC:
return result.StatusCode, nil
}
}