Add unit test for lxc conf merge and native opts

Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
This commit is contained in:
Michael Crosby 2014-03-24 07:16:40 +00:00
parent 9a7be1b015
commit 10fdbc0467
8 changed files with 265 additions and 47 deletions

View File

@ -383,14 +383,8 @@ func populateCommand(c *Container) {
} }
} }
// merge in the lxc conf options into the generic config map // TODO: this can be removed after lxc-conf is fully deprecated
if lxcConf := c.hostConfig.LxcConf; lxcConf != nil { mergeLxcConfIntoOptions(c.hostConfig, driverConfig)
lxc := driverConfig["lxc"]
for _, pair := range lxcConf {
lxc = append(lxc, fmt.Sprintf("%s = %s", pair.Key, pair.Value))
}
driverConfig["lxc"] = lxc
}
resources := &execdriver.Resources{ resources := &execdriver.Resources{
Memory: c.Config.Memory, Memory: c.Config.Memory,

View File

@ -120,7 +120,7 @@ lxc.cgroup.cpu.shares = {{.Resources.CpuShares}}
{{if .Config.lxc}} {{if .Config.lxc}}
{{range $value := .Config.lxc}} {{range $value := .Config.lxc}}
{{$value}} lxc.{{$value}}
{{end}} {{end}}
{{end}} {{end}}
` `

View File

@ -31,20 +31,6 @@ 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
} }
// GetSupportedActions returns a list of all the avaliable actions supported by the driver
// TODO: this should return a description also
func GetSupportedActions() []string {
var (
i int
out = make([]string, len(actions))
)
for k := range actions {
out[i] = k
i++
}
return out
}
func cpusetCpus(container *libcontainer.Container, context interface{}, value string) error { func cpusetCpus(container *libcontainer.Container, 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")

View File

@ -0,0 +1,166 @@
package configuration
import (
"github.com/dotcloud/docker/runtime/execdriver/native/template"
"testing"
)
func TestSetReadonlyRootFs(t *testing.T) {
var (
container = template.New()
opts = []string{
"fs.readonly=true",
}
)
if container.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.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 !container1.CapabilitiesMask.Get("NET_ADMIN").Enabled {
t.Fatal("container one should have NET_ADMIN enabled")
}
if container2.CapabilitiesMask.Get("NET_ADMIN").Enabled {
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.Context["apparmor_profile"] != expected {
t.Fatalf("expected profile %s got %s", expected, container.Context["apparmor_profile"])
}
}
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 TestCgroupMemory(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 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 !container.CapabilitiesMask.Get("MKNOD").Enabled {
t.Fatal("container should have MKNOD enabled")
}
if !container.CapabilitiesMask.Get("SYS_ADMIN").Enabled {
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
for _, c := range container.CapabilitiesMask {
c.Enabled = true
}
if err := ParseConfiguration(container, nil, opts); err != nil {
t.Fatal(err)
}
if container.CapabilitiesMask.Get("MKNOD").Enabled {
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.Get("NEWNET").Enabled {
t.Fatal("container should not have NEWNET enabled")
}
}

View File

@ -5,30 +5,53 @@ import (
"github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/libcontainer"
"github.com/dotcloud/docker/runtime/execdriver" "github.com/dotcloud/docker/runtime/execdriver"
"github.com/dotcloud/docker/runtime/execdriver/native/configuration" "github.com/dotcloud/docker/runtime/execdriver/native/configuration"
"github.com/dotcloud/docker/runtime/execdriver/native/template"
"os" "os"
) )
// 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.Container, error) {
container := getDefaultTemplate() container := template.New()
container.Hostname = getEnv("HOSTNAME", c.Env) container.Hostname = getEnv("HOSTNAME", c.Env)
container.Tty = c.Tty container.Tty = c.Tty
container.User = c.User container.User = c.User
container.WorkingDir = c.WorkingDir container.WorkingDir = c.WorkingDir
container.Env = c.Env container.Env = c.Env
container.Cgroups.Name = c.ID
// check to see if we are running in ramdisk to disable pivot root
container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != ""
loopbackNetwork := libcontainer.Network{ if err := d.createNetwork(container, c); err != nil {
Mtu: c.Network.Mtu, return nil, err
Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0),
Gateway: "localhost",
Type: "loopback",
Context: libcontainer.Context{},
} }
if c.Privileged {
if err := d.setPrivileged(container); err != nil {
return nil, err
}
}
if err := d.setupCgroups(container, c); err != nil {
return nil, err
}
if err := d.setupMounts(container, c); err != nil {
return nil, err
}
if err := configuration.ParseConfiguration(container, d.activeContainers, c.Config["native"]); err != nil {
return nil, err
}
return container, nil
}
func (d *driver) createNetwork(container *libcontainer.Container, c *execdriver.Command) error {
container.Networks = []*libcontainer.Network{ container.Networks = []*libcontainer.Network{
&loopbackNetwork, {
Mtu: c.Network.Mtu,
Address: fmt.Sprintf("%s/%d", "127.0.0.1", 0),
Gateway: "localhost",
Type: "loopback",
Context: libcontainer.Context{},
},
} }
if c.Network.Interface != nil { if c.Network.Interface != nil {
@ -44,27 +67,30 @@ func (d *driver) createContainer(c *execdriver.Command) (*libcontainer.Container
} }
container.Networks = append(container.Networks, &vethNetwork) container.Networks = append(container.Networks, &vethNetwork)
} }
return nil
}
container.Cgroups.Name = c.ID func (d *driver) setPrivileged(container *libcontainer.Container) error {
if c.Privileged { for _, c := range container.CapabilitiesMask {
container.CapabilitiesMask = nil c.Enabled = true
container.Cgroups.DeviceAccess = true
container.Context["apparmor_profile"] = "unconfined"
} }
container.Cgroups.DeviceAccess = true
container.Context["apparmor_profile"] = "unconfined"
return nil
}
func (d *driver) setupCgroups(container *libcontainer.Container, 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
container.Cgroups.MemorySwap = c.Resources.MemorySwap container.Cgroups.MemorySwap = c.Resources.MemorySwap
} }
// check to see if we are running in ramdisk to disable pivot root return nil
container.NoPivotRoot = os.Getenv("DOCKER_RAMDISK") != "" }
func (d *driver) setupMounts(container *libcontainer.Container, c *execdriver.Command) error {
for _, m := range c.Mounts { for _, m := range c.Mounts {
container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private}) container.Mounts = append(container.Mounts, libcontainer.Mount{m.Source, m.Destination, m.Writable, m.Private})
} }
return nil
if err := configuration.ParseConfiguration(container, d.activeContainers, c.Config["native"]); err != nil {
return nil, err
}
return container, nil
} }

