1
2
3 package main
4
5 import (
6 "context"
7 "fmt"
8 "os"
9 "path/filepath"
10 "strings"
11 "sync"
12
13 "github.com/Microsoft/hcsshim/internal/log"
14 "github.com/Microsoft/hcsshim/internal/oci"
15 "github.com/Microsoft/hcsshim/internal/uvm"
16 "github.com/Microsoft/hcsshim/osversion"
17 "github.com/Microsoft/hcsshim/pkg/annotations"
18 eventstypes "github.com/containerd/containerd/api/events"
19 "github.com/containerd/containerd/errdefs"
20 "github.com/containerd/containerd/runtime"
21 "github.com/containerd/containerd/runtime/v2/task"
22 "github.com/opencontainers/runtime-spec/specs-go"
23 "github.com/pkg/errors"
24 "golang.org/x/sync/errgroup"
25 )
26
27
28
29
30 type shimPod interface {
31
32 ID() string
33
34
35
36
37
38 CreateTask(ctx context.Context, req *task.CreateTaskRequest, s *specs.Spec) (shimTask, error)
39
40
41
42 GetTask(tid string) (shimTask, error)
43
44
45
46 ListTasks() ([]shimTask, error)
47
48
49
50
51
52
53
54
55
56
57
58
59
60 KillTask(ctx context.Context, tid, eid string, signal uint32, all bool) error
61
62
63
64
65
66
67
68 DeleteTask(ctx context.Context, tid string) error
69 }
70
71 func createPod(ctx context.Context, events publisher, req *task.CreateTaskRequest, s *specs.Spec) (_ shimPod, err error) {
72 log.G(ctx).WithField("tid", req.ID).Debug("createPod")
73
74 if osversion.Build() < osversion.RS5 {
75 return nil, errors.Wrapf(errdefs.ErrFailedPrecondition, "pod support is not available on Windows versions previous to RS5 (%d)", osversion.RS5)
76 }
77
78 ct, sid, err := oci.GetSandboxTypeAndID(s.Annotations)
79 if err != nil {
80 return nil, err
81 }
82 if ct != oci.KubernetesContainerTypeSandbox {
83 return nil, errors.Wrapf(
84 errdefs.ErrFailedPrecondition,
85 "expected annotation: '%s': '%s' got '%s'",
86 annotations.KubernetesContainerType,
87 oci.KubernetesContainerTypeSandbox,
88 ct)
89 }
90 if sid != req.ID {
91 return nil, errors.Wrapf(
92 errdefs.ErrFailedPrecondition,
93 "expected annotation '%s': '%s' got '%s'",
94 annotations.KubernetesSandboxID,
95 req.ID,
96 sid)
97 }
98
99 owner := filepath.Base(os.Args[0])
100 isWCOW := oci.IsWCOW(s)
101
102 p := pod{
103 events: events,
104 id: req.ID,
105 spec: s,
106 }
107
108 var parent *uvm.UtilityVM
109 var lopts *uvm.OptionsLCOW
110 if oci.IsIsolated(s) {
111
112 opts, err := oci.SpecToUVMCreateOpts(ctx, s, fmt.Sprintf("%s@vm", req.ID), owner)
113 if err != nil {
114 return nil, err
115 }
116 switch opts.(type) {
117 case *uvm.OptionsLCOW:
118 lopts = (opts).(*uvm.OptionsLCOW)
119 lopts.BundleDirectory = req.Bundle
120 parent, err = uvm.CreateLCOW(ctx, lopts)
121 if err != nil {
122 return nil, err
123 }
124 case *uvm.OptionsWCOW:
125 wopts := (opts).(*uvm.OptionsWCOW)
126
127
128
129
130 layersLen := len(s.Windows.LayerFolders)
131 layers := make([]string, layersLen)
132 copy(layers, s.Windows.LayerFolders)
133
134 vmPath := filepath.Join(layers[layersLen-1], "vm")
135 err := os.MkdirAll(vmPath, 0)
136 if err != nil {
137 return nil, err
138 }
139 layers[layersLen-1] = vmPath
140 wopts.LayerFolders = layers
141
142 parent, err = uvm.CreateWCOW(ctx, wopts)
143 if err != nil {
144 return nil, err
145 }
146 }
147 err = parent.Start(ctx)
148 if err != nil {
149 parent.Close()
150 return nil, err
151 }
152
153 } else if oci.IsJobContainer(s) {
154
155 p.sandboxTask = newWcowPodSandboxTask(ctx, events, req.ID, req.Bundle, parent, "")
156 if err := events.publishEvent(
157 ctx,
158 runtime.TaskCreateEventTopic,
159 &eventstypes.TaskCreate{
160 ContainerID: req.ID,
161 Bundle: req.Bundle,
162 Rootfs: req.Rootfs,
163 IO: &eventstypes.TaskIO{
164 Stdin: req.Stdin,
165 Stdout: req.Stdout,
166 Stderr: req.Stderr,
167 Terminal: req.Terminal,
168 },
169 Checkpoint: "",
170 Pid: 0,
171 }); err != nil {
172 return nil, err
173 }
174 p.jobContainer = true
175 return &p, nil
176 } else if !isWCOW {
177 return nil, errors.Wrap(errdefs.ErrFailedPrecondition, "oci spec does not contain WCOW or LCOW spec")
178 }
179
180 defer func() {
181
182 if err != nil && parent != nil {
183 parent.Close()
184 }
185 }()
186
187 p.host = parent
188 if parent != nil {
189 cid := req.ID
190 if id, ok := s.Annotations[annotations.NcproxyContainerID]; ok {
191 cid = id
192 }
193 caAddr := fmt.Sprintf(uvm.ComputeAgentAddrFmt, cid)
194 if err := parent.CreateAndAssignNetworkSetup(ctx, caAddr, cid); err != nil {
195 return nil, err
196 }
197 }
198
199
200
201
202
203
204
205
206
207
208
209 nsid := ""
210 if isWCOW && parent != nil {
211 if s.Windows != nil && s.Windows.Network != nil {
212 nsid = s.Windows.Network.NetworkNamespace
213 }
214
215 if nsid != "" {
216 if err := parent.ConfigureNetworking(ctx, nsid); err != nil {
217 return nil, errors.Wrapf(err, "failed to setup networking for pod %q", req.ID)
218 }
219 }
220 p.sandboxTask = newWcowPodSandboxTask(ctx, events, req.ID, req.Bundle, parent, nsid)
221
222
223 if err := events.publishEvent(
224 ctx,
225 runtime.TaskCreateEventTopic,
226 &eventstypes.TaskCreate{
227 ContainerID: req.ID,
228 Bundle: req.Bundle,
229 Rootfs: req.Rootfs,
230 IO: &eventstypes.TaskIO{
231 Stdin: req.Stdin,
232 Stdout: req.Stdout,
233 Stderr: req.Stderr,
234 Terminal: req.Terminal,
235 },
236 Checkpoint: "",
237 Pid: 0,
238 }); err != nil {
239 return nil, err
240 }
241 } else {
242 if isWCOW {
243 defaultArgs := "c:\\windows\\system32\\cmd.exe"
244
245
246
247
248
249
250
251 if (len(s.Process.Args) == 1 && strings.EqualFold(s.Process.Args[0], defaultArgs)) ||
252 strings.EqualFold(s.Process.CommandLine, defaultArgs) {
253 log.G(ctx).Warning("Detected CMD override for pause container entrypoint." +
254 "Please consider switching to a pause image with an explicit cmd set")
255 s.Process.CommandLine = "cmd /c ping -t 127.0.0.1 > nul"
256 }
257 }
258
259
260 lt, err := newHcsTask(ctx, events, parent, true, req, s)
261 if err != nil {
262 return nil, err
263 }
264 p.sandboxTask = lt
265 }
266 return &p, nil
267 }
268
269 var _ = (shimPod)(&pod{})
270
271 type pod struct {
272 events publisher
273
274
275
276 id string
277
278
279
280
281
282 sandboxTask shimTask
283
284
285
286
287 host *uvm.UtilityVM
288
289
290
291
292 jobContainer bool
293
294
295 spec *specs.Spec
296
297 workloadTasks sync.Map
298 }
299
300 func (p *pod) ID() string {
301 return p.id
302 }
303
304 func (p *pod) CreateTask(ctx context.Context, req *task.CreateTaskRequest, s *specs.Spec) (_ shimTask, err error) {
305 if req.ID == p.id {
306 return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "task with id: '%s' already exists", req.ID)
307 }
308 e, _ := p.sandboxTask.GetExec("")
309 if e.State() != shimExecStateRunning {
310 return nil, errors.Wrapf(errdefs.ErrFailedPrecondition, "task with id: '%s' cannot be created in pod: '%s' which is not running", req.ID, p.id)
311 }
312
313 _, ok := p.workloadTasks.Load(req.ID)
314 if ok {
315 return nil, errors.Wrapf(errdefs.ErrAlreadyExists, "task with id: '%s' already exists id pod: '%s'", req.ID, p.id)
316 }
317
318 if p.jobContainer {
319
320
321
322
323
324 if !oci.IsJobContainer(s) {
325 return nil, errors.New("cannot create a normal process isolated container if the pod sandbox is a job container")
326 }
327
328
329
330 oci.SandboxAnnotationsPassThrough(
331 p.spec.Annotations,
332 s.Annotations,
333 annotations.HostProcessInheritUser,
334 annotations.HostProcessRootfsLocation,
335 )
336 }
337
338 ct, sid, err := oci.GetSandboxTypeAndID(s.Annotations)
339 if err != nil {
340 return nil, err
341 }
342 if ct != oci.KubernetesContainerTypeContainer {
343 return nil, errors.Wrapf(
344 errdefs.ErrFailedPrecondition,
345 "expected annotation: '%s': '%s' got '%s'",
346 annotations.KubernetesContainerType,
347 oci.KubernetesContainerTypeContainer,
348 ct)
349 }
350 if sid != p.id {
351 return nil, errors.Wrapf(
352 errdefs.ErrFailedPrecondition,
353 "expected annotation '%s': '%s' got '%s'",
354 annotations.KubernetesSandboxID,
355 p.id,
356 sid)
357 }
358
359 st, err := newHcsTask(ctx, p.events, p.host, false, req, s)
360 if err != nil {
361 return nil, err
362 }
363
364 p.workloadTasks.Store(req.ID, st)
365 return st, nil
366 }
367
368 func (p *pod) GetTask(tid string) (shimTask, error) {
369 if tid == p.id {
370 return p.sandboxTask, nil
371 }
372 raw, loaded := p.workloadTasks.Load(tid)
373 if !loaded {
374 return nil, errors.Wrapf(errdefs.ErrNotFound, "task with id: '%s' not found", tid)
375 }
376 return raw.(shimTask), nil
377 }
378
379 func (p *pod) ListTasks() (_ []shimTask, err error) {
380 tasks := []shimTask{p.sandboxTask}
381 p.workloadTasks.Range(func(key, value interface{}) bool {
382 wt, loaded := value.(shimTask)
383 if !loaded {
384 err = fmt.Errorf("failed to load tasks %s", key)
385 return false
386 }
387 tasks = append(tasks, wt)
388
389
390 return true
391 })
392 if err != nil {
393 return nil, err
394 }
395 return tasks, nil
396 }
397
398 func (p *pod) KillTask(ctx context.Context, tid, eid string, signal uint32, all bool) error {
399 t, err := p.GetTask(tid)
400 if err != nil {
401 return err
402 }
403 if all && eid != "" {
404 return errors.Wrapf(errdefs.ErrFailedPrecondition, "cannot signal all with non empty ExecID: '%s'", eid)
405 }
406 eg := errgroup.Group{}
407 if all && tid == p.id {
408
409 p.workloadTasks.Range(func(key, value interface{}) bool {
410 wt := value.(shimTask)
411 eg.Go(func() error {
412 return wt.KillExec(ctx, eid, signal, all)
413 })
414
415
416
417 return true
418 })
419 }
420 eg.Go(func() error {
421 return t.KillExec(ctx, eid, signal, all)
422 })
423 return eg.Wait()
424 }
425
426 func (p *pod) DeleteTask(ctx context.Context, tid string) error {
427
428
429
430
431
432
433 t, err := p.GetTask(tid)
434 if err != nil {
435 return errors.Wrap(err, "could not find task to delete")
436 }
437
438 e, err := t.GetExec("")
439 if err != nil {
440 return errors.Wrap(err, "could not get initial exec")
441 }
442 if e.State() == shimExecStateRunning {
443 return errors.Wrap(errdefs.ErrFailedPrecondition, "cannot delete task with running exec")
444 }
445
446 if p.id != tid {
447 p.workloadTasks.Delete(tid)
448 }
449
450 return nil
451 }
452
View as plain text