Merge pull request #7110 from tiborvass/merge-6907
Docker create (rebase of 6907)
This commit is contained in:
commit
ca39a3e36b
12 changed files with 646 additions and 106 deletions
|
@ -1965,9 +1965,112 @@ func (cli *DockerCli) pullImage(image string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdRun(args ...string) error {
|
||||
type cidFile struct {
|
||||
path string
|
||||
file *os.File
|
||||
written bool
|
||||
}
|
||||
|
||||
func newCIDFile(path string) (*cidFile, error) {
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
return nil, fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", path)
|
||||
}
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to create the container ID file: %s", err)
|
||||
}
|
||||
|
||||
return &cidFile{path: path, file: f}, nil
|
||||
}
|
||||
|
||||
func (cid *cidFile) Close() error {
|
||||
cid.file.Close()
|
||||
|
||||
if !cid.written {
|
||||
if err := os.Remove(cid.path); err != nil {
|
||||
return fmt.Errorf("failed to remove the CID file '%s': %s \n", cid.path, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cid *cidFile) Write(id string) error {
|
||||
if _, err := cid.file.Write([]byte(id)); err != nil {
|
||||
return fmt.Errorf("Failed to write the container ID to the file: %s", err)
|
||||
}
|
||||
cid.written = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) createContainer(config *runconfig.Config, hostConfig *runconfig.HostConfig, cidfile, name string) (engine.Env, error) {
|
||||
containerValues := url.Values{}
|
||||
if name != "" {
|
||||
containerValues.Set("name", name)
|
||||
}
|
||||
|
||||
var data interface{}
|
||||
if hostConfig != nil {
|
||||
data = runconfig.MergeConfigs(config, hostConfig)
|
||||
} else {
|
||||
data = config
|
||||
}
|
||||
|
||||
var containerIDFile *cidFile
|
||||
if cidfile != "" {
|
||||
var err error
|
||||
if containerIDFile, err = newCIDFile(cidfile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer containerIDFile.Close()
|
||||
}
|
||||
|
||||
//create the container
|
||||
stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), data, false)
|
||||
//if image not found try to pull it
|
||||
if statusCode == 404 {
|
||||
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image)
|
||||
|
||||
if err = cli.pullImage(config.Image); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Retry
|
||||
if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), data, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result engine.Env
|
||||
if err := result.Decode(stream); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, warning := range result.GetList("Warnings") {
|
||||
fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
|
||||
}
|
||||
|
||||
if containerIDFile != nil {
|
||||
if err = containerIDFile.Write(result.Get("Id")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdCreate(args ...string) error {
|
||||
// FIXME: just use runconfig.Parse already
|
||||
config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil)
|
||||
cmd := cli.Subcmd("create", "IMAGE [COMMAND] [ARG...]", "Create a new container")
|
||||
|
||||
// These are flags not stored in Config/HostConfig
|
||||
var (
|
||||
flName = cmd.String([]string{"-name"}, "", "Assign a name to the container")
|
||||
)
|
||||
|
||||
config, hostConfig, cmd, err := runconfig.ParseSubcommand(cmd, args, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1976,82 +2079,70 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Retrieve relevant client-side config
|
||||
createResult, err := cli.createContainer(config, hostConfig, hostConfig.ContainerIDFile, *flName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(cli.out, "%s\n", createResult.Get("Id"))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *DockerCli) CmdRun(args ...string) error {
|
||||
// FIXME: just use runconfig.Parse already
|
||||
cmd := cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container")
|
||||
|
||||
// These are flags not stored in Config/HostConfig
|
||||
var (
|
||||
flName = cmd.Lookup("name")
|
||||
flRm = cmd.Lookup("rm")
|
||||
flSigProxy = cmd.Lookup("sig-proxy")
|
||||
autoRemove, _ = strconv.ParseBool(flRm.Value.String())
|
||||
sigProxy, _ = strconv.ParseBool(flSigProxy.Value.String())
|
||||
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 the container in the background and print the new container ID")
|
||||
flSigProxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy received signals to the process (even in non-TTY mode). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.")
|
||||
flName = cmd.String([]string{"#name", "-name"}, "", "Assign a name to the container")
|
||||
flAttach *opts.ListOpts
|
||||
|
||||
ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d")
|
||||
ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
|
||||
ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
||||
)
|
||||
|
||||
// Disable sigProxy in case on TTY
|
||||
config, hostConfig, cmd, err := runconfig.ParseSubcommand(cmd, args, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.Image == "" {
|
||||
cmd.Usage()
|
||||
return nil
|
||||
}
|
||||
|
||||
if *flDetach {
|
||||
if fl := cmd.Lookup("attach"); fl != nil {
|
||||
flAttach = fl.Value.(*opts.ListOpts)
|
||||
if flAttach.Len() != 0 {
|
||||
return ErrConflictAttachDetach
|
||||
}
|
||||
}
|
||||
if *flAutoRemove {
|
||||
return ErrConflictDetachAutoRemove
|
||||
}
|
||||
|
||||
config.AttachStdin = false
|
||||
config.AttachStdout = false
|
||||
config.AttachStderr = false
|
||||
config.StdinOnce = false
|
||||
}
|
||||
|
||||
// Disable flSigProxy in case on TTY
|
||||
sigProxy := *flSigProxy
|
||||
if config.Tty {
|
||||
sigProxy = false
|
||||
}
|
||||
|
||||
var containerIDFile io.WriteCloser
|
||||
if len(hostConfig.ContainerIDFile) > 0 {
|
||||
if _, err := os.Stat(hostConfig.ContainerIDFile); err == nil {
|
||||
return fmt.Errorf("Container ID file found, make sure the other container isn't running or delete %s", hostConfig.ContainerIDFile)
|
||||
}
|
||||
if containerIDFile, err = os.Create(hostConfig.ContainerIDFile); err != nil {
|
||||
return fmt.Errorf("Failed to create the container ID file: %s", err)
|
||||
}
|
||||
defer func() {
|
||||
containerIDFile.Close()
|
||||
var (
|
||||
cidFileInfo os.FileInfo
|
||||
err error
|
||||
)
|
||||
if cidFileInfo, err = os.Stat(hostConfig.ContainerIDFile); err != nil {
|
||||
return
|
||||
}
|
||||
if cidFileInfo.Size() == 0 {
|
||||
if err := os.Remove(hostConfig.ContainerIDFile); err != nil {
|
||||
fmt.Printf("failed to remove Container ID file '%s': %s \n", hostConfig.ContainerIDFile, err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
containerValues := url.Values{}
|
||||
if name := flName.Value.String(); name != "" {
|
||||
containerValues.Set("name", name)
|
||||
}
|
||||
|
||||
//create the container
|
||||
stream, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false)
|
||||
//if image not found try to pull it
|
||||
if statusCode == 404 {
|
||||
fmt.Fprintf(cli.err, "Unable to find image '%s' locally\n", config.Image)
|
||||
|
||||
if err = cli.pullImage(config.Image); err != nil {
|
||||
return err
|
||||
}
|
||||
// Retry
|
||||
if stream, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config, false); err != nil {
|
||||
return err
|
||||
}
|
||||
} else if err != nil {
|
||||
runResult, err := cli.createContainer(config, nil, hostConfig.ContainerIDFile, *flName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var runResult engine.Env
|
||||
if err := runResult.Decode(stream); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, warning := range runResult.GetList("Warnings") {
|
||||
fmt.Fprintf(cli.err, "WARNING: %s\n", warning)
|
||||
}
|
||||
|
||||
if len(hostConfig.ContainerIDFile) > 0 {
|
||||
if _, err = containerIDFile.Write([]byte(runResult.Get("Id"))); err != nil {
|
||||
return fmt.Errorf("Failed to write the container ID to the file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if sigProxy {
|
||||
sigc := cli.forwardAllSignals(runResult.Get("Id"))
|
||||
defer signal.StopCatch(sigc)
|
||||
|
@ -2071,6 +2162,10 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
}()
|
||||
}
|
||||
|
||||
if *flAutoRemove && (hostConfig.RestartPolicy.Name == "always" || hostConfig.RestartPolicy.Name == "on-failure") {
|
||||
return ErrConflictRestartPolicyAndAutoRemove
|
||||
}
|
||||
|
||||
// We need to instanciate the chan because the select needs it. It can
|
||||
// be closed but can't be uninitialized.
|
||||
hijacked := make(chan io.Closer)
|
||||
|
@ -2158,7 +2253,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
|
|||
var status int
|
||||
|
||||
// Attached mode
|
||||
if autoRemove {
|
||||
if *flAutoRemove {
|
||||
// Autoremove: wait for the container to finish, retrieve
|
||||
// the exit code and remove the container
|
||||
if _, _, err := readBody(cli.call("POST", "/containers/"+runResult.Get("Id")+"/wait", nil, false)); err != nil {
|
||||
|
|
|
@ -217,6 +217,75 @@ _docker_cp()
|
|||
fi
|
||||
}
|
||||
|
||||
_docker_create()
|
||||
{
|
||||
case "$prev" in
|
||||
-a|--attach)
|
||||
COMPREPLY=( $( compgen -W 'stdin stdout stderr' -- "$cur" ) )
|
||||
return
|
||||
;;
|
||||
--cidfile|--env-file)
|
||||
_filedir
|
||||
return
|
||||
;;
|
||||
--volumes-from)
|
||||
__docker_containers_all
|
||||
return
|
||||
;;
|
||||
-v|--volume)
|
||||
case "$cur" in
|
||||
*:*)
|
||||
# TODO somehow do _filedir for stuff inside the image, if it's already specified (which is also somewhat difficult to determine)
|
||||
;;
|
||||
'')
|
||||
COMPREPLY=( $( compgen -W '/' -- "$cur" ) )
|
||||
compopt -o nospace
|
||||
;;
|
||||
/*)
|
||||
_filedir
|
||||
compopt -o nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
-e|--env)
|
||||
COMPREPLY=( $( compgen -e -- "$cur" ) )
|
||||
compopt -o nospace
|
||||
return
|
||||
;;
|
||||
--link)
|
||||
case "$cur" in
|
||||
*:*)
|
||||
;;
|
||||
*)
|
||||
__docker_containers_running
|
||||
COMPREPLY=( $( compgen -W "${COMPREPLY[*]}" -S ':' ) )
|
||||
compopt -o nospace
|
||||
;;
|
||||
esac
|
||||
return
|
||||
;;
|
||||
--entrypoint|-h|--hostname|-m|--memory|-u|--user|-w|--workdir|-c|--cpu-shares|-n|--name|-p|--publish|--expose|--dns|--lxc-conf)
|
||||
return
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "-n --networking --privileged -P --publish-all -i --interactive -t --tty --cidfile --entrypoint -h --hostname -m --memory -u --user -w --workdir -c --cpu-shares --name -a --attach -v --volume --link -e --env -p --publish --expose --dns --volumes-from --lxc-conf" -- "$cur" ) )
|
||||
;;
|
||||
*)
|
||||
local counter=$(__docker_pos_first_nonflag '--cidfile|--volumes-from|-v|--volume|-e|--env|--entrypoint|-h|--hostname|-m|--memory|-u|--user|-w|--workdir|-c|--cpu-shares|-n|--name|-a|--attach|--link|-p|--publish|--expose|--dns|--lxc-conf')
|
||||
|
||||
if [ $cword -eq $counter ]; then
|
||||
__docker_image_repos_and_tags_and_ids
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_docker_diff()
|
||||
{
|
||||
local counter=$(__docker_pos_first_nonflag)
|
||||
|
@ -670,6 +739,7 @@ _docker()
|
|||
build
|
||||
commit
|
||||
cp
|
||||
create
|
||||
diff
|
||||
events
|
||||
exec
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
function __fish_docker_no_subcommand --description 'Test if docker has yet to be given the subcommand'
|
||||
for i in (commandline -opc)
|
||||
if contains -- $i attach build commit cp diff events export history images import info insert inspect kill load login logs port ps pull push restart rm rmi run save search start stop tag top version wait
|
||||
if contains -- $i attach build commit cp create diff events export history images import info insert inspect kill load login logs port ps pull push restart rm rmi run save search start stop tag top version wait
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
@ -88,6 +88,33 @@ complete -c docker -A -f -n '__fish_seen_subcommand_from commit' -a '(__fish_pri
|
|||
# cp
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a cp -d "Copy files/folders from a container's filesystem to the host path"
|
||||
|
||||
# create
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a run -d 'Run a command in a new container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s P -l publish-all -d 'Publish all exposed ports to the host interfaces'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s a -l attach -d 'Attach to stdin, stdout or stderr.'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s c -l cpu-shares -d 'CPU shares (relative weight)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l cidfile -d 'Write the container ID to the file'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l dns -d 'Set custom dns servers'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s e -l env -d 'Set environment variables'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l entrypoint -d 'Overwrite the default entrypoint of the image'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l expose -d 'Expose a port from the container without publishing it to your host'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s h -l hostname -d 'Container host name'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s i -l interactive -d 'Keep stdin open even if not attached'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l link -d 'Add link to another container (name:alias)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l lxc-conf -d 'Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s m -l memory -d 'Memory limit (format: <number><optional unit>, where unit = b, k, m or g)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s n -l networking -d 'Enable networking for this container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l name -d 'Assign a name to the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s p -l publish -d "Publish a container's port to the host (format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort) (use 'docker port' to see the actual mapping)"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l privileged -d 'Give extended privileges to this container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s t -l tty -d 'Allocate a pseudo-tty'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s u -l user -d 'Username or UID'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s v -l volume -d 'Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -l volumes-from -d 'Mount volumes from the specified container(s)'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -s w -l workdir -d 'Working directory inside the container'
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from run' -a '(__fish_print_docker_images)' -d "Image"
|
||||
|
||||
|
||||
# diff
|
||||
complete -c docker -f -n '__fish_docker_no_subcommand' -a diff -d "Inspect changes on a container's filesystem"
|
||||
complete -c docker -A -f -n '__fish_seen_subcommand_from diff' -a '(__fish_print_docker_containers all)' -d "Container"
|
||||
|
|
|
@ -224,6 +224,32 @@ __docker_subcommand () {
|
|||
;;
|
||||
esac
|
||||
;;
|
||||
(create)
|
||||
_arguments \
|
||||
'-P[Publish all exposed ports to the host]' \
|
||||
'-a[Attach to stdin, stdout or stderr]' \
|
||||
'-c=-[CPU shares (relative weight)]:CPU shares:(0 10 100 200 500 800 1000)' \
|
||||
'--cidfile=-[Write the container ID to the file]:CID file:_files' \
|
||||
'*--dns=-[Set custom dns servers]:dns server: ' \
|
||||
'*-e=-[Set environment variables]:environment variable: ' \
|
||||
'--entrypoint=-[Overwrite the default entrypoint of the image]:entry point: ' \
|
||||
'*--expose=-[Expose a port from the container without publishing it]: ' \
|
||||
'-h=-[Container host name]:hostname:_hosts' \
|
||||
'-i[Keep stdin open even if not attached]' \
|
||||
'--link=-[Add link to another container]:link:->link' \
|
||||
'--lxc-conf=-[Add custom lxc options]:lxc options: ' \
|
||||
'-m=-[Memory limit (in bytes)]:limit: ' \
|
||||
'--name=-[Container name]:name: ' \
|
||||
'*-p=-[Expose a container'"'"'s port to the host]:port:_ports' \
|
||||
'--privileged[Give extended privileges to this container]' \
|
||||
'-t[Allocate a pseudo-tty]' \
|
||||
'-u=-[Username or UID]:user:_users' \
|
||||
'*-v=-[Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)]:volume: '\
|
||||
'--volumes-from=-[Mount volumes from the specified container]:volume: ' \
|
||||
'-w=-[Working directory inside the container]:directory:_directories' \
|
||||
'(-):images:__docker_images' \
|
||||
'(-):command: _command_names -e' \
|
||||
'*::arguments: _normal'
|
||||
(diff|export)
|
||||
_arguments '*:containers:__docker_containers'
|
||||
;;
|
||||
|
|
|
@ -50,6 +50,14 @@ func (daemon *Daemon) ContainerCreate(job *engine.Job) engine.Status {
|
|||
for _, warning := range buildWarnings {
|
||||
job.Errorf("%s\n", warning)
|
||||
}
|
||||
|
||||
if job.EnvExists("HostConfig") {
|
||||
hostConfig := runconfig.ContainerHostConfigFromJob(job)
|
||||
if err := daemon.setHostConfig(container, hostConfig); err != nil {
|
||||
return job.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
return engine.StatusOK
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ func init() {
|
|||
{"build", "Build an image from a Dockerfile"},
|
||||
{"commit", "Create a new image from a container's changes"},
|
||||
{"cp", "Copy files/folders from a container's filesystem to the host path"},
|
||||
{"create", "Create a new container"},
|
||||
{"diff", "Inspect changes on a container's filesystem"},
|
||||
{"events", "Get real time events from the server"},
|
||||
{"exec", "Run a command in an existing container"},
|
||||
|
|
131
docs/man/docker-create.1.md
Normal file
131
docs/man/docker-create.1.md
Normal file
|
@ -0,0 +1,131 @@
|
|||
% DOCKER(1) Docker User Manuals
|
||||
% Docker Community
|
||||
% JUNE 2014
|
||||
# NAME
|
||||
docker-create - Create a new container
|
||||
|
||||
# SYNOPSIS
|
||||
**docker create**
|
||||
[**-a**|**--attach**[=*[]*]]
|
||||
[**-c**|**--cpu-shares**[=*0*]]
|
||||
[**--cap-add**[=*[]*]]
|
||||
[**--cap-drop**[=*[]*]]
|
||||
[**--cidfile**[=*CIDFILE*]]
|
||||
[**--cpuset**[=*CPUSET*]]
|
||||
[**--device**[=*[]*]]
|
||||
[**--dns-search**[=*[]*]]
|
||||
[**--dns**[=*[]*]]
|
||||
[**-e**|**--env**[=*[]*]]
|
||||
[**--entrypoint**[=*ENTRYPOINT*]]
|
||||
[**--env-file**[=*[]*]]
|
||||
[**--expose**[=*[]*]]
|
||||
[**-h**|**--hostname**[=*HOSTNAME*]]
|
||||
[**-i**|**--interactive**[=*false*]]
|
||||
[**--link**[=*[]*]]
|
||||
[**--lxc-conf**[=*[]*]]
|
||||
[**-m**|**--memory**[=*MEMORY*]]
|
||||
[**--name**[=*NAME*]]
|
||||
[**--net**[=*"bridge"*]]
|
||||
[**-P**|**--publish-all**[=*false*]]
|
||||
[**-p**|**--publish**[=*[]*]]
|
||||
[**--privileged**[=*false*]]
|
||||
[**-t**|**--tty**[=*false*]]
|
||||
[**-u**|**--user**[=*USER*]]
|
||||
[**-v**|**--volume**[=*[]*]]
|
||||
[**--volumes-from**[=*[]*]]
|
||||
[**-w**|**--workdir**[=*WORKDIR*]]
|
||||
IMAGE [COMMAND] [ARG...]
|
||||
|
||||
# OPTIONS
|
||||
**-a**, **--attach**=[]
|
||||
Attach to STDIN, STDOUT or STDERR.
|
||||
|
||||
**-c**, **--cpu-shares**=0
|
||||
CPU shares (relative weight)
|
||||
|
||||
**--cap-add**=[]
|
||||
Add Linux capabilities
|
||||
|
||||
**--cap-drop**=[]
|
||||
Drop Linux capabilities
|
||||
|
||||
**--cidfile**=""
|
||||
Write the container ID to the file
|
||||
|
||||
**--cpuset**=""
|
||||
CPUs in which to allow execution (0-3, 0,1)
|
||||
|
||||
**--device**=[]
|
||||
Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc)
|
||||
|
||||
**--dns-search**=[]
|
||||
Set custom DNS search domains
|
||||
|
||||
**--dns**=[]
|
||||
Set custom DNS servers
|
||||
|
||||
**-e**, **--env**=[]
|
||||
Set environment variables
|
||||
|
||||
**--entrypoint**=""
|
||||
Overwrite the default ENTRYPOINT of the image
|
||||
|
||||
**--env-file**=[]
|
||||
Read in a line delimited file of environment variables
|
||||
|
||||
**--expose**=[]
|
||||
Expose a port from the container without publishing it to your host
|
||||
|
||||
**-h**, **--hostname**=""
|
||||
Container host name
|
||||
|
||||
**-i**, **--interactive**=*true*|*false*
|
||||
Keep STDIN open even if not attached. The default is *false*.
|
||||
|
||||
**--link**=[]
|
||||
Add link to another container in the form of name:alias
|
||||
|
||||
**--lxc-conf**=[]
|
||||
(lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
|
||||
|
||||
**-m**, **--memory**=""
|
||||
Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
|
||||
|
||||
**--name**=""
|
||||
Assign a name to the container
|
||||
|
||||
**--net**="bridge"
|
||||
Set the Network mode for the container
|
||||
'bridge': creates a new network stack for the container on the docker bridge
|
||||
'none': no networking for this container
|
||||
'container:<name|id>': reuses another container network stack
|
||||
'host': use the host network stack inside the container. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
|
||||
|
||||
**-P**, **--publish-all**=*true*|*false*
|
||||
Publish all exposed ports to the host interfaces. The default is *false*.
|
||||
|
||||
**-p**, **--publish**=[]
|
||||
Publish a container's port to the host
|
||||
format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort
|
||||
(use 'docker port' to see the actual mapping)
|
||||
|
||||
**--privileged**=*true*|*false*
|
||||
Give extended privileges to this container. The default is *false*.
|
||||
|
||||
**-t**, **--tty**=*true*|*false*
|
||||
Allocate a pseudo-TTY. The default is *false*.
|
||||
|
||||
**-u**, **--user**=""
|
||||
Username or UID
|
||||
|
||||
**-v**, **--volume**=[]
|
||||
Bind mount a volume (e.g., from the host: -v /host:/container, from Docker: -v /container)
|
||||
|
||||
**--volumes-from**=[]
|
||||
Mount volumes from the specified container(s)
|
||||
|
||||
**-w**, **--workdir**=""
|
||||
Working directory inside the container
|
||||
|
||||
# HISTORY
|
||||
August 2014, updated by Sven Dowideit <SvenDowideit@home.org.au>
|
|
@ -89,10 +89,12 @@ unix://[/path/to/socket] to use.
|
|||
**docker-cp(1)**
|
||||
Copy files/folders from a container's filesystem to the host at path
|
||||
|
||||
**docker-create(1)**
|
||||
Create a new container
|
||||
|
||||
**docker-diff(1)**
|
||||
Inspect changes on a container's filesystem
|
||||
|
||||
|
||||
**docker-events(1)**
|
||||
Get real time events from the server
|
||||
|
||||
|
|
|
@ -370,6 +370,63 @@ path. Paths are relative to the root of the filesystem.
|
|||
|
||||
Copy files/folders from the PATH to the HOSTPATH
|
||||
|
||||
|
||||
## create
|
||||
|
||||
Creates a new container.
|
||||
|
||||
Usage: docker create [OPTIONS] IMAGE[:TAG] [COMMAND] [ARG...]
|
||||
|
||||
|
||||
-a, --attach=[] Attach to STDIN, STDOUT, STDERR.
|
||||
-c, --cpu-shares=0 CPU shares (relative weight)
|
||||
--cidfile="" Write the container ID to the file
|
||||
--dns=[] Set custom DNS servers
|
||||
--dns-search=[] Set custom DNS search domains
|
||||
-e, --env=[] Set environment variables
|
||||
--entrypoint="" Overwrite the default entrypoint of the image
|
||||
--env-file=[] Read in a line delimited file of environment variables
|
||||
--expose=[] Expose a port from the container without publishing it to your host
|
||||
-h, --hostname="" Container host name
|
||||
-i, --interactive=false Keep `STDIN` open even if not attached
|
||||
--link=[] Add link to another container (name:alias)
|
||||
--lxc-conf=[] (lxc exec-driver only) Add custom lxc options --lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
|
||||
-m, --memory="" Memory limit (format: <number><optional unit>, where unit = b, k, m or g)
|
||||
--name="" Assign a name to the container
|
||||
--net="bridge" Set the Network mode for the container
|
||||
'bridge': creates a new network stack for the container on the docker bridge
|
||||
'none': no networking for this container
|
||||
'container:<name|id>': reuses another container network stack
|
||||
'host': use the host network stack inside the container
|
||||
-p, --publish=[] Publish a container's port to the host
|
||||
format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort
|
||||
(use 'docker port' to see the actual mapping)
|
||||
-P, --publish-all=false Publish all exposed ports to the host interfaces
|
||||
--privileged=false Give extended privileges to this container
|
||||
-t, --tty=false Allocate a pseudo-TTY
|
||||
-u, --user="" Username or UID
|
||||
-v, --volume=[] Bind mount a volume (e.g. from the host: -v /host:/container, from docker: -v /container)
|
||||
--volumes-from=[] Mount volumes from the specified container(s)
|
||||
-w, --workdir="" Working directory inside the container
|
||||
|
||||
|
||||
The `docker create` command creates a writeable container layer over
|
||||
the specified image and prepares it for running the specified command.
|
||||
The container ID is then printed to `STDOUT`.
|
||||
This is similar to `docker run -d` except the container is never started.
|
||||
You can then use the `docker start <container_id>` command to start the
|
||||
container at any point.
|
||||
|
||||
This is useful when you want to set up a container configuration ahead
|
||||
of time so that it is ready to start when you need it.
|
||||
|
||||
### Example:
|
||||
|
||||
$ sudo docker create -t -i fedora bash
|
||||
6d8af538ec541dd581ebc2a24153a28329acb5268abe5ef868c1f1a261221752
|
||||
$ sudo docker start -a -i 6d8af538ec5
|
||||
bash-4.2#
|
||||
|
||||
## diff
|
||||
|
||||
List the changed files and directories in a container᾿s filesystem
|
||||
|
|
116
integration-cli/docker_cli_create_test.go
Normal file
116
integration-cli/docker_cli_create_test.go
Normal file
|
@ -0,0 +1,116 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Make sure we can create a simple container with some args
|
||||
func TestDockerCreateArgs(t *testing.T) {
|
||||
runCmd := exec.Command(dockerBinary, "create", "busybox", "command", "arg1", "arg2", "arg with space")
|
||||
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
||||
errorOut(err, t, out)
|
||||
|
||||
cleanedContainerID := stripTrailingCharacters(out)
|
||||
|
||||
inspectCmd := exec.Command(dockerBinary, "inspect", cleanedContainerID)
|
||||
inspectOut, _, err := runCommandWithOutput(inspectCmd)
|
||||
errorOut(err, t, fmt.Sprintf("out should've been a container id: %v %v", inspectOut, err))
|
||||
|
||||
containers := []struct {
|
||||
ID string
|
||||
Created time.Time
|
||||
Path string
|
||||
Args []string
|
||||
Image string
|
||||
}{}
|
||||
if err := json.Unmarshal([]byte(inspectOut), &containers); err != nil {
|
||||
t.Fatalf("Error inspecting the container: %s", err)
|
||||
}
|
||||
if len(containers) != 1 {
|
||||
t.Fatalf("Unexpected container count. Expected 0, received: %d", len(containers))
|
||||
}
|
||||
|
||||
c := containers[0]
|
||||
if c.Path != "command" {
|
||||
t.Fatalf("Unexpected container path. Expected command, received: %s", c.Path)
|
||||
}
|
||||
|
||||
b := false
|
||||
expected := []string{"arg1", "arg2", "arg with space"}
|
||||
for i, arg := range expected {
|
||||
if arg != c.Args[i] {
|
||||
b = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if len(c.Args) != len(expected) || b {
|
||||
t.Fatalf("Unexpected args. Expected %v, received: %v", expected, c.Args)
|
||||
}
|
||||
|
||||
deleteAllContainers()
|
||||
|
||||
logDone("create - args")
|
||||
}
|
||||
|
||||
// Make sure we can set hostconfig options too
|
||||
func TestDockerCreateHostConfig(t *testing.T) {
|
||||
runCmd := exec.Command(dockerBinary, "create", "-P", "busybox", "echo")
|
||||
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
||||
errorOut(err, t, out)
|
||||
|
||||
cleanedContainerID := stripTrailingCharacters(out)
|
||||
|
||||
inspectCmd := exec.Command(dockerBinary, "inspect", cleanedContainerID)
|
||||
inspectOut, _, err := runCommandWithOutput(inspectCmd)
|
||||
errorOut(err, t, fmt.Sprintf("out should've been a container id: %v %v", inspectOut, err))
|
||||
|
||||
containers := []struct {
|
||||
HostConfig *struct {
|
||||
PublishAllPorts bool
|
||||
}
|
||||
}{}
|
||||
if err := json.Unmarshal([]byte(inspectOut), &containers); err != nil {
|
||||
t.Fatalf("Error inspecting the container: %s", err)
|
||||
}
|
||||
if len(containers) != 1 {
|
||||
t.Fatalf("Unexpected container count. Expected 0, received: %d", len(containers))
|
||||
}
|
||||
|
||||
c := containers[0]
|
||||
if c.HostConfig == nil {
|
||||
t.Fatalf("Expected HostConfig, got none")
|
||||
}
|
||||
|
||||
if !c.HostConfig.PublishAllPorts {
|
||||
t.Fatalf("Expected PublishAllPorts, got false")
|
||||
}
|
||||
|
||||
deleteAllContainers()
|
||||
|
||||
logDone("create - hostconfig")
|
||||
}
|
||||
|
||||
// "test123" should be printed by docker create + start
|
||||
func TestDockerCreateEchoStdout(t *testing.T) {
|
||||
runCmd := exec.Command(dockerBinary, "create", "busybox", "echo", "test123")
|
||||
out, _, _, err := runCommandWithStdoutStderr(runCmd)
|
||||
errorOut(err, t, out)
|
||||
|
||||
cleanedContainerID := stripTrailingCharacters(out)
|
||||
|
||||
runCmd = exec.Command(dockerBinary, "start", "-ai", cleanedContainerID)
|
||||
out, _, _, err = runCommandWithStdoutStderr(runCmd)
|
||||
errorOut(err, t, out)
|
||||
|
||||
if out != "test123\n" {
|
||||
t.Errorf("container should've printed 'test123', got '%s'", out)
|
||||
}
|
||||
|
||||
deleteAllContainers()
|
||||
|
||||
logDone("create - echo test123")
|
||||
}
|
|
@ -57,7 +57,27 @@ type HostConfig struct {
|
|||
RestartPolicy RestartPolicy
|
||||
}
|
||||
|
||||
// This is used by the create command when you want to set both the
|
||||
// Config and the HostConfig in the same call
|
||||
type ConfigAndHostConfig struct {
|
||||
Config
|
||||
HostConfig HostConfig
|
||||
}
|
||||
|
||||
func MergeConfigs(config *Config, hostConfig *HostConfig) *ConfigAndHostConfig {
|
||||
return &ConfigAndHostConfig{
|
||||
*config,
|
||||
*hostConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func ContainerHostConfigFromJob(job *engine.Job) *HostConfig {
|
||||
if job.EnvExists("HostConfig") {
|
||||
hostConfig := HostConfig{}
|
||||
job.GetenvJson("HostConfig", &hostConfig)
|
||||
return &hostConfig
|
||||
}
|
||||
|
||||
hostConfig := &HostConfig{
|
||||
ContainerIDFile: job.Getenv("ContainerIDFile"),
|
||||
Privileged: job.GetenvBool("Privileged"),
|
||||
|
|
|
@ -17,18 +17,15 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrInvalidWorkingDirectory = fmt.Errorf("The working directory is invalid. It needs to be an absolute path.")
|
||||
ErrConflictAttachDetach = fmt.Errorf("Conflicting options: -a and -d")
|
||||
ErrConflictContainerNetworkAndLinks = fmt.Errorf("Conflicting options: --net=container can't be used with links. This would result in undefined behavior.")
|
||||
ErrConflictContainerNetworkAndDns = fmt.Errorf("Conflicting options: --net=container can't be used with --dns. This configuration is invalid.")
|
||||
ErrConflictDetachAutoRemove = fmt.Errorf("Conflicting options: --rm and -d")
|
||||
ErrConflictNetworkHostname = fmt.Errorf("Conflicting options: -h and the network mode (--net)")
|
||||
ErrConflictHostNetworkAndDns = fmt.Errorf("Conflicting options: --net=host can't be used with --dns. This configuration is invalid.")
|
||||
ErrConflictHostNetworkAndLinks = fmt.Errorf("Conflicting options: --net=host can't be used with links. This would result in undefined behavior.")
|
||||
ErrConflictRestartPolicyAndAutoRemove = fmt.Errorf("Conflicting options: --restart and --rm")
|
||||
ErrInvalidWorkingDirectory = fmt.Errorf("The working directory is invalid. It needs to be an absolute path.")
|
||||
ErrConflictContainerNetworkAndLinks = fmt.Errorf("Conflicting options: --net=container can't be used with links. This would result in undefined behavior.")
|
||||
ErrConflictContainerNetworkAndDns = fmt.Errorf("Conflicting options: --net=container can't be used with --dns. This configuration is invalid.")
|
||||
ErrConflictNetworkHostname = fmt.Errorf("Conflicting options: -h and the network mode (--net)")
|
||||
ErrConflictHostNetworkAndDns = fmt.Errorf("Conflicting options: --net=host can't be used with --dns. This configuration is invalid.")
|
||||
ErrConflictHostNetworkAndLinks = fmt.Errorf("Conflicting options: --net=host can't be used with links. This would result in undefined behavior.")
|
||||
)
|
||||
|
||||
//FIXME Only used in tests
|
||||
// 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)
|
||||
|
@ -60,8 +57,6 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
|
|||
flCapAdd = opts.NewListOpts(nil)
|
||||
flCapDrop = opts.NewListOpts(nil)
|
||||
|
||||
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 and 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")
|
||||
|
@ -77,15 +72,13 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
|
|||
flCpuset = cmd.String([]string{"-cpuset"}, "", "CPUs in which to allow execution (0-3, 0,1)")
|
||||
flNetMode = cmd.String([]string{"-net"}, "bridge", "Set the Network mode for the container\n'bridge': creates a new network stack for the container on the docker bridge\n'none': no networking for this container\n'container:<name|id>': reuses another container network stack\n'host': use the host network stack inside the container. Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.")
|
||||
flRestartPolicy = cmd.String([]string{"-restart"}, "", "Restart policy to apply when a container exits (no, on-failure[:max-retry], always)")
|
||||
// For documentation purpose
|
||||
_ = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy received signals to the process (even in non-TTY mode). SIGCHLD, SIGSTOP, and SIGKILL are not proxied.")
|
||||
_ = 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 in the form of name:alias")
|
||||
cmd.Var(&flDevices, []string{"-device"}, "Add a host device to the container (e.g. --device=/dev/sdc:/dev/xvdc)")
|
||||
|
||||
cmd.Var(&flEnv, []string{"e", "-env"}, "Set environment variables")
|
||||
cmd.Var(&flEnvFile, []string{"-env-file"}, "Read in a line delimited file of environment variables")
|
||||
|
||||
|
@ -109,15 +102,15 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
|
|||
}
|
||||
|
||||
// Validate input params
|
||||
if *flDetach && flAttach.Len() > 0 {
|
||||
return nil, nil, cmd, ErrConflictAttachDetach
|
||||
}
|
||||
if *flWorkingDir != "" && !path.IsAbs(*flWorkingDir) {
|
||||
return nil, nil, cmd, ErrInvalidWorkingDirectory
|
||||
}
|
||||
if *flDetach && *flAutoRemove {
|
||||
return nil, nil, cmd, ErrConflictDetachAutoRemove
|
||||
}
|
||||
|
||||
var (
|
||||
attachStdin = flAttach.Get("stdin")
|
||||
attachStdout = flAttach.Get("stdout")
|
||||
attachStderr = flAttach.Get("stderr")
|
||||
)
|
||||
|
||||
if *flNetMode != "bridge" && *flNetMode != "none" && *flHostname != "" {
|
||||
return nil, nil, cmd, ErrConflictNetworkHostname
|
||||
|
@ -140,13 +133,11 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
|
|||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
if flAttach.Len() == 0 {
|
||||
attachStdout = true
|
||||
attachStderr = true
|
||||
if *flStdin {
|
||||
attachStdin = true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,10 +245,6 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
|
|||
return nil, nil, cmd, err
|
||||
}
|
||||
|
||||
if *flAutoRemove && (restartPolicy.Name == "always" || restartPolicy.Name == "on-failure") {
|
||||
return nil, nil, cmd, ErrConflictRestartPolicyAndAutoRemove
|
||||
}
|
||||
|
||||
config := &Config{
|
||||
Hostname: hostname,
|
||||
Domainname: domainname,
|
||||
|
@ -270,9 +257,9 @@ func parseRun(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Conf
|
|||
Memory: flMemory,
|
||||
CpuShares: *flCpuShares,
|
||||
Cpuset: *flCpuset,
|
||||
AttachStdin: flAttach.Get("stdin"),
|
||||
AttachStdout: flAttach.Get("stdout"),
|
||||
AttachStderr: flAttach.Get("stderr"),
|
||||
AttachStdin: attachStdin,
|
||||
AttachStdout: attachStdout,
|
||||
AttachStderr: attachStderr,
|
||||
Env: envVariables,
|
||||
Cmd: runCmd,
|
||||
Image: image,
|
||||
|
|
Loading…
Reference in a new issue