Tizen_4.0 base
[platform/upstream/docker-engine.git] / hack / integration-cli-on-swarm / host / host.go
1 package main
2
3 import (
4         "context"
5         "flag"
6         "fmt"
7         "io"
8         "io/ioutil"
9         "os"
10         "strings"
11         "time"
12
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"
18 )
19
20 const (
21         defaultStackName       = "integration-cli-on-swarm"
22         defaultVolumeName      = "integration-cli-on-swarm"
23         defaultMasterImageName = "integration-cli-master"
24         defaultWorkerImageName = "integration-cli-worker"
25 )
26
27 func main() {
28         rc, err := xmain()
29         if err != nil {
30                 logrus.Fatalf("fatal error: %v", err)
31         }
32         os.Exit(rc)
33 }
34
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")
46         flag.Parse()
47         if *chunks == 0 {
48                 *chunks = *replicas
49         }
50         if *randSeed == int64(0) {
51                 *randSeed = time.Now().UnixNano()
52         }
53         cli, err := client.NewEnvClient()
54         if err != nil {
55                 return 1, err
56         }
57         if hasStack(cli, defaultStackName) {
58                 logrus.Infof("Removing stack %s", defaultStackName)
59                 removeStack(cli, defaultStackName)
60         }
61         if hasVolume(cli, defaultVolumeName) {
62                 logrus.Infof("Removing volume %s", defaultVolumeName)
63                 removeVolume(cli, defaultVolumeName)
64         }
65         if err = ensureImages(cli, []string{defaultWorkerImageName, defaultMasterImageName}); err != nil {
66                 return 1, err
67         }
68         workerImageForStack := defaultWorkerImageName
69         if *pushWorkerImage != "" {
70                 logrus.Infof("Pushing %s to %s", defaultWorkerImageName, *pushWorkerImage)
71                 if err = pushImage(cli, *pushWorkerImage, defaultWorkerImageName); err != nil {
72                         return 1, err
73                 }
74                 workerImageForStack = *pushWorkerImage
75         }
76         compose, err := createCompose("", cli, composeOptions{
77                 Replicas:     *replicas,
78                 Chunks:       *chunks,
79                 MasterImage:  defaultMasterImageName,
80                 WorkerImage:  workerImageForStack,
81                 Volume:       defaultVolumeName,
82                 Shuffle:      *shuffle,
83                 RandSeed:     *randSeed,
84                 DryRun:       *dryRun,
85                 KeepExecutor: *keepExecutor,
86         })
87         if err != nil {
88                 return 1, err
89         }
90         filters, err := filtersBytes(*filtersFile)
91         if err != nil {
92                 return 1, err
93         }
94         logrus.Infof("Creating volume %s with input data", defaultVolumeName)
95         if err = createVolumeWithData(cli,
96                 defaultVolumeName,
97                 map[string][]byte{"/input": filters},
98                 defaultMasterImageName); err != nil {
99                 return 1, err
100         }
101         logrus.Infof("Deploying stack %s from %s", defaultStackName, compose)
102         defer func() {
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)
109         }()
110         if err = deployStack(cli, defaultStackName, compose); err != nil {
111                 return 1, err
112         }
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`",
115                 defaultStackName)
116         masterContainerID, err := waitForMasterUp(cli, defaultStackName)
117         if err != nil {
118                 return 1, err
119         }
120         rc, err := waitForContainerCompletion(cli, os.Stdout, os.Stderr, masterContainerID)
121         if err != nil {
122                 return 1, err
123         }
124         logrus.Infof("Exit status: %d", rc)
125         return int(rc), nil
126 }
127
128 func ensureImages(cli *client.Client, images []string) error {
129         for _, image := range images {
130                 _, _, err := cli.ImageInspectWithRaw(context.Background(), image)
131                 if err != nil {
132                         return fmt.Errorf("could not find image %s, please run `make build-integration-cli-on-swarm`: %v",
133                                 image, err)
134                 }
135         }
136         return nil
137 }
138
139 func filtersBytes(optionalFiltersFile string) ([]byte, error) {
140         var b []byte
141         if optionalFiltersFile == "" {
142                 tests, err := enumerateTests(".")
143                 if err != nil {
144                         return b, err
145                 }
146                 b = []byte(strings.Join(tests, "\n") + "\n")
147         } else {
148                 var err error
149                 b, err = ioutil.ReadFile(optionalFiltersFile)
150                 if err != nil {
151                         return b, err
152                 }
153         }
154         return b, nil
155 }
156
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)
160
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{
166                 All:     true,
167                 Filters: fil,
168         })
169         if err != nil {
170                 return "", err
171         }
172         if len(masters) == 0 {
173                 return "", fmt.Errorf("master not running in stack %s?", stackName)
174         }
175         return masters[0].ID, nil
176 }
177
178 func waitForContainerCompletion(cli *client.Client, stdout, stderr io.Writer, containerID string) (int64, error) {
179         stream, err := cli.ContainerLogs(context.Background(),
180                 containerID,
181                 types.ContainerLogsOptions{
182                         ShowStdout: true,
183                         ShowStderr: true,
184                         Follow:     true,
185                 })
186         if err != nil {
187                 return 1, err
188         }
189         stdcopy.StdCopy(stdout, stderr, stream)
190         stream.Close()
191         resultC, errC := cli.ContainerWait(context.Background(), containerID, "")
192         select {
193         case err := <-errC:
194                 return 1, err
195         case result := <-resultC:
196                 return result.StatusCode, nil
197         }
198 }