Merge pull request #6650 from crosbymichael/update-libcontainer-post-refactor

Update libcontainer dep to 5210a236b92a8022a673108f347
This commit is contained in:
Michael Crosby 2014-06-24 13:51:04 -07:00
commit 408963ed4d
40 changed files with 742 additions and 453 deletions

View File

@ -29,7 +29,7 @@ func finalizeNamespace(args *execdriver.InitArgs) error {
if !args.Privileged { if !args.Privileged {
// drop capabilities in bounding set before changing user // drop capabilities in bounding set before changing user
if err := capabilities.DropBoundingSet(container); err != nil { if err := capabilities.DropBoundingSet(container.Capabilities); err != nil {
return fmt.Errorf("drop bounding set %s", err) return fmt.Errorf("drop bounding set %s", err)
} }
@ -49,7 +49,7 @@ func finalizeNamespace(args *execdriver.InitArgs) error {
} }
// drop all other capabilities // drop all other capabilities
if err := capabilities.DropCapabilities(container); err != nil { if err := capabilities.DropCapabilities(container.Capabilities); err != nil {
return fmt.Errorf("drop capabilities %s", err) return fmt.Errorf("drop capabilities %s", err)
} }
} }

View File

@ -11,7 +11,7 @@ import (
"github.com/dotcloud/docker/pkg/units" "github.com/dotcloud/docker/pkg/units"
) )
type Action func(*libcontainer.Container, interface{}, string) error type Action func(*libcontainer.Config, interface{}, string) error
var actions = map[string]Action{ var actions = map[string]Action{
"cap.add": addCap, // add a cap "cap.add": addCap, // add a cap
@ -35,7 +35,7 @@ var actions = map[string]Action{
"fs.readonly": readonlyFs, // make the rootfs of the container read only "fs.readonly": readonlyFs, // make the rootfs of the container read only
} }
func cpusetCpus(container *libcontainer.Container, context interface{}, value string) error { func cpusetCpus(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil { if container.Cgroups == nil {
return fmt.Errorf("cannot set cgroups when they are disabled") return fmt.Errorf("cannot set cgroups when they are disabled")
} }
@ -44,7 +44,7 @@ func cpusetCpus(container *libcontainer.Container, context interface{}, value st
return nil return nil
} }
func systemdSlice(container *libcontainer.Container, context interface{}, value string) error { func systemdSlice(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil { if container.Cgroups == nil {
return fmt.Errorf("cannot set slice when cgroups are disabled") return fmt.Errorf("cannot set slice when cgroups are disabled")
} }
@ -53,12 +53,12 @@ func systemdSlice(container *libcontainer.Container, context interface{}, value
return nil return nil
} }
func apparmorProfile(container *libcontainer.Container, context interface{}, value string) error { func apparmorProfile(container *libcontainer.Config, context interface{}, value string) error {
container.Context["apparmor_profile"] = value container.Context["apparmor_profile"] = value
return nil return nil
} }
func cpuShares(container *libcontainer.Container, context interface{}, value string) error { func cpuShares(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil { if container.Cgroups == nil {
return fmt.Errorf("cannot set cgroups when they are disabled") return fmt.Errorf("cannot set cgroups when they are disabled")
} }
@ -70,7 +70,7 @@ func cpuShares(container *libcontainer.Container, context interface{}, value str
return nil return nil
} }
func memory(container *libcontainer.Container, context interface{}, value string) error { func memory(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil { if container.Cgroups == nil {
return fmt.Errorf("cannot set cgroups when they are disabled") return fmt.Errorf("cannot set cgroups when they are disabled")
} }
@ -83,7 +83,7 @@ func memory(container *libcontainer.Container, context interface{}, value string
return nil return nil
} }
func memoryReservation(container *libcontainer.Container, context interface{}, value string) error { func memoryReservation(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil { if container.Cgroups == nil {
return fmt.Errorf("cannot set cgroups when they are disabled") return fmt.Errorf("cannot set cgroups when they are disabled")
} }
@ -96,7 +96,7 @@ func memoryReservation(container *libcontainer.Container, context interface{}, v
return nil return nil
} }
func memorySwap(container *libcontainer.Container, context interface{}, value string) error { func memorySwap(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil { if container.Cgroups == nil {
return fmt.Errorf("cannot set cgroups when they are disabled") return fmt.Errorf("cannot set cgroups when they are disabled")
} }
@ -108,12 +108,12 @@ func memorySwap(container *libcontainer.Container, context interface{}, value st
return nil return nil
} }
func addCap(container *libcontainer.Container, context interface{}, value string) error { func addCap(container *libcontainer.Config, context interface{}, value string) error {
container.Capabilities = append(container.Capabilities, value) container.Capabilities = append(container.Capabilities, value)
return nil return nil
} }
func dropCap(container *libcontainer.Container, context interface{}, value string) error { func dropCap(container *libcontainer.Config, context interface{}, value string) error {
// If the capability is specified multiple times, remove all instances. // If the capability is specified multiple times, remove all instances.
for i, capability := range container.Capabilities { for i, capability := range container.Capabilities {
if capability == value { if capability == value {
@ -125,27 +125,27 @@ func dropCap(container *libcontainer.Container, context interface{}, value strin
return nil return nil
} }
func addNamespace(container *libcontainer.Container, context interface{}, value string) error { func addNamespace(container *libcontainer.Config, context interface{}, value string) error {
container.Namespaces[value] = true container.Namespaces[value] = true
return nil return nil
} }
func dropNamespace(container *libcontainer.Container, context interface{}, value string) error { func dropNamespace(container *libcontainer.Config, context interface{}, value string) error {
container.Namespaces[value] = false container.Namespaces[value] = false
return nil return nil
} }
func readonlyFs(container *libcontainer.Container, context interface{}, value string) error { func readonlyFs(container *libcontainer.Config, context interface{}, value string) error {
switch value { switch value {
case "1", "true": case "1", "true":
container.ReadonlyFs = true container.MountConfig.ReadonlyFs = true
default: default:
container.ReadonlyFs = false container.MountConfig.ReadonlyFs = false
} }
return nil return nil
} }
func joinNetNamespace(container *libcontainer.Container, context interface{}, value string) error { func joinNetNamespace(container *libcontainer.Config, context interface{}, value string) error {
var ( var (
running = context.(map[string]*exec.Cmd) running = context.(map[string]*exec.Cmd)
cmd = running[value] cmd = running[value]
@ -154,28 +154,13 @@ func joinNetNamespace(container *libcontainer.Container, context interface{}, va
if cmd == nil || cmd.Process == nil { if cmd == nil || cmd.Process == nil {
return fmt.Errorf("%s is not a valid running container to join", value) return fmt.Errorf("%s is not a valid running container to join", value)
} }
nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net") nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
container.Networks = append(container.Networks, &libcontainer.Network{ container.Networks = append(container.Networks, &libcontainer.Network{
Type: "netns", Type: "netns",
Context: libcontainer.Context{ NsPath: nspath,
"nspath": nspath,
},
}) })
return nil
}
func vethMacAddress(container *libcontainer.Container, context interface{}, value string) error {
var veth *libcontainer.Network
for _, network := range container.Networks {
if network.Type == "veth" {
veth = network
break
}
}
if veth == nil {
return fmt.Errorf("not veth configured for container")
}
veth.Context["mac"] = value
return nil return nil
} }
@ -183,7 +168,7 @@ func vethMacAddress(container *libcontainer.Container, context interface{}, valu
// container's default configuration. // container's default configuration.
// //
// TODO: this can be moved to a general utils or parser in pkg // TODO: this can be moved to a general utils or parser in pkg
func ParseConfiguration(container *libcontainer.Container, running map[string]*exec.Cmd, opts []string) error { func ParseConfiguration(container *libcontainer.Config, running map[string]*exec.Cmd, opts []string) error {
for _, opt := range opts { for _, opt := range opts {
kv := strings.SplitN(opt, "=", 2) kv := strings.SplitN(opt, "=", 2)
if len(kv) < 2 { if len(kv) < 2 {

View File

@ -3,7 +3,7 @@ package configuration
import ( import (
"testing" "testing"
"github.com/docker/libcontainer" "github.com/docker/libcontainer/security/capabilities"
"github.com/dotcloud/docker/daemon/execdriver/native/template" "github.com/dotcloud/docker/daemon/execdriver/native/template"
) )
@ -25,14 +25,14 @@ func TestSetReadonlyRootFs(t *testing.T) {
} }
) )
if container.ReadonlyFs { if container.MountConfig.ReadonlyFs {
t.Fatal("container should not have a readonly rootfs by default") t.Fatal("container should not have a readonly rootfs by default")
} }
if err := ParseConfiguration(container, nil, opts); err != nil { if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if !container.ReadonlyFs { if !container.MountConfig.ReadonlyFs {
t.Fatal("container should have a readonly rootfs") t.Fatal("container should have a readonly rootfs")
} }
} }
@ -165,7 +165,7 @@ func TestDropCap(t *testing.T) {
} }
) )
// enabled all caps like in privileged mode // enabled all caps like in privileged mode
container.Capabilities = libcontainer.GetAllCapabilities() container.Capabilities = capabilities.GetAllCapabilities()
if err := ParseConfiguration(container, nil, opts); err != nil { if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -9,6 +9,8 @@ import (
"github.com/docker/libcontainer" "github.com/docker/libcontainer"
"github.com/docker/libcontainer/apparmor" "github.com/docker/libcontainer/apparmor"
"github.com/docker/libcontainer/devices" "github.com/docker/libcontainer/devices"
"github.com/docker/libcontainer/mount"
"github.com/docker/libcontainer/security/capabilities"
"github.com/dotcloud/docker/daemon/execdriver" "github.com/dotcloud/docker/daemon/execdriver"
"github.com/dotcloud/docker/daemon/execdriver/native/configuration" "github.com/dotcloud/docker/daemon/execdriver/native/configuration"
"github.com/dotcloud/docker/daemon/execdriver/native/template" "github.com/dotcloud/docker/daemon/execdriver/native/template"
@ -16,7 +18,7 @@ import (
// createContainer populates and configures the container type with the // createContainer populates and configures the container type with the
// data provided by the execdriver.Command // data provided by the execdriver.Command
func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container, error) { func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, error) {
container := template.New() container := template.New()
container.Hostname = getEnv("HOSTNAME", c.Env) container.Hostname = getEnv("HOSTNAME", c.Env)
@ -26,65 +28,71 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
container.Env = c.Env container.Env = c.Env
container.Cgroups.Name = c.ID container.Cgroups.Name = c.ID
container.Cgroups.AllowedDevices = c.AllowedDevices container.Cgroups.AllowedDevices = c.AllowedDevices
container.DeviceNodes = c.AutoCreatedDevices container.MountConfig.DeviceNodes = c.AutoCreatedDevices
// check to see if we are running in ramdisk to disable pivot root // check to see if we are running in ramdisk to disable pivot root
container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" container.MountConfig.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
container.Context["restrictions"] = "true" container.Context["restrictions"] = "true"
if err := d.createNetwork(container, c); err != nil { if err := d.createNetwork(container, c); err != nil {
return nil, err return nil, err
} }
if c.Privileged { if c.Privileged {
if err := d.setPrivileged(container); err != nil { if err := d.setPrivileged(container); err != nil {
return nil, err return nil, err
} }
} }
if err := d.setupCgroups(container, c); err != nil { if err := d.setupCgroups(container, c); err != nil {
return nil, err return nil, err
} }
if err := d.setupMounts(container, c); err != nil { if err := d.setupMounts(container, c); err != nil {
return nil, err return nil, err
} }
if err := d.setupLabels(container, c); err != nil { if err := d.setupLabels(container, c); err != nil {
return nil, err return nil, err
} }
cmds := make(map[string]*exec.Cmd) cmds := make(map[string]*exec.Cmd)
d.Lock() d.Lock()
for k, v := range d.activeContainers { for k, v := range d.activeContainers {
cmds[k] = v.cmd cmds[k] = v.cmd
} }
d.Unlock() d.Unlock()
if err := configuration.ParseConfiguration(container, cmds, c.Config["native"]); err != nil { if err := configuration.ParseConfiguration(container, cmds, c.Config["native"]); err != nil {
return nil, err return nil, err
} }
return container, nil return container, nil
} }
func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error { func (d *driver) createNetwork(container *libcontainer.Config, c *execdriver.Command) error {
if c.Network.HostNetworking { if c.Network.HostNetworking {
container.Namespaces["NEWNET"] = false container.Namespaces["NEWNET"] = false
return nil return nil
} }
container.Networks = []*libcontainer.Network{ container.Networks = []*libcontainer.Network{
{ {
Mtu: c.Network.Mtu, Mtu: c.Network.Mtu,
Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0), Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0),
Gateway: "localhost", Gateway: "localhost",
Type: "loopback", Type: "loopback",
Context: libcontainer.Context{},
}, },
} }
if c.Network.Interface != nil { if c.Network.Interface != nil {
vethNetwork := libcontainer.Network{ vethNetwork := libcontainer.Network{
Mtu: c.Network.Mtu, Mtu: c.Network.Mtu,
Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen), Address: fmt.Sprintf("%s/%d", c.Network.Interface.IPAddress, c.Network.Interface.IPPrefixLen),
Gateway: c.Network.Interface.Gateway, Gateway: c.Network.Interface.Gateway,
Type: "veth", Type: "veth",
Context: libcontainer.Context{ Bridge: c.Network.Interface.Bridge,
"prefix": "veth", VethPrefix: "veth",
"bridge": c.Network.Interface.Bridge,
},
} }
container.Networks = append(container.Networks, &vethNetwork) container.Networks = append(container.Networks, &vethNetwork)
} }
@ -93,6 +101,7 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.
d.Lock() d.Lock()
active := d.activeContainers[c.Network.ContainerID] active := d.activeContainers[c.Network.ContainerID]
d.Unlock() d.Unlock()
if active == nil || active.cmd.Process == nil { if active == nil || active.cmd.Process == nil {
return fmt.Errorf("%s is not a valid running container to join", c.Network.ContainerID) return fmt.Errorf("%s is not a valid running container to join", c.Network.ContainerID)
} }
@ -100,34 +109,34 @@ func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.
nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net") nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
container.Networks = append(container.Networks, &libcontainer.Network{ container.Networks = append(container.Networks, &libcontainer.Network{
Type: "netns", Type: "netns",
Context: libcontainer.Context{ NsPath: nspath,
"nspath": nspath,
},
}) })
} }
return nil return nil
} }
func (d *driver) setPrivileged(container *libcontainer.Container) (err error) { func (d *driver) setPrivileged(container *libcontainer.Config) (err error) {
container.Capabilities = libcontainer.GetAllCapabilities() container.Capabilities = capabilities.GetAllCapabilities()
container.Cgroups.AllowAllDevices = true container.Cgroups.AllowAllDevices = true
hostDeviceNodes, err := devices.GetHostDeviceNodes() hostDeviceNodes, err := devices.GetHostDeviceNodes()
if err != nil { if err != nil {
return err return err
} }
container.DeviceNodes = hostDeviceNodes container.MountConfig.DeviceNodes = hostDeviceNodes
delete(container.Context, "restrictions") delete(container.Context, "restrictions")
if apparmor.IsEnabled() { if apparmor.IsEnabled() {
container.Context["apparmor_profile"] = "unconfined" container.Context["apparmor_profile"] = "unconfined"
} }
return nil return nil
} }
func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.Command) error { func (d *driver) setupCgroups(container *libcontainer.Config, c *execdriver.Command) error {
if c.Resources != nil { if c.Resources != nil {
container.Cgroups.CpuShares = c.Resources.CpuShares container.Cgroups.CpuShares = c.Resources.CpuShares
container.Cgroups.Memory = c.Resources.Memory container.Cgroups.Memory = c.Resources.Memory
@ -135,12 +144,13 @@ func (d *driver) setupCgroups(container *libcontainer.Container, c *execdriver.C
container.Cgroups.MemorySwap = c.Resources.MemorySwap container.Cgroups.MemorySwap = c.Resources.MemorySwap
container.Cgroups.CpusetCpus = c.Resources.Cpuset container.Cgroups.CpusetCpus = c.Resources.Cpuset
} }
return nil return nil
} }
func (d *driver) setupMounts(container *libcontainer.Container, c *execdriver.Command) error { func (d *driver) setupMounts(container *libcontainer.Config, c *execdriver.Command) error {
for _, m := range c.Mounts { for _, m := range c.Mounts {
container.Mounts = append(container.Mounts, libcontainer.Mount{ container.MountConfig.Mounts = append(container.MountConfig.Mounts, mount.Mount{
Type: "bind", Type: "bind",
Source: m.Source, Source: m.Source,
Destination: m.Destination, Destination: m.Destination,
@ -148,11 +158,13 @@ func (d *driver) setupMounts(container *libcontainer.Container, c *execdriver.Co
Private: m.Private, Private: m.Private,
}) })
} }
return nil return nil
} }
func (d *driver) setupLabels(container *libcontainer.Container, c *execdriver.Command) error { func (d *driver) setupLabels(container *libcontainer.Config, c *execdriver.Command) error {
container.Context["process_label"] = c.Config["process_label"][0] container.Context["process_label"] = c.Config["process_label"][0]
container.Context["mount_label"] = c.Config["mount_label"][0] container.Context["mount_label"] = c.Config["mount_label"][0]
return nil return nil
} }

View File

@ -27,7 +27,7 @@ const (
func init() { func init() {
execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error { execdriver.RegisterInitFunc(DriverName, func(args *execdriver.InitArgs) error {
var container *libcontainer.Container var container *libcontainer.Config
f, err := os.Open(filepath.Join(args.Root, "container.json")) f, err := os.Open(filepath.Join(args.Root, "container.json"))
if err != nil { if err != nil {
return err return err
@ -54,7 +54,7 @@ func init() {
} }
type activeContainer struct { type activeContainer struct {
container *libcontainer.Container container *libcontainer.Config
cmd *exec.Cmd cmd *exec.Cmd
} }
@ -83,7 +83,7 @@ func NewDriver(root, initPath string) (*driver, error) {
} }
func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) { func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallback execdriver.StartCallback) (int, error) {
// take the Command and populate the libcontainer.Container from it // take the Command and populate the libcontainer.Config from it
container, err := d.createContainer(c) container, err := d.createContainer(c)
if err != nil { if err != nil {
return -1, err return -1, err
@ -110,7 +110,7 @@ func (d *driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, startCallba
term := getTerminal(c, pipes) term := getTerminal(c, pipes)
return namespaces.Exec(container, term, c.Rootfs, dataPath, args, func(container *libcontainer.Container, console, rootfs, dataPath, init string, child *os.File, args []string) *exec.Cmd { return namespaces.Exec(container, term, c.Rootfs, dataPath, args, func(container *libcontainer.Config, console, rootfs, dataPath, init string, child *os.File, args []string) *exec.Cmd {
// we need to join the rootfs because namespaces will setup the rootfs and chroot // we need to join the rootfs because namespaces will setup the rootfs and chroot
initPath := filepath.Join(c.Rootfs, c.InitPath) initPath := filepath.Join(c.Rootfs, c.InitPath)
@ -229,7 +229,7 @@ func (d *driver) GetPidsForContainer(id string) ([]int, error) {
return fs.GetPids(c) return fs.GetPids(c)
} }
func (d *driver) writeContainerFile(container *libcontainer.Container, id string) error { func (d *driver) writeContainerFile(container *libcontainer.Config, id string) error {
data, err := json.Marshal(container) data, err := json.Marshal(container)
if err != nil { if err != nil {
return err return err

View File

@ -7,8 +7,8 @@ import (
) )
// New returns the docker default configuration for libcontainer // New returns the docker default configuration for libcontainer
func New() *libcontainer.Container { func New() *libcontainer.Config {
container := &libcontainer.Container{ container := &libcontainer.Config{
Capabilities: []string{ Capabilities: []string{
"CHOWN", "CHOWN",
"DAC_OVERRIDE", "DAC_OVERRIDE",
@ -34,10 +34,13 @@ func New() *libcontainer.Container {
Parent: "docker", Parent: "docker",
AllowAllDevices: false, AllowAllDevices: false,
}, },
Context: libcontainer.Context{}, MountConfig: &libcontainer.MountConfig{},
Context: make(map[string]string),
} }
if apparmor.IsEnabled() { if apparmor.IsEnabled() {
container.Context["apparmor_profile"] = "docker-default" container.Context["apparmor_profile"] = "docker-default"
} }
return container return container
} }

View File

@ -61,4 +61,4 @@ mv tmp-tar src/code.google.com/p/go/src/pkg/archive/tar
clone git github.com/godbus/dbus v1 clone git github.com/godbus/dbus v1
clone git github.com/coreos/go-systemd v2 clone git github.com/coreos/go-systemd v2
clone git github.com/docker/libcontainer v1.0.1 clone git github.com/docker/libcontainer 5210a236b92a8022a673108f3471fed0a046bd05

View File

@ -1,5 +1,9 @@
## libcontainer - reference implementation for containers ## libcontainer - reference implementation for containers
### Note on API changes:
Please bear with us while we work on making the libcontainer API stable and something that we can support long term. We are currently discussing the API with the community, therefore, if you currently depend on libcontainer please pin your dependency at a specific tag or commit id. Please join the discussion and help shape the API.
#### Background #### Background
libcontainer specifies configuration options for what a container is. It provides a native Go implementation libcontainer specifies configuration options for what a container is. It provides a native Go implementation
@ -13,7 +17,7 @@ a `container.json` file is placed with the runtime configuration for how the pro
should be contained and run. Environment, networking, and different capabilities for the should be contained and run. Environment, networking, and different capabilities for the
process are specified in this file. The configuration is used for each process executed inside the container. process are specified in this file. The configuration is used for each process executed inside the container.
See the `container.json` file for what the configuration should look like. See the `sample_configs` folder for examples of what the container configuration should look like.
Using this configuration and the current directory holding the rootfs for a process, one can use libcontainer to exec the container. Running the life of the namespace, a `pid` file Using this configuration and the current directory holding the rootfs for a process, one can use libcontainer to exec the container. Running the life of the namespace, a `pid` file
is written to the current directory with the pid of the namespaced process to the external world. A client can use this pid to wait, kill, or perform other operation with the container. If a user tries to run a new process inside an existing container with a live namespace, the namespace will be joined by the new process. is written to the current directory with the pid of the namespaced process to the external world. A client can use this pid to wait, kill, or perform other operation with the container. If a user tries to run a new process inside an existing container with a live namespace, the namespace will be joined by the new process.

View File

@ -57,12 +57,13 @@ func GetStats(c *cgroups.Cgroup) (*cgroups.Stats, error) {
d, err := getCgroupData(c, 0) d, err := getCgroupData(c, 0)
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("getting CgroupData %s", err)
} }
for _, sys := range subsystems { for sysName, sys := range subsystems {
if err := sys.GetStats(d, stats); err != nil { // Don't fail if a cgroup hierarchy was not found.
return nil, err if err := sys.GetStats(d, stats); err != nil && err != cgroups.ErrNotFound {
return nil, fmt.Errorf("getting stats for system %q %s", sysName, err)
} }
} }

View File

@ -2,25 +2,22 @@ package libcontainer
import ( import (
"github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/cgroups"
"github.com/docker/libcontainer/devices" "github.com/docker/libcontainer/mount"
"github.com/docker/libcontainer/network"
) )
// Context is a generic key value pair that allows arbatrary data to be sent type MountConfig mount.MountConfig
type Context map[string]string
type Network network.Network
// Config defines configuration options for executing a process inside a contained environment.
type Config struct {
// Mount specific options.
MountConfig *MountConfig `json:"mount_config,omitempty"`
// Container defines configuration options for executing a process inside a contained environment
type Container struct {
// 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"`
// ReadonlyFs will remount the container's rootfs as readonly where only externally mounted
// bind mounts are writtable
ReadonlyFs bool `json:"readonly_fs,omitempty"`
// NoPivotRoot will use MS_MOVE and a chroot to jail the process into the container's rootfs
// This is a common option when the container is running in ramdisk
NoPivotRoot bool `json:"no_pivot_root,omitempty"`
// User will set the uid and gid of the executing process running inside the container // User will set the uid and gid of the executing process running inside the container
User string `json:"user,omitempty"` User string `json:"user,omitempty"`
@ -58,37 +55,8 @@ type Container struct {
// on the container's creation // on the container's creation
// This is commonly used to specify apparmor profiles, selinux labels, and different restrictions // This is commonly used to specify apparmor profiles, selinux labels, and different restrictions
// placed on the container's processes // placed on the container's processes
Context Context `json:"context,omitempty"` // TODO(vishh): Avoid overloading this field with params for different subsystems. Strongtype this.
Context map[string]string `json:"context,omitempty"`
// Mounts specify additional source and destination paths that will be mounted inside the container's
// rootfs and mount namespace if specified
Mounts Mounts `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 []*devices.Device `json:"device_nodes,omitempty"`
}
// Network defines configuration for a container's networking stack
//
// The network configuration can be omited from a container causing the
// container to be setup with the host's networking stack
type Network struct {
// Type sets the networks type, commonly veth and loopback
Type string `json:"type,omitempty"`
// Context is a generic key value format for setting additional options that are specific to
// the network type
Context Context `json:"context,omitempty"`
// Address contains the IP and mask to set on the network interface
Address string `json:"address,omitempty"`
// Gateway sets the gateway address that is used as the default for the interface
Gateway string `json:"gateway,omitempty"`
// Mtu sets the mtu value for the interface and will be mirrored on both the host and
// container's interfaces if a pair is created, specifically in the case of type veth
Mtu int `json:"mtu,omitempty"`
} }
// Routes can be specified to create entries in the route table as the container is started // Routes can be specified to create entries in the route table as the container is started

View File

@ -4,6 +4,8 @@ import (
"encoding/json" "encoding/json"
"os" "os"
"testing" "testing"
"github.com/docker/libcontainer/devices"
) )
// Checks whether the expected capability is specified in the capabilities. // Checks whether the expected capability is specified in the capabilities.
@ -16,14 +18,28 @@ func contains(expected string, values []string) bool {
return false return false
} }
func TestContainerJsonFormat(t *testing.T) { func containsDevice(expected *devices.Device, values []*devices.Device) bool {
f, err := os.Open("container.json") for _, d := range values {
if d.Path == expected.Path &&
d.CgroupPermissions == expected.CgroupPermissions &&
d.FileMode == expected.FileMode &&
d.MajorNumber == expected.MajorNumber &&
d.MinorNumber == expected.MinorNumber &&
d.Type == expected.Type {
return true
}
}
return false
}
func TestConfigJsonFormat(t *testing.T) {
f, err := os.Open("sample_configs/attach_to_bridge.json")
if err != nil { if err != nil {
t.Fatal("Unable to open container.json") t.Fatal("Unable to open container.json")
} }
defer f.Close() defer f.Close()
var container *Container var container *Config
if err := json.NewDecoder(f).Decode(&container); err != nil { if err := json.NewDecoder(f).Decode(&container); err != nil {
t.Fatalf("failed to decode container config: %s", err) t.Fatalf("failed to decode container config: %s", err)
} }
@ -61,4 +77,42 @@ func TestContainerJsonFormat(t *testing.T) {
t.Log("capabilities mask should contain SYS_CHROOT") t.Log("capabilities mask should contain SYS_CHROOT")
t.Fail() t.Fail()
} }
for _, n := range container.Networks {
if n.Type == "veth" {
if n.Bridge != "docker0" {
t.Logf("veth bridge should be docker0 but received %q", n.Bridge)
t.Fail()
}
if n.Address != "172.17.0.101/16" {
t.Logf("veth address should be 172.17.0.101/61 but received %q", n.Address)
t.Fail()
}
if n.VethPrefix != "veth" {
t.Logf("veth prefix should be veth but received %q", n.VethPrefix)
t.Fail()
}
if n.Gateway != "172.17.42.1" {
t.Logf("veth gateway should be 172.17.42.1 but received %q", n.Gateway)
t.Fail()
}
if n.Mtu != 1500 {
t.Logf("veth mtu should be 1500 but received %d", n.Mtu)
t.Fail()
}
break
}
}
for _, d := range devices.DefaultSimpleDevices {
if !containsDevice(d, container.MountConfig.DeviceNodes) {
t.Logf("expected defice configuration for %s", d.Path)
t.Fail()
}
}
} }

View File

@ -8,7 +8,6 @@ import (
"path/filepath" "path/filepath"
"syscall" "syscall"
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/label" "github.com/docker/libcontainer/label"
"github.com/docker/libcontainer/mount/nodes" "github.com/docker/libcontainer/mount/nodes"
"github.com/dotcloud/docker/pkg/symlink" "github.com/dotcloud/docker/pkg/symlink"
@ -28,12 +27,12 @@ type mount struct {
// InitializeMountNamespace setups up the devices, mount points, and filesystems for use inside a // InitializeMountNamespace setups up the devices, mount points, and filesystems for use inside a
// new mount namepsace // new mount namepsace
func InitializeMountNamespace(rootfs, console string, container *libcontainer.Container) error { func InitializeMountNamespace(rootfs, console string, mountConfig *MountConfig) error {
var ( var (
err error err error
flag = syscall.MS_PRIVATE flag = syscall.MS_PRIVATE
) )
if container.NoPivotRoot { if mountConfig.NoPivotRoot {
flag = syscall.MS_SLAVE flag = syscall.MS_SLAVE
} }
if err := system.Mount("", "/", "", uintptr(flag|syscall.MS_REC), ""); err != nil { if err := system.Mount("", "/", "", uintptr(flag|syscall.MS_REC), ""); err != nil {
@ -42,16 +41,16 @@ func InitializeMountNamespace(rootfs, console string, container *libcontainer.Co
if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil { if err := system.Mount(rootfs, rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, ""); err != nil {
return fmt.Errorf("mouting %s as bind %s", rootfs, err) return fmt.Errorf("mouting %s as bind %s", rootfs, err)
} }
if err := mountSystem(rootfs, container); err != nil { if err := mountSystem(rootfs, mountConfig); err != nil {
return fmt.Errorf("mount system %s", err) return fmt.Errorf("mount system %s", err)
} }
if err := setupBindmounts(rootfs, container.Mounts); err != nil { if err := setupBindmounts(rootfs, mountConfig.Mounts); err != nil {
return fmt.Errorf("bind mounts %s", err) return fmt.Errorf("bind mounts %s", err)
} }
if err := nodes.CreateDeviceNodes(rootfs, container.DeviceNodes); err != nil { if err := nodes.CreateDeviceNodes(rootfs, mountConfig.DeviceNodes); err != nil {
return fmt.Errorf("create device nodes %s", err) return fmt.Errorf("create device nodes %s", err)
} }
if err := SetupPtmx(rootfs, console, container.Context["mount_label"]); err != nil { if err := SetupPtmx(rootfs, console, mountConfig.MountLabel); err != nil {
return err return err
} }
if err := setupDevSymlinks(rootfs); err != nil { if err := setupDevSymlinks(rootfs); err != nil {
@ -61,7 +60,7 @@ func InitializeMountNamespace(rootfs, console string, container *libcontainer.Co
return fmt.Errorf("chdir into %s %s", rootfs, err) return fmt.Errorf("chdir into %s %s", rootfs, err)
} }
if container.NoPivotRoot { if mountConfig.NoPivotRoot {
err = MsMoveRoot(rootfs) err = MsMoveRoot(rootfs)
} else { } else {
err = PivotRoot(rootfs) err = PivotRoot(rootfs)
@ -70,7 +69,7 @@ func InitializeMountNamespace(rootfs, console string, container *libcontainer.Co
return err return err
} }
if container.ReadonlyFs { if mountConfig.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)
} }
@ -83,8 +82,8 @@ func InitializeMountNamespace(rootfs, console string, container *libcontainer.Co
// mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts // mountSystem sets up linux specific system mounts like sys, proc, shm, and devpts
// inside the mount namespace // inside the mount namespace
func mountSystem(rootfs string, container *libcontainer.Container) error { func mountSystem(rootfs string, mountConfig *MountConfig) error {
for _, m := range newSystemMounts(rootfs, container.Context["mount_label"], container.Mounts) { for _, m := range newSystemMounts(rootfs, mountConfig.MountLabel, mountConfig.Mounts) {
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)
} }
@ -145,7 +144,7 @@ func setupDevSymlinks(rootfs string) error {
return nil return nil
} }
func setupBindmounts(rootfs string, bindMounts libcontainer.Mounts) error { func setupBindmounts(rootfs string, bindMounts Mounts) error {
for _, m := range bindMounts.OfType("bind") { for _, m := range bindMounts.OfType("bind") {
var ( var (
flags = syscall.MS_BIND | syscall.MS_REC flags = syscall.MS_BIND | syscall.MS_REC
@ -188,7 +187,7 @@ func setupBindmounts(rootfs string, bindMounts libcontainer.Mounts) error {
// TODO: this is crappy right now and should be cleaned up with a better way of handling system and // TODO: this is crappy right now and should be cleaned up with a better way of handling system and
// standard bind mounts allowing them to be more dynamic // standard bind mounts allowing them to be more dynamic
func newSystemMounts(rootfs, mountLabel string, mounts libcontainer.Mounts) []mount { func newSystemMounts(rootfs, mountLabel string, mounts Mounts) []mount {
systemMounts := []mount{ systemMounts := []mount{
{source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags}, {source: "proc", path: filepath.Join(rootfs, "proc"), device: "proc", flags: defaultMountFlags},
{source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaultMountFlags}, {source: "sysfs", path: filepath.Join(rootfs, "sys"), device: "sysfs", flags: defaultMountFlags},

View File

@ -3,10 +3,11 @@
package nodes package nodes
import ( import (
"github.com/docker/libcontainer" "errors"
"github.com/docker/libcontainer/devices" "github.com/docker/libcontainer/devices"
) )
func CreateDeviceNodes(rootfs string, nodesToCreate []*devices.Device) error { func CreateDeviceNodes(rootfs string, nodesToCreate []*devices.Device) error {
return libcontainer.ErrUnsupported return errors.New("Unsupported method")
} }

View File

@ -0,0 +1,47 @@
package mount
import (
"errors"
"github.com/docker/libcontainer/devices"
)
type MountConfig struct {
// NoPivotRoot will use MS_MOVE and a chroot to jail the process into the container's rootfs
// This is a common option when the container is running in ramdisk
NoPivotRoot bool `json:"no_pivot_root,omitempty"`
// ReadonlyFs will remount the container's rootfs as readonly where only externally mounted
// bind mounts are writtable
ReadonlyFs bool `json:"readonly_fs,omitempty"`
// Mounts specify additional source and destination paths that will be mounted inside the container's
// rootfs and mount namespace if specified
Mounts Mounts `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 []*devices.Device `json:"device_nodes,omitempty"`
MountLabel string `json:"mount_label,omitempty"`
}
type Mount struct {
Type string `json:"type,omitempty"`
Source string `json:"source,omitempty"` // Source path, in the host namespace
Destination string `json:"destination,omitempty"` // Destination path, in the container
Writable bool `json:"writable,omitempty"`
Private bool `json:"private,omitempty"`
}
type Mounts []Mount
var ErrUnsupported = errors.New("Unsupported method")
func (s Mounts) OfType(t string) Mounts {
out := Mounts{}
for _, m := range s {
if m.Type == t {
out = append(out, m)
}
}
return out
}

View File

@ -7,4 +7,4 @@ import (
"github.com/docker/libcontainer" "github.com/docker/libcontainer"
) )
type CreateCommand func(container *libcontainer.Container, console, rootfs, dataPath, init string, childPipe *os.File, args []string) *exec.Cmd type CreateCommand func(container *libcontainer.Config, console, rootfs, dataPath, init string, childPipe *os.File, args []string) *exec.Cmd

View File

@ -15,9 +15,11 @@ import (
"github.com/dotcloud/docker/pkg/system" "github.com/dotcloud/docker/pkg/system"
) )
// TODO(vishh): This is part of the libcontainer API and it does much more than just namespaces related work.
// Move this to libcontainer package.
// Exec performes setup outside of a namespace so that a container can be // Exec performes setup outside of a namespace so that a container can be
// executed. Exec is a high level function for working with container namespaces. // executed. Exec is a high level function for working with container namespaces.
func Exec(container *libcontainer.Container, term Terminal, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) { func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) {
var ( var (
master *os.File master *os.File
console string console string
@ -103,7 +105,7 @@ func Exec(container *libcontainer.Container, term Terminal, rootfs, dataPath str
// root: the path to the container json file and information // root: the path to the container json file and information
// pipe: sync pipe to syncronize the parent and child processes // pipe: sync pipe to syncronize the parent and child processes
// args: the arguemnts to pass to the container to run as the user's program // args: the arguemnts to pass to the container to run as the user's program
func DefaultCreateCommand(container *libcontainer.Container, console, rootfs, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { func DefaultCreateCommand(container *libcontainer.Config, console, rootfs, dataPath, init string, pipe *os.File, args []string) *exec.Cmd {
// get our binary name from arg0 so we can always reexec ourself // get our binary name from arg0 so we can always reexec ourself
env := []string{ env := []string{
"console=" + console, "console=" + console,
@ -135,7 +137,7 @@ func DefaultCreateCommand(container *libcontainer.Container, console, rootfs, da
// SetupCgroups applies the cgroup restrictions to the process running in the contaienr based // SetupCgroups applies the cgroup restrictions to the process running in the contaienr based
// on the container's configuration // on the container's configuration
func SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveCgroup, error) { func SetupCgroups(container *libcontainer.Config, nspid int) (cgroups.ActiveCgroup, error) {
if container.Cgroups != nil { if container.Cgroups != nil {
c := container.Cgroups c := container.Cgroups
if systemd.UseSystemd() { if systemd.UseSystemd() {
@ -148,14 +150,14 @@ func SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveC
// InitializeNetworking creates the container's network stack outside of the namespace and moves // InitializeNetworking creates the container's network stack outside of the namespace and moves
// interfaces into the container's net namespaces if necessary // interfaces into the container's net namespaces if necessary
func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error { func InitializeNetworking(container *libcontainer.Config, nspid int, pipe *SyncPipe) error {
context := libcontainer.Context{} context := map[string]string{}
for _, config := range container.Networks { for _, config := range container.Networks {
strategy, err := network.GetStrategy(config.Type) strategy, err := network.GetStrategy(config.Type)
if err != nil { if err != nil {
return err return err
} }
if err := strategy.Create(config, nspid, context); err != nil { if err := strategy.Create((*network.Network)(config), nspid, context); err != nil {
return err return err
} }
} }
@ -167,7 +169,7 @@ func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *Sy
func GetNamespaceFlags(namespaces map[string]bool) (flag int) { func GetNamespaceFlags(namespaces map[string]bool) (flag int) {
for key, enabled := range namespaces { for key, enabled := range namespaces {
if enabled { if enabled {
if ns := libcontainer.GetNamespace(key); ns != nil { if ns := GetNamespace(key); ns != nil {
flag |= ns.Value flag |= ns.Value
} }
} }

View File

@ -13,7 +13,7 @@ import (
) )
// ExecIn uses an existing pid and joins the pid's namespaces with the new command. // ExecIn uses an existing pid and joins the pid's namespaces with the new command.
func ExecIn(container *libcontainer.Container, nspid int, args []string) error { func ExecIn(container *libcontainer.Config, nspid int, args []string) error {
// TODO(vmarmol): If this gets too long, send it over a pipe to the child. // TODO(vmarmol): If this gets too long, send it over a pipe to the child.
// Marshall the container into JSON since it won't be available in the namespace. // Marshall the container into JSON since it won't be available in the namespace.
containerJson, err := json.Marshal(container) containerJson, err := json.Marshal(container)
@ -31,7 +31,7 @@ func ExecIn(container *libcontainer.Container, nspid int, args []string) error {
} }
// NsEnter is run after entering the namespace. // NsEnter is run after entering the namespace.
func NsEnter(container *libcontainer.Container, nspid int, args []string) error { func NsEnter(container *libcontainer.Config, nspid int, args []string) error {
// clear the current processes env and replace it with the environment // clear the current processes env and replace it with the environment
// defined on the container // defined on the container
if err := LoadContainerEnvironment(container); err != nil { if err := LoadContainerEnvironment(container); err != nil {

View File

@ -23,9 +23,11 @@ import (
"github.com/dotcloud/docker/pkg/user" "github.com/dotcloud/docker/pkg/user"
) )
// TODO(vishh): This is part of the libcontainer API and it does much more than just namespaces related work.
// Move this to libcontainer package.
// Init is the init process that first runs inside a new namespace to setup mounts, users, networking, // Init is the init process that first runs inside a new namespace to setup mounts, users, networking,
// and other options required for the new container. // and other options required for the new container.
func Init(container *libcontainer.Container, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) error { func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) error {
rootfs, err := utils.ResolveRootfs(uncleanRootfs) rootfs, err := utils.ResolveRootfs(uncleanRootfs)
if err != nil { if err != nil {
return err return err
@ -67,7 +69,9 @@ func Init(container *libcontainer.Container, uncleanRootfs, consolePath string,
label.Init() label.Init()
if err := mount.InitializeMountNamespace(rootfs, consolePath, container); err != nil { if err := mount.InitializeMountNamespace(rootfs,
consolePath,
(*mount.MountConfig)(container.MountConfig)); err != nil {
return fmt.Errorf("setup mount namespace %s", err) return fmt.Errorf("setup mount namespace %s", err)
} }
if container.Hostname != "" { if container.Hostname != "" {
@ -157,14 +161,14 @@ func SetupUser(u string) error {
// setupVethNetwork uses the Network config if it is not nil to initialize // setupVethNetwork uses the Network config if it is not nil to initialize
// the new veth interface inside the container for use by changing the name to eth0 // the new veth interface inside the container for use by changing the name to eth0
// setting the MTU and IP address along with the default gateway // setting the MTU and IP address along with the default gateway
func setupNetwork(container *libcontainer.Container, context libcontainer.Context) error { func setupNetwork(container *libcontainer.Config, context map[string]string) error {
for _, config := range container.Networks { for _, config := range container.Networks {
strategy, err := network.GetStrategy(config.Type) strategy, err := network.GetStrategy(config.Type)
if err != nil { if err != nil {
return err return err
} }
err1 := strategy.Initialize(config, context) err1 := strategy.Initialize((*network.Network)(config), context)
if err1 != nil { if err1 != nil {
return err1 return err1
} }
@ -172,7 +176,7 @@ func setupNetwork(container *libcontainer.Container, context libcontainer.Contex
return nil return nil
} }
func setupRoute(container *libcontainer.Container) error { func setupRoute(container *libcontainer.Config) error {
for _, config := range container.Routes { for _, config := range container.Routes {
if err := netlink.AddRoute(config.Destination, config.Source, config.Gateway, config.InterfaceName); err != nil { if err := netlink.AddRoute(config.Destination, config.Source, config.Gateway, config.InterfaceName); err != nil {
return err return err
@ -184,7 +188,7 @@ func setupRoute(container *libcontainer.Container) error {
// FinalizeNamespace drops the caps, sets the correct user // FinalizeNamespace drops the caps, sets the correct user
// and working dir, and closes any leaky file descriptors // and working dir, and closes any leaky file descriptors
// before execing the command inside the namespace // before execing the command inside the namespace
func FinalizeNamespace(container *libcontainer.Container) error { func FinalizeNamespace(container *libcontainer.Config) error {
// Ensure that all non-standard fds we may have accidentally // Ensure that all non-standard fds we may have accidentally
// inherited are marked close-on-exec so they stay out of the // inherited are marked close-on-exec so they stay out of the
// container // container
@ -193,7 +197,7 @@ func FinalizeNamespace(container *libcontainer.Container) error {
} }
// drop capabilities in bounding set before changing user // drop capabilities in bounding set before changing user
if err := capabilities.DropBoundingSet(container); err != nil { if err := capabilities.DropBoundingSet(container.Capabilities); err != nil {
return fmt.Errorf("drop bounding set %s", err) return fmt.Errorf("drop bounding set %s", err)
} }
@ -211,7 +215,7 @@ func FinalizeNamespace(container *libcontainer.Container) error {
} }
// drop all other capabilities // drop all other capabilities
if err := capabilities.DropCapabilities(container); err != nil { if err := capabilities.DropCapabilities(container.Capabilities); err != nil {
return fmt.Errorf("drop capabilities %s", err) return fmt.Errorf("drop capabilities %s", err)
} }
@ -224,7 +228,7 @@ func FinalizeNamespace(container *libcontainer.Container) error {
return nil return nil
} }
func LoadContainerEnvironment(container *libcontainer.Container) error { func LoadContainerEnvironment(container *libcontainer.Config) error {
os.Clearenv() os.Clearenv()
for _, pair := range container.Env { for _, pair := range container.Env {
p := strings.SplitN(pair, "=", 2) p := strings.SplitN(pair, "=", 2)

View File

@ -5,8 +5,6 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"github.com/docker/libcontainer"
) )
// SyncPipe allows communication to and from the child processes // SyncPipe allows communication to and from the child processes
@ -45,7 +43,7 @@ func (s *SyncPipe) Parent() *os.File {
return s.parent return s.parent
} }
func (s *SyncPipe) SendToChild(context libcontainer.Context) error { func (s *SyncPipe) SendToChild(context map[string]string) error {
data, err := json.Marshal(context) data, err := json.Marshal(context)
if err != nil { if err != nil {
return err return err
@ -54,12 +52,12 @@ func (s *SyncPipe) SendToChild(context libcontainer.Context) error {
return nil return nil
} }
func (s *SyncPipe) ReadFromParent() (libcontainer.Context, error) { func (s *SyncPipe) ReadFromParent() (map[string]string, error) {
data, err := ioutil.ReadAll(s.child) data, err := ioutil.ReadAll(s.child)
if err != nil { if err != nil {
return nil, fmt.Errorf("error reading from sync pipe %s", err) return nil, fmt.Errorf("error reading from sync pipe %s", err)
} }
var context libcontainer.Context var context map[string]string
if len(data) > 0 { if len(data) > 0 {
if err := json.Unmarshal(data, &context); err != nil { if err := json.Unmarshal(data, &context); err != nil {
return nil, err return nil, err

View File

@ -0,0 +1,50 @@
package namespaces
import "errors"
type (
Namespace struct {
Key string `json:"key,omitempty"`
Value int `json:"value,omitempty"`
File string `json:"file,omitempty"`
}
Namespaces []*Namespace
)
// namespaceList is used to convert the libcontainer types
// into the names of the files located in /proc/<pid>/ns/* for
// each namespace
var (
namespaceList = Namespaces{}
ErrUnkownNamespace = errors.New("Unknown namespace")
ErrUnsupported = errors.New("Unsupported method")
)
func (ns *Namespace) String() string {
return ns.Key
}
func GetNamespace(key string) *Namespace {
for _, ns := range namespaceList {
if ns.Key == key {
cpy := *ns
return &cpy
}
}
return nil
}
// Contains returns true if the specified Namespace is
// in the slice
func (n Namespaces) Contains(ns string) bool {
return n.Get(ns) != nil
}
func (n Namespaces) Get(ns string) *Namespace {
for _, nsp := range n {
if nsp != nil && nsp.Key == ns {
return nsp
}
}
return nil
}

View File

@ -1,4 +1,4 @@
package libcontainer package namespaces
import ( import (
"syscall" "syscall"

View File

@ -1,4 +1,4 @@
package libcontainer package namespaces
import ( import (
"testing" "testing"
@ -28,17 +28,3 @@ func TestNamespacesContains(t *testing.T) {
t.Fatal("namespaces should contain NEWPID but does not") t.Fatal("namespaces should contain NEWPID but does not")
} }
} }
func TestCapabilitiesContains(t *testing.T) {
caps := Capabilities{
GetCapability("MKNOD"),
GetCapability("SETPCAP"),
}
if caps.Contains("SYS_ADMIN") {
t.Fatal("capabilities should not contain SYS_ADMIN")
}
if !caps.Contains("MKNOD") {
t.Fatal("capabilities should contain MKNOD but does not")
}
}

View File

@ -7,20 +7,20 @@ import (
"github.com/docker/libcontainer/cgroups" "github.com/docker/libcontainer/cgroups"
) )
func Exec(container *libcontainer.Container, term Terminal, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) { func Exec(container *libcontainer.Config, term Terminal, rootfs, dataPath string, args []string, createCommand CreateCommand, startCallback func()) (int, error) {
return -1, libcontainer.ErrUnsupported return -1, ErrUnsupported
} }
func Init(container *libcontainer.Container, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) error { func Init(container *libcontainer.Config, uncleanRootfs, consolePath string, syncPipe *SyncPipe, args []string) error {
return libcontainer.ErrUnsupported return ErrUnsupported
} }
func InitializeNetworking(container *libcontainer.Container, nspid int, pipe *SyncPipe) error { func InitializeNetworking(container *libcontainer.Config, nspid int, pipe *SyncPipe) error {
return libcontainer.ErrUnsupported return ErrUnsupported
} }
func SetupCgroups(container *libcontainer.Container, nspid int) (cgroups.ActiveCgroup, error) { func SetupCgroups(container *libcontainer.Config, nspid int) (cgroups.ActiveCgroup, error) {
return nil, libcontainer.ErrUnsupported return nil, ErrUnsupported
} }
func GetNamespaceFlags(namespaces map[string]bool) (flag int) { func GetNamespaceFlags(namespaces map[string]bool) (flag int) {

View File

@ -1,19 +1,20 @@
// +build linux
package network package network
import ( import (
"fmt" "fmt"
"github.com/docker/libcontainer"
) )
// Loopback is a network strategy that provides a basic loopback device // Loopback is a network strategy that provides a basic loopback device
type Loopback struct { type Loopback struct {
} }
func (l *Loopback) Create(n *libcontainer.Network, nspid int, context libcontainer.Context) error { func (l *Loopback) Create(n *Network, nspid int, context map[string]string) error {
return nil return nil
} }
func (l *Loopback) Initialize(config *libcontainer.Network, context libcontainer.Context) error { func (l *Loopback) Initialize(config *Network, context map[string]string) error {
if err := SetMtu("lo", config.Mtu); err != nil { if err := SetMtu("lo", config.Mtu); err != nil {
return fmt.Errorf("set lo mtu to %d %s", config.Mtu, err) return fmt.Errorf("set lo mtu to %d %s", config.Mtu, err)
} }

View File

@ -1,3 +1,5 @@
// +build linux
package network package network
import ( import (
@ -5,7 +7,6 @@ import (
"os" "os"
"syscall" "syscall"
"github.com/docker/libcontainer"
"github.com/dotcloud/docker/pkg/system" "github.com/dotcloud/docker/pkg/system"
) )
@ -13,12 +14,12 @@ import (
type NetNS struct { type NetNS struct {
} }
func (v *NetNS) Create(n *libcontainer.Network, nspid int, context libcontainer.Context) error { func (v *NetNS) Create(n *Network, nspid int, context map[string]string) error {
context["nspath"] = n.Context["nspath"] context["nspath"] = n.NsPath
return nil return nil
} }
func (v *NetNS) Initialize(config *libcontainer.Network, context libcontainer.Context) error { func (v *NetNS) Initialize(config *Network, context map[string]string) error {
nspath, exists := context["nspath"] nspath, exists := context["nspath"]
if !exists { if !exists {
return fmt.Errorf("nspath does not exist in network context") return fmt.Errorf("nspath does not exist in network context")

View File

@ -1,8 +1,11 @@
// +build linux
package network package network
import ( import (
"github.com/docker/libcontainer/netlink"
"net" "net"
"github.com/docker/libcontainer/netlink"
) )
func InterfaceUp(name string) error { func InterfaceUp(name string) error {

View File

@ -1,9 +1,9 @@
// +build linux
package network package network
import ( import (
"errors" "errors"
"github.com/docker/libcontainer"
) )
var ( var (
@ -19,8 +19,8 @@ var strategies = map[string]NetworkStrategy{
// NetworkStrategy represents a specific network configuration for // NetworkStrategy represents a specific network configuration for
// a container's networking stack // a container's networking stack
type NetworkStrategy interface { type NetworkStrategy interface {
Create(*libcontainer.Network, int, libcontainer.Context) error Create(*Network, int, map[string]string) error
Initialize(*libcontainer.Network, libcontainer.Context) error Initialize(*Network, map[string]string) error
} }
// GetStrategy returns the specific network strategy for the // GetStrategy returns the specific network strategy for the

View File

@ -0,0 +1,29 @@
package network
// Network defines configuration for a container's networking stack
//
// The network configuration can be omited from a container causing the
// container to be setup with the host's networking stack
type Network struct {
// Type sets the networks type, commonly veth and loopback
Type string `json:"type,omitempty"`
// Path to network namespace
NsPath string `json:"ns_path,omitempty"`
// The bridge to use.
Bridge string `json:"bridge,omitempty"`
// Prefix for the veth interfaces.
VethPrefix string `json:"veth_prefix,omitempty"`
// Address contains the IP and mask to set on the network interface
Address string `json:"address,omitempty"`
// Gateway sets the gateway address that is used as the default for the interface
Gateway string `json:"gateway,omitempty"`
// Mtu sets the mtu value for the interface and will be mirrored on both the host and
// container's interfaces if a pair is created, specifically in the case of type veth
Mtu int `json:"mtu,omitempty"`
}

View File

@ -1,8 +1,10 @@
// +build linux
package network package network
import ( import (
"fmt" "fmt"
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/utils" "github.com/docker/libcontainer/utils"
) )
@ -14,17 +16,16 @@ type Veth struct {
const defaultDevice = "eth0" const defaultDevice = "eth0"
func (v *Veth) Create(n *libcontainer.Network, nspid int, context libcontainer.Context) error { func (v *Veth) Create(n *Network, nspid int, context map[string]string) error {
var ( var (
bridge string bridge = n.Bridge
prefix string prefix = n.VethPrefix
exists bool
) )
if bridge, exists = n.Context["bridge"]; !exists { if bridge == "" {
return fmt.Errorf("bridge does not exist in network context") return fmt.Errorf("bridge is not specified")
} }
if prefix, exists = n.Context["prefix"]; !exists { if prefix == "" {
return fmt.Errorf("veth prefix does not exist in network context") return fmt.Errorf("veth prefix is not specified")
} }
name1, name2, err := createVethPair(prefix) name1, name2, err := createVethPair(prefix)
if err != nil { if err != nil {
@ -47,7 +48,7 @@ func (v *Veth) Create(n *libcontainer.Network, nspid int, context libcontainer.C
return nil return nil
} }
func (v *Veth) Initialize(config *libcontainer.Network, context libcontainer.Context) error { func (v *Veth) Initialize(config *Network, context map[string]string) error {
var ( var (
vethChild string vethChild string
exists bool exists bool

View File

@ -48,7 +48,7 @@ func execAction(context *cli.Context) {
// error. // error.
// //
// Signals sent to the current process will be forwarded to container. // Signals sent to the current process will be forwarded to container.
func startContainer(container *libcontainer.Container, term namespaces.Terminal, dataPath string, args []string) (int, error) { func startContainer(container *libcontainer.Config, term namespaces.Terminal, dataPath string, args []string) (int, error) {
var ( var (
cmd *exec.Cmd cmd *exec.Cmd
sigc = make(chan os.Signal, 10) sigc = make(chan os.Signal, 10)
@ -56,7 +56,7 @@ func startContainer(container *libcontainer.Container, term namespaces.Terminal,
signal.Notify(sigc) signal.Notify(sigc)
createCommand := func(container *libcontainer.Container, console, rootfs, dataPath, init string, pipe *os.File, args []string) *exec.Cmd { createCommand := func(container *libcontainer.Config, console, rootfs, dataPath, init string, pipe *os.File, args []string) *exec.Cmd {
cmd = namespaces.DefaultCreateCommand(container, console, rootfs, dataPath, init, pipe, args) cmd = namespaces.DefaultCreateCommand(container, console, rootfs, dataPath, init, pipe, args)
if logPath != "" { if logPath != "" {
cmd.Env = append(cmd.Env, fmt.Sprintf("log=%s", logPath)) cmd.Env = append(cmd.Env, fmt.Sprintf("log=%s", logPath))

View File

@ -30,7 +30,7 @@ func specAction(context *cli.Context) {
} }
// returns the container spec in json format. // returns the container spec in json format.
func getContainerSpec(container *libcontainer.Container) (string, error) { func getContainerSpec(container *libcontainer.Config) (string, error) {
spec, err := json.MarshalIndent(container, "", "\t") spec, err := json.MarshalIndent(container, "", "\t")
if err != nil { if err != nil {
return "", err return "", err

View File

@ -31,7 +31,7 @@ func statsAction(context *cli.Context) {
} }
// returns the container stats in json format. // returns the container stats in json format.
func getContainerStats(container *libcontainer.Container) (string, error) { func getContainerStats(container *libcontainer.Config) (string, error) {
stats, err := fs.GetStats(container.Cgroups) stats, err := fs.GetStats(container.Cgroups)
if err != nil { if err != nil {
return "", err return "", err

View File

@ -11,14 +11,14 @@ import (
"github.com/docker/libcontainer" "github.com/docker/libcontainer"
) )
func loadContainer() (*libcontainer.Container, error) { func loadContainer() (*libcontainer.Config, error) {
f, err := os.Open(filepath.Join(dataPath, "container.json")) f, err := os.Open(filepath.Join(dataPath, "container.json"))
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer f.Close() defer f.Close()
var container *libcontainer.Container var container *libcontainer.Config
if err := json.NewDecoder(f).Decode(&container); err != nil { if err := json.NewDecoder(f).Decode(&container); err != nil {
return nil, err return nil, err
} }
@ -51,8 +51,8 @@ func openLog(name string) error {
return nil return nil
} }
func loadContainerFromJson(rawData string) (*libcontainer.Container, error) { func loadContainerFromJson(rawData string) (*libcontainer.Config, error) {
var container *libcontainer.Container var container *libcontainer.Config
if err := json.Unmarshal([]byte(rawData), &container); err != nil { if err := json.Unmarshal([]byte(rawData), &container); err != nil {
return nil, err return nil, err

View File

@ -0,0 +1,5 @@
These configuration files can be used with `nsinit` to quickly develop, test,
and experiment with features of libcontainer.
When consuming these configuration files, copy them into your rootfs and rename
the file to `container.json` for use with `nsinit`.

View File

@ -121,66 +121,61 @@
"process_label": "", "process_label": "",
"restrictions": "true" "restrictions": "true"
}, },
"device_nodes": [ "mount_config": {
{ "device_nodes": [
"cgroup_permissions": "rwm", {
"major_number": 10, "cgroup_permissions": "rwm",
"minor_number": 229, "file_mode": 438,
"path": "/dev/fuse", "major_number": 1,
"type": 99 "minor_number": 3,
}, "path": "/dev/null",
{ "type": 99
"cgroup_permissions": "rwm", },
"file_mode": 438, {
"major_number": 1, "cgroup_permissions": "rwm",
"minor_number": 3, "file_mode": 438,
"path": "/dev/null", "major_number": 1,
"type": 99 "minor_number": 5,
}, "path": "/dev/zero",
{ "type": 99
"cgroup_permissions": "rwm", },
"file_mode": 438, {
"major_number": 1, "cgroup_permissions": "rwm",
"minor_number": 5, "file_mode": 438,
"path": "/dev/zero", "major_number": 1,
"type": 99 "minor_number": 7,
}, "path": "/dev/full",
{ "type": 99
"cgroup_permissions": "rwm", },
"file_mode": 438, {
"major_number": 1, "cgroup_permissions": "rwm",
"minor_number": 7, "file_mode": 438,
"path": "/dev/full", "major_number": 5,
"type": 99 "path": "/dev/tty",
}, "type": 99
{ },
"cgroup_permissions": "rwm", {
"file_mode": 438, "cgroup_permissions": "rwm",
"major_number": 5, "file_mode": 438,
"path": "/dev/tty", "major_number": 1,
"type": 99 "minor_number": 9,
}, "path": "/dev/urandom",
{ "type": 99
"cgroup_permissions": "rwm", },
"file_mode": 438, {
"major_number": 1, "cgroup_permissions": "rwm",
"minor_number": 9, "file_mode": 438,
"path": "/dev/urandom", "major_number": 1,
"type": 99 "minor_number": 8,
}, "path": "/dev/random",
{ "type": 99
"cgroup_permissions": "rwm", }
"file_mode": 438, ]
"major_number": 1, },
"minor_number": 8,
"path": "/dev/random",
"type": 99
}
],
"environment": [ "environment": [
"HOME=/", "HOME=/",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOSTNAME=2d388ea3bd92", "HOSTNAME=koye",
"TERM=xterm" "TERM=xterm"
], ],
"hostname": "koye", "hostname": "koye",
@ -200,10 +195,8 @@
}, },
{ {
"address": "172.17.0.101/16", "address": "172.17.0.101/16",
"context": { "bridge": "docker0",
"bridge": "docker0", "veth_prefix": "veth",
"prefix": "veth"
},
"gateway": "172.17.42.1", "gateway": "172.17.42.1",
"mtu": 1500, "mtu": 1500,
"type": "veth" "type": "veth"

View File

@ -0,0 +1,199 @@
{
"capabilities": [
"CHOWN",
"DAC_OVERRIDE",
"FOWNER",
"MKNOD",
"NET_RAW",
"SETGID",
"SETUID",
"SETFCAP",
"SETPCAP",
"NET_BIND_SERVICE",
"SYS_CHROOT",
"KILL"
],
"cgroups": {
"allowed_devices": [
{
"cgroup_permissions": "m",
"major_number": -1,
"minor_number": -1,
"type": 99
},
{
"cgroup_permissions": "m",
"major_number": -1,
"minor_number": -1,
"type": 98
},
{
"cgroup_permissions": "rwm",
"major_number": 5,
"minor_number": 1,
"path": "/dev/console",
"type": 99
},
{
"cgroup_permissions": "rwm",
"major_number": 4,
"path": "/dev/tty0",
"type": 99
},
{
"cgroup_permissions": "rwm",
"major_number": 4,
"minor_number": 1,
"path": "/dev/tty1",
"type": 99
},
{
"cgroup_permissions": "rwm",
"major_number": 136,
"minor_number": -1,
"type": 99
},
{
"cgroup_permissions": "rwm",
"major_number": 5,
"minor_number": 2,
"type": 99
},
{
"cgroup_permissions": "rwm",
"major_number": 10,
"minor_number": 200,
"type": 99
},
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 1,
"minor_number": 3,
"path": "/dev/null",
"type": 99
},
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 1,
"minor_number": 5,
"path": "/dev/zero",
"type": 99
},
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 1,
"minor_number": 7,
"path": "/dev/full",
"type": 99
},
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 5,
"path": "/dev/tty",
"type": 99
},
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 1,
"minor_number": 9,
"path": "/dev/urandom",
"type": 99
},
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 1,
"minor_number": 8,
"path": "/dev/random",
"type": 99
}
],
"name": "docker-koye",
"parent": "docker"
},
"context": {
"mount_label": "",
"process_label": "",
"restrictions": "true"
},
"mount_config": {
"device_nodes": [
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 1,
"minor_number": 3,
"path": "/dev/null",
"type": 99
},
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 1,
"minor_number": 5,
"path": "/dev/zero",
"type": 99
},
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 1,
"minor_number": 7,
"path": "/dev/full",
"type": 99
},
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 5,
"path": "/dev/tty",
"type": 99
},
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 1,
"minor_number": 9,
"path": "/dev/urandom",
"type": 99
},
{
"cgroup_permissions": "rwm",
"file_mode": 438,
"major_number": 1,
"minor_number": 8,
"path": "/dev/random",
"type": 99
}
]
},
"environment": [
"HOME=/",
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"HOSTNAME=koye",
"TERM=xterm"
],
"hostname": "koye",
"namespaces": {
"NEWIPC": true,
"NEWNET": true,
"NEWNS": true,
"NEWPID": true,
"NEWUTS": true
},
"networks": [
{
"address": "127.0.0.1/0",
"gateway": "localhost",
"mtu": 1500,
"type": "loopback"
}
],
"tty": true,
"user": "daemon"
}

View File

@ -3,7 +3,6 @@ package capabilities
import ( import (
"os" "os"
"github.com/docker/libcontainer"
"github.com/syndtr/gocapability/capability" "github.com/syndtr/gocapability/capability"
) )
@ -11,13 +10,13 @@ const allCapabilityTypes = capability.CAPS | capability.BOUNDS
// DropBoundingSet drops the capability bounding set to those specified in the // DropBoundingSet drops the capability bounding set to those specified in the
// container configuration. // container configuration.
func DropBoundingSet(container *libcontainer.Container) error { func DropBoundingSet(capabilities []string) error {
c, err := capability.NewPid(os.Getpid()) c, err := capability.NewPid(os.Getpid())
if err != nil { if err != nil {
return err return err
} }
keep := getEnabledCapabilities(container) keep := getEnabledCapabilities(capabilities)
c.Clear(capability.BOUNDS) c.Clear(capability.BOUNDS)
c.Set(capability.BOUNDS, keep...) c.Set(capability.BOUNDS, keep...)
@ -29,13 +28,13 @@ func DropBoundingSet(container *libcontainer.Container) error {
} }
// DropCapabilities drops all capabilities for the current process expect those specified in the container configuration. // DropCapabilities drops all capabilities for the current process expect those specified in the container configuration.
func DropCapabilities(container *libcontainer.Container) error { func DropCapabilities(capList []string) error {
c, err := capability.NewPid(os.Getpid()) c, err := capability.NewPid(os.Getpid())
if err != nil { if err != nil {
return err return err
} }
keep := getEnabledCapabilities(container) keep := getEnabledCapabilities(capList)
c.Clear(allCapabilityTypes) c.Clear(allCapabilityTypes)
c.Set(allCapabilityTypes, keep...) c.Set(allCapabilityTypes, keep...)
@ -46,10 +45,10 @@ func DropCapabilities(container *libcontainer.Container) error {
} }
// getEnabledCapabilities returns the capabilities that should not be dropped by the container. // getEnabledCapabilities returns the capabilities that should not be dropped by the container.
func getEnabledCapabilities(container *libcontainer.Container) []capability.Cap { func getEnabledCapabilities(capList []string) []capability.Cap {
keep := []capability.Cap{} keep := []capability.Cap{}
for _, capability := range container.Capabilities { for _, capability := range capList {
if c := libcontainer.GetCapability(capability); c != nil { if c := GetCapability(capability); c != nil {
keep = append(keep, c.Value) keep = append(keep, c.Value)
} }
} }

View File

@ -0,0 +1,90 @@
package capabilities
import "github.com/syndtr/gocapability/capability"
type (
CapabilityMapping struct {
Key string `json:"key,omitempty"`
Value capability.Cap `json:"value,omitempty"`
}
Capabilities []*CapabilityMapping
)
func (c *CapabilityMapping) String() string {
return c.Key
}
func GetCapability(key string) *CapabilityMapping {
for _, capp := range capabilityList {
if capp.Key == key {
cpy := *capp
return &cpy
}
}
return nil
}
func GetAllCapabilities() []string {
output := make([]string, len(capabilityList))
for i, capability := range capabilityList {
output[i] = capability.String()
}
return output
}
// Contains returns true if the specified Capability is
// in the slice
func (c Capabilities) contains(capp string) bool {
return c.get(capp) != nil
}
func (c Capabilities) get(capp string) *CapabilityMapping {
for _, cap := range c {
if cap.Key == capp {
return cap
}
}
return nil
}
var capabilityList = Capabilities{
{Key: "SETPCAP", Value: capability.CAP_SETPCAP},
{Key: "SYS_MODULE", Value: capability.CAP_SYS_MODULE},
{Key: "SYS_RAWIO", Value: capability.CAP_SYS_RAWIO},
{Key: "SYS_PACCT", Value: capability.CAP_SYS_PACCT},
{Key: "SYS_ADMIN", Value: capability.CAP_SYS_ADMIN},
{Key: "SYS_NICE", Value: capability.CAP_SYS_NICE},
{Key: "SYS_RESOURCE", Value: capability.CAP_SYS_RESOURCE},
{Key: "SYS_TIME", Value: capability.CAP_SYS_TIME},
{Key: "SYS_TTY_CONFIG", Value: capability.CAP_SYS_TTY_CONFIG},
{Key: "MKNOD", Value: capability.CAP_MKNOD},
{Key: "AUDIT_WRITE", Value: capability.CAP_AUDIT_WRITE},
{Key: "AUDIT_CONTROL", Value: capability.CAP_AUDIT_CONTROL},
{Key: "MAC_OVERRIDE", Value: capability.CAP_MAC_OVERRIDE},
{Key: "MAC_ADMIN", Value: capability.CAP_MAC_ADMIN},
{Key: "NET_ADMIN", Value: capability.CAP_NET_ADMIN},
{Key: "SYSLOG", Value: capability.CAP_SYSLOG},
{Key: "SETUID", Value: capability.CAP_SETUID},
{Key: "SETGID", Value: capability.CAP_SETGID},
{Key: "CHOWN", Value: capability.CAP_CHOWN},
{Key: "NET_RAW", Value: capability.CAP_NET_RAW},
{Key: "DAC_OVERRIDE", Value: capability.CAP_DAC_OVERRIDE},
{Key: "FOWNER", Value: capability.CAP_FOWNER},
{Key: "DAC_READ_SEARCH", Value: capability.CAP_DAC_READ_SEARCH},
{Key: "FSETID", Value: capability.CAP_FSETID},
{Key: "KILL", Value: capability.CAP_KILL},
{Key: "SETGID", Value: capability.CAP_SETGID},
{Key: "SETUID", Value: capability.CAP_SETUID},
{Key: "LINUX_IMMUTABLE", Value: capability.CAP_LINUX_IMMUTABLE},
{Key: "NET_BIND_SERVICE", Value: capability.CAP_NET_BIND_SERVICE},
{Key: "NET_BROADCAST", Value: capability.CAP_NET_BROADCAST},
{Key: "IPC_LOCK", Value: capability.CAP_IPC_LOCK},
{Key: "IPC_OWNER", Value: capability.CAP_IPC_OWNER},
{Key: "SYS_CHROOT", Value: capability.CAP_SYS_CHROOT},
{Key: "SYS_PTRACE", Value: capability.CAP_SYS_PTRACE},
{Key: "SYS_BOOT", Value: capability.CAP_SYS_BOOT},
{Key: "LEASE", Value: capability.CAP_LEASE},
{Key: "SETFCAP", Value: capability.CAP_SETFCAP},
{Key: "WAKE_ALARM", Value: capability.CAP_WAKE_ALARM},
{Key: "BLOCK_SUSPEND", Value: capability.CAP_BLOCK_SUSPEND},
}

View File

@ -0,0 +1,19 @@
package capabilities
import (
"testing"
)
func TestCapabilitiesContains(t *testing.T) {
caps := Capabilities{
GetCapability("MKNOD"),
GetCapability("SETPCAP"),
}
if caps.contains("SYS_ADMIN") {
t.Fatal("capabilities should not contain SYS_ADMIN")
}
if !caps.contains("MKNOD") {
t.Fatal("capabilities should contain MKNOD but does not")
}
}

View File

@ -1,165 +0,0 @@
package libcontainer
import (
"errors"
"github.com/syndtr/gocapability/capability"
)
var (
ErrUnkownNamespace = errors.New("Unknown namespace")
ErrUnkownCapability = errors.New("Unknown capability")
ErrUnsupported = errors.New("Unsupported method")
)
type Mounts []Mount
func (s Mounts) OfType(t string) Mounts {
out := Mounts{}
for _, m := range s {
if m.Type == t {
out = append(out, m)
}
}
return out
}
type Mount struct {
Type string `json:"type,omitempty"`
Source string `json:"source,omitempty"` // Source path, in the host namespace
Destination string `json:"destination,omitempty"` // Destination path, in the container
Writable bool `json:"writable,omitempty"`
Private bool `json:"private,omitempty"`
}
// namespaceList is used to convert the libcontainer types
// into the names of the files located in /proc/<pid>/ns/* for
// each namespace
var (
namespaceList = Namespaces{}
capabilityList = Capabilities{
{Key: "SETPCAP", Value: capability.CAP_SETPCAP},
{Key: "SYS_MODULE", Value: capability.CAP_SYS_MODULE},
{Key: "SYS_RAWIO", Value: capability.CAP_SYS_RAWIO},
{Key: "SYS_PACCT", Value: capability.CAP_SYS_PACCT},
{Key: "SYS_ADMIN", Value: capability.CAP_SYS_ADMIN},
{Key: "SYS_NICE", Value: capability.CAP_SYS_NICE},
{Key: "SYS_RESOURCE", Value: capability.CAP_SYS_RESOURCE},
{Key: "SYS_TIME", Value: capability.CAP_SYS_TIME},
{Key: "SYS_TTY_CONFIG", Value: capability.CAP_SYS_TTY_CONFIG},
{Key: "MKNOD", Value: capability.CAP_MKNOD},
{Key: "AUDIT_WRITE", Value: capability.CAP_AUDIT_WRITE},
{Key: "AUDIT_CONTROL", Value: capability.CAP_AUDIT_CONTROL},
{Key: "MAC_OVERRIDE", Value: capability.CAP_MAC_OVERRIDE},
{Key: "MAC_ADMIN", Value: capability.CAP_MAC_ADMIN},
{Key: "NET_ADMIN", Value: capability.CAP_NET_ADMIN},
{Key: "SYSLOG", Value: capability.CAP_SYSLOG},
{Key: "SETUID", Value: capability.CAP_SETUID},
{Key: "SETGID", Value: capability.CAP_SETGID},
{Key: "CHOWN", Value: capability.CAP_CHOWN},
{Key: "NET_RAW", Value: capability.CAP_NET_RAW},
{Key: "DAC_OVERRIDE", Value: capability.CAP_DAC_OVERRIDE},
{Key: "FOWNER", Value: capability.CAP_FOWNER},
{Key: "DAC_READ_SEARCH", Value: capability.CAP_DAC_READ_SEARCH},
{Key: "FSETID", Value: capability.CAP_FSETID},
{Key: "KILL", Value: capability.CAP_KILL},
{Key: "SETGID", Value: capability.CAP_SETGID},
{Key: "SETUID", Value: capability.CAP_SETUID},
{Key: "LINUX_IMMUTABLE", Value: capability.CAP_LINUX_IMMUTABLE},
{Key: "NET_BIND_SERVICE", Value: capability.CAP_NET_BIND_SERVICE},
{Key: "NET_BROADCAST", Value: capability.CAP_NET_BROADCAST},
{Key: "IPC_LOCK", Value: capability.CAP_IPC_LOCK},
{Key: "IPC_OWNER", Value: capability.CAP_IPC_OWNER},
{Key: "SYS_CHROOT", Value: capability.CAP_SYS_CHROOT},
{Key: "SYS_PTRACE", Value: capability.CAP_SYS_PTRACE},
{Key: "SYS_BOOT", Value: capability.CAP_SYS_BOOT},
{Key: "LEASE", Value: capability.CAP_LEASE},
{Key: "SETFCAP", Value: capability.CAP_SETFCAP},
{Key: "WAKE_ALARM", Value: capability.CAP_WAKE_ALARM},
{Key: "BLOCK_SUSPEND", Value: capability.CAP_BLOCK_SUSPEND},
}
)
type (
Namespace struct {
Key string `json:"key,omitempty"`
Value int `json:"value,omitempty"`
File string `json:"file,omitempty"`
}
Namespaces []*Namespace
)
func (ns *Namespace) String() string {
return ns.Key
}
func GetNamespace(key string) *Namespace {
for _, ns := range namespaceList {
if ns.Key == key {
cpy := *ns
return &cpy
}
}
return nil
}
// Contains returns true if the specified Namespace is
// in the slice
func (n Namespaces) Contains(ns string) bool {
return n.Get(ns) != nil
}
func (n Namespaces) Get(ns string) *Namespace {
for _, nsp := range n {
if nsp != nil && nsp.Key == ns {
return nsp
}
}
return nil
}
type (
Capability struct {
Key string `json:"key,omitempty"`
Value capability.Cap `json:"value,omitempty"`
}
Capabilities []*Capability
)
func (c *Capability) String() string {
return c.Key
}
func GetCapability(key string) *Capability {
for _, capp := range capabilityList {
if capp.Key == key {
cpy := *capp
return &cpy
}
}
return nil
}
func GetAllCapabilities() []string {
output := make([]string, len(capabilityList))
for i, capability := range capabilityList {
output[i] = capability.String()
}
return output
}
// Contains returns true if the specified Capability is
// in the slice
func (c Capabilities) Contains(capp string) bool {
return c.Get(capp) != nil
}
func (c Capabilities) Get(capp string) *Capability {
for _, cap := range c {
if cap.Key == capp {
return cap
}
}
return nil
}