5
5
"io"
6
6
"os"
7
7
"path/filepath"
8
- "runtime"
9
8
"sync"
10
9
"syscall"
11
10
"time"
@@ -17,19 +16,13 @@ import (
17
16
"github.com/containerd/containerd"
18
17
"github.com/containerd/containerd/cio"
19
18
"github.com/containerd/containerd/mount"
20
- containerdoci "github.com/containerd/containerd/oci"
21
- "github.com/containerd/continuity/fs"
22
- "github.com/docker/docker/pkg/idtools"
23
19
"github.com/moby/buildkit/executor"
24
20
"github.com/moby/buildkit/executor/oci"
25
21
resourcestypes "github.com/moby/buildkit/executor/resources/types"
26
22
gatewayapi "github.com/moby/buildkit/frontend/gateway/pb"
27
23
"github.com/moby/buildkit/identity"
28
- "github.com/moby/buildkit/snapshot"
29
24
"github.com/moby/buildkit/solver/pb"
30
25
"github.com/moby/buildkit/util/network"
31
- rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv"
32
- "github.com/opencontainers/runtime-spec/specs-go"
33
26
"github.com/pkg/errors"
34
27
)
35
28
@@ -39,7 +32,7 @@ type containerdExecutor struct {
39
32
networkProviders map [pb.NetMode ]network.Provider
40
33
cgroupParent string
41
34
dnsConfig * oci.DNSConfig
42
- running map [string ]chan error
35
+ running map [string ]* containerState
43
36
mu sync.Mutex
44
37
apparmorProfile string
45
38
selinux bool
@@ -72,23 +65,36 @@ func New(client *containerd.Client, root, cgroup string, networkProviders map[pb
72
65
networkProviders : networkProviders ,
73
66
cgroupParent : cgroup ,
74
67
dnsConfig : dnsConfig ,
75
- running : make (map [string ]chan error ),
68
+ running : make (map [string ]* containerState ),
76
69
apparmorProfile : apparmorProfile ,
77
70
selinux : selinux ,
78
71
traceSocket : traceSocket ,
79
72
rootless : rootless ,
80
73
}
81
74
}
82
75
76
+ type containerState struct {
77
+ done chan error
78
+ // On linux the rootfsPath is used to ensure the CWD exists, to fetch user information
79
+ // and as a bind mount for the root FS of the container.
80
+ rootfsPath string
81
+ // On Windows we need to use the root mounts to achieve the same thing that Linux does
82
+ // with rootfsPath. So we save both in details.
83
+ rootMounts []mount.Mount
84
+ }
85
+
83
86
func (w * containerdExecutor ) Run (ctx context.Context , id string , root executor.Mount , mounts []executor.Mount , process executor.ProcessInfo , started chan <- struct {}) (rec resourcestypes.Recorder , err error ) {
84
87
if id == "" {
85
88
id = identity .NewID ()
86
89
}
87
90
88
91
startedOnce := sync.Once {}
89
92
done := make (chan error , 1 )
93
+ details := & containerState {
94
+ done : done ,
95
+ }
90
96
w .mu .Lock ()
91
- w .running [id ] = done
97
+ w .running [id ] = details
92
98
w .mu .Unlock ()
93
99
defer func () {
94
100
w .mu .Lock ()
@@ -104,61 +110,19 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root executor.M
104
110
}()
105
111
106
112
meta := process .Meta
107
-
108
- resolvConf , err := oci .GetResolvConf (ctx , w .root , nil , w .dnsConfig )
109
- if err != nil {
110
- return nil , err
111
- }
112
-
113
- hostsFile , clean , err := oci .GetHostsFile (ctx , w .root , meta .ExtraHosts , nil , meta .Hostname )
114
- if err != nil {
115
- return nil , err
116
- }
117
- if clean != nil {
118
- defer clean ()
119
- }
120
-
121
- mountable , err := root .Src .Mount (ctx , false )
122
- if err != nil {
123
- return nil , err
124
- }
125
-
126
- rootMounts , release , err := mountable .Mount ()
113
+ resolvConf , hostsFile , releasers , err := w .prepareExecutionEnv (ctx , root , mounts , meta , details )
127
114
if err != nil {
128
115
return nil , err
129
116
}
130
- if release != nil {
131
- defer release ()
132
- }
133
117
134
- lm := snapshot .LocalMounterWithMounts (rootMounts )
135
- rootfsPath , err := lm .Mount ()
136
- if err != nil {
137
- return nil , err
118
+ if releasers != nil {
119
+ defer releasers ()
138
120
}
139
- defer lm .Unmount ()
140
- defer executor .MountStubsCleaner (ctx , rootfsPath , mounts , meta .RemoveMountStubsRecursive )()
141
121
142
- uid , gid , sgids , err := oci .GetUser (rootfsPath , meta .User )
143
- if err != nil {
122
+ if err := w .ensureCWD (ctx , details , meta ); err != nil {
144
123
return nil , err
145
124
}
146
125
147
- identity := idtools.Identity {
148
- UID : int (uid ),
149
- GID : int (gid ),
150
- }
151
-
152
- newp , err := fs .RootPath (rootfsPath , meta .Cwd )
153
- if err != nil {
154
- return nil , errors .Wrapf (err , "working dir %s points to invalid target" , newp )
155
- }
156
- if _ , err := os .Stat (newp ); err != nil {
157
- if err := idtools .MkdirAllAndChown (newp , 0755 , identity ); err != nil {
158
- return nil , errors .Wrapf (err , "failed to create working directory %s" , newp )
159
- }
160
- }
161
-
162
126
provider , ok := w .networkProviders [meta .NetMode ]
163
127
if ! ok {
164
128
return nil , errors .Errorf ("unknown network mode %s" , meta .NetMode )
@@ -173,22 +137,12 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root executor.M
173
137
bklog .G (ctx ).Info ("enabling HostNetworking" )
174
138
}
175
139
176
- opts := []containerdoci.SpecOpts {oci .WithUIDGID (uid , gid , sgids )}
177
- if meta .ReadonlyRootFS {
178
- opts = append (opts , containerdoci .WithRootFSReadonly ())
179
- }
180
-
181
- processMode := oci .ProcessSandbox // FIXME(AkihiroSuda)
182
- spec , cleanup , err := oci .GenerateSpec (ctx , meta , mounts , id , resolvConf , hostsFile , namespace , w .cgroupParent , processMode , nil , w .apparmorProfile , w .selinux , w .traceSocket , opts ... )
140
+ spec , releaseSpec , err := w .createOCISpec (ctx , id , resolvConf , hostsFile , namespace , mounts , meta , details )
183
141
if err != nil {
184
142
return nil , err
185
143
}
186
- defer cleanup ()
187
- spec .Process .Terminal = meta .Tty
188
- if w .rootless {
189
- if err := rootlessspecconv .ToRootless (spec ); err != nil {
190
- return nil , err
191
- }
144
+ if releaseSpec != nil {
145
+ defer releaseSpec ()
192
146
}
193
147
194
148
container , err := w .client .NewContainer (ctx , id ,
@@ -210,20 +164,12 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root executor.M
210
164
cioOpts = append (cioOpts , cio .WithTerminal )
211
165
}
212
166
213
- rootfs := containerd .WithRootFS ([]mount.Mount {{
214
- Source : rootfsPath ,
215
- Type : "bind" ,
216
- Options : []string {"rbind" },
217
- }})
218
- if runtime .GOOS == "freebsd" {
219
- rootfs = containerd .WithRootFS ([]mount.Mount {{
220
- Source : rootfsPath ,
221
- Type : "nullfs" ,
222
- Options : []string {},
223
- }})
167
+ taskOpts , err := details .getTaskOpts ()
168
+ if err != nil {
169
+ return nil , err
224
170
}
225
171
226
- task , err := container .NewTask (ctx , cio .NewCreator (cioOpts ... ), rootfs )
172
+ task , err := container .NewTask (ctx , cio .NewCreator (cioOpts ... ), taskOpts )
227
173
if err != nil {
228
174
return nil , err
229
175
}
@@ -259,17 +205,16 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
259
205
// is in the process of being created and check again every 100ms or until
260
206
// context is canceled.
261
207
208
+ w .mu .Lock ()
209
+ details , ok := w .running [id ]
210
+ w .mu .Unlock ()
211
+
212
+ if ! ok {
213
+ return errors .Errorf ("container %s not found" , id )
214
+ }
262
215
var container containerd.Container
263
216
var task containerd.Task
264
217
for {
265
- w .mu .Lock ()
266
- done , ok := w .running [id ]
267
- w .mu .Unlock ()
268
-
269
- if ! ok {
270
- return errors .Errorf ("container %s not found" , id )
271
- }
272
-
273
218
if container == nil {
274
219
container , _ = w .client .LoadContainer (ctx , id )
275
220
}
@@ -285,7 +230,7 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
285
230
select {
286
231
case <- ctx .Done ():
287
232
return ctx .Err ()
288
- case err , ok := <- done :
233
+ case err , ok := <- details . done :
289
234
if ! ok || err == nil {
290
235
return errors .Errorf ("container %s has stopped" , id )
291
236
}
@@ -301,23 +246,20 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
301
246
}
302
247
303
248
proc := spec .Process
304
-
305
- // TODO how do we get rootfsPath for oci.GetUser in case user passed in username rather than uid:gid?
306
- // For now only support uid:gid
307
249
if meta .User != "" {
308
- uid , gid , err := oci . ParseUIDGID (meta .User )
250
+ userSpec , err := getUserSpec (meta .User , details . rootfsPath )
309
251
if err != nil {
310
252
return errors .WithStack (err )
311
253
}
312
- proc .User = specs.User {
313
- UID : uid ,
314
- GID : gid ,
315
- AdditionalGids : []uint32 {},
316
- }
254
+ proc .User = userSpec
317
255
}
318
256
319
257
proc .Terminal = meta .Tty
320
- proc .Args = meta .Args
258
+ // setArgs will set the proper command line arguments for this process.
259
+ // On Windows, this will set the CommandLine field. On Linux it will set the
260
+ // Args field.
261
+ setArgs (proc , meta .Args )
262
+
321
263
if meta .Cwd != "" {
322
264
spec .Process .Cwd = meta .Cwd
323
265
}
0 commit comments