Skip to content

Commit b1d4ebc

Browse files
committed
container-manager
1 parent 8ccc637 commit b1d4ebc

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
package containermanager
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
"io"
8+
9+
api "github.com/docker/docker/api/types"
10+
"github.com/docker/docker/api/types/container"
11+
"github.com/docker/docker/client"
12+
"github.com/dockersamples/gopher-task-system/internal/types"
13+
"github.com/pkg/errors"
14+
)
15+
16+
type ContainerManager interface {
17+
PullImage(ctx context.Context, image string) error
18+
CreateContainer(ctx context.Context, task types.Task) (string, error)
19+
StartContainer(ctx context.Context, id string) error
20+
WaitForContainer(ctx context.Context, id string) (bool, error)
21+
RemoveContainer(ctx context.Context, id string) error
22+
}
23+
24+
type DockerClient interface {
25+
client.ImageAPIClient
26+
client.ContainerAPIClient
27+
}
28+
29+
type ImagePullStatus struct {
30+
Status string `json:"status"`
31+
Error string `json:"error"`
32+
Progress string `json:"progress"`
33+
ProgressDetail struct {
34+
Current int `json:"current"`
35+
Total int `json:"total"`
36+
} `json:"progressDetail"`
37+
}
38+
39+
type containermanager struct {
40+
cli DockerClient
41+
}
42+
43+
func NewContainerManager(cli DockerClient) ContainerManager {
44+
return &containermanager{
45+
cli: cli,
46+
}
47+
}
48+
49+
// PullImage outputs to stdout the contents of the runner image.
50+
func (m *containermanager) PullImage(ctx context.Context, image string) error {
51+
out, err := m.cli.ImagePull(ctx, image, api.ImagePullOptions{})
52+
if err != nil {
53+
return errors.Wrap(err, "DOCKER PULL")
54+
}
55+
56+
defer func() {
57+
if err := out.Close(); err != nil {
58+
fmt.Println(err)
59+
}
60+
}()
61+
62+
fd := json.NewDecoder(out)
63+
var status *ImagePullStatus
64+
for {
65+
if err := fd.Decode(&status); err != nil {
66+
if errors.Is(err, io.EOF) {
67+
break
68+
}
69+
70+
return errors.Wrap(err, "DOCKER PULL")
71+
}
72+
73+
if status.Error != "" {
74+
return errors.Wrap(errors.New(status.Error), "DOCKER PULL")
75+
}
76+
77+
// uncomment to log image pull status
78+
// fmt.Println(status)
79+
}
80+
81+
return nil
82+
}
83+
84+
// CreateContainer creates a new container and returns it ID.
85+
func (m *containermanager) CreateContainer(ctx context.Context, task types.Task) (string, error) {
86+
config := &container.Config{
87+
Image: task.Runner,
88+
Cmd: task.Command,
89+
}
90+
91+
res, err := m.cli.ContainerCreate(ctx, config, &container.HostConfig{}, nil, nil, task.Name)
92+
if err != nil {
93+
return "", err
94+
}
95+
96+
return res.ID, nil
97+
}
98+
99+
// StartContainer starts the container created with given ID.
100+
func (m *containermanager) StartContainer(ctx context.Context, id string) error {
101+
return m.cli.ContainerStart(ctx, id, api.ContainerStartOptions{})
102+
}
103+
104+
// WaitForContainer waits for the running container to finish.
105+
func (m *containermanager) WaitForContainer(ctx context.Context, id string) (bool, error) {
106+
// check if the container is in running state
107+
if _, err := m.cli.ContainerInspect(ctx, id); err != nil {
108+
return true, nil
109+
}
110+
111+
// send API call to wait for the container completion
112+
wait, errC := m.cli.ContainerWait(ctx, id, container.WaitConditionNotRunning)
113+
114+
// check if container exit code is 0, and return accordingly
115+
select {
116+
case status := <-wait:
117+
if status.StatusCode == 0 {
118+
return true, nil
119+
}
120+
121+
return false, nil
122+
case err := <-errC:
123+
return false, err
124+
case <-ctx.Done():
125+
return false, ctx.Err()
126+
}
127+
}
128+
129+
// RemoveContainer removes the given container../0+
130+
func (m *containermanager) RemoveContainer(ctx context.Context, id string) error {
131+
return m.cli.ContainerRemove(ctx, id, api.ContainerRemoveOptions{})
132+
}

0 commit comments

Comments
 (0)