/
spartan.go
116 lines (108 loc) · 2.81 KB
/
spartan.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package runner
import (
"context"
"errors"
"fmt"
"os/exec"
"strings"
"github.com/MovieStoreGuy/forerunner/config"
"github.com/MovieStoreGuy/forerunner/cortana"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/client"
)
// Spartan is the object type to allow management of
// running the docker image
type Spartan struct {
cli *client.Client
ctx context.Context
conf *config.Set
ids []string
Cortana *cortana.Sentient
}
// New will create an instance of Spartan with the desired config
func New(conf *config.Set) (*Spartan, error) {
cli, err := client.NewEnvClient()
if err != nil {
return nil, err
}
if conf == nil {
conf = config.Default()
}
return &Spartan{
cli: cli,
ctx: context.Background(),
conf: conf,
Cortana: cortana.New(),
}, nil
}
// Start will run the docker image
func (s *Spartan) Start(image string) error {
if !s.containerExists(image) {
return fmt.Errorf("The container %s doesn't appear to exist", image)
}
mode := container.NetworkMode(s.conf.Network)
resp, err := s.cli.ContainerCreate(s.ctx, &container.Config{
Image: image,
Env: s.conf.Environment,
}, &container.HostConfig{NetworkMode: mode}, nil, "")
if err != nil {
return err
}
if err = s.cli.ContainerStart(s.ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
return err
}
s.ids = append(s.ids, resp.ID)
return s.runCommands(s.conf.Cmds...)
}
func (s *Spartan) runCommands(cmds ...string) error {
for _, str := range cmds {
cmd := strings.Split(str, " ")
c := exec.Command(cmd[0], cmd[1:]...)
stdout, err := c.StdoutPipe()
if err != nil {
return errors.New("Unable to connect stdout")
}
stderr, err := c.StderrPipe()
if err != nil {
return errors.New("Unable to connect stderr")
}
// Write outputs as soon as they are received
go s.Cortana.Follow(stdout)
go s.Cortana.Follow(stderr)
if err = c.Run(); err != nil {
return err
}
}
return nil
}
// Stop will stop the given image from running and ensure that anything else
// created with it is also cleaned up
func (s *Spartan) Stop() error {
for _, id := range s.ids {
logs, err := s.cli.ContainerLogs(s.ctx, id, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true})
if err != nil {
return err
}
s.Cortana.Follow(logs)
if err := s.cli.ContainerStop(s.ctx, id, nil); err != nil {
return err
}
if err := s.cli.ContainerRemove(s.ctx, id, types.ContainerRemoveOptions{Force: true}); err != nil {
return err
}
}
return nil
}
func (s *Spartan) containerExists(image string) bool {
images, err := s.cli.ImageList(s.ctx, types.ImageListOptions{All: true})
if err != nil {
return false
}
for _, sum := range images {
if strings.Contains(strings.Join(sum.RepoTags, " "), image) {
return true
}
}
return false
}