summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmd/podman/common/create.go7
-rw-r--r--cmd/podman/common/create_opts.go1
-rw-r--r--cmd/podman/common/specgen.go1
-rw-r--r--cmd/podman/containers/create.go5
-rw-r--r--cmd/podman/containers/run.go3
-rw-r--r--cmd/podman/play/kube.go4
-rw-r--r--docs/source/Introduction.rst2
-rw-r--r--docs/source/markdown/podman-create.1.md79
-rw-r--r--docs/source/markdown/podman-import.1.md2
-rw-r--r--docs/source/markdown/podman-play-kube.1.md4
-rw-r--r--docs/source/markdown/podman-run.1.md86
-rw-r--r--go.mod4
-rw-r--r--go.sum8
-rw-r--r--libpod/container_config.go2
-rw-r--r--libpod/container_inspect.go1
-rw-r--r--libpod/define/container_inspect.go1
-rw-r--r--libpod/diff.go12
-rw-r--r--libpod/image/image.go3
-rw-r--r--libpod/oci_conmon_linux.go2
-rw-r--r--libpod/options.go11
-rw-r--r--libpod/runtime_ctr.go11
-rw-r--r--libpod/runtime_img.go3
-rw-r--r--pkg/api/handlers/libpod/images.go10
-rw-r--r--pkg/api/handlers/libpod/play.go22
-rw-r--r--pkg/api/server/register_play.go6
-rw-r--r--pkg/bindings/play/types.go4
-rw-r--r--pkg/bindings/play/types_kube_options.go17
-rw-r--r--pkg/bindings/test/networks_test.go86
-rw-r--r--pkg/bindings/test/volumes_test.go8
-rw-r--r--pkg/domain/entities/play.go8
-rw-r--r--pkg/domain/infra/abi/images.go4
-rw-r--r--pkg/domain/infra/abi/play.go21
-rw-r--r--pkg/domain/infra/tunnel/play.go2
-rw-r--r--pkg/specgen/generate/container_create.go3
-rw-r--r--pkg/specgen/generate/storage.go9
-rw-r--r--pkg/specgen/specgen.go4
-rw-r--r--test/e2e/inspect_test.go10
-rw-r--r--test/e2e/play_kube_test.go81
-rw-r--r--test/e2e/run_test.go16
-rw-r--r--test/e2e/save_test.go4
-rw-r--r--test/e2e/start_test.go24
-rw-r--r--test/system/330-corrupt-images.bats134
-rw-r--r--vendor/github.com/containers/image/v5/copy/copy.go8
-rw-r--r--vendor/github.com/containers/image/v5/oci/layout/oci_src.go2
-rw-r--r--vendor/github.com/containers/image/v5/storage/storage_image.go2
-rw-r--r--vendor/github.com/containers/image/v5/version/version.go2
-rw-r--r--vendor/modules.txt4
47 files changed, 676 insertions, 67 deletions
diff --git a/cmd/podman/common/create.go b/cmd/podman/common/create.go
index 220a30a10..da391d30d 100644
--- a/cmd/podman/common/create.go
+++ b/cmd/podman/common/create.go
@@ -817,6 +817,13 @@ func DefineCreateFlags(cmd *cobra.Command, cf *ContainerCLIOpts) {
)
_ = cmd.RegisterFlagCompletionFunc(cgroupConfFlagName, completion.AutocompleteNone)
+ pidFileFlagName := "pidfile"
+ createFlags.StringVar(
+ &cf.PidFile,
+ pidFileFlagName, "",
+ "Write the container process ID to the file")
+ _ = cmd.RegisterFlagCompletionFunc(pidFileFlagName, completion.AutocompleteDefault)
+
_ = createFlags.MarkHidden("signature-policy")
if registry.IsRemote() {
_ = createFlags.MarkHidden("env-host")
diff --git a/cmd/podman/common/create_opts.go b/cmd/podman/common/create_opts.go
index e14918fe1..040dc6570 100644
--- a/cmd/podman/common/create_opts.go
+++ b/cmd/podman/common/create_opts.go
@@ -122,6 +122,7 @@ type ContainerCLIOpts struct {
VolumesFrom []string
Workdir string
SeccompPolicy string
+ PidFile string
Net *entities.NetOptions
diff --git a/cmd/podman/common/specgen.go b/cmd/podman/common/specgen.go
index 363a8f5f9..310a07a00 100644
--- a/cmd/podman/common/specgen.go
+++ b/cmd/podman/common/specgen.go
@@ -644,6 +644,7 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
s.Timezone = c.Timezone
s.Umask = c.Umask
s.Secrets = c.Secrets
+ s.PidFile = c.PidFile
return nil
}
diff --git a/cmd/podman/containers/create.go b/cmd/podman/containers/create.go
index 2da9aaf5e..507e9c221 100644
--- a/cmd/podman/containers/create.go
+++ b/cmd/podman/containers/create.go
@@ -63,6 +63,11 @@ func createFlags(cmd *cobra.Command) {
common.DefineNetFlags(cmd)
flags.SetNormalizeFunc(utils.AliasFlags)
+
+ if registry.IsRemote() {
+ _ = flags.MarkHidden("conmon-pidfile")
+ _ = flags.MarkHidden("pidfile")
+ }
}
func init() {
diff --git a/cmd/podman/containers/run.go b/cmd/podman/containers/run.go
index 1a9fa2f0f..8e27977c0 100644
--- a/cmd/podman/containers/run.go
+++ b/cmd/podman/containers/run.go
@@ -76,8 +76,11 @@ func runFlags(cmd *cobra.Command) {
detachKeysFlagName := "detach-keys"
flags.StringVar(&runOpts.DetachKeys, detachKeysFlagName, containerConfig.DetachKeys(), "Override the key sequence for detaching a container. Format is a single character `[a-Z]` or a comma separated sequence of `ctrl-<value>`, where `<value>` is one of: `a-cf`, `@`, `^`, `[`, `\\`, `]`, `^` or `_`")
_ = cmd.RegisterFlagCompletionFunc(detachKeysFlagName, common.AutocompleteDetachKeys)
+
if registry.IsRemote() {
_ = flags.MarkHidden("preserve-fds")
+ _ = flags.MarkHidden("conmon-pidfile")
+ _ = flags.MarkHidden("pidfile")
}
}
diff --git a/cmd/podman/play/kube.go b/cmd/podman/play/kube.go
index ddba5dc0f..30d6d86f0 100644
--- a/cmd/podman/play/kube.go
+++ b/cmd/podman/play/kube.go
@@ -65,6 +65,10 @@ func init() {
flags.StringVar(&kubeOptions.Network, networkFlagName, "", "Connect pod to CNI network(s)")
_ = kubeCmd.RegisterFlagCompletionFunc(networkFlagName, common.AutocompleteNetworkFlag)
+ staticIPFlagName := "ip"
+ flags.IPSliceVar(&kubeOptions.StaticIPs, staticIPFlagName, nil, "Static IP addresses to assign to the pods")
+ _ = kubeCmd.RegisterFlagCompletionFunc(staticIPFlagName, completion.AutocompleteNone)
+
logDriverFlagName := "log-driver"
flags.StringVar(&kubeOptions.LogDriver, logDriverFlagName, "", "Logging driver for the container")
_ = kubeCmd.RegisterFlagCompletionFunc(logDriverFlagName, common.AutocompleteLogDriver)
diff --git a/docs/source/Introduction.rst b/docs/source/Introduction.rst
index 9fdce6962..3fa86f868 100644
--- a/docs/source/Introduction.rst
+++ b/docs/source/Introduction.rst
@@ -32,7 +32,7 @@ There’s an old saying that “nobody runs an operating system just to run an o
Sometimes we can find a publicly available container image for the exact workload we’re looking for and it will already be packaged exactly how we want. But, more often than not, there’s something that we want to add, remove, or customize. It could be as simple as a configuration setting for security or performance, or as complex as adding a complex workload. Either way, containers make it fairly easy to make the changes we need.
-Container Images aren’t actually images, they’re repositories often made up of multiple layers. These layers can easily be added, saved, and shared with others by using a Containerfile (Dockerfile). This single file often contains all the instructions needed to build the new and can easily be shared with others publicly using tools like GitHub.
+Container Images aren’t actually images, they’re repositories often made up of multiple layers. These layers can easily be added, saved, and shared with others by using a Containerfile (Dockerfile). This single file often contains all the instructions needed to build a new container image and can easily be shared with others publicly using tools like GitHub.
Here's an example of how to build a Nginx web server on top of a Debian base image using the Dockerfile maintained by Nginx and published in GitHub::
diff --git a/docs/source/markdown/podman-create.1.md b/docs/source/markdown/podman-create.1.md
index ae6dfe03b..059a09ae1 100644
--- a/docs/source/markdown/podman-create.1.md
+++ b/docs/source/markdown/podman-create.1.md
@@ -149,6 +149,7 @@ Write the container ID to the file
#### **\-\-conmon-pidfile**=*path*
Write the pid of the `conmon` process to a file. `conmon` runs in a separate process than Podman, so this is necessary when using systemd to restart Podman containers.
+(This option is not available with the remote Podman client)
#### **\-\-cpu-period**=*limit*
@@ -974,11 +975,72 @@ Remote connections use local containers.conf for defaults
Set the umask inside the container. Defaults to `0022`.
Remote connections use local containers.conf for defaults
-#### **\-\-uidmap**=*container_uid:host_uid:amount*
+#### **\-\-uidmap**=*container_uid*:*from_uid*:*amount*
-UID map for the user namespace. Using this flag will run the container with user namespace enabled. It conflicts with the `--userns` and `--subuidname` flags.
+Run the container in a new user namespace using the supplied mapping. This
+option conflicts with the **\-\-userns** and **\-\-subuidname** options. This
+option provides a way to map host UIDs to container UIDs. It can be passed
+several times to map different ranges.
-The following example maps uids 0-2000 in the container to the uids 30000-31999 on the host and gids 0-2000 in the container to the gids 30000-31999 on the host. `--uidmap=0:30000:2000`
+The _from_uid_ value is based upon the user running the command, either rootful or rootless users.
+* rootful user: *container_uid*:*host_uid*:*amount*
+* rootless user: *container_uid*:*intermediate_uid*:*amount*
+
+When **podman create** is called by a privileged user, the option **\-\-uidmap**
+works as a direct mapping between host UIDs and container UIDs.
+
+host UID -> container UID
+
+The _amount_ specifies the number of consecutive UIDs that will be mapped.
+If for example _amount_ is **4** the mapping would look like:
+
+| host UID | container UID |
+| - | - |
+| _from_uid_ | _container_uid_ |
+| _from_uid_ + 1 | _container_uid_ + 1 |
+| _from_uid_ + 2 | _container_uid_ + 2 |
+| _from_uid_ + 3 | _container_uid_ + 3 |
+
+When **podman create** is called by an unprivileged user (i.e. running rootless),
+the value _from_uid_ is interpreted as an "intermediate UID". In the rootless
+case, host UIDs are not mapped directly to container UIDs. Instead the mapping
+happens over two mapping steps:
+
+host UID -> intermediate UID -> container UID
+
+The **\-\-uidmap** option only influences the second mapping step.
+
+The first mapping step is derived by Podman from the contents of the file
+_/etc/subuid_ and the UID of the user calling Podman.
+
+First mapping step:
+
+| host UID | intermediate UID |
+| - | - |
+| UID for the user starting Podman | 0 |
+| 1st subordinate UID for the user starting Podman | 1 |
+| 2nd subordinate UID for the user starting Podman | 2 |
+| 3rd subordinate UID for the user starting Podman | 3 |
+| nth subordinate UID for the user starting Podman | n |
+
+To be able to use intermediate UIDs greater than zero, the user needs to have
+subordinate UIDs configured in _/etc/subuid_. See **subuid**(5).
+
+The second mapping step is configured with **\-\-uidmap**.
+
+If for example _amount_ is **5** the second mapping step would look like:
+
+| intermediate UID | container UID |
+| - | - |
+| _from_uid_ | _container_uid_ |
+| _from_uid_ + 1 | _container_uid_ + 1 |
+| _from_uid_ + 2 | _container_uid_ + 2 |
+| _from_uid_ + 3 | _container_uid_ + 3 |
+| _from_uid_ + 4 | _container_uid_ + 4 |
+
+Even if a user does not have any subordinate UIDs in _/etc/subuid_,
+**\-\-uidmap** could still be used to map the normal UID of the user to a
+container UID by running `podman create --uidmap $container_uid:0:1 --user $container_uid ...`.
#### **\-\-ulimit**=*option*
@@ -1224,6 +1286,17 @@ The default working directory for running binaries within a container is the roo
The image developer can set a different default with the WORKDIR instruction. The operator
can override the working directory by using the **-w** option.
+#### **\-\-pidfile**=*path*
+
+When the pidfile location is specified, the container process' PID will be written to the pidfile. (This option is not available with the remote Podman client)
+If the pidfile option is not specified, the container process' PID will be written to /run/containers/storage/${storage-driver}-containers/$CID/userdata/pidfile.
+
+After the container is started, the location for the pidfile can be discovered with the following `podman inspect` command:
+
+ $ podman inspect --format '{{ .PidFile }}' $CID
+ /run/containers/storage/${storage-driver}-containers/$CID/userdata/pidfile
+
+
## EXAMPLES
### Create a container using a local image
diff --git a/docs/source/markdown/podman-import.1.md b/docs/source/markdown/podman-import.1.md
index 38c2ab680..171ba4fbd 100644
--- a/docs/source/markdown/podman-import.1.md
+++ b/docs/source/markdown/podman-import.1.md
@@ -34,7 +34,7 @@ Set commit message for imported image
Shows progress on the import
-**-verbose**
+**\-\-verbose**
Print additional debugging information
diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md
index 91899a8bd..1074c27f8 100644
--- a/docs/source/markdown/podman-play-kube.1.md
+++ b/docs/source/markdown/podman-play-kube.1.md
@@ -62,6 +62,10 @@ The [username[:password]] to use to authenticate with the registry if required.
If one or both values are not supplied, a command line prompt will appear and the
value can be entered. The password is entered without echo.
+#### **\-\-ip**=*IP address*
+
+Assign a static ip address to the pod. This option can be specified several times when play kube creates more than one pod.
+
#### **\-\-log-driver**=driver
Set logging driver for all created containers.
diff --git a/docs/source/markdown/podman-run.1.md b/docs/source/markdown/podman-run.1.md
index 40b271828..b607a65ff 100644
--- a/docs/source/markdown/podman-run.1.md
+++ b/docs/source/markdown/podman-run.1.md
@@ -167,6 +167,7 @@ Write the container ID to *file*.
#### **\-\-conmon-pidfile**=*file*
Write the pid of the **conmon** process to a file. As **conmon** runs in a separate process than Podman, this is necessary when using systemd to restart Podman containers.
+(This option is not available with the remote Podman client)
#### **\-\-cpu-period**=*limit*
@@ -1046,23 +1047,72 @@ Remote connections use local containers.conf for defaults
Set the umask inside the container. Defaults to `0022`.
Remote connections use local containers.conf for defaults
-#### **\-\-uidmap**=*container_uid*:*host_uid*:*amount*
+#### **\-\-uidmap**=*container_uid*:*from_uid*:*amount*
-Run the container in a new user namespace using the supplied mapping. This option conflicts
-with the **\-\-userns** and **\-\-subuidname** flags.
-This option can be passed several times to map different ranges. If calling **podman run**
-as an unprivileged user, the user needs to have the right to use the mapping. See **subuid**(5).
+Run the container in a new user namespace using the supplied mapping. This
+option conflicts with the **\-\-userns** and **\-\-subuidname** options. This
+option provides a way to map host UIDs to container UIDs. It can be passed
+several times to map different ranges.
-The following example maps uids 0-1999 in the container to the uids 30000-31999 on the host: **\-\-uidmap=0:30000:2000**.
+The _from_uid_ value is based upon the user running the command, either rootful or rootless users.
+* rootful user: *container_uid*:*host_uid*:*amount*
+* rootless user: *container_uid*:*intermediate_uid*:*amount*
-**Important note:** The new user namespace mapping based on **\-\-uidmap** is based on the initial mapping made in the _/etc/subuid_ file.
-Assuming there is a _/etc/subuid_ mapping **username:100000:65536**, then **username** is initially mapped to a namespace starting with
-uid **100000** for **65536** ids. From here the **\-\-uidmap** mapping to the new namespace starts from **0** again, but is based on the initial mapping.
-Meaning **username** is initially mapped to uid **100000** which is referenced as **0** in the following **\-\-uidmap** mapping. In terms of the example
-above: The user **username** is mapped to user **100000** of the initial namespace then the
-**30000**st id of this namespace (which is uid 130000 in this namespace) is mapped to container namespace user id **0**. (username -> 100000 / 30000 -> 0)
+When **podman run** is called by a privileged user, the option **\-\-uidmap**
+works as a direct mapping between host UIDs and container UIDs.
-_Note_: A minimal mapping has to have at least container uid **0** mapped to the parent user namespace.
+host UID -> container UID
+
+The _amount_ specifies the number of consecutive UIDs that will be mapped.
+If for example _amount_ is **4** the mapping would look like:
+
+| host UID | container UID |
+| - | - |
+| _from_uid_ | _container_uid_ |
+| _from_uid_ + 1 | _container_uid_ + 1 |
+| _from_uid_ + 2 | _container_uid_ + 2 |
+| _from_uid_ + 3 | _container_uid_ + 3 |
+
+When **podman run** is called by an unprivileged user (i.e. running rootless),
+the value _from_uid_ is interpreted as an "intermediate UID". In the rootless
+case, host UIDs are not mapped directly to container UIDs. Instead the mapping
+happens over two mapping steps:
+
+host UID -> intermediate UID -> container UID
+
+The **\-\-uidmap** option only influences the second mapping step.
+
+The first mapping step is derived by Podman from the contents of the file
+_/etc/subuid_ and the UID of the user calling Podman.
+
+First mapping step:
+
+| host UID | intermediate UID |
+| - | - |
+| UID for the user starting Podman | 0 |
+| 1st subordinate UID for the user starting Podman | 1 |
+| 2nd subordinate UID for the user starting Podman | 2 |
+| 3rd subordinate UID for the user starting Podman | 3 |
+| nth subordinate UID for the user starting Podman | n |
+
+To be able to use intermediate UIDs greater than zero, the user needs to have
+subordinate UIDs configured in _/etc/subuid_. See **subuid**(5).
+
+The second mapping step is configured with **\-\-uidmap**.
+
+If for example _amount_ is **5** the second mapping step would look like:
+
+| intermediate UID | container UID |
+| - | - |
+| _from_uid_ | _container_uid_ |
+| _from_uid_ + 1 | _container_uid_ + 1 |
+| _from_uid_ + 2 | _container_uid_ + 2 |
+| _from_uid_ + 3 | _container_uid_ + 3 |
+| _from_uid_ + 4 | _container_uid_ + 4 |
+
+Even if a user does not have any subordinate UIDs in _/etc/subuid_,
+**\-\-uidmap** could still be used to map the normal UID of the user to a
+container UID by running `podman run --uidmap $container_uid:0:1 --user $container_uid ...`.
#### **\-\-ulimit**=*option*
@@ -1305,6 +1355,16 @@ The default working directory for running binaries within a container is the roo
The image developer can set a different default with the WORKDIR instruction. The operator
can override the working directory by using the **-w** option.
+#### **\-\-pidfile**=*path*
+
+When the pidfile location is specified, the container process' PID will be written to the pidfile. (This option is not available with the remote Podman client)
+If the pidfile option is not specified, the container process' PID will be written to /run/containers/storage/${storage-driver}-containers/$CID/userdata/pidfile.
+
+After the container is started, the location for the pidfile can be discovered with the following `podman inspect` command:
+
+ $ podman inspect --format '{{ .PidFile }}' $CID
+ /run/containers/storage/${storage-driver}-containers/$CID/userdata/pidfile
+
## Exit Status
The exit code from **podman run** gives information about why the container
diff --git a/go.mod b/go.mod
index f1c03bce8..629948c8d 100644
--- a/go.mod
+++ b/go.mod
@@ -13,7 +13,7 @@ require (
github.com/containers/buildah v1.20.1-0.20210402144408-36a37402d0c8
github.com/containers/common v0.36.0
github.com/containers/conmon v2.0.20+incompatible
- github.com/containers/image/v5 v5.11.0
+ github.com/containers/image/v5 v5.11.1
github.com/containers/ocicrypt v1.1.1
github.com/containers/psgo v1.5.2
github.com/containers/storage v1.29.0
@@ -57,7 +57,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
- github.com/uber/jaeger-client-go v2.25.0+incompatible
+ github.com/uber/jaeger-client-go v2.27.0+incompatible
github.com/vbauerster/mpb/v6 v6.0.3
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852
go.etcd.io/bbolt v1.3.5
diff --git a/go.sum b/go.sum
index cbe6f7628..4210a307f 100644
--- a/go.sum
+++ b/go.sum
@@ -198,8 +198,8 @@ github.com/containers/common v0.36.0/go.mod h1:rMzxgD7nMGw++cEbsp+NZv0UJO4rgXbm7
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.10.5/go.mod h1:SgIbWEedCNBbn2FI5cH0/jed1Ecy2s8XK5zTxvJTzII=
-github.com/containers/image/v5 v5.11.0 h1:SwxGucW1AZ8H/5KH9jW70lo9WyuOrtxafutyQ9RPPLw=
-github.com/containers/image/v5 v5.11.0/go.mod h1:dCbUB4w6gmxIEOCsE0tZQppr8iBoXb4Evr74ZKlmwoI=
+github.com/containers/image/v5 v5.11.1 h1:mNybUvU6zXUwcMsQaa3n+Idsru5pV+GE7k4oRuPzYi0=
+github.com/containers/image/v5 v5.11.1/go.mod h1:HC9lhJ/Nz5v3w/5Co7H431kLlgzlVlOC+auD/er3OqE=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b h1:Q8ePgVfHDplZ7U33NwHZkrVELsZP5fYj9pM5WBZB2GE=
github.com/containers/libtrust v0.0.0-20190913040956-14b96171aa3b/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
@@ -783,8 +783,8 @@ github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/u-root/u-root v7.0.0+incompatible/go.mod h1:RYkpo8pTHrNjW08opNd/U6p/RJE7K0D8fXO0d47+3YY=
-github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U=
-github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
+github.com/uber/jaeger-client-go v2.27.0+incompatible h1:6WVONolFJiB8Vx9bq4z9ddyV/SXSpfvvtb7Yl/TGHiE=
+github.com/uber/jaeger-client-go v2.27.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
diff --git a/libpod/container_config.go b/libpod/container_config.go
index be24b54d6..e6c3be1bd 100644
--- a/libpod/container_config.go
+++ b/libpod/container_config.go
@@ -364,4 +364,6 @@ type ContainerMiscConfig struct {
Timezone string `json:"timezone,omitempty"`
// Umask is the umask inside the container.
Umask string `json:"umask,omitempty"`
+ // PidFile is the file that saves the pid of the container process
+ PidFile string `json:"pid_file,omitempty"`
}
diff --git a/libpod/container_inspect.go b/libpod/container_inspect.go
index e0569e2d4..61cc43314 100644
--- a/libpod/container_inspect.go
+++ b/libpod/container_inspect.go
@@ -128,6 +128,7 @@ func (c *Container) getContainerInspectData(size bool, driverData *define.Driver
StaticDir: config.StaticDir,
OCIRuntime: config.OCIRuntime,
ConmonPidFile: config.ConmonPidFile,
+ PidFile: config.PidFile,
Name: config.Name,
RestartCount: int32(runtimeInfo.RestartCount),
Driver: driverData.Name,
diff --git a/libpod/define/container_inspect.go b/libpod/define/container_inspect.go
index 0f355d20a..1a38f5b0a 100644
--- a/libpod/define/container_inspect.go
+++ b/libpod/define/container_inspect.go
@@ -627,6 +627,7 @@ type InspectContainerData struct {
OCIConfigPath string `json:"OCIConfigPath,omitempty"`
OCIRuntime string `json:"OCIRuntime,omitempty"`
ConmonPidFile string `json:"ConmonPidFile"`
+ PidFile string `json:"PidFile"`
Name string `json:"Name"`
RestartCount int32 `json:"RestartCount"`
Driver string `json:"Driver"`
diff --git a/libpod/diff.go b/libpod/diff.go
index 36d60b838..df1acf4bb 100644
--- a/libpod/diff.go
+++ b/libpod/diff.go
@@ -1,8 +1,6 @@
package libpod
import (
- "io"
-
"github.com/containers/podman/v3/libpod/layers"
"github.com/containers/storage/pkg/archive"
"github.com/pkg/errors"
@@ -46,16 +44,6 @@ func (r *Runtime) GetDiff(from, to string) ([]archive.Change, error) {
return rchanges, err
}
-// ApplyDiffTarStream applies the changes stored in 'diff' to the layer 'to'
-func (r *Runtime) ApplyDiffTarStream(to string, diff io.Reader) error {
- toLayer, err := r.getLayerID(to)
- if err != nil {
- return err
- }
- _, err = r.store.ApplyDiff(toLayer, diff)
- return err
-}
-
// GetLayerID gets a full layer id given a full or partial id
// If the id matches a container or image, the id of the top layer is returned
// If the id matches a layer, the top layer id is returned
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 12dc22360..3c9fb3a37 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -617,7 +617,8 @@ func (i *Image) TopLayer() string {
func (i *Image) Remove(ctx context.Context, force bool) error {
parent, err := i.GetParent(ctx)
if err != nil {
- return err
+ logrus.Warnf("error determining parent of image: %v, ignoring the error", err)
+ parent = nil
}
if _, err := i.imageruntime.store.DeleteImage(i.ID(), true); err != nil {
return err
diff --git a/libpod/oci_conmon_linux.go b/libpod/oci_conmon_linux.go
index e3da9a237..c1acec977 100644
--- a/libpod/oci_conmon_linux.go
+++ b/libpod/oci_conmon_linux.go
@@ -1016,7 +1016,7 @@ func (r *ConmonOCIRuntime) createOCIContainer(ctr *Container, restoreOptions *Co
}
}
- args := r.sharedConmonArgs(ctr, ctr.ID(), ctr.bundlePath(), filepath.Join(ctr.state.RunDir, "pidfile"), ctr.LogPath(), r.exitsDir, ociLog, ctr.LogDriver(), logTag)
+ args := r.sharedConmonArgs(ctr, ctr.ID(), ctr.bundlePath(), ctr.config.PidFile, ctr.LogPath(), r.exitsDir, ociLog, ctr.LogDriver(), logTag)
if ctr.config.Spec.Process.Terminal {
args = append(args, "-t")
diff --git a/libpod/options.go b/libpod/options.go
index 333a7c4a5..5cd0f7b88 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1692,6 +1692,17 @@ func WithSecrets(secretNames []string) CtrCreateOption {
}
}
+// WithPidFile adds pidFile to the container
+func WithPidFile(pidFile string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return define.ErrCtrFinalized
+ }
+ ctr.config.PidFile = pidFile
+ return nil
+ }
+}
+
// Pod Creation Options
// WithInfraImage sets the infra image for libpod.
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index f9b5c5c51..0acf88cbc 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -69,6 +69,13 @@ func (r *Runtime) RestoreContainer(ctx context.Context, rSpec *spec.Spec, config
ctr.config.ConmonPidFile = ""
}
+ // If the path to PidFile starts with the default value (RunRoot), then
+ // the user has not specified '--pidfile' during run or create (probably).
+ // In that case reset PidFile to be set to the default value later.
+ if strings.HasPrefix(ctr.config.PidFile, r.storageConfig.RunRoot) {
+ ctr.config.PidFile = ""
+ }
+
return r.setupContainer(ctx, ctr)
}
@@ -366,6 +373,10 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
ctr.config.ConmonPidFile = filepath.Join(ctr.state.RunDir, "conmon.pid")
}
+ if ctr.config.PidFile == "" {
+ ctr.config.PidFile = filepath.Join(ctr.state.RunDir, "pidfile")
+ }
+
// Go through named volumes and add them.
// If they don't exist they will be created using basic options.
// Maintain an array of them - we need to lock them later.
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index 3588467a5..2b101c01f 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -66,7 +66,8 @@ func (r *Runtime) RemoveImage(ctx context.Context, img *image.Image, force bool)
hasChildren, err := img.IsParent(ctx)
if err != nil {
- return nil, err
+ logrus.Warnf("error determining if an image is a parent: %v, ignoring the error", err)
+ hasChildren = false
}
if (len(img.Names()) > 1 && !img.InputIsID()) || hasChildren {
diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 158babcdc..92882cc40 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -270,6 +270,16 @@ func ExportImages(w http.ResponseWriter, r *http.Request) {
return
}
+ // if format is dir, server will save to an archive
+ // the client will unArchive after receive the archive file
+ // so must convert is at here
+ switch query.Format {
+ case define.OCIManifestDir:
+ query.Format = define.OCIArchive
+ case define.V2s2ManifestDir:
+ query.Format = define.V2s2Archive
+ }
+
switch query.Format {
case define.V2s2Archive, define.OCIArchive:
tmpfile, err := ioutil.TempFile("", "api.tar")
diff --git a/pkg/api/handlers/libpod/play.go b/pkg/api/handlers/libpod/play.go
index eba5386b6..96f572a8b 100644
--- a/pkg/api/handlers/libpod/play.go
+++ b/pkg/api/handlers/libpod/play.go
@@ -3,6 +3,7 @@ package libpod
import (
"io"
"io/ioutil"
+ "net"
"net/http"
"os"
@@ -20,10 +21,11 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)
query := struct {
- Network string `schema:"network"`
- TLSVerify bool `schema:"tlsVerify"`
- LogDriver string `schema:"logDriver"`
- Start bool `schema:"start"`
+ Network string `schema:"network"`
+ TLSVerify bool `schema:"tlsVerify"`
+ LogDriver string `schema:"logDriver"`
+ Start bool `schema:"start"`
+ StaticIPs []string `schema:"staticIPs"`
}{
TLSVerify: true,
Start: true,
@@ -35,6 +37,17 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
return
}
+ staticIPs := make([]net.IP, 0, len(query.StaticIPs))
+ for _, ipString := range query.StaticIPs {
+ ip := net.ParseIP(ipString)
+ if ip == nil {
+ utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
+ errors.Errorf("Invalid IP address %s", ipString))
+ return
+ }
+ staticIPs = append(staticIPs, ip)
+ }
+
// Fetch the K8s YAML file from the body, and copy it to a temp file.
tmpfile, err := ioutil.TempFile("", "libpod-play-kube.yml")
if err != nil {
@@ -71,6 +84,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
Network: query.Network,
Quiet: true,
LogDriver: query.LogDriver,
+ StaticIPs: staticIPs,
}
if _, found := r.URL.Query()["tlsVerify"]; found {
options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
diff --git a/pkg/api/server/register_play.go b/pkg/api/server/register_play.go
index d21029db5..da37abb70 100644
--- a/pkg/api/server/register_play.go
+++ b/pkg/api/server/register_play.go
@@ -34,6 +34,12 @@ func (s *APIServer) registerPlayHandlers(r *mux.Router) error {
// type: boolean
// default: true
// description: Start the pod after creating it.
+ // - in: query
+ // name: staticIPs
+ // type: array
+ // description: Static IPs used for the pods.
+ // items:
+ // type: string
// - in: body
// name: request
// description: Kubernetes YAML file.
diff --git a/pkg/bindings/play/types.go b/pkg/bindings/play/types.go
index 5fb9a4d41..6598ec3c2 100644
--- a/pkg/bindings/play/types.go
+++ b/pkg/bindings/play/types.go
@@ -1,5 +1,7 @@
package play
+import "net"
+
//go:generate go run ../generator/generator.go KubeOptions
// KubeOptions are optional options for replaying kube YAML files
type KubeOptions struct {
@@ -23,6 +25,8 @@ type KubeOptions struct {
// SeccompProfileRoot - path to a directory containing seccomp
// profiles.
SeccompProfileRoot *string
+ // StaticIPs - Static IP address used by the pod(s).
+ StaticIPs *[]net.IP
// ConfigMaps - slice of pathnames to kubernetes configmap YAMLs.
ConfigMaps *[]string
// LogDriver for the container. For example: journald
diff --git a/pkg/bindings/play/types_kube_options.go b/pkg/bindings/play/types_kube_options.go
index 78396a090..a1786f553 100644
--- a/pkg/bindings/play/types_kube_options.go
+++ b/pkg/bindings/play/types_kube_options.go
@@ -1,6 +1,7 @@
package play
import (
+ "net"
"net/url"
"github.com/containers/podman/v3/pkg/bindings/internal/util"
@@ -164,6 +165,22 @@ func (o *KubeOptions) GetSeccompProfileRoot() string {
return *o.SeccompProfileRoot
}
+// WithStaticIPs
+func (o *KubeOptions) WithStaticIPs(value []net.IP) *KubeOptions {
+ v := &value
+ o.StaticIPs = v
+ return o
+}
+
+// GetStaticIPs
+func (o *KubeOptions) GetStaticIPs() []net.IP {
+ var staticIPs []net.IP
+ if o.StaticIPs == nil {
+ return staticIPs
+ }
+ return *o.StaticIPs
+}
+
// WithConfigMaps
func (o *KubeOptions) WithConfigMaps(value []string) *KubeOptions {
v := &value
diff --git a/pkg/bindings/test/networks_test.go b/pkg/bindings/test/networks_test.go
index df7d7cd1c..b53fc4bd3 100644
--- a/pkg/bindings/test/networks_test.go
+++ b/pkg/bindings/test/networks_test.go
@@ -2,10 +2,12 @@ package test_bindings
import (
"context"
+ "fmt"
"net/http"
"time"
"github.com/containers/podman/v3/pkg/bindings"
+ "github.com/containers/podman/v3/pkg/bindings/containers"
"github.com/containers/podman/v3/pkg/bindings/network"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@@ -116,4 +118,88 @@ var _ = Describe("Podman networks", func() {
Expect(err).To(BeNil())
Expect(data[0]["name"]).To(Equal(name))
})
+
+ It("list networks", func() {
+ // create a bunch of named networks and make verify with list
+ netNames := []string{"homer", "bart", "lisa", "maggie", "marge"}
+ for i := 0; i < 5; i++ {
+ opts := network.CreateOptions{
+ Name: &netNames[i],
+ }
+ _, err = network.Create(connText, &opts)
+ Expect(err).To(BeNil())
+ }
+ list, err := network.List(connText, nil)
+ Expect(err).To(BeNil())
+ Expect(len(list)).To(BeNumerically(">=", 5))
+ for _, n := range list {
+ if n.Name != "podman" {
+ Expect(StringInSlice(n.Name, netNames)).To(BeTrue())
+ }
+ }
+
+ // list with bad filter should be 500
+ filters := make(map[string][]string)
+ filters["foobar"] = []string{"1234"}
+ options := new(network.ListOptions).WithFilters(filters)
+ _, err = network.List(connText, options)
+ Expect(err).ToNot(BeNil())
+ code, _ := bindings.CheckResponseCode(err)
+ Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
+
+ // filter list with success
+ filters = make(map[string][]string)
+ filters["name"] = []string{"homer"}
+ options = new(network.ListOptions).WithFilters(filters)
+ list, err = network.List(connText, options)
+ Expect(err).To(BeNil())
+ Expect(len(list)).To(BeNumerically("==", 1))
+ Expect(list[0].Name).To(Equal("homer"))
+ })
+
+ It("remove network", func() {
+ // removing a noName network should result in 404
+ _, err := network.Remove(connText, "noName", nil)
+ code, err := bindings.CheckResponseCode(err)
+ Expect(err).To(BeNil())
+ Expect(code).To(BeNumerically("==", http.StatusNotFound))
+
+ // Removing an unused network should work
+ name := "unused"
+ opts := network.CreateOptions{
+ Name: &name,
+ }
+ _, err = network.Create(connText, &opts)
+ Expect(err).To(BeNil())
+ report, err := network.Remove(connText, name, nil)
+ Expect(err).To(BeNil())
+ Expect(report[0].Name).To(Equal(name))
+
+ // Removing a network that is being used without force should be 500
+ name = "used"
+ opts = network.CreateOptions{
+ Name: &name,
+ }
+ _, err = network.Create(connText, &opts)
+ Expect(err).To(BeNil())
+
+ // Start container and wait
+ container := "ntest"
+ session := bt.runPodman([]string{"run", "-dt", fmt.Sprintf("--network=%s", name), "--name", container, alpine.name, "top"})
+ session.Wait(45)
+ Expect(session.ExitCode()).To(BeZero())
+
+ _, err = network.Remove(connText, name, nil)
+ code, err = bindings.CheckResponseCode(err)
+ Expect(err).To(BeNil())
+ Expect(code).To(BeNumerically("==", http.StatusInternalServerError))
+
+ // Removing with a network in use with force should work with a stopped container
+ err = containers.Stop(connText, container, new(containers.StopOptions).WithTimeout(0))
+ Expect(err).To(BeNil())
+ options := new(network.RemoveOptions).WithForce(true)
+ report, err = network.Remove(connText, name, options)
+ Expect(err).To(BeNil())
+ Expect(report[0].Name).To(Equal(name))
+ })
})
diff --git a/pkg/bindings/test/volumes_test.go b/pkg/bindings/test/volumes_test.go
index 91f6444cc..14bda114e 100644
--- a/pkg/bindings/test/volumes_test.go
+++ b/pkg/bindings/test/volumes_test.go
@@ -83,7 +83,8 @@ var _ = Describe("Podman volumes", func() {
It("remove volume", func() {
// removing a bogus volume should result in 404
err := volumes.Remove(connText, "foobar", nil)
- code, _ := bindings.CheckResponseCode(err)
+ code, err := bindings.CheckResponseCode(err)
+ Expect(err).To(BeNil())
Expect(code).To(BeNumerically("==", http.StatusNotFound))
// Removing an unused volume should work
@@ -97,9 +98,12 @@ var _ = Describe("Podman volumes", func() {
Expect(err).To(BeNil())
session := bt.runPodman([]string{"run", "-dt", "-v", fmt.Sprintf("%s:/foobar", vol.Name), "--name", "vtest", alpine.name, "top"})
session.Wait(45)
+ Expect(session.ExitCode()).To(BeZero())
+
err = volumes.Remove(connText, vol.Name, nil)
Expect(err).ToNot(BeNil())
- code, _ = bindings.CheckResponseCode(err)
+ code, err = bindings.CheckResponseCode(err)
+ Expect(err).To(BeNil())
Expect(code).To(BeNumerically("==", http.StatusConflict))
// Removing with a volume in use with force should work with a stopped container
diff --git a/pkg/domain/entities/play.go b/pkg/domain/entities/play.go
index cd8bb9506..c69bb0867 100644
--- a/pkg/domain/entities/play.go
+++ b/pkg/domain/entities/play.go
@@ -1,6 +1,10 @@
package entities
-import "github.com/containers/image/v5/types"
+import (
+ "net"
+
+ "github.com/containers/image/v5/types"
+)
// PlayKubeOptions controls playing kube YAML files.
type PlayKubeOptions struct {
@@ -24,6 +28,8 @@ type PlayKubeOptions struct {
// SeccompProfileRoot - path to a directory containing seccomp
// profiles.
SeccompProfileRoot string
+ // StaticIPs - Static IP address used by the pod(s).
+ StaticIPs []net.IP
// ConfigMaps - slice of pathnames to kubernetes configmap YAMLs.
ConfigMaps []string
// LogDriver for the container. For example: journald
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index c02eb2bfc..84c7ebecd 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -620,8 +620,8 @@ func (ir *ImageEngine) Remove(ctx context.Context, images []string, opts entitie
for _, img := range storageImages {
isParent, err := img.IsParent(ctx)
if err != nil {
- rmErrors = append(rmErrors, err)
- continue
+ logrus.Warnf("%v, ignoring the error", err)
+ isParent = false
}
// Skip parent images.
if isParent {
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index 6eecef2de..4a13a8029 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -16,6 +16,7 @@ import (
"github.com/containers/podman/v3/libpod/define"
"github.com/containers/podman/v3/libpod/image"
"github.com/containers/podman/v3/pkg/domain/entities"
+ "github.com/containers/podman/v3/pkg/specgen"
"github.com/containers/podman/v3/pkg/specgen/generate"
"github.com/containers/podman/v3/pkg/specgen/generate/kube"
"github.com/containers/podman/v3/pkg/util"
@@ -50,6 +51,8 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
return nil, errors.Wrapf(err, "unable to sort kube kinds in %q", path)
}
+ ipIndex := 0
+
// create pod on each document if it is a pod or deployment
// any other kube kind will be skipped
for _, document := range documentList {
@@ -70,7 +73,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
podTemplateSpec.ObjectMeta = podYAML.ObjectMeta
podTemplateSpec.Spec = podYAML.Spec
- r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options)
+ r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options, &ipIndex)
if err != nil {
return nil, err
}
@@ -84,7 +87,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Deployment", path)
}
- r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options)
+ r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex)
if err != nil {
return nil, err
}
@@ -118,7 +121,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
return report, nil
}
-func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
+func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int) (*entities.PlayKubeReport, error) {
var (
deploymentName string
podSpec v1.PodTemplateSpec
@@ -140,7 +143,7 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
// create "replicas" number of pods
for i = 0; i < numReplicas; i++ {
podName := fmt.Sprintf("%s-pod-%d", deploymentName, i)
- podReport, err := ic.playKubePod(ctx, podName, &podSpec, options)
+ podReport, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex)
if err != nil {
return nil, errors.Wrapf(err, "error encountered while bringing up pod %s", podName)
}
@@ -149,7 +152,7 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
return &report, nil
}
-func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
+func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions, ipIndex *int) (*entities.PlayKubeReport, error) {
var (
registryCreds *types.DockerAuthConfig
writer io.Writer
@@ -190,9 +193,17 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
// networks.
networks := strings.Split(options.Network, ",")
logrus.Debugf("Pod joining CNI networks: %v", networks)
+ p.NetNS.NSMode = specgen.Bridge
p.CNINetworks = append(p.CNINetworks, networks...)
}
}
+ if len(options.StaticIPs) > *ipIndex {
+ p.StaticIP = &options.StaticIPs[*ipIndex]
+ *ipIndex++
+ } else if len(options.StaticIPs) > 0 {
+ // only warn if the user has set at least one ip ip
+ logrus.Warn("No more static ips left using a random one")
+ }
// Create the Pod
pod, err := generate.MakePod(p, ic.Libpod)
diff --git a/pkg/domain/infra/tunnel/play.go b/pkg/domain/infra/tunnel/play.go
index 9f9076114..e52e1a1f7 100644
--- a/pkg/domain/infra/tunnel/play.go
+++ b/pkg/domain/infra/tunnel/play.go
@@ -11,7 +11,7 @@ import (
func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, opts entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
options := new(play.KubeOptions).WithAuthfile(opts.Authfile).WithUsername(opts.Username).WithPassword(opts.Password)
options.WithCertDir(opts.CertDir).WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithConfigMaps(opts.ConfigMaps)
- options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Network).WithSeccompProfileRoot(opts.SeccompProfileRoot)
+ options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Network).WithSeccompProfileRoot(opts.SeccompProfileRoot).WithStaticIPs(opts.StaticIPs)
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
options.WithSkipTLSVerify(s == types.OptionalBoolTrue)
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index ef9975021..13d4b4926 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -375,6 +375,9 @@ func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.
}
options = append(options, libpod.WithDependencyCtrs(deps))
}
+ if s.PidFile != "" {
+ options = append(options, libpod.WithPidFile(s.PidFile))
+ }
return options, nil
}
diff --git a/pkg/specgen/generate/storage.go b/pkg/specgen/generate/storage.go
index e135f4728..8066834f7 100644
--- a/pkg/specgen/generate/storage.go
+++ b/pkg/specgen/generate/storage.go
@@ -57,10 +57,13 @@ func finalizeMounts(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Ru
}
for _, m := range s.Mounts {
- if _, ok := unifiedMounts[m.Destination]; ok {
- return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", m.Destination)
+ // Ensure that mount dest is clean, so that it can be
+ // compared against named volumes and avoid duplicate mounts.
+ cleanDestination := filepath.Clean(m.Destination)
+ if _, ok := unifiedMounts[cleanDestination]; ok {
+ return nil, nil, nil, errors.Wrapf(errDuplicateDest, "conflict in specified mounts - multiple mounts at %q", cleanDestination)
}
- unifiedMounts[m.Destination] = m
+ unifiedMounts[cleanDestination] = m
}
for _, m := range commonMounts {
diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go
index 28111f96d..e3d4b1436 100644
--- a/pkg/specgen/specgen.go
+++ b/pkg/specgen/specgen.go
@@ -171,6 +171,10 @@ type ContainerBasicConfig struct {
// container. Dependencies can be specified by name or full/partial ID.
// Optional.
DependencyContainers []string `json:"dependencyContainers,omitempty"`
+ // PidFile is the file that saves container process id.
+ // set tags as `json:"-"` for not supported remote
+ // Optional.
+ PidFile string `json:"-"`
}
// ContainerStorageConfig contains information on the storage configuration of a
diff --git a/test/e2e/inspect_test.go b/test/e2e/inspect_test.go
index 772ebed05..ba018b5ad 100644
--- a/test/e2e/inspect_test.go
+++ b/test/e2e/inspect_test.go
@@ -508,4 +508,14 @@ var _ = Describe("Podman inspect", func() {
Expect(data[0].HostConfig.CapDrop[1]).To(Equal("CAP_MKNOD"))
Expect(data[0].HostConfig.CapDrop[2]).To(Equal("CAP_NET_RAW"))
})
+
+ It("podman inspect container with GO format for PidFile", func() {
+ SkipIfRemote("pidfile not handled by remote")
+ session, ec, _ := podmanTest.RunLsContainer("test1")
+ Expect(ec).To(Equal(0))
+
+ session = podmanTest.Podman([]string{"inspect", "--format", "{{.PidFile}}", "test1"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ })
})
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index e479b88cc..f89da4c05 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -12,6 +12,7 @@ import (
"github.com/containers/podman/v3/pkg/util"
. "github.com/containers/podman/v3/test/utils"
+ "github.com/containers/storage/pkg/stringid"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/opencontainers/selinux/go-selinux"
@@ -1716,6 +1717,38 @@ spec:
}
})
+ It("podman play kube --ip", func() {
+ var i, numReplicas int32
+ numReplicas = 3
+ deployment := getDeployment(withReplicas(numReplicas))
+ err := generateKubeYaml("deployment", deployment, kubeYaml)
+ Expect(err).To(BeNil())
+
+ net := "playkube" + stringid.GenerateNonCryptoID()
+ session := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.31.0/24", net})
+ session.WaitWithDefaultTimeout()
+ defer podmanTest.removeCNINetwork(net)
+ Expect(session.ExitCode()).To(BeZero())
+
+ ips := []string{"10.25.31.5", "10.25.31.10", "10.25.31.15"}
+ playArgs := []string{"play", "kube", "--network", net}
+ for _, ip := range ips {
+ playArgs = append(playArgs, "--ip", ip)
+ }
+
+ kube := podmanTest.Podman(append(playArgs, kubeYaml))
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ podNames := getPodNamesInDeployment(deployment)
+ for i = 0; i < numReplicas; i++ {
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "{{ .NetworkSettings.Networks." + net + ".IPAddress }}"})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ Expect(inspect.OutputToString()).To(Equal(ips[i]))
+ }
+ })
+
It("podman play kube test with network portbindings", func() {
ip := "127.0.0.100"
port := "5000"
@@ -1861,6 +1894,54 @@ spec:
Expect(inspect.OutputToString()).To(ContainSubstring(correct))
})
+ It("podman play kube test duplicate volume destination between host path and image volumes", func() {
+ // Create host test directory and file
+ testdir := "testdir"
+ testfile := "testfile"
+
+ hostPathDir := filepath.Join(tempdir, testdir)
+ err := os.Mkdir(hostPathDir, 0755)
+ Expect(err).To(BeNil())
+
+ hostPathDirFile := filepath.Join(hostPathDir, testfile)
+ f, err := os.Create(hostPathDirFile)
+ Expect(err).To(BeNil())
+ f.Close()
+
+ // Create container image with named volume
+ containerfile := fmt.Sprintf(`
+FROM %s
+VOLUME %s`, ALPINE, hostPathDir+"/")
+
+ image := "podman-kube-test:podman"
+ podmanTest.BuildImage(containerfile, image, "false")
+
+ // Create and play kube pod
+ ctr := getCtr(withVolumeMount(hostPathDir+"/", false), withImage(image))
+ pod := getPod(withCtr(ctr), withVolume(getHostPathVolume("Directory", hostPathDir+"/")))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ result := podmanTest.Podman([]string{"exec", getCtrNameInPod(pod), "ls", hostPathDir + "/" + testfile})
+ result.WaitWithDefaultTimeout()
+ Expect(result.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(pod)})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+
+ // If two volumes are specified and share the same destination,
+ // only one will be mounted. Host path volumes take precedence.
+ ctrJSON := inspect.InspectContainerToJSON()
+ Expect(len(ctrJSON[0].Mounts)).To(Equal(1))
+ Expect(ctrJSON[0].Mounts[0].Type).To(Equal("bind"))
+
+ })
+
It("podman play kube test with PersistentVolumeClaim volume", func() {
volumeName := "namedVolume"
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index cefe00655..93505d742 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -1613,4 +1613,20 @@ WORKDIR /madethis`, BB)
Expect(running.ExitCode()).To(Equal(0))
Expect(len(running.OutputToStringArray())).To(Equal(2))
})
+
+ It("podman run with pidfile", func() {
+ SkipIfRemote("pidfile not handled by remote")
+ pidfile := tempdir + "pidfile"
+ session := podmanTest.Podman([]string{"run", "--pidfile", pidfile, ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ readFirstLine := func(path string) string {
+ content, err := ioutil.ReadFile(path)
+ Expect(err).To(BeNil())
+ return strings.Split(string(content), "\n")[0]
+ }
+ containerPID := readFirstLine(pidfile)
+ _, err = strconv.Atoi(containerPID) // Make sure it's a proper integer
+ Expect(err).To(BeNil())
+ })
})
diff --git a/test/e2e/save_test.go b/test/e2e/save_test.go
index 42ee7440b..69184649f 100644
--- a/test/e2e/save_test.go
+++ b/test/e2e/save_test.go
@@ -79,7 +79,7 @@ var _ = Describe("Podman save", func() {
})
It("podman save to directory with oci format", func() {
- if rootless.IsRootless() && podmanTest.RemoteTest {
+ if rootless.IsRootless() {
Skip("Requires a fix in containers image for chown/lchown")
}
outdir := filepath.Join(podmanTest.TempDir, "save")
@@ -90,7 +90,7 @@ var _ = Describe("Podman save", func() {
})
It("podman save to directory with v2s2 docker format", func() {
- if rootless.IsRootless() && podmanTest.RemoteTest {
+ if rootless.IsRootless() {
Skip("Requires a fix in containers image for chown/lchown")
}
outdir := filepath.Join(podmanTest.TempDir, "save")
diff --git a/test/e2e/start_test.go b/test/e2e/start_test.go
index f527b67f6..cb2db0810 100644
--- a/test/e2e/start_test.go
+++ b/test/e2e/start_test.go
@@ -1,7 +1,10 @@
package integration
import (
+ "io/ioutil"
"os"
+ "strconv"
+ "strings"
. "github.com/containers/podman/v3/test/utils"
. "github.com/onsi/ginkgo"
@@ -206,4 +209,25 @@ var _ = Describe("Podman start", func() {
session.WaitWithDefaultTimeout()
Expect(session).Should(Exit(125))
})
+
+ It("podman start container with special pidfile", func() {
+ SkipIfRemote("pidfile not handled by remote")
+ pidfile := tempdir + "pidfile"
+ session := podmanTest.Podman([]string{"create", "--pidfile", pidfile, ALPINE, "ls"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ cid := session.OutputToString()
+
+ session = podmanTest.Podman([]string{"start", cid})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ readFirstLine := func(path string) string {
+ content, err := ioutil.ReadFile(path)
+ Expect(err).To(BeNil())
+ return strings.Split(string(content), "\n")[0]
+ }
+ containerPID := readFirstLine(pidfile)
+ _, err = strconv.Atoi(containerPID) // Make sure it's a proper integer
+ Expect(err).To(BeNil())
+ })
})
diff --git a/test/system/330-corrupt-images.bats b/test/system/330-corrupt-images.bats
new file mode 100644
index 000000000..9836de363
--- /dev/null
+++ b/test/system/330-corrupt-images.bats
@@ -0,0 +1,134 @@
+#!/usr/bin/env bats -*- bats -*-
+#
+# All tests in here perform nasty manipulations on image storage.
+#
+
+load helpers
+
+###############################################################################
+# BEGIN setup/teardown
+
+# Create a scratch directory; this is what we'll use for image store and cache
+if [ -z "${PODMAN_CORRUPT_TEST_WORKDIR}" ]; then
+ export PODMAN_CORRUPT_TEST_WORKDIR=$(mktemp -d --tmpdir=${BATS_TMPDIR:-${TMPDIR:-/tmp}} podman_corrupt_test.XXXXXX)
+fi
+
+PODMAN_CORRUPT_TEST_IMAGE_FQIN=quay.io/libpod/alpine@sha256:634a8f35b5f16dcf4aaa0822adc0b1964bb786fca12f6831de8ddc45e5986a00
+PODMAN_CORRUPT_TEST_IMAGE_ID=961769676411f082461f9ef46626dd7a2d1e2b2a38e6a44364bcbecf51e66dd4
+
+# All tests in this file (and ONLY in this file) run with a custom rootdir
+function setup() {
+ skip_if_remote "none of these tests run under podman-remote"
+ _PODMAN_TEST_OPTS="--root ${PODMAN_CORRUPT_TEST_WORKDIR}/root"
+}
+
+function teardown() {
+ # No other tests should ever run with this custom rootdir
+ unset _PODMAN_TEST_OPTS
+
+ is_remote && return
+
+ # Clean up
+ umount ${PODMAN_CORRUPT_TEST_WORKDIR}/root/overlay || true
+ if is_rootless; then
+ run_podman unshare rm -rf ${PODMAN_CORRUPT_TEST_WORKDIR}/root
+ else
+ rm -rf ${PODMAN_CORRUPT_TEST_WORKDIR}/root
+ fi
+}
+
+# END setup/teardown
+###############################################################################
+# BEGIN primary test helper
+
+# This is our main action, invoked by every actual test. It:
+# - creates a new empty rootdir
+# - populates it with our crafted test image
+# - removes [ manifest, blob ]
+# - confirms that "podman images" throws an error
+# - runs the specified command (rmi -a -f, prune, reset, etc)
+# - confirms that it succeeds, and also emits expected warnings
+function _corrupt_image_test() {
+ # Run this test twice: once removing manifest, once removing blob
+ for what_to_rm in manifest blob; do
+ # I have no idea, but this sometimes remains mounted
+ umount ${PODMAN_CORRUPT_TEST_WORKDIR}/root/overlay || true
+ # Start with a fresh storage root, load prefetched image into it.
+ /bin/rm -rf ${PODMAN_CORRUPT_TEST_WORKDIR}/root
+ mkdir -p ${PODMAN_CORRUPT_TEST_WORKDIR}/root
+ run_podman load -i ${PODMAN_CORRUPT_TEST_WORKDIR}/img.tar
+ # "podman load" restores it without a tag, which (a) causes rmi-by-name
+ # to fail, and (b) causes "podman images" to exit 0 instead of 125
+ run_podman tag ${PODMAN_CORRUPT_TEST_IMAGE_ID} ${PODMAN_CORRUPT_TEST_IMAGE_FQIN}
+
+ # shortcut variable name
+ local id=${PODMAN_CORRUPT_TEST_IMAGE_ID}
+
+ case "$what_to_rm" in
+ manifest) rm_path=manifest ;;
+ blob) rm_path="=$(echo -n "sha256:$id" | base64 -w0)" ;;
+ *) die "Internal error: unknown action '$what_to_rm'" ;;
+ esac
+
+ # Corruptify, and confirm that 'podman images' throws an error
+ rm -v ${PODMAN_CORRUPT_TEST_WORKDIR}/root/*-images/$id/${rm_path}
+ run_podman 125 images
+ is "$output" "Error: error retrieving label for image \"$id\": you may need to remove the image to resolve the error"
+
+ # Run the requested command. Confirm it succeeds, with suitable warnings
+ run_podman $*
+ is "$output" ".*error determining parent of image" \
+ "$* with missing $what_to_rm"
+
+ run_podman images -a --noheading
+ is "$output" "" "podman images -a, after $*, is empty"
+ done
+}
+
+# END primary test helper
+###############################################################################
+# BEGIN first "test" does a one-time pull of our desired image
+
+@test "podman corrupt images - initialize" {
+ # Pull once, save cached copy.
+ run_podman pull $PODMAN_CORRUPT_TEST_IMAGE_FQIN
+ run_podman save -o ${PODMAN_CORRUPT_TEST_WORKDIR}/img.tar \
+ $PODMAN_CORRUPT_TEST_IMAGE_FQIN
+}
+
+# END first "test" does a one-time pull of our desired image
+###############################################################################
+# BEGIN actual tests
+
+@test "podman corrupt images - rmi -f <image-id>" {
+ _corrupt_image_test "rmi -f ${PODMAN_CORRUPT_TEST_IMAGE_ID}"
+}
+
+@test "podman corrupt images - rmi -f <image-name>" {
+ _corrupt_image_test "rmi -f ${PODMAN_CORRUPT_TEST_IMAGE_FQIN}"
+}
+
+@test "podman corrupt images - rmi -f -a" {
+ _corrupt_image_test "rmi -f -a"
+}
+
+@test "podman corrupt images - image prune" {
+ _corrupt_image_test "image prune -a -f"
+}
+
+@test "podman corrupt images - system reset" {
+ _corrupt_image_test "image prune -a -f"
+}
+
+# END actual tests
+###############################################################################
+# BEGIN final cleanup
+
+@test "podman corrupt images - cleanup" {
+ rm -rf ${PODMAN_CORRUPT_TEST_WORKDIR}
+}
+
+# END final cleanup
+###############################################################################
+
+# vim: filetype=sh
diff --git a/vendor/github.com/containers/image/v5/copy/copy.go b/vendor/github.com/containers/image/v5/copy/copy.go
index 165a8be4b..fb704283b 100644
--- a/vendor/github.com/containers/image/v5/copy/copy.go
+++ b/vendor/github.com/containers/image/v5/copy/copy.go
@@ -43,6 +43,10 @@ type digestingReader struct {
validationSucceeded bool
}
+// FIXME: disable early layer commits temporarily until a solid solution to
+// address #1205 has been found.
+const enableEarlyCommit = false
+
var (
// ErrDecryptParamsMissing is returned if there is missing decryption parameters
ErrDecryptParamsMissing = errors.New("Necessary DecryptParameters not present")
@@ -1185,7 +1189,7 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, to
// layers which requires passing the index of the layer.
// Hence, we need to special case and cast.
dest, ok := ic.c.dest.(internalTypes.ImageDestinationWithOptions)
- if ok {
+ if ok && enableEarlyCommit {
options := internalTypes.TryReusingBlobOptions{
Cache: ic.c.blobInfoCache,
CanSubstitute: ic.canSubstituteBlobs,
@@ -1546,7 +1550,7 @@ func (c *copier) copyBlobFromStream(ctx context.Context, srcStream io.Reader, sr
// which requires passing the index of the layer. Hence, we need to
// special case and cast.
dest, ok := c.dest.(internalTypes.ImageDestinationWithOptions)
- if ok {
+ if ok && enableEarlyCommit {
options := internalTypes.PutBlobOptions{
Cache: c.blobInfoCache,
IsConfig: isConfig,
diff --git a/vendor/github.com/containers/image/v5/oci/layout/oci_src.go b/vendor/github.com/containers/image/v5/oci/layout/oci_src.go
index 6801c8432..9925aeda7 100644
--- a/vendor/github.com/containers/image/v5/oci/layout/oci_src.go
+++ b/vendor/github.com/containers/image/v5/oci/layout/oci_src.go
@@ -15,7 +15,6 @@ import (
"github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
)
type ociImageSource struct {
@@ -95,7 +94,6 @@ func (s *ociImageSource) GetManifest(ctx context.Context, instanceDigest *digest
m, err := ioutil.ReadFile(manifestPath)
if err != nil {
- logrus.Errorf("Error HERE")
return nil, "", err
}
if mimeType == "" {
diff --git a/vendor/github.com/containers/image/v5/storage/storage_image.go b/vendor/github.com/containers/image/v5/storage/storage_image.go
index ae020dd66..3a2c18c89 100644
--- a/vendor/github.com/containers/image/v5/storage/storage_image.go
+++ b/vendor/github.com/containers/image/v5/storage/storage_image.go
@@ -763,7 +763,7 @@ func (s *storageImageDestination) commitLayer(ctx context.Context, blob manifest
}
// Carry over the previous ID for empty non-base layers.
- if blob.EmptyLayer && index > 0 {
+ if blob.EmptyLayer {
s.indexToStorageID[index] = &lastLayer
return nil
}
diff --git a/vendor/github.com/containers/image/v5/version/version.go b/vendor/github.com/containers/image/v5/version/version.go
index 3e9f09aab..23b2e3571 100644
--- a/vendor/github.com/containers/image/v5/version/version.go
+++ b/vendor/github.com/containers/image/v5/version/version.go
@@ -8,7 +8,7 @@ const (
// VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 11
// VersionPatch is for backwards-compatible bug fixes
- VersionPatch = 0
+ VersionPatch = 1
// VersionDev indicates development branch. Releases will be empty string.
VersionDev = ""
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 431a21c8a..35655b01e 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -115,7 +115,7 @@ github.com/containers/common/pkg/umask
github.com/containers/common/version
# github.com/containers/conmon v2.0.20+incompatible
github.com/containers/conmon/runner/config
-# github.com/containers/image/v5 v5.11.0
+# github.com/containers/image/v5 v5.11.1
github.com/containers/image/v5/copy
github.com/containers/image/v5/directory
github.com/containers/image/v5/directory/explicitfilepath
@@ -575,7 +575,7 @@ github.com/stretchr/testify/require
github.com/syndtr/gocapability/capability
# github.com/tchap/go-patricia v2.3.0+incompatible
github.com/tchap/go-patricia/patricia
-# github.com/uber/jaeger-client-go v2.25.0+incompatible
+# github.com/uber/jaeger-client-go v2.27.0+incompatible
github.com/uber/jaeger-client-go/log
github.com/uber/jaeger-client-go/thrift
github.com/uber/jaeger-client-go/thrift-gen/agent