diff --git a/cmd/podman/common/completion.go b/cmd/podman/common/completion.go index 75825f1ea1..5f6ad7e057 100644 --- a/cmd/podman/common/completion.go +++ b/cmd/podman/common/completion.go @@ -964,6 +964,13 @@ func AutocompleteUserNamespace(cmd *cobra.Command, args []string, toComplete str return results, directive } +// AutocompleteBaseHostsFile - Autocomplete base hosts file options. +// -> "image", "none", paths +func AutocompleteBaseHostsFile(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { + BaseHostsFileModes := []string{"image", "none"} + return BaseHostsFileModes, cobra.ShellCompDirectiveNoSpace +} + // AutocompleteCgroupMode - Autocomplete cgroup mode options. // -> "enabled", "disabled", "no-conmon", "split" func AutocompleteCgroupMode(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { diff --git a/cmd/podman/common/netflags.go b/cmd/podman/common/netflags.go index d33642a7d9..8083259e98 100644 --- a/cmd/podman/common/netflags.go +++ b/cmd/podman/common/netflags.go @@ -26,6 +26,13 @@ func DefineNetFlags(cmd *cobra.Command) { ) _ = cmd.RegisterFlagCompletionFunc(addHostFlagName, completion.AutocompleteNone) + baseHostsFileFlagName := "base-hosts-file" + netFlags.String( + baseHostsFileFlagName, "", + `Path to a hosts file to copy the entries into the container, or one of the special values. ("image"|"none")`, + ) + _ = cmd.RegisterFlagCompletionFunc(baseHostsFileFlagName, AutocompleteBaseHostsFile) + dnsFlagName := "dns" netFlags.StringSlice( dnsFlagName, podmanConfig.ContainersConf.DNSServers(), diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go index 6d97ea7d65..095474e70b 100644 --- a/cmd/podman/containers/create.go +++ b/cmd/podman/containers/create.go @@ -308,6 +308,9 @@ func CreateInit(c *cobra.Command, vals entities.ContainerCreateOptions, isInfra if c.Flag("shm-size-systemd").Changed { vals.ShmSizeSystemd = c.Flag("shm-size-systemd").Value.String() } + if c.Flag("base-hosts-file").Changed { + vals.BaseHostsFile = c.Flag("base-hosts-file").Value.String() + } if (c.Flag("dns").Changed || c.Flag("dns-option").Changed || c.Flag("dns-search").Changed) && vals.Net != nil && (vals.Net.Network.NSMode == specgen.NoNetwork || vals.Net.Network.IsContainer()) { return vals, fmt.Errorf("conflicting options: dns and the network mode: " + string(vals.Net.Network.NSMode)) } diff --git a/docs/source/markdown/options/base-hosts-file.md b/docs/source/markdown/options/base-hosts-file.md new file mode 100644 index 0000000000..ba0d622f3a --- /dev/null +++ b/docs/source/markdown/options/base-hosts-file.md @@ -0,0 +1,9 @@ +####> This option file is used in: +####> podman create, pod create, run +####> If file is edited, make sure the changes +####> are applicable to all of those. +#### **--base-hosts-file**=*path* | *image* | *none* + +BaseHostsFile is the path to a hosts file, the entries from this file are added to the <> _/etc/hosts_ file. +As special value "image" is allowed which uses the _/etc/hosts_ file from within the image and "none" which uses no base file at all. +If it is empty we should default to the base_hosts_file configuration in _containers.conf_. diff --git a/docs/source/markdown/podman-create.1.md.in b/docs/source/markdown/podman-create.1.md.in index 0c0b325b85..905c6b8817 100644 --- a/docs/source/markdown/podman-create.1.md.in +++ b/docs/source/markdown/podman-create.1.md.in @@ -75,6 +75,8 @@ and specified with a _tag_. @@option authfile +@@option base-hosts-file + @@option blkio-weight @@option blkio-weight-device diff --git a/docs/source/markdown/podman-pod-create.1.md.in b/docs/source/markdown/podman-pod-create.1.md.in index 46f52d3966..d8bf59f2fe 100644 --- a/docs/source/markdown/podman-pod-create.1.md.in +++ b/docs/source/markdown/podman-pod-create.1.md.in @@ -33,6 +33,8 @@ For example, if a pod was created via **podman pod create --cpus=5**, specifying The /etc/hosts file is shared between all containers in the pod. +@@option base-hosts-file + @@option blkio-weight @@option blkio-weight-device diff --git a/docs/source/markdown/podman-run.1.md.in b/docs/source/markdown/podman-run.1.md.in index 7132673861..aa062752e8 100644 --- a/docs/source/markdown/podman-run.1.md.in +++ b/docs/source/markdown/podman-run.1.md.in @@ -94,6 +94,8 @@ and specified with a _tag_. @@option authfile +@@option base-hosts-file + @@option blkio-weight @@option blkio-weight-device diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index d57dedee40..5cd3ad0652 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -182,6 +182,7 @@ type ContainerCreateOptions struct { Annotation []string Attach []string Authfile string + BaseHostsFile string BlkIOWeight string BlkIOWeightDevice []string CapAdd []string diff --git a/pkg/specgenutil/specgen.go b/pkg/specgenutil/specgen.go index 306a19b394..744e0cacc1 100644 --- a/pkg/specgenutil/specgen.go +++ b/pkg/specgenutil/specgen.go @@ -633,6 +633,11 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions if len(s.Hostname) == 0 || len(c.Hostname) != 0 { s.Hostname = c.Hostname } + + if len(s.BaseHostsFile) == 0 || len(c.BaseHostsFile) != 0 { + s.BaseHostsFile = c.BaseHostsFile + } + sysctl := map[string]string{} if ctl := c.Sysctl; len(ctl) > 0 { sysctl, err = util.ValidateSysctls(ctl) diff --git a/test/e2e/containers_conf_test.go b/test/e2e/containers_conf_test.go index 8360c79117..0cfd90c765 100644 --- a/test/e2e/containers_conf_test.go +++ b/test/e2e/containers_conf_test.go @@ -461,6 +461,60 @@ var _ = Describe("Verify podman containers.conf usage", func() { Expect(session.OutputToString()).To(ContainSubstring("test")) }) + Describe("base_hosts_file in containers.conf", func() { + var baseHostsFile string + var session *PodmanSessionIntegration + + JustBeforeEach(func() { + confFile := filepath.Join(podmanTest.TempDir, "containers.conf") + err = os.WriteFile(confFile, []byte(fmt.Sprintf("[containers]\nbase_hosts_file=\"%s\"\nno_hosts=false\n", baseHostsFile)), 0755) + Expect(err).ToNot(HaveOccurred()) + os.Setenv("CONTAINERS_CONF_OVERRIDE", confFile) + if IsRemote() { + podmanTest.RestartRemoteService() + } + + session = podmanTest.Podman([]string{"run", "--rm", ALPINE, "cat", "/etc/hosts"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + }) + + Describe("base_hosts_file=/path/to/hosts", func() { + BeforeEach(func() { + hostsFile := filepath.Join(podmanTest.TempDir, "hosts") + err := os.WriteFile(hostsFile, []byte("12.34.56.78 test.example.com"), 0755) + Expect(err).ToNot(HaveOccurred()) + + baseHostsFile = hostsFile + }) + + It("should use the hosts file from the container image", func() { + Expect(session.OutputToString()).To(ContainSubstring("12.34.56.78 test.example.com")) + }) + }) + + Describe("base_hosts_file=image", func() { + BeforeEach(func() { + baseHostsFile = "image" + }) + + It("should use the hosts file from the container image", func() { + Expect(session.OutputToString()).To(ContainSubstring("localhost localhost.localdomain")) + }) + }) + + Describe("base_hosts_file=none", func() { + BeforeEach(func() { + baseHostsFile = "none" + }) + + It("should use the hosts file from the container image", func() { + Expect(session.OutputToString()).ToNot(ContainSubstring("localhost.localdomain")) + Expect(session.OutputToString()).To(ContainSubstring("localhost")) + }) + }) + }) + It("seccomp profile path", func() { configPath := filepath.Join(podmanTest.TempDir, "containers.conf") os.Setenv("CONTAINERS_CONF", configPath) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 3be0213b51..be8427f45c 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -148,6 +148,31 @@ var _ = Describe("Podman pod create", func() { Expect(podCreate).Should(Exit(125)) }) + It("podman create pod with --base-hosts-file", func() { + confFile := filepath.Join(podmanTest.TempDir, "containers.conf") + err := os.WriteFile(confFile, []byte("[containers]\nbase_hosts_file=\"none\"\n"), 0755) + Expect(err).ToNot(HaveOccurred()) + os.Setenv("CONTAINERS_CONF", confFile) + if IsRemote() { + podmanTest.RestartRemoteService() + } + + hostsFile := filepath.Join(podmanTest.TempDir, "hosts") + err = os.WriteFile(hostsFile, []byte("12.34.56.78 test.example.com"), 0755) + Expect(err).ToNot(HaveOccurred()) + + // Create flag should override containers.conf + name := "test" + podCreate := podmanTest.Podman([]string{"pod", "create", "--base-hosts-file=" + hostsFile, "--name", name}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate).Should(ExitCleanly()) + + session := podmanTest.Podman([]string{"run", "--pod", name, "--rm", ALPINE, "cat", "/etc/hosts"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(ContainSubstring("12.34.56.78 test.example.com")) + }) + It("podman create pod with DNS server set", func() { name := "test" server := "12.34.56.78" diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go index b42b2b661c..5b8a16d200 100644 --- a/test/e2e/run_test.go +++ b/test/e2e/run_test.go @@ -1483,6 +1483,22 @@ VOLUME %s`, ALPINE, volPath, volPath) Expect(session).To(ExitWithError()) }) + It("podman run with --base-hosts-file", func() { + confFile := filepath.Join(podmanTest.TempDir, "containers.conf") + err = os.WriteFile(confFile, []byte("[containers]\nbase_hosts_file=\"none\"\n"), 0755) + Expect(err).ToNot(HaveOccurred()) + os.Setenv("CONTAINERS_CONF", confFile) + if IsRemote() { + podmanTest.RestartRemoteService() + } + + // Run flag should override containers.conf + session := podmanTest.Podman([]string{"run", "--rm", "--base-hosts-file=image", ALPINE, "cat", "/etc/hosts"}) + session.WaitWithDefaultTimeout() + Expect(session).Should(ExitCleanly()) + Expect(session.OutputToString()).To(ContainSubstring("localhost localhost.localdomain")) + }) + It("podman run with restart-policy always restarts containers", func() { testDir := filepath.Join(podmanTest.RunRoot, "restart-test") err := os.MkdirAll(testDir, 0755)