diff --git a/cmd/oci-runtime-tool/generate.go b/cmd/oci-runtime-tool/generate.go index ea934fcb8..57261c52d 100644 --- a/cmd/oci-runtime-tool/generate.go +++ b/cmd/oci-runtime-tool/generate.go @@ -3,7 +3,6 @@ package main import ( "fmt" "os" - "runtime" "strconv" "strings" @@ -15,7 +14,7 @@ import ( var generateFlags = []cli.Flag{ cli.StringFlag{Name: "apparmor", Usage: "specifies the the apparmor profile for the container"}, - cli.StringFlag{Name: "arch", Value: runtime.GOARCH, Usage: "architecture the container is created for"}, + cli.StringFlag{Name: "arch", Usage: "architecture the container is created for"}, cli.StringSliceFlag{Name: "args", Usage: "command to run in the container"}, cli.StringSliceFlag{Name: "bind", Usage: "bind mount directories src:dest[:options...]"}, cli.StringSliceFlag{Name: "cap-add", Usage: "add Linux capabilities"}, @@ -52,7 +51,7 @@ var generateFlags = []cli.Flag{ cli.StringFlag{Name: "network", Usage: "network namespace"}, cli.BoolFlag{Name: "no-new-privileges", Usage: "set no new privileges bit for the container process"}, cli.IntFlag{Name: "oom-score-adj", Usage: "oom_score_adj for the container"}, - cli.StringFlag{Name: "os", Value: runtime.GOOS, Usage: "operating system the container is created for"}, + cli.StringFlag{Name: "os", Usage: "operating system the container is created for"}, cli.StringFlag{Name: "output", Usage: "output file (defaults to stdout)"}, cli.StringFlag{Name: "pid", Usage: "pid namespace"}, cli.StringSliceFlag{Name: "poststart", Usage: "set command to run in poststart hooks"}, @@ -92,21 +91,28 @@ var generateCommand = cli.Command{ Before: before, Action: func(context *cli.Context) error { // Start from the default template. - specgen := generate.New() + var osPointer *string + if context.IsSet("os") { + goos := context.String("os") + osPointer = &goos + } + specgen, err := generate.New(osPointer) + if err != nil { + return err + } var template string if context.IsSet("template") { template = context.String("template") } if template != "" { - var err error specgen, err = generate.NewFromFile(template) if err != nil { return err } } - err := setupSpec(&specgen, context) + err = setupSpec(&specgen, context) if err != nil { return err } @@ -141,8 +147,19 @@ func setupSpec(g *generate.Generator, context *cli.Context) error { g.SetHostname(context.String("hostname")) } - g.SetPlatformOS(context.String("os")) - g.SetPlatformArch(context.String("arch")) + if context.IsSet("os") { + err := g.SetPlatformOS(context.String("os")) + if err != nil { + return err + } + } + + if context.IsSet("arch") { + err := g.SetPlatformArch(context.String("arch")) + if err != nil { + return err + } + } if context.IsSet("label") { annotations := context.StringSlice("label") diff --git a/generate/generate.go b/generate/generate.go index 20bc601bb..ba09c2755 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -11,6 +11,7 @@ import ( rspec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate/seccomp" + "github.com/Sirupsen/logrus" "github.com/syndtr/gocapability/capability" ) @@ -30,8 +31,14 @@ type ExportOptions struct { Seccomp bool // seccomp toggles if only seccomp should be exported } -// New creates a spec Generator with the default spec. -func New() Generator { +// New creates a spec Generator with the default spec for the target +// OS (which defaults to runtime.GOOS). +func New(os *string) (generator Generator, err error) { + var goos string + goos = runtime.GOOS + if os == nil { + os = &goos + } spec := rspec.Spec{ Version: rspec.Version, Platform: rspec.Platform{ @@ -44,41 +51,45 @@ func New() Generator { }, Process: rspec.Process{ Terminal: false, - User: rspec.User{}, Args: []string{ "sh", }, - Env: []string{ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - "TERM=xterm", - }, - Cwd: "/", - Capabilities: []string{ - "CAP_CHOWN", - "CAP_DAC_OVERRIDE", - "CAP_FSETID", - "CAP_FOWNER", - "CAP_MKNOD", - "CAP_NET_RAW", - "CAP_SETGID", - "CAP_SETUID", - "CAP_SETFCAP", - "CAP_SETPCAP", - "CAP_NET_BIND_SERVICE", - "CAP_SYS_CHROOT", - "CAP_KILL", - "CAP_AUDIT_WRITE", - }, - Rlimits: []rspec.Rlimit{ - { - Type: "RLIMIT_NOFILE", - Hard: uint64(1024), - Soft: uint64(1024), - }, - }, }, Hostname: "mrsdalloway", - Mounts: []rspec.Mount{ + } + + if *os == "linux" { + spec.Process.User = rspec.User{} + spec.Process.Env = []string{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "TERM=xterm", + } + spec.Process.Cwd = "/" + spec.Process.Capabilities = []string{ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE", + } + spec.Process.Rlimits = []rspec.Rlimit{ + { + Type: "RLIMIT_NOFILE", + Hard: uint64(1024), + Soft: uint64(1024), + }, + } + + spec.Mounts = []rspec.Mount{ { Destination: "/proc", Type: "proc", @@ -115,8 +126,9 @@ func New() Generator { Source: "sysfs", Options: []string{"nosuid", "noexec", "nodev", "ro"}, }, - }, - Linux: &rspec.Linux{ + } + + spec.Linux = &rspec.Linux{ Resources: &rspec.Resources{ Devices: []rspec.DeviceCgroup{ { @@ -143,12 +155,15 @@ func New() Generator { }, }, Devices: []rspec.Device{}, - }, + } + } else { + return generator, fmt.Errorf("no defaults configured for %s", *os) } spec.Linux.Seccomp = seccomp.DefaultProfile(&spec) - return Generator{ + generator = Generator{ spec: &spec, } + return generator, nil } // NewFromSpec creates a spec Generator from a given spec. @@ -270,15 +285,23 @@ func (g *Generator) RemoveAnnotation(key string) { } // SetPlatformOS sets g.spec.Process.OS. -func (g *Generator) SetPlatformOS(os string) { +func (g *Generator) SetPlatformOS(os string) error { + if g.HostSpecific && os != runtime.GOOS { + return fmt.Errorf("cannot set platform.os to %s on a %s host", os, runtime.GOOS) + } g.initSpec() g.spec.Platform.OS = os + return nil } // SetPlatformArch sets g.spec.Platform.Arch. -func (g *Generator) SetPlatformArch(arch string) { +func (g *Generator) SetPlatformArch(arch string) error { + if g.HostSpecific && arch != runtime.GOARCH { + logrus.Warnf("setting platform.arch to %s on a %s host", arch, runtime.GOARCH) + } g.initSpec() g.spec.Platform.Arch = arch + return nil } // SetProcessUID sets g.spec.Process.User.UID. diff --git a/man/oci-runtime-tool-generate.1.md b/man/oci-runtime-tool-generate.1.md index aba61aa4c..2102dc008 100644 --- a/man/oci-runtime-tool-generate.1.md +++ b/man/oci-runtime-tool-generate.1.md @@ -173,7 +173,13 @@ read the configuration from `config.json`. Specifies oom_score_adj for the container. **--os**=OS - Operating system used within the container + Operating system used within the container. When no template is + set, the default is to use the `GOOS` ocitools was built with and to + default other settings based on this setting. For example, if + `GOOS` is `linux`, `ocitools generate` will generate a default Linux + config, `ocitools generate --os=windows` will generate a default + Windows config, and `ocitools --host-specific generate --os=windows` + will exit with an error. **--output**=PATH Instead of writing the configuration JSON to stdout, write it to a diff --git a/validate/validate.go b/validate/validate.go index bce44d974..274feaa56 100644 --- a/validate/validate.go +++ b/validate/validate.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "reflect" + "runtime" "strings" "unicode" "unicode/utf8" @@ -164,6 +165,16 @@ func (v *Validator) CheckPlatform() (msgs []string) { } msgs = append(msgs, fmt.Sprintf("Operation system %q of the bundle is not supported yet.", platform.OS)) + if hostCheck { + if platform.OS != runtime.GOOS { + msgs = append(msgs, fmt.Sprintf("platform.os is %s, not %s", platform.OS, runtime.GOOS)) + } + if platform.Arch != runtime.GOARCH { + // warning, not an error, since kernels can support multiple architectures + msgs = append(msgs, fmt.Sprintf("platform.arch is %s, not %s", platform.Arch, runtime.GOARCH)) + } + } + return }