Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8b081ac

Browse files
committed
correctly detect ports and volumes
1 parent 1dbcf0f commit 8b081ac

File tree

2 files changed

+75
-46
lines changed

2 files changed

+75
-46
lines changed

agent/agentcontainers/containers_dockercli.go

+46-40
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ func (dcl *DockerCLILister) List(ctx context.Context) (codersdk.WorkspaceAgentLi
5858
return codersdk.WorkspaceAgentListContainersResponse{}, xerrors.Errorf("scan docker ps output: %w", err)
5959
}
6060

61+
dockerPsStderr := strings.TrimSpace(stderrBuf.String())
62+
6163
// now we can get the detailed information for each container
6264
// Run `docker inspect` on each container ID
6365
stdoutBuf.Reset()
@@ -71,6 +73,8 @@ func (dcl *DockerCLILister) List(ctx context.Context) (codersdk.WorkspaceAgentLi
7173
return codersdk.WorkspaceAgentListContainersResponse{}, xerrors.Errorf("run docker inspect: %w: %s", err, strings.TrimSpace(stderrBuf.String()))
7274
}
7375

76+
dockerInspectStderr := strings.TrimSpace(stderrBuf.String())
77+
7478
// NOTE: There is an unavoidable potential race condition where a
7579
// container is removed between `docker ps` and `docker inspect`.
7680
// In this case, stderr will contain an error message but stdout
@@ -92,24 +96,41 @@ func (dcl *DockerCLILister) List(ctx context.Context) (codersdk.WorkspaceAgentLi
9296
res.Containers[idx] = out
9397
}
9498

99+
if dockerPsStderr != "" {
100+
res.Warnings = append(res.Warnings, dockerPsStderr)
101+
}
102+
if dockerInspectStderr != "" {
103+
res.Warnings = append(res.Warnings, dockerInspectStderr)
104+
}
105+
95106
return res, nil
96107
}
97108

98109
// To avoid a direct dependency on the Docker API, we use the docker CLI
99110
// to fetch information about containers.
100111
type dockerInspect struct {
101-
ID string `json:"Id"`
102-
Created time.Time `json:"Created"`
103-
Name string `json:"Name"`
104-
Config dockerInspectConfig `json:"Config"`
105-
State dockerInspectState `json:"State"`
112+
ID string `json:"Id"`
113+
Created time.Time `json:"Created"`
114+
Config dockerInspectConfig `json:"Config"`
115+
HostConfig dockerInspectHostConfig `json:"HostConfig"`
116+
Name string `json:"Name"`
117+
Mounts []dockerInspectMount `json:"Mounts"`
118+
State dockerInspectState `json:"State"`
106119
}
107120

108121
type dockerInspectConfig struct {
109-
ExposedPorts map[string]struct{} `json:"ExposedPorts"`
110-
Image string `json:"Image"`
111-
Labels map[string]string `json:"Labels"`
112-
Volumes map[string]struct{} `json:"Volumes"`
122+
Image string `json:"Image"`
123+
Labels map[string]string `json:"Labels"`
124+
}
125+
126+
type dockerInspectHostConfig struct {
127+
PortBindings map[string]any `json:"PortBindings"`
128+
}
129+
130+
type dockerInspectMount struct {
131+
Source string `json:"Source"`
132+
Destination string `json:"Destination"`
133+
Type string `json:"Type"`
113134
}
114135

