-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathcontainer.go
155 lines (133 loc) · 4.88 KB
/
container.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package docker
import (
"bytes"
"context"
"io/ioutil"
"os"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/network"
"github.com/docker/docker/pkg/stdcopy"
specs "github.com/opencontainers/image-spec/specs-go/v1"
)
// ExecResult gets the result of a executed docker command
type ExecResult struct {
// StdOut displays what is send to stdout by the container. This is normally in docker logs
// See: https://docs.docker.com/config/containers/logging/
StdOut string
// StdOut displays what is send to stderr by the container. This is normally in docker logs
// See: https://docs.docker.com/config/containers/logging/
StdErr string
// ExitCode of the process. See https://tldp.org/LDP/abs/html/exitcodes.html for details
ExitCode int
}
// InspectExecResp copies container execution results into an ExecResult
// object after thje command is finished executing. Returns error on failure
// See: https://docs.docker.com/engine/reference/commandline/attach/ for details
func (c *Client) InspectExecResp(ctx context.Context, id string) (ExecResult, error) {
var execResult ExecResult
resp, err := c.ContainerExecAttach(ctx, id, types.ExecStartCheck{})
if err != nil {
return execResult, err
}
defer resp.Close()
// read the output
var outBuf, errBuf bytes.Buffer
outputDone := make(chan error)
go func() {
// StdCopy demultiplexes the stream into two buffers
_, err = stdcopy.StdCopy(&outBuf, &errBuf, resp.Reader)
outputDone <- err
}()
select {
case err := <-outputDone:
if err != nil {
return execResult, err
}
break
case <-ctx.Done():
return execResult, ctx.Err()
}
stdout, err := ioutil.ReadAll(&outBuf)
if err != nil {
return execResult, err
}
stderr, err := ioutil.ReadAll(&errBuf)
if err != nil {
return execResult, err
}
res, err := c.ContainerExecInspect(ctx, id)
if err != nil {
return execResult, err
}
execResult.ExitCode = res.ExitCode
execResult.StdOut = string(stdout)
execResult.StdErr = string(stderr)
return execResult, nil
}
// ExecRaw allows you to pass a custom types.ExecConfig to a container
// running your command. This method will then attach to the container and return ExecResult
// using InspectExecResp. See https://docs.docker.com/engine/reference/commandline/exec/
// or Client.ContainerExecCreate for details
func (c *Client) ExecRaw(containerID string, config types.ExecConfig) (ExecResult, error) {
execution, err := c.ContainerExecCreate(c.Ctx, containerID, config)
if err != nil {
return ExecResult{}, err
}
return c.InspectExecResp(c.Ctx, execution.ID)
}
// Exec executes a command cmd in a container using ExecRaw
// This method will then attach to the container and return ExecResult
// using InspectExecResp. See https://docs.docker.com/engine/reference/commandline/exec/ for details
func (c *Client) Exec(containerID string, cmd []string) (ExecResult, error) {
config := types.ExecConfig{
AttachStderr: true,
AttachStdout: true,
Cmd: cmd,
}
return c.ExecRaw(containerID, config)
}
// CreateContainer creates a container with a given *container.Config
// making sure to pull the image using PullImage if not present and session the SessionId
// for variadic teardown. See https://docs.docker.com/engine/reference/commandline/create/ for details
func (c Client) CreateContainer(config *container.Config, hostConfig *container.HostConfig, networkingConfig *network.NetworkingConfig, platform *specs.Platform, containerName string) (container.ContainerCreateCreatedBody, error) {
err := c.PullImage(config.Image)
if err != nil {
return container.ContainerCreateCreatedBody{}, err
}
if config.Labels == nil {
config.Labels = make(map[string]string)
}
config.Labels["sessionId"] = c.SessionID
return c.ContainerCreate(c.Ctx, config, hostConfig, networkingConfig, platform, containerName)
}
// PrintContainerLogs copies logs for a running, non-executing command to stdout
// See https://docs.docker.com/engine/reference/commandline/logs/ for details
// TODO: in the futre this should be more flexible
func (c Client) PrintContainerLogs(containerID string) {
go func() {
statusCh, errCh := c.ContainerWait(c.Ctx, containerID, container.WaitConditionNotRunning)
select {
case err := <-errCh:
if err != nil {
panic(err)
}
case <-statusCh:
}
out, err := c.ContainerLogs(c.Ctx, containerID, types.ContainerLogsOptions{ShowStdout: true})
if err != nil {
// container is dead
return
}
_, _ = stdcopy.StdCopy(os.Stdout, os.Stderr, out)
}()
}
// ContainerStatus fetches a container's status and normalizes it
// see: https://docs.docker.com/engine/reference/commandline/inspect/
func (c Client) ContainerStatus(containerID string) (ContainerStatus, error) {
inspectedContainer, err := c.ContainerInspect(c.Ctx, containerID)
if err != nil {
return "", err
}
return ContainerStatus(inspectedContainer.ContainerJSONBase.State.Status), err
}