Rename Fs fields to fs

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2015-02-03 17:44:58 -08:00
parent e48806d39d
commit 5fc19e8db5
24 changed files with 266 additions and 196 deletions

View File

@ -8,7 +8,7 @@ In the design and development of libcontainer we try to follow these principles:
* Less code is better.
* Fewer components are better. Do you really need to add one more class?
* 50 lines of straightforward, readable code is better than 10 lines of magic that nobody can understand.
* Don't do later what you can do now. "//FIXME: refactor" is not acceptable in new code.
* Don't do later what you can do now. "//TODO: refactor" is not acceptable in new code.
* When hesitating between two options, choose the one that is easier to reverse.
* "No" is temporary; "Yes" is forever. If you're not sure about a new feature, say no. You can change your mind later.
* Containers must be portable to the greatest possible number of machines. Be suspicious of any change which makes machines less interchangeable.

View File

@ -83,7 +83,7 @@ func (m *Manager) Apply(pid int) error {
if err := sys.Set(d); err != nil {
return err
}
// FIXME: Apply should, ideally, be reentrant or be broken up into a separate
// TODO: Apply should, ideally, be reentrant or be broken up into a separate
// create and join phase so that the cgroup hierarchy for a container can be
// created then join consists of writing the process pids to cgroup.procs
p, err := d.path(name)

View File

@ -26,22 +26,22 @@ type Config struct {
// This is required when using read only root filesystems. In these cases, a read/writeable path can be (bind) mounted somewhere inside the root filesystem to act as pivot.
PivotDir string `json:"pivot_dir,omitempty"`
// ReadonlyFs will remount the container's rootfs as readonly where only externally mounted
// bind mounts are writtable
ReadonlyFs bool `json:"readonly_fs,omitempty"`
// Path to a directory containing the container's root filesystem.
Rootfs string `json:"rootfs,omitempty"`
// Readonlyfs will remount the container's rootfs as readonly where only externally mounted
// bind mounts are writtable.
Readonlyfs bool `json:"readonlyfs,omitempty"`
// Mounts specify additional source and destination paths that will be mounted inside the container's
// rootfs and mount namespace if specified
Mounts []*Mount `json:"mounts,omitempty"`
// The device nodes that should be automatically created within the container upon container start. Note, make sure that the node is marked as allowed in the cgroup as well!
DeviceNodes []*Device `json:"device_nodes,omitempty"`
Devices []*Device `json:"devices,omitempty"`
MountLabel string `json:"mount_label,omitempty"`
// Pathname to container's root filesystem
RootFs string `json:"root_fs,omitempty"`
// Hostname optionally sets the container's hostname if provided
Hostname string `json:"hostname,omitempty"`

View File

@ -114,7 +114,7 @@ func TestConfigJsonFormat(t *testing.T) {
}
for _, d := range DefaultSimpleDevices {
if !containsDevice(d, container.DeviceNodes) {
if !containsDevice(d, container.Devices) {
t.Logf("expected device configuration for %s", d.Path)
t.Fail()
}
@ -163,3 +163,69 @@ func TestRemoveNamespace(t *testing.T) {
t.Fatalf("namespaces should have 0 items but reports %d", len(ns))
}
}
func TestHostUIDNoUSERNS(t *testing.T) {
config := &Config{
Namespaces: Namespaces{},
}
uid, err := config.HostUID()
if err != nil {
t.Fatal(err)
}
if uid != 0 {
t.Fatal("expected uid 0 with no USERNS but received %d", uid)
}
}
func TestHostUIDWithUSERNS(t *testing.T) {
config := &Config{
Namespaces: Namespaces{{Type: NEWUSER}},
UidMappings: []IDMap{
{
ContainerID: 0,
HostID: 1000,
Size: 1,
},
},
}
uid, err := config.HostUID()
if err != nil {
t.Fatal(err)
}
if uid != 1000 {
t.Fatal("expected uid 1000 with no USERNS but received %d", uid)
}
}
func TestHostGIDNoUSERNS(t *testing.T) {
config := &Config{
Namespaces: Namespaces{},
}
uid, err := config.HostGID()
if err != nil {
t.Fatal(err)
}
if uid != 0 {
t.Fatal("expected gid 0 with no USERNS but received %d", uid)
}
}
func TestHostGIDWithUSERNS(t *testing.T) {
config := &Config{
Namespaces: Namespaces{{Type: NEWUSER}},
GidMappings: []IDMap{
{
ContainerID: 0,
HostID: 1000,
Size: 1,
},
},
}
uid, err := config.HostGID()
if err != nil {
t.Fatal(err)
}
if uid != 1000 {
t.Fatal("expected gid 1000 with no USERNS but received %d", uid)
}
}

View File

@ -10,18 +10,28 @@ const (
)
type Device struct {
// Device type, block, char, etc.
Type rune `json:"type,omitempty"`
// It is fine if this is an empty string in the case that you are using Wildcards
// Path to the device.
Path string `json:"path,omitempty"`
// Use the wildcard constant for wildcards.
// Major is the device's major number.
Major int64 `json:"major,omitempty"`
// Use the wildcard constant for wildcards.
// Minor is the device's minor number.
Minor int64 `json:"minor,omitempty"`
// Typically just "rwm"
// Cgroup permissions format, rwm.
Permissions string `json:"permissions,omitempty"`
// The permission bits of the file's mode
// FileMode permission bits for the device.
FileMode os.FileMode `json:"file_mode,omitempty"`
// Uid of the device.
Uid uint32 `json:"uid,omitempty"`
// Gid of the device.
Gid uint32 `json:"gid,omitempty"`
}

View File

@ -52,7 +52,7 @@ func (m *Mount) bindMount(rootfs, mountLabel string) error {
return err
}
// FIXME: (crosbymichael) This does not belong here and should be done a layer above
// TODO: (crosbymichael) This does not belong here and should be done a layer above
dest, err = symlink.FollowSymlinkInScope(dest, rootfs)
if err != nil {
return err
@ -94,7 +94,7 @@ func (m *Mount) tmpfsMount(rootfs, mountLabel string) error {
dest = filepath.Join(rootfs, m.Destination)
)
// FIXME: (crosbymichael) This does not belong here and should be done a layer above
// TODO: (crosbymichael) This does not belong here and should be done a layer above
if dest, err = symlink.FollowSymlinkInScope(dest, rootfs); err != nil {
return err
}

View File

@ -20,12 +20,12 @@ type Network struct {
// Address contains the IPv4 and mask to set on the network interface
Address string `json:"address,omitempty"`
// IPv6Address contains the IPv6 and mask to set on the network interface
IPv6Address string `json:"ipv6_address,omitempty"`
// Gateway sets the gateway address that is used as the default for the interface
Gateway string `json:"gateway,omitempty"`
// IPv6Address contains the IPv6 and mask to set on the network interface
IPv6Address string `json:"ipv6_address,omitempty"`
// IPv6Gateway sets the ipv6 gateway address that is used as the default for the interface
IPv6Gateway string `json:"ipv6_gateway,omitempty"`

View File

@ -60,10 +60,10 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) {
}
func HostDevices() ([]*configs.Device, error) {
return getDeviceNodes("/dev")
return getDevices("/dev")
}
func getDeviceNodes(path string) ([]*configs.Device, error) {
func getDevices(path string) ([]*configs.Device, error) {
files, err := ioutilReadDir(path)
if err != nil {
return nil, err
@ -76,7 +76,7 @@ func getDeviceNodes(path string) ([]*configs.Device, error) {
case "pts", "shm", "fd", "mqueue":
continue
default:
sub, err := getDeviceNodes(filepath.Join(path, f.Name()))
sub, err := getDevices(filepath.Join(path, f.Name()))
if err != nil {
return nil, err
}

View File

@ -27,7 +27,7 @@ func testExecPS(t *testing.T, userns bool) {
return
}
rootfs, err := newRootFs()
rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
@ -65,7 +65,7 @@ func TestIPCPrivate(t *testing.T) {
return
}
rootfs, err := newRootFs()
rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
@ -96,7 +96,7 @@ func TestIPCHost(t *testing.T) {
return
}
rootfs, err := newRootFs()
rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
@ -128,7 +128,7 @@ func TestIPCJoinPath(t *testing.T) {
return
}
rootfs, err := newRootFs()
rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
@ -161,7 +161,7 @@ func TestIPCBadPath(t *testing.T) {
return
}
rootfs, err := newRootFs()
rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
@ -181,7 +181,7 @@ func TestRlimit(t *testing.T) {
return
}
rootfs, err := newRootFs()
rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
@ -232,7 +232,7 @@ func TestEnter(t *testing.T) {
}
defer os.RemoveAll(root)
rootfs, err := newRootFs()
rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}
@ -335,7 +335,7 @@ func TestFreeze(t *testing.T) {
}
defer os.RemoveAll(root)
rootfs, err := newRootFs()
rootfs, err := newRootfs()
if err != nil {
t.Fatal(err)
}

View File

@ -12,7 +12,7 @@ import (
// and the default setup for devices
func newTemplateConfig(rootfs string) *configs.Config {
return &configs.Config{
RootFs: rootfs,
Rootfs: rootfs,
Capabilities: []string{
"CHOWN",
"DAC_OVERRIDE",
@ -43,7 +43,7 @@ func newTemplateConfig(rootfs string) *configs.Config {
AllowedDevices: configs.DefaultAllowedDevices,
},
DeviceNodes: configs.DefaultAutoCreatedDevices,
Devices: configs.DefaultAutoCreatedDevices,
Hostname: "integration",
Env: []string{
"HOME=/root",

View File

@ -29,7 +29,7 @@ type stdBuffers struct {
}
func writeConfig(config *configs.Config) error {
f, err := os.OpenFile(filepath.Join(config.RootFs, "container.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
f, err := os.OpenFile(filepath.Join(config.Rootfs, "container.json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0700)
if err != nil {
return err
}
@ -51,8 +51,8 @@ func loadConfig() (*configs.Config, error) {
return container, nil
}
// newRootFs creates a new tmp directory and copies the busybox root filesystem
func newRootFs() (string, error) {
// newRootfs creates a new tmp directory and copies the busybox root filesystem
func newRootfs() (string, error) {
dir, err := ioutil.TempDir("", "")
if err != nil {
return "", err

View File

@ -6,17 +6,13 @@ import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"syscall"
"github.com/docker/libcontainer/apparmor"
"github.com/docker/libcontainer/cgroups"
"github.com/docker/libcontainer/configs"
"github.com/docker/libcontainer/label"
"github.com/docker/libcontainer/mount"
"github.com/docker/libcontainer/network"
"github.com/docker/libcontainer/system"
"github.com/golang/glog"
@ -106,7 +102,7 @@ func (c *linuxContainer) Start(process *Process) (int, error) {
cmd.Stdout = process.Stdout
cmd.Stderr = process.Stderr
cmd.Env = c.config.Env
cmd.Dir = c.config.RootFs
cmd.Dir = c.config.Rootfs
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
@ -161,7 +157,7 @@ func (c *linuxContainer) startNewProcess(cmd *exec.Cmd, args []string) (int, err
return -1, terr
}
// Enter cgroups.
if err := enterCgroups(c.state, pid.Pid); err != nil {
if err := c.enterCgroups(pid.Pid); err != nil {
return terminate(err)
}
encoder := json.NewEncoder(parent)
@ -398,7 +394,7 @@ func executeSetupCmd(args []string, ppid int, container *configs.Config, process
}
defer parent.Close()
command.ExtraFiles = []*os.File{child}
command.Dir = container.RootFs
command.Dir = container.Rootfs
command.Env = append(command.Env,
fmt.Sprintf("_LIBCONTAINER_INITPID=%d", ppid),
fmt.Sprintf("_LIBCONTAINER_USERNS=1"))
@ -460,120 +456,6 @@ type pid struct {
Pid int `json:"Pid"`
}
// Finalize entering into a container and execute a specified command
func InitIn(pipe *os.File) (err error) {
defer func() {
// if we have an error during the initialization of the container's init then send it back to the
// parent process in the form of an initError.
if err != nil {
// ensure that any data sent from the parent is consumed so it doesn't
// receive ECONNRESET when the child writes to the pipe.
ioutil.ReadAll(pipe)
if err := json.NewEncoder(pipe).Encode(initError{
Message: err.Error(),
}); err != nil {
panic(err)
}
}
// ensure that this pipe is always closed
pipe.Close()
}()
decoder := json.NewDecoder(pipe)
var config *configs.Config
if err := decoder.Decode(&config); err != nil {
return err
}
var process *processArgs
if err := decoder.Decode(&process); err != nil {
return err
}
if err := finalizeSetns(config); err != nil {
return err
}
if err := system.Execv(process.Args[0], process.Args[0:], config.Env); err != nil {
return err
}
panic("unreachable")
}
// finalize expects that the setns calls have been setup and that is has joined an
// existing namespace
func finalizeSetns(container *configs.Config) error {
// clear the current processes env and replace it with the environment defined on the container
if err := loadContainerEnvironment(container); err != nil {
return err
}
if err := setupRlimits(container); err != nil {
return fmt.Errorf("setup rlimits %s", err)
}
if err := finalizeNamespace(container); err != nil {
return err
}
if err := apparmor.ApplyProfile(container.AppArmorProfile); err != nil {
return fmt.Errorf("set apparmor profile %s: %s", container.AppArmorProfile, err)
}
if container.ProcessLabel != "" {
if err := label.SetProcessLabel(container.ProcessLabel); err != nil {
return err
}
}
return nil
}
// SetupContainer is run to setup mounts and networking related operations
// for a user namespace enabled process as a user namespace root doesn't
// have permissions to perform these operations.
// The setup process joins all the namespaces of user namespace enabled init
// except the user namespace, so it run as root in the root user namespace
// to perform these operations.
func SetupContainer(process *processArgs) error {
container := process.Config
networkState := process.NetworkState
// TODO : move to validation
/*
rootfs, err := utils.ResolveRootfs(container.RootFs)
if err != nil {
return err
}
*/
// clear the current processes env and replace it with the environment
// defined on the container
if err := loadContainerEnvironment(container); err != nil {
return err
}
cloneFlags := container.Namespaces.CloneFlags()
if (cloneFlags & syscall.CLONE_NEWNET) == 0 {
if len(container.Networks) != 0 || len(container.Routes) != 0 {
return fmt.Errorf("unable to apply network parameters without network namespace")
}
} else {
if err := setupNetwork(container, networkState); err != nil {
return fmt.Errorf("setup networking %s", err)
}
if err := setupRoute(container); err != nil {
return fmt.Errorf("setup route %s", err)
}
}
label.Init()
// InitializeMountNamespace() can be executed only for a new mount namespace
if (cloneFlags & syscall.CLONE_NEWNS) != 0 {
if err := mount.InitializeMountNamespace(container); err != nil {
return fmt.Errorf("setup mount namespace %s", err)
}
}
return nil
}
func enterCgroups(state *configs.State, pid int) error {
return cgroups.EnterPid(state.CgroupPaths, pid)
func (c *linuxContainer) enterCgroups(pid int) error {
return cgroups.EnterPid(c.state.CgroupPaths, pid)
}

View File

@ -141,7 +141,7 @@ func (l *linuxFactory) StartInitialization(pipefd uintptr) (err error) {
setupUserns := os.Getenv("_LIBCONTAINER_USERNS") != ""
pid := os.Getenv("_LIBCONTAINER_INITPID")
if pid != "" && !setupUserns {
return InitIn(pipe)
return initIn(pipe)
}
defer func() {
// if we have an error during the initialization of the container's init then send it back to the
@ -169,7 +169,7 @@ func (l *linuxFactory) StartInitialization(pipefd uintptr) (err error) {
return err
}
if setupUserns {
err = SetupContainer(process)
err = setupContainer(process)
if err == nil {
os.Exit(0)
} else {
@ -348,7 +348,6 @@ func (l *linuxFactory) initUserNs(uncleanRootfs string, process *processArgs) (e
if config.WorkingDir == "" {
config.WorkingDir = "/"
}
if err := setupRlimits(config); err != nil {
return fmt.Errorf("setup rlimits %s", err)
}
@ -557,3 +556,117 @@ func joinExistingNamespaces(namespaces []configs.Namespace) error {
}
return nil
}
// setupContainer is run to setup mounts and networking related operations
// for a user namespace enabled process as a user namespace root doesn't
// have permissions to perform these operations.
// The setup process joins all the namespaces of user namespace enabled init
// except the user namespace, so it run as root in the root user namespace
// to perform these operations.
func setupContainer(process *processArgs) error {
container := process.Config
networkState := process.NetworkState
// TODO : move to validation
/*
rootfs, err := utils.ResolveRootfs(container.Rootfs)
if err != nil {
return err
}
*/
// clear the current processes env and replace it with the environment
// defined on the container
if err := loadContainerEnvironment(container); err != nil {
return err
}
cloneFlags := container.Namespaces.CloneFlags()
if (cloneFlags & syscall.CLONE_NEWNET) == 0 {
if len(container.Networks) != 0 || len(container.Routes) != 0 {
return fmt.Errorf("unable to apply network parameters without network namespace")
}
} else {
if err := setupNetwork(container, networkState); err != nil {
return fmt.Errorf("setup networking %s", err)
}
if err := setupRoute(container); err != nil {
return fmt.Errorf("setup route %s", err)
}
}
label.Init()
// InitializeMountNamespace() can be executed only for a new mount namespace
if (cloneFlags & syscall.CLONE_NEWNS) != 0 {
if err := mount.InitializeMountNamespace(container); err != nil {
return fmt.Errorf("setup mount namespace %s", err)
}
}
return nil
}
// Finalize entering into a container and execute a specified command
func initIn(pipe *os.File) (err error) {
defer func() {
// if we have an error during the initialization of the container's init then send it back to the
// parent process in the form of an initError.
if err != nil {
// ensure that any data sent from the parent is consumed so it doesn't
// receive ECONNRESET when the child writes to the pipe.
ioutil.ReadAll(pipe)
if err := json.NewEncoder(pipe).Encode(initError{
Message: err.Error(),
}); err != nil {
panic(err)
}
}
// ensure that this pipe is always closed
pipe.Close()
}()
decoder := json.NewDecoder(pipe)
var config *configs.Config
if err := decoder.Decode(&config); err != nil {
return err
}
var process *processArgs
if err := decoder.Decode(&process); err != nil {
return err
}
if err := finalizeSetns(config); err != nil {
return err
}
if err := system.Execv(process.Args[0], process.Args[0:], config.Env); err != nil {
return err
}
panic("unreachable")
}
// finalize expects that the setns calls have been setup and that is has joined an
// existing namespace
func finalizeSetns(container *configs.Config) error {
// clear the current processes env and replace it with the environment defined on the container
if err := loadContainerEnvironment(container); err != nil {
return err
}
if err := setupRlimits(container); err != nil {
return fmt.Errorf("setup rlimits %s", err)
}
if err := finalizeNamespace(container); err != nil {
return err
}
if err := apparmor.ApplyProfile(container.AppArmorProfile); err != nil {
return fmt.Errorf("set apparmor profile %s: %s", container.AppArmorProfile, err)
}
if container.ProcessLabel != "" {
if err := label.SetProcessLabel(container.ProcessLabel); err != nil {
return err
}
}
return nil
}

View File

@ -86,7 +86,7 @@ func TestFactoryLoadContainer(t *testing.T) {
var (
id = "1"
expectedConfig = &configs.Config{
RootFs: "/mycontainer/root",
Rootfs: "/mycontainer/root",
}
expectedState = &configs.State{
InitPid: 1024,
@ -119,8 +119,8 @@ func TestFactoryLoadContainer(t *testing.T) {
config := container.Config()
if config.RootFs != expectedConfig.RootFs {
t.Fatalf("expected rootfs %q but received %q", expectedConfig.RootFs, config.RootFs)
if config.Rootfs != expectedConfig.Rootfs {
t.Fatalf("expected rootfs %q but received %q", expectedConfig.Rootfs, config.Rootfs)
}
lcontainer, ok := container.(*linuxContainer)

View File

@ -33,11 +33,11 @@ func InitializeMountNamespace(config *configs.Config) (err error) {
}
// apply any user specified mounts within the new mount namespace
for _, m := range config.Mounts {
if err := m.Mount(config.RootFs, config.MountLabel); err != nil {
if err := m.Mount(config.Rootfs, config.MountLabel); err != nil {
return err
}
}
if err := createDeviceNodes(config); err != nil {
if err := createDevices(config); err != nil {
return err
}
if err := setupPtmx(config); err != nil {
@ -51,21 +51,21 @@ func InitializeMountNamespace(config *configs.Config) (err error) {
return err
}
}
if err := setupDevSymlinks(config.RootFs); err != nil {
if err := setupDevSymlinks(config.Rootfs); err != nil {
return err
}
if err := syscall.Chdir(config.RootFs); err != nil {
if err := syscall.Chdir(config.Rootfs); err != nil {
return err
}
if config.NoPivotRoot {
err = msMoveRoot(config.RootFs)
err = msMoveRoot(config.Rootfs)
} else {
err = pivotRoot(config.RootFs, config.PivotDir)
err = pivotRoot(config.Rootfs, config.PivotDir)
}
if err != nil {
return err
}
if config.ReadonlyFs {
if config.Readonlyfs {
if err := setReadonly(); err != nil {
return fmt.Errorf("set readonly %s", err)
}
@ -77,7 +77,7 @@ func InitializeMountNamespace(config *configs.Config) (err error) {
// mountSystem sets up linux specific system mounts like mqueue, sys, proc, shm, and devpts
// inside the mount namespace
func mountSystem(config *configs.Config) error {
for _, m := range newSystemMounts(config.RootFs, config.MountLabel, config.RestrictSys) {
for _, m := range newSystemMounts(config.Rootfs, config.MountLabel, config.RestrictSys) {
if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) {
return fmt.Errorf("mkdirall %s %s", m.path, err)
}
@ -164,10 +164,10 @@ func reOpenDevNull(rootfs string) error {
}
// Create the device nodes in the container.
func createDeviceNodes(config *configs.Config) error {
func createDevices(config *configs.Config) error {
oldMask := syscall.Umask(0000)
for _, node := range config.DeviceNodes {
if err := createDeviceNode(config.RootFs, node); err != nil {
for _, node := range config.Devices {
if err := createDeviceNode(config.Rootfs, node); err != nil {
syscall.Umask(oldMask)
return err
}
@ -211,5 +211,5 @@ func prepareRoot(config *configs.Config) error {
if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil {
return err
}
return syscall.Mount(config.RootFs, config.RootFs, "bind", syscall.MS_BIND|syscall.MS_REC, "")
return syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "")
}

View File

@ -12,7 +12,7 @@ import (
)
func setupPtmx(config *configs.Config) error {
ptmx := filepath.Join(config.RootFs, "dev/ptmx")
ptmx := filepath.Join(config.Rootfs, "dev/ptmx")
if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
return err
}
@ -28,7 +28,7 @@ func setupPtmx(config *configs.Config) error {
if err != nil {
return err
}
return console.Setup(config.RootFs, config.Console, config.MountLabel, uid, gid)
return console.Setup(config.Rootfs, config.Console, config.MountLabel, uid, gid)
}
return nil
}

View File

@ -2,19 +2,18 @@ package libcontainer
import "io"
// Configuration for a process to be run inside a container.
// Process specifies the configuration and IO for a process inside
// a container.
type Process struct {
// The command to be run followed by any arguments.
Args []string
// Stdin is a pointer to a reader which provides the standard input stream.
// Stdout is a pointer to a writer which receives the standard output stream.
// Stderr is a pointer to a writer which receives the standard error stream.
//
// If a reader or writer is nil, the input stream is assumed to be empty and the output is
// discarded.
//
// Stdout and Stderr may refer to the same writer in which case the output is interspersed.
Stdin io.Reader
// Stdout is a pointer to a writer which receives the standard output stream.
Stdout io.Writer
// Stderr is a pointer to a writer which receives the standard error stream.
Stderr io.Writer
}

View File

@ -118,7 +118,7 @@
},
"restrict_sys": true,
"apparmor_profile": "docker-default",
"device_nodes": [
"devices": [
{
"permissions": "rwm",
"file_mode": 438,

View File

@ -117,7 +117,7 @@
"parent": "docker"
},
"restrict_sys": true,
"device_nodes": [
"devices": [
{
"permissions": "rwm",
"file_mode": 438,

View File

@ -117,7 +117,7 @@
"parent": "docker"
},
"restrict_sys": true,
"device_nodes": [
"devices": [
{
"permissions": "rwm",
"file_mode": 438,

View File

@ -117,7 +117,7 @@
"parent": "docker"
},
"restrict_sys": true,
"device_nodes": [
"devices": [
{
"permissions": "rwm",
"file_mode": 438,

View File

@ -117,7 +117,7 @@
"parent": "docker"
},
"restrict_sys": true,
"device_nodes": [
"devices": [
{
"permissions": "rwm",
"file_mode": 438,

View File

@ -119,7 +119,7 @@
"restrict_sys": true,
"process_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475",
"mount_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475",
"device_nodes": [
"devices": [
{
"permissions": "rwm",
"file_mode": 438,

View File

@ -117,7 +117,7 @@
"parent": "docker"
},
"restrict_sys": true,
"device_nodes": [
"devices": [
{
"permissions": "rwm",
"file_mode": 438,