Skip to content

Commit 05eb728

Browse files
authored
Merge pull request moby#3517 from gabriel-samfira/enable-windows-executor
[Windows] Implement executor on Windows
2 parents 6ae419d + cc5657c commit 05eb728

File tree

8 files changed

+542
-108
lines changed

8 files changed

+542
-108
lines changed

executor/containerdexecutor/executor.go

Lines changed: 42 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"io"
66
"os"
77
"path/filepath"
8-
"runtime"
98
"sync"
109
"syscall"
1110
"time"
@@ -17,19 +16,13 @@ import (
1716
"github.com/containerd/containerd"
1817
"github.com/containerd/containerd/cio"
1918
"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"
2319
"github.com/moby/buildkit/executor"
2420
"github.com/moby/buildkit/executor/oci"
2521
resourcestypes "github.com/moby/buildkit/executor/resources/types"
2622
gatewayapi "github.com/moby/buildkit/frontend/gateway/pb"
2723
"github.com/moby/buildkit/identity"
28-
"github.com/moby/buildkit/snapshot"
2924
"github.com/moby/buildkit/solver/pb"
3025
"github.com/moby/buildkit/util/network"
31-
rootlessspecconv "github.com/moby/buildkit/util/rootless/specconv"
32-
"github.com/opencontainers/runtime-spec/specs-go"
3326
"github.com/pkg/errors"
3427
)
3528

@@ -39,7 +32,7 @@ type containerdExecutor struct {
3932
networkProviders map[pb.NetMode]network.Provider
4033
cgroupParent string
4134
dnsConfig *oci.DNSConfig
42-
running map[string]chan error
35+
running map[string]*containerState
4336
mu sync.Mutex
4437
apparmorProfile string
4538
selinux bool
@@ -72,23 +65,36 @@ func New(client *containerd.Client, root, cgroup string, networkProviders map[pb
7265
networkProviders: networkProviders,
7366
cgroupParent: cgroup,
7467
dnsConfig: dnsConfig,
75-
running: make(map[string]chan error),
68+
running: make(map[string]*containerState),
7669
apparmorProfile: apparmorProfile,
7770
selinux: selinux,
7871
traceSocket: traceSocket,
7972
rootless: rootless,
8073
}
8174
}
8275

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+
8386
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) {
8487
if id == "" {
8588
id = identity.NewID()
8689
}
8790

8891
startedOnce := sync.Once{}
8992
done := make(chan error, 1)
93+
details := &containerState{
94+
done: done,
95+
}
9096
w.mu.Lock()
91-
w.running[id] = done
97+
w.running[id] = details
9298
w.mu.Unlock()
9399
defer func() {
94100
w.mu.Lock()
@@ -104,61 +110,19 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root executor.M
104110
}()
105111

106112
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)
127114
if err != nil {
128115
return nil, err
129116
}
130-
if release != nil {
131-
defer release()
132-
}
133117

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()
138120
}
139-
defer lm.Unmount()
140-
defer executor.MountStubsCleaner(ctx, rootfsPath, mounts, meta.RemoveMountStubsRecursive)()
141121

142-
uid, gid, sgids, err := oci.GetUser(rootfsPath, meta.User)
143-
if err != nil {
122+
if err := w.ensureCWD(ctx, details, meta); err != nil {
144123
return nil, err
145124
}
146125

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-
162126
provider, ok := w.networkProviders[meta.NetMode]
163127
if !ok {
164128
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
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, releaseSpec, err := w.createOCISpec(ctx, id, resolvConf, hostsFile, namespace, mounts, meta, details)
183141
if err != nil {
184142
return nil, err
185143
}
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()
192146
}
193147

194148
container, err := w.client.NewContainer(ctx, id,
@@ -210,20 +164,12 @@ func (w *containerdExecutor) Run(ctx context.Context, id string, root executor.M
210164
cioOpts = append(cioOpts, cio.WithTerminal)
211165
}
212166

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
224170
}
225171

226-
task, err := container.NewTask(ctx, cio.NewCreator(cioOpts...), rootfs)
172+
task, err := container.NewTask(ctx, cio.NewCreator(cioOpts...), taskOpts)
227173
if err != nil {
228174
return nil, err
229175
}
@@ -259,17 +205,16 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
259205
// is in the process of being created and check again every 100ms or until
260206
// context is canceled.
261207

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+
}
262215
var container containerd.Container
263216
var task containerd.Task
264217
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-
273218
if container == nil {
274219
container, _ = w.client.LoadContainer(ctx, id)
275220
}
@@ -285,7 +230,7 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
285230
select {
286231
case <-ctx.Done():
287232
return ctx.Err()
288-
case err, ok := <-done:
233+
case err, ok := <-details.done:
289234
if !ok || err == nil {
290235
return errors.Errorf("container %s has stopped", id)
291236
}
@@ -301,23 +246,20 @@ func (w *containerdExecutor) Exec(ctx context.Context, id string, process execut
301246
}
302247

303248
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
307249
if meta.User != "" {
308-
uid, gid, err := oci.ParseUIDGID(meta.User)
250+
userSpec, err := getUserSpec(meta.User, details.rootfsPath)
309251
if err != nil {
310252
return errors.WithStack(err)
311253
}
312-
proc.User = specs.User{
313-
UID: uid,
314-
GID: gid,
315-
AdditionalGids: []uint32{},
316-
}
254+
proc.User = userSpec
317255
}
318256

319257
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+
321263
if meta.Cwd != "" {
322264
spec.Process.Cwd = meta.Cwd
323265
}

0 commit comments

Comments
 (0)