Rename Fs fields to fs
Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
parent
e48806d39d
commit
5fc19e8db5
|
@ -8,7 +8,7 @@ In the design and development of libcontainer we try to follow these principles:
|
||||||
* Less code is better.
|
* Less code is better.
|
||||||
* Fewer components are better. Do you really need to add one more class?
|
* 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.
|
* 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.
|
* 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.
|
* "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.
|
* Containers must be portable to the greatest possible number of machines. Be suspicious of any change which makes machines less interchangeable.
|
||||||
|
|
|
@ -83,7 +83,7 @@ func (m *Manager) Apply(pid int) error {
|
||||||
if err := sys.Set(d); err != nil {
|
if err := sys.Set(d); err != nil {
|
||||||
return err
|
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
|
// 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
|
// created then join consists of writing the process pids to cgroup.procs
|
||||||
p, err := d.path(name)
|
p, err := d.path(name)
|
||||||
|
|
|
@ -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.
|
// 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"`
|
PivotDir string `json:"pivot_dir,omitempty"`
|
||||||
|
|
||||||
// ReadonlyFs will remount the container's rootfs as readonly where only externally mounted
|
// Path to a directory containing the container's root filesystem.
|
||||||
// bind mounts are writtable
|
Rootfs string `json:"rootfs,omitempty"`
|
||||||
ReadonlyFs bool `json:"readonly_fs,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
|
// Mounts specify additional source and destination paths that will be mounted inside the container's
|
||||||
// rootfs and mount namespace if specified
|
// rootfs and mount namespace if specified
|
||||||
Mounts []*Mount `json:"mounts,omitempty"`
|
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!
|
// 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"`
|
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 optionally sets the container's hostname if provided
|
||||||
Hostname string `json:"hostname,omitempty"`
|
Hostname string `json:"hostname,omitempty"`
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,7 @@ func TestConfigJsonFormat(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range DefaultSimpleDevices {
|
for _, d := range DefaultSimpleDevices {
|
||||||
if !containsDevice(d, container.DeviceNodes) {
|
if !containsDevice(d, container.Devices) {
|
||||||
t.Logf("expected device configuration for %s", d.Path)
|
t.Logf("expected device configuration for %s", d.Path)
|
||||||
t.Fail()
|
t.Fail()
|
||||||
}
|
}
|
||||||
|
@ -163,3 +163,69 @@ func TestRemoveNamespace(t *testing.T) {
|
||||||
t.Fatalf("namespaces should have 0 items but reports %d", len(ns))
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -10,19 +10,29 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Device struct {
|
type Device struct {
|
||||||
|
// Device type, block, char, etc.
|
||||||
Type rune `json:"type,omitempty"`
|
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"`
|
Path string `json:"path,omitempty"`
|
||||||
// Use the wildcard constant for wildcards.
|
|
||||||
|
// Major is the device's major number.
|
||||||
Major int64 `json:"major,omitempty"`
|
Major int64 `json:"major,omitempty"`
|
||||||
// Use the wildcard constant for wildcards.
|
|
||||||
|
// Minor is the device's minor number.
|
||||||
Minor int64 `json:"minor,omitempty"`
|
Minor int64 `json:"minor,omitempty"`
|
||||||
// Typically just "rwm"
|
|
||||||
|
// Cgroup permissions format, rwm.
|
||||||
Permissions string `json:"permissions,omitempty"`
|
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"`
|
FileMode os.FileMode `json:"file_mode,omitempty"`
|
||||||
Uid uint32 `json:"uid,omitempty"`
|
|
||||||
Gid uint32 `json:"gid,omitempty"`
|
// Uid of the device.
|
||||||
|
Uid uint32 `json:"uid,omitempty"`
|
||||||
|
|
||||||
|
// Gid of the device.
|
||||||
|
Gid uint32 `json:"gid,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Device) CgroupString() string {
|
func (d *Device) CgroupString() string {
|
||||||
|
|
|
@ -52,7 +52,7 @@ func (m *Mount) bindMount(rootfs, mountLabel string) error {
|
||||||
return err
|
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)
|
dest, err = symlink.FollowSymlinkInScope(dest, rootfs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -94,7 +94,7 @@ func (m *Mount) tmpfsMount(rootfs, mountLabel string) error {
|
||||||
dest = filepath.Join(rootfs, m.Destination)
|
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 {
|
if dest, err = symlink.FollowSymlinkInScope(dest, rootfs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,12 +20,12 @@ type Network struct {
|
||||||
// Address contains the IPv4 and mask to set on the network interface
|
// Address contains the IPv4 and mask to set on the network interface
|
||||||
Address string `json:"address,omitempty"`
|
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 sets the gateway address that is used as the default for the interface
|
||||||
Gateway string `json:"gateway,omitempty"`
|
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 sets the ipv6 gateway address that is used as the default for the interface
|
||||||
IPv6Gateway string `json:"ipv6_gateway,omitempty"`
|
IPv6Gateway string `json:"ipv6_gateway,omitempty"`
|
||||||
|
|
||||||
|
|
|
@ -60,10 +60,10 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func HostDevices() ([]*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)
|
files, err := ioutilReadDir(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -76,7 +76,7 @@ func getDeviceNodes(path string) ([]*configs.Device, error) {
|
||||||
case "pts", "shm", "fd", "mqueue":
|
case "pts", "shm", "fd", "mqueue":
|
||||||
continue
|
continue
|
||||||
default:
|
default:
|
||||||
sub, err := getDeviceNodes(filepath.Join(path, f.Name()))
|
sub, err := getDevices(filepath.Join(path, f.Name()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ func testExecPS(t *testing.T, userns bool) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rootfs, err := newRootFs()
|
rootfs, err := newRootfs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ func TestIPCPrivate(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rootfs, err := newRootFs()
|
rootfs, err := newRootfs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ func TestIPCHost(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rootfs, err := newRootFs()
|
rootfs, err := newRootfs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ func TestIPCJoinPath(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rootfs, err := newRootFs()
|
rootfs, err := newRootfs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ func TestIPCBadPath(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rootfs, err := newRootFs()
|
rootfs, err := newRootfs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ func TestRlimit(t *testing.T) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
rootfs, err := newRootFs()
|
rootfs, err := newRootfs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -232,7 +232,7 @@ func TestEnter(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(root)
|
defer os.RemoveAll(root)
|
||||||
|
|
||||||
rootfs, err := newRootFs()
|
rootfs, err := newRootfs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -335,7 +335,7 @@ func TestFreeze(t *testing.T) {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(root)
|
defer os.RemoveAll(root)
|
||||||
|
|
||||||
rootfs, err := newRootFs()
|
rootfs, err := newRootfs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
// and the default setup for devices
|
// and the default setup for devices
|
||||||
func newTemplateConfig(rootfs string) *configs.Config {
|
func newTemplateConfig(rootfs string) *configs.Config {
|
||||||
return &configs.Config{
|
return &configs.Config{
|
||||||
RootFs: rootfs,
|
Rootfs: rootfs,
|
||||||
Capabilities: []string{
|
Capabilities: []string{
|
||||||
"CHOWN",
|
"CHOWN",
|
||||||
"DAC_OVERRIDE",
|
"DAC_OVERRIDE",
|
||||||
|
@ -43,7 +43,7 @@ func newTemplateConfig(rootfs string) *configs.Config {
|
||||||
AllowedDevices: configs.DefaultAllowedDevices,
|
AllowedDevices: configs.DefaultAllowedDevices,
|
||||||
},
|
},
|
||||||
|
|
||||||
DeviceNodes: configs.DefaultAutoCreatedDevices,
|
Devices: configs.DefaultAutoCreatedDevices,
|
||||||
Hostname: "integration",
|
Hostname: "integration",
|
||||||
Env: []string{
|
Env: []string{
|
||||||
"HOME=/root",
|
"HOME=/root",
|
||||||
|
|
|
@ -29,7 +29,7 @@ type stdBuffers struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeConfig(config *configs.Config) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,8 @@ func loadConfig() (*configs.Config, error) {
|
||||||
return container, nil
|
return container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// newRootFs creates a new tmp directory and copies the busybox root filesystem
|
// newRootfs creates a new tmp directory and copies the busybox root filesystem
|
||||||
func newRootFs() (string, error) {
|
func newRootfs() (string, error) {
|
||||||
dir, err := ioutil.TempDir("", "")
|
dir, err := ioutil.TempDir("", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
@ -6,17 +6,13 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/docker/libcontainer/apparmor"
|
|
||||||
"github.com/docker/libcontainer/cgroups"
|
"github.com/docker/libcontainer/cgroups"
|
||||||
"github.com/docker/libcontainer/configs"
|
"github.com/docker/libcontainer/configs"
|
||||||
"github.com/docker/libcontainer/label"
|
|
||||||
"github.com/docker/libcontainer/mount"
|
|
||||||
"github.com/docker/libcontainer/network"
|
"github.com/docker/libcontainer/network"
|
||||||
"github.com/docker/libcontainer/system"
|
"github.com/docker/libcontainer/system"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
@ -106,7 +102,7 @@ func (c *linuxContainer) Start(process *Process) (int, error) {
|
||||||
cmd.Stdout = process.Stdout
|
cmd.Stdout = process.Stdout
|
||||||
cmd.Stderr = process.Stderr
|
cmd.Stderr = process.Stderr
|
||||||
cmd.Env = c.config.Env
|
cmd.Env = c.config.Env
|
||||||
cmd.Dir = c.config.RootFs
|
cmd.Dir = c.config.Rootfs
|
||||||
if cmd.SysProcAttr == nil {
|
if cmd.SysProcAttr == nil {
|
||||||
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||||
}
|
}
|
||||||
|
@ -161,7 +157,7 @@ func (c *linuxContainer) startNewProcess(cmd *exec.Cmd, args []string) (int, err
|
||||||
return -1, terr
|
return -1, terr
|
||||||
}
|
}
|
||||||
// Enter cgroups.
|
// Enter cgroups.
|
||||||
if err := enterCgroups(c.state, pid.Pid); err != nil {
|
if err := c.enterCgroups(pid.Pid); err != nil {
|
||||||
return terminate(err)
|
return terminate(err)
|
||||||
}
|
}
|
||||||
encoder := json.NewEncoder(parent)
|
encoder := json.NewEncoder(parent)
|
||||||
|
@ -398,7 +394,7 @@ func executeSetupCmd(args []string, ppid int, container *configs.Config, process
|
||||||
}
|
}
|
||||||
defer parent.Close()
|
defer parent.Close()
|
||||||
command.ExtraFiles = []*os.File{child}
|
command.ExtraFiles = []*os.File{child}
|
||||||
command.Dir = container.RootFs
|
command.Dir = container.Rootfs
|
||||||
command.Env = append(command.Env,
|
command.Env = append(command.Env,
|
||||||
fmt.Sprintf("_LIBCONTAINER_INITPID=%d", ppid),
|
fmt.Sprintf("_LIBCONTAINER_INITPID=%d", ppid),
|
||||||
fmt.Sprintf("_LIBCONTAINER_USERNS=1"))
|
fmt.Sprintf("_LIBCONTAINER_USERNS=1"))
|
||||||
|
@ -460,120 +456,6 @@ type pid struct {
|
||||||
Pid int `json:"Pid"`
|
Pid int `json:"Pid"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finalize entering into a container and execute a specified command
|
func (c *linuxContainer) enterCgroups(pid int) error {
|
||||||
func InitIn(pipe *os.File) (err error) {
|
return cgroups.EnterPid(c.state.CgroupPaths, pid)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
119
linux_factory.go
119
linux_factory.go
|
@ -141,7 +141,7 @@ func (l *linuxFactory) StartInitialization(pipefd uintptr) (err error) {
|
||||||
setupUserns := os.Getenv("_LIBCONTAINER_USERNS") != ""
|
setupUserns := os.Getenv("_LIBCONTAINER_USERNS") != ""
|
||||||
pid := os.Getenv("_LIBCONTAINER_INITPID")
|
pid := os.Getenv("_LIBCONTAINER_INITPID")
|
||||||
if pid != "" && !setupUserns {
|
if pid != "" && !setupUserns {
|
||||||
return InitIn(pipe)
|
return initIn(pipe)
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
// if we have an error during the initialization of the container's init then send it back to the
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
if setupUserns {
|
if setupUserns {
|
||||||
err = SetupContainer(process)
|
err = setupContainer(process)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
} else {
|
} else {
|
||||||
|
@ -348,7 +348,6 @@ func (l *linuxFactory) initUserNs(uncleanRootfs string, process *processArgs) (e
|
||||||
if config.WorkingDir == "" {
|
if config.WorkingDir == "" {
|
||||||
config.WorkingDir = "/"
|
config.WorkingDir = "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := setupRlimits(config); err != nil {
|
if err := setupRlimits(config); err != nil {
|
||||||
return fmt.Errorf("setup rlimits %s", err)
|
return fmt.Errorf("setup rlimits %s", err)
|
||||||
}
|
}
|
||||||
|
@ -557,3 +556,117 @@ func joinExistingNamespaces(namespaces []configs.Namespace) error {
|
||||||
}
|
}
|
||||||
return nil
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ func TestFactoryLoadContainer(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
id = "1"
|
id = "1"
|
||||||
expectedConfig = &configs.Config{
|
expectedConfig = &configs.Config{
|
||||||
RootFs: "/mycontainer/root",
|
Rootfs: "/mycontainer/root",
|
||||||
}
|
}
|
||||||
expectedState = &configs.State{
|
expectedState = &configs.State{
|
||||||
InitPid: 1024,
|
InitPid: 1024,
|
||||||
|
@ -119,8 +119,8 @@ func TestFactoryLoadContainer(t *testing.T) {
|
||||||
|
|
||||||
config := container.Config()
|
config := container.Config()
|
||||||
|
|
||||||
if config.RootFs != expectedConfig.RootFs {
|
if config.Rootfs != expectedConfig.Rootfs {
|
||||||
t.Fatalf("expected rootfs %q but received %q", expectedConfig.RootFs, config.RootFs)
|
t.Fatalf("expected rootfs %q but received %q", expectedConfig.Rootfs, config.Rootfs)
|
||||||
}
|
}
|
||||||
|
|
||||||
lcontainer, ok := container.(*linuxContainer)
|
lcontainer, ok := container.(*linuxContainer)
|
||||||
|
|
|
@ -33,11 +33,11 @@ func InitializeMountNamespace(config *configs.Config) (err error) {
|
||||||
}
|
}
|
||||||
// apply any user specified mounts within the new mount namespace
|
// apply any user specified mounts within the new mount namespace
|
||||||
for _, m := range config.Mounts {
|
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
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := createDeviceNodes(config); err != nil {
|
if err := createDevices(config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := setupPtmx(config); err != nil {
|
if err := setupPtmx(config); err != nil {
|
||||||
|
@ -51,21 +51,21 @@ func InitializeMountNamespace(config *configs.Config) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := setupDevSymlinks(config.RootFs); err != nil {
|
if err := setupDevSymlinks(config.Rootfs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := syscall.Chdir(config.RootFs); err != nil {
|
if err := syscall.Chdir(config.Rootfs); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if config.NoPivotRoot {
|
if config.NoPivotRoot {
|
||||||
err = msMoveRoot(config.RootFs)
|
err = msMoveRoot(config.Rootfs)
|
||||||
} else {
|
} else {
|
||||||
err = pivotRoot(config.RootFs, config.PivotDir)
|
err = pivotRoot(config.Rootfs, config.PivotDir)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if config.ReadonlyFs {
|
if config.Readonlyfs {
|
||||||
if err := setReadonly(); err != nil {
|
if err := setReadonly(); err != nil {
|
||||||
return fmt.Errorf("set readonly %s", err)
|
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
|
// mountSystem sets up linux specific system mounts like mqueue, sys, proc, shm, and devpts
|
||||||
// inside the mount namespace
|
// inside the mount namespace
|
||||||
func mountSystem(config *configs.Config) error {
|
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) {
|
if err := os.MkdirAll(m.path, 0755); err != nil && !os.IsExist(err) {
|
||||||
return fmt.Errorf("mkdirall %s %s", m.path, 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.
|
// Create the device nodes in the container.
|
||||||
func createDeviceNodes(config *configs.Config) error {
|
func createDevices(config *configs.Config) error {
|
||||||
oldMask := syscall.Umask(0000)
|
oldMask := syscall.Umask(0000)
|
||||||
for _, node := range config.DeviceNodes {
|
for _, node := range config.Devices {
|
||||||
if err := createDeviceNode(config.RootFs, node); err != nil {
|
if err := createDeviceNode(config.Rootfs, node); err != nil {
|
||||||
syscall.Umask(oldMask)
|
syscall.Umask(oldMask)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -211,5 +211,5 @@ func prepareRoot(config *configs.Config) error {
|
||||||
if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil {
|
if err := syscall.Mount("", "/", "", uintptr(flag), ""); err != nil {
|
||||||
return err
|
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, "")
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupPtmx(config *configs.Config) error {
|
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) {
|
if err := os.Remove(ptmx); err != nil && !os.IsNotExist(err) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ func setupPtmx(config *configs.Config) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
15
process.go
15
process.go
|
@ -2,19 +2,18 @@ package libcontainer
|
||||||
|
|
||||||
import "io"
|
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 {
|
type Process struct {
|
||||||
// The command to be run followed by any arguments.
|
// The command to be run followed by any arguments.
|
||||||
Args []string
|
Args []string
|
||||||
|
|
||||||
// Stdin is a pointer to a reader which provides the standard input stream.
|
// Stdin is a pointer to a reader which provides the standard input stream.
|
||||||
|
Stdin io.Reader
|
||||||
|
|
||||||
// Stdout is a pointer to a writer which receives the standard output 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 io.Writer
|
Stdout io.Writer
|
||||||
|
|
||||||
|
// Stderr is a pointer to a writer which receives the standard error stream.
|
||||||
Stderr io.Writer
|
Stderr io.Writer
|
||||||
}
|
}
|
||||||
|
|
|
@ -118,7 +118,7 @@
|
||||||
},
|
},
|
||||||
"restrict_sys": true,
|
"restrict_sys": true,
|
||||||
"apparmor_profile": "docker-default",
|
"apparmor_profile": "docker-default",
|
||||||
"device_nodes": [
|
"devices": [
|
||||||
{
|
{
|
||||||
"permissions": "rwm",
|
"permissions": "rwm",
|
||||||
"file_mode": 438,
|
"file_mode": 438,
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
"parent": "docker"
|
"parent": "docker"
|
||||||
},
|
},
|
||||||
"restrict_sys": true,
|
"restrict_sys": true,
|
||||||
"device_nodes": [
|
"devices": [
|
||||||
{
|
{
|
||||||
"permissions": "rwm",
|
"permissions": "rwm",
|
||||||
"file_mode": 438,
|
"file_mode": 438,
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
"parent": "docker"
|
"parent": "docker"
|
||||||
},
|
},
|
||||||
"restrict_sys": true,
|
"restrict_sys": true,
|
||||||
"device_nodes": [
|
"devices": [
|
||||||
{
|
{
|
||||||
"permissions": "rwm",
|
"permissions": "rwm",
|
||||||
"file_mode": 438,
|
"file_mode": 438,
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
"parent": "docker"
|
"parent": "docker"
|
||||||
},
|
},
|
||||||
"restrict_sys": true,
|
"restrict_sys": true,
|
||||||
"device_nodes": [
|
"devices": [
|
||||||
{
|
{
|
||||||
"permissions": "rwm",
|
"permissions": "rwm",
|
||||||
"file_mode": 438,
|
"file_mode": 438,
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
"parent": "docker"
|
"parent": "docker"
|
||||||
},
|
},
|
||||||
"restrict_sys": true,
|
"restrict_sys": true,
|
||||||
"device_nodes": [
|
"devices": [
|
||||||
{
|
{
|
||||||
"permissions": "rwm",
|
"permissions": "rwm",
|
||||||
"file_mode": 438,
|
"file_mode": 438,
|
||||||
|
|
|
@ -119,7 +119,7 @@
|
||||||
"restrict_sys": true,
|
"restrict_sys": true,
|
||||||
"process_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475",
|
"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",
|
"mount_label": "system_u:system_r:svirt_lxc_net_t:s0:c164,c475",
|
||||||
"device_nodes": [
|
"devices": [
|
||||||
{
|
{
|
||||||
"permissions": "rwm",
|
"permissions": "rwm",
|
||||||
"file_mode": 438,
|
"file_mode": 438,
|
||||||
|
|
|
@ -117,7 +117,7 @@
|
||||||
"parent": "docker"
|
"parent": "docker"
|
||||||
},
|
},
|
||||||
"restrict_sys": true,
|
"restrict_sys": true,
|
||||||
"device_nodes": [
|
"devices": [
|
||||||
{
|
{
|
||||||
"permissions": "rwm",
|
"permissions": "rwm",
|
||||||
"file_mode": 438,
|
"file_mode": 438,
|
||||||
|
|
Loading…
Reference in New Issue