Merge pull request #15473 from umohnani8/empty-dir
Add emptyDir volume support to kube play
This commit is contained in:
commit
8266dbe7a9
|
@ -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`.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue