Merge pull request #2425 from dotcloud/links-allow-name

Add -name for docker run
This commit is contained in:
Michael Crosby 2013-10-28 18:16:06 -07:00
Родитель c0662488c7 0d2924408b
Коммит a52e23c5e4
23 изменённых файлов: 258 добавлений и 307 удалений

80
api.go
Просмотреть файл

@ -6,7 +6,6 @@ import (
"encoding/json"
"fmt"
"github.com/dotcloud/docker/auth"
"github.com/dotcloud/docker/gograph"
"github.com/dotcloud/docker/utils"
"github.com/gorilla/mux"
"io"
@ -522,8 +521,12 @@ func postImagesPush(srv *Server, version float64, w http.ResponseWriter, r *http
}
func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return nil
}
config := &Config{}
out := &APIRun{}
name := r.Form.Get("name")
if err := json.NewDecoder(r.Body).Decode(config); err != nil {
return err
@ -539,7 +542,7 @@ func postContainersCreate(srv *Server, version float64, w http.ResponseWriter, r
config.Dns = defaultDns
}
id, warnings, err := srv.ContainerCreate(config)
id, warnings, err := srv.ContainerCreate(config, name)
if err != nil {
return err
}
@ -649,6 +652,10 @@ func postContainersStart(srv *Server, version float64, w http.ResponseWriter, r
return fmt.Errorf("Missing parameter")
}
name := vars["name"]
// Register any links from the host config before starting the container
if err := srv.RegisterLinks(name, hostConfig); err != nil {
return err
}
if err := srv.ContainerStart(name, hostConfig); err != nil {
return err
}
@ -1019,73 +1026,6 @@ func makeHttpHandler(srv *Server, logging bool, localMethod string, localRoute s
}
}
func getContainersLinks(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if err := parseForm(r); err != nil {
return err
}
runtime := srv.runtime
all, err := getBoolParam(r.Form.Get("all"))
if err != nil {
return err
}
out := []APILink{}
err = runtime.containerGraph.Walk("/", func(p string, e *gograph.Entity) error {
if container := runtime.Get(e.ID()); container != nil {
if !all && strings.Contains(p, container.ID) {
return nil
}
out = append(out, APILink{
Path: p,
ContainerID: container.ID,
Image: runtime.repositories.ImageName(container.Image),
})
}
return nil
}, -1)
if err != nil {
return err
}
return writeJSON(w, http.StatusOK, out)
}
func postContainerLink(srv *Server, version float64, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
if vars == nil {
return fmt.Errorf("Missing parameter")
}
values := make(map[string]string)
if matchesContentType(r.Header.Get("Content-Type"), "application/json") && r.Body != nil {
defer r.Body.Close()
dec := json.NewDecoder(r.Body)
if err := dec.Decode(&values); err != nil {
return err
}
} else {
return fmt.Errorf("Invalid json body")
}
currentName := values["currentName"]
newName := values["newName"]
if currentName == "" {
return fmt.Errorf("currentName cannot be empty")
}
if newName == "" {
return fmt.Errorf("newName cannot be empty")
}
if err := srv.runtime.RenameLink(currentName, newName); err != nil {
if strings.HasSuffix(err.Error(), "name are not unique") {
return fmt.Errorf("Conflict, %s already exists", newName)
}
return err
}
return nil
}
func createRouter(srv *Server, logging bool) (*mux.Router, error) {
r := mux.NewRouter()
@ -1106,7 +1046,6 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
"/containers/{name:.*}/json": getContainersByName,
"/containers/{name:.*}/top": getContainersTop,
"/containers/{name:.*}/attach/ws": wsContainersAttach,
"/containers/links": getContainersLinks,
},
"POST": {
"/auth": postAuth,
@ -1125,7 +1064,6 @@ func createRouter(srv *Server, logging bool) (*mux.Router, error) {
"/containers/{name:.*}/resize": postContainersResize,
"/containers/{name:.*}/attach": postContainersAttach,
"/containers/{name:.*}/copy": postContainersCopy,
"/containers/link": postContainerLink,
},
"DELETE": {
"/containers/{name:.*}": deleteContainers,

Просмотреть файл

@ -121,9 +121,3 @@ type APICopy struct {
Resource string
HostPath string
}
type APILink struct {
Path string
ContainerID string
Image string
}

Просмотреть файл

@ -352,7 +352,7 @@ func TestGetContainersJSON(t *testing.T) {
container, _, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"},
})
}, "")
if err != nil {
t.Fatal(err)
}
@ -391,6 +391,7 @@ func TestGetContainersExport(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -441,6 +442,7 @@ func TestGetContainersChanges(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/rm", "/etc/passwd"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -485,6 +487,7 @@ func TestGetContainersTop(t *testing.T) {
Cmd: []string{"/bin/sh", "-c", "cat"},
OpenStdin: true,
},
"",
)
if err != nil {
t.Fatal(err)
@ -566,6 +569,7 @@ func TestGetContainersByName(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "test"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -597,6 +601,7 @@ func TestPostCommit(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -692,6 +697,7 @@ func TestPostContainersKill(t *testing.T) {
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
"",
)
if err != nil {
t.Fatal(err)
@ -734,6 +740,7 @@ func TestPostContainersRestart(t *testing.T) {
Cmd: []string{"/bin/top"},
OpenStdin: true,
},
"",
)
if err != nil {
t.Fatal(err)
@ -788,6 +795,7 @@ func TestPostContainersStart(t *testing.T) {
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
"",
)
if err != nil {
t.Fatal(err)
@ -840,6 +848,7 @@ func TestPostContainersStop(t *testing.T) {
Cmd: []string{"/bin/top"},
OpenStdin: true,
},
"",
)
if err != nil {
t.Fatal(err)
@ -887,6 +896,7 @@ func TestPostContainersWait(t *testing.T) {
Cmd: []string{"/bin/sleep", "1"},
OpenStdin: true,
},
"",
)
if err != nil {
t.Fatal(err)
@ -929,6 +939,7 @@ func TestPostContainersAttach(t *testing.T) {
Cmd: []string{"/bin/cat"},
OpenStdin: true,
},
"",
)
if err != nil {
t.Fatal(err)
@ -1018,6 +1029,7 @@ func TestPostContainersAttachStderr(t *testing.T) {
Cmd: []string{"/bin/sh", "-c", "/bin/cat >&2"},
OpenStdin: true,
},
"",
)
if err != nil {
t.Fatal(err)
@ -1107,7 +1119,7 @@ func TestDeleteContainers(t *testing.T) {
container, _, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test"},
})
}, "")
if err != nil {
t.Fatal(err)
}
@ -1299,6 +1311,7 @@ func TestPostContainersCopy(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"touch", "/test.txt"},
},
"",
)
if err != nil {
t.Fatal(err)

Просмотреть файл

@ -335,7 +335,7 @@ func (b *buildFile) CmdAdd(args string) error {
b.config.Image = b.image
// Create the container and start it
container, _, err := b.runtime.Create(b.config)
container, _, err := b.runtime.Create(b.config, "")
if err != nil {
return err
}
@ -370,7 +370,7 @@ func (b *buildFile) run() (string, error) {
b.config.Image = b.image
// Create the container and start it
c, _, err := b.runtime.Create(b.config)
c, _, err := b.runtime.Create(b.config, "")
if err != nil {
return "", err
}
@ -433,7 +433,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error {
}
}
container, _, err := b.runtime.Create(b.config)
container, _, err := b.runtime.Create(b.config, "")
if err != nil {
return err
}

Просмотреть файл

@ -91,7 +91,6 @@ func (cli *DockerCli) CmdHelp(args ...string) error {
{"insert", "Insert a file in an image"},
{"inspect", "Return low-level information on a container"},
{"kill", "Kill a running container"},
{"link", "Link the container with a new name"},
{"login", "Register or Login to the docker registry server"},
{"logs", "Fetch the logs of a container"},
{"port", "Lookup the public-facing port which is NAT-ed to PRIVATE_PORT"},
@ -1163,12 +1162,15 @@ func (cli *DockerCli) CmdPs(args ...string) error {
if !*noTrunc {
out.ID = utils.TruncateID(out.ID)
}
// Remove the leading / from the names
for i := 0; i < len(out.Names); i++ {
out.Names[i] = out.Names[i][1:]
}
if !*quiet {
if !*noTrunc {
out.Command = utils.Trunc(out.Command, 20)
for i := 0; i < len(out.Names); i++ {
out.Names[i] = utils.Trunc(out.Names[i], 10)
}
}
fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", out.ID, out.Image, out.Command, utils.HumanDuration(time.Now().Sub(time.Unix(out.Created, 0))), out.Status, displayablePorts(out.Ports), strings.Join(out.Names, ","))
if *size {
@ -1191,27 +1193,6 @@ func (cli *DockerCli) CmdPs(args ...string) error {
return nil
}
func (cli *DockerCli) CmdLink(args ...string) error {
cmd := Subcmd("link", "CURRENT_NAME NEW_NAME", "Link the container with a new name")
if err := cmd.Parse(args); err != nil {
return nil
}
if cmd.NArg() != 2 {
cmd.Usage()
return nil
}
body := map[string]string{
"currentName": cmd.Arg(0),
"newName": cmd.Arg(1),
}
_, _, err := cli.call("POST", "/containers/link", body)
if err != nil {
return err
}
return nil
}
func (cli *DockerCli) CmdCommit(args ...string) error {
cmd := Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY [TAG]]", "Create a new image from a container's changes")
flComment := cmd.String("m", "", "Commit message")
@ -1535,6 +1516,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
flSigProxy := cmd.Lookup("sig-proxy")
sigProxy, _ := strconv.ParseBool(flSigProxy.Value.String())
flName := cmd.Lookup("name")
var containerIDFile *os.File
if len(hostConfig.ContainerIDFile) > 0 {
@ -1547,9 +1529,14 @@ func (cli *DockerCli) CmdRun(args ...string) error {
}
defer containerIDFile.Close()
}
containerValues := url.Values{}
name := flName.Value.String()
if name != "" {
containerValues.Set("name", name)
}
//create the container
body, statusCode, err := cli.call("POST", "/containers/create", config)
body, statusCode, err := cli.call("POST", "/containers/create?"+containerValues.Encode(), config)
//if image not found try to pull it
if statusCode == 404 {
_, tag := utils.ParseRepositoryTag(config.Image)
@ -1590,7 +1577,7 @@ func (cli *DockerCli) CmdRun(args ...string) error {
if err != nil {
return err
}
body, _, err = cli.call("POST", "/containers/create", config)
body, _, err = cli.call("POST", "/containers/create?"+containerValues.Encode(), config)
if err != nil {
return err
}

Просмотреть файл

@ -44,6 +44,7 @@ type Container struct {
ResolvConfPath string
HostnamePath string
HostsPath string
Name string
cmd *exec.Cmd
stdout *utils.WriteBroadcaster
@ -167,6 +168,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
flPrivileged := cmd.Bool("privileged", false, "Give extended privileges to this container")
flAutoRemove := cmd.Bool("rm", false, "Automatically remove the container when it exits (incompatible with -d)")
flSigProxy := cmd.Bool("sig-proxy", false, "Proxify all received signal to the process (even in non-tty mode)")
cmd.String("name", "", "Assign a name to the container")
if capabilities != nil && *flMemory > 0 && !capabilities.MemoryLimit {
//fmt.Fprintf(stdout, "WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
@ -199,7 +201,7 @@ func ParseRun(args []string, capabilities *Capabilities) (*Config, *HostConfig,
cmd.Var(&flLxcOpts, "lxc-conf", "Add custom lxc options -lxc-conf=\"lxc.cgroup.cpuset.cpus = 0,1\"")
var flLinks utils.ListOpts
cmd.Var(&flLinks, "link", "Add link to another container (containerid:alias)")
cmd.Var(&flLinks, "link", "Add link to another container (name:alias)")
if err := cmd.Parse(args); err != nil {
return nil, nil, cmd, err
@ -858,7 +860,7 @@ func (container *Container) Start(hostConfig *HostConfig) (err error) {
// Init any links between the parent and children
runtime := container.runtime
children, err := runtime.Children(fmt.Sprintf("/%s", container.ID))
children, err := runtime.Children(container.Name)
if err != nil {
return err
}

Просмотреть файл

@ -23,6 +23,7 @@ func TestIDFormat(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/sh", "-c", "echo hello world"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -393,6 +394,7 @@ func TestOutput(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -415,6 +417,7 @@ func TestContainerNetwork(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"ping", "-c", "1", "127.0.0.1"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -438,6 +441,7 @@ func TestKillDifferentUser(t *testing.T) {
OpenStdin: true,
User: "daemon",
},
"",
)
if err != nil {
t.Fatal(err)
@ -492,7 +496,7 @@ func TestCreateVolume(t *testing.T) {
if err != nil {
t.Fatal(err)
}
c, _, err := runtime.Create(config)
c, _, err := runtime.Create(config, "")
if err != nil {
t.Fatal(err)
}
@ -511,6 +515,7 @@ func TestKill(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -554,7 +559,7 @@ func TestExitCode(t *testing.T) {
trueContainer, _, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/true", ""},
})
}, "")
if err != nil {
t.Fatal(err)
}
@ -569,7 +574,7 @@ func TestExitCode(t *testing.T) {
falseContainer, _, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/false", ""},
})
}, "")
if err != nil {
t.Fatal(err)
}
@ -589,6 +594,7 @@ func TestRestart(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foobar"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -621,6 +627,7 @@ func TestRestartStdin(t *testing.T) {
OpenStdin: true,
},
"",
)
if err != nil {
t.Fatal(err)
@ -697,6 +704,7 @@ func TestUser(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"id"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -717,6 +725,7 @@ func TestUser(t *testing.T) {
User: "root",
},
"",
)
if err != nil {
t.Fatal(err)
@ -737,6 +746,7 @@ func TestUser(t *testing.T) {
User: "0",
},
"",
)
if err != nil || container.State.ExitCode != 0 {
t.Fatal(err)
@ -757,6 +767,7 @@ func TestUser(t *testing.T) {
User: "1",
},
"",
)
if err != nil {
t.Fatal(err)
@ -779,6 +790,7 @@ func TestUser(t *testing.T) {
User: "daemon",
},
"",
)
if err != nil {
t.Fatal(err)
@ -799,6 +811,7 @@ func TestUser(t *testing.T) {
User: "unknownuser",
},
"",
)
if err != nil {
t.Fatal(err)
@ -818,6 +831,7 @@ func TestMultipleContainers(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -828,6 +842,7 @@ func TestMultipleContainers(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"sleep", "2"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -874,6 +889,7 @@ func TestStdin(t *testing.T) {
OpenStdin: true,
},
"",
)
if err != nil {
t.Fatal(err)
@ -919,6 +935,7 @@ func TestTty(t *testing.T) {
OpenStdin: true,
},
"",
)
if err != nil {
t.Fatal(err)
@ -962,6 +979,7 @@ func TestEnv(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"env"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -1013,6 +1031,7 @@ func TestEntrypoint(t *testing.T) {
Entrypoint: []string{"/bin/echo"},
Cmd: []string{"-n", "foobar"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -1035,6 +1054,7 @@ func TestEntrypointNoCmd(t *testing.T) {
Image: GetTestImage(runtime).ID,
Entrypoint: []string{"/bin/echo", "foobar"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -1089,6 +1109,7 @@ func TestLXCConfig(t *testing.T) {
Memory: int64(mem),
CpuShares: int64(cpu),
},
"",
)
if err != nil {
t.Fatal(err)
@ -1111,6 +1132,7 @@ func TestCustomLxcConfig(t *testing.T) {
Hostname: "foobar",
},
"",
)
if err != nil {
t.Fatal(err)
@ -1140,6 +1162,7 @@ func BenchmarkRunSequencial(b *testing.B) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"},
},
"",
)
if err != nil {
b.Fatal(err)
@ -1172,6 +1195,7 @@ func BenchmarkRunParallel(b *testing.B) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"echo", "-n", "foo"},
},
"",
)
if err != nil {
complete <- err
@ -1324,6 +1348,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
Cmd: []string{"/bin/echo", "-n", "foobar"},
Volumes: map[string]struct{}{"/test": {}},
},
"",
)
if err != nil {
t.Fatal(err)
@ -1343,6 +1368,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) {
Cmd: []string{"/bin/echo", "-n", "foobar"},
VolumesFrom: container.ID,
},
"",
)
if err != nil {
t.Fatal(err)
@ -1378,6 +1404,7 @@ func TestRestartWithVolumes(t *testing.T) {
Cmd: []string{"echo", "-n", "foobar"},
Volumes: map[string]struct{}{"/test": {}},
},
"",
)
if err != nil {
t.Fatal(err)
@ -1421,6 +1448,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"},
Volumes: map[string]struct{}{"/test": {}},
},
"",
)
if err != nil {
t.Fatal(err)
@ -1450,6 +1478,7 @@ func TestVolumesFromWithVolumes(t *testing.T) {
VolumesFrom: container.ID,
Volumes: map[string]struct{}{"/test": {}},
},
"",
)
if err != nil {
t.Fatal(err)
@ -1484,7 +1513,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) {
if err != nil {
t.Fatal(err)
}
c, _, err := runtime.Create(config)
c, _, err := runtime.Create(config, "")
if err != nil {
t.Fatal(err)
}
@ -1555,6 +1584,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"},
Volumes: map[string]struct{}{"/test": {}},
},
"",
)
if err != nil {
t.Fatal(err)
@ -1583,6 +1613,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
Cmd: []string{"sh", "-c", "echo -n bar > /other/foo"},
Volumes: map[string]struct{}{"/other": {}},
},
"",
)
if err != nil {
t.Fatal(err)
@ -1603,7 +1634,7 @@ func TestMultipleVolumesFrom(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"/bin/echo", "-n", "foobar"},
VolumesFrom: strings.Join([]string{container.ID, container2.ID}, ","),
})
}, "")
if err != nil {
t.Fatal(err)

Просмотреть файл

@ -151,6 +151,7 @@ Create a container
}
:jsonparam config: the container's configuration
:query name: container name to use
:statuscode 201: no error
:statuscode 404: no such container
:statuscode 406: impossible to attach (container not running)

Просмотреть файл

@ -403,33 +403,6 @@ Insert file from github
Kill a running container
.. _cli_link:
``link``
--------
::
Usage: docker link CURRENT_NAME NEW_NAME
Link a container to a new name.
Examples:
~~~~~~~~~
.. code-block:: bash
$ docker link /59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc /redis
$ docker ls
NAME ID IMAGE
/redis 59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc crosbymichael/redis:latest
/59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc 59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc crosbymichael/redis:latest
This will create a new link for the existing name ``/59669e088202c2ebe150b4346cb3301562d073b51261176a354a74e8f618bfbc``
with the new name ``/redis`` so that we can new reference the same container under the new name ``/redis``.
.. _cli_login:
``login``
@ -604,7 +577,8 @@ network communication.
-lxc-conf=[]: Add custom lxc options -lxc-conf="lxc.cgroup.cpuset.cpus = 0,1"
-sig-proxy=false: Proxify all received signal to the process (even in non-tty mode)
-expose=[]: Expose a port from the container without publishing it to your host
-link="": Add link to another container (containerid:alias)
-link="": Add link to another container (name:alias)
-name="": Assign the specified name to the container. If no name is specific docker will generate a random name
Examples
--------
@ -680,12 +654,20 @@ without publishing the port to the host system's interfaces.
.. code-block:: bash
docker run -link /redis:redis ubuntu bash
docker run -name console -t -i ubuntu bash
This will create and run a new container with the container name
being ``console``.
.. code-block:: bash
docker run -link /redis:redis -name console ubuntu bash
The ``-link`` flag will link the container named ``/redis`` into the
newly created container with the alias ``redis``. The new container
can access the network and environment of the redis container via
environment variables.
environment variables. The ``-name`` flag will assign the name ``console``
to the newly created container.
.. _cli_search:

Просмотреть файл

@ -54,26 +54,14 @@ Run the redis container
.. code-block:: bash
docker run -d -e PASSWORD=docker crosbymichael/redis --requirepass docker
docker run -d -e PASSWORD=docker -name redis crosbymichael/redis --requirepass=docker
This will run our redis container using the default port of 6379 and using
as password to secure our service. Next we will link the redis container to
a new name using ``docker link``.
Linking an existing container
-----------------------------
Docker will automatically create an initial link with the container's id but
because the is long and not very user friendly we can link the container with
a new name.
.. code-block:: bash
docker link /39588b6a45100ef5b328b2c302ea085624f29e6cbab70f88be04793af02cec89 /redis
Now we can reference our running redis service using the friendly name ``/redis``.
We can issue all the commands that you would expect; start, stop, attach, using the new name.
This will run our redis container using the default port of 6379 and using docker
as password to secure our service. By specifying the ``-name`` flag on run
we will assign the name ``redis`` to this container.
We can issue all the commands that you would expect; start, stop, attach, using the name.
The name also allows us to link other containers into this one. If you do not specify a
name on docker run, docker will automatically generate a name for your container.
Linking redis as a child
------------------------
@ -90,12 +78,12 @@ Now lets start our web application with a link into redis.
.. code-block:: bash
docker run -t -i -link /redis:db ubuntu bash
docker run -t -i -link /redis:db -name webapp ubuntu bash
root@4c01db0b339c:/# env
HOSTNAME=4c01db0b339c
DB_NAME=/4c01db0b339cf19958731255a796ee072040a652f51652a4ade190ab8c27006f/db
DB_NAME=/webapp/db
TERM=xterm
DB_PORT=tcp://172.17.0.8:6379
DB_PORT_6379_TCP=tcp://172.17.0.8:6379
@ -118,7 +106,7 @@ network and environment information from the child.
.. code-block:: bash
# The name of the child container
DB_NAME=/4c01db0b339cf19958731255a796ee072040a652f51652a4ade190ab8c27006f/db
DB_NAME=/webapp/db
# The default protocol, ip, and port of the service running in the container
DB_PORT=tcp://172.17.0.8:6379
# A specific protocol, ip, and port of various services
@ -129,4 +117,4 @@ network and environment information from the child.
Accessing the network information along with the environment of the child container allows
us to easily connect to the redis service on the specific ip and port and use the password
specified in the environment.
specified in the environment.

Просмотреть файл

@ -132,6 +132,11 @@ func (db *Database) Set(fullPath, id string) (*Entity, error) {
return e, nil
}
// Return true if a name already exists in the database
func (db *Database) Exists(name string) bool {
return db.Get(name) != nil
}
func (db *Database) setEdge(parentPath, name string, e *Entity) error {
parent, err := db.get(parentPath)
if err != nil {
@ -189,14 +194,22 @@ func (db *Database) get(name string) (*Entity, error) {
// The key will be the full path of the entity
func (db *Database) List(name string, depth int) Entities {
out := Entities{}
for c := range db.children(name, depth) {
e, err := db.get(name)
if err != nil {
return out
}
for c := range db.children(e, name, depth) {
out[c.FullPath] = c.Entity
}
return out
}
func (db *Database) Walk(name string, walkFunc WalkFunc, depth int) error {
for c := range db.children(name, depth) {
e, err := db.get(name)
if err != nil {
return err
}
for c := range db.children(e, name, depth) {
if err := walkFunc(c.FullPath, c.Entity); err != nil {
return err
}
@ -327,10 +340,9 @@ type WalkMeta struct {
Edge *Edge
}
func (db *Database) children(name string, depth int) <-chan WalkMeta {
func (db *Database) children(e *Entity, name string, depth int) <-chan WalkMeta {
out := make(chan WalkMeta)
e, err := db.get(name)
if err != nil {
if e == nil {
close(out)
return out
}
@ -370,7 +382,7 @@ func (db *Database) children(name string, depth int) <-chan WalkMeta {
if depth != -1 {
nDepth -= 1
}
sc := db.children(meta.FullPath, nDepth)
sc := db.children(child, meta.FullPath, nDepth)
for c := range sc {
out <- c
}

Просмотреть файл

@ -28,6 +28,7 @@ func TestNewDatabase(t *testing.T) {
if db == nil {
t.Fatal("Database should not be nil")
}
db.Close()
defer destroyTestDb(dbpath)
}
@ -465,3 +466,26 @@ func TestRefPaths(t *testing.T) {
t.Fatalf("Expected reference count to be 2, got %d", len(refs))
}
}
func TestExistsTrue(t *testing.T) {
db, dbpath := newTestDb(t)
defer destroyTestDb(dbpath)
db.Set("/testing", "1")
if !db.Exists("/testing") {
t.Fatalf("/tesing should exist")
}
}
func TestExistsFalse(t *testing.T) {
db, dbpath := newTestDb(t)
defer destroyTestDb(dbpath)
db.Set("toerhe", "1")
if db.Exists("/testing") {
t.Fatalf("/tesing should not exist")
}
}

Просмотреть файл

@ -54,7 +54,7 @@ func (l *Link) ToEnv() []string {
alias := strings.ToUpper(l.Alias())
if p := l.getDefaultPort(); p != nil {
env = append(env, fmt.Sprintf("%s_PORT=%s:%s", alias, l.ChildIP, p.Port()))
env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
}
// Load exposed ports into the environment

Просмотреть файл

@ -89,7 +89,7 @@ func TestLinkEnv(t *testing.T) {
}
env[parts[0]] = parts[1]
}
if env["DOCKER_PORT"] != "172.0.17.2:6379" {
if env["DOCKER_PORT"] != "tcp://172.0.17.2:6379" {
t.Fatalf("Expected 172.0.17.2:6379, got %s", env["DOCKER_PORT"])
}
if env["DOCKER_PORT_6379_TCP"] != "tcp://172.0.17.2:6379" {

Просмотреть файл

Просмотреть файл

@ -265,7 +265,10 @@ func (runtime *Runtime) restore() error {
// Any containers that are left over do not exist in the graph
for _, container := range containers {
// Try to set the default name for a container if it exists prior to links
if _, err := runtime.containerGraph.Set(fmt.Sprintf("/%s", container.ID), container.ID); err != nil {
name := generateRandomName(runtime)
container.Name = name
if _, err := runtime.containerGraph.Set(name, container.ID); err != nil {
utils.Debugf("Setting default id - %s", err)
}
register(container)
@ -306,8 +309,8 @@ func (runtime *Runtime) UpdateCapabilities(quiet bool) {
}
}
// Create creates a new container from the given configuration.
func (runtime *Runtime) Create(config *Config) (*Container, []string, 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) {
// Lookup image
img, err := runtime.repositories.LookupImage(config.Image)
if err != nil {
@ -345,8 +348,15 @@ func (runtime *Runtime) Create(config *Config) (*Container, []string, error) {
// Generate id
id := GenerateID()
// Set the default enitity in the graph
if _, err := runtime.containerGraph.Set(fmt.Sprintf("/%s", id), id); err != nil {
if name == "" {
name = generateRandomName(runtime)
}
if name[0] != '/' {
name = "/" + name
}
// Set the enitity in the graph using the default name specified
if _, err := runtime.containerGraph.Set(name, id); err != nil {
return nil, nil, err
}
@ -378,6 +388,7 @@ func (runtime *Runtime) Create(config *Config) (*Container, []string, error) {
NetworkSettings: &NetworkSettings{},
// FIXME: do we need to store this in the container?
SysInitPath: sysInitPath,
Name: name,
}
container.root = runtime.containerRoot(container.ID)
// Step 1: create the container directory.
@ -483,16 +494,7 @@ func (runtime *Runtime) Commit(container *Container, repository, tag, comment, a
return img, nil
}
// Strip the leading slash from the name to look up if it
// is a truncated id
// Prepend the slash back after finding the name
func (runtime *Runtime) getFullName(name string) string {
if name[0] == '/' {
name = name[1:]
}
if id, err := runtime.idIndex.Get(name); err == nil {
name = id
}
if name[0] != '/' {
name = "/" + name
}
@ -500,9 +502,7 @@ func (runtime *Runtime) getFullName(name string) string {
}
func (runtime *Runtime) GetByName(name string) (*Container, error) {
name = runtime.getFullName(name)
entity := runtime.containerGraph.Get(name)
entity := runtime.containerGraph.Get(runtime.getFullName(name))
if entity == nil {
return nil, fmt.Errorf("Could not find entity for %s", name)
}
@ -514,6 +514,7 @@ func (runtime *Runtime) GetByName(name string) (*Container, error) {
}
func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
name = runtime.getFullName(name)
children := make(map[string]*Container)
err := runtime.containerGraph.Walk(name, func(p string, e *gograph.Entity) error {
@ -531,44 +532,13 @@ func (runtime *Runtime) Children(name string) (map[string]*Container, error) {
return children, nil
}
func (runtime *Runtime) RenameLink(oldName, newName string) error {
oldName = runtime.getFullName(oldName)
entity := runtime.containerGraph.Get(oldName)
if entity == nil {
return fmt.Errorf("Could not find entity for %s", oldName)
}
if newName[0] != '/' {
newName = "/" + newName
}
// This is not rename but adding a new link for the default name
// Strip the leading '/'
if entity.ID() == oldName[1:] {
_, err := runtime.containerGraph.Set(newName, entity.ID())
func (runtime *Runtime) RegisterLink(parent, child *Container, alias string) error {
fullName := path.Join(parent.Name, alias)
if !runtime.containerGraph.Exists(fullName) {
_, err := runtime.containerGraph.Set(fullName, child.ID)
return err
}
return runtime.containerGraph.Rename(oldName, newName)
}
func (runtime *Runtime) Link(parentName, childName, alias string) error {
if id, err := runtime.idIndex.Get(parentName); err == nil {
parentName = id
}
parent := runtime.containerGraph.Get(parentName)
if parent == nil {
return fmt.Errorf("Could not get container for %s", parentName)
}
if id, err := runtime.idIndex.Get(childName); err == nil {
childName = id
}
child := runtime.containerGraph.Get(childName)
if child == nil {
return fmt.Errorf("Could not get container for %s", childName)
}
_, err := runtime.containerGraph.Set(path.Join(parentName, alias), child.ID())
return err
return nil
}
// FIXME: harmonize with NewGraph()

Просмотреть файл

@ -197,6 +197,7 @@ func TestRuntimeCreate(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
},
"",
)
if err != nil {
t.Fatal(err)
@ -234,7 +235,7 @@ func TestRuntimeCreate(t *testing.T) {
}
// Make sure create with bad parameters returns an error
if _, _, err = runtime.Create(&Config{Image: GetTestImage(runtime).ID}); err == nil {
if _, _, err = runtime.Create(&Config{Image: GetTestImage(runtime).ID}, ""); err == nil {
t.Fatal("Builder.Create should throw an error when Cmd is missing")
}
@ -243,6 +244,7 @@ func TestRuntimeCreate(t *testing.T) {
Image: GetTestImage(runtime).ID,
Cmd: []string{},
},
"",
); err == nil {
t.Fatal("Builder.Create should throw an error when Cmd is empty")
}
@ -252,7 +254,7 @@ func TestRuntimeCreate(t *testing.T) {
Cmd: []string{"/bin/ls"},
PortSpecs: []string{"80"},
}
container, _, err = runtime.Create(config)
container, _, err = runtime.Create(config, "")
_, err = runtime.Commit(container, "testrepo", "testtag", "", "", config)
if err != nil {
@ -267,7 +269,7 @@ func TestDestroy(t *testing.T) {
container, _, err := runtime.Create(&Config{
Image: GetTestImage(runtime).ID,
Cmd: []string{"ls", "-al"},
})
}, "")
if err != nil {
t.Fatal(err)
}
@ -361,7 +363,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container,
Cmd: []string{"sh", "-c", cmd},
PortSpecs: []string{fmt.Sprintf("%s/%s", strPort, proto)},
ExposedPorts: ep,
})
}, "")
if err != nil {
nuke(runtime)
t.Fatal(err)
@ -573,6 +575,9 @@ func TestReloadContainerLinks(t *testing.T) {
h1 := &HostConfig{}
// Add a link to container 2
h1.Links = []string{"/" + container2.ID + ":first"}
if err := runtime1.RegisterLink(container1, container2, "first"); err != nil {
t.Fatal(err)
}
if err := container1.Start(h1); err != nil {
t.Fatal(err)
}
@ -620,7 +625,7 @@ func TestReloadContainerLinks(t *testing.T) {
t.Logf("Number of links: %d", runtime2.containerGraph.Refs("0"))
// Verify that the link is still registered in the runtime
entity := runtime2.containerGraph.Get(fmt.Sprintf("/%s", container1.ID))
entity := runtime2.containerGraph.Get(container1.Name)
if entity == nil {
t.Fatal("Entity should not be nil")
}
@ -636,13 +641,17 @@ func TestDefaultContainerName(t *testing.T) {
t.Fatal(err)
}
shortId, _, err := srv.ContainerCreate(config)
shortId, _, err := srv.ContainerCreate(config, "some_name")
if err != nil {
t.Fatal(err)
}
container := runtime.Get(shortId)
containerID := container.ID
if container.Name != "/some_name" {
t.Fatalf("Expect /some_name got %s", container.Name)
}
paths := runtime.containerGraph.RefPaths(containerID)
if paths == nil || len(paths) == 0 {
t.Fatalf("Could not find edges for %s", containerID)
@ -654,12 +663,12 @@ func TestDefaultContainerName(t *testing.T) {
if edge.EntityID != containerID {
t.Fatalf("Expected %s got %s", containerID, edge.EntityID)
}
if edge.Name != containerID {
t.Fatalf("Expected %s got %s", containerID, edge.Name)
if edge.Name != "some_name" {
t.Fatalf("Expected some_name got %s", edge.Name)
}
}
func TestDefaultContainerRename(t *testing.T) {
func TestRandomContainerName(t *testing.T) {
runtime := mkRuntime(t)
defer nuke(runtime)
srv := &Server{runtime: runtime}
@ -669,24 +678,30 @@ func TestDefaultContainerRename(t *testing.T) {
t.Fatal(err)
}
shortId, _, err := srv.ContainerCreate(config)
shortId, _, err := srv.ContainerCreate(config, "")
if err != nil {
t.Fatal(err)
}
container := runtime.Get(shortId)
containerID := container.ID
if err := runtime.RenameLink(fmt.Sprintf("/%s", containerID), "/webapp"); err != nil {
t.Fatal(err)
if container.Name == "" {
t.Fatalf("Expected not empty container name")
}
webapp, err := runtime.GetByName("/webapp")
if err != nil {
t.Fatal(err)
paths := runtime.containerGraph.RefPaths(containerID)
if paths == nil || len(paths) == 0 {
t.Fatalf("Could not find edges for %s", containerID)
}
if webapp.ID != container.ID {
t.Fatalf("Expect webapp id to match container id: %s != %s", webapp.ID, container.ID)
edge := paths[0]
if edge.ParentID != "0" {
t.Fatalf("Expected engine got %s", edge.ParentID)
}
if edge.EntityID != containerID {
t.Fatalf("Expected %s got %s", containerID, edge.EntityID)
}
if edge.Name == "" {
t.Fatalf("Expected not empty container name")
}
}
@ -700,16 +715,12 @@ func TestLinkChildContainer(t *testing.T) {
t.Fatal(err)
}
shortId, _, err := srv.ContainerCreate(config)
shortId, _, err := srv.ContainerCreate(config, "/webapp")
if err != nil {
t.Fatal(err)
}
container := runtime.Get(shortId)
if err := runtime.RenameLink(fmt.Sprintf("/%s", container.ID), "/webapp"); err != nil {
t.Fatal(err)
}
webapp, err := runtime.GetByName("/webapp")
if err != nil {
t.Fatal(err)
@ -724,17 +735,14 @@ func TestLinkChildContainer(t *testing.T) {
t.Fatal(err)
}
shortId, _, err = srv.ContainerCreate(config)
shortId, _, err = srv.ContainerCreate(config, "")
if err != nil {
t.Fatal(err)
}
childContainer := runtime.Get(shortId)
if err := runtime.RenameLink(fmt.Sprintf("/%s", childContainer.ID), "/db"); err != nil {
t.Fatal(err)
}
if err := runtime.Link("/webapp", "/db", "db"); err != nil {
if err := runtime.RegisterLink(webapp, childContainer, "db"); err != nil {
t.Fatal(err)
}
@ -758,16 +766,12 @@ func TestGetAllChildren(t *testing.T) {
t.Fatal(err)
}
shortId, _, err := srv.ContainerCreate(config)
shortId, _, err := srv.ContainerCreate(config, "/webapp")
if err != nil {
t.Fatal(err)
}
container := runtime.Get(shortId)
if err := runtime.RenameLink(fmt.Sprintf("/%s", container.ID), "/webapp"); err != nil {
t.Fatal(err)
}
webapp, err := runtime.GetByName("/webapp")
if err != nil {
t.Fatal(err)
@ -782,17 +786,14 @@ func TestGetAllChildren(t *testing.T) {
t.Fatal(err)
}
shortId, _, err = srv.ContainerCreate(config)
shortId, _, err = srv.ContainerCreate(config, "")
if err != nil {
t.Fatal(err)
}
childContainer := runtime.Get(shortId)
if err := runtime.RenameLink(fmt.Sprintf("/%s", childContainer.ID), "/db"); err != nil {
t.Fatal(err)
}
if err := runtime.Link("/webapp", "/db", "db"); err != nil {
if err := runtime.RegisterLink(webapp, childContainer, "db"); err != nil {
t.Fatal(err)
}

Просмотреть файл

@ -156,7 +156,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.
return "", err
}
c, _, err := srv.runtime.Create(config)
c, _, err := srv.runtime.Create(config, "")
if err != nil {
return "", err
}
@ -937,7 +937,7 @@ func (srv *Server) ImageImport(src, repo, tag string, in io.Reader, out io.Write
return nil
}
func (srv *Server) ContainerCreate(config *Config) (string, []string, error) {
func (srv *Server) ContainerCreate(config *Config, name string) (string, []string, error) {
if config.Memory != 0 && config.Memory < 524288 {
return "", nil, fmt.Errorf("Memory limit must be given in bytes (minimum 524288 bytes)")
}
@ -949,7 +949,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, []string, error) {
if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit {
config.MemorySwap = -1
}
container, buildWarnings, err := srv.runtime.Create(config)
container, buildWarnings, err := srv.runtime.Create(config, name)
if err != nil {
if srv.runtime.graph.IsNotExist(err) {
@ -1209,13 +1209,12 @@ func (srv *Server) ImageGetCached(imgID string, config *Config) (*Image, error)
return nil, nil
}
func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error {
func (srv *Server) RegisterLinks(name string, hostConfig *HostConfig) error {
runtime := srv.runtime
container := runtime.Get(name)
if container == nil {
return fmt.Errorf("No such container: %s", name)
}
// Register links
if hostConfig != nil && hostConfig.Links != nil {
for _, l := range hostConfig.Links {
@ -1223,16 +1222,28 @@ func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error {
if err != nil {
return err
}
childName := parts["name"]
if childName[0] != '/' {
childName = "/" + childName
child, err := srv.runtime.GetByName(parts["name"])
if err != nil {
return err
}
if err := runtime.Link(fmt.Sprintf("/%s", container.ID), childName, parts["alias"]); err != nil {
if child == nil {
return fmt.Errorf("Could not get container for %s", parts["name"])
}
if err := runtime.RegisterLink(container, child, parts["alias"]); err != nil {
return err
}
}
}
return nil
}
func (srv *Server) ContainerStart(name string, hostConfig *HostConfig) error {
runtime := srv.runtime
container := runtime.Get(name)
if container == nil {
return fmt.Errorf("No such container: %s", name)
}
if err := container.Start(hostConfig); err != nil {
return fmt.Errorf("Error starting container %s: %s", name, err)

Просмотреть файл

@ -89,7 +89,7 @@ func TestCreateRm(t *testing.T) {
t.Fatal(err)
}
id, _, err := srv.ContainerCreate(config)
id, _, err := srv.ContainerCreate(config, "")
if err != nil {
t.Fatal(err)
}
@ -119,7 +119,7 @@ func TestCreateRmVolumes(t *testing.T) {
t.Fatal(err)
}
id, _, err := srv.ContainerCreate(config)
id, _, err := srv.ContainerCreate(config, "")
if err != nil {
t.Fatal(err)
}
@ -158,7 +158,7 @@ func TestCommit(t *testing.T) {
t.Fatal(err)
}
id, _, err := srv.ContainerCreate(config)
id, _, err := srv.ContainerCreate(config, "")
if err != nil {
t.Fatal(err)
}
@ -179,7 +179,7 @@ func TestCreateStartRestartStopStartKillRm(t *testing.T) {
t.Fatal(err)
}
id, _, err := srv.ContainerCreate(config)
id, _, err := srv.ContainerCreate(config, "")
if err != nil {
t.Fatal(err)
}
@ -231,6 +231,7 @@ func TestRunWithTooLowMemoryLimit(t *testing.T) {
CpuShares: 1000,
Cmd: []string{"/bin/cat"},
},
"",
); err == nil {
t.Errorf("Memory limit is smaller than the allowed limit. Container creation should've failed!")
}
@ -397,7 +398,7 @@ func TestRmi(t *testing.T) {
t.Fatal(err)
}
containerID, _, err := srv.ContainerCreate(config)
containerID, _, err := srv.ContainerCreate(config, "")
if err != nil {
t.Fatal(err)
}
@ -418,7 +419,7 @@ func TestRmi(t *testing.T) {
t.Fatal(err)
}
containerID, _, err = srv.ContainerCreate(config)
containerID, _, err = srv.ContainerCreate(config, "")
if err != nil {
t.Fatal(err)
}

Просмотреть файл

@ -81,25 +81,3 @@ func sortContainers(containers []*Container, predicate func(i, j *Container) boo
s := &containerSorter{containers, predicate}
sort.Sort(s)
}
type apiLinkSorter struct {
links []APILink
by func(i, j APILink) bool
}
func (s *apiLinkSorter) Len() int {
return len(s.links)
}
func (s *apiLinkSorter) Swap(i, j int) {
s.links[i], s.links[j] = s.links[j], s.links[i]
}
func (s *apiLinkSorter) Less(i, j int) bool {
return s.by(s.links[i], s.links[j])
}
func sortLinks(links []APILink, predicate func(i, j APILink) bool) {
s := &apiLinkSorter{links, predicate}
sort.Sort(s)
}

Просмотреть файл

@ -2,6 +2,7 @@ package docker
import (
"fmt"
"github.com/dotcloud/docker/namesgenerator"
"github.com/dotcloud/docker/utils"
"strconv"
"strings"
@ -289,3 +290,20 @@ func migratePortMappings(config *Config) error {
func parseLink(rawLink string) (map[string]string, error) {
return utils.PartParser("name:alias", rawLink)
}
type checker struct {
runtime *Runtime
}
func (c *checker) Exists(name string) bool {
return c.runtime.containerGraph.Exists("/" + name)
}
// Generate a random and unique name
func generateRandomName(runtime *Runtime) string {
n, err := namesgenerator.GenerateRandomName(&checker{runtime})
if err != nil {
panic(err)
}
return n
}

Просмотреть файл

@ -129,7 +129,7 @@ func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConf
if config.Image == "_" {
config.Image = GetTestImage(r).ID
}
c, _, err := r.Create(config)
c, _, err := r.Create(config, "")
if err != nil {
return nil, nil, err
}