Move the canonical run configuration objects to a sub-package

* Config is now runconfig.Config
    * HostConfig is now runconfig.HostConfig
    * MergeConfig is now runconfig.Merge
    * CompareConfig is now runconfig.Compare
    * ParseRun is now runconfig.Parse
    * ContainerConfigFromJob is now runconfig.ContainerConfigFromJob
    * ContainerHostConfigFromJob is now runconfig.ContainerHostConfigFromJob

This facilitates refactoring commands.go and shrinks the core.

Docker-DCO-1.1-Signed-off-by: Solomon Hykes <solomon@docker.com> (github: shykes)
This commit is contained in:
Solomon Hykes 2014-02-11 20:04:39 -08:00
parent 9a9690360c
commit 6393c38339
23 changed files with 731 additions and 674 deletions

View File

@ -9,6 +9,7 @@ import (
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/auth"
"github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"io"
"io/ioutil"
@ -38,7 +39,7 @@ type buildFile struct {
image string
maintainer string
config *Config
config *runconfig.Config
contextPath string
context *utils.TarSum
@ -101,7 +102,7 @@ func (b *buildFile) CmdFrom(name string) error {
}
}
b.image = image.ID
b.config = &Config{}
b.config = &runconfig.Config{}
if image.Config != nil {
b.config = image.Config
}
@ -158,14 +159,14 @@ func (b *buildFile) CmdRun(args string) error {
if b.image == "" {
return fmt.Errorf("Please provide a source image with `from` prior to run")
}
config, _, _, err := ParseRun(append([]string{b.image}, b.buildCmdFromJson(args)...), nil)
config, _, _, err := runconfig.Parse(append([]string{b.image}, b.buildCmdFromJson(args)...), nil)
if err != nil {
return err
}
cmd := b.config.Cmd
b.config.Cmd = nil
MergeConfig(b.config, config)
runconfig.Merge(b.config, config)
defer func(cmd []string) { b.config.Cmd = cmd }(cmd)
@ -742,7 +743,7 @@ func NewBuildFile(srv *Server, outStream, errStream io.Writer, verbose, utilizeC
return &buildFile{
runtime: srv.runtime,
srv: srv,
config: &Config{},
config: &runconfig.Config{},
outStream: outStream,
errStream: errStream,
tmpContainers: make(map[string]struct{}),

View File

@ -15,10 +15,9 @@ import (
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/nat"
flag "github.com/dotcloud/docker/pkg/mflag"
"github.com/dotcloud/docker/pkg/opts"
"github.com/dotcloud/docker/pkg/sysinfo"
"github.com/dotcloud/docker/pkg/term"
"github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"io"
"io/ioutil"
@ -1449,11 +1448,11 @@ func (cli *DockerCli) CmdCommit(args ...string) error {
v.Set("comment", *flComment)
v.Set("author", *flAuthor)
var (
config *Config
config *runconfig.Config
env engine.Env
)
if *flConfig != "" {
config = &Config{}
config = &runconfig.Config{}
if err := json.Unmarshal([]byte(*flConfig), config); err != nil {
return err
}
@ -1743,210 +1742,9 @@ func (cli *DockerCli) CmdTag(args ...string) error {
return nil
}
//FIXME Only used in tests
func ParseRun(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
cmd := flag.NewFlagSet("run", flag.ContinueOnError)
cmd.SetOutput(ioutil.Discard)
cmd.Usage = nil
return parseRun(cmd, args, sysInfo)
}
func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
var (
// FIXME: use utils.ListOpts for attach and volumes?
flAttach = opts.NewListOpts(opts.ValidateAttach)
flVolumes = opts.NewListOpts(opts.ValidatePath)
flLinks = opts.NewListOpts(opts.ValidateLink)
flEnv = opts.NewListOpts(opts.ValidateEnv)
flPublish opts.ListOpts
flExpose opts.ListOpts
flDns opts.ListOpts
flVolumesFrom opts.ListOpts
flLxcOpts opts.ListOpts
flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
flNetwork = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container")
flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
flPublishAll = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-tty")
flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file")
flEntrypoint = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default entrypoint of the image")
flHostname = cmd.String([]string{"h", "-hostname"}, "", "Container host name")
flMemoryString = cmd.String([]string{"m", "-memory"}, "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID")
flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
flCpuShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
// For documentation purpose
_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
)
cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to stdin, stdout or stderr.")
cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat))
cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
if err := cmd.Parse(args); err != nil {
return nil, nil, cmd, err
}
// Check if the kernel supports memory limit cgroup.
if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit {
*flMemoryString = ""
}
// Validate input params
if *flDetach && flAttach.Len() > 0 {
return nil, nil, cmd, ErrConflictAttachDetach
}
if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
return nil, nil, cmd, ErrInvalidWorikingDirectory
}
if *flDetach && *flAutoRemove {
return nil, nil, cmd, ErrConflictDetachAutoRemove
}
// If neither -d or -a are set, attach to everything by default
if flAttach.Len() == 0 && !*flDetach {
if !*flDetach {
flAttach.Set("stdout")
flAttach.Set("stderr")
if *flStdin {
flAttach.Set("stdin")
}
}
}
var flMemory int64
if *flMemoryString != "" {
parsedMemory, err := utils.RAMInBytes(*flMemoryString)
if err != nil {
return nil, nil, cmd, err
}
flMemory = parsedMemory
}
var binds []string
// add any bind targets to the list of container volumes
for bind := range flVolumes.GetMap() {
if arr := strings.Split(bind, ":"); len(arr) > 1 {
if arr[0] == "/" {
return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
}
dstDir := arr[1]
flVolumes.Set(dstDir)
binds = append(binds, bind)
flVolumes.Delete(bind)
} else if bind == "/" {
return nil, nil, cmd, fmt.Errorf("Invalid volume: path can't be '/'")
}
}
var (
parsedArgs = cmd.Args()
runCmd []string
entrypoint []string
image string
)
if len(parsedArgs) >= 1 {
image = cmd.Arg(0)
}
if len(parsedArgs) > 1 {
runCmd = parsedArgs[1:]
}
if *flEntrypoint != "" {
entrypoint = []string{*flEntrypoint}
}
lxcConf, err := parseLxcConfOpts(flLxcOpts)
if err != nil {
return nil, nil, cmd, err
}
var (
domainname string
hostname = *flHostname
parts = strings.SplitN(hostname, ".", 2)
)
if len(parts) > 1 {
hostname = parts[0]
domainname = parts[1]
}
ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
if err != nil {
return nil, nil, cmd, err
}
// Merge in exposed ports to the map of published ports
for _, e := range flExpose.GetAll() {
if strings.Contains(e, ":") {
return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
}
p := nat.NewPort(nat.SplitProtoPort(e))
if _, exists := ports[p]; !exists {
ports[p] = struct{}{}
}
}
config := &Config{
Hostname: hostname,
Domainname: domainname,
PortSpecs: nil, // Deprecated
ExposedPorts: ports,
User: *flUser,
Tty: *flTty,
NetworkDisabled: !*flNetwork,
OpenStdin: *flStdin,
Memory: flMemory,
CpuShares: *flCpuShares,
AttachStdin: flAttach.Get("stdin"),
AttachStdout: flAttach.Get("stdout"),
AttachStderr: flAttach.Get("stderr"),
Env: flEnv.GetAll(),
Cmd: runCmd,
Dns: flDns.GetAll(),
Image: image,
Volumes: flVolumes.GetMap(),
VolumesFrom: strings.Join(flVolumesFrom.GetAll(), ","),
Entrypoint: entrypoint,
WorkingDir: *flWorkingDir,
}
hostConfig := &HostConfig{
Binds: binds,
ContainerIDFile: *flContainerIDFile,
LxcConf: lxcConf,
Privileged: *flPrivileged,
PortBindings: portBindings,
Links: flLinks.GetAll(),
PublishAllPorts: *flPublishAll,
}
if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
config.MemorySwap = -1
}
// When allocating stdin in attached mode, close stdin at client disconnect
if config.OpenStdin && config.AttachStdin {
config.StdinOnce = true
}
return config, hostConfig, cmd, nil
}
func (cli *DockerCli) CmdRun(args ...string) error {
config, hostConfig, cmd, err := parseRun(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
// FIXME: just use runconfig.Parse already
config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
if err != nil {
return err
}

View File

@ -1,16 +1,17 @@
package docker
import (
"github.com/dotcloud/docker/runconfig"
"strings"
"testing"
)
func parse(t *testing.T, args string) (*Config, *HostConfig, error) {
config, hostConfig, _, err := ParseRun(strings.Split(args+" ubuntu bash", " "), nil)
func parse(t *testing.T, args string) (*runconfig.Config, *runconfig.HostConfig, error) {
config, hostConfig, _, err := runconfig.Parse(strings.Split(args+" ubuntu bash", " "), nil)
return config, hostConfig, err
}
func mustParse(t *testing.T, args string) (*Config, *HostConfig) {
func mustParse(t *testing.T, args string) (*runconfig.Config, *runconfig.HostConfig) {
config, hostConfig, err := parse(t, args)
if err != nil {
t.Fatal(err)

View File

@ -11,6 +11,7 @@ import (
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/pkg/mount"
"github.com/dotcloud/docker/pkg/term"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"github.com/kr/pty"
"io"
@ -42,7 +43,7 @@ type Container struct {
Path string
Args []string
Config *Config
Config *runconfig.Config
State State
Image string
@ -68,109 +69,11 @@ type Container struct {
// Store rw/ro in a separate structure to preserve reverse-compatibility on-disk.
// Easier than migrating older container configs :)
VolumesRW map[string]bool
hostConfig *HostConfig
hostConfig *runconfig.HostConfig
activeLinks map[string]*Link
}
// Note: the Config structure should hold only portable information about the container.
// Here, "portable" means "independent from the host we are running on".
// Non-portable information *should* appear in HostConfig.
type Config struct {
Hostname string
Domainname string
User string
Memory int64 // Memory limit (in bytes)
MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap
CpuShares int64 // CPU shares (relative weight vs. other containers)
AttachStdin bool
AttachStdout bool
AttachStderr bool
PortSpecs []string // Deprecated - Can be in the format of 8080/tcp
ExposedPorts map[nat.Port]struct{}
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
OpenStdin bool // Open stdin
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
Env []string
Cmd []string
Dns []string
Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
Volumes map[string]struct{}
VolumesFrom string
WorkingDir string
Entrypoint []string
NetworkDisabled bool
OnBuild []string
}
func ContainerConfigFromJob(job *engine.Job) *Config {
config := &Config{
Hostname: job.Getenv("Hostname"),
Domainname: job.Getenv("Domainname"),
User: job.Getenv("User"),
Memory: job.GetenvInt64("Memory"),
MemorySwap: job.GetenvInt64("MemorySwap"),
CpuShares: job.GetenvInt64("CpuShares"),
AttachStdin: job.GetenvBool("AttachStdin"),
AttachStdout: job.GetenvBool("AttachStdout"),
AttachStderr: job.GetenvBool("AttachStderr"),
Tty: job.GetenvBool("Tty"),
OpenStdin: job.GetenvBool("OpenStdin"),
StdinOnce: job.GetenvBool("StdinOnce"),
Image: job.Getenv("Image"),
VolumesFrom: job.Getenv("VolumesFrom"),
WorkingDir: job.Getenv("WorkingDir"),
NetworkDisabled: job.GetenvBool("NetworkDisabled"),
}
job.GetenvJson("ExposedPorts", &config.ExposedPorts)
job.GetenvJson("Volumes", &config.Volumes)
if PortSpecs := job.GetenvList("PortSpecs"); PortSpecs != nil {
config.PortSpecs = PortSpecs
}
if Env := job.GetenvList("Env"); Env != nil {
config.Env = Env
}
if Cmd := job.GetenvList("Cmd"); Cmd != nil {
config.Cmd = Cmd
}
if Dns := job.GetenvList("Dns"); Dns != nil {
config.Dns = Dns
}
if Entrypoint := job.GetenvList("Entrypoint"); Entrypoint != nil {
config.Entrypoint = Entrypoint
}
return config
}
type HostConfig struct {
Binds []string
ContainerIDFile string
LxcConf []KeyValuePair
Privileged bool
PortBindings nat.PortMap
Links []string
PublishAllPorts bool
}
func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
hostConfig := &HostConfig{
ContainerIDFile: job.Getenv("ContainerIDFile"),
Privileged: job.GetenvBool("Privileged"),
PublishAllPorts: job.GetenvBool("PublishAllPorts"),
}
job.GetenvJson("LxcConf", &hostConfig.LxcConf)
job.GetenvJson("PortBindings", &hostConfig.PortBindings)
if Binds := job.GetenvList("Binds"); Binds != nil {
hostConfig.Binds = Binds
}
if Links := job.GetenvList("Links"); Links != nil {
hostConfig.Links = Links
}
return hostConfig
}
type BindMap struct {
SrcPath string
DstPath string
@ -178,18 +81,10 @@ type BindMap struct {
}
var (
ErrContainerStart = errors.New("The container failed to start. Unknown error")
ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.")
ErrInvalidWorikingDirectory = errors.New("The working directory is invalid. It needs to be an absolute path.")
ErrConflictAttachDetach = errors.New("Conflicting options: -a and -d")
ErrConflictDetachAutoRemove = errors.New("Conflicting options: -rm and -d")
ErrContainerStart = errors.New("The container failed to start. Unknown error")
ErrContainerStartTimeout = errors.New("The container failed to start due to timed out.")
)
type KeyValuePair struct {
Key string
Value string
}
// FIXME: move deprecated port stuff to nat to clean up the core.
type PortMapping map[string]string // Deprecated
@ -292,7 +187,7 @@ func (container *Container) ToDisk() (err error) {
}
func (container *Container) readHostConfig() error {
container.hostConfig = &HostConfig{}
container.hostConfig = &runconfig.HostConfig{}
// If the hostconfig file does not exist, do not read it.
// (We still have to initialize container.hostConfig,
// but that's OK, since we just did that above.)

View File

@ -5,23 +5,6 @@ import (
"testing"
)
func TestParseLxcConfOpt(t *testing.T) {
opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
for _, o := range opts {
k, v, err := parseLxcOpt(o)
if err != nil {
t.FailNow()
}
if k != "lxc.utsname" {
t.Fail()
}
if v != "docker" {
t.Fail()
}
}
}
func TestParseNetworkOptsPrivateOnly(t *testing.T) {
ports, bindings, err := nat.ParsePortSpecs([]string{"192.168.1.100::80"})
if err != nil {

View File

@ -5,6 +5,7 @@ import (
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/dockerversion"
"github.com/dotcloud/docker/graphdriver"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"io"
"io/ioutil"
@ -126,7 +127,7 @@ func (graph *Graph) Get(name string) (*Image, error) {
}
// Create creates a new image and registers it in the graph.
func (graph *Graph) Create(layerData archive.Archive, container *Container, comment, author string, config *Config) (*Image, error) {
func (graph *Graph) Create(layerData archive.Archive, container *Container, comment, author string, config *runconfig.Config) (*Image, error) {
img := &Image{
ID: GenerateID(),
Comment: comment,

View File

@ -7,6 +7,7 @@ import (
"fmt"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/graphdriver"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"io"
"io/ioutil"
@ -18,17 +19,17 @@ import (
)
type Image struct {
ID string `json:"id"`
Parent string `json:"parent,omitempty"`
Comment string `json:"comment,omitempty"`
Created time.Time `json:"created"`
Container string `json:"container,omitempty"`
ContainerConfig Config `json:"container_config,omitempty"`
DockerVersion string `json:"docker_version,omitempty"`
Author string `json:"author,omitempty"`
Config *Config `json:"config,omitempty"`
Architecture string `json:"architecture,omitempty"`
OS string `json:"os,omitempty"`
ID string `json:"id"`
Parent string `json:"parent,omitempty"`
Comment string `json:"comment,omitempty"`
Created time.Time `json:"created"`
Container string `json:"container,omitempty"`
ContainerConfig runconfig.Config `json:"container_config,omitempty"`
DockerVersion string `json:"docker_version,omitempty"`
Author string `json:"author,omitempty"`
Config *runconfig.Config `json:"config,omitempty"`
Architecture string `json:"architecture,omitempty"`
OS string `json:"os,omitempty"`
graph *Graph
Size int64
}

View File

@ -10,6 +10,7 @@ import (
"github.com/dotcloud/docker/api"
"github.com/dotcloud/docker/dockerversion"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"io"
"net"
@ -309,7 +310,7 @@ func TestGetContainersJSON(t *testing.T) {
}
beginLen := len(outs.Data)
containerID := createTestContainer(eng, &docker.Config{
containerID := createTestContainer(eng, &runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"echo", "test"},
}, t)
@ -346,7 +347,7 @@ func TestGetContainersExport(t *testing.T) {
// Create a container and remove a file
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"touch", "/test"},
},
@ -394,7 +395,7 @@ func TestGetContainersChanges(t *testing.T) {
// Create a container and remove a file
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"/bin/rm", "/etc/passwd"},
},
@ -433,7 +434,7 @@ func TestGetContainersTop(t *testing.T) {
defer mkRuntimeFromEngine(eng, t).Nuke()
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"/bin/sh", "-c", "cat"},
OpenStdin: true,
@ -510,7 +511,7 @@ func TestGetContainersByName(t *testing.T) {
// Create a container and remove a file
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"echo", "test"},
},
@ -542,7 +543,7 @@ func TestPostCommit(t *testing.T) {
// Create a container and remove a file
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"touch", "/test"},
},
@ -578,7 +579,7 @@ func TestPostContainersCreate(t *testing.T) {
eng := NewTestEngine(t)
defer mkRuntimeFromEngine(eng, t).Nuke()
configJSON, err := json.Marshal(&docker.Config{
configJSON, err := json.Marshal(&runconfig.Config{
Image: unitTestImageID,
Memory: 33554432,
Cmd: []string{"touch", "/test"},
@ -620,7 +621,7 @@ func TestPostContainersKill(t *testing.T) {
defer mkRuntimeFromEngine(eng, t).Nuke()
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
@ -659,7 +660,7 @@ func TestPostContainersRestart(t *testing.T) {
defer mkRuntimeFromEngine(eng, t).Nuke()
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"/bin/top"},
OpenStdin: true,
@ -705,7 +706,7 @@ func TestPostContainersStart(t *testing.T) {
containerID := createTestContainer(
eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
@ -713,7 +714,7 @@ func TestPostContainersStart(t *testing.T) {
t,
)
hostConfigJSON, err := json.Marshal(&docker.HostConfig{})
hostConfigJSON, err := json.Marshal(&runconfig.HostConfig{})
req, err := http.NewRequest("POST", "/containers/"+containerID+"/start", bytes.NewReader(hostConfigJSON))
if err != nil {
@ -758,7 +759,7 @@ func TestRunErrorBindMountRootSource(t *testing.T) {
containerID := createTestContainer(
eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
@ -766,7 +767,7 @@ func TestRunErrorBindMountRootSource(t *testing.T) {
t,
)
hostConfigJSON, err := json.Marshal(&docker.HostConfig{
hostConfigJSON, err := json.Marshal(&runconfig.HostConfig{
Binds: []string{"/:/tmp"},
})
@ -792,7 +793,7 @@ func TestPostContainersStop(t *testing.T) {
defer mkRuntimeFromEngine(eng, t).Nuke()
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"/bin/top"},
OpenStdin: true,
@ -832,7 +833,7 @@ func TestPostContainersWait(t *testing.T) {
defer mkRuntimeFromEngine(eng, t).Nuke()
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"/bin/sleep", "1"},
OpenStdin: true,
@ -870,7 +871,7 @@ func TestPostContainersAttach(t *testing.T) {
defer mkRuntimeFromEngine(eng, t).Nuke()
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"/bin/cat"},
OpenStdin: true,
@ -948,7 +949,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
defer mkRuntimeFromEngine(eng, t).Nuke()
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"/bin/sh", "-c", "/bin/cat >&2"},
OpenStdin: true,
@ -1029,7 +1030,7 @@ func TestDeleteContainers(t *testing.T) {
defer mkRuntimeFromEngine(eng, t).Nuke()
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"touch", "/test"},
},
@ -1164,7 +1165,7 @@ func TestPostContainersCopy(t *testing.T) {
// Create a container and remove a file
containerID := createTestContainer(eng,
&docker.Config{
&runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"touch", "/test.txt"},
},

View File

@ -3,7 +3,7 @@ package docker
import (
"bufio"
"fmt"
"github.com/dotcloud/docker"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"io"
"io/ioutil"
@ -20,7 +20,7 @@ func TestIDFormat(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container1, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
},
@ -234,7 +234,7 @@ func TestCommitAutoRun(t *testing.T) {
t.Errorf("Container shouldn't be running")
}
img, err := runtime.Commit(container1, "", "", "unit test commited image", "", &docker.Config{Cmd: []string{"cat", "/world"}})
img, err := runtime.Commit(container1, "", "", "unit test commited image", "", &runconfig.Config{Cmd: []string{"cat", "/world"}})
if err != nil {
t.Error(err)
}
@ -415,7 +415,7 @@ func TestOutput(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"},
},
@ -438,7 +438,7 @@ func TestContainerNetwork(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"ping", "-c", "1", "127.0.0.1"},
},
@ -460,7 +460,7 @@ func TestKillDifferentUser(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"},
OpenStdin: true,
@ -520,7 +520,7 @@ func TestCreateVolume(t *testing.T) {
runtime := mkRuntimeFromEngine(eng, t)
defer nuke(runtime)
config, hc, _, err := docker.ParseRun([]string{"-v", "/var/lib/data", unitTestImageID, "echo", "hello", "world"}, nil)
config, hc, _, err := runconfig.Parse([]string{"-v", "/var/lib/data", unitTestImageID, "echo", "hello", "world"}, nil)
if err != nil {
t.Fatal(err)
}
@ -552,7 +552,7 @@ func TestCreateVolume(t *testing.T) {
func TestKill(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"},
},
@ -596,7 +596,7 @@ func TestExitCode(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
trueContainer, _, err := runtime.Create(&docker.Config{
trueContainer, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true"},
}, "")
@ -611,7 +611,7 @@ func TestExitCode(t *testing.T) {
t.Fatalf("Unexpected exit code %d (expected 0)", code)
}
falseContainer, _, err := runtime.Create(&docker.Config{
falseContainer, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/false"},
}, "")
@ -630,7 +630,7 @@ func TestExitCode(t *testing.T) {
func TestRestart(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"},
},
@ -661,7 +661,7 @@ func TestRestart(t *testing.T) {
func TestRestartStdin(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"},
@ -739,7 +739,7 @@ func TestUser(t *testing.T) {
defer nuke(runtime)
// Default user must be root
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
},
@ -758,7 +758,7 @@ func TestUser(t *testing.T) {
}
// Set a username
container, _, err = runtime.Create(&docker.Config{
container, _, err = runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
@ -779,7 +779,7 @@ func TestUser(t *testing.T) {
}
// Set a UID
container, _, err = runtime.Create(&docker.Config{
container, _, err = runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
@ -800,7 +800,7 @@ func TestUser(t *testing.T) {
}
// Set a different user by uid
container, _, err = runtime.Create(&docker.Config{
container, _, err = runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
@ -823,7 +823,7 @@ func TestUser(t *testing.T) {
}
// Set a different user by username
container, _, err = runtime.Create(&docker.Config{
container, _, err = runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
@ -844,7 +844,7 @@ func TestUser(t *testing.T) {
}
// Test an wrong username
container, _, err = runtime.Create(&docker.Config{
container, _, err = runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
@ -866,7 +866,7 @@ func TestMultipleContainers(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container1, _, err := runtime.Create(&docker.Config{
container1, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"},
},
@ -877,7 +877,7 @@ func TestMultipleContainers(t *testing.T) {
}
defer runtime.Destroy(container1)
container2, _, err := runtime.Create(&docker.Config{
container2, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"},
},
@ -921,7 +921,7 @@ func TestMultipleContainers(t *testing.T) {
func TestStdin(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"},
@ -966,7 +966,7 @@ func TestStdin(t *testing.T) {
func TestTty(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat"},
@ -1013,7 +1013,7 @@ func TestEnv(t *testing.T) {
os.Setenv("TRICKY", "tri\ncky\n")
runtime := mkRuntime(t)
defer nuke(runtime)
config, _, _, err := docker.ParseRun([]string{"-e=FALSE=true", "-e=TRUE", "-e=TRICKY", GetTestImage(runtime).ID, "env"}, nil)
config, _, _, err := runconfig.Parse([]string{"-e=FALSE=true", "-e=TRUE", "-e=TRICKY", GetTestImage(runtime).ID, "env"}, nil)
if err != nil {
t.Fatal(err)
}
@ -1067,7 +1067,7 @@ func TestEntrypoint(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Entrypoint: []string{"/bin/echo"},
Cmd: []string{"-n", "foobar"},
@ -1091,7 +1091,7 @@ func TestEntrypointNoCmd(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Entrypoint: []string{"/bin/echo", "foobar"},
},
@ -1114,7 +1114,7 @@ func BenchmarkRunSequencial(b *testing.B) {
runtime := mkRuntime(b)
defer nuke(runtime)
for i := 0; i < b.N; i++ {
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"},
},
@ -1147,7 +1147,7 @@ func BenchmarkRunParallel(b *testing.B) {
complete := make(chan error)
tasks = append(tasks, complete)
go func(i int, complete chan error) {
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"},
},
@ -1301,7 +1301,7 @@ func TestFromVolumesInReadonlyMode(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/echo", "-n", "foobar"},
Volumes: map[string]struct{}{"/test": {}},
@ -1321,7 +1321,7 @@ func TestFromVolumesInReadonlyMode(t *testing.T) {
}
container2, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/echo", "-n", "foobar"},
VolumesFrom: container.ID + ":ro",
@ -1362,7 +1362,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/echo", "-n", "foobar"},
Volumes: map[string]struct{}{"/test": {}},
@ -1382,7 +1382,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
}
container2, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/echo", "-n", "foobar"},
VolumesFrom: container.ID,
@ -1418,7 +1418,7 @@ func TestRestartWithVolumes(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"},
Volumes: map[string]struct{}{"/test": {}},
@ -1462,7 +1462,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"},
Volumes: map[string]struct{}{"/test": {}},
@ -1491,7 +1491,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
}
container2, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"cat", "/test/foo"},
VolumesFrom: container.ID,
@ -1529,7 +1529,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
runtime := mkRuntimeFromEngine(eng, t)
defer nuke(runtime)
config, hc, _, err := docker.ParseRun([]string{"-n=false", GetTestImage(runtime).ID, "ip", "addr", "show"}, nil)
config, hc, _, err := runconfig.Parse([]string{"-n=false", GetTestImage(runtime).ID, "ip", "addr", "show"}, nil)
if err != nil {
t.Fatal(err)
}
@ -1617,7 +1617,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"},
Volumes: map[string]struct{}{"/test": {}},
@ -1646,7 +1646,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
}
container2, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sh", "-c", "echo -n bar > /other/foo"},
Volumes: map[string]struct{}{"/other": {}},
@ -1668,7 +1668,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
}
container3, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/echo", "-n", "foobar"},
VolumesFrom: strings.Join([]string{container.ID, container2.ID}, ","),
@ -1696,7 +1696,7 @@ func TestRestartGhost(t *testing.T) {
defer nuke(runtime)
container, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"},
Volumes: map[string]struct{}{"/test": {}},

View File

@ -6,6 +6,7 @@ import (
"github.com/dotcloud/docker"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/sysinit"
"github.com/dotcloud/docker/utils"
"io"
@ -200,7 +201,7 @@ func TestRuntimeCreate(t *testing.T) {
t.Errorf("Expected 0 containers, %v found", len(runtime.List()))
}
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
},
@ -243,23 +244,23 @@ func TestRuntimeCreate(t *testing.T) {
// Test that conflict error displays correct details
testContainer, _, _ := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
},
"conflictname",
)
if _, _, err := runtime.Create(&docker.Config{Image: GetTestImage(runtime).ID, Cmd: []string{"ls", "-al"}}, testContainer.Name); err == nil || !strings.Contains(err.Error(), utils.TruncateID(testContainer.ID)) {
if _, _, err := runtime.Create(&runconfig.Config{Image: GetTestImage(runtime).ID, Cmd: []string{"ls", "-al"}}, testContainer.Name); err == nil || !strings.Contains(err.Error(), utils.TruncateID(testContainer.ID)) {
t.Fatalf("Name conflict error doesn't include the correct short id. Message was: %s", err.Error())
}
// Make sure create with bad parameters returns an error
if _, _, err = runtime.Create(&docker.Config{Image: GetTestImage(runtime).ID}, ""); err == nil {
if _, _, err = runtime.Create(&runconfig.Config{Image: GetTestImage(runtime).ID}, ""); err == nil {
t.Fatal("Builder.Create should throw an error when Cmd is missing")
}
if _, _, err := runtime.Create(
&docker.Config{
&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{},
},
@ -268,7 +269,7 @@ func TestRuntimeCreate(t *testing.T) {
t.Fatal("Builder.Create should throw an error when Cmd is empty")
}
config := &docker.Config{
config := &runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/ls"},
PortSpecs: []string{"80"},
@ -281,7 +282,7 @@ func TestRuntimeCreate(t *testing.T) {
}
// test expose 80:8000
container, warnings, err := runtime.Create(&docker.Config{
container, warnings, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
PortSpecs: []string{"80:8000"},
@ -300,7 +301,7 @@ func TestDestroy(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
}, "")
@ -712,7 +713,7 @@ func TestDefaultContainerName(t *testing.T) {
runtime := mkRuntimeFromEngine(eng, t)
defer nuke(runtime)
config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
if err != nil {
t.Fatal(err)
}
@ -736,7 +737,7 @@ func TestRandomContainerName(t *testing.T) {
runtime := mkRuntimeFromEngine(eng, t)
defer nuke(runtime)
config, _, _, err := docker.ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
config, _, _, err := runconfig.Parse([]string{GetTestImage(runtime).ID, "echo test"}, nil)
if err != nil {
t.Fatal(err)
}
@ -767,7 +768,7 @@ func TestContainerNameValidation(t *testing.T) {
{"abc-123_AAA.1", true},
{"\000asdf", false},
} {
config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
if err != nil {
if !test.Valid {
continue
@ -808,7 +809,7 @@ func TestLinkChildContainer(t *testing.T) {
runtime := mkRuntimeFromEngine(eng, t)
defer nuke(runtime)
config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
if err != nil {
t.Fatal(err)
}
@ -824,7 +825,7 @@ func TestLinkChildContainer(t *testing.T) {
t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
}
config, _, _, err = docker.ParseRun([]string{GetTestImage(runtime).ID, "echo test"}, nil)
config, _, _, err = runconfig.Parse([]string{GetTestImage(runtime).ID, "echo test"}, nil)
if err != nil {
t.Fatal(err)
}
@ -850,7 +851,7 @@ func TestGetAllChildren(t *testing.T) {
runtime := mkRuntimeFromEngine(eng, t)
defer nuke(runtime)
config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
if err != nil {
t.Fatal(err)
}
@ -866,7 +867,7 @@ func TestGetAllChildren(t *testing.T) {
t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
}
config, _, _, err = docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
config, _, _, err = runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
if err != nil {
t.Fatal(err)
}
@ -903,7 +904,7 @@ func TestDestroyWithInitLayer(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
container, _, err := runtime.Create(&docker.Config{
container, _, err := runtime.Create(&runconfig.Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
}, "")

View File

@ -3,6 +3,7 @@ package docker
import (
"github.com/dotcloud/docker"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/runconfig"
"strings"
"testing"
"time"
@ -71,7 +72,7 @@ func TestCreateRm(t *testing.T) {
eng := NewTestEngine(t)
defer mkRuntimeFromEngine(eng, t).Nuke()
config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
if err != nil {
t.Fatal(err)
}
@ -118,7 +119,7 @@ func TestCreateNumberHostname(t *testing.T) {
eng := NewTestEngine(t)
defer mkRuntimeFromEngine(eng, t).Nuke()
config, _, _, err := docker.ParseRun([]string{"-h", "web.0", unitTestImageID, "echo test"}, nil)
config, _, _, err := runconfig.Parse([]string{"-h", "web.0", unitTestImageID, "echo test"}, nil)
if err != nil {
t.Fatal(err)
}
@ -130,7 +131,7 @@ func TestCreateNumberUsername(t *testing.T) {
eng := NewTestEngine(t)
defer mkRuntimeFromEngine(eng, t).Nuke()
config, _, _, err := docker.ParseRun([]string{"-u", "1002", unitTestImageID, "echo test"}, nil)
config, _, _, err := runconfig.Parse([]string{"-u", "1002", unitTestImageID, "echo test"}, nil)
if err != nil {
t.Fatal(err)
}
@ -142,7 +143,7 @@ func TestCreateRmVolumes(t *testing.T) {
eng := NewTestEngine(t)
defer mkRuntimeFromEngine(eng, t).Nuke()
config, hostConfig, _, err := docker.ParseRun([]string{"-v", "/srv", unitTestImageID, "echo", "test"}, nil)
config, hostConfig, _, err := runconfig.Parse([]string{"-v", "/srv", unitTestImageID, "echo", "test"}, nil)
if err != nil {
t.Fatal(err)
}
@ -202,7 +203,7 @@ func TestCommit(t *testing.T) {
eng := NewTestEngine(t)
defer mkRuntimeFromEngine(eng, t).Nuke()
config, _, _, err := docker.ParseRun([]string{unitTestImageID, "/bin/cat"}, nil)
config, _, _, err := runconfig.Parse([]string{unitTestImageID, "/bin/cat"}, nil)
if err != nil {
t.Fatal(err)
}
@ -224,7 +225,7 @@ func TestRestartKillWait(t *testing.T) {
runtime := mkRuntimeFromEngine(eng, t)
defer runtime.Nuke()
config, hostConfig, _, err := docker.ParseRun([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
config, hostConfig, _, err := runconfig.Parse([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
if err != nil {
t.Fatal(err)
}
@ -302,7 +303,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
srv := mkServerFromEngine(eng, t)
defer mkRuntimeFromEngine(eng, t).Nuke()
config, hostConfig, _, err := docker.ParseRun([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
config, hostConfig, _, err := runconfig.Parse([]string{"-i", unitTestImageID, "/bin/cat"}, nil)
if err != nil {
t.Fatal(err)
}
@ -401,7 +402,7 @@ func TestRmi(t *testing.T) {
initialImages := getAllImages(eng, t)
config, hostConfig, _, err := docker.ParseRun([]string{unitTestImageID, "echo", "test"}, nil)
config, hostConfig, _, err := runconfig.Parse([]string{unitTestImageID, "echo", "test"}, nil)
if err != nil {
t.Fatal(err)
}
@ -548,7 +549,7 @@ func TestListContainers(t *testing.T) {
srv := mkServerFromEngine(eng, t)
defer mkRuntimeFromEngine(eng, t).Nuke()
config := docker.Config{
config := runconfig.Config{
Image: unitTestImageID,
Cmd: []string{"/bin/sh", "-c", "cat"},
OpenStdin: true,
@ -671,7 +672,7 @@ func TestDeleteTagWithExistingContainers(t *testing.T) {
}
// Create a container from the image
config, _, _, err := docker.ParseRun([]string{unitTestImageID, "echo test"}, nil)
config, _, _, err := runconfig.Parse([]string{unitTestImageID, "echo test"}, nil)
if err != nil {
t.Fatal(err)
}

View File

@ -16,6 +16,7 @@ import (
"github.com/dotcloud/docker"
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
)
@ -48,7 +49,7 @@ func mkRuntime(f utils.Fataler) *docker.Runtime {
return r
}
func createNamedTestContainer(eng *engine.Engine, config *docker.Config, f utils.Fataler, name string) (shortId string) {
func createNamedTestContainer(eng *engine.Engine, config *runconfig.Config, f utils.Fataler, name string) (shortId string) {
job := eng.Job("create", name)
if err := job.ImportEnv(config); err != nil {
f.Fatal(err)
@ -60,7 +61,7 @@ func createNamedTestContainer(eng *engine.Engine, config *docker.Config, f utils
return
}
func createTestContainer(eng *engine.Engine, config *docker.Config, f utils.Fataler) (shortId string) {
func createTestContainer(eng *engine.Engine, config *runconfig.Config, f utils.Fataler) (shortId string) {
return createNamedTestContainer(eng, config, f, "")
}
@ -252,8 +253,8 @@ func readFile(src string, t *testing.T) (content string) {
// dynamically replaced by the current test image.
// The caller is responsible for destroying the container.
// Call t.Fatal() at the first error.
func mkContainer(r *docker.Runtime, args []string, t *testing.T) (*docker.Container, *docker.HostConfig, error) {
config, hc, _, err := docker.ParseRun(args, nil)
func mkContainer(r *docker.Runtime, args []string, t *testing.T) (*docker.Container, *runconfig.HostConfig, error) {
config, hc, _, err := runconfig.Parse(args, nil)
defer func() {
if err != nil && t != nil {
t.Fatal(err)

View File

@ -2,13 +2,14 @@ package docker
import (
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/runconfig"
"strings"
"testing"
)
func newMockLinkContainer(id string, ip string) *Container {
return &Container{
Config: &Config{},
Config: &runconfig.Config{},
ID: id,
NetworkSettings: &NetworkSettings{
IPAddress: ip,

67
runconfig/compare.go Normal file
View File

@ -0,0 +1,67 @@
package runconfig
// Compare two Config struct. Do not compare the "Image" nor "Hostname" fields
// If OpenStdin is set, then it differs
func Compare(a, b *Config) bool {
if a == nil || b == nil ||
a.OpenStdin || b.OpenStdin {
return false
}
if a.AttachStdout != b.AttachStdout ||
a.AttachStderr != b.AttachStderr ||
a.User != b.User ||
a.Memory != b.Memory ||
a.MemorySwap != b.MemorySwap ||
a.CpuShares != b.CpuShares ||
a.OpenStdin != b.OpenStdin ||
a.Tty != b.Tty ||
a.VolumesFrom != b.VolumesFrom {
return false
}
if len(a.Cmd) != len(b.Cmd) ||
len(a.Dns) != len(b.Dns) ||
len(a.Env) != len(b.Env) ||
len(a.PortSpecs) != len(b.PortSpecs) ||
len(a.ExposedPorts) != len(b.ExposedPorts) ||
len(a.Entrypoint) != len(b.Entrypoint) ||
len(a.Volumes) != len(b.Volumes) {
return false
}
for i := 0; i < len(a.Cmd); i++ {
if a.Cmd[i] != b.Cmd[i] {
return false
}
}
for i := 0; i < len(a.Dns); i++ {
if a.Dns[i] != b.Dns[i] {
return false
}
}
for i := 0; i < len(a.Env); i++ {
if a.Env[i] != b.Env[i] {
return false
}
}
for i := 0; i < len(a.PortSpecs); i++ {
if a.PortSpecs[i] != b.PortSpecs[i] {
return false
}
}
for k := range a.ExposedPorts {
if _, exists := b.ExposedPorts[k]; !exists {
return false
}
}
for i := 0; i < len(a.Entrypoint); i++ {
if a.Entrypoint[i] != b.Entrypoint[i] {
return false
}
}
for key := range a.Volumes {
if _, exists := b.Volumes[key]; !exists {
return false
}
}
return true
}

76
runconfig/config.go Normal file
View File

@ -0,0 +1,76 @@
package runconfig
import (
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/nat"
)
// Note: the Config structure should hold only portable information about the container.
// Here, "portable" means "independent from the host we are running on".
// Non-portable information *should* appear in HostConfig.
type Config struct {
Hostname string
Domainname string
User string
Memory int64 // Memory limit (in bytes)
MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap
CpuShares int64 // CPU shares (relative weight vs. other containers)
AttachStdin bool
AttachStdout bool
AttachStderr bool
PortSpecs []string // Deprecated - Can be in the format of 8080/tcp
ExposedPorts map[nat.Port]struct{}
Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
OpenStdin bool // Open stdin
StdinOnce bool // If true, close stdin after the 1 attached client disconnects.
Env []string
Cmd []string
Dns []string
Image string // Name of the image as it was passed by the operator (eg. could be symbolic)
Volumes map[string]struct{}
VolumesFrom string
WorkingDir string
Entrypoint []string
NetworkDisabled bool
OnBuild []string
}
func ContainerConfigFromJob(job *engine.Job) *Config {
config := &Config{
Hostname: job.Getenv("Hostname"),
Domainname: job.Getenv("Domainname"),
User: job.Getenv("User"),
Memory: job.GetenvInt64("Memory"),
MemorySwap: job.GetenvInt64("MemorySwap"),
CpuShares: job.GetenvInt64("CpuShares"),
AttachStdin: job.GetenvBool("AttachStdin"),
AttachStdout: job.GetenvBool("AttachStdout"),
AttachStderr: job.GetenvBool("AttachStderr"),
Tty: job.GetenvBool("Tty"),
OpenStdin: job.GetenvBool("OpenStdin"),
StdinOnce: job.GetenvBool("StdinOnce"),
Image: job.Getenv("Image"),
VolumesFrom: job.Getenv("VolumesFrom"),
WorkingDir: job.Getenv("WorkingDir"),
NetworkDisabled: job.GetenvBool("NetworkDisabled"),
}
job.GetenvJson("ExposedPorts", &config.ExposedPorts)
job.GetenvJson("Volumes", &config.Volumes)
if PortSpecs := job.GetenvList("PortSpecs"); PortSpecs != nil {
config.PortSpecs = PortSpecs
}
if Env := job.GetenvList("Env"); Env != nil {
config.Env = Env
}
if Cmd := job.GetenvList("Cmd"); Cmd != nil {
config.Cmd = Cmd
}
if Dns := job.GetenvList("Dns"); Dns != nil {
config.Dns = Dns
}
if Entrypoint := job.GetenvList("Entrypoint"); Entrypoint != nil {
config.Entrypoint = Entrypoint
}
return config
}

View File

@ -1,11 +1,11 @@
package docker
package runconfig
import (
"github.com/dotcloud/docker/nat"
"testing"
)
func TestCompareConfig(t *testing.T) {
func TestCompare(t *testing.T) {
volumes1 := make(map[string]struct{})
volumes1["/test1"] = struct{}{}
config1 := Config{
@ -45,24 +45,24 @@ func TestCompareConfig(t *testing.T) {
VolumesFrom: "11111111",
Volumes: volumes2,
}
if CompareConfig(&config1, &config2) {
t.Fatalf("CompareConfig should return false, Dns are different")
if Compare(&config1, &config2) {
t.Fatalf("Compare should return false, Dns are different")
}
if CompareConfig(&config1, &config3) {
t.Fatalf("CompareConfig should return false, PortSpecs are different")
if Compare(&config1, &config3) {
t.Fatalf("Compare should return false, PortSpecs are different")
}
if CompareConfig(&config1, &config4) {
t.Fatalf("CompareConfig should return false, VolumesFrom are different")
if Compare(&config1, &config4) {
t.Fatalf("Compare should return false, VolumesFrom are different")
}
if CompareConfig(&config1, &config5) {
t.Fatalf("CompareConfig should return false, Volumes are different")
if Compare(&config1, &config5) {
t.Fatalf("Compare should return false, Volumes are different")
}
if !CompareConfig(&config1, &config1) {
t.Fatalf("CompareConfig should return true")
if !Compare(&config1, &config1) {
t.Fatalf("Compare should return true")
}
}
func TestMergeConfig(t *testing.T) {
func TestMerge(t *testing.T) {
volumesImage := make(map[string]struct{})
volumesImage["/test1"] = struct{}{}
volumesImage["/test2"] = struct{}{}
@ -83,7 +83,7 @@ func TestMergeConfig(t *testing.T) {
Volumes: volumesUser,
}
if err := MergeConfig(configUser, configImage); err != nil {
if err := Merge(configUser, configImage); err != nil {
t.Error(err)
}
@ -134,7 +134,7 @@ func TestMergeConfig(t *testing.T) {
ExposedPorts: ports,
}
if err := MergeConfig(configUser, configImage2); err != nil {
if err := Merge(configUser, configImage2); err != nil {
t.Error(err)
}

39
runconfig/hostconfig.go Normal file
View File

@ -0,0 +1,39 @@
package runconfig
import (
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/nat"
)
type HostConfig struct {
Binds []string
ContainerIDFile string
LxcConf []KeyValuePair
Privileged bool
PortBindings nat.PortMap
Links []string
PublishAllPorts bool
}
type KeyValuePair struct {
Key string
Value string
}
func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
hostConfig := &HostConfig{
ContainerIDFile: job.Getenv("ContainerIDFile"),
Privileged: job.GetenvBool("Privileged"),
PublishAllPorts: job.GetenvBool("PublishAllPorts"),
}
job.GetenvJson("LxcConf", &hostConfig.LxcConf)
job.GetenvJson("PortBindings", &hostConfig.PortBindings)
if Binds := job.GetenvList("Binds"); Binds != nil {
hostConfig.Binds = Binds
}
if Links := job.GetenvList("Links"); Links != nil {
hostConfig.Links = Links
}
return hostConfig
}

119
runconfig/merge.go Normal file
View File

@ -0,0 +1,119 @@
package runconfig
import (
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/utils"
"strings"
)
func Merge(userConf, imageConf *Config) error {
if userConf.User == "" {
userConf.User = imageConf.User
}
if userConf.Memory == 0 {
userConf.Memory = imageConf.Memory
}
if userConf.MemorySwap == 0 {
userConf.MemorySwap = imageConf.MemorySwap
}
if userConf.CpuShares == 0 {
userConf.CpuShares = imageConf.CpuShares
}
if userConf.ExposedPorts == nil || len(userConf.ExposedPorts) == 0 {
userConf.ExposedPorts = imageConf.ExposedPorts
} else if imageConf.ExposedPorts != nil {
if userConf.ExposedPorts == nil {
userConf.ExposedPorts = make(nat.PortSet)
}
for port := range imageConf.ExposedPorts {
if _, exists := userConf.ExposedPorts[port]; !exists {
userConf.ExposedPorts[port] = struct{}{}
}
}
}
if userConf.PortSpecs != nil && len(userConf.PortSpecs) > 0 {
if userConf.ExposedPorts == nil {
userConf.ExposedPorts = make(nat.PortSet)
}
ports, _, err := nat.ParsePortSpecs(userConf.PortSpecs)
if err != nil {
return err
}
for port := range ports {
if _, exists := userConf.ExposedPorts[port]; !exists {
userConf.ExposedPorts[port] = struct{}{}
}
}
userConf.PortSpecs = nil
}
if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
// FIXME: I think we can safely remove this. Leaving it for now for the sake of reverse-compat paranoia.
utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
if userConf.ExposedPorts == nil {
userConf.ExposedPorts = make(nat.PortSet)
}
ports, _, err := nat.ParsePortSpecs(imageConf.PortSpecs)
if err != nil {
return err
}
for port := range ports {
if _, exists := userConf.ExposedPorts[port]; !exists {
userConf.ExposedPorts[port] = struct{}{}
}
}
}
if !userConf.Tty {
userConf.Tty = imageConf.Tty
}
if !userConf.OpenStdin {
userConf.OpenStdin = imageConf.OpenStdin
}
if !userConf.StdinOnce {
userConf.StdinOnce = imageConf.StdinOnce
}
if userConf.Env == nil || len(userConf.Env) == 0 {
userConf.Env = imageConf.Env
} else {
for _, imageEnv := range imageConf.Env {
found := false
imageEnvKey := strings.Split(imageEnv, "=")[0]
for _, userEnv := range userConf.Env {
userEnvKey := strings.Split(userEnv, "=")[0]
if imageEnvKey == userEnvKey {
found = true
}
}
if !found {
userConf.Env = append(userConf.Env, imageEnv)
}
}
}
if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
userConf.Cmd = imageConf.Cmd
}
if userConf.Dns == nil || len(userConf.Dns) == 0 {
userConf.Dns = imageConf.Dns
} else {
//duplicates aren't an issue here
userConf.Dns = append(userConf.Dns, imageConf.Dns...)
}
if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 {
userConf.Entrypoint = imageConf.Entrypoint
}
if userConf.WorkingDir == "" {
userConf.WorkingDir = imageConf.WorkingDir
}
if userConf.VolumesFrom == "" {
userConf.VolumesFrom = imageConf.VolumesFrom
}
if userConf.Volumes == nil || len(userConf.Volumes) == 0 {
userConf.Volumes = imageConf.Volumes
} else {
for k, v := range imageConf.Volumes {
userConf.Volumes[k] = v
}
}
return nil
}

246
runconfig/parse.go Normal file
View File

@ -0,0 +1,246 @@
package runconfig
import (
"fmt"
"github.com/dotcloud/docker/nat"
flag "github.com/dotcloud/docker/pkg/mflag"
"github.com/dotcloud/docker/pkg/opts"
"github.com/dotcloud/docker/pkg/sysinfo"
"github.com/dotcloud/docker/utils"
"io/ioutil"
"path"
"strings"
)
var (
ErrInvalidWorikingDirectory = fmt.Errorf("The working directory is invalid. It needs to be an absolute path.")
ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d")
ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: -rm and -d")
)
//FIXME Only used in tests
func Parse(args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
cmd := flag.NewFlagSet("run", flag.ContinueOnError)
cmd.SetOutput(ioutil.Discard)
cmd.Usage = nil
return parseRun(cmd, args, sysInfo)
}
// FIXME: this maps the legacy commands.go code. It should be merged with Parse to only expose a single parse function.
func ParseSubcommand(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
return parseRun(cmd, args, sysInfo)
}
func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, *HostConfig, *flag.FlagSet, error) {
var (
// FIXME: use utils.ListOpts for attach and volumes?
flAttach = opts.NewListOpts(opts.ValidateAttach)
flVolumes = opts.NewListOpts(opts.ValidatePath)
flLinks = opts.NewListOpts(opts.ValidateLink)
flEnv = opts.NewListOpts(opts.ValidateEnv)
flPublish opts.ListOpts
flExpose opts.ListOpts
flDns opts.ListOpts
flVolumesFrom opts.ListOpts
flLxcOpts opts.ListOpts
flAutoRemove = cmd.Bool([]string{"#rm", "-rm"}, false, "Automatically remove the container when it exits (incompatible with -d)")
flDetach = cmd.Bool([]string{"d", "-detach"}, false, "Detached mode: Run container in the background, print new container id")
flNetwork = cmd.Bool([]string{"n", "-networking"}, true, "Enable networking for this container")
flPrivileged = cmd.Bool([]string{"#privileged", "-privileged"}, false, "Give extended privileges to this container")
flPublishAll = cmd.Bool([]string{"P", "-publish-all"}, false, "Publish all exposed ports to the host interfaces")
flStdin = cmd.Bool([]string{"i", "-interactive"}, false, "Keep stdin open even if not attached")
flTty = cmd.Bool([]string{"t", "-tty"}, false, "Allocate a pseudo-tty")
flContainerIDFile = cmd.String([]string{"#cidfile", "-cidfile"}, "", "Write the container ID to the file")
flEntrypoint = cmd.String([]string{"#entrypoint", "-entrypoint"}, "", "Overwrite the default entrypoint of the image")
flHostname = cmd.String([]string{"h", "-hostname"}, "", "Container host name")
flMemoryString = cmd.String([]string{"m", "-memory"}, "", "Memory limit (format: <number><optional unit>, where unit = b, k, m or g)")
flUser = cmd.String([]string{"u", "-user"}, "", "Username or UID")
flWorkingDir = cmd.String([]string{"w", "-workdir"}, "", "Working directory inside the container")
flCpuShares = cmd.Int64([]string{"c", "-cpu-shares"}, 0, "CPU shares (relative weight)")
// For documentation purpose
_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxify all received signal to the process (even in non-tty mode)")
_ = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
)
cmd.Var(&flAttach, []string{"a", "-attach"}, "Attach to stdin, stdout or stderr.")
cmd.Var(&flVolumes, []string{"v", "-volume"}, "Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)")
cmd.Var(&flLinks, []string{"#link", "-link"}, "Add link to another container (name:alias)")
cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host (format: %s) (use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat))
cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom dns servers")
cmd.Var(&flVolumesFrom, []string{"#volumes-from", "-volumes-from"}, "Mount volumes from the specified container(s)")
cmd.Var(&flLxcOpts, []string{"#lxc-conf", "-lxc-conf"}, "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
if err := cmd.Parse(args); err != nil {
return nil, nil, cmd, err
}
// Check if the kernel supports memory limit cgroup.
if sysInfo != nil && *flMemoryString != "" && !sysInfo.MemoryLimit {
*flMemoryString = ""
}
// Validate input params
if *flDetach && flAttach.Len() > 0 {
return nil, nil, cmd, ErrConflictAttachDetach
}
if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
return nil, nil, cmd, ErrInvalidWorikingDirectory
}
if *flDetach && *flAutoRemove {
return nil, nil, cmd, ErrConflictDetachAutoRemove
}
// If neither -d or -a are set, attach to everything by default
if flAttach.Len() == 0 && !*flDetach {
if !*flDetach {
flAttach.Set("stdout")
flAttach.Set("stderr")
if *flStdin {
flAttach.Set("stdin")
}
}
}
var flMemory int64
if *flMemoryString != "" {
parsedMemory, err := utils.RAMInBytes(*flMemoryString)
if err != nil {
return nil, nil, cmd, err
}
flMemory = parsedMemory
}
var binds []string
// add any bind targets to the list of container volumes
for bind := range flVolumes.GetMap() {
if arr := strings.Split(bind, ":"); len(arr) > 1 {
if arr[0] == "/" {
return nil, nil, cmd, fmt.Errorf("Invalid bind mount: source can't be '/'")
}
dstDir := arr[1]
flVolumes.Set(dstDir)
binds = append(binds, bind)
flVolumes.Delete(bind)
} else if bind == "/" {
return nil, nil, cmd, fmt.Errorf("Invalid volume: path can't be '/'")
}
}
var (
parsedArgs = cmd.Args()
runCmd []string
entrypoint []string
image string
)
if len(parsedArgs) >= 1 {
image = cmd.Arg(0)
}
if len(parsedArgs) > 1 {
runCmd = parsedArgs[1:]
}
if *flEntrypoint != "" {
entrypoint = []string{*flEntrypoint}
}
lxcConf, err := parseLxcConfOpts(flLxcOpts)
if err != nil {
return nil, nil, cmd, err
}
var (
domainname string
hostname = *flHostname
parts = strings.SplitN(hostname, ".", 2)
)
if len(parts) > 1 {
hostname = parts[0]
domainname = parts[1]
}
ports, portBindings, err := nat.ParsePortSpecs(flPublish.GetAll())
if err != nil {
return nil, nil, cmd, err
}
// Merge in exposed ports to the map of published ports
for _, e := range flExpose.GetAll() {
if strings.Contains(e, ":") {
return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
}
p := nat.NewPort(nat.SplitProtoPort(e))
if _, exists := ports[p]; !exists {
ports[p] = struct{}{}
}
}
config := &Config{
Hostname: hostname,
Domainname: domainname,
PortSpecs: nil, // Deprecated
ExposedPorts: ports,
User: *flUser,
Tty: *flTty,
NetworkDisabled: !*flNetwork,
OpenStdin: *flStdin,
Memory: flMemory,
CpuShares: *flCpuShares,
AttachStdin: flAttach.Get("stdin"),
AttachStdout: flAttach.Get("stdout"),
AttachStderr: flAttach.Get("stderr"),
Env: flEnv.GetAll(),
Cmd: runCmd,
Dns: flDns.GetAll(),
Image: image,
Volumes: flVolumes.GetMap(),
VolumesFrom: strings.Join(flVolumesFrom.GetAll(), ","),
Entrypoint: entrypoint,
WorkingDir: *flWorkingDir,
}
hostConfig := &HostConfig{
Binds: binds,
ContainerIDFile: *flContainerIDFile,
LxcConf: lxcConf,
Privileged: *flPrivileged,
PortBindings: portBindings,
Links: flLinks.GetAll(),
PublishAllPorts: *flPublishAll,
}
if sysInfo != nil && flMemory > 0 && !sysInfo.SwapLimit {
//fmt.Fprintf(stdout, "WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
config.MemorySwap = -1
}
// When allocating stdin in attached mode, close stdin at client disconnect
if config.OpenStdin && config.AttachStdin {
config.StdinOnce = true
}
return config, hostConfig, cmd, nil
}
func parseLxcConfOpts(opts opts.ListOpts) ([]KeyValuePair, error) {
out := make([]KeyValuePair, opts.Len())
for i, o := range opts.GetAll() {
k, v, err := parseLxcOpt(o)
if err != nil {
return nil, err
}
out[i] = KeyValuePair{Key: k, Value: v}
}
return out, nil
}
func parseLxcOpt(opt string) (string, string, error) {
parts := strings.SplitN(opt, "=", 2)
if len(parts) != 2 {
return "", "", fmt.Errorf("Unable to parse lxc conf option: %s", opt)
}
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
}

22
runconfig/parse_test.go Normal file
View File

@ -0,0 +1,22 @@
package runconfig
import (
"testing"
)
func TestParseLxcConfOpt(t *testing.T) {
opts := []string{"lxc.utsname=docker", "lxc.utsname = docker "}
for _, o := range opts {
k, v, err := parseLxcOpt(o)
if err != nil {
t.FailNow()
}
if k != "lxc.utsname" {
t.Fail()
}
if v != "docker" {
t.Fail()
}
}
}

View File

@ -18,6 +18,7 @@ import (
"github.com/dotcloud/docker/networkdriver/portallocator"
"github.com/dotcloud/docker/pkg/graphdb"
"github.com/dotcloud/docker/pkg/sysinfo"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"io"
"io/ioutil"
@ -329,7 +330,7 @@ func (runtime *Runtime) restore() error {
}
// Create creates a new container from the given configuration with a given name.
func (runtime *Runtime) Create(config *Config, name string) (*Container, []string, error) {
func (runtime *Runtime) Create(config *runconfig.Config, name string) (*Container, []string, error) {
// Lookup image
img, err := runtime.repositories.LookupImage(config.Image)
if err != nil {
@ -347,7 +348,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
return nil, nil, fmt.Errorf("Cannot create container with more than %d parents", MaxImageDepth)
}
checkDeprecatedExpose := func(config *Config) bool {
checkDeprecatedExpose := func(config *runconfig.Config) bool {
if config != nil {
if config.PortSpecs != nil {
for _, p := range config.PortSpecs {
@ -366,7 +367,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
}
if img.Config != nil {
if err := MergeConfig(config, img.Config); err != nil {
if err := runconfig.Merge(config, img.Config); err != nil {
return nil, nil, err
}
}
@ -441,7 +442,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
Path: entrypoint,
Args: args, //FIXME: de-duplicate from config
Config: config,
hostConfig: &HostConfig{},
hostConfig: &runconfig.HostConfig{},
Image: img.ID, // Always use the resolved image id
NetworkSettings: &NetworkSettings{},
Name: name,
@ -518,7 +519,7 @@ func (runtime *Runtime) Create(config *Config, name string) (*Container, []strin
// Commit creates a new filesystem image from the current state of a container.
// The image can optionally be tagged into a repository
func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) {
func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *runconfig.Config) (*Image, error) {
// FIXME: freeze the container before copying it to avoid data corruption?
// FIXME: this shouldn't be in commands.
if err := container.Mount(); err != nil {

View File

@ -10,6 +10,7 @@ import (
"github.com/dotcloud/docker/engine"
"github.com/dotcloud/docker/pkg/graphdb"
"github.com/dotcloud/docker/registry"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"io"
"io/ioutil"
@ -662,7 +663,7 @@ func (srv *Server) ImageInsert(job *engine.Job) engine.Status {
}
defer file.Body.Close()
config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.sysInfo)
config, _, _, err := runconfig.Parse([]string{img.ID, "echo", "insert", url, path}, srv.runtime.sysInfo)
if err != nil {
return job.Error(err)
}
@ -1043,7 +1044,7 @@ func (srv *Server) ContainerCommit(job *engine.Job) engine.Status {
if container == nil {
return job.Errorf("No such container: %s", name)
}
var config Config
var config runconfig.Config
if err := job.GetenvJson("config", &config); err != nil {
return job.Error(err)
}
@ -1623,7 +1624,7 @@ func (srv *Server) ContainerCreate(job *engine.Job) engine.Status {
} else if len(job.Args) > 1 {
return job.Errorf("Usage: %s", job.Name)
}
config := ContainerConfigFromJob(job)
config := runconfig.ContainerConfigFromJob(job)
if config.Memory != 0 && config.Memory < 524288 {
return job.Errorf("Minimum memory limit allowed is 512k")
}
@ -1989,7 +1990,7 @@ func (srv *Server) canDeleteImage(imgID string) error {
return nil
}
func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error) {
func (srv *Server) ImageGetCached(imgID string, config *runconfig.Config) (*Image, error) {
// Retrieve all images
images, err := srv.runtime.graph.Map()
@ -2013,7 +2014,7 @@ func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error)
if err != nil {
return nil, err
}
if CompareConfig(&img.ContainerConfig, config) {
if runconfig.Compare(&img.ContainerConfig, config) {
if match == nil || match.Created.Before(img.Created) {
match = img
}
@ -2022,7 +2023,7 @@ func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error)
return match, nil
}
func (srv *Server) RegisterLinks(container *Container, hostConfig *HostConfig) error {
func (srv *Server) RegisterLinks(container *Container, hostConfig *runconfig.HostConfig) error {
runtime := srv.runtime
if hostConfig != nil && hostConfig.Links != nil {
@ -2066,7 +2067,7 @@ func (srv *Server) ContainerStart(job *engine.Job) engine.Status {
}
// If no environment was set, then no hostconfig was passed.
if len(job.Environ()) > 0 {
hostConfig := ContainerHostConfigFromJob(job)
hostConfig := runconfig.ContainerHostConfigFromJob(job)
// Validate the HostConfig binds. Make sure that:
// 1) the source of a bind mount isn't /
// The bind mount "/:/foo" isn't allowed.
@ -2310,7 +2311,7 @@ func (srv *Server) JobInspect(job *engine.Job) engine.Status {
}
object = &struct {
*Container
HostConfig *HostConfig
HostConfig *runconfig.HostConfig
}{container, container.hostConfig}
default:
return job.Errorf("Unknown kind: %s", kind)

205
utils.go
View File

@ -1,14 +1,12 @@
package docker
import (
"fmt"
"github.com/dotcloud/docker/archive"
"github.com/dotcloud/docker/nat"
"github.com/dotcloud/docker/pkg/namesgenerator"
"github.com/dotcloud/docker/pkg/opts"
"github.com/dotcloud/docker/runconfig"
"github.com/dotcloud/docker/utils"
"io"
"strings"
"sync/atomic"
)
@ -16,204 +14,7 @@ type Change struct {
archive.Change
}
// Compare two Config struct. Do not compare the "Image" nor "Hostname" fields
// If OpenStdin is set, then it differs
func CompareConfig(a, b *Config) bool {
if a == nil || b == nil ||
a.OpenStdin || b.OpenStdin {
return false
}
if a.AttachStdout != b.AttachStdout ||
a.AttachStderr != b.AttachStderr ||
a.User != b.User ||
a.Memory != b.Memory ||
a.MemorySwap != b.MemorySwap ||
a.CpuShares != b.CpuShares ||
a.OpenStdin != b.OpenStdin ||
a.Tty != b.Tty ||
a.VolumesFrom != b.VolumesFrom {
return false
}
if len(a.Cmd) != len(b.Cmd) ||
len(a.Dns) != len(b.Dns) ||
len(a.Env) != len(b.Env) ||
len(a.PortSpecs) != len(b.PortSpecs) ||
len(a.ExposedPorts) != len(b.ExposedPorts) ||
len(a.Entrypoint) != len(b.Entrypoint) ||
len(a.Volumes) != len(b.Volumes) {
return false
}
for i := 0; i < len(a.Cmd); i++ {
if a.Cmd[i] != b.Cmd[i] {
return false
}
}
for i := 0; i < len(a.Dns); i++ {
if a.Dns[i] != b.Dns[i] {
return false
}
}
for i := 0; i < len(a.Env); i++ {
if a.Env[i] != b.Env[i] {
return false
}
}
for i := 0; i < len(a.PortSpecs); i++ {
if a.PortSpecs[i] != b.PortSpecs[i] {
return false
}
}
for k := range a.ExposedPorts {
if _, exists := b.ExposedPorts[k]; !exists {
return false
}
}
for i := 0; i < len(a.Entrypoint); i++ {
if a.Entrypoint[i] != b.Entrypoint[i] {
return false
}
}
for key := range a.Volumes {
if _, exists := b.Volumes[key]; !exists {
return false
}
}
return true
}
func MergeConfig(userConf, imageConf *Config) error {
if userConf.User == "" {
userConf.User = imageConf.User
}
if userConf.Memory == 0 {
userConf.Memory = imageConf.Memory
}
if userConf.MemorySwap == 0 {
userConf.MemorySwap = imageConf.MemorySwap
}
if userConf.CpuShares == 0 {
userConf.CpuShares = imageConf.CpuShares
}
if userConf.ExposedPorts == nil || len(userConf.ExposedPorts) == 0 {
userConf.ExposedPorts = imageConf.ExposedPorts
} else if imageConf.ExposedPorts != nil {
if userConf.ExposedPorts == nil {
userConf.ExposedPorts = make(nat.PortSet)
}
for port := range imageConf.ExposedPorts {
if _, exists := userConf.ExposedPorts[port]; !exists {
userConf.ExposedPorts[port] = struct{}{}
}
}
}
if userConf.PortSpecs != nil && len(userConf.PortSpecs) > 0 {
if userConf.ExposedPorts == nil {
userConf.ExposedPorts = make(nat.PortSet)
}
ports, _, err := nat.ParsePortSpecs(userConf.PortSpecs)
if err != nil {
return err
}
for port := range ports {
if _, exists := userConf.ExposedPorts[port]; !exists {
userConf.ExposedPorts[port] = struct{}{}
}
}
userConf.PortSpecs = nil
}
if imageConf.PortSpecs != nil && len(imageConf.PortSpecs) > 0 {
utils.Debugf("Migrating image port specs to containter: %s", strings.Join(imageConf.PortSpecs, ", "))
if userConf.ExposedPorts == nil {
userConf.ExposedPorts = make(nat.PortSet)
}
ports, _, err := nat.ParsePortSpecs(imageConf.PortSpecs)
if err != nil {
return err
}
for port := range ports {
if _, exists := userConf.ExposedPorts[port]; !exists {
userConf.ExposedPorts[port] = struct{}{}
}
}
}
if !userConf.Tty {
userConf.Tty = imageConf.Tty
}
if !userConf.OpenStdin {
userConf.OpenStdin = imageConf.OpenStdin
}
if !userConf.StdinOnce {
userConf.StdinOnce = imageConf.StdinOnce
}
if userConf.Env == nil || len(userConf.Env) == 0 {
userConf.Env = imageConf.Env
} else {
for _, imageEnv := range imageConf.Env {
found := false
imageEnvKey := strings.Split(imageEnv, "=")[0]
for _, userEnv := range userConf.Env {
userEnvKey := strings.Split(userEnv, "=")[0]
if imageEnvKey == userEnvKey {
found = true
}
}
if !found {
userConf.Env = append(userConf.Env, imageEnv)
}
}
}
if userConf.Cmd == nil || len(userConf.Cmd) == 0 {
userConf.Cmd = imageConf.Cmd
}
if userConf.Dns == nil || len(userConf.Dns) == 0 {
userConf.Dns = imageConf.Dns
} else {
//duplicates aren't an issue here
userConf.Dns = append(userConf.Dns, imageConf.Dns...)
}
if userConf.Entrypoint == nil || len(userConf.Entrypoint) == 0 {
userConf.Entrypoint = imageConf.Entrypoint
}
if userConf.WorkingDir == "" {
userConf.WorkingDir = imageConf.WorkingDir
}
if userConf.VolumesFrom == "" {
userConf.VolumesFrom = imageConf.VolumesFrom
}
if userConf.Volumes == nil || len(userConf.Volumes) == 0 {
userConf.Volumes = imageConf.Volumes
} else {
for k, v := range imageConf.Volumes {
userConf.Volumes[k] = v
}
}
return nil
}
func parseLxcConfOpts(opts opts.ListOpts) ([]KeyValuePair, error) {
out := make([]KeyValuePair, opts.Len())
for i, o := range opts.GetAll() {
k, v, err := parseLxcOpt(o)
if err != nil {
return nil, err
}
out[i] = KeyValuePair{Key: k, Value: v}
}
return out, nil
}
func parseLxcOpt(opt string) (string, string, error) {
parts := strings.SplitN(opt, "=", 2)
if len(parts) != 2 {
return "", "", fmt.Errorf("Unable to parse lxc conf option: %s", opt)
}
return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
}
func migratePortMappings(config *Config, hostConfig *HostConfig) error {
func migratePortMappings(config *runconfig.Config, hostConfig *runconfig.HostConfig) error {
if config.PortSpecs != nil {
ports, bindings, err := nat.ParsePortSpecs(config.PortSpecs)
if err != nil {
@ -222,7 +23,7 @@ func migratePortMappings(config *Config, hostConfig *HostConfig) error {
config.PortSpecs = nil
if len(bindings) > 0 {
if hostConfig == nil {
hostConfig = &HostConfig{}
hostConfig = &runconfig.HostConfig{}
}
hostConfig.PortBindings = bindings
}