View File

@ -1,13 +1,12 @@
package native package template
import ( import (
"github.com/dotcloud/docker/pkg/cgroups" "github.com/dotcloud/docker/pkg/cgroups"
"github.com/dotcloud/docker/pkg/libcontainer" "github.com/dotcloud/docker/pkg/libcontainer"
) )
// getDefaultTemplate returns the docker default for // New returns the docker default configuration for libcontainer
// the libcontainer configuration file func New() *libcontainer.Container {
func getDefaultTemplate() *libcontainer.Container {
return &libcontainer.Container{ return &libcontainer.Container{
CapabilitiesMask: libcontainer.Capabilities{ CapabilitiesMask: libcontainer.Capabilities{
libcontainer.GetCapability("SETPCAP"), libcontainer.GetCapability("SETPCAP"),

View File

@ -1,9 +1,11 @@
package runtime package runtime
import ( import (
"fmt"
"github.com/dotcloud/docker/nat" "github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/pkg/namesgenerator" "github.com/dotcloud/docker/pkg/namesgenerator"
"github.com/dotcloud/docker/runconfig" "github.com/dotcloud/docker/runconfig"
"strings"
) )
func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostConfig) error { func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostConfig) error {
@ -30,6 +32,24 @@ func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostCon
return nil return nil
} }
func mergeLxcConfIntoOptions(hostConfig *runconfig.HostConfig, driverConfig map[string][]string) {
if hostConfig == nil {
return
}
// merge in the lxc conf options into the generic config map
if lxcConf := hostConfig.LxcConf; lxcConf != nil {
lxc := driverConfig["lxc"]
for _, pair := range lxcConf {
// because lxc conf gets the driver name lxc.XXXX we need to trim it off
// and let the lxc driver add it back later if needed
parts := strings.SplitN(pair.Key, ".", 2)
lxc = append(lxc, fmt.Sprintf("%s=%s", parts[1], pair.Value))
}
driverConfig["lxc"] = lxc
}
}
type checker struct { type checker struct {
runtime *Runtime runtime *Runtime
} }

27
runtime/utils_test.go Normal file
View File

@ -0,0 +1,27 @@
package runtime
import (
"github.com/dotcloud/docker/runconfig"
"testing"
)
func TestMergeLxcConfig(t *testing.T) {
var (
hostConfig = &runconfig.HostConfig{
LxcConf: []runconfig.KeyValuePair{
{Key: "lxc.cgroups.cpuset", Value: "1,2"},
},
}
driverConfig = make(map[string][]string)
)
mergeLxcConfIntoOptions(hostConfig, driverConfig)
if l := len(driverConfig["lxc"]); l > 1 {
t.Fatalf("expected lxc options len of 1 got %d", l)
}
cpuset := driverConfig["lxc"][0]
if expected := "cgroups.cpuset=1,2"; cpuset != expected {
t.Fatalf("expected %s got %s", expected, cpuset)
}
}