Strongly type exec driver context

This also removes dead code in the native driver for a past feature that
was never fully implemented.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
Upstream-commit: 32dca1a7b0e800d796e54fc8f253818ba64fa075
Component: engine
This commit is contained in:
Michael Crosby
2014-09-29 22:40:26 +00:00
parent 1a11728b82
commit 280ec2913c
10 changed files with 53 additions and 464 deletions

View File

@ -1,188 +0,0 @@
package configuration
import (
"fmt"
"os/exec"
"path/filepath"
"strconv"
"strings"
"github.com/docker/docker/pkg/units"
"github.com/docker/libcontainer"
)
type Action func(*libcontainer.Config, interface{}, string) error
var actions = map[string]Action{
"cap.add": addCap, // add a cap
"cap.drop": dropCap, // drop a cap
"ns.add": addNamespace, // add a namespace
"ns.drop": dropNamespace, // drop a namespace when cloning
"net.join": joinNetNamespace, // join another containers net namespace
"cgroups.cpu_shares": cpuShares, // set the cpu shares
"cgroups.memory": memory, // set the memory limit
"cgroups.memory_reservation": memoryReservation, // set the memory reservation
"cgroups.memory_swap": memorySwap, // set the memory swap limit
"cgroups.cpuset.cpus": cpusetCpus, // set the cpus used
"systemd.slice": systemdSlice, // set parent Slice used for systemd unit
"apparmor_profile": apparmorProfile, // set the apparmor profile to apply
"fs.readonly": readonlyFs, // make the rootfs of the container read only
}
func cpusetCpus(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil {
return fmt.Errorf("cannot set cgroups when they are disabled")
}
container.Cgroups.CpusetCpus = value
return nil
}
func systemdSlice(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil {
return fmt.Errorf("cannot set slice when cgroups are disabled")
}
container.Cgroups.Slice = value
return nil
}
func apparmorProfile(container *libcontainer.Config, context interface{}, value string) error {
container.AppArmorProfile = value
return nil
}
func cpuShares(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil {
return fmt.Errorf("cannot set cgroups when they are disabled")
}
v, err := strconv.ParseInt(value, 10, 0)
if err != nil {
return err
}
container.Cgroups.CpuShares = v
return nil
}
func memory(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil {
return fmt.Errorf("cannot set cgroups when they are disabled")
}
v, err := units.RAMInBytes(value)
if err != nil {
return err
}
container.Cgroups.Memory = v
return nil
}
func memoryReservation(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil {
return fmt.Errorf("cannot set cgroups when they are disabled")
}
v, err := units.RAMInBytes(value)
if err != nil {
return err
}
container.Cgroups.MemoryReservation = v
return nil
}
func memorySwap(container *libcontainer.Config, context interface{}, value string) error {
if container.Cgroups == nil {
return fmt.Errorf("cannot set cgroups when they are disabled")
}
v, err := strconv.ParseInt(value, 0, 64)
if err != nil {
return err
}
container.Cgroups.MemorySwap = v
return nil
}
func addCap(container *libcontainer.Config, context interface{}, value string) error {
container.Capabilities = append(container.Capabilities, value)
return nil
}
func dropCap(container *libcontainer.Config, context interface{}, value string) error {
// If the capability is specified multiple times, remove all instances.
for i, capability := range container.Capabilities {
if capability == value {
container.Capabilities = append(container.Capabilities[:i], container.Capabilities[i+1:]...)
}
}
// The capability wasn't found so we will drop it anyways.
return nil
}
func addNamespace(container *libcontainer.Config, context interface{}, value string) error {
container.Namespaces[value] = true
return nil
}
func dropNamespace(container *libcontainer.Config, context interface{}, value string) error {
container.Namespaces[value] = false
return nil
}
func readonlyFs(container *libcontainer.Config, context interface{}, value string) error {
switch value {
case "1", "true":
container.MountConfig.ReadonlyFs = true
default:
container.MountConfig.ReadonlyFs = false
}
return nil
}
func joinNetNamespace(container *libcontainer.Config, context interface{}, value string) error {
var (
running = context.(map[string]*exec.Cmd)
cmd = running[value]
)
if cmd == nil || cmd.Process == nil {
return fmt.Errorf("%s is not a valid running container to join", value)
}
nspath := filepath.Join("/proc", fmt.Sprint(cmd.Process.Pid), "ns", "net")
container.Networks = append(container.Networks, &libcontainer.Network{
Type: "netns",
NsPath: nspath,
})
return nil
}
// configureCustomOptions takes string commands from the user and allows modification of the
// container's default configuration.
//
// TODO: this can be moved to a general utils or parser in pkg
func ParseConfiguration(container *libcontainer.Config, running map[string]*exec.Cmd, opts []string) error {
for _, opt := range opts {
kv := strings.SplitN(opt, "=", 2)
if len(kv) < 2 {
return fmt.Errorf("invalid format for %s", opt)
}
action, exists := actions[kv[0]]
if !exists {
return fmt.Errorf("%s is not a valid option for the native driver", kv[0])
}
if err := action(container, running, kv[1]); err != nil {
return err
}
}
return nil
}

View File

