summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/source/markdown/podman-build.1.md8
-rw-r--r--docs/source/markdown/podman-generate-kube.1.md115
-rw-r--r--docs/source/markdown/podman-play-kube.1.md4
-rw-r--r--go.mod2
-rw-r--r--go.sum6
-rw-r--r--libpod/container_internal_linux.go14
-rw-r--r--libpod/image/filters.go14
-rw-r--r--libpod/image/prune.go24
-rw-r--r--libpod/kube.go32
-rw-r--r--libpod/network/netconflist.go29
-rw-r--r--pkg/domain/filters/containers.go24
-rw-r--r--pkg/domain/filters/pods.go21
-rw-r--r--pkg/domain/filters/volumes.go17
-rw-r--r--pkg/util/filters.go27
-rw-r--r--pkg/util/filters_test.go113
-rw-r--r--test/e2e/generate_kube_test.go30
-rw-r--r--test/e2e/run_test.go23
-rw-r--r--vendor/modules.txt2
18 files changed, 371 insertions, 134 deletions
diff --git a/docs/source/markdown/podman-build.1.md b/docs/source/markdown/podman-build.1.md
index 24093d414..8fcfe555e 100644
--- a/docs/source/markdown/podman-build.1.md
+++ b/docs/source/markdown/podman-build.1.md
@@ -650,6 +650,10 @@ If --userns-gid-map-group is specified, but --userns-uid-map-user is not
specified, `podman` will assume that the specified group name is also a
suitable user name to use as the default setting for this option.
+**NOTE:** When this option is specified by a rootless user, the specified
+mappings are relative to the rootless usernamespace in the container, rather
+than being relative to the host as it would be when run rootful.
+
#### **--userns-gid-map-group**=*group*
Specifies that a GID mapping which should be used to set ownership, at the
@@ -661,6 +665,10 @@ If --userns-uid-map-user is specified, but --userns-gid-map-group is not
specified, `podman` will assume that the specified user name is also a
suitable group name to use as the default setting for this option.
+**NOTE:** When this option is specified by a rootless user, the specified
+mappings are relative to the rootless usernamespace in the container, rather
+than being relative to the host as it would be when run rootful.
+
#### **--uts**=*how*
Sets the configuration for UTS namespaces when the handling `RUN` instructions.
diff --git a/docs/source/markdown/podman-generate-kube.1.md b/docs/source/markdown/podman-generate-kube.1.md
index 019bae539..0e5a31ae6 100644
--- a/docs/source/markdown/podman-generate-kube.1.md
+++ b/docs/source/markdown/podman-generate-kube.1.md
@@ -6,10 +6,14 @@ podman-generate-kube - Generate Kubernetes YAML based on a pod or container
**podman generate kube** [*options*] *container...* | *pod*
## DESCRIPTION
-**podman generate kube** will generate Kubernetes Pod YAML (v1 specification) from Podman one or more containers or a single pod. Whether
+**podman generate kube** will generate Kubernetes Pod YAML (v1 specification) from Podman from one or more containers or a single pod. Whether
the input is for containers or a pod, Podman will always generate the specification as a Pod. The input may be in the form
of a pod or one or more container names or IDs.
+Volumes appear in the generated YAML according to two different volume types. Bind-mounted volumes become *hostPath* volume types and named volumes become *persistentVolumeClaim* volume types. Generated *hostPath* volume types will be one of three subtypes depending on the state of the host path: *DirectoryOrCreate* when no file or directory exists at the host, *Directory* when host path is a directory, or *File* when host path is a file. The value for *claimName* for a *persistentVolumeClaim* is the name of the named volume registered in Podman.
+
+Potential name conflicts between volumes are avoided by using a standard naming scheme for each volume type. The *hostPath* volume types are named according to the path on the host machine, replacing forward slashes with hyphens less any leading and trailing forward slashes. The special case of the filesystem root, `/`, translates to the name `root`. Additionally, the name is suffixed with `-host` to avoid naming conflicts with *persistentVolumeClaim* volumes. Each *persistentVolumeClaim* volume type uses the name of its associated named volume suffixed with `-pvc`.
+
Note that the generated Kubernetes YAML file can be used to re-run the deployment via podman-play-kube(1).
## OPTIONS
@@ -25,7 +29,7 @@ random port is assigned by Podman in the specification.
## EXAMPLES
-Create Kubernetes Pod YAML for a container called `some-mariadb` .
+Create Kubernetes Pod YAML for a container called `some-mariadb`.
```
$ sudo podman generate kube some-mariadb
# Generation of Kubernetes YAML is still under development!
@@ -81,6 +85,113 @@ spec:
status: {}
```
+Create Kubernetes Pod YAML for a container with the directory `/home/user/my-data` on the host bind-mounted in the container to `/volume`.
+```
+$ podman generate kube my-container-with-bind-mounted-data
+# Generation of Kubernetes YAML is still under development!
+#
+# Save the output of this file and use kubectl create -f to import
+# it into Kubernetes.
+#
+# Created with podman-3.1.0-dev
+apiVersion: v1
+kind: Pod
+metadata:
+ creationTimestamp: "2021-03-18T16:26:08Z"
+ labels:
+ app: my-container-with-bind-mounted-data
+ name: my-container-with-bind-mounted-data
+spec:
+ containers:
+ - command:
+ - /bin/sh
+ env:
+ - name: PATH
+ value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+ - name: TERM
+ value: xterm
+ - name: container
+ value: podman
+ image: docker.io/library/alpine:latest
+ name: test-bind-mount
+ resources: {}
+ securityContext:
+ allowPrivilegeEscalation: true
+ capabilities:
+ drop:
+ - CAP_MKNOD
+ - CAP_NET_RAW
+ - CAP_AUDIT_WRITE
+ privileged: false
+ readOnlyRootFilesystem: false
+ seLinuxOptions: {}
+ volumeMounts:
+ - mountPath: /volume
+ name: home-user-my-data-host
+ workingDir: /
+ dnsConfig: {}
+ restartPolicy: Never
+ volumes:
+ - hostPath:
+ path: /home/user/my-data
+ type: Directory
+ name: home-user-my-data-host
+status: {}
+```
+
+Create Kubernetes Pod YAML for a container with the named volume `priceless-data` mounted in the container at `/volume`.
+```
+$ podman generate kube my-container-using-priceless-data
+# Generation of Kubernetes YAML is still under development!
+#
+# Save the output of this file and use kubectl create -f to import
+# it into Kubernetes.
+#
+# Created with podman-3.1.0-dev
+apiVersion: v1
+kind: Pod
+metadata:
+ creationTimestamp: "2021-03-18T16:26:08Z"
+ labels:
+ app: my-container-using-priceless-data
+ name: my-container-using-priceless-data
+spec:
+ containers:
+ - command:
+ - /bin/sh
+ env:
+ - name: PATH
+ value: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
+ - name: TERM
+ value: xterm
+ - name: container
+ value: podman
+ image: docker.io/library/alpine:latest
+ name: test-bind-mount
+ resources: {}
+ securityContext:
+ allowPrivilegeEscalation: true
+ capabilities:
+ drop:
+ - CAP_MKNOD
+ - CAP_NET_RAW
+ - CAP_AUDIT_WRITE
+ privileged: false
+ readOnlyRootFilesystem: false
+ seLinuxOptions: {}
+ volumeMounts:
+ - mountPath: /volume
+ name: priceless-data-pvc
+ workingDir: /
+ dnsConfig: {}
+ restartPolicy: Never
+ volumes:
+ - name: priceless-data-pvc
+ persistentVolumeClaim:
+ claimName: priceless-data
+status: {}
+```
+
Create Kubernetes Pod YAML for a pod called `demoweb` and include a service.
```
$ sudo podman generate kube -s demoweb
diff --git a/docs/source/markdown/podman-play-kube.1.md b/docs/source/markdown/podman-play-kube.1.md
index 0a34a622f..1be597b94 100644
--- a/docs/source/markdown/podman-play-kube.1.md
+++ b/docs/source/markdown/podman-play-kube.1.md
@@ -11,7 +11,9 @@ podman-play-kube - Create pods and containers based on Kubernetes YAML
Ideally the input file would be one created by Podman (see podman-generate-kube(1)). This would guarantee a smooth import and expected results.
-Note: HostPath volume types created by play kube will be given an SELinux private label (Z)
+Only two volume types are supported by play kube, the *hostPath* and *persistentVolumeClaim* volume types. For the *hostPath* volume type, only the *default (empty)*, *DirectoryOrCreate*, *Directory*, *FileOrCreate*, *File*, and *Socket* subtypes are supported. The *CharDevice* and *BlockDevice* subtypes are not supported. Podman interprets the value of *hostPath* *path* as a file path when it contains at least one forward slash, otherwise Podman treats the value as the name of a named volume. When using a *persistentVolumeClaim*, the value for *claimName* is the name for the Podman named volume.
+
+Note: *hostPath* volume types created by play kube will be given an SELinux private label (Z)
Note: If the `:latest` tag is used, Podman will attempt to pull the image from a registry. If the image was built locally with Podman or Buildah, it will have `localhost` as the domain, in that case, Podman will use the image from the local store even if it has the `:latest` tag.
diff --git a/go.mod b/go.mod
index 680f9bd9c..1f94095a2 100644
--- a/go.mod
+++ b/go.mod
@@ -53,7 +53,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0
github.com/rivo/uniseg v0.2.0 // indirect
- github.com/rootless-containers/rootlesskit v0.14.0-beta.0
+ github.com/rootless-containers/rootlesskit v0.14.0
github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.1.3
github.com/spf13/pflag v1.0.5
diff --git a/go.sum b/go.sum
index e7a89e5c3..385047cc9 100644
--- a/go.sum
+++ b/go.sum
@@ -485,7 +485,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw=
github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
-github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -688,8 +687,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rootless-containers/rootlesskit v0.14.0-beta.0 h1:S0VzvU7sEvqCTkxPAxzJ1OZpG9a8oG9FSwkVhk0b8PM=
-github.com/rootless-containers/rootlesskit v0.14.0-beta.0/go.mod h1:5UDnrX52Dyoyz2lK66mjHftWpK9YSp1ghO+fY1ZkxFc=
+github.com/rootless-containers/rootlesskit v0.14.0 h1:4zfZqDv7JzsVuMkj4ZMB9cs9mQQnyl1gWBsrpOYcmtk=
+github.com/rootless-containers/rootlesskit v0.14.0/go.mod h1:nV3TpRISvwhZQSwo0nmQQnxjCxXr3mvrMi0oASLvzcg=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
@@ -709,7 +708,6 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.8.0/go.mod h1:4GuYW9TZmE769R5STWrRakJc4UqQ3+QQ95fyz7ENv1A=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
diff --git a/libpod/container_internal_linux.go b/libpod/container_internal_linux.go
index 24319f4b5..94c6c3840 100644
--- a/libpod/container_internal_linux.go
+++ b/libpod/container_internal_linux.go
@@ -1503,16 +1503,24 @@ func (c *Container) makeBindMounts() error {
}
// Make /etc/localtime
- if c.Timezone() != "" {
+ ctrTimezone := c.Timezone()
+ if ctrTimezone != "" {
+ // validate the format of the timezone specified if it's not "local"
+ if ctrTimezone != "local" {
+ _, err = time.LoadLocation(ctrTimezone)
+ if err != nil {
+ return errors.Wrapf(err, "error finding timezone for container %s", c.ID())
+ }
+ }
if _, ok := c.state.BindMounts["/etc/localtime"]; !ok {
var zonePath string
- if c.Timezone() == "local" {
+ if ctrTimezone == "local" {
zonePath, err = filepath.EvalSymlinks("/etc/localtime")
if err != nil {
return errors.Wrapf(err, "error finding local timezone for container %s", c.ID())
}
} else {
- zone := filepath.Join("/usr/share/zoneinfo", c.Timezone())
+ zone := filepath.Join("/usr/share/zoneinfo", ctrTimezone)
zonePath, err = filepath.EvalSymlinks(zone)
if err != nil {
return errors.Wrapf(err, "error setting timezone for container %s", c.ID())
diff --git a/libpod/image/filters.go b/libpod/image/filters.go
index 37d3cb6a5..d316c6956 100644
--- a/libpod/image/filters.go
+++ b/libpod/image/filters.go
@@ -9,6 +9,7 @@ import (
"time"
"github.com/containers/podman/v3/pkg/inspect"
+ "github.com/containers/podman/v3/pkg/util"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -78,23 +79,14 @@ func ReadOnlyFilter(readOnly bool) ResultFilter {
}
// LabelFilter allows you to filter by images labels key and/or value
-func LabelFilter(ctx context.Context, labelfilter string) ResultFilter {
+func LabelFilter(ctx context.Context, filter string) ResultFilter {
// We need to handle both label=key and label=key=value
return func(i *Image) bool {
- var value string
- splitFilter := strings.SplitN(labelfilter, "=", 2)
- key := splitFilter[0]
- if len(splitFilter) > 1 {
- value = splitFilter[1]
- }
labels, err := i.Labels(ctx)
if err != nil {
return false
}
- if len(strings.TrimSpace(labels[key])) > 0 && len(strings.TrimSpace(value)) == 0 {
- return true
- }
- return labels[key] == value
+ return util.MatchLabelFilters([]string{filter}, labels)
}
}
diff --git a/libpod/image/prune.go b/libpod/image/prune.go
index d6ae5feaf..7ee3e077e 100644
--- a/libpod/image/prune.go
+++ b/libpod/image/prune.go
@@ -3,11 +3,10 @@ package image
import (
"context"
"strings"
- "time"
"github.com/containers/podman/v3/libpod/events"
"github.com/containers/podman/v3/pkg/domain/entities/reports"
- "github.com/containers/podman/v3/pkg/timetype"
+ "github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
@@ -16,36 +15,19 @@ import (
func generatePruneFilterFuncs(filter, filterValue string) (ImageFilter, error) {
switch filter {
case "label":
- var filterArray = strings.SplitN(filterValue, "=", 2)
- var filterKey = filterArray[0]
- if len(filterArray) > 1 {
- filterValue = filterArray[1]
- } else {
- filterValue = ""
- }
return func(i *Image) bool {
labels, err := i.Labels(context.Background())
if err != nil {
return false
}
- for labelKey, labelValue := range labels {
- if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
- return true
- }
- }
- return false
+ return util.MatchLabelFilters([]string{filterValue}, labels)
}, nil
case "until":
- ts, err := timetype.GetTimestamp(filterValue, time.Now())
- if err != nil {
- return nil, err
- }
- seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
+ until, err := util.ComputeUntilTimestamp([]string{filterValue})
if err != nil {
return nil, err
}
- until := time.Unix(seconds, nanoseconds)
return func(i *Image) bool {
if !until.IsZero() && i.Created().After((until)) {
return true
diff --git a/libpod/kube.go b/libpod/kube.go
index 407c4ae00..b4dd4f10a 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -330,8 +330,6 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, *v1.PodDNS
}
if len(c.config.UserVolumes) > 0 {
- // TODO When we until we can resolve what the volume name should be, this is disabled
- // Volume names need to be coordinated "globally" in the kube files.
volumeMounts, volumes, err := libpodMountsToKubeVolumeMounts(c)
if err != nil {
return kubeContainer, kubeVolumes, nil, err
@@ -493,8 +491,7 @@ func libpodEnvVarsToKubeEnvVars(envs []string) ([]v1.EnvVar, error) {
// libpodMountsToKubeVolumeMounts converts the containers mounts to a struct kube understands
func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume, error) {
- // TODO when named volumes are supported in play kube, also parse named volumes here
- _, mounts := c.sortUserVolumes(c.config.Spec)
+ namedVolumes, mounts := c.sortUserVolumes(c.config.Spec)
vms := make([]v1.VolumeMount, 0, len(mounts))
vos := make([]v1.Volume, 0, len(mounts))
for _, m := range mounts {
@@ -505,9 +502,34 @@ func libpodMountsToKubeVolumeMounts(c *Container) ([]v1.VolumeMount, []v1.Volume
vms = append(vms, vm)
vos = append(vos, vo)
}
+ for _, v := range namedVolumes {
+ vm, vo := generateKubePersistentVolumeClaim(v)
+ vms = append(vms, vm)
+ vos = append(vos, vo)
+ }
return vms, vos, nil
}
+// generateKubePersistentVolumeClaim converts a ContainerNamedVolume to a Kubernetes PersistentVolumeClaim
+func generateKubePersistentVolumeClaim(v *ContainerNamedVolume) (v1.VolumeMount, v1.Volume) {
+ ro := util.StringInSlice("ro", v.Options)
+
+ // To avoid naming conflicts with any host path mounts, add a unique suffix to the volume's name.
+ name := v.Name + "-pvc"
+
+ vm := v1.VolumeMount{}
+ vm.Name = name
+ vm.MountPath = v.Dest
+ vm.ReadOnly = ro
+
+ pvc := v1.PersistentVolumeClaimVolumeSource{ClaimName: v.Name, ReadOnly: ro}
+ vs := v1.VolumeSource{}
+ vs.PersistentVolumeClaim = &pvc
+ vo := v1.Volume{Name: name, VolumeSource: vs}
+
+ return vm, vo
+}
+
// generateKubeVolumeMount takes a user specified mount and returns
// a kubernetes VolumeMount (to be added to the container) and a kubernetes Volume
// (to be added to the pod)
@@ -519,6 +541,8 @@ func generateKubeVolumeMount(m specs.Mount) (v1.VolumeMount, v1.Volume, error) {
if err != nil {
return vm, vo, err
}
+ // To avoid naming conflicts with any persistent volume mounts, add a unique suffix to the volume's name.
+ name += "-host"
vm.Name = name
vm.MountPath = m.Destination
if util.StringInSlice("ro", m.Options) {
diff --git a/libpod/network/netconflist.go b/libpod/network/netconflist.go
index b358bc530..08816f2bd 100644
--- a/libpod/network/netconflist.go
+++ b/libpod/network/netconflist.go
@@ -225,7 +225,7 @@ func IfPassesFilter(netconf *libcni.NetworkConfigList, filters map[string][]stri
case "label":
// matches all labels
- result = matchPruneLabelFilters(netconf, filterValues)
+ result = util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf))
case "driver":
// matches only for the DefaultNetworkDriver
@@ -260,9 +260,9 @@ func IfPassesPruneFilter(config *config.Config, netconf *libcni.NetworkConfigLis
for key, filterValues := range f {
switch strings.ToLower(key) {
case "label":
- return matchPruneLabelFilters(netconf, filterValues), nil
+ return util.MatchLabelFilters(filterValues, GetNetworkLabels(netconf)), nil
case "until":
- until, err := util.ComputeUntilTimestamp(key, filterValues)
+ until, err := util.ComputeUntilTimestamp(filterValues)
if err != nil {
return false, err
}
@@ -280,29 +280,6 @@ func IfPassesPruneFilter(config *config.Config, netconf *libcni.NetworkConfigLis
return false, nil
}
-func matchPruneLabelFilters(netconf *libcni.NetworkConfigList, filterValues []string) bool {
- labels := GetNetworkLabels(netconf)
- result := true
-outer:
- for _, filterValue := range filterValues {
- filterArray := strings.SplitN(filterValue, "=", 2)
- filterKey := filterArray[0]
- if len(filterArray) > 1 {
- filterValue = filterArray[1]
- } else {
- filterValue = ""
- }
- for labelKey, labelValue := range labels {
- if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
- result = true
- continue outer
- }
- }
- result = false
- }
- return result
-}
-
func getCreatedTimestamp(config *config.Config, netconf *libcni.NetworkConfigList) (*time.Time, error) {
networkConfigPath, err := GetCNIConfigPathByNameOrID(config, netconf.Name)
if err != nil {
diff --git a/pkg/domain/filters/containers.go b/pkg/domain/filters/containers.go
index 02727e841..84cf03764 100644
--- a/pkg/domain/filters/containers.go
+++ b/pkg/domain/filters/containers.go
@@ -23,27 +23,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
case "label":
// we have to match that all given labels exits on that container
return func(c *libpod.Container) bool {
- labels := c.Labels()
- for _, filterValue := range filterValues {
- matched := false
- filterArray := strings.SplitN(filterValue, "=", 2)
- filterKey := filterArray[0]
- if len(filterArray) > 1 {
- filterValue = filterArray[1]
- } else {
- filterValue = ""
- }
- for labelKey, labelValue := range labels {
- if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
- matched = true
- break
- }
- }
- if !matched {
- return false
- }
- }
- return true
+ return util.MatchLabelFilters(filterValues, c.Labels())
}, nil
case "name":
// we only have to match one name
@@ -185,7 +165,7 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
return false
}, nil
case "until":
- until, err := util.ComputeUntilTimestamp(filter, filterValues)
+ until, err := util.ComputeUntilTimestamp(filterValues)
if err != nil {
return nil, err
}
diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go
index 0490a4848..9a1c7d19d 100644
--- a/pkg/domain/filters/pods.go
+++ b/pkg/domain/filters/pods.go
@@ -114,26 +114,7 @@ func GeneratePodFilterFunc(filter string, filterValues []string) (
case "label":
return func(p *libpod.Pod) bool {
labels := p.Labels()
- for _, filterValue := range filterValues {
- matched := false
- filterArray := strings.SplitN(filterValue, "=", 2)
- filterKey := filterArray[0]
- if len(filterArray) > 1 {
- filterValue = filterArray[1]
- } else {
- filterValue = ""
- }
- for labelKey, labelValue := range labels {
- if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
- matched = true
- break
- }
- }
- if !matched {
- return false
- }
- }
- return true
+ return util.MatchLabelFilters(filterValues, labels)
}, nil
case "network":
return func(p *libpod.Pod) bool {
diff --git a/pkg/domain/filters/volumes.go b/pkg/domain/filters/volumes.go
index bc1756cf5..9b2fc5280 100644
--- a/pkg/domain/filters/volumes.go
+++ b/pkg/domain/filters/volumes.go
@@ -5,6 +5,7 @@ import (
"strings"
"github.com/containers/podman/v3/libpod"
+ "github.com/containers/podman/v3/pkg/util"
"github.com/pkg/errors"
)
@@ -29,21 +30,9 @@ func GenerateVolumeFilters(filters url.Values) ([]libpod.VolumeFilter, error) {
return v.Scope() == scopeVal
})
case "label":
- filterArray := strings.SplitN(val, "=", 2)
- filterKey := filterArray[0]
- var filterVal string
- if len(filterArray) > 1 {
- filterVal = filterArray[1]
- } else {
- filterVal = ""
- }
+ filter := val
vf = append(vf, func(v *libpod.Volume) bool {
- for labelKey, labelValue := range v.Labels() {
- if labelKey == filterKey && (filterVal == "" || labelValue == filterVal) {
- return true
- }
- }
- return false
+ return util.MatchLabelFilters([]string{filter}, v.Labels())
})
case "opt":
filterArray := strings.SplitN(val, "=", 2)
diff --git a/pkg/util/filters.go b/pkg/util/filters.go
index 51b2c5331..43bf646f1 100644
--- a/pkg/util/filters.go
+++ b/pkg/util/filters.go
@@ -11,11 +11,11 @@ import (
"github.com/pkg/errors"
)
-// ComputeUntilTimestamp extracts unitil timestamp from filters
-func ComputeUntilTimestamp(filter string, filterValues []string) (time.Time, error) {
+// ComputeUntilTimestamp extracts until timestamp from filters
+func ComputeUntilTimestamp(filterValues []string) (time.Time, error) {
invalid := time.Time{}
if len(filterValues) != 1 {
- return invalid, errors.Errorf("specify exactly one timestamp for %s", filter)
+ return invalid, errors.Errorf("specify exactly one timestamp for until")
}
ts, err := timetype.GetTimestamp(filterValues[0], time.Now())
if err != nil {
@@ -93,3 +93,24 @@ func PrepareFilters(r *http.Request) (*map[string][]string, error) {
}
return &filterMap, nil
}
+
+// MatchLabelFilters matches labels and returs true if they are valid
+func MatchLabelFilters(filterValues []string, labels map[string]string) bool {
+outer:
+ for _, filterValue := range filterValues {
+ filterArray := strings.SplitN(filterValue, "=", 2)
+ filterKey := filterArray[0]
+ if len(filterArray) > 1 {
+ filterValue = filterArray[1]
+ } else {
+ filterValue = ""
+ }
+ for labelKey, labelValue := range labels {
+ if labelKey == filterKey && (filterValue == "" || labelValue == filterValue) {
+ continue outer
+ }
+ }
+ return false
+ }
+ return true
+}
diff --git a/pkg/util/filters_test.go b/pkg/util/filters_test.go
new file mode 100644
index 000000000..47259013e
--- /dev/null
+++ b/pkg/util/filters_test.go
@@ -0,0 +1,113 @@
+package util
+
+import (
+ "testing"
+)
+
+func TestMatchLabelFilters(t *testing.T) {
+ testLabels := map[string]string{
+ "label1": "",
+ "label2": "test",
+ "label3": "",
+ }
+ type args struct {
+ filterValues []string
+ labels map[string]string
+ }
+ tests := []struct {
+ name string
+ args args
+ want bool
+ }{
+ {
+ name: "Match when all filters the same as labels",
+ args: args{
+ filterValues: []string{"label1", "label3", "label2=test"},
+ labels: testLabels,
+ },
+ want: true,
+ },
+ {
+ name: "Match when filter value not provided in args",
+ args: args{
+ filterValues: []string{"label2"},
+ labels: testLabels,
+ },
+ want: true,
+ },
+ {
+ name: "Match when no filter value is given",
+ args: args{
+ filterValues: []string{"label2="},
+ labels: testLabels,
+ },
+ want: true,
+ },
+ {
+ name: "Do not match when filter value differs",
+ args: args{
+ filterValues: []string{"label2=differs"},
+ labels: testLabels,
+ },
+ want: false,
+ },
+ {
+ name: "Do not match when filter value not listed in labels",
+ args: args{
+ filterValues: []string{"label1=xyz"},
+ labels: testLabels,
+ },
+ want: false,
+ },
+ {
+ name: "Do not match when one from many not ok",
+ args: args{
+ filterValues: []string{"label1=xyz", "invalid=valid"},
+ labels: testLabels,
+ },
+ want: false,
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ if got := MatchLabelFilters(tt.args.filterValues, tt.args.labels); got != tt.want {
+ t.Errorf("MatchLabelFilters() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestComputeUntilTimestamp(t *testing.T) {
+ tests := []struct {
+ name string
+ args []string
+ wantErr bool
+ }{
+ {
+ name: "Return error when more values in list",
+ args: []string{"5h", "6s"},
+ wantErr: true,
+ },
+ {
+ name: "Return error when invalid time",
+ args: []string{"invalidTime"},
+ wantErr: true,
+ },
+ {
+ name: "Do not return error when correct time format supplied",
+ args: []string{"44m"},
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ tt := tt
+ t.Run(tt.name, func(t *testing.T) {
+ _, err := ComputeUntilTimestamp(tt.args)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("ComputeUntilTimestamp() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ })
+ }
+}
diff --git a/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go
index bc7c21785..9cfda0e75 100644
--- a/test/e2e/generate_kube_test.go
+++ b/test/e2e/generate_kube_test.go
@@ -478,6 +478,36 @@ var _ = Describe("Podman generate kube", func() {
Expect(inspect.OutputToString()).To(ContainSubstring(vol1))
})
+ It("podman generate kube with persistent volume claim", func() {
+ vol := "vol-test-persistent-volume-claim"
+
+ // we need a container name because IDs don't persist after rm/play
+ ctrName := "test-persistent-volume-claim"
+ ctrNameInKubePod := "test1-test-persistent-volume-claim"
+
+ session := podmanTest.Podman([]string{"run", "-d", "--pod", "new:test1", "--name", ctrName, "-v", vol + ":/volume/:z", "alpine", "top"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+
+ outputFile := filepath.Join(podmanTest.RunRoot, "pod.yaml")
+ kube := podmanTest.Podman([]string{"generate", "kube", "test1", "-f", outputFile})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube.ExitCode()).To(Equal(0))
+
+ rm := podmanTest.Podman([]string{"pod", "rm", "-f", "test1"})
+ rm.WaitWithDefaultTimeout()
+ Expect(rm.ExitCode()).To(Equal(0))
+
+ play := podmanTest.Podman([]string{"play", "kube", outputFile})
+ play.WaitWithDefaultTimeout()
+ Expect(play.ExitCode()).To(Equal(0))
+
+ inspect := podmanTest.Podman([]string{"inspect", ctrNameInKubePod})
+ inspect.WaitWithDefaultTimeout()
+ Expect(inspect.ExitCode()).To(Equal(0))
+ Expect(inspect.OutputToString()).To(ContainSubstring(vol))
+ })
+
It("podman generate kube sharing pid namespace", func() {
podName := "test"
podSession := podmanTest.Podman([]string{"pod", "create", "--name", podName, "--share", "pid"})
diff --git a/test/e2e/run_test.go b/test/e2e/run_test.go
index 4e5106731..bb1f9590d 100644
--- a/test/e2e/run_test.go
+++ b/test/e2e/run_test.go
@@ -1412,7 +1412,28 @@ USER mail`
})
It("podman run --tz", func() {
- session := podmanTest.Podman([]string{"run", "--tz", "foo", "--rm", ALPINE, "date"})
+ testDir := filepath.Join(podmanTest.RunRoot, "tz-test")
+ err := os.MkdirAll(testDir, 0755)
+ Expect(err).To(BeNil())
+
+ tzFile := filepath.Join(testDir, "tzfile.txt")
+ file, err := os.Create(tzFile)
+ Expect(err).To(BeNil())
+
+ _, err = file.WriteString("Hello")
+ Expect(err).To(BeNil())
+ file.Close()
+
+ badTZFile := fmt.Sprintf("../../../%s", tzFile)
+ session := podmanTest.Podman([]string{"run", "--tz", badTZFile, "--rm", ALPINE, "date"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Not(Equal(0)))
+ Expect(session.ErrorToString()).To(ContainSubstring("error finding timezone for container"))
+
+ err = os.Remove(tzFile)
+ Expect(err).To(BeNil())
+
+ session = podmanTest.Podman([]string{"run", "--tz", "foo", "--rm", ALPINE, "date"})
session.WaitWithDefaultTimeout()
Expect(session.ExitCode()).To(Not(Equal(0)))
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 02c5d7867..22a2ece0a 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -518,7 +518,7 @@ github.com/prometheus/procfs/internal/fs
github.com/prometheus/procfs/internal/util
# github.com/rivo/uniseg v0.2.0
github.com/rivo/uniseg
-# github.com/rootless-containers/rootlesskit v0.14.0-beta.0
+# github.com/rootless-containers/rootlesskit v0.14.0
github.com/rootless-containers/rootlesskit/pkg/api
github.com/rootless-containers/rootlesskit/pkg/msgutil
github.com/rootless-containers/rootlesskit/pkg/port