@@ -58,6 +58,8 @@ func (dcl *DockerCLILister) List(ctx context.Context) (codersdk.WorkspaceAgentLi
58
58
return codersdk.WorkspaceAgentListContainersResponse {}, xerrors .Errorf ("scan docker ps output: %w" , err )
59
59
}
60
60
61
+ dockerPsStderr := strings .TrimSpace (stderrBuf .String ())
62
+
61
63
// now we can get the detailed information for each container
62
64
// Run `docker inspect` on each container ID
63
65
stdoutBuf .Reset ()
@@ -71,6 +73,8 @@ func (dcl *DockerCLILister) List(ctx context.Context) (codersdk.WorkspaceAgentLi
71
73
return codersdk.WorkspaceAgentListContainersResponse {}, xerrors .Errorf ("run docker inspect: %w: %s" , err , strings .TrimSpace (stderrBuf .String ()))
72
74
}
73
75
76
+ dockerInspectStderr := strings .TrimSpace (stderrBuf .String ())
77
+
74
78
// NOTE: There is an unavoidable potential race condition where a
75
79
// container is removed between `docker ps` and `docker inspect`.
76
80
// In this case, stderr will contain an error message but stdout
@@ -92,24 +96,41 @@ func (dcl *DockerCLILister) List(ctx context.Context) (codersdk.WorkspaceAgentLi
92
96
res .Containers [idx ] = out
93
97
}
94
98
99
+ if dockerPsStderr != "" {
100
+ res .Warnings = append (res .Warnings , dockerPsStderr )
101
+ }
102
+ if dockerInspectStderr != "" {
103
+ res .Warnings = append (res .Warnings , dockerInspectStderr )
104
+ }
105
+
95
106
return res , nil
96
107
}
97
108
98
109
// To avoid a direct dependency on the Docker API, we use the docker CLI
99
110
// to fetch information about containers.
100
111
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"`
106
119
}
107
120
108
121
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"`
113
134
}
114
135
115
136
type dockerInspectState struct {
@@ -144,14 +165,17 @@ func convertDockerInspect(in dockerInspect) (codersdk.WorkspaceAgentDevcontainer
144
165
ID : in .ID ,
145
166
Image : in .Config .Image ,
146
167
Labels : in .Config .Labels ,
147
- Ports : make ([]codersdk.WorkspaceAgentListeningPort , 0 , len ( in . Config . ExposedPorts ) ),
168
+ Ports : make ([]codersdk.WorkspaceAgentListeningPort , 0 ),
148
169
Running : in .State .Running ,
149
170
Status : in .State .String (),
150
- Volumes : make (map [string ]string , len (in .Config . Volumes )),
171
+ Volumes : make (map [string ]string , len (in .Mounts )),
151
172
}
152
173
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.
155
179
sort .Strings (portKeys )
156
180
for _ , p := range portKeys {
157
181
if port , network , err := convertDockerPort (p ); err != nil {
@@ -164,15 +188,15 @@ func convertDockerInspect(in dockerInspect) (codersdk.WorkspaceAgentDevcontainer
164
188
}
165
189
}
166
190
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
176
200
}
177
201
178
202
return out , warns
@@ -202,21 +226,3 @@ func convertDockerPort(in string) (uint16, string, error) {
202
226
return 0 , "" , xerrors .Errorf ("invalid port format: %s" , in )
203
227
}
204
228
}
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
- }
0 commit comments