@ -1,193 +0,0 @@
package configuration
import (
"testing"
"github.com/docker/docker/daemon/execdriver/native/template"
"github.com/docker/libcontainer/security/capabilities"
)
// Checks whether the expected capability is specified in the capabilities.
func hasCapability(expected string, capabilities []string) bool {
for _, capability := range capabilities {
if capability == expected {
return true
}
}
return false
}
func TestSetReadonlyRootFs(t *testing.T) {
var (
container = template.New()
opts = []string{
"fs.readonly=true",
}
)
if container.MountConfig.ReadonlyFs {
t.Fatal("container should not have a readonly rootfs by default")
}
if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err)
}
if !container.MountConfig.ReadonlyFs {
t.Fatal("container should have a readonly rootfs")
}
}
func TestConfigurationsDoNotConflict(t *testing.T) {
var (
container1 = template.New()
container2 = template.New()
opts = []string{
"cap.add=NET_ADMIN",
}
)
if err := ParseConfiguration(container1, nil, opts); err != nil {
t.Fatal(err)
}
if !hasCapability("NET_ADMIN", container1.Capabilities) {
t.Fatal("container one should have NET_ADMIN enabled")
}
if hasCapability("NET_ADMIN", container2.Capabilities) {
t.Fatal("container two should not have NET_ADMIN enabled")
}
}
func TestCpusetCpus(t *testing.T) {
var (
container = template.New()
opts = []string{
"cgroups.cpuset.cpus=1,2",
}
)
if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err)
}
if expected := "1,2"; container.Cgroups.CpusetCpus != expected {
t.Fatalf("expected %s got %s for cpuset.cpus", expected, container.Cgroups.CpusetCpus)
}
}
func TestAppArmorProfile(t *testing.T) {
var (
container = template.New()
opts = []string{
"apparmor_profile=koye-the-protector",
}
)
if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err)
}
if expected := "koye-the-protector"; container.AppArmorProfile != expected {
t.Fatalf("expected profile %s got %s", expected, container.AppArmorProfile)
}
}
func TestCpuShares(t *testing.T) {
var (
container = template.New()
opts = []string{
"cgroups.cpu_shares=1048",
}
)
if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err)
}
if expected := int64(1048); container.Cgroups.CpuShares != expected {
t.Fatalf("expected cpu shares %d got %d", expected, container.Cgroups.CpuShares)
}
}
func TestMemory(t *testing.T) {
var (
container = template.New()
opts = []string{
"cgroups.memory=500m",
}
)
if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err)
}
if expected := int64(500 * 1024 * 1024); container.Cgroups.Memory != expected {
t.Fatalf("expected memory %d got %d", expected, container.Cgroups.Memory)
}
}
func TestMemoryReservation(t *testing.T) {
var (
container = template.New()
opts = []string{
"cgroups.memory_reservation=500m",
}
)
if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err)
}
if expected := int64(500 * 1024 * 1024); container.Cgroups.MemoryReservation != expected {
t.Fatalf("expected memory reservation %d got %d", expected, container.Cgroups.MemoryReservation)
}
}
func TestAddCap(t *testing.T) {
var (
container = template.New()
opts = []string{
"cap.add=MKNOD",
"cap.add=SYS_ADMIN",
}
)
if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err)
}
if !hasCapability("MKNOD", container.Capabilities) {
t.Fatal("container should have MKNOD enabled")
}
if !hasCapability("SYS_ADMIN", container.Capabilities) {
t.Fatal("container should have SYS_ADMIN enabled")
}
}
func TestDropCap(t *testing.T) {
var (
container = template.New()
opts = []string{
"cap.drop=MKNOD",
}
)
// enabled all caps like in privileged mode
container.Capabilities = capabilities.GetAllCapabilities()
if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err)
}
if hasCapability("MKNOD", container.Capabilities) {
t.Fatal("container should not have MKNOD enabled")
}
}
func TestDropNamespace(t *testing.T) {
var (
container = template.New()
opts = []string{
"ns.drop=NEWNET",
}
)
if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err)
}
if container.Namespaces["NEWNET"] {
t.Fatal("container should not have NEWNET enabled")
}
}

View File

@ -9,7 +9,6 @@ import (
"path/filepath"
"github.com/docker/docker/daemon/execdriver"
"github.com/docker/docker/daemon/execdriver/native/configuration"
"github.com/docker/docker/daemon/execdriver/native/template"
"github.com/docker/libcontainer"
"github.com/docker/libcontainer/apparmor"
@ -69,10 +68,6 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Config, e
}
d.Unlock()
if err := configuration.ParseConfiguration(container, cmds, c.Config["native"]); err != nil {
return nil, err
}
return container, nil
}
@ -175,8 +170,8 @@ func (d *driver) setupMounts(container *libcontainer.Config, c *execdriver.Comma
}
func (d *driver) setupLabels(container *libcontainer.Config, c *execdriver.Command) error {
container.ProcessLabel = c.Config["process_label"][0]
container.MountConfig.MountLabel = c.Config["mount_label"][0]
container.ProcessLabel = c.ProcessLabel
container.MountConfig.MountLabel = c.MountLabel
return nil
}