summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/specgen.go11
-rw-r--r--cmd/podman/containers/cp.go4
-rw-r--r--docs/source/Tutorials.rst1
-rw-r--r--docs/tutorials/README.md4
-rw-r--r--docs/tutorials/basic_networking.md291
-rw-r--r--docs/tutorials/podman_bridge.pngbin0 -> 48144 bytes
-rw-r--r--docs/tutorials/podman_macvlan.pngbin0 -> 37646 bytes
-rw-r--r--docs/tutorials/podman_pod.pngbin0 -> 53274 bytes
-rw-r--r--docs/tutorials/podman_rootless_default.pngbin0 -> 40293 bytes
-rw-r--r--pkg/domain/infra/abi/archive.go7
-rw-r--r--pkg/rootless/rootless_linux.c6
-rw-r--r--test/e2e/run_memory_test.go24
-rw-r--r--test/system/065-cp.bats26
13 files changed, 362 insertions, 12 deletions
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index 975c76fd9..eff8b43aa 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -148,17 +148,16 @@ func getMemoryLimits(s *specgen.SpecGenerator, c *ContainerCLIOpts) (*specs.Linu
}
if m := c.MemorySwap; len(m) > 0 {
var ms int64
- if m == "-1" {
- ms = int64(-1)
- s.ResourceLimits.Memory.Swap = &ms
- } else {
+ // only set memory swap if it was set
+ // -1 indicates unlimited
+ if m != "-1" {
ms, err = units.RAMInBytes(m)
+ memory.Swap = &ms
if err != nil {
return nil, errors.Wrapf(err, "invalid value for memory")
}
+ hasLimits = true
}
- memory.Swap = &ms
- hasLimits = true
}
if m := c.KernelMemory; len(m) > 0 {
mk, err := units.RAMInBytes(m)
diff --git a/cmd/podman/containers/cp.go b/cmd/podman/containers/cp.go
index 69b61a06c..5db05719c 100644
--- a/cmd/podman/containers/cp.go
+++ b/cmd/podman/containers/cp.go
@@ -311,8 +311,8 @@ func copyToContainer(container string, containerPath string, hostPath string) er
}
getOptions := buildahCopiah.GetOptions{
- // Unless the specified path ends with ".", we want to copy the base directory.
- KeepDirectoryNames: !strings.HasSuffix(hostPath, "."),
+ // Unless the specified points to ".", we want to copy the base directory.
+ KeepDirectoryNames: hostInfo.IsDir && filepath.Base(hostPath) != ".",
}
if !hostInfo.IsDir && (!containerInfo.IsDir || containerInfoErr != nil) {
// If we're having a file-to-file copy, make sure to
diff --git a/docs/source/Tutorials.rst b/docs/source/Tutorials.rst
index e3e869d5b..e48d1e853 100644
--- a/docs/source/Tutorials.rst
+++ b/docs/source/Tutorials.rst
@@ -11,3 +11,4 @@ Here are a number of useful tutorials to get you up and running with Podman. If
* `Podman remote-client tutorial <https://github.com/containers/podman/blob/master/docs/tutorials/remote_client.md>`_: A brief how-to on using the Podman remote-client.
* `How to use libpod for custom/derivative projects <https://github.com/containers/podman/blob/master/docs/tutorials/podman-derivative-api.md>`_: How the libpod API can be used within your own project.
* `How to use Podman's Go bindings <https://github.com/containers/podman/blob/master/docs/tutorials/podman-go-bindings.md>`_: A brief how-to on using Podman's Go bindings in external applications.
+* `Common network setups <https://github.com/containers/podman/blob/master/docs/tutorials/basic_networking.md>`_: A basic guide to common network setups for Podman.
diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md
index 246d235ee..455459062 100644
--- a/docs/tutorials/README.md
+++ b/docs/tutorials/README.md
@@ -31,3 +31,7 @@ Learn how to setup and use image signing with Podman.
**[Go Bindings](podman-go-bindings.md)**
A brief how-to on using Podman's Go bindings in external applications.
+
+**[Go Bindings](basic_networking.md)**
+
+A basic guide to common network setups with Podman
diff --git a/docs/tutorials/basic_networking.md b/docs/tutorials/basic_networking.md
new file mode 100644
index 000000000..7544c1cfd
--- /dev/null
+++ b/docs/tutorials/basic_networking.md
@@ -0,0 +1,291 @@
+![PODMAN logo](../../logo/podman-logo-source.svg)
+
+
+# Basic Networking Guide for Podman
+
+
+It seems once people master the basics of containers, networking is one of the first
+aspects they begin experimenting with. And in regards to networking, it takes very
+little experimentation before ending up on the deep end of the pool. The following
+guide shows the most common network setups for Podman rootfull and rootless containers.
+Each setup is supported with an example.
+
+
+## Differences between rootfull and rootless container networking
+
+One of the guiding factors on networking for containers with Podman is going to be
+whether or not the container is run by a root user or not. This is because unprivileged
+users cannot create networking interfaces on the host. Therefore, with rootfull
+containers, the default networking mode is to use the Container Network Interface
+(CNI) plugins and specifically the bridge plugin. For rootless, the default network
+mode is slirp4netns. Because of the limited privileges, slirp4netns lacks some of
+the features of CNI networking; for example, slirp4netns cannot give containers a
+routable IP address.
+
+## Firewalls
+
+The role of a firewall will not impact the setup and configuration of networking,
+but it will impact traffic on those networks. The most obvious is inbound network
+traffic to the container host, which is being passed onto containers usually with
+port mapping. Depending on the firewall implementation, we have observed firewall
+ports being opened automatically due to running a container with a port mapping (for
+example). If container traffic does not seem to work properly, check the firewall
+and allow traffic on ports the container is using. A common problem is that
+reloading the firewall deletes the cni iptables rules resulting in a loss of
+network connectivity for rootful containers. Podman v3 provides the podman
+network reload command to restore this without having to restart the container.
+
+## Basic Network Setups
+
+Most containers and pods being run with Podman adhere to a couple of simple scenarios.
+By default, rootfull Podman will create a bridged network. This is the most straightforward
+and preferred network setup for Podman. Bridge networking creates an interface for
+the container on an internal bridge network, which is then connected to the internet
+via Network Address Translation(NAT). We also see users wanting to use `macvlan`
+for networking as well. The `macvlan` plugin forwards an entire network interface
+from the host into the container, allowing it access to the network the host is connected
+to. And finally, the default network configuration for rootless containers is slirp4netns.
+The slirp4netns network mode has limited capabilities but can be run on users without
+root privileges. It creates a tunnel from the host into the container to forward
+traffic.
+
+### Bridge
+
+CNI defines a bridge network as where an internal network is created where both the
+container and host are attached. Then this network is capable of allowing the containers
+to communicate outside of the host.
+
+
+![bridge_network](podman_bridge.png)
+
+Consider the above illustration. It depicts a laptop user running two containers:
+a web and db instance. These two containers are on the virtual network with the
+host. Additionally, by default, these containers can initiate communications outside
+the laptop (to the Internet for example). The containers on the virtual network
+typically have non-routable, also known as private IP addresses.
+
+When dealing with communication that is being initiated outside the host, the outside
+client typically must address the laptop’s external network interface and given port
+number. Assuming the host allows incoming traffic, the host will know to forward
+the incoming traffic on that port to the specific container. To accomplish this,
+firewall rules are added to forward traffic when a container requests a specific
+port be forwarded.
+
+Bridge networking is the default for Podman containers created as root. Podman provides
+a default bridge network, but you can create others using the `podman network create`
+command. Containers can be joined to a CNI network when they are created with the
+`--network` flag, or after they are created via the `podman network connect` and
+`podman network disconnect` commands.
+
+As mentioned earlier, slirp4netns is the default network configuration for rootless
+users. But as of Podman version 3.0, rootless users can also use CNI networking.
+The user experience of rootless CNI is very akin to a rootfull CNI, except that
+there is no default network configuration provided. You simply need to create a
+network, and the one will be created as a bridge network.
+
+```
+$ podman network create
+```
+
+When rootless containers are run with a CNI networking configuration, a “side-car”
+container for running CNI is also run. Do not remove this container while your rootless
+containers are running. if you remove this container (e.g by accident) all attached
+containers lose network connectivity. In order to restore the network connectivity
+all containers with networks must be restarted. This will automatically recreate
+the "side-car" container. For rootfull containers, there is no “side-car” container
+as rootfull users have the permissions to create and modify network interfaces on
+the host.
+
+#### Example
+
+By default, rootfull containers use the CNI bridge plugin for its default configuration.
+In this case, no network name must be passed to Podman. However, you can create
+additional bridged networks with the podman create command. In that case, you will
+have to set the network name.
+
+The following example shows how to set up a web server and expose it to the network
+outside the host as both rootfull and rootless. It will also show how an outside
+client can connect to the container.
+
+```
+(rootfull) $ sudo podman run -dt --name webserver -p 8080:80 quay.io/libpod/banner
+00f3440c7576aae2d5b193c40513c29c7964e96bf797cf0cc352c2b68ccbe66a
+```
+
+As mentioned earlier, for rootless containers using CNI, a network must first be
+created.
+```
+$ podman network create
+/home/baude/.config/cni/net.d/cni-podman1.conflist
+```
+Now run the container.
+```
+$ podman run -dt --name webserver --net cni-podman1 -p 8081:80 quay.io/libpod/banner
+269fd0d6b2c8ed60f2ca41d7beceec2471d72fb9a33aa8ca45b81dc9a0abbb12
+```
+Note in the above run command, the container’s port 80 (where the Nginx server is
+running) was mapped to the host’s port 8080. Port 8080 was chosen to demonstrate
+how the host and container ports can be mapped for external access. The port could
+very well have been 80 as well (except for rootless users).
+
+To connect from an outside client to the webserver, simply point an HTTP client to
+the host’s IP address at port 8080 for rootfull and port 8081 for rootless.
+```
+(outside_host): $ curl 192.168.99.109:8080
+ ___ __
+ / _ \___ ___/ /_ _ ___ ____
+ / ___/ _ \/ _ / ' \/ _ `/ _ \
+/_/ \___/\_,_/_/_/_/\_,_/_//_/
+
+(outside_host): $ curl 192.168.99.109:8081
+ ___ __
+ / _ \___ ___/ /_ _ ___ ____
+ / ___/ _ \/ _ / ' \/ _ `/ _ \
+/_/ \___/\_,_/_/_/_/\_,_/_//_/
+```
+
+### Macvlan
+
+With macvlan, the container is given access to a physical network interface on the
+host. This interface can configure multiple subinterfaces. And each subinterface
+is capable of having its own MAC and IP address. In the case of Podman containers,
+the container will present itself as if it is on the same network as the host.
+
+![macvlan_network](podman_bridge.png)
+
+In the illustration, outside clients will be able to access the web container by
+its IP address directly. Usually the network information, including IP address,
+is leased from a DHCP server like most other network clients on the network. If
+the laptop is running a firewall, such as firewalld, then accommodations will need
+to be made for proper access.
+
+#### Example
+
+The following example demonstrates how to set up a web container on a macvlan and
+how to access that container from outside the host. First, create the macvlan network.
+ You need to know the network interface on the host that connects to the routable
+network. In the example case, it is eth0.
+```
+$ sudo podman network create -d macvlan -o parent=eth0 webnetwork
+/etc/cni/net.d/webnetwork.conflist
+```
+The next step is to ensure that the DHCP CNI plugin is running. This plugin facilitates
+the DHCP lease from the network.
+```
+$ sudo /usr/libexec/cni/dhcp daemon
+```
+Now run the container and be certain to attach it to the network we created earlier.
+```
+$ sudo podman run -dt --name webserver --network webnetwork quay.io/libpod/banner
+03d82083c434d7e937fc0b87c25401f46ab5050007df403bf988e25e52c5cc40
+[baude@localhost ~]$ sudo podman exec webserver ip address show eth0
+2: eth0@if3: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue state
+UP
+link/ether 0a:3c:e2:eb:87:0f brd ff:ff:ff:ff:ff:ff
+inet 192.168.99.186/24 brd 192.168.99.255 scope global eth0
+valid_lft forever preferred_lft forever
+inet6 fe80::83c:e2ff:feeb:870f/64 scope link
+valid_lft forever preferred_lft forever
+```
+Because the container has a routable IP address (on this network) and is not being
+managed by firewalld, no change to the firewall is needed.
+```
+(outside_host): $ curl http://192.168.99.186
+ ___ __
+ / _ \___ ___/ /_ _ ___ ____
+ / ___/ _ \/ _ / ' \/ _ `/ _ \
+/_/ \___/\_,_/_/_/_/\_,_/_//_/
+```
+
+
+
+### Slirp4netns
+
+Slirp4netns is the default network setup for rootless containers and pods. It was
+invented because unprivileged users are not allowed to make network interfaces on
+the host. Slirp4netns creates a TAP device in the container’s network namespace
+and connects to the usermode TCP/IP stack. Consider the following illustration.
+
+![slirp_network](podman_rootless_default.png)
+
+The unprivileged user on this laptop has created two containers: a DB container and
+a web container. Both of these containers have the ability to access content on
+networks outside the laptop. And outside clients can access the containers if the
+container is bound to a host port and the laptop firewall allows it. Remember, unprivileged
+users must use ports 1024 through 65535 as lower ports require root privileges. (CAP_NET_BIND_SERVICE)
+Note: this can be adjusted using the `sysctl net.ipv4.ip_unprivileged_port_start`
+
+One of the drawbacks of slirp4netns is that the containers are completely isolated
+from each other. Unlike the bridge approach, there is no virtual network. For containers
+to communicate with each other, they can use the port mappings with the host system,
+or they can be put into a Pod where they share the same network namespace. See [Communicating
+between containers and pods](#Communicating-between-containers-and-pods) for more information.
+
+#### Example
+
+The following example will show how two rootless containers can communicate with
+each other where one is a web server. Then it will show how a client on the host’s
+network can communicate with the rootless web server.
+
+First, run the rootless web server and map port 80 from the container to a non-privileged
+port like 8080.
+```
+$ podman run -dt --name webserver -p 8080:80 quay.io/libpod/banner
+17ea33ccd7f55ff45766b3ec596b990a5f2ba66eb9159cb89748a85dc3cebfe0
+```
+Because rootfull containers cannot communicate with each other directly with TCP/IP
+via IP addresses, the host and the port mapping are used. To do so, the IP address
+of the host (interface) must be known.
+```
+$ ip address show eth0
+3: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group
+default qlen 1000
+link/ether 3c:e1:a1:c1:7a:3f brd ff:ff:ff:ff:ff:ff
+altname eth0
+inet 192.168.99.109/24 brd 192.168.99.255 scope global dynamic noprefixroute eth0
+valid_lft 78808sec preferred_lft 78808sec
+inet6 fe80::5632:6f10:9e76:c33/64 scope link noprefixroute
+valid_lft forever preferred_lft forever
+```
+From another rootless container, use the host’s IP address and port to communicate
+between the two rootless containers successfully.
+```
+$ podman run -it quay.io/libpod/banner curl http://192.168.99.109:8080
+ ___ __
+ / _ \___ ___/ /_ _ ___ ____
+ / ___/ _ \/ _ / ' \/ _ `/ _ \
+/_/ \___/\_,_/_/_/_/\_,_/_//_/
+```
+
+From a client outside the host, the IP address and port can also be used:
+```
+(outside_host): $ curl http://192.168.99.109:8080
+ ___ __
+ / _ \___ ___/ /_ _ ___ ____
+ / ___/ _ \/ _ / ' \/ _ `/ _ \
+/_/ \___/\_,_/_/_/_/\_,_/_//_/
+```
+
+## Communicating between containers and pods
+
+Most users of containers have a decent understanding of how containers communicate
+with each other and the rest of the world. Usually each container has its own IP
+address and networking information. They communicate amongst each other using regular
+TCP/IP means like IP addresses or, in many cases, using DNS names often based on
+the container name. But pods are a collection of one or more containers, and with
+that, some uniqueness is inherited.
+
+By definition, all containers in a Podman pod share the same network namespace. This
+fact means that they will have the same IP address, MAC addresses, and port mappings.
+You can conveniently communicate between containers in a pod by using localhost.
+
+![slirp_network](podman_pod.png)
+
+The above illustration describes a Pod on a bridged network. As depicted, the Pod
+has two containers “inside” it: a DB and a Web container. Because they share the
+same network namespace, the DB and Web container can communicate with each other
+using localhost (127.0.0.1). Furthermore, they are also both addressable by the
+IP address (and DNS name if applicable) assigned to the Pod itself.
+
+For more information on container to container networking, see Configuring container
+networking with Podman.
diff --git a/docs/tutorials/podman_bridge.png b/docs/tutorials/podman_bridge.png
new file mode 100644
index 000000000..c0cb88951
--- /dev/null
+++ b/docs/tutorials/podman_bridge.png
Binary files differ
diff --git a/docs/tutorials/podman_macvlan.png b/docs/tutorials/podman_macvlan.png
new file mode 100644
index 000000000..04d9c9080
--- /dev/null
+++ b/docs/tutorials/podman_macvlan.png
Binary files differ
diff --git a/docs/tutorials/podman_pod.png b/docs/tutorials/podman_pod.png
new file mode 100644
index 000000000..17718e20b
--- /dev/null
+++ b/docs/tutorials/podman_pod.png
Binary files differ
diff --git a/docs/tutorials/podman_rootless_default.png b/docs/tutorials/podman_rootless_default.png
new file mode 100644
index 000000000..5d4a66644
--- /dev/null
+++ b/docs/tutorials/podman_rootless_default.png
Binary files differ
diff --git a/pkg/domain/infra/abi/archive.go b/pkg/domain/infra/abi/archive.go
index c64dfb02a..f38f5c132 100644
--- a/pkg/domain/infra/abi/archive.go
+++ b/pkg/domain/infra/abi/archive.go
@@ -3,6 +3,7 @@ package abi
import (
"context"
"io"
+ "path/filepath"
"strings"
buildahCopiah "github.com/containers/buildah/copier"
@@ -93,7 +94,7 @@ func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID
containerPath = "/."
}
- _, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerMountPoint, containerPath)
+ statInfo, resolvedRoot, resolvedContainerPath, err := ic.containerStat(container, containerMountPoint, containerPath)
if err != nil {
unmount()
return nil, err
@@ -110,8 +111,8 @@ func (ic *ContainerEngine) ContainerCopyToArchive(ctx context.Context, nameOrID
return func() error {
defer container.Unmount(false)
getOptions := buildahCopiah.GetOptions{
- // Unless the specified path ends with ".", we want to copy the base directory.
- KeepDirectoryNames: !strings.HasSuffix(resolvedContainerPath, "."),
+ // Unless the specified points to ".", we want to copy the base directory.
+ KeepDirectoryNames: statInfo.IsDir && filepath.Base(containerPath) != ".",
UIDMap: idMappings.UIDMap,
GIDMap: idMappings.GIDMap,
ChownDirs: idPair,
diff --git a/pkg/rootless/rootless_linux.c b/pkg/rootless/rootless_linux.c
index 2e1fddc48..d588d848b 100644
--- a/pkg/rootless/rootless_linux.c
+++ b/pkg/rootless/rootless_linux.c
@@ -196,7 +196,11 @@ can_use_shortcut ()
return false;
if (strstr (argv[0], "podman") == NULL)
- return false;
+ {
+ free (argv[0]);
+ free (argv);
+ return false;
+ }
for (argc = 0; argv[argc]; argc++)
{
diff --git a/test/e2e/run_memory_test.go b/test/e2e/run_memory_test.go
index ad3a2b54f..8371d3cae 100644
--- a/test/e2e/run_memory_test.go
+++ b/test/e2e/run_memory_test.go
@@ -2,6 +2,7 @@ package integration
import (
"os"
+ "strconv"
. "github.com/containers/podman/v2/test/utils"
. "github.com/onsi/ginkgo"
@@ -90,4 +91,27 @@ var _ = Describe("Podman run memory", func() {
Expect(session.ExitCode()).To(Equal(0))
Expect(session.OutputToString()).To(Equal("41943040"))
})
+
+ It("podman run kernel-memory test", func() {
+ if podmanTest.Host.Distribution == "ubuntu" {
+ Skip("Unable to perform test on Ubuntu distributions due to memory management")
+ }
+ var session *PodmanSessionIntegration
+ if CGROUPSV2 {
+ session = podmanTest.Podman([]string{"run", "--memory", "256m", "--memory-swap", "-1", ALPINE, "cat", "/sys/fs/cgroup/memory.swap.max"})
+ } else {
+ session = podmanTest.Podman([]string{"run", "--cgroupns=private", ALPINE, "cat", "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes"})
+ }
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(BeZero())
+ output := session.OutputToString()
+ Expect(err).To(BeNil())
+ if CGROUPSV2 {
+ Expect(output).To(Equal("max"))
+ } else {
+ crazyHighNumber, err := strconv.ParseInt(output, 10, 64)
+ Expect(err).To(BeZero())
+ Expect(crazyHighNumber).To(BeNumerically(">", 936854771712))
+ }
+ })
})
diff --git a/test/system/065-cp.bats b/test/system/065-cp.bats
index d3cf1c274..0fcc437d4 100644
--- a/test/system/065-cp.bats
+++ b/test/system/065-cp.bats
@@ -18,6 +18,8 @@ load helpers
echo "${randomcontent[0]}" > $srcdir/hostfile0
echo "${randomcontent[1]}" > $srcdir/hostfile1
echo "${randomcontent[2]}" > $srcdir/hostfile2
+ mkdir -p $srcdir/subdir
+ echo "${randomcontent[2]}" > $srcdir/subdir/dotfile.
run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity
run_podman exec cpcontainer mkdir /srv/subdir
@@ -50,6 +52,11 @@ load helpers
is "$output" "${randomcontent[$id]}" "$description (cp -> ctr:$dest)"
done < <(parse_table "$tests")
+ # Dots are special for dirs not files.
+ run_podman cp $srcdir/subdir/dotfile. cpcontainer:/tmp
+ run_podman exec cpcontainer cat /tmp/dotfile.
+ is "$output" "${randomcontent[2]}" "$description (cp -> ctr:$dest)"
+
# Host path does not exist.
run_podman 125 cp $srcdir/IdoNotExist cpcontainer:/tmp
is "$output" 'Error: ".*/IdoNotExist" could not be found on the host' \
@@ -76,12 +83,14 @@ load helpers
)
run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity
run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/containerfile"
+ run_podman exec cpcontainer sh -c "echo ${randomcontent[0]} > /tmp/dotfile."
run_podman exec cpcontainer sh -c "echo ${randomcontent[1]} > /srv/containerfile1"
run_podman exec cpcontainer sh -c "mkdir /srv/subdir; echo ${randomcontent[2]} > /srv/subdir/containerfile2"
# format is: <id> | <source arg to cp> | <destination arg (appended to $srcdir) to cp> | <full dest path (appended to $srcdir)> | <test name>
tests="
0 | /tmp/containerfile | | /containerfile | copy to srcdir/
+0 | /tmp/dotfile. | | /dotfile. | copy to srcdir/
0 | /tmp/containerfile | / | /containerfile | copy to srcdir/
0 | /tmp/containerfile | /. | /containerfile | copy to srcdir/.
0 | /tmp/containerfile | /newfile | /newfile | copy to srcdir/newfile
@@ -117,12 +126,18 @@ load helpers
echo "${randomcontent[0]}" > $srcdir/hostfile0
echo "${randomcontent[1]}" > $srcdir/hostfile1
+ # "." and "dir/." will copy the contents, so make sure that a dir ending
+ # with dot is treated correctly.
+ mkdir -p $srcdir.
+ cp $srcdir/* $srcdir./
+
run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity
run_podman exec cpcontainer mkdir /srv/subdir
# format is: <source arg to cp (appended to srcdir)> | <destination arg to cp> | <full dest path> | <test name>
tests="
| / | /dir-test | copy to root
+ . | / | /dir-test. | copy dotdir to root
/ | /tmp | /tmp/dir-test | copy to tmp
/. | /usr/ | /usr/ | copy contents of dir to usr/
| . | /srv/dir-test | copy to workdir (rel path)
@@ -153,6 +168,9 @@ load helpers
run_podman run -d --name cpcontainer --workdir=/srv $IMAGE sleep infinity
run_podman exec cpcontainer sh -c 'mkdir /srv/subdir; echo "This first file is on the container" > /srv/subdir/containerfile1'
run_podman exec cpcontainer sh -c 'echo "This second file is on the container as well" > /srv/subdir/containerfile2'
+ # "." and "dir/." will copy the contents, so make sure that a dir ending
+ # with dot is treated correctly.
+ run_podman exec cpcontainer sh -c 'mkdir /tmp/subdir.; cp /srv/subdir/* /tmp/subdir./'
run_podman cp cpcontainer:/srv $srcdir
run cat $srcdir/srv/subdir/containerfile1
@@ -174,6 +192,14 @@ load helpers
is "$output" "This first file is on the container"
run cat $srcdir/containerfile2
is "$output" "This second file is on the container as well"
+ rm -rf $srcdir/subdir
+
+ run_podman cp cpcontainer:/tmp/subdir. $srcdir
+ run cat $srcdir/subdir./containerfile1
+ is "$output" "This first file is on the container"
+ run cat $srcdir/subdir./containerfile2
+ is "$output" "This second file is on the container as well"
+ rm -rf $srcdir/subdir.
run_podman rm -f cpcontainer
}