Skip to content

Commit 6044313

Browse files
authored
Merge pull request containerd#7960 from mxpv/darwin
Add basic spec and mounts for Darwin
2 parents 76585e1 + 1ade777 commit 6044313

7 files changed

+201
-32
lines changed

oci/spec.go

+18-5
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ import (
2121
"path/filepath"
2222
"runtime"
2323

24-
"github.com/containerd/containerd/namespaces"
25-
"github.com/containerd/containerd/platforms"
24+
"github.com/opencontainers/runtime-spec/specs-go"
2625

2726
"github.com/containerd/containerd/containers"
28-
specs "github.com/opencontainers/runtime-spec/specs-go"
27+
"github.com/containerd/containerd/namespaces"
28+
"github.com/containerd/containerd/platforms"
2929
)
3030

3131
const (
@@ -66,15 +66,19 @@ func generateDefaultSpecWithPlatform(ctx context.Context, platform, id string, s
6666
return err
6767
}
6868

69-
if plat.OS == "windows" {
69+
switch plat.OS {
70+
case "windows":
7071
err = populateDefaultWindowsSpec(ctx, s, id)
71-
} else {
72+
case "darwin":
73+
err = populateDefaultDarwinSpec(s)
74+
default:
7275
err = populateDefaultUnixSpec(ctx, s, id)
7376
if err == nil && runtime.GOOS == "windows" {
7477
// To run LCOW we have a Linux and Windows section. Add an empty one now.
7578
s.Windows = &specs.Windows{}
7679
}
7780
}
81+
7882
return err
7983
}
8084

@@ -207,3 +211,12 @@ func populateDefaultWindowsSpec(ctx context.Context, s *Spec, id string) error {
207211
}
208212
return nil
209213
}
214+
215+
func populateDefaultDarwinSpec(s *Spec) error {
216+
*s = Spec{
217+
Version: specs.Version,
218+
Root: &specs.Root{},
219+
Process: &specs.Process{Cwd: "/"},
220+
}
221+
return nil
222+
}

oci/spec_opts_test.go

+15-9
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,16 @@ import (
3030
"strings"
3131
"testing"
3232

33+
"github.com/opencontainers/go-digest"
34+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
35+
"github.com/opencontainers/runtime-spec/specs-go"
3336
"github.com/stretchr/testify/assert"
3437
"github.com/stretchr/testify/require"
3538

39+
"github.com/containerd/containerd/containers"
3640
"github.com/containerd/containerd/content"
3741
"github.com/containerd/containerd/errdefs"
38-
"github.com/opencontainers/go-digest"
39-
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
40-
41-
"github.com/containerd/containerd/containers"
4242
"github.com/containerd/containerd/namespaces"
43-
"github.com/opencontainers/runtime-spec/specs-go"
4443
)
4544

4645
type blob []byte
@@ -302,13 +301,20 @@ func TestWithDefaultSpec(t *testing.T) {
302301
t.Fatal(err)
303302
}
304303

