From 97323808ed57cf52311a80c55339f8927614b7f0 Mon Sep 17 00:00:00 2001 From: Matthew Heon Date: Mon, 17 Feb 2020 16:49:59 -0500 Subject: Add network options to podman pod create Enables most of the network-related functionality from `podman run` in `podman pod create`. Custom CNI networks can be specified, host networking is supported, DNS options can be configured. Also enables host networking in `podman play kube`. Fixes #2808 Fixes #3837 Fixes #4432 Fixes #4718 Fixes #4770 Signed-off-by: Matthew Heon --- cmd/podman/pod_create.go | 17 +-- completions/bash/podman | 8 ++ docs/source/markdown/podman-pod-create.1.md | 54 +++++++-- libpod/container_internal_linux.go | 23 ++-- libpod/options.go | 43 ++++++++ libpod/runtime_pod_infra_linux.go | 80 +++++++------- pkg/adapter/pods.go | 91 +++++++++++++-- pkg/adapter/pods_remote.go | 2 +- pkg/varlinkapi/pods.go | 16 +-- test/e2e/pod_create_test.go | 165 ++++++++++++++++++++++++++++ 10 files changed, 402 insertions(+), 97 deletions(-) diff --git a/cmd/podman/pod_create.go b/cmd/podman/pod_create.go index 0f72780f9..810f62f02 100644 --- a/cmd/podman/pod_create.go +++ b/cmd/podman/pod_create.go @@ -45,19 +45,7 @@ func init() { podCreateCommand.SetUsageTemplate(UsageTemplate()) flags := podCreateCommand.Flags() flags.SetInterspersed(false) - // When we are ready to add the network options to the create commmand, we need to uncomment - // the following - - //flags.AddFlagSet(getNetFlags()) - - // Once this is uncommented, then the publish option below needs to be removed because it - // conflicts with the publish in getNetFlags. Upon removal, the c.Publish will not work - // anymore and needs to be cleaned up. I suggest starting with removing the Publish attribute - // from PodCreateValues structure. Running make should then expose all areas that need to be - // addressed. To get the value of publish (and other flags in getNetFlags, use the syntax: - // c.(" 0 { + if len(c.StringSlice("publish")) > 0 { if !c.Infra { return errors.Errorf("you must have an infra container to publish port bindings to the host") } diff --git a/completions/bash/podman b/completions/bash/podman index 10d40a0c9..958633bf0 100644 --- a/completions/bash/podman +++ b/completions/bash/podman @@ -2897,13 +2897,21 @@ _podman_image_exists() { _podman_pod_create() { local options_with_args=" + --add-host --cgroup-parent + --dns + --dns-opt + --dns-search --infra-command --infra-image + --ip --label-file --label -l + --mac-address --name + --network + --no-hosts --podidfile --publish -p diff --git a/docs/source/markdown/podman-pod-create.1.md b/docs/source/markdown/podman-pod-create.1.md index cd1de6401..dba31f681 100644 --- a/docs/source/markdown/podman-pod-create.1.md +++ b/docs/source/markdown/podman-pod-create.1.md @@ -15,50 +15,82 @@ containers added to it. The pod id is printed to STDOUT. You can then use ## OPTIONS +**--add-host**=_host_:_ip_ + +Add a host to the /etc/hosts file shared between all containers in the pod. + **--cgroup-parent**=*path* Path to cgroups under which the cgroup for the pod will be created. If the path is not absolute, the path is considered to be relative to the cgroups path of the init process. Cgroups will be created if they do not already exist. +**--dns**=*ipaddr* + +Set custom DNS servers in the /etc/resolv.conf file that will be shared between all containers in the pod. A special option, "none" is allowed which disables creation of /etc/resolv.conf for the pod. + +**--dns-opt**=*option* + +Set custom DNS options in the /etc/resolv.conf file that will be shared between all containers in the pod. + +**--dns-search**=*domain* + +Set custom DNS search domains in the /etc/resolv.conf file that will be shared between all containers in the pod. + **--help** -Print usage statement +Print usage statement. -**--infra** +**--infra**=**true**|**false** -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 +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. **--infra-command**=*command* -The command that will be run to start the infra container. Default: "/pause" +The command that will be run to start the infra container. Default: "/pause". **--infra-image**=*image* -The image that will be created for the infra container. Default: "k8s.gcr.io/pause:3.1" +The image that will be created for the infra container. Default: "k8s.gcr.io/pause:3.1". + +**--ip**=*ipaddr* + +Set a static IP for the pod's shared network. **-l**, **--label**=*label* -Add metadata to a pod (e.g., --label com.example.key=value) +Add metadata to a pod (e.g., --label com.example.key=value). **--label-file**=*label* -Read in a line delimited file of labels +Read in a line delimited file of labels. + +**--mac-address**=*address* + +Set a static MAC address for the pod's shared network. **-n**, **--name**=*name* -Assign a name to the pod +Assign a name to the pod. + +**--network**=*mode* + +Set network mode for the pod. Supported values are *bridge* (the default), *host* (do not create a network namespace, all containers in the pod will use the host's network), or a comma-separated list of the names of CNI networks the pod should join. + +**--no-hosts**=**true**|**false** + +Disable creation of /etc/hosts for the pod. **--podidfile**=*podid* -Write the pod ID to the file +Write the pod ID to the file. **-p**, **--publish**=*port* -Publish a port or range of ports from the pod to the host +Publish a port or range of ports from the pod to the host. Format: `ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort` Both hostPort and containerPort can be specified as a range of ports. When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range. -Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT` +Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT`. NOTE: This cannot be modified once the pod is created. diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go index 561dbdc1c..739026264 100644 --- a/libpod/container_internal_linux.go +++ b/libpod/container_internal_linux.go @@ -1114,22 +1114,17 @@ func (c *Container) makeBindMounts() error { return errors.Wrapf(err, "error fetching bind mounts from dependency %s of container %s", depCtr.ID(), c.ID()) } - if !c.config.UseImageResolvConf { - // 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 { - c.state.BindMounts["/etc/resolv.conf"] = resolvPath - } + // 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 !c.config.UseImageResolvConf && exists { + c.state.BindMounts["/etc/resolv.conf"] = resolvPath } - if !c.config.UseImageHosts { - // check if dependency container has an /etc/hosts file - hostsPath, exists := bindMounts["/etc/hosts"] - if !exists { - return errors.Errorf("error finding hosts file of dependency container %s for container %s", depCtr.ID(), c.ID()) - } - + // check if dependency container has an /etc/hosts file. + // It may not have one, so only use it if it does. + hostsPath, exists := bindMounts["/etc/hosts"] + if !c.config.UseImageHosts && exists { depCtr.lock.Lock() // generate a hosts file for the dependency container, // based on either its old hosts file, or the default, diff --git a/libpod/options.go b/libpod/options.go index 1d8405841..1fd588867 100644 --- a/libpod/options.go +++ b/libpod/options.go @@ -1784,6 +1784,9 @@ func WithInfraContainerPorts(bindings []ocicni.PortMapping) PodCreateOption { if pod.valid { return define.ErrPodFinalized } + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot set pod ports as no infra container is being created") + } pod.config.InfraContainer.PortBindings = bindings return nil } @@ -1796,6 +1799,10 @@ func WithPodStaticIP(ip net.IP) PodCreateOption { return define.ErrPodFinalized } + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot set pod static IP as no infra container is being created") + } + if pod.config.InfraContainer.HostNetwork { return errors.Wrapf(define.ErrInvalidArg, "cannot set static IP if host network is specified") } @@ -1817,6 +1824,10 @@ func WithPodStaticMAC(mac net.HardwareAddr) PodCreateOption { return define.ErrPodFinalized } + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot set pod static MAC as no infra container is being created") + } + if pod.config.InfraContainer.HostNetwork { return errors.Wrapf(define.ErrInvalidArg, "cannot set static MAC if host network is specified") } @@ -1839,6 +1850,10 @@ func WithPodUseImageResolvConf() PodCreateOption { return define.ErrPodFinalized } + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") + } + if len(pod.config.InfraContainer.DNSServer) != 0 || len(pod.config.InfraContainer.DNSSearch) != 0 || len(pod.config.InfraContainer.DNSOption) != 0 { @@ -1858,6 +1873,10 @@ func WithPodDNS(dnsServer []string) PodCreateOption { return define.ErrPodFinalized } + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") + } + if pod.config.InfraContainer.UseImageResolvConf { return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS servers if pod will not create /etc/resolv.conf") } @@ -1875,6 +1894,10 @@ func WithPodDNSSearch(dnsSearch []string) PodCreateOption { return define.ErrPodFinalized } + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") + } + if pod.config.InfraContainer.UseImageResolvConf { return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS search domains if pod will not create /etc/resolv.conf") } @@ -1892,6 +1915,10 @@ func WithPodDNSOption(dnsOption []string) PodCreateOption { return define.ErrPodFinalized } + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod DNS as no infra container is being created") + } + if pod.config.InfraContainer.UseImageResolvConf { return errors.Wrapf(define.ErrInvalidArg, "cannot add DNS options if pod will not create /etc/resolv.conf") } @@ -1910,6 +1937,10 @@ func WithPodUseImageHosts() PodCreateOption { return define.ErrPodFinalized } + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod hosts as no infra container is being created") + } + if len(pod.config.InfraContainer.HostAdd) != 0 { return errors.Wrapf(define.ErrInvalidArg, "not creating /etc/hosts conflicts with adding to the hosts file") } @@ -1927,6 +1958,10 @@ func WithPodHosts(hosts []string) PodCreateOption { return define.ErrPodFinalized } + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod hosts as no infra container is being created") + } + if pod.config.InfraContainer.UseImageHosts { return errors.Wrapf(define.ErrInvalidArg, "cannot add to /etc/hosts if container is using image hosts") } @@ -1944,6 +1979,10 @@ func WithPodNetworks(networks []string) PodCreateOption { return define.ErrPodFinalized } + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod CNI networks as no infra container is being created") + } + if (pod.config.InfraContainer.StaticIP != nil || pod.config.InfraContainer.StaticMAC != nil) && len(networks) > 1 { return errors.Wrapf(define.ErrInvalidArg, "cannot join more than one CNI network if setting a static IP or MAC address") @@ -1966,6 +2005,10 @@ func WithPodHostNetwork() PodCreateOption { return define.ErrPodFinalized } + if !pod.config.InfraContainer.HasInfraContainer { + return errors.Wrapf(define.ErrInvalidArg, "cannot configure pod host networking as no infra container is being created") + } + if len(pod.config.InfraContainer.PortBindings) > 0 || pod.config.InfraContainer.StaticIP != nil || pod.config.InfraContainer.StaticMAC != nil || diff --git a/libpod/runtime_pod_infra_linux.go b/libpod/runtime_pod_infra_linux.go index 3aded61f2..a6cac2b72 100644 --- a/libpod/runtime_pod_infra_linux.go +++ b/libpod/runtime_pod_infra_linux.go @@ -37,6 +37,7 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID isRootless := rootless.IsRootless() entryCmd := []string{r.config.InfraCommand} + var options []CtrCreateOption // I've seen circumstances where config is being passed as nil. // Let's err on the side of safety and make sure it's safe to use. if config != nil { @@ -68,6 +69,44 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID g.AddProcessEnv(nameValSlice[0], nameValSlice[1]) } } + + // Since user namespace sharing is not implemented, we only need to check if it's rootless + if !p.config.InfraContainer.HostNetwork { + netmode := "bridge" + if isRootless { + netmode = "slirp4netns" + } + // PostConfigureNetNS should not be set since user namespace sharing is not implemented + // and rootless networking no longer supports post configuration setup + options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, false, netmode, p.config.InfraContainer.Networks)) + } else if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { + return nil, errors.Wrapf(err, "error removing network namespace from pod %s infra container", p.ID()) + } + + if p.config.InfraContainer.StaticIP != nil { + options = append(options, WithStaticIP(p.config.InfraContainer.StaticIP)) + } + if p.config.InfraContainer.StaticMAC != nil { + options = append(options, WithStaticMAC(p.config.InfraContainer.StaticMAC)) + } + if p.config.InfraContainer.UseImageResolvConf { + options = append(options, WithUseImageResolvConf()) + } + if len(p.config.InfraContainer.DNSServer) > 0 { + options = append(options, WithDNS(p.config.InfraContainer.DNSServer)) + } + if len(p.config.InfraContainer.DNSSearch) > 0 { + options = append(options, WithDNSSearch(p.config.InfraContainer.DNSSearch)) + } + if len(p.config.InfraContainer.DNSOption) > 0 { + options = append(options, WithDNSOption(p.config.InfraContainer.DNSOption)) + } + if p.config.InfraContainer.UseImageHosts { + options = append(options, WithUseImageHosts()) + } + if len(p.config.InfraContainer.HostAdd) > 0 { + options = append(options, WithHosts(p.config.InfraContainer.HostAdd)) + } } g.SetRootReadonly(true) @@ -87,52 +126,11 @@ func (r *Runtime) makeInfraContainer(ctx context.Context, p *Pod, imgName, imgID } containerName := p.ID()[:IDTruncLength] + "-infra" - var options []CtrCreateOption options = append(options, r.WithPod(p)) options = append(options, WithRootFSFromImage(imgID, imgName, false)) options = append(options, WithName(containerName)) options = append(options, withIsInfra()) - // Since user namespace sharing is not implemented, we only need to check if it's rootless - if !p.config.InfraContainer.HostNetwork { - netmode := "bridge" - if isRootless { - netmode = "slirp4netns" - } - // PostConfigureNetNS should not be set since user namespace sharing is not implemented - // and rootless networking no longer supports post configuration setup - options = append(options, WithNetNS(p.config.InfraContainer.PortBindings, false, netmode, p.config.InfraContainer.Networks)) - } else { - if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil { - return nil, errors.Wrapf(err, "error removing network namespace from pod %s infra container", p.ID()) - } - } - - if p.config.InfraContainer.StaticIP != nil { - options = append(options, WithStaticIP(p.config.InfraContainer.StaticIP)) - } - if p.config.InfraContainer.StaticMAC != nil { - options = append(options, WithStaticMAC(p.config.InfraContainer.StaticMAC)) - } - if p.config.InfraContainer.UseImageResolvConf { - options = append(options, WithUseImageResolvConf()) - } - if len(p.config.InfraContainer.DNSServer) > 0 { - options = append(options, WithDNS(p.config.InfraContainer.DNSServer)) - } - if len(p.config.InfraContainer.DNSSearch) > 0 { - options = append(options, WithDNSSearch(p.config.InfraContainer.DNSSearch)) - } - if len(p.config.InfraContainer.DNSOption) > 0 { - options = append(options, WithDNSOption(p.config.InfraContainer.DNSOption)) - } - if p.config.InfraContainer.UseImageHosts { - options = append(options, WithUseImageHosts()) - } - if len(p.config.InfraContainer.HostAdd) > 0 { - options = append(options, WithHosts(p.config.InfraContainer.HostAdd)) - } - return r.newContainer(ctx, g.Config, options...) } diff --git a/pkg/adapter/pods.go b/pkg/adapter/pods.go index 49f086ef3..0d9fa7210 100644 --- a/pkg/adapter/pods.go +++ b/pkg/adapter/pods.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "io/ioutil" + "net" "os" "path/filepath" "strings" @@ -248,6 +249,17 @@ func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateVa err error ) + // This needs to be first, as a lot of options depend on + // WithInfraContainer() + if cli.Infra { + options = append(options, libpod.WithInfraContainer()) + nsOptions, err := shared.GetNamespaceOptions(strings.Split(cli.Share, ",")) + if err != nil { + return "", err + } + options = append(options, nsOptions...) + } + if cli.Flag("cgroup-parent").Changed { options = append(options, libpod.WithPodCgroupParent(cli.CgroupParent)) } @@ -264,17 +276,78 @@ func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateVa options = append(options, libpod.WithPodHostname(cli.Hostname)) } - if cli.Infra { - options = append(options, libpod.WithInfraContainer()) - nsOptions, err := shared.GetNamespaceOptions(strings.Split(cli.Share, ",")) + if cli.Flag("add-host").Changed { + options = append(options, libpod.WithPodHosts(cli.StringSlice("add-host"))) + } + if cli.Flag("dns").Changed { + dns := cli.StringSlice("dns") + foundHost := false + for _, entry := range dns { + if entry == "host" { + foundHost = true + } + } + if foundHost && len(dns) > 1 { + return "", errors.Errorf("cannot set dns=host and still provide other DNS servers") + } + if foundHost { + options = append(options, libpod.WithPodUseImageResolvConf()) + } else { + options = append(options, libpod.WithPodDNS(cli.StringSlice("dns"))) + } + } + if cli.Flag("dns-opt").Changed { + options = append(options, libpod.WithPodDNSOption(cli.StringSlice("dns-opt"))) + } + if cli.Flag("dns-search").Changed { + options = append(options, libpod.WithPodDNSSearch(cli.StringSlice("dns-search"))) + } + if cli.Flag("ip").Changed { + ip := net.ParseIP(cli.String("ip")) + if ip == nil { + return "", errors.Errorf("invalid IP address %q passed to --ip", cli.String("ip")) + } + + options = append(options, libpod.WithPodStaticIP(ip)) + } + if cli.Flag("mac-address").Changed { + mac, err := net.ParseMAC(cli.String("mac-address")) if err != nil { - return "", err + return "", errors.Wrapf(err, "invalid MAC address %q passed to --mac-address", cli.String("mac-address")) + } + + options = append(options, libpod.WithPodStaticMAC(mac)) + } + if cli.Flag("network").Changed { + netValue := cli.String("network") + switch strings.ToLower(netValue) { + case "bridge": + // Do nothing. + // TODO: Maybe this should be split between slirp and + // bridge? Better to wait until someone asks... + logrus.Debugf("Pod using default network mode") + case "host": + logrus.Debugf("Pod will use host networking") + options = append(options, libpod.WithPodHostNetwork()) + case "": + return "", errors.Errorf("invalid value passed to --net: must provide a comma-separated list of CNI networks or host") + default: + // We'll assume this is a comma-separated list of CNI + // networks. + networks := strings.Split(netValue, ",") + logrus.Debugf("Pod joining CNI networks: %v", networks) + options = append(options, libpod.WithPodNetworks(networks)) + } + } + if cli.Flag("no-hosts").Changed { + if cli.Bool("no-hosts") { + options = append(options, libpod.WithPodUseImageHosts()) } - options = append(options, nsOptions...) } - if len(cli.Publish) > 0 { - portBindings, err := shared.CreatePortBindings(cli.Publish) + publish := cli.StringSlice("publish") + if len(publish) > 0 { + portBindings, err := shared.CreatePortBindings(publish) if err != nil { return "", err } @@ -497,6 +570,10 @@ func (r *LocalRuntime) PlayKubeYAML(ctx context.Context, c *cliconfig.KubePlayVa } podOptions = append(podOptions, libpod.WithPodHostname(hostname)) + if podYAML.Spec.HostNetwork { + podOptions = append(podOptions, libpod.WithPodHostNetwork()) + } + nsOptions, err := shared.GetNamespaceOptions(strings.Split(shared.DefaultKernelNamespaces, ",")) if err != nil { return nil, err diff --git a/pkg/adapter/pods_remote.go b/pkg/adapter/pods_remote.go index 5ef1a9216..20f089628 100644 --- a/pkg/adapter/pods_remote.go +++ b/pkg/adapter/pods_remote.go @@ -185,7 +185,7 @@ func (r *LocalRuntime) CreatePod(ctx context.Context, cli *cliconfig.PodCreateVa Infra: cli.Infra, InfraCommand: cli.InfraCommand, InfraImage: cli.InfraCommand, - Publish: cli.Publish, + Publish: cli.StringSlice("publish"), } return iopodman.CreatePod().Call(r.Conn, pc) diff --git a/pkg/varlinkapi/pods.go b/pkg/varlinkapi/pods.go index 1ebe5d424..2ec45f7a1 100644 --- a/pkg/varlinkapi/pods.go +++ b/pkg/varlinkapi/pods.go @@ -16,6 +16,14 @@ import ( // CreatePod ... func (i *LibpodAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCreate) error { var options []libpod.PodCreateOption + if create.Infra { + options = append(options, libpod.WithInfraContainer()) + nsOptions, err := shared.GetNamespaceOptions(create.Share) + if err != nil { + return err + } + options = append(options, nsOptions...) + } if create.CgroupParent != "" { options = append(options, libpod.WithPodCgroupParent(create.CgroupParent)) } @@ -43,14 +51,6 @@ func (i *LibpodAPI) CreatePod(call iopodman.VarlinkCall, create iopodman.PodCrea options = append(options, libpod.WithInfraContainerPorts(portBindings)) } - if create.Infra { - options = append(options, libpod.WithInfraContainer()) - nsOptions, err := shared.GetNamespaceOptions(create.Share) - if err != nil { - return err - } - options = append(options, nsOptions...) - } options = append(options, libpod.WithPodCgroups()) pod, err := i.Runtime.NewPod(getContext(), options...) diff --git a/test/e2e/pod_create_test.go b/test/e2e/pod_create_test.go index 2efa36141..e0a10c202 100644 --- a/test/e2e/pod_create_test.go +++ b/test/e2e/pod_create_test.go @@ -1,7 +1,9 @@ package integration import ( + "fmt" "os" + "strings" . "github.com/containers/libpod/test/utils" . "github.com/onsi/ginkgo" @@ -117,4 +119,167 @@ var _ = Describe("Podman pod create", func() { session.WaitWithDefaultTimeout() Expect(session.ExitCode()).To(Equal(125)) }) + + It("podman create pod with --no-hosts", func() { + SkipIfRemote() + name := "test" + podCreate := podmanTest.Podman([]string{"pod", "create", "--no-hosts", "--name", name}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(0)) + + alpineResolvConf := podmanTest.Podman([]string{"run", "-ti", "--rm", "--no-hosts", ALPINE, "cat", "/etc/hosts"}) + alpineResolvConf.WaitWithDefaultTimeout() + Expect(alpineResolvConf.ExitCode()).To(Equal(0)) + + podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/hosts"}) + podResolvConf.WaitWithDefaultTimeout() + Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(podResolvConf.OutputToString()).To(Equal(alpineResolvConf.OutputToString())) + }) + + It("podman create pod with --no-hosts and no infra should fail", func() { + SkipIfRemote() + name := "test" + podCreate := podmanTest.Podman([]string{"pod", "create", "--no-hosts", "--name", name, "--infra=false"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(125)) + }) + + It("podman create pod with --add-host", func() { + SkipIfRemote() + name := "test" + podCreate := podmanTest.Podman([]string{"pod", "create", "--add-host", "test.example.com:12.34.56.78", "--name", name}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(0)) + + podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/hosts"}) + podResolvConf.WaitWithDefaultTimeout() + Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(strings.Contains(podResolvConf.OutputToString(), "12.34.56.78 test.example.com")).To(BeTrue()) + }) + + It("podman create pod with --add-host and no infra should fail", func() { + SkipIfRemote() + name := "test" + podCreate := podmanTest.Podman([]string{"pod", "create", "--add-host", "test.example.com:12.34.56.78", "--name", name, "--infra=false"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(125)) + }) + + It("podman create pod with DNS server set", func() { + SkipIfRemote() + name := "test" + server := "12.34.56.78" + podCreate := podmanTest.Podman([]string{"pod", "create", "--dns", server, "--name", name}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(0)) + + podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/resolv.conf"}) + podResolvConf.WaitWithDefaultTimeout() + Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(strings.Contains(podResolvConf.OutputToString(), fmt.Sprintf("nameserver %s", server))).To(BeTrue()) + }) + + It("podman create pod with DNS server set and no infra should fail", func() { + SkipIfRemote() + name := "test" + server := "12.34.56.78" + podCreate := podmanTest.Podman([]string{"pod", "create", "--dns", server, "--name", name, "--infra=false"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(125)) + }) + + It("podman create pod with DNS option set", func() { + SkipIfRemote() + name := "test" + option := "attempts:5" + podCreate := podmanTest.Podman([]string{"pod", "create", "--dns-opt", option, "--name", name}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(0)) + + podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/resolv.conf"}) + podResolvConf.WaitWithDefaultTimeout() + Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(strings.Contains(podResolvConf.OutputToString(), fmt.Sprintf("options %s", option))).To(BeTrue()) + }) + + It("podman create pod with DNS option set and no infra should fail", func() { + SkipIfRemote() + name := "test" + option := "attempts:5" + podCreate := podmanTest.Podman([]string{"pod", "create", "--dns-opt", option, "--name", name, "--infra=false"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(125)) + }) + + It("podman create pod with DNS search domain set", func() { + SkipIfRemote() + name := "test" + search := "example.com" + podCreate := podmanTest.Podman([]string{"pod", "create", "--dns-search", search, "--name", name}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(0)) + + podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "cat", "/etc/resolv.conf"}) + podResolvConf.WaitWithDefaultTimeout() + Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(strings.Contains(podResolvConf.OutputToString(), fmt.Sprintf("search %s", search))).To(BeTrue()) + }) + + It("podman create pod with DNS search domain set and no infra should fail", func() { + SkipIfRemote() + name := "test" + search := "example.com" + podCreate := podmanTest.Podman([]string{"pod", "create", "--dns-search", search, "--name", name, "--infra=false"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(125)) + }) + + It("podman create pod with IP address", func() { + SkipIfRemote() + SkipIfRootless() + name := "test" + ip := GetRandomIPAddress() + podCreate := podmanTest.Podman([]string{"pod", "create", "--ip", ip, "--name", name}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(0)) + + podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"}) + podResolvConf.WaitWithDefaultTimeout() + Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(strings.Contains(podResolvConf.OutputToString(), ip)).To(BeTrue()) + }) + + It("podman create pod with IP address and no infra should fail", func() { + SkipIfRemote() + name := "test" + ip := GetRandomIPAddress() + podCreate := podmanTest.Podman([]string{"pod", "create", "--ip", ip, "--name", name, "--infra=false"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(125)) + }) + + It("podman create pod with MAC address", func() { + SkipIfRemote() + SkipIfRootless() + name := "test" + mac := "92:d0:c6:0a:29:35" + podCreate := podmanTest.Podman([]string{"pod", "create", "--mac-address", mac, "--name", name}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(0)) + + podResolvConf := podmanTest.Podman([]string{"run", "--pod", name, "-ti", "--rm", ALPINE, "ip", "addr"}) + podResolvConf.WaitWithDefaultTimeout() + Expect(podResolvConf.ExitCode()).To(Equal(0)) + Expect(strings.Contains(podResolvConf.OutputToString(), mac)).To(BeTrue()) + }) + + It("podman create pod with MAC address and no infra should fail", func() { + SkipIfRemote() + name := "test" + mac := "92:d0:c6:0a:29:35" + podCreate := podmanTest.Podman([]string{"pod", "create", "--mac-address", mac, "--name", name, "--infra=false"}) + podCreate.WaitWithDefaultTimeout() + Expect(podCreate.ExitCode()).To(Equal(125)) + }) }) -- cgit v1.2.3-54-g00ecf