Add --no-hostname option

Fixes: https://github.com/containers/podman/issues/25002

Also add the ability to inspect containers for
UseImageHosts and UseImageHostname.

Finally fixed some bugs in handling of --no-hosts for Pods,
which I descovered.

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2025-01-13 12:05:25 -05:00
parent 04e6488315
commit 6565bde6e8
35 changed files with 160 additions and 21 deletions

View File

@ -95,6 +95,10 @@ func DefineNetFlags(cmd *cobra.Command) {
)
_ = cmd.RegisterFlagCompletionFunc(publishFlagName, completion.AutocompleteNone)
netFlags.Bool(
"no-hostname", false, "Do not create /etc/hostname within the container, instead use the version from the image",
)
netFlags.Bool(
"no-hosts", podmanConfig.ContainersConfDefaultsRO.Containers.NoHosts,
"Do not create /etc/hosts within the container, instead use the version from the image",
@ -192,6 +196,11 @@ func NetFlagsToNetOptions(opts *entities.NetOptions, flags pflag.FlagSet) (*enti
}
}
opts.NoHostname, err = flags.GetBool("no-hostname")
if err != nil {
return nil, err
}
opts.NoHosts, err = flags.GetBool("no-hosts")
if err != nil {
return nil, err

View File

@ -98,6 +98,7 @@ func init() {
func playFlags(cmd *cobra.Command) {
flags := cmd.Flags()
flags.SetNormalizeFunc(utils.AliasFlags)
podmanConfig := registry.PodmanConfig()
annotationFlagName := "annotation"
flags.StringArrayVar(
@ -139,7 +140,8 @@ func playFlags(cmd *cobra.Command) {
)
_ = cmd.RegisterFlagCompletionFunc(usernsFlagName, common.AutocompleteUserNamespace)
flags.BoolVar(&playOptions.NoHosts, "no-hosts", false, "Do not create /etc/hosts within the pod's containers, instead use the version from the image")
flags.BoolVar(&playOptions.NoHostname, "no-hostname", false, "Do not create /etc/hostname within the container, instead use the version from the image")
flags.BoolVar(&playOptions.NoHosts, "no-hosts", podmanConfig.ContainersConfDefaultsRO.Containers.NoHosts, "Do not create /etc/hosts within the pod's containers, instead use the version from the image")
flags.BoolVarP(&playOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
flags.BoolVar(&playOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
flags.BoolVar(&playOptions.StartCLI, "start", true, "Start the pod after creating it")

View File

@ -1,9 +1,9 @@
####> This option file is used in:
####> podman build, farm build
####> podman build, create, farm build, kube play, pod create, run
####> If file is edited, make sure the changes
####> are applicable to all of those.
#### **--no-hostname**
Do not create the _/etc/hostname_ file in the container for RUN instructions.
Do not create the _/etc/hostname_ file in the containers.
By default, Buildah manages the _/etc/hostname_ file, adding the container's own hostname. When the **--no-hostname** option is set, the image's _/etc/hostname_ will be preserved unmodified if it exists.
By default, Podman manages the _/etc/hostname_ file, adding the container's own hostname. When the **--no-hostname** option is set, the image's _/etc/hostname_ will be preserved unmodified if it exists.

View File

@ -62,6 +62,10 @@ Valid placeholders for the Go template are listed below:
| .SizeRw | Size of upper (R/W) container layer, in bytes [1] |
| .State ... | Container state info (struct) |
| .StaticDir | Path to container metadata dir (string) |
| .UseImageHostname | Use /etc/hostname from the image if it exists? (string: true/false)
|
| .UseImageHosts | Use /etc/hosts from the image? (string: true/false)
|
[1] This format specifier requires the **--size** option

View File

@ -268,6 +268,8 @@ If used together with **--pod**, the container does not join the pod's network n
@@option no-healthcheck
@@option no-hostname
@@option no-hosts
This option conflicts with **--add-host**.

View File

@ -228,6 +228,8 @@ Note: When joining multiple networks use the **--network name:mac=\<mac\>** synt
When no network option is specified and *host* network mode is not configured in the YAML file, a new network stack is created and pods are attached to it making possible pod to pod communication.
@@option no-hostname
@@option no-hosts
This option conflicts with host added in the Kubernetes YAML.

View File

@ -128,6 +128,8 @@ Invalid if using **--dns**, **--dns-option**, or **--dns-search** with **--netwo
@@option network-alias
@@option no-hostname
@@option no-hosts
This option conflicts with **--add-host**.

View File

@ -287,6 +287,8 @@ If used together with **--pod**, the container joins the pod's network namespace
@@option no-healthcheck
@@option no-hostname
@@option no-hosts
This option conflicts with **--add-host**.

View File

@ -286,10 +286,13 @@ type ContainerNetworkConfig struct {
// DNS options to be set in container resolv.conf
// With override options in host resolv if set
DNSOption []string `json:"dnsOption,omitempty"`
// UseImageHostname indicates that /etc/hostname should not be
// bind-mounted inside the container.
UseImageHostname bool `json:"useImageHostname"`
// UseImageHosts indicates that /etc/hosts should not be
// bind-mounted inside the container.
// Conflicts with HostAdd.
UseImageHosts bool
UseImageHosts bool `json:"useImageHosts"`
// BaseHostsFile is the 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
// special flags `image` or `none`.
@ -472,6 +475,8 @@ type InfraInherit struct {
Volumes []*specgen.NamedVolume `json:"volumes,omitempty"`
ShmSize *int64 `json:"shm_size"`
ShmSizeSystemd *int64 `json:"shm_size_systemd"`
UseImageHosts bool `json:"use_image_hosts"`
UseImageHostname bool `json:"use_image_hostname"`
}
// IsDefaultShmSize determines if the user actually set the shm in the parent ctr or if it has been set to the default size

View File

@ -171,6 +171,8 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
IsService: c.IsService(),
KubeExitCodePropagation: config.KubeExitCodePropagation.String(),
LockNumber: c.lock.ID(),
UseImageHosts: c.config.UseImageHosts,
UseImageHostname: c.config.UseImageHostname,
}
if config.RootfsImageID != "" { // May not be set if the container was created with --rootfs

View File

@ -2098,7 +2098,7 @@ rootless=%d
}
}
return c.makePlatformBindMounts()
return c.makeHostnameBindMount()
}
// createResolvConf create the resolv.conf file and bind mount it

View File

@ -313,7 +313,7 @@ func setVolumeAtime(mountPoint string, st os.FileInfo) error {
return nil
}
func (c *Container) makePlatformBindMounts() error {
func (c *Container) makeHostnameBindMount() error {
return nil
}

View File

@ -688,7 +688,11 @@ func setVolumeAtime(mountPoint string, st os.FileInfo) error {
return nil
}
func (c *Container) makePlatformBindMounts() error {
func (c *Container) makeHostnameBindMount() error {
if c.config.UseImageHostname {
return nil
}
// Make /etc/hostname
// This should never change, so no need to recreate if it exists
if _, ok := c.state.BindMounts["/etc/hostname"]; !ok {

View File

@ -798,6 +798,8 @@ type InspectContainerData struct {
LockNumber uint32 `json:"lockNumber"`
Config *InspectContainerConfig `json:"Config"`
HostConfig *InspectContainerHostConfig `json:"HostConfig"`
UseImageHosts bool `json:"UseImageHosts"`
UseImageHostname bool `json:"UseImageHostname"`
}
// InspectExecSession contains information about a given exec session.

View File

@ -118,6 +118,9 @@ type InspectPodInfraConfig struct {
// DNSOption is a set of DNS options that will be used by the infra
// container's resolv.conf and shared with the remainder of the pod.
DNSOption []string
// NoManageHostname indicates that the pod will not manage /etc/hostname
// and instead each container will handle their own.
NoManageHostname bool
// NoManageHosts indicates that the pod will not manage /etc/hosts and
// instead each container will handle their own.
NoManageHosts bool

View File

@ -1389,6 +1389,19 @@ func WithUseImageResolvConf() CtrCreateOption {
}
}
// WithUseImageHostname tells the container not to bind-mount /etc/hostname in.
func WithUseImageHostname() CtrCreateOption {
return func(ctr *Container) error {
if ctr.valid {
return define.ErrCtrFinalized
}
ctr.config.UseImageHostname = true
return nil
}
}
// WithUseImageHosts tells the container not to bind-mount /etc/hosts in.
// This conflicts with WithHosts().
func WithUseImageHosts() CtrCreateOption {

View File

@ -677,6 +677,7 @@ func (p *Pod) Inspect() (*define.InspectPodData, error) {
infraConfig.HostNetwork = p.NetworkMode() == "host"
infraConfig.StaticIP = infra.config.ContainerNetworkConfig.StaticIP
infraConfig.NoManageResolvConf = infra.config.UseImageResolvConf
infraConfig.NoManageHostname = infra.config.UseImageHostname
infraConfig.NoManageHosts = infra.config.UseImageHosts
infraConfig.CPUPeriod = p.CPUPeriod()
infraConfig.CPUQuota = p.CPUQuota()

View File

@ -109,6 +109,7 @@ func KubePlay(w http.ResponseWriter, r *http.Request) {
LogDriver string `schema:"logDriver"`
LogOptions []string `schema:"logOptions"`
Network []string `schema:"network"`
NoHostname bool `schema:"noHostname"`
NoHosts bool `schema:"noHosts"`
NoTrunc bool `schema:"noTrunc"`
Replace bool `schema:"replace"`
@ -182,6 +183,7 @@ func KubePlay(w http.ResponseWriter, r *http.Request) {
LogDriver: logDriver,
LogOptions: query.LogOptions,
Networks: query.Network,
NoHostname: query.NoHostname,
NoHosts: query.NoHosts,
Password: password,
PublishPorts: query.PublishPorts,

View File

@ -20,6 +20,8 @@ type PlayOptions struct {
Password *string
// Network - name of the networks to connect to.
Network *[]string
// NoHostname - do not generate /etc/hostname file in pod's containers
NoHostname *bool
// NoHosts - do not generate /etc/hosts file in pod's containers
NoHosts *bool
// Quiet - suppress output when pulling images.

View File

@ -108,6 +108,21 @@ func (o *PlayOptions) GetNetwork() []string {
return *o.Network
}
// WithNoHostname set field NoHostname to given value
func (o *PlayOptions) WithNoHostname(value bool) *PlayOptions {
o.NoHostname = &value
return o
}
// GetNoHostname returns value of field NoHostname
func (o *PlayOptions) GetNoHostname() bool {
if o.NoHostname == nil {
var z bool
return z
}
return *o.NoHostname
}
// WithNoHosts set field NoHosts to given value
func (o *PlayOptions) WithNoHosts(value bool) *PlayOptions {
o.NoHosts = &value

View File

@ -27,6 +27,9 @@ type PlayKubeOptions struct {
ExitCodePropagation string
// Replace indicates whether to delete and recreate a yaml file
Replace bool
// Do not create /etc/hostname within the pod's containers,
// instead use the version from the image
NoHostname bool
// Do not create /etc/hosts within the pod's containers,
// instead use the version from the image
NoHosts bool

View File

@ -367,6 +367,7 @@ func ToPodSpecGen(s specgen.PodSpecGenerator, p *PodCreateOptions) (*specgen.Pod
s.DNSSearch = p.Net.DNSSearch
s.DNSOption = p.Net.DNSOptions
s.NoManageHosts = p.Net.NoHosts
s.NoManageHostname = p.Net.NoHostname
s.HostAdd = p.Net.AddHosts
s.HostsFile = p.Net.HostsFile
}

View File

@ -40,6 +40,7 @@ type NetFlags struct {
MacAddr string `json:"mac-address,omitempty"`
Publish []string `json:"publish,omitempty"`
IP string `json:"ip,omitempty"`
NoHostname bool `json:"no-hostname,omitempty"`
NoHosts bool `json:"no-hosts,omitempty"`
Network string `json:"network,omitempty"`
NetworkAlias []string `json:"network-alias,omitempty"`
@ -57,6 +58,7 @@ type NetOptions struct {
DNSServers []net.IP `json:"dns_server,omitempty"`
HostsFile string `json:"hosts_file,omitempty"`
Network specgen.Namespace `json:"netns,omitempty"`
NoHostname bool `json:"no_manage_hostname,omitempty"`
NoHosts bool `json:"no_manage_hosts,omitempty"`
PublishPorts []types.PortMapping `json:"portmappings,omitempty"`
// NetworkOptions are additional options for each network

View File

@ -625,7 +625,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
podOpt := entities.PodCreateOptions{
Infra: true,
Net: &entities.NetOptions{NoHosts: options.NoHosts},
Net: &entities.NetOptions{NoHosts: options.NoHosts, NoHostname: options.NoHostname},
ExitPolicy: string(config.PodExitPolicyStop),
}
podOpt, err = kube.ToPodOpt(ctx, podName, podOpt, options.PublishAllPorts, podYAML)

View File

@ -65,7 +65,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, body io.Reader, opts en
if opts.Annotations != nil {
options.WithAnnotations(opts.Annotations)
}
options.WithNoHosts(opts.NoHosts).WithUserns(opts.Userns)
options.WithNoHostname(opts.NoHostname).WithNoHosts(opts.NoHosts).WithUserns(opts.Userns)
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
options.WithSkipTLSVerify(s == types.OptionalBoolTrue)
}

View File

@ -495,6 +495,8 @@ func ConfigToSpec(rt *libpod.Runtime, specg *specgen.SpecGenerator, containerID
specg.Networks = conf.Networks
specg.ShmSize = &conf.ShmSize
specg.ShmSizeSystemd = &conf.ShmSizeSystemd
specg.UseImageHostname = &conf.UseImageHostname
specg.UseImageHosts = &conf.UseImageHosts
mapSecurityConfig(conf, specg)

View File

@ -43,7 +43,7 @@ import (
)
func ToPodOpt(ctx context.Context, podName string, p entities.PodCreateOptions, publishAllPorts bool, podYAML *v1.PodTemplateSpec) (entities.PodCreateOptions, error) {
p.Net = &entities.NetOptions{NoHosts: p.Net.NoHosts}
p.Net = &entities.NetOptions{NoHosts: p.Net.NoHosts, NoHostname: p.Net.NoHostname}
p.Name = podName
p.Labels = podYAML.ObjectMeta.Labels

View File

@ -366,6 +366,9 @@ func namespaceOptions(s *specgen.SpecGenerator, rt *libpod.Runtime, pod *libpod.
} else if len(s.HostAdd) > 0 {
toReturn = append(toReturn, libpod.WithHosts(s.HostAdd))
}
if s.UseImageHostname != nil && *s.UseImageHostname {
toReturn = append(toReturn, libpod.WithUseImageHostname())
}
if len(s.DNSSearch) > 0 {
toReturn = append(toReturn, libpod.WithDNSSearch(s.DNSSearch))
}

View File

@ -272,6 +272,9 @@ func MapSpec(p *specgen.PodSpecGenerator) (*specgen.SpecGenerator, error) {
if p.NoManageHosts {
spec.UseImageHosts = &p.NoManageHosts
}
if p.NoManageHostname {
spec.UseImageHostname = &p.NoManageHostname
}
if len(p.InfraConmonPidFile) > 0 {
spec.ConmonPidFile = p.InfraConmonPidFile

View File

@ -159,6 +159,10 @@ type PodNetworkConfig struct {
// Conflicts with NoInfra=true.
// Optional.
DNSOption []string `json:"dns_option,omitempty"`
// NoManageHostname indicates that /etc/hostname should not be managed
// by the pod. Instead, each container will create a separate
// /etc/hostname as they would if not in a pod.
NoManageHostname bool `json:"no_manage_hostname,omitempty"`
// NoManageHosts indicates that /etc/hosts should not be managed by the
// pod. Instead, each container will create a separate /etc/hosts as
// they would if not in a pod.

View File

@ -534,6 +534,10 @@ type ContainerNetworkConfig struct {
// Conflicts with UseImageResolvConf.
// Optional.
DNSOptions []string `json:"dns_option,omitempty"`
// UseImageHostname indicates that /etc/hostname should not be managed by
// Podman, and instead sourced from the image.
// Optional.
UseImageHostname *bool `json:"use_image_hostname,omitempty"`
// UseImageHosts indicates that /etc/hosts should not be managed by
// Podman, and instead sourced from the image.
// Conflicts with HostAdd.

View File

@ -593,6 +593,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *entities.ContainerCreateOptions
s.DNSSearch = c.Net.DNSSearch
s.DNSOptions = c.Net.DNSOptions
s.NetworkOptions = c.Net.NetworkOptions
s.UseImageHostname = &c.Net.NoHostname
s.UseImageHosts = &c.Net.NoHosts
}
if len(s.HostUsers) == 0 || len(c.HostUsers) != 0 {

View File

@ -2384,26 +2384,37 @@ var _ = Describe("Podman kube play", func() {
Expect(label).To(ContainSubstring("unconfined_u:system_r:spc_t:s0"))
})
It("--no-host", func() {
It("--no-hostname", func() {
err := writeYaml(checkInfraImagePodYaml, kubeYaml)
Expect(err).ToNot(HaveOccurred())
kube := podmanTest.Podman([]string{"kube", "play", "--no-hosts", kubeYaml})
kube.WaitWithDefaultTimeout()
Expect(kube).Should(ExitCleanly())
podmanTest.PodmanExitCleanly("kube", "play", "--no-hostname", kubeYaml)
alpineHostname := podmanTest.PodmanExitCleanly("run", "--rm", "--no-hostname", ALPINE, "cat", "/etc/hostname")
podInspect := podmanTest.Podman([]string{"pod", "inspect", "check-infra-image"})
podInspect.WaitWithDefaultTimeout()
Expect(podInspect).Should(ExitCleanly())
podInspect := podmanTest.PodmanExitCleanly("pod", "inspect", "check-infra-image")
data := podInspect.InspectPodToJSON()
for _, ctr := range data.Containers {
if strings.HasSuffix(ctr.Name, "-infra") {
continue
}
exec := podmanTest.Podman([]string{"exec", ctr.ID, "cat", "/etc/hosts"})
exec.WaitWithDefaultTimeout()
Expect(exec).Should(ExitCleanly())
exec := podmanTest.PodmanExitCleanly("exec", ctr.ID, "cat", "/etc/hostname")
Expect(exec.OutputToString()).To(Equal(alpineHostname.OutputToString()))
}
})
It("--no-host", func() {
err := writeYaml(checkInfraImagePodYaml, kubeYaml)
Expect(err).ToNot(HaveOccurred())
kube := podmanTest.PodmanExitCleanly("kube", "play", "--no-hosts", kubeYaml)
podInspect := podmanTest.PodmanExitCleanly("pod", "inspect", "check-infra-image")
data := podInspect.InspectPodToJSON()
for _, ctr := range data.Containers {
if strings.HasSuffix(ctr.Name, "-infra") {
continue
}
exec := podmanTest.PodmanExitCleanly("exec", ctr.ID, "cat", "/etc/hosts")
Expect(exec.OutputToString()).To(Not(ContainSubstring("check-infra-image")))
}
})

View File

@ -123,6 +123,22 @@ var _ = Describe("Podman pod create", func() {
Expect(podResolvConf.OutputToString()).To(Equal(alpineResolvConf.OutputToString()))
})
It("podman create pod with --no-hostname", func() {
name := "test"
podCreate := podmanTest.Podman([]string{"pod", "create", "--no-hostname", "--name", name})
podCreate.WaitWithDefaultTimeout()
Expect(podCreate).Should(ExitCleanly())
alpineHostname := podmanTest.Podman([]string{"run", "--rm", "--no-hostname", ALPINE, "cat", "/etc/hostname"})
alpineHostname.WaitWithDefaultTimeout()
Expect(alpineHostname).Should(ExitCleanly())
podHostname := podmanTest.Podman([]string{"run", "--pod", name, "--rm", ALPINE, "cat", "/etc/hostname"})
podHostname.WaitWithDefaultTimeout()
Expect(podHostname).Should(ExitCleanly())
Expect(podHostname.OutputToString()).To(Equal(alpineHostname.OutputToString()))
})
It("podman create pod with --no-hosts and no infra should fail", func() {
name := "test"
podCreate := podmanTest.Podman([]string{"pod", "create", "--no-hosts", "--name", name, "--infra=false"})

View File

@ -1711,4 +1711,21 @@ search | $IMAGE |
run_podman rm -f -t0 $cname
}
# bats test_tags=ci:parallel
@test "podman run - no-hostname" {
randomname=c_$(safename)
echo "\
from $IMAGE
RUN umount /etc/hostname; rm /etc/hostname
" > $PODMAN_TMPDIR/Containerfile
run_podman build -t $randomname --cap-add SYS_ADMIN ${PODMAN_TMPDIR}
run_podman run --rm $randomname ls /etc/hostname
run_podman 1 run --no-hostname --rm $randomname ls /etc/hostname
is "$output" "ls: /etc/hostname: No such file or directory" "container did not add /etc/hostname"
run_podman rmi $randomname
}
# vim: filetype=sh