305-
var expected Spec
306-
var err error
307-
if runtime.GOOS == "windows" {
304+
var (
305+
expected Spec
306+
err error
307+
)
308+
309+
switch runtime.GOOS {
310+
case "windows":
308311
err = populateDefaultWindowsSpec(ctx, &expected, c.ID)
309-
} else {
312+
case "darwin":
313+
err = populateDefaultDarwinSpec(&expected)
314+
default:
310315
err = populateDefaultUnixSpec(ctx, &expected, c.ID)
311316
}
317+
312318
if err != nil {
313319
t.Fatal(err)
314320
}

oci/spec_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ import (
2121
"runtime"
2222
"testing"
2323

24+
"github.com/opencontainers/runtime-spec/specs-go"
25+
2426
"github.com/containerd/containerd/containers"
2527
"github.com/containerd/containerd/namespaces"
2628
"github.com/containerd/containerd/pkg/testutil"
27-
specs "github.com/opencontainers/runtime-spec/specs-go"
2829
)
2930

3031
func TestGenerateSpec(t *testing.T) {
@@ -39,7 +40,7 @@ func TestGenerateSpec(t *testing.T) {
3940
t.Fatal("GenerateSpec() returns a nil spec")
4041
}
4142

42-
if runtime.GOOS != "windows" {
43+
if runtime.GOOS == "linux" {
4344
// check for matching caps
4445
defaults := defaultUnixCaps()
4546
for _, cl := range [][]string{
@@ -61,9 +62,9 @@ func TestGenerateSpec(t *testing.T) {
6162
t.Errorf("ns at %d does not match set %q != %q", i, defaultNS[i], ns)
6263
}
6364
}
64-
} else {
65+
} else if runtime.GOOS == "windows" {
6566
if s.Windows == nil {
66-
t.Fatal("Windows section of spec not filled in on Windows platform")
67+
t.Fatal("Windows section of spec not filled in for Windows spec")
6768
}
6869
}
6970

@@ -122,7 +123,7 @@ func TestSpecWithTTY(t *testing.T) {
122123
if !s.Process.Terminal {
123124
t.Error("terminal net set WithTTY()")
124125
}
125-
if runtime.GOOS != "windows" {
126+
if runtime.GOOS == "linux" {
126127
v := s.Process.Env[len(s.Process.Env)-1]
127128
if v != "TERM=xterm" {
128129
t.Errorf("xterm not set in env for TTY")

pkg/cri/opts/spec_darwin_opts.go

+119
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
Copyright The containerd Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package opts
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"os"
23+
"path/filepath"
24+
"sort"
25+
26+
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
27+
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
28+
29+
"github.com/containerd/containerd/containers"
30+
"github.com/containerd/containerd/oci"
31+
osinterface "github.com/containerd/containerd/pkg/os"
32+
)
33+
34+
// WithDarwinMounts adds mounts from CRI's container config + extra mounts.
35+
func WithDarwinMounts(osi osinterface.OS, config *runtime.ContainerConfig, extra []*runtime.Mount) oci.SpecOpts {
36+
return func(ctx context.Context, client oci.Client, container *containers.Container, s *oci.Spec) error {
37+
// mergeMounts merge CRI mounts with extra mounts. If a mount destination
38+
// is mounted by both a CRI mount and an extra mount, the CRI mount will
39+
// be kept.
40+
var (
41+
criMounts = config.GetMounts()
42+
mounts = append([]*runtime.Mount{}, criMounts...)
43+
)
44+
45+
// Copy all mounts from extra mounts, except for mounts overridden by CRI.
46+
for _, e := range extra {
47+
found := false
48+
for _, c := range criMounts {
49+
if cleanMount(e.ContainerPath) == cleanMount(c.ContainerPath) {
50+
found = true
51+
break
52+
}
53+
}
54+
if !found {
55+
mounts = append(mounts, e)
56+
}
57+
}
58+
59+
// Sort mounts in number of parts. This ensures that high level mounts don't
60+
// shadow other mounts.
61+
sort.Sort(orderedMounts(mounts))
62+
63+
// Copy all mounts from default mounts, except for
64+
// - mounts overridden by supplied mount;
65+
mountSet := make(map[string]struct{})
66+
for _, m := range mounts {
67+
mountSet[filepath.Clean(m.ContainerPath)] = struct{}{}
68+
}
69+
70+
defaultMounts := s.Mounts
71+
s.Mounts = nil
72+
73+
for _, m := range defaultMounts {
74+
dst := cleanMount(m.Destination)
75+
if _, ok := mountSet[dst]; ok {
76+
// filter out mount overridden by a supplied mount
77+
continue
78+
}
79+
s.Mounts = append(s.Mounts, m)
80+
}
81+
82+
for _, mount := range mounts {
83+
var (
84+
dst = mount.GetContainerPath()
85+
src = mount.GetHostPath()
86+
)
87+
88+
// Create the host path if it doesn't exist.
89+
if _, err := osi.Stat(src); err != nil {
90+
if !os.IsNotExist(err) {
91+
return fmt.Errorf("failed to stat %q: %w", src, err)
92+
}
93+
if err := osi.MkdirAll(src, 0755); err != nil {
94+
return fmt.Errorf("failed to mkdir %q: %w", src, err)
95+
}
96+
}
97+
98+
src, err := osi.ResolveSymbolicLink(src)
99+
if err != nil {
100+
return fmt.Errorf("failed to resolve symlink %q: %w", src, err)
101+
}
102+
103+
var options []string
104+
if mount.GetReadonly() {
105+
options = append(options, "ro")
106+
} else {
107+
options = append(options, "rw")
108+
}
109+
110+
s.Mounts = append(s.Mounts, runtimespec.Mount{
111+
Source: src,
112+
Destination: dst,
113+
Type: "bind",
114+
Options: options,
115+
})
116+
}
117+
return nil
118+
}
119+
}

pkg/cri/sbserver/container_create.go

+23-11
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ func (c *criService) volumeMounts(containerRootDir string, criMounts []*runtime.
353353
}
354354

355355
// runtimeSpec returns a default runtime spec used in cri-containerd.
356-
func (c *criService) runtimeSpec(id string, baseSpecFile string, opts ...oci.SpecOpts) (*runtimespec.Spec, error) {
356+
func (c *criService) runtimeSpec(id string, platform platforms.Platform, baseSpecFile string, opts ...oci.SpecOpts) (*runtimespec.Spec, error) {
357357
// GenerateSpec needs namespace.
358358
ctx := ctrdutil.NamespacedContext()
359359
container := &containers.Container{ID: id}
@@ -379,7 +379,7 @@ func (c *criService) runtimeSpec(id string, baseSpecFile string, opts ...oci.Spe
379379
return &spec, nil
380380
}
381381

382-
spec, err := oci.GenerateSpec(ctx, nil, container, opts...)
382+
spec, err := oci.GenerateSpecWithPlatform(ctx, nil, platforms.Format(platform), container, opts...)
383383
if err != nil {
384384
return nil, fmt.Errorf("failed to generate spec: %w", err)
385385
}
@@ -421,14 +421,18 @@ func (c *criService) buildContainerSpec(
421421
ociRuntime config.Runtime,
422422
) (_ *runtimespec.Spec, retErr error) {
423423
var (
424+
specOpts []oci.SpecOpts
425+
err error
426+
427+
// Platform helpers
424428
isLinux = platform.OS == "linux"
425429
isWindows = platform.OS == "windows"
426430
isDarwin = platform.OS == "darwin"
427431
)
428432

429433
switch {
430434
case isLinux:
431-
return c.buildLinuxSpec(
435+
specOpts, err = c.buildLinuxSpec(
432436
id,
433437
sandboxID,
434438
sandboxPid,
@@ -442,7 +446,7 @@ func (c *criService) buildContainerSpec(
442446
ociRuntime,
443447
)
444448
case isWindows:
445-
return c.buildWindowsSpec(
449+
specOpts, err = c.buildWindowsSpec(
446450
id,
447451
sandboxID,
448452
sandboxPid,
@@ -456,7 +460,7 @@ func (c *criService) buildContainerSpec(
456460
ociRuntime,
457461
)
458462
case isDarwin:
459-
return c.buildDarwinSpec(
463+
specOpts, err = c.buildDarwinSpec(
460464
id,
461465
sandboxID,
462466
containerName,
@@ -470,6 +474,12 @@ func (c *criService) buildContainerSpec(
470474
default:
471475
return nil, fmt.Errorf("unsupported spec platform: %s", platform.OS)
472476
}
477+
478+
if err != nil {
479+
return nil, fmt.Errorf("failed to generate spec opts: %w", err)
480+
}
481+
482+
return c.runtimeSpec(id, platform, ociRuntime.BaseRuntimeSpec, specOpts...)
473483
}
474484

475485
func (c *criService) buildLinuxSpec(
@@ -484,7 +494,7 @@ func (c *criService) buildLinuxSpec(
484494
imageConfig *imagespec.ImageConfig,
485495
extraMounts []*runtime.Mount,
486496
ociRuntime config.Runtime,
487-
) (_ *runtimespec.Spec, retErr error) {
497+
) (_ []oci.SpecOpts, retErr error) {
488498
specOpts := []oci.SpecOpts{
489499
oci.WithoutRunMount,
490500
}
@@ -704,7 +714,7 @@ func (c *criService) buildLinuxSpec(
704714
specOpts = append(specOpts, oci.WithLinuxNamespace(runtimespec.LinuxNamespace{Type: runtimespec.CgroupNamespace}))
705715
}
706716

707-
return c.runtimeSpec(id, ociRuntime.BaseRuntimeSpec, specOpts...)
717+
return specOpts, nil
708718
}
709719

710720
func (c *criService) buildWindowsSpec(
@@ -719,7 +729,7 @@ func (c *criService) buildWindowsSpec(
719729
imageConfig *imagespec.ImageConfig,
720730
extraMounts []*runtime.Mount,
721731
ociRuntime config.Runtime,
722-
) (_ *runtimespec.Spec, retErr error) {
732+
) (_ []oci.SpecOpts, retErr error) {
723733
specOpts := []oci.SpecOpts{
724734
customopts.WithProcessArgs(config, imageConfig),
725735
}
@@ -807,7 +817,7 @@ func (c *criService) buildWindowsSpec(
807817
customopts.WithAnnotation(annotations.WindowsHostProcess, strconv.FormatBool(sandboxHpc)),
808818
)
809819

810-
return c.runtimeSpec(id, ociRuntime.BaseRuntimeSpec, specOpts...)
820+
return specOpts, nil
811821
}
812822

813823
func (c *criService) buildDarwinSpec(
@@ -820,7 +830,7 @@ func (c *criService) buildDarwinSpec(
820830
imageConfig *imagespec.ImageConfig,
821831
extraMounts []*runtime.Mount,
822832
ociRuntime config.Runtime,
823-
) (_ *runtimespec.Spec, retErr error) {
833+
) (_ []oci.SpecOpts, retErr error) {
824834
specOpts := []oci.SpecOpts{
825835
customopts.WithProcessArgs(config, imageConfig),
826836
}
@@ -843,6 +853,8 @@ func (c *criService) buildDarwinSpec(
843853
}
844854
specOpts = append(specOpts, oci.WithEnv(env))
845855

856+
specOpts = append(specOpts, customopts.WithDarwinMounts(c.os, config, extraMounts))
857+
846858
for pKey, pValue := range getPassthroughAnnotations(sandboxConfig.Annotations,
847859
ociRuntime.PodAnnotations) {
848860
specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
@@ -863,5 +875,5 @@ func (c *criService) buildDarwinSpec(
863875
customopts.WithAnnotation(annotations.ImageName, imageName),
864876
)
865877

866-
return c.runtimeSpec(id, ociRuntime.BaseRuntimeSpec, specOpts...)
878+
return specOpts, nil
867879
}

0 commit comments

Comments
 (0)