aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2022-08-31 09:36:04 -0400
committerGitHub <noreply@github.com>2022-08-31 09:36:04 -0400
commit8266dbe7a90f6f03995b42315b3ac2da54cd018f (patch)
treede047861dc6efaaf66ce626ccd57501b0eccbb08
parent60e4b762b3c6eef38e3f2d635011bbeafa157ba1 (diff)
parent98169c20ddc09d8fa14d556e93cad3259b5ccca9 (diff)
downloadpodman-8266dbe7a90f6f03995b42315b3ac2da54cd018f.tar.gz
podman-8266dbe7a90f6f03995b42315b3ac2da54cd018f.tar.bz2
podman-8266dbe7a90f6f03995b42315b3ac2da54cd018f.zip
Merge pull request #15473 from umohnani8/empty-dir
Add emptyDir volume support to kube play
-rw-r--r--docs/source/markdown/podman-kube-play.1.md.in2
-rw-r--r--libpod/container.go3
-rw-r--r--libpod/options.go7
-rw-r--r--libpod/runtime_ctr.go11
-rw-r--r--pkg/domain/infra/abi/play.go2
-rw-r--r--pkg/k8s.io/api/core/v1/types.go4
-rw-r--r--pkg/specgen/generate/container_create.go7
-rw-r--r--pkg/specgen/generate/kube/kube.go9
-rw-r--r--pkg/specgen/generate/kube/volume.go14
-rw-r--r--pkg/specgen/volumes.go3
-rw-r--r--test/e2e/play_kube_test.go51
11 files changed, 98 insertions, 15 deletions
diff --git a/docs/source/markdown/podman-kube-play.1.md.in b/docs/source/markdown/podman-kube-play.1.md.in
index e9f847abc..bcd5687ca 100644
--- a/docs/source/markdown/podman-kube-play.1.md.in
+++ b/docs/source/markdown/podman-kube-play.1.md.in
@@ -21,7 +21,7 @@ Currently, the supported Kubernetes kinds are:
`Kubernetes Pods or Deployments`
-Only two volume types are supported by kube play, the *hostPath* and *persistentVolumeClaim* volume types. For the *hostPath* volume type, only the *default (empty)*, *DirectoryOrCreate*, *Directory*, *FileOrCreate*, *File*, *Socket*, *CharDevice* and *BlockDevice* subtypes are 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.
+Only three volume types are supported by kube play, the *hostPath*, *emptyDir*, and *persistentVolumeClaim* volume types. For the *hostPath* volume type, only the *default (empty)*, *DirectoryOrCreate*, *Directory*, *FileOrCreate*, *File*, *Socket*, *CharDevice* and *BlockDevice* subtypes are 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. When using an *emptyDir* volume, podman creates an anonymous volume that is attached the containers running inside the pod and is deleted once the pod is removed.
Note: When playing a kube YAML with init containers, the init container will be created with init type value `once`. To change the default type, use the `io.podman.annotations.init.container.type` annotation to set the type to `always`.
diff --git a/libpod/container.go b/libpod/container.go
index 6c05b1084..44a8669fd 100644
--- a/libpod/container.go
+++ b/libpod/container.go
@@ -237,6 +237,9 @@ type ContainerNamedVolume struct {
Dest string `json:"dest"`
// Options are fstab style mount options
Options []string `json:"options,omitempty"`
+ // IsAnonymous sets the named volume as anonymous even if it has a name
+ // This is used for emptyDir volumes from a kube yaml
+ IsAnonymous bool `json:"setAnonymous,omitempty"`
}
// ContainerOverlayVolume is a overlay volume that will be mounted into the
diff --git a/libpod/options.go b/libpod/options.go
index d31741094..56d5265d2 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1413,9 +1413,10 @@ func WithNamedVolumes(volumes []*ContainerNamedVolume) CtrCreateOption {
}
ctr.config.NamedVolumes = append(ctr.config.NamedVolumes, &ContainerNamedVolume{
- Name: vol.Name,
- Dest: vol.Dest,
- Options: mountOpts,
+ Name: vol.Name,
+ Dest: vol.Dest,
+ Options: mountOpts,
+ IsAnonymous: vol.IsAnonymous,
})
}
diff --git a/libpod/runtime_ctr.go b/libpod/runtime_ctr.go
index 703ae5cbe..b43114fab 100644
--- a/libpod/runtime_ctr.go
+++ b/libpod/runtime_ctr.go
@@ -474,6 +474,11 @@ func (r *Runtime) setupContainer(ctx context.Context, ctr *Container) (_ *Contai
return nil, fmt.Errorf("error retrieving named volume %s for new container: %w", vol.Name, err)
}
}
+ if vol.IsAnonymous {
+ // If SetAnonymous is true, make this an anonymous volume
+ // this is needed for emptyDir volumes from kube yamls
+ isAnonymous = true
+ }
logrus.Debugf("Creating new volume %s for container", vol.Name)
@@ -814,11 +819,11 @@ func (r *Runtime) removeContainer(ctx context.Context, c *Container, force, remo
// Ignore error, since podman will report original error
volumesFrom, _ := c.volumesFrom()
if len(volumesFrom) > 0 {
- logrus.Debugf("Cleaning up volume not possible since volume is in use (%s)", v)
+ logrus.Debugf("Cleaning up volume not possible since volume is in use (%s)", v.Name)
continue
}
}
- logrus.Errorf("Cleaning up volume (%s): %v", v, err)
+ logrus.Errorf("Cleaning up volume (%s): %v", v.Name, err)
}
}
}
@@ -968,7 +973,7 @@ func (r *Runtime) evictContainer(ctx context.Context, idOrName string, removeVol
continue
}
if err := r.removeVolume(ctx, volume, false, timeout, false); err != nil && err != define.ErrNoSuchVolume && err != define.ErrVolumeBeingUsed {
- logrus.Errorf("Cleaning up volume (%s): %v", v, err)
+ logrus.Errorf("Cleaning up volume (%s): %v", v.Name, err)
}
}
}
diff --git a/pkg/domain/infra/abi/play.go b/pkg/domain/infra/abi/play.go
index faa89cc26..6ea20a4f2 100644
--- a/pkg/domain/infra/abi/play.go
+++ b/pkg/domain/infra/abi/play.go
@@ -436,7 +436,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
}
// Go through the volumes and create a podman volume for all volumes that have been
- // defined by a configmap
+ // defined by a configmap or secret
for _, v := range volumes {
if (v.Type == kube.KubeVolumeTypeConfigMap || v.Type == kube.KubeVolumeTypeSecret) && !v.Optional {
vol, err := ic.Libpod.NewVolume(ctx, libpod.WithVolumeName(v.Source))
diff --git a/pkg/k8s.io/api/core/v1/types.go b/pkg/k8s.io/api/core/v1/types.go
index 384965769..d47178878 100644
--- a/pkg/k8s.io/api/core/v1/types.go
+++ b/pkg/k8s.io/api/core/v1/types.go
@@ -58,6 +58,10 @@ type VolumeSource struct {
ConfigMap *ConfigMapVolumeSource `json:"configMap,omitempty"`
// Secret represents a secret that should be mounted as a volume
Secret *SecretVolumeSource `json:"secret,omitempty"`
+ // emptyDir represents a temporary directory that shares a pod's lifetime.
+ // More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir
+ // +optional
+ EmptyDir *EmptyDirVolumeSource `json:"emptyDir,omitempty"`
}
// PersistentVolumeClaimVolumeSource references the user's PVC in the same namespace.
diff --git a/pkg/specgen/generate/container_create.go b/pkg/specgen/generate/container_create.go
index e9cec2873..819800176 100644
--- a/pkg/specgen/generate/container_create.go
+++ b/pkg/specgen/generate/container_create.go
@@ -387,9 +387,10 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l
var vols []*libpod.ContainerNamedVolume
for _, v := range volumes {
vols = append(vols, &libpod.ContainerNamedVolume{
- Name: v.Name,
- Dest: v.Dest,
- Options: v.Options,
+ Name: v.Name,
+ Dest: v.Dest,
+ Options: v.Options,
+ IsAnonymous: v.IsAnonymous,
})
}
options = append(options, libpod.WithNamedVolumes(vols))
diff --git a/pkg/specgen/generate/kube/kube.go b/pkg/specgen/generate/kube/kube.go
index e9abf419b..375b719d3 100644
--- a/pkg/specgen/generate/kube/kube.go
+++ b/pkg/specgen/generate/kube/kube.go
@@ -406,8 +406,15 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
Name: volumeSource.Source,
Options: options,
}
-
s.Volumes = append(s.Volumes, &secretVolume)
+ case KubeVolumeTypeEmptyDir:
+ emptyDirVolume := specgen.NamedVolume{
+ Dest: volume.MountPath,
+ Name: volumeSource.Source,
+ Options: options,
+ IsAnonymous: true,
+ }
+ s.Volumes = append(s.Volumes, &emptyDirVolume)
default:
return nil, errors.New("unsupported volume source type")
}
diff --git a/pkg/specgen/generate/kube/volume.go b/pkg/specgen/generate/kube/volume.go
index c12adadd8..230521ec6 100644
--- a/pkg/specgen/generate/kube/volume.go
+++ b/pkg/specgen/generate/kube/volume.go
@@ -32,6 +32,7 @@ const (
KubeVolumeTypeBlockDevice
KubeVolumeTypeCharDevice
KubeVolumeTypeSecret
+ KubeVolumeTypeEmptyDir
)
//nolint:revive
@@ -219,8 +220,13 @@ func VolumeFromConfigMap(configMapVolumeSource *v1.ConfigMapVolumeSource, config
return kv, nil
}
+// Create a kubeVolume for an emptyDir volume
+func VolumeFromEmptyDir(emptyDirVolumeSource *v1.EmptyDirVolumeSource, name string) (*KubeVolume, error) {
+ return &KubeVolume{Type: KubeVolumeTypeEmptyDir, Source: name}, nil
+}
+
// Create a KubeVolume from one of the supported VolumeSource
-func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager) (*KubeVolume, error) {
+func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, secretsManager *secrets.SecretsManager, volName string) (*KubeVolume, error) {
switch {
case volumeSource.HostPath != nil:
return VolumeFromHostPath(volumeSource.HostPath)
@@ -230,8 +236,10 @@ func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap, s
return VolumeFromConfigMap(volumeSource.ConfigMap, configMaps)
case volumeSource.Secret != nil:
return VolumeFromSecret(volumeSource.Secret, secretsManager)
+ case volumeSource.EmptyDir != nil:
+ return VolumeFromEmptyDir(volumeSource.EmptyDir, volName)
default:
- return nil, errors.New("HostPath, ConfigMap, and PersistentVolumeClaim are currently the only supported VolumeSource")
+ return nil, errors.New("HostPath, ConfigMap, EmptyDir, and PersistentVolumeClaim are currently the only supported VolumeSource")
}
}
@@ -240,7 +248,7 @@ func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap, secre
volumes := make(map[string]*KubeVolume)
for _, specVolume := range specVolumes {
- volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps, secretsManager)
+ volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps, secretsManager, specVolume.Name)
if err != nil {
return nil, fmt.Errorf("failed to create volume %q: %w", specVolume.Name, err)
}
diff --git a/pkg/specgen/volumes.go b/pkg/specgen/volumes.go
index 84de4fdd1..e70ed5b13 100644
--- a/pkg/specgen/volumes.go
+++ b/pkg/specgen/volumes.go
@@ -23,6 +23,9 @@ type NamedVolume struct {
Dest string
// Options are options that the named volume will be mounted with.
Options []string
+ // IsAnonymous sets the named volume as anonymous even if it has a name
+ // This is used for emptyDir volumes from a kube yaml
+ IsAnonymous bool
}
// OverlayVolume holds information about a overlay volume that will be mounted into
diff --git a/test/e2e/play_kube_test.go b/test/e2e/play_kube_test.go
index 7d3a2224c..d1eb960cd 100644
--- a/test/e2e/play_kube_test.go
+++ b/test/e2e/play_kube_test.go
@@ -509,6 +509,9 @@ spec:
volumes:
{{ range . }}
- name: {{ .Name }}
+ {{- if (eq .VolumeType "EmptyDir") }}
+ emptyDir: {}
+ {{- end }}
{{- if (eq .VolumeType "HostPath") }}
hostPath:
path: {{ .HostPath.Path }}
@@ -1242,12 +1245,15 @@ type ConfigMap struct {
Optional bool
}
+type EmptyDir struct{}
+
type Volume struct {
VolumeType string
Name string
HostPath
PersistentVolumeClaim
ConfigMap
+ EmptyDir
}
// getHostPathVolume takes a type and a location for a HostPath
@@ -1289,6 +1295,14 @@ func getConfigMapVolume(vName string, items []map[string]string, optional bool)
}
}
+func getEmptyDirVolume() *Volume {
+ return &Volume{
+ VolumeType: "EmptyDir",
+ Name: defaultVolName,
+ EmptyDir: EmptyDir{},
+ }
+}
+
type Env struct {
Name string
Value string
@@ -2762,6 +2776,43 @@ VOLUME %s`, ALPINE, hostPathDir+"/")
Expect(kube).Should(Exit(0))
})
+ It("podman play kube with emptyDir volume", func() {
+ podName := "test-pod"
+ ctrName1 := "vol-test-ctr"
+ ctrName2 := "vol-test-ctr-2"
+ ctr1 := getCtr(withVolumeMount("/test-emptydir", false), withImage(BB), withName(ctrName1))
+ ctr2 := getCtr(withVolumeMount("/test-emptydir-2", false), withImage(BB), withName(ctrName2))
+ pod := getPod(withPodName(podName), withVolume(getEmptyDirVolume()), withCtr(ctr1), withCtr(ctr2))
+ err = generateKubeYaml("pod", pod, kubeYaml)
+ Expect(err).To(BeNil())
+
+ kube := podmanTest.Podman([]string{"play", "kube", kubeYaml})
+ kube.WaitWithDefaultTimeout()
+ Expect(kube).Should(Exit(0))
+
+ emptyDirCheck1 := podmanTest.Podman([]string{"exec", podName + "-" + ctrName1, "ls", "/test-emptydir"})
+ emptyDirCheck1.WaitWithDefaultTimeout()
+ Expect(emptyDirCheck1).Should(Exit(0))
+
+ emptyDirCheck2 := podmanTest.Podman([]string{"exec", podName + "-" + ctrName2, "ls", "/test-emptydir-2"})
+ emptyDirCheck2.WaitWithDefaultTimeout()
+ Expect(emptyDirCheck2).Should(Exit(0))
+
+ volList1 := podmanTest.Podman([]string{"volume", "ls", "-q"})
+ volList1.WaitWithDefaultTimeout()
+ Expect(volList1).Should(Exit(0))
+ Expect(volList1.OutputToString()).To(Equal(defaultVolName))
+
+ remove := podmanTest.Podman([]string{"pod", "rm", "-f", podName})
+ remove.WaitWithDefaultTimeout()
+ Expect(remove).Should(Exit(0))
+
+ volList2 := podmanTest.Podman([]string{"volume", "ls", "-q"})
+ volList2.WaitWithDefaultTimeout()
+ Expect(volList2).Should(Exit(0))
+ Expect(volList2.OutputToString()).To(Equal(""))
+ })
+
It("podman play kube applies labels to pods", func() {
var numReplicas int32 = 5
expectedLabelKey := "key1"