Skip to content

Commit

Permalink
Add --hosts-file flag
Browse files Browse the repository at this point in the history
* Add --hosts-file flag to container create, container run, and pod create
* Add tests for base_hosts_file in containers.conf

Signed-off-by: Gavin Lam <[email protected]>
  • Loading branch information
gavinkflam committed Nov 12, 2024
1 parent 483a132 commit 451a94e
Show file tree
Hide file tree
Showing 12 changed files with 139 additions and 0 deletions.
7 changes: 7 additions & 0 deletions cmd/podman/common/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,13 @@ func AutocompleteNetworks(cmd *cobra.Command, args []string, toComplete string)
return getNetworks(cmd, toComplete, completeDefault)
}

// AutocompleteHostsFile - Autocomplete hosts file options.
// -> "image", "none", paths
func AutocompleteHostsFile(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
hostsFileModes := []string{"image", "none"}
return hostsFileModes, cobra.ShellCompDirectiveDefault
}

// AutocompleteDefaultOneArg - Autocomplete path only for the first argument.
func AutocompleteDefaultOneArg(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 {
Expand Down
7 changes: 7 additions & 0 deletions cmd/podman/common/netflags.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ func DefineNetFlags(cmd *cobra.Command) {
)
_ = cmd.RegisterFlagCompletionFunc(addHostFlagName, completion.AutocompleteNone)

hostsFileFlagName := "hosts-file"
netFlags.String(
hostsFileFlagName, "",
`Path to a hosts file to copy the entries into the container, or one of the special values. ("image"|"none")`,
)
_ = cmd.RegisterFlagCompletionFunc(hostsFileFlagName, AutocompleteHostsFile)

dnsFlagName := "dns"
netFlags.StringSlice(
dnsFlagName, podmanConfig.ContainersConf.DNSServers(),
Expand Down
3 changes: 3 additions & 0 deletions cmd/podman/containers/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,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("hosts-file").Changed {
vals.HostsFile = c.Flag("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, errors.New("conflicting options: dns and the network mode: " + string(vals.Net.Network.NSMode))
}
Expand Down
12 changes: 12 additions & 0 deletions docs/source/markdown/options/hosts-file.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
####> 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.
#### **--hosts-file**=*path* | *none* | *image*

Base file to create the `/etc/hosts` file inside the container. This must either
be an absolute path to a file on the host system, or one of the following
special flags:
"" Follow the `base_hosts_file` configuration in _containers.conf_ (the default)
`none` Do not use a base file (i.e. start with an empty file)
`image` Use the container image's `/etc/hosts` file as base file
2 changes: 2 additions & 0 deletions docs/source/markdown/podman-create.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ Print usage statement

@@option hostname.container

@@option hosts-file

@@option hostuser

@@option http-proxy
Expand Down
2 changes: 2 additions & 0 deletions docs/source/markdown/podman-pod-create.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ Print usage statement.

@@option hostname.pod

@@option hosts-file

#### **--infra**

Create an infra container and associate it with the pod. An infra container is a lightweight container used to coordinate the shared kernel namespace of a pod. Default: true.
Expand Down
2 changes: 2 additions & 0 deletions docs/source/markdown/podman-run.1.md.in
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ Print usage statement

@@option hostname.container

@@option hosts-file

@@option hostuser

@@option http-proxy
Expand Down
1 change: 1 addition & 0 deletions pkg/domain/entities/pods.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ type ContainerCreateOptions struct {
HealthTimeout string
HealthOnFailure string
Hostname string `json:"hostname,omitempty"`
HostsFile string
HTTPProxy bool
HostUsers []string
ImageVolume string
Expand Down
5 changes: 5 additions & 0 deletions pkg/specgenutil/specgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,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.HostsFile) != 0 {
s.BaseHostsFile = c.HostsFile
}

sysctl := map[string]string{}
if ctl := c.Sysctl; len(ctl) > 0 {
sysctl, err = util.ValidateSysctls(ctl)
Expand Down
54 changes: 54 additions & 0 deletions test/e2e/containers_conf_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,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 file path", 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 not use any hosts files", 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)
Expand Down
28 changes: 28 additions & 0 deletions test/e2e/pod_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,34 @@ var _ = Describe("Podman pod create", func() {
Expect(podCreate).Should(ExitWithError(125, "NoInfra and HostAdd are mutually exclusive pod options: invalid pod spec"))
})

It("podman create pod with --hosts-file", func() {
// This test only work locally
if IsRemote() {
GinkgoWriter.Println("Bypassing subsequent tests due to remote environment")
return
}

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)

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", "--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"
Expand Down
16 changes: 16 additions & 0 deletions test/e2e/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,22 @@ VOLUME %s`, ALPINE, volPath, volPath)
Expect(session).To(ExitWithError(125, "--no-hosts and --add-host cannot be set together"))
})

It("podman run with --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_OVERRIDE", confFile)
if IsRemote() {
podmanTest.RestartRemoteService()
}

// Hosts file flag should override base_hosts_file in containers.conf
session := podmanTest.Podman([]string{"run", "--rm", "--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)
Expand Down

0 comments on commit 451a94e

Please sign in to comment.