115136
type dockerInspectState struct {
@@ -144,14 +165,17 @@ func convertDockerInspect(in dockerInspect) (codersdk.WorkspaceAgentDevcontainer
144165
ID: in.ID,
145166
Image: in.Config.Image,
146167
Labels: in.Config.Labels,
147-
Ports: make([]codersdk.WorkspaceAgentListeningPort, 0, len(in.Config.ExposedPorts)),
168+
Ports: make([]codersdk.WorkspaceAgentListeningPort, 0),
148169
Running: in.State.Running,
149170
Status: in.State.String(),
150-
Volumes: make(map[string]string, len(in.Config.Volumes)),
171+
Volumes: make(map[string]string, len(in.Mounts)),
151172
}
152173

153-
// sort the keys for deterministic output
154-
portKeys := maps.Keys(in.Config.ExposedPorts)
174+
if in.HostConfig.PortBindings == nil {
175+
in.HostConfig.PortBindings = make(map[string]any)
176+
}
177+
portKeys := maps.Keys(in.HostConfig.PortBindings)
178+
// Sort the ports for deterministic output.
155179
sort.Strings(portKeys)
156180
for _, p := range portKeys {
157181
if port, network, err := convertDockerPort(p); err != nil {
@@ -164,15 +188,15 @@ func convertDockerInspect(in dockerInspect) (codersdk.WorkspaceAgentDevcontainer
164188
}
165189
}
166190

167-
// sort the keys for deterministic output
168-
volKeys := maps.Keys(in.Config.Volumes)
169-
sort.Strings(volKeys)
170-
for _, k := range volKeys {
171-
if v0, v1, err := convertDockerVolume(k); err != nil {
172-
warns = append(warns, err.Error())
173-
} else {
174-
out.Volumes[v0] = v1
175-
}
191+
if in.Mounts == nil {
192+
in.Mounts = []dockerInspectMount{}
193+
}
194+
// Sort the mounts for deterministic output.
195+
sort.Slice(in.Mounts, func(i, j int) bool {
196+
return in.Mounts[i].Source < in.Mounts[j].Source
197+
})
198+
for _, k := range in.Mounts {
199+
out.Volumes[k.Source] = k.Destination
176200
}
177201

178202
return out, warns
@@ -202,21 +226,3 @@ func convertDockerPort(in string) (uint16, string, error) {
202226
return 0, "", xerrors.Errorf("invalid port format: %s", in)
203227
}
204228
}
205-
206-
// convertDockerVolume converts a Docker volume string to a host path and
207-
// container path. If the host path is not specified, the container path is used
208-
// as the host path.
209-
// example: "/host/path=/container/path" -> "/host/path", "/container/path"
210-
//
211-
// "/container/path" -> "/container/path", "/container/path"
212-
func convertDockerVolume(in string) (hostPath, containerPath string, err error) {
213-
parts := strings.Split(in, "=")
214-
switch len(parts) {
215-
case 1:
216-
return parts[0], parts[0], nil
217-
case 2:
218-
return parts[0], parts[1], nil
219-
default:
220-
return "", "", xerrors.Errorf("invalid volume format: %s", in)
221-
}
222-
}

agent/agentcontainers/containers_internal_test.go

+29-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package agentcontainers
22

33
import (
4+
"fmt"
45
"os/exec"
56
"runtime"
7+
"strconv"
68
"strings"
79
"testing"
810
"time"
@@ -39,18 +41,34 @@ func TestDockerCLIContainerLister(t *testing.T) {
3941
pool, err := dockertest.NewPool("")
4042
require.NoError(t, err, "Could not connect to docker")
4143
testLabelValue := uuid.New().String()
44+
// Create a temporary directory to validate that we surface mounts correctly.
45+
testTempDir := t.TempDir()
46+
// Pick a random port to expose for testing port bindings.
47+
testRandPort := testutil.RandomPortNoListen(t)
4248
ct, err := pool.RunWithOptions(&dockertest.RunOptions{
43-
Repository: "busybox",
44-
Tag: "latest",
45-
Cmd: []string{"sleep", "infnity"},
46-
Labels: map[string]string{"com.coder.test": testLabelValue},
49+
Repository: "busybox",
50+
Tag: "latest",
51+
Cmd: []string{"sleep", "infnity"},
52+
Labels: map[string]string{"com.coder.test": testLabelValue},
53+
Mounts: []string{testTempDir + ":" + testTempDir},
54+
ExposedPorts: []string{fmt.Sprintf("%d/tcp", testRandPort)},
55+
PortBindings: map[docker.Port][]docker.PortBinding{
56+
docker.Port(fmt.Sprintf("%d/tcp", testRandPort)): {
57+
{
58+
HostIP: "0.0.0.0",
59+
HostPort: strconv.FormatInt(int64(testRandPort), 10),
60+
},
61+
},
62+
},
4763
}, func(config *docker.HostConfig) {
4864
config.AutoRemove = true
4965
config.RestartPolicy = docker.RestartPolicy{Name: "no"}
5066
})
5167
require.NoError(t, err, "Could not start test docker container")
68+
t.Logf("Created container %q", ct.Container.Name)
5269
t.Cleanup(func() {
5370
assert.NoError(t, pool.Purge(ct), "Could not purge resource %q", ct.Container.Name)
71+
t.Logf("Purged container %q", ct.Container.Name)
5472
})
5573

5674
dcl := NewDocker(agentexec.DefaultExecer)
@@ -70,8 +88,13 @@ func TestDockerCLIContainerLister(t *testing.T) {
7088
assert.Equal(t, ct.Container.Config.Labels, foundContainer.Labels)
7189
assert.True(t, foundContainer.Running)
7290
assert.Equal(t, "running", foundContainer.Status)
73-
assert.Len(t, foundContainer.Ports, 0)
74-
assert.Len(t, foundContainer.Volumes, 0)
91+
if assert.Len(t, foundContainer.Ports, 1) {
92+
assert.Equal(t, testRandPort, foundContainer.Ports[0].Port)
93+
assert.Equal(t, "tcp", foundContainer.Ports[0].Network)
94+
}
95+
if assert.Len(t, foundContainer.Volumes, 1) {
96+
assert.Equal(t, testTempDir, foundContainer.Volumes[testTempDir])
97+
}
7598
break
7699
}
77100
}

0 commit comments

Comments
 (0)