mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Update /etc/hosts when linked container is restarted
Docker-DCO-1.1-Signed-off-by: Victor Vieux <vieux@docker.com> (github: vieux)
This commit is contained in:
parent
345e3811fe
commit
450740c891
8 changed files with 149 additions and 5 deletions
|
@ -297,6 +297,9 @@ func (container *Container) Start() (err error) {
|
||||||
if err := container.initializeNetworking(); err != nil {
|
if err := container.initializeNetworking(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := container.updateParentsHosts(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
container.verifyDaemonSettings()
|
container.verifyDaemonSettings()
|
||||||
if err := prepareVolumesForContainer(container); err != nil {
|
if err := prepareVolumesForContainer(container); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -390,10 +393,7 @@ func (container *Container) buildHostnameFile() error {
|
||||||
return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
|
return ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (container *Container) buildHostnameAndHostsFiles(IP string) error {
|
func (container *Container) buildHostsFiles(IP string) error {
|
||||||
if err := container.buildHostnameFile(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
hostsPath, err := container.getRootResourcePath("hosts")
|
hostsPath, err := container.getRootResourcePath("hosts")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -416,6 +416,14 @@ func (container *Container) buildHostnameAndHostsFiles(IP string) error {
|
||||||
return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, &extraContent)
|
return etchosts.Build(container.HostsPath, IP, container.Config.Hostname, container.Config.Domainname, &extraContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (container *Container) buildHostnameAndHostsFiles(IP string) error {
|
||||||
|
if err := container.buildHostnameFile(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return container.buildHostsFiles(IP)
|
||||||
|
}
|
||||||
|
|
||||||
func (container *Container) allocateNetwork() error {
|
func (container *Container) allocateNetwork() error {
|
||||||
mode := container.hostConfig.NetworkMode
|
mode := container.hostConfig.NetworkMode
|
||||||
if container.Config.NetworkDisabled || mode.IsContainer() || mode.IsHost() {
|
if container.Config.NetworkDisabled || mode.IsContainer() || mode.IsHost() {
|
||||||
|
@ -878,6 +886,26 @@ func (container *Container) setupContainerDns() error {
|
||||||
return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644)
|
return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (container *Container) updateParentsHosts() error {
|
||||||
|
parents, err := container.daemon.Parents(container.Name)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, cid := range parents {
|
||||||
|
if cid == "0" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
c := container.daemon.Get(cid)
|
||||||
|
if c != nil && !container.daemon.config.DisableNetwork && !container.hostConfig.NetworkMode.IsContainer() && !container.hostConfig.NetworkMode.IsHost() {
|
||||||
|
if err := etchosts.Update(c.HostsPath, container.NetworkSettings.IPAddress, container.Name[1:]); err != nil {
|
||||||
|
return fmt.Errorf("Failed to update /etc/hosts in parent container: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (container *Container) initializeNetworking() error {
|
func (container *Container) initializeNetworking() error {
|
||||||
var err error
|
var err error
|
||||||
if container.hostConfig.NetworkMode.IsHost() {
|
if container.hostConfig.NetworkMode.IsHost() {
|
||||||
|
|
|
@ -621,6 +621,15 @@ func (daemon *Daemon) Children(name string) (map[string]*Container, error) {
|
||||||
return children, nil
|
return children, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (daemon *Daemon) Parents(name string) ([]string, error) {
|
||||||
|
name, err := GetFullContainerName(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return daemon.containerGraph.Parents(name)
|
||||||
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) RegisterLink(parent, child *Container, alias string) error {
|
func (daemon *Daemon) RegisterLink(parent, child *Container, alias string) error {
|
||||||
fullName := path.Join(parent.Name, alias)
|
fullName := path.Join(parent.Name, alias)
|
||||||
if !daemon.containerGraph.Exists(fullName) {
|
if !daemon.containerGraph.Exists(fullName) {
|
||||||
|
|
|
@ -150,7 +150,10 @@ Four different options affect container domain name services.
|
||||||
`CONTAINER_NAME`. This lets processes inside the new container
|
`CONTAINER_NAME`. This lets processes inside the new container
|
||||||
connect to the hostname `ALIAS` without having to know its IP. The
|
connect to the hostname `ALIAS` without having to know its IP. The
|
||||||
`--link=` option is discussed in more detail below, in the section
|
`--link=` option is discussed in more detail below, in the section
|
||||||
[Communication between containers](#between-containers).
|
[Communication between containers](#between-containers). Docker updates
|
||||||
|
the ALIAS entry in the /etc/hosts file of the recipient containers
|
||||||
|
in order to keep the link since Docker may assign a different IP
|
||||||
|
address to the linked containers on restart.
|
||||||
|
|
||||||
* `--dns=IP_ADDRESS...` — sets the IP addresses added as `server`
|
* `--dns=IP_ADDRESS...` — sets the IP addresses added as `server`
|
||||||
lines to the container's `/etc/resolv.conf` file. Processes in the
|
lines to the container's `/etc/resolv.conf` file. Processes in the
|
||||||
|
|
|
@ -432,6 +432,9 @@ mechanism to communicate with a linked container by its alias:
|
||||||
$ docker run -d --name servicename busybox sleep 30
|
$ docker run -d --name servicename busybox sleep 30
|
||||||
$ docker run -i -t --link servicename:servicealias busybox ping -c 1 servicealias
|
$ docker run -i -t --link servicename:servicealias busybox ping -c 1 servicealias
|
||||||
|
|
||||||
|
If you restart the source container (`servicename` in this case), the recipient
|
||||||
|
container's `/etc/hosts` entry will be automatically updated.
|
||||||
|
|
||||||
## VOLUME (Shared Filesystems)
|
## VOLUME (Shared Filesystems)
|
||||||
|
|
||||||
-v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro].
|
-v=[]: Create a bind mount with: [host-dir]:[container-dir]:[rw|ro].
|
||||||
|
|
|
@ -241,6 +241,16 @@ to make use of your `db` container.
|
||||||
> example, you could have multiple (differently named) web containers attached to your
|
> example, you could have multiple (differently named) web containers attached to your
|
||||||
>`db` container.
|
>`db` container.
|
||||||
|
|
||||||
|
If you restart the source container, the linked containers `/etc/hosts` files
|
||||||
|
will be automatically updated with the source container's new IP address,
|
||||||
|
allowing linked communication to continue.
|
||||||
|
|
||||||
|
$ sudo docker restart db
|
||||||
|
root@aed84ee21bde:/opt/webapp# cat /etc/hosts
|
||||||
|
172.17.0.7 aed84ee21bde
|
||||||
|
. . .
|
||||||
|
172.17.0.9 db
|
||||||
|
|
||||||
# Next step
|
# Next step
|
||||||
|
|
||||||
Now that you know how to link Docker containers together, the next step is
|
Now that you know how to link Docker containers together, the next step is
|
||||||
|
|
|
@ -1706,3 +1706,50 @@ func TestBindMounts(t *testing.T) {
|
||||||
t.Fatalf("Output should be %q, actual out: %q", expected, content)
|
t.Fatalf("Output should be %q, actual out: %q", expected, content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHostsLinkedContainerUpdate(t *testing.T) {
|
||||||
|
tmpdir, err := ioutil.TempDir("", "docker-integration")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tmpdir)
|
||||||
|
|
||||||
|
out, _, err := runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--name", "c1", "busybox", "sleep", "5"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO fix docker cp and /etc/hosts
|
||||||
|
out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "run", "-d", "--link", "c1:c1", "--name", "c2", "busybox", "sh", "-c", "while true;do cp /etc/hosts /hosts; done"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "cp", "c2:/hosts", tmpdir+"/1"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "restart", "-t", "0", "c1"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, _, err = runCommandWithOutput(exec.Command(dockerBinary, "cp", "c2:/hosts", tmpdir+"/2"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, _, _, err = runCommandWithStdoutStderr(exec.Command("diff", tmpdir+"/1", tmpdir+"/2"))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expecting error, got none")
|
||||||
|
}
|
||||||
|
out = stripTrailingCharacters(out)
|
||||||
|
if out == "" {
|
||||||
|
t.Fatalf("expected /etc/hosts to be updated, but wasn't")
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteAllContainers()
|
||||||
|
|
||||||
|
logDone("run - /etc/hosts updated in parent when restart")
|
||||||
|
}
|
||||||
|
|
|
@ -281,6 +281,18 @@ func (db *Database) Children(name string, depth int) ([]WalkMeta, error) {
|
||||||
return db.children(e, name, depth, nil)
|
return db.children(e, name, depth, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the parents of a specified entity
|
||||||
|
func (db *Database) Parents(name string) ([]string, error) {
|
||||||
|
db.mux.RLock()
|
||||||
|
defer db.mux.RUnlock()
|
||||||
|
|
||||||
|
e, err := db.get(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return db.parents(e)
|
||||||
|
}
|
||||||
|
|
||||||
// Return the refrence count for a specified id
|
// Return the refrence count for a specified id
|
||||||
func (db *Database) Refs(id string) int {
|
func (db *Database) Refs(id string) int {
|
||||||
db.mux.RLock()
|
db.mux.RLock()
|
||||||
|
@ -466,6 +478,28 @@ func (db *Database) children(e *Entity, name string, depth int, entities []WalkM
|
||||||
return entities, nil
|
return entities, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Database) parents(e *Entity) (parents []string, err error) {
|
||||||
|
if e == nil {
|
||||||
|
return parents, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := db.conn.Query("SELECT parent_id FROM edge where entity_id = ?;", e.id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var parentId string
|
||||||
|
if err := rows.Scan(&parentId); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
parents = append(parents, parentId)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parents, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Return the entity based on the parent path and name
|
// Return the entity based on the parent path and name
|
||||||
func (db *Database) child(parent *Entity, name string) *Entity {
|
func (db *Database) child(parent *Entity, name string) *Entity {
|
||||||
var id string
|
var id string
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
var defaultContent = map[string]string{
|
var defaultContent = map[string]string{
|
||||||
|
@ -41,3 +42,12 @@ func Build(path, IP, hostname, domainname string, extraContent *map[string]strin
|
||||||
|
|
||||||
return ioutil.WriteFile(path, content.Bytes(), 0644)
|
return ioutil.WriteFile(path, content.Bytes(), 0644)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Update(path, IP, hostname string) error {
|
||||||
|
old, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var re = regexp.MustCompile(fmt.Sprintf("(\\S*)(\\t%s)", regexp.QuoteMeta(hostname)))
|
||||||
|
return ioutil.WriteFile(path, re.ReplaceAll(old, []byte(IP+"$2")), 0644)
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue