66 "os"
77 "path/filepath"
88 "runtime"
9+ "strings"
910 "sync"
1011 "syscall"
1112 "time"
@@ -17,19 +18,13 @@ import (
1718 "github.com/containerd/containerd"
1819 "github.com/containerd/containerd/cio"
1920 "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"
2321 "github.com/moby/buildkit/executor"
2422 "github.com/moby/buildkit/executor/oci"
2523 resourcestypes "github.com/moby/buildkit/executor/resources/types"
2624 gatewayapi "github.com/moby/buildkit/frontend/gateway/pb"
2725 "github.com/moby/buildkit/identity"
28- "github.com/moby/buildkit/snapshot"
2926 "github.com/moby/buildkit/solver/pb"
3027 "github.com/moby/buildkit/util/network"
31- rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv"
32- "github.com/opencontainers/runtime-spec/specs-go"
3328 "github.com/pkg/errors"
3429)
3530
@@ -39,7 +34,7 @@ type containerdExecutor struct {
3934 networkProviders map [pb.NetMode ]network.Provider
4035 cgroupParent string
4136 dnsConfig * oci.DNSConfig
42- running map [string ]chan error
37+ running map [string ]* jobDetails
4338 mu sync.Mutex
4439 apparmorProfile string
4540 selinux bool
@@ -72,23 +67,36 @@ func New(client *containerd.Client, root, cgroup string, networkProviders map[pb
7267 networkProviders : networkProviders ,
7368 cgroupParent : cgroup ,
7469 dnsConfig : dnsConfig ,
75- running : make (map [string ]chan error ),
70+ running : make (map [string ]* jobDetails ),
7671 apparmorProfile : apparmorProfile ,
7772 selinux : selinux ,
7873 traceSocket : traceSocket ,
7974 rootless : rootless ,
8075 }
8176}
8277
78+ type jobDetails struct {
79+ done chan error
80+ // On linux the rootfsPath is used to ensure the CWD exists, to fetch user information
81+ // and as a bind mount for the root FS of the container.
82+ rootfsPath string
83+ // On Windows we need to use the root mounts to achieve the same thing that Linux does
84+ // with rootfsPath. So we save both in details.
85+ rootMounts []mount.Mount
86+ }
87+
8388func (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 ) {
8489 if id == "" {
8590 id = identity .NewID ()
8691 }
8792
8893 startedOnce := sync.Once {}
8994 done := make (chan error , 1 )
95+ details := & jobDetails {
96+ done : done ,
97+ }
9098 w .mu .Lock ()
91- w .running [id ] = done
99+ w .running [id ] = details
92100 w .mu .Unlock ()
93101 defer func () {
94102 w .mu .Lock ()
@@ -104,60 +112,16 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root executor.M
104112 }()
105113
106114 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 )
115+ releasers , resolvConf , hostsFile , err := w .prepareExecutionEnv (ctx , root , mounts , meta , details )
122116 if err != nil {
117+ releasers ()
123118 return nil , err
124119 }
120+ defer releasers ()
125121
126- rootMounts , release , err := mountable .Mount ()
127- if err != nil {
122+ if err := w .ensureCWD (ctx , details , meta ); err != nil {
128123 return nil , err
129124 }
130- if release != nil {
131- defer release ()
132- }
133-
134- lm := snapshot .LocalMounterWithMounts (rootMounts )
135- rootfsPath , err := lm .Mount ()
136- if err != nil {
137- return nil , err
138- }
139- defer lm .Unmount ()
140- defer executor .MountStubsCleaner (ctx , rootfsPath , mounts , meta .RemoveMountStubsRecursive )()
141-
142- uid , gid , sgids , err := oci .GetUser (rootfsPath , meta .User )
143- if err != nil {
144- return nil , err
145- }
146-
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- }
161125
162126 provider , ok := w .networkProviders [meta .NetMode ]
163127 if ! ok {
@@ -173,23 +137,12 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root executor.M
173137 bklog .G (ctx ).Info ("enabling HostNetworking" )
174138 }
175139
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 , specReleasers , err := w .getOCISpec (ctx , id , resolvConf , hostsFile , namespace , mounts , meta , details )
183141 if err != nil {
142+ specReleasers ()
184143 return nil , err
185144 }
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- }
192- }
145+ defer specReleasers ()
193146
194147 container , err := w .client .NewContainer (ctx , id ,
195148 containerd .WithSpec (spec ),
@@ -210,20 +163,12 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root executor.M
210163 cioOpts = append (cioOpts , cio .WithTerminal )
211164 }
212165
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- }})
166+ taskOpts , err := w .getTaskOpts (ctx , details )
167+ if err != nil {
168+ return nil , err
224169 }
225170
226- task , err := container .NewTask (ctx , cio .NewCreator (cioOpts ... ), rootfs )
171+ task , err := container .NewTask (ctx , cio .NewCreator (cioOpts ... ), taskOpts )
227172 if err != nil {
228173 return nil , err
229174 }
@@ -259,17 +204,16 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
259204 // is in the process of being created and check again every 100ms or until
260205 // context is canceled.
261206
207+ w .mu .Lock ()
208+ details , ok := w .running [id ]
209+ w .mu .Unlock ()
210+
211+ if ! ok {
212+ return errors .Errorf ("container %s not found" , id )
213+ }
262214 var container containerd.Container
263215 var task containerd.Task
264216 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-
273217 if container == nil {
274218 container , _ = w .client .LoadContainer (ctx , id )
275219 }
@@ -285,7 +229,7 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
285229 select {
286230 case <- ctx .Done ():
287231 return ctx .Err ()
288- case err , ok := <- done :
232+ case err , ok := <- details . done :
289233 if ! ok || err == nil {
290234 return errors .Errorf ("container %s has stopped" , id )
291235 }
@@ -301,23 +245,24 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
301245 }
302246
303247 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
307248 if meta .User != "" {
308- uid , gid , err := oci . ParseUIDGID (meta .User )
249+ userSpec , err := getUserSpec (meta .User , details . rootfsPath )
309250 if err != nil {
310251 return errors .WithStack (err )
311252 }
312- proc .User = specs.User {
313- UID : uid ,
314- GID : gid ,
315- AdditionalGids : []uint32 {},
316- }
253+ proc .User = userSpec
317254 }
318255
319256 proc .Terminal = meta .Tty
320- proc .Args = meta .Args
257+
258+ if runtime .GOOS == "windows" {
259+ // On Windows passing in Args will lead to double escaping by hcsshim, which leads to errors.
260+ // The recommendation is to use CommandLine.
261+ proc .CommandLine = strings .Join (meta .Args , " " )
262+ } else {
263+ proc .Args = meta .Args
264+ }
265+
321266 if meta .Cwd != "" {
322267 spec .Process .Cwd = meta .Cwd
323268 }
0 commit comments