diff options
-rwxr-xr-x | contrib/cirrus/runner.sh | 7 | ||||
-rw-r--r-- | docs/source/Tutorials.rst | 2 | ||||
-rw-r--r-- | docs/source/includes.rst | 2 | ||||
-rw-r--r-- | docs/tutorials/README.md | 6 | ||||
-rw-r--r-- | docs/tutorials/podman-go-bindings.md | 543 | ||||
-rw-r--r-- | libpod/networking_linux.go | 102 | ||||
-rw-r--r-- | pkg/bindings/README.md | 77 |
7 files changed, 163 insertions, 576 deletions
diff --git a/contrib/cirrus/runner.sh b/contrib/cirrus/runner.sh index cac679466..da43ffb0a 100755 --- a/contrib/cirrus/runner.sh +++ b/contrib/cirrus/runner.sh @@ -173,7 +173,7 @@ function _run_swagger() { trap "rm -f $envvarsfile" EXIT # contains secrets # Warning: These values must _not_ be quoted, podman will not remove them. #shellcheck disable=SC2154 - cat <<eof>>$envvarsfile + cat <<eof >>$envvarsfile GCPJSON=$GCPJSON GCPNAME=$GCPNAME GCPPROJECT=$GCPPROJECT @@ -336,6 +336,11 @@ msg "************************************************************" # shellcheck disable=SC2154 if [[ "$PRIV_NAME" == "rootless" ]] && [[ "$UID" -eq 0 ]]; then + # Remove /var/lib/cni, it is not required for rootless cni. + # We have to test that it works without this directory. + # https://github.com/containers/podman/issues/10857 + rm -rf /var/lib/cni + req_env_vars ROOTLESS_USER msg "Re-executing runner through ssh as user '$ROOTLESS_USER'" msg "************************************************************" diff --git a/docs/source/Tutorials.rst b/docs/source/Tutorials.rst index e48d1e853..cb9ab644d 100644 --- a/docs/source/Tutorials.rst +++ b/docs/source/Tutorials.rst @@ -10,5 +10,5 @@ Here are a number of useful tutorials to get you up and running with Podman. If * `How to sign and distribute container images using Podman <https://github.com/containers/podman/blob/master/docs/tutorials/image_signing.md>`_: Learn how to setup and use image signing with Podman. * `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. +* `How to use Podman's Go RESTful bindings <https://github.com/containers/podman/tree/main/pkg/bindings>`_: An introduction to using our RESTful Golang bindings in an external application. * `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/source/includes.rst b/docs/source/includes.rst index 8d3b6e2db..6e04d77f4 100644 --- a/docs/source/includes.rst +++ b/docs/source/includes.rst @@ -16,4 +16,4 @@ .. _podman run: http://docs.podman.io/en/latest/markdown/podman-run.1.html .. _podman build: http://docs.podman.io/en/latest/markdown/podman-build.1.html .. _podman push: http://docs.podman.io/en/latest/markdown/podman-push.1.html -.. image:: https://github.com/containers/podman/blob/master/logo/podman-logo.png?raw=true +.. image:: https://github.com/containers/podman/blob/main/logo/podman-logo.png?raw=true diff --git a/docs/tutorials/README.md b/docs/tutorials/README.md index 455459062..2a3c85c55 100644 --- a/docs/tutorials/README.md +++ b/docs/tutorials/README.md @@ -28,10 +28,6 @@ How the libpod API can be used within your own project. 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)** +**[Basic Networking](basic_networking.md)** A basic guide to common network setups with Podman diff --git a/docs/tutorials/podman-go-bindings.md b/docs/tutorials/podman-go-bindings.md deleted file mode 100644 index 2bbf4e5de..000000000 --- a/docs/tutorials/podman-go-bindings.md +++ /dev/null @@ -1,543 +0,0 @@ -![PODMAN logo](../../logo/podman-logo-source.svg) - -# Podman Go bindings - -## Introduction - -In the release of Podman 2.0, we removed the experimental tag -from its recently introduced RESTful service. While it might -be interesting to interact with a RESTFul server using curl, -using a set of Go based bindings is probably a more direct -route to a production ready application. Let’s take a look -at how easily that can be accomplished. - -If you haven't yet, [install Go](https://golang.org/doc/install). - -Be careful to double-check that the version of golang is new -enough (i.e. `go version`), version 1.13.x or higher is -supported. If needed, Go sources and binaries can be fetched -from the [official Go website](https://golang.org/dl/). - -The Podman Go bindings are a set of functions to allow -developers to execute Podman operations from within their Go -based application. The Go bindings connect to a Podman service -which can run locally or on a remote machine. You can perform -many operations including pulling and listing images, starting, -stopping or inspecting containers. Currently, the Podman -repository has bindings available for operations on images, -containers, pods, networks and manifests among others. The -bindings are available on the [v2.0 branch in the -upstream Podman repository](https://github.com/containers/podman/tree/v2.0). -You can fetch the bindings for your application using Go modules: - -```bash -$ cd $HOME -$ mkdir example && cd example -$ go mod init example.com -go: creating new go.mod: module example.com -$ go get github.com/containers/podman/v3 -[...] -``` - -This creates a new `go.mod` file in the current directory that looks as follows: - -```bash -module example.com - -go 1.16 - -require github.com/containers/libpod/v3 v3.0.1 // indirect -``` - -You can also try a demo application with the Go modules created already: - -```bash -$ git clone https://github.com/containers/Demos -$ cd Demos/podman_go_bindings -$ ls -README.md go.mod go.sum main.go -``` - - -## How do I use them - -In this tutorial, you will learn through basic examples how to: - -0. [Start the Podman system service](#start-service) -1. [Connect to the Podman system service](#connect-service) -2. [Pull images](#pull-images) -3. [List images](#list-images) -4. [Create and start a container from an image](#create-start-container) -5. [List containers](#list-containers) -6. [Inspect the container](#inspect-container) -7. [Stop the container](#stop-container) -8. [Debugging tips](#debugging-tips) - - -### Start the Podman system service <a name="start-service"></a> -The recommended way to start Podman system service in production mode -is via systemd socket-activation: - -```bash -$ systemctl --user start podman.socket -``` - -There’s no timeout specified when starting the system service via socket-activation. - -For purposes of this demo, we will start the service using the Podman -command itself. If you prefer the system service to timeout after, say, -5000 seconds, you can run it like so: - -```bash -$ podman system service -t 5000 -``` - -Note that the 5000 seconds uptime is refreshed after every command is received. -If you want the service to stay up until the machine is shutdown or the process -is terminated, use `0` (zero) instead of 5000. For this demo, we will use no timeout: - -```bash -$ podman system service -t 0 -``` - - -Open another terminal window and check if the Podman socket exists: - -```bash -$ ls /run/user/${UID}/podman -podman.sock -``` - -If you’re running the system service as root, podman.sock will be found in /run/podman: -```bash -# ls /run/podman -podman.sock -``` - - -### Connect to the Podman system service <a name="connect-service"></a> -First, you need to create a connection that connects to the system service. -The critical piece of information for setting up a new connection is the endpoint. -The endpoint comes in the form of an URI (method:/path/to/socket). For example, -to connect to the local rootful socket the URI would be `unix:/run/podman/podman.sock` -and for a rootless user it would be `unix:$(XDG_RUNTIME_DIR)/podman/podman.sock`, -typically: `unix:/run/user/${UID}/podman/podman.sock`. - - -The following Go example snippet shows how to set up a connection for a rootless user. -```Go -package main - -import ( - "context" - "fmt" - "os" - - "github.com/containers/libpod/v3/libpod/define" - "github.com/containers/libpod/v3/pkg/bindings" - "github.com/containers/libpod/v3/pkg/bindings/containers" - "github.com/containers/libpod/v3/pkg/bindings/images" - "github.com/containers/libpod/v3/pkg/domain/entities" - "github.com/containers/libpod/v3/pkg/specgen" -) - -func main() { - fmt.Println("Welcome to the Podman Go bindings tutorial") - - // Get Podman socket location - sock_dir := os.Getenv("XDG_RUNTIME_DIR") - socket := "unix:" + sock_dir + "/podman/podman.sock" - - // Connect to Podman socket - connText, err := bindings.NewConnection(context.Background(), socket) - if err != nil { - fmt.Println(err) - os.Exit(1) - } -} -``` - -The `connText` variable received from the NewConnection function is of type -context.Context(). In subsequent uses of the bindings, you will use this context -to direct the bindings to your connection. This can be seen in the examples below. - -### Pull an image <a name="pull-images"></a> - -Next, we will pull a couple of images using the images.Pull() binding. -This binding takes three arguments: - - The context variable created by the bindings.NewConnection() call in the first example - - The image name - - Options for image pull - -**Append the following lines to your function:** - -```Go - // Pull Busybox image (Sample 1) - fmt.Println("Pulling Busybox image...") - _, err = images.Pull(connText, "docker.io/busybox", &images.PullOptions{}) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - // Pull Fedora image (Sample 2) - rawImage := "registry.fedoraproject.org/fedora:latest" - fmt.Println("Pulling Fedora image...") - _, err = images.Pull(connText, rawImage, &images.PullOptions{}) - if err != nil { - fmt.Println(err) - os.Exit(1) - } -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling Busybox image... -Pulling Fedora image... -$ -``` - -The system service side should echo messages like so: - -```bash -Trying to pull docker.io/busybox... -Getting image source signatures -Copying blob 61c5ed1cbdf8 [--------------------------------------] 0.0b / 0.0b -Copying config 018c9d7b79 done -Writing manifest to image destination -Storing signatures -Trying to pull registry.fedoraproject.org/fedora:latest... -Getting image source signatures -Copying blob dd9f43919ba0 [--------------------------------------] 0.0b / 0.0b -Copying config 00ff39a8bf done -Writing manifest to image destination -Storing signatures -``` - - -### List images <a name="list-images"></a> -Next, we will pull an image using the images.List() binding. -This binding takes three arguments: - - The context variable created earlier - - An optional bool 'all' - - An optional map of filters - -**Append the following lines to your function:** - -```Go - // List images - imageSummary, err := images.List(connText, &images.ListOptions{}) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - var names []string - for _, i := range imageSummary { - names = append(names, i.RepoTags...) - } - fmt.Println("Listing images...") - fmt.Println(names) -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling Busybox image... -Pulling Fedora image... -Listing images... -[docker.io/library/busybox:latest registry.fedoraproject.org/fedora:latest] -$ -``` - - -### Create and Start a Container from an Image <a name="create-start-container"></a> - -To create the container spec, we use specgen.NewSpecGenerator() followed by -calling containers.CreateWithSpec() to actually create a new container. -specgen.NewSpecGenerator() takes 2 arguments: - - name of the image - - whether it's a rootfs - -containers.CreateWithSpec() takes 2 arguments: - - the context created earlier - - the spec created by NewSpecGenerator - -Next, the container is actually started using the containers.Start() binding. -containers.Start() takes three arguments: - - the context - - the name or ID of the container created - - an optional parameter for detach keys - -After the container is started, it's a good idea to ensure the container is -in a running state before you proceed with further operations. -The containers.Wait() takes care of that. -containers.Wait() takes three arguments: - - the context - - the name or ID of the container created - - container state (running/paused/stopped) - -**Append the following lines to your function:** - -```Go - // Container create - s := specgen.NewSpecGenerator(rawImage, false) - s.Terminal = true - r, err := containers.CreateWithSpec(connText, s, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - // Container start - fmt.Println("Starting Fedora container...") - err = containers.Start(connText, r.ID, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - - running := define.ContainerStateRunning - _, err = containers.Wait(connText, r.ID, &containers.WaitOptions{Condition: []define.ContainerStatus{running}}) - if err != nil { - fmt.Println(err) - os.Exit(1) - } -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling image... -Starting Fedora container... -$ -``` - -Check if the container is running: - -```bash -$ podman ps -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -665831d31e90 registry.fedoraproject.org/fedora:latest /bin/bash Less than a second ago Up Less than a second ago dazzling_mclean -$ -``` - - -### List Containers <a name="list-containers"></a> - -Containers can be listed using the containers.List() binding. -containers.List() takes seven arguments: - - the context - - output filters - - boolean to show all containers, by default only running containers are listed - - number of latest created containers, all states (running/paused/stopped) - - boolean to print pod information - - boolean to print rootfs size - - boolean to print oci runtime and container state - -**Append the following lines to your function:** - -```Go - // Container list - var latestContainers = 1 - containerLatestList, err := containers.List(connText, &containers.ListOptions{Last: &latestContainers}) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - fmt.Printf("Latest container is %s\n", containerLatestList[0].Names[0]) -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling Busybox image... -Pulling Fedora image... -Listing images... -[docker.io/library/busybox:latest registry.fedoraproject.org/fedora:latest] -Starting Fedora container... -Latest container is dazzling_mclean -$ -``` - - -### Inspect Container <a name="inspect-container"></a> -Containers can be inspected using the containers.Inspect() binding. -containers.Inspect() takes 3 arguments: - - context - - image name or ID - - optional boolean to check for container size - - -**Append the following lines to your function:** - -```Go - // Container inspect - ctrData, err := containers.Inspect(connText, r.ID, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - fmt.Printf("Container uses image %s\n", ctrData.ImageName) - fmt.Printf("Container running status is %s\n", ctrData.State.Status) -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling Busybox image... -Pulling Fedora image... -Listing images... -[docker.io/library/busybox:latest registry.fedoraproject.org/fedora:latest] -Starting Fedora container... -Latest container is peaceful_noether -Fedora Container uses image registry.fedoraproject.org/fedora:latest -Fedora Container running status is running -$ -``` - - -### Stop Container <a name="stop-container"></a> - -A container can be stopped by the containers.Stop() binding. -containers.Stop() takes 3 arguments: - - context - - image name or ID - - optional timeout - -**Append the following lines to your function:** - -```Go - // Container stop - fmt.Println("Stopping the container...") - err = containers.Stop(connText, r.ID, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - ctrData, err = containers.Inspect(connText, r.ID, nil) - if err != nil { - fmt.Println(err) - os.Exit(1) - } - fmt.Printf("Container running status is now %s\n", ctrData.State.Status) -``` - -**Run it:** - -```bash -$ go run main.go -Welcome to the Podman Go bindings tutorial -Pulling Busybox image... -Pulling Fedora image... -Listing images... -[docker.io/library/busybox:latest registry.fedoraproject.org/fedora:latest] -Starting Fedora container... -Latest container is peaceful_noether -Fedora Container uses image registry.fedoraproject.org/fedora:latest -Fedora Container running status is running -Stopping Fedora container... -Container running status is now exited -``` - - -### Debugging tips <a name="debugging-tips"></a> - -To debug in a development setup, you can start the Podman system service -in debug mode like so: - -```bash -$ podman --log-level=debug system service -t 0 -``` - -The `--log-level=debug` echoes all the logged requests and is useful to -trace the execution path at a finer granularity. A snippet of a sample run looks like: - -```bash -INFO[0000] podman filtering at log level debug -DEBU[0000] Called service.PersistentPreRunE(podman --log-level=debug system service -t0) -DEBU[0000] Ignoring libpod.conf EventsLogger setting "/home/lsm5/.config/containers/containers.conf". Use "journald" if you want to change this setting and remove libpod.conf files. -DEBU[0000] Reading configuration file "/usr/share/containers/containers.conf" -DEBU[0000] Merged system config "/usr/share/containers/containers.conf": {Editors note: the remainder of this line was removed due to Jekyll formatting errors.} -DEBU[0000] Using conmon: "/usr/bin/conmon" -DEBU[0000] Initializing boltdb state at /home/lsm5/.local/share/containers/storage/libpod/bolt_state.db -DEBU[0000] Overriding run root "/run/user/1000/containers" with "/run/user/1000" from database -DEBU[0000] Using graph driver overlay -DEBU[0000] Using graph root /home/lsm5/.local/share/containers/storage -DEBU[0000] Using run root /run/user/1000 -DEBU[0000] Using static dir /home/lsm5/.local/share/containers/storage/libpod -DEBU[0000] Using tmp dir /run/user/1000/libpod/tmp -DEBU[0000] Using volume path /home/lsm5/.local/share/containers/storage/volumes -DEBU[0000] Set libpod namespace to "" -DEBU[0000] Not configuring container store -DEBU[0000] Initializing event backend file -DEBU[0000] using runtime "/usr/bin/runc" -DEBU[0000] using runtime "/usr/bin/crun" -WARN[0000] Error initializing configured OCI runtime kata: no valid executable found for OCI runtime kata: invalid argument -DEBU[0000] using runtime "/usr/bin/crun" -INFO[0000] Setting parallel job count to 25 -INFO[0000] podman filtering at log level debug -DEBU[0000] Called service.PersistentPreRunE(podman --log-level=debug system service -t0) -DEBU[0000] Ignoring libpod.conf EventsLogger setting "/home/lsm5/.config/containers/containers.conf". Use "journald" if you want to change this setting and remove libpod.conf files. -DEBU[0000] Reading configuration file "/usr/share/containers/containers.conf" -``` - -If the Podman system service has been started via systemd socket activation, -you can view the logs using journalctl. The logs after a sample run look like so: - -```bash -$ journalctl --user --no-pager -u podman.socket --- Reboot -- -Jul 22 13:50:40 nagato.nanadai.me systemd[1048]: Listening on Podman API Socket. -$ -``` - -```bash -$ journalctl --user --no-pager -u podman.service -Jul 22 13:50:53 nagato.nanadai.me systemd[1048]: Starting Podman API Service... -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 38480630a8bdaa3e1a0ebd34c94038591b0d7ad994b37be5b4f2072bb6ef0879: error acquiring lock 0 for volume 38480630a8bdaa3e1a0ebd34c94038591b0d7ad994b37be5b4f2072bb6ef0879: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 47d410af4d762a0cc456a89e58f759937146fa3be32b5e95a698a1d4069f4024: error acquiring lock 0 for volume 47d410af4d762a0cc456a89e58f759937146fa3be32b5e95a698a1d4069f4024: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 86e73f082e344dad38c8792fb86b2017c4f133f2a8db87f239d1d28a78cf0868: error acquiring lock 0 for volume 86e73f082e344dad38c8792fb86b2017c4f133f2a8db87f239d1d28a78cf0868: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 9a16ea764be490a5563e384d9074ab0495e4d9119be380c664037d6cf1215631: error acquiring lock 0 for volume 9a16ea764be490a5563e384d9074ab0495e4d9119be380c664037d6cf1215631: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume bfd6b2a97217f8655add13e0ad3f6b8e1c79bc1519b7a1e15361a107ccf57fc0: error acquiring lock 0 for volume bfd6b2a97217f8655add13e0ad3f6b8e1c79bc1519b7a1e15361a107ccf57fc0: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume f9b9f630982452ebcbed24bd229b142fbeecd5d4c85791fca440b21d56fef563: error acquiring lock 0 for volume f9b9f630982452ebcbed24bd229b142fbeecd5d4c85791fca440b21d56fef563: file exists" -Jul 22 13:50:54 nagato.nanadai.me podman[1527]: Trying to pull registry.fedoraproject.org/fedora:latest... -Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Getting image source signatures -Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Copying blob sha256:dd9f43919ba05f05d4f783c31e83e5e776c4f5d29dd72b9ec5056b9576c10053 -Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Copying config sha256:00ff39a8bf19f810a7e641f7eb3ddc47635913a19c4996debd91fafb6b379069 -Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Writing manifest to image destination -Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Storing signatures -Jul 22 13:50:55 nagato.nanadai.me systemd[1048]: podman.service: unit configures an IP firewall, but not running as root. -Jul 22 13:50:55 nagato.nanadai.me systemd[1048]: (This warning is only shown for the first unit using IP firewalling.) -Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: podman.service: Succeeded. -Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: Finished Podman API Service. -Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: podman.service: Consumed 1.339s CPU time. -$ -``` - - -## Wrap Up -Podman provides a set of Go bindings to allow developers to integrate Podman -functionality conveniently in their Go application. These Go bindings require -the Podman system service to be running in the background and this can easily -be achieved using systemd socket activation. Once set up, you are able to use a -set of Go based bindings to create, maintain and monitor your container images, -containers and pods in a way which fits very nicely in many production environments. - - -## References -- Podman is available for most major distributions along with macOS and Windows. -Installation details are available on the [Podman official website](https://podman.io/getting-started/). - -- Documentation can be found at the [Podman Docs page](https://docs.podman.io). -It also includes a section on the [RESTful API](https://docs.podman.io/en/latest/Reference.html). diff --git a/libpod/networking_linux.go b/libpod/networking_linux.go index 1b775a4f3..48b0c495c 100644 --- a/libpod/networking_linux.go +++ b/libpod/networking_linux.go @@ -111,13 +111,33 @@ type RootlessCNI struct { lock lockfile.Locker } +// getPath will join the given path to the rootless cni dir +func (r *RootlessCNI) getPath(path string) string { + return filepath.Join(r.dir, path) +} + +// Do - run the given function in the rootless cni ns. +// It does not lock the rootlessCNI lock, the caller +// should only lock when needed, e.g. for cni operations. func (r *RootlessCNI) Do(toRun func() error) error { err := r.ns.Do(func(_ ns.NetNS) error { - // before we can run the given function - // we have to setup all mounts correctly - - // create a new mount namespace - // this should happen inside the netns thread + // Before we can run the given function, + // we have to setup all mounts correctly. + + // The order of the mounts is IMPORTANT. + // The idea of the extra mount ns is to make /run and /var/lib/cni writeable + // for the cni plugins but not affecting the podman user namespace. + // Because the plugins also need access to XDG_RUNTIME_DIR/netns some special setup is needed. + + // The following bind mounts are needed + // 1. XDG_RUNTIME_DIR/netns -> XDG_RUNTIME_DIR/rootless-cni/XDG_RUNTIME_DIR/netns + // 2. /run/systemd -> XDG_RUNTIME_DIR/rootless-cni/run/systemd (only if it exists) + // 3. XDG_RUNTIME_DIR/rootless-cni/resolv.conf -> /etc/resolv.conf or XDG_RUNTIME_DIR/rootless-cni/run/symlink/target + // 4. XDG_RUNTIME_DIR/rootless-cni/var/lib/cni -> /var/lib/cni (if /var/lib/cni does not exists use the parent dir) + // 5. XDG_RUNTIME_DIR/rootless-cni/run -> /run + + // Create a new mount namespace, + // this must happen inside the netns thread. err := unix.Unshare(unix.CLONE_NEWNS) if err != nil { return errors.Wrapf(err, "cannot create a new mount namespace") @@ -127,33 +147,55 @@ func (r *RootlessCNI) Do(toRun func() error) error { if err != nil { return errors.Wrap(err, "could not get network namespace directory") } - newNetNsDir := filepath.Join(r.dir, netNsDir) - // mount the netns into the new run to keep them accessible - // otherwise cni setup will fail because it cannot access the netns files + newNetNsDir := r.getPath(netNsDir) + // 1. Mount the netns into the new run to keep them accessible. + // Otherwise cni setup will fail because it cannot access the netns files. err = unix.Mount(netNsDir, newNetNsDir, "none", unix.MS_BIND|unix.MS_SHARED|unix.MS_REC, "") if err != nil { return errors.Wrap(err, "failed to mount netns directory for rootless cni") } - // mount resolv.conf to make use of the host dns - err = unix.Mount(filepath.Join(r.dir, "resolv.conf"), "/etc/resolv.conf", "none", unix.MS_BIND, "") - if err != nil { - return errors.Wrap(err, "failed to mount resolv.conf for rootless cni") - } - - // also keep /run/systemd if it exists - // many files are symlinked into this dir, for example /dev/log + // 2. Also keep /run/systemd if it exists. + // Many files are symlinked into this dir, for example /dev/log. runSystemd := "/run/systemd" _, err = os.Stat(runSystemd) if err == nil { - newRunSystemd := filepath.Join(r.dir, runSystemd[1:]) + newRunSystemd := r.getPath(runSystemd) err = unix.Mount(runSystemd, newRunSystemd, "none", unix.MS_BIND|unix.MS_REC, "") if err != nil { return errors.Wrap(err, "failed to mount /run/systemd directory for rootless cni") } } - // cni plugins need access to /var/lib/cni and /run + // 3. On some distros /etc/resolv.conf is symlinked to somewhere under /run. + // Because the kernel will follow the symlink before mounting, it is not + // possible to mount a file at /etc/resolv.conf. We have to ensure that + // the link target will be available in the mount ns. + // see: https://github.com/containers/podman/issues/10855 + resolvePath := "/etc/resolv.conf" + resolvePath, err = filepath.EvalSymlinks(resolvePath) + if err != nil { + return err + } + if strings.HasPrefix(resolvePath, "/run/") { + resolvePath = r.getPath(resolvePath) + err = os.MkdirAll(filepath.Dir(resolvePath), 0700) + if err != nil { + return errors.Wrap(err, "failed to create rootless-cni resolv.conf directory") + } + // we want to bind mount on this file so we have to create the file first + _, err = os.OpenFile(resolvePath, os.O_CREATE|os.O_RDONLY, 0700) + if err != nil { + return errors.Wrap(err, "failed to create rootless-cni resolv.conf file") + } + } + // mount resolv.conf to make use of the host dns + err = unix.Mount(r.getPath("resolv.conf"), resolvePath, "none", unix.MS_BIND, "") + if err != nil { + return errors.Wrap(err, "failed to mount resolv.conf for rootless cni") + } + + // 4. CNI plugins need access to /var/lib/cni and /run varDir := "" varTarget := persistentCNIDir // we can only mount to a target dir which exists, check /var/lib/cni recursively @@ -161,10 +203,10 @@ func (r *RootlessCNI) Do(toRun func() error) error { // configs under /var/custom and this would break for { if _, err := os.Stat(varTarget); err == nil { - varDir = filepath.Join(r.dir, strings.TrimPrefix(varTarget, "/")) + varDir = r.getPath(varTarget) break } - varTarget = filepath.Base(varTarget) + varTarget = filepath.Dir(varTarget) if varTarget == "/" { break } @@ -177,8 +219,9 @@ func (r *RootlessCNI) Do(toRun func() error) error { if err != nil { return errors.Wrapf(err, "failed to mount %s for rootless cni", varTarget) } - runDir := filepath.Join(r.dir, "run") - // recursive mount to keep the netns mount + + // 5. Mount the new prepared run dir to /run, it has to be recursive to keep the other bind mounts. + runDir := r.getPath("run") err = unix.Mount(runDir, "/run", "none", unix.MS_BIND|unix.MS_REC, "") if err != nil { return errors.Wrap(err, "failed to mount /run for rootless cni") @@ -221,7 +264,7 @@ func (r *RootlessCNI) Cleanup(runtime *Runtime) error { // make sure the the cni results (cache) dir is empty // libpod instances with another root dir are not covered by the check above // this allows several libpod instances to use the same rootless cni ns - contents, err := ioutil.ReadDir(filepath.Join(r.dir, "var/lib/cni/results")) + contents, err := ioutil.ReadDir(r.getPath("var/lib/cni/results")) if (err == nil && len(contents) == 0) || os.IsNotExist(err) { logrus.Debug("Cleaning up rootless cni namespace") err = netns.UnmountNS(r.ns) @@ -233,7 +276,7 @@ func (r *RootlessCNI) Cleanup(runtime *Runtime) error { if err != nil { logrus.Error(err) } - b, err := ioutil.ReadFile(filepath.Join(r.dir, "rootless-cni-slirp4netns.pid")) + b, err := ioutil.ReadFile(r.getPath("rootless-cni-slirp4netns.pid")) if err == nil { var i int i, err = strconv.Atoi(string(b)) @@ -395,10 +438,15 @@ func (r *Runtime) GetRootlessCNINetNs(new bool) (*RootlessCNI, error) { if err != nil { return nil, err } + conf, err = resolvconf.FilterResolvDNS(conf.Content, netOptions.enableIPv6, true) + if err != nil { + return nil, err + } searchDomains := resolvconf.GetSearchDomains(conf.Content) dnsOptions := resolvconf.GetOptions(conf.Content) + nameServers := resolvconf.GetNameservers(conf.Content) - _, err = resolvconf.Build(filepath.Join(cniDir, "resolv.conf"), []string{resolveIP.String()}, searchDomains, dnsOptions) + _, err = resolvconf.Build(filepath.Join(cniDir, "resolv.conf"), append([]string{resolveIP.String()}, nameServers...), searchDomains, dnsOptions) if err != nil { return nil, errors.Wrap(err, "failed to create rootless cni resolv.conf") } @@ -500,7 +548,9 @@ func (r *Runtime) setUpOCICNIPod(podNetwork ocicni.PodNetwork) ([]ocicni.NetResu // rootlessCNINS is nil if we are root if rootlessCNINS != nil { // execute the cni setup in the rootless net ns + rootlessCNINS.lock.Lock() err = rootlessCNINS.Do(setUpPod) + rootlessCNINS.lock.Unlock() } else { err = setUpPod() } @@ -712,7 +762,9 @@ func (r *Runtime) teardownOCICNIPod(podNetwork ocicni.PodNetwork) error { // rootlessCNINS is nil if we are root if rootlessCNINS != nil { // execute the cni setup in the rootless net ns + rootlessCNINS.lock.Lock() err = rootlessCNINS.Do(tearDownPod) + rootlessCNINS.lock.Unlock() if err == nil { err = rootlessCNINS.Cleanup(r) } diff --git a/pkg/bindings/README.md b/pkg/bindings/README.md index 6fd7d7831..f41304e0f 100644 --- a/pkg/bindings/README.md +++ b/pkg/bindings/README.md @@ -154,3 +154,80 @@ func main() { fmt.Println("Container started.") } ``` + +## Debugging tips <a name="debugging-tips"></a> + +To debug in a development setup, you can start the Podman system service +in debug mode like: + +```bash +$ podman --log-level=debug system service -t 0 +``` + +The `--log-level=debug` echoes all the logged requests and is useful to +trace the execution path at a finer granularity. A snippet of a sample run looks like: + +```bash +INFO[0000] podman filtering at log level debug +DEBU[0000] Called service.PersistentPreRunE(podman --log-level=debug system service -t0) +DEBU[0000] Ignoring libpod.conf EventsLogger setting "/home/lsm5/.config/containers/containers.conf". Use "journald" if you want to change this setting and remove libpod.conf files. +DEBU[0000] Reading configuration file "/usr/share/containers/containers.conf" +DEBU[0000] Merged system config "/usr/share/containers/containers.conf": {Editors note: the remainder of this line was removed due to Jekyll formatting errors.} +DEBU[0000] Using conmon: "/usr/bin/conmon" +DEBU[0000] Initializing boltdb state at /home/lsm5/.local/share/containers/storage/libpod/bolt_state.db +DEBU[0000] Overriding run root "/run/user/1000/containers" with "/run/user/1000" from database +DEBU[0000] Using graph driver overlay +DEBU[0000] Using graph root /home/lsm5/.local/share/containers/storage +DEBU[0000] Using run root /run/user/1000 +DEBU[0000] Using static dir /home/lsm5/.local/share/containers/storage/libpod +DEBU[0000] Using tmp dir /run/user/1000/libpod/tmp +DEBU[0000] Using volume path /home/lsm5/.local/share/containers/storage/volumes +DEBU[0000] Set libpod namespace to "" +DEBU[0000] Not configuring container store +DEBU[0000] Initializing event backend file +DEBU[0000] using runtime "/usr/bin/runc" +DEBU[0000] using runtime "/usr/bin/crun" +WARN[0000] Error initializing configured OCI runtime kata: no valid executable found for OCI runtime kata: invalid argument +DEBU[0000] using runtime "/usr/bin/crun" +INFO[0000] Setting parallel job count to 25 +INFO[0000] podman filtering at log level debug +DEBU[0000] Called service.PersistentPreRunE(podman --log-level=debug system service -t0) +DEBU[0000] Ignoring libpod.conf EventsLogger setting "/home/lsm5/.config/containers/containers.conf". Use "journald" if you want to change this setting and remove libpod.conf files. +DEBU[0000] Reading configuration file "/usr/share/containers/containers.conf" +``` + +If the Podman system service has been started via systemd socket activation, +you can view the logs using journalctl. The logs after a sample run look like: + +```bash +$ journalctl --user --no-pager -u podman.socket +-- Reboot -- +Jul 22 13:50:40 nagato.nanadai.me systemd[1048]: Listening on Podman API Socket. +$ +``` + +```bash +$ journalctl --user --no-pager -u podman.service +Jul 22 13:50:53 nagato.nanadai.me systemd[1048]: Starting Podman API Service... +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 38480630a8bdaa3e1a0ebd34c94038591b0d7ad994b37be5b4f2072bb6ef0879: error acquiring lock 0 for volume 38480630a8bdaa3e1a0ebd34c94038591b0d7ad994b37be5b4f2072bb6ef0879: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 47d410af4d762a0cc456a89e58f759937146fa3be32b5e95a698a1d4069f4024: error acquiring lock 0 for volume 47d410af4d762a0cc456a89e58f759937146fa3be32b5e95a698a1d4069f4024: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 86e73f082e344dad38c8792fb86b2017c4f133f2a8db87f239d1d28a78cf0868: error acquiring lock 0 for volume 86e73f082e344dad38c8792fb86b2017c4f133f2a8db87f239d1d28a78cf0868: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume 9a16ea764be490a5563e384d9074ab0495e4d9119be380c664037d6cf1215631: error acquiring lock 0 for volume 9a16ea764be490a5563e384d9074ab0495e4d9119be380c664037d6cf1215631: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume bfd6b2a97217f8655add13e0ad3f6b8e1c79bc1519b7a1e15361a107ccf57fc0: error acquiring lock 0 for volume bfd6b2a97217f8655add13e0ad3f6b8e1c79bc1519b7a1e15361a107ccf57fc0: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: time="2020-07-22T13:50:54-04:00" level=error msg="Error refreshing volume f9b9f630982452ebcbed24bd229b142fbeecd5d4c85791fca440b21d56fef563: error acquiring lock 0 for volume f9b9f630982452ebcbed24bd229b142fbeecd5d4c85791fca440b21d56fef563: file exists" +Jul 22 13:50:54 nagato.nanadai.me podman[1527]: Trying to pull registry.fedoraproject.org/fedora:latest... +Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Getting image source signatures +Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Copying blob sha256:dd9f43919ba05f05d4f783c31e83e5e776c4f5d29dd72b9ec5056b9576c10053 +Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Copying config sha256:00ff39a8bf19f810a7e641f7eb3ddc47635913a19c4996debd91fafb6b379069 +Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Writing manifest to image destination +Jul 22 13:50:55 nagato.nanadai.me podman[1527]: Storing signatures +Jul 22 13:50:55 nagato.nanadai.me systemd[1048]: podman.service: unit configures an IP firewall, but not running as root. +Jul 22 13:50:55 nagato.nanadai.me systemd[1048]: (This warning is only shown for the first unit using IP firewalling.) +Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: podman.service: Succeeded. +Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: Finished Podman API Service. +Jul 22 13:51:15 nagato.nanadai.me systemd[1048]: podman.service: Consumed 1.339s CPU time. +$ +``` + +You can also verify that the information being passed back and forth is correct by putting +with a tool like `socat`, which can dump what the socket is seeing. |