diff --git a/libpod/container.go b/libpod/container.go index b5346e5810..18d867f415 100644 --- a/libpod/container.go +++ b/libpod/container.go @@ -1003,7 +1003,7 @@ func (c *Container) IsReadOnly() bool { // NetworkDisabled returns whether the container is running with a disabled network func (c *Container) NetworkDisabled() (bool, error) { if c.config.NetNsCtr != "" { - container, err := c.runtime.LookupContainer(c.config.NetNsCtr) + container, err := c.runtime.state.Container(c.config.NetNsCtr) if err != nil { return false, err } diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 4f2955110d..93d20491eb 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -26,6 +26,7 @@ import ( "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/secrets" "github.com/containers/storage/pkg/idtools" + "github.com/mrunalp/fileutils" "github.com/opencontainers/runc/libcontainer/user" spec "github.com/opencontainers/runtime-spec/specs-go" "github.com/opencontainers/runtime-tools/generate" @@ -645,28 +646,68 @@ func (c *Container) makeBindMounts() error { } if !netDisabled { - // Make /etc/resolv.conf - if _, ok := c.state.BindMounts["/etc/resolv.conf"]; ok { - // If it already exists, delete so we can recreate + // If /etc/resolv.conf and /etc/hosts exist, delete them so we + // will recreate + if path, ok := c.state.BindMounts["/etc/resolv.conf"]; ok { + if err := os.Remove(path); err != nil && !os.IsNotExist(err) { + return errors.Wrapf(err, "error removing container %s resolv.conf", c.ID()) + } delete(c.state.BindMounts, "/etc/resolv.conf") } - newResolv, err := c.generateResolvConf() - if err != nil { - return errors.Wrapf(err, "error creating resolv.conf for container %s", c.ID()) - } - c.state.BindMounts["/etc/resolv.conf"] = newResolv - - // Make /etc/hosts - if _, ok := c.state.BindMounts["/etc/hosts"]; ok { - // If it already exists, delete so we can recreate + if path, ok := c.state.BindMounts["/etc/hosts"]; ok { + if err := os.Remove(path); err != nil && !os.IsNotExist(err) { + return errors.Wrapf(err, "error removing container %s hosts", c.ID()) + } delete(c.state.BindMounts, "/etc/hosts") } - newHosts, err := c.generateHosts() - if err != nil { - return errors.Wrapf(err, "error creating hosts file for container %s", c.ID()) - } - c.state.BindMounts["/etc/hosts"] = newHosts + if c.config.NetNsCtr != "" { + // We share a net namespace + // We want /etc/resolv.conf and /etc/hosts from the + // other container + depCtr, err := c.runtime.state.Container(c.config.NetNsCtr) + if err != nil { + return errors.Wrapf(err, "error fetching dependency %s of container %s", c.config.NetNsCtr, c.ID()) + } + + // We need that container's bind mounts + bindMounts, err := depCtr.BindMounts() + if err != nil { + return errors.Wrapf(err, "error fetching bind mounts from dependency %s of container %s", depCtr.ID(), c.ID()) + } + + // The other container may not have a resolv.conf or /etc/hosts + // If it doesn't, don't copy them + resolvPath, exists := bindMounts["/etc/resolv.conf"] + if exists { + resolvDest := filepath.Join(c.state.RunDir, "resolv.conf") + if err := fileutils.CopyFile(resolvPath, resolvDest); err != nil { + return errors.Wrapf(err, "error copying resolv.conf from dependency container %s of container %s", depCtr.ID(), c.ID()) + } + c.state.BindMounts["/etc/resolv.conf"] = resolvDest + } + + hostsPath, exists := bindMounts["/etc/hosts"] + if exists { + hostsDest := filepath.Join(c.state.RunDir, "hosts") + if err := fileutils.CopyFile(hostsPath, hostsDest); err != nil { + return errors.Wrapf(err, "error copying hosts file from dependency container %s of container %s", depCtr.ID(), c.ID()) + } + c.state.BindMounts["/etc/hosts"] = hostsDest + } + } else { + newResolv, err := c.generateResolvConf() + if err != nil { + return errors.Wrapf(err, "error creating resolv.conf for container %s", c.ID()) + } + c.state.BindMounts["/etc/resolv.conf"] = newResolv + + newHosts, err := c.generateHosts() + if err != nil { + return errors.Wrapf(err, "error creating hosts file for container %s", c.ID()) + } + c.state.BindMounts["/etc/hosts"] = newHosts + } } // SHM is always added when we mount the container diff --git a/test/e2e/run_networking_test.go b/test/e2e/run_networking_test.go index 1b05ac0319..68b1f06dec 100644 --- a/test/e2e/run_networking_test.go +++ b/test/e2e/run_networking_test.go @@ -9,7 +9,7 @@ import ( . "github.com/onsi/gomega" ) -var _ = Describe("Podman rmi", func() { +var _ = Describe("Podman run networking", func() { var ( tempdir string err error @@ -145,4 +145,35 @@ var _ = Describe("Podman rmi", func() { match, _ := session.GrepString("foobar") Expect(match).Should(BeTrue()) }) + + It("podman run --net container: copies hosts and resolv", func() { + ctrName := "ctr1" + ctr1 := podmanTest.RunTopContainer(ctrName) + ctr1.WaitWithDefaultTimeout() + Expect(ctr1.ExitCode()).To(Equal(0)) + + // Exec in and modify /etc/resolv.conf and /etc/hosts + exec1 := podmanTest.Podman([]string{"exec", ctrName, "sh", "-c", "echo nameserver 192.0.2.1 > /etc/resolv.conf"}) + exec1.WaitWithDefaultTimeout() + Expect(exec1.ExitCode()).To(Equal(0)) + + exec2 := podmanTest.Podman([]string{"exec", ctrName, "sh", "-c", "echo 192.0.2.2 test1 > /etc/hosts"}) + exec2.WaitWithDefaultTimeout() + Expect(exec2.ExitCode()).To(Equal(0)) + + ctrName2 := "ctr2" + ctr2 := podmanTest.Podman([]string{"run", "-d", "--net=container:" + ctrName, "--name", ctrName2, ALPINE, "top"}) + ctr2.WaitWithDefaultTimeout() + Expect(ctr2.ExitCode()).To(Equal(0)) + + exec3 := podmanTest.Podman([]string{"exec", "-i", ctrName2, "cat", "/etc/resolv.conf"}) + exec3.WaitWithDefaultTimeout() + Expect(exec3.ExitCode()).To(Equal(0)) + Expect(exec3.OutputToString()).To(ContainSubstring("nameserver 192.0.2.1")) + + exec4 := podmanTest.Podman([]string{"exec", "-i", ctrName2, "cat", "/etc/hosts"}) + exec4.WaitWithDefaultTimeout() + Expect(exec4.ExitCode()).To(Equal(0)) + Expect(exec4.OutputToString()).To(ContainSubstring("192.0.2.2 test1")) + }) })