13 "github.com/Sirupsen/logrus"
14 "github.com/docker/docker/api/types"
15 "github.com/docker/docker/api/types/filters"
16 "github.com/docker/docker/client"
17 "github.com/docker/docker/pkg/stdcopy"
21 defaultStackName = "integration-cli-on-swarm"
22 defaultVolumeName = "integration-cli-on-swarm"
23 defaultMasterImageName = "integration-cli-master"
24 defaultWorkerImageName = "integration-cli-worker"
30 logrus.Fatalf("fatal error: %v", err)
35 func xmain() (int, error) {
36 // Should we use cobra maybe?
37 replicas := flag.Int("replicas", 1, "Number of worker service replica")
38 chunks := flag.Int("chunks", 0, "Number of test chunks executed in batch (0 == replicas)")
39 pushWorkerImage := flag.String("push-worker-image", "", "Push the worker image to the registry. Required for distribuetd execution. (empty == not to push)")
40 shuffle := flag.Bool("shuffle", false, "Shuffle the input so as to mitigate makespan nonuniformity")
41 // flags below are rarely used
42 randSeed := flag.Int64("rand-seed", int64(0), "Random seed used for shuffling (0 == curent time)")
43 filtersFile := flag.String("filters-file", "", "Path to optional file composed of `-check.f` filter strings")
44 dryRun := flag.Bool("dry-run", false, "Dry run")
45 keepExecutor := flag.Bool("keep-executor", false, "Do not auto-remove executor containers, which is used for running privileged programs on Swarm")
50 if *randSeed == int64(0) {
51 *randSeed = time.Now().UnixNano()
53 cli, err := client.NewEnvClient()
57 if hasStack(cli, defaultStackName) {
58 logrus.Infof("Removing stack %s", defaultStackName)
59 removeStack(cli, defaultStackName)
61 if hasVolume(cli, defaultVolumeName) {
62 logrus.Infof("Removing volume %s", defaultVolumeName)
63 removeVolume(cli, defaultVolumeName)
65 if err = ensureImages(cli, []string{defaultWorkerImageName, defaultMasterImageName}); err != nil {
68 workerImageForStack := defaultWorkerImageName
69 if *pushWorkerImage != "" {
70 logrus.Infof("Pushing %s to %s", defaultWorkerImageName, *pushWorkerImage)
71 if err = pushImage(cli, *pushWorkerImage, defaultWorkerImageName); err != nil {
74 workerImageForStack = *pushWorkerImage
76 compose, err := createCompose("", cli, composeOptions{
79 MasterImage: defaultMasterImageName,
80 WorkerImage: workerImageForStack,
81 Volume: defaultVolumeName,
85 KeepExecutor: *keepExecutor,
90 filters, err := filtersBytes(*filtersFile)
94 logrus.Infof("Creating volume %s with input data", defaultVolumeName)
95 if err = createVolumeWithData(cli,
97 map[string][]byte{"/input": filters},
98 defaultMasterImageName); err != nil {
101 logrus.Infof("Deploying stack %s from %s", defaultStackName, compose)
103 logrus.Infof("NOTE: You may want to inspect or clean up following resources:")
104 logrus.Infof(" - Stack: %s", defaultStackName)
105 logrus.Infof(" - Volume: %s", defaultVolumeName)
106 logrus.Infof(" - Compose file: %s", compose)
107 logrus.Infof(" - Master image: %s", defaultMasterImageName)
108 logrus.Infof(" - Worker image: %s", workerImageForStack)
110 if err = deployStack(cli, defaultStackName, compose); err != nil {
113 logrus.Infof("The log will be displayed here after some duration."+
114 "You can watch the live status via `docker service logs %s_worker`",
116 masterContainerID, err := waitForMasterUp(cli, defaultStackName)
120 rc, err := waitForContainerCompletion(cli, os.Stdout, os.Stderr, masterContainerID)
124 logrus.Infof("Exit status: %d", rc)
128 func ensureImages(cli *client.Client, images []string) error {
129 for _, image := range images {
130 _, _, err := cli.ImageInspectWithRaw(context.Background(), image)
132 return fmt.Errorf("could not find image %s, please run `make build-integration-cli-on-swarm`: %v",
139 func filtersBytes(optionalFiltersFile string) ([]byte, error) {
141 if optionalFiltersFile == "" {
142 tests, err := enumerateTests(".")
146 b = []byte(strings.Join(tests, "\n") + "\n")
149 b, err = ioutil.ReadFile(optionalFiltersFile)
157 func waitForMasterUp(cli *client.Client, stackName string) (string, error) {
158 // FIXME(AkihiroSuda): it should retry until master is up, rather than pre-sleeping
159 time.Sleep(10 * time.Second)
161 fil := filters.NewArgs()
162 fil.Add("label", "com.docker.stack.namespace="+stackName)
163 // FIXME(AkihiroSuda): we should not rely on internal service naming convention
164 fil.Add("label", "com.docker.swarm.service.name="+stackName+"_master")
165 masters, err := cli.ContainerList(context.Background(), types.ContainerListOptions{
172 if len(masters) == 0 {
173 return "", fmt.Errorf("master not running in stack %s?", stackName)
175 return masters[0].ID, nil
178 func waitForContainerCompletion(cli *client.Client, stdout, stderr io.Writer, containerID string) (int64, error) {
179 stream, err := cli.ContainerLogs(context.Background(),
181 types.ContainerLogsOptions{
189 stdcopy.StdCopy(stdout, stderr, stream)
191 resultC, errC := cli.ContainerWait(context.Background(), containerID, "")
195 case result := <-resultC:
196 return result.StatusCode, nil