mirror of
https://github.com/containers/podman.git
synced 2025-06-29 23:22:40 +08:00
Add volumes-from support using annotation in kube yaml
The reserved annotation io.podman.annotations.volumes-from is made public to let user define volumes-from to have one container mount volumes of other containers. The annotation format is: io.podman.annotations.volumes-from/tgtCtr: "srcCtr1:mntOpts1;srcCtr2:mntOpts;..." Fixes: containers#16819 Signed-off-by: Vikas Goel <vikas.goel@gmail.com>
This commit is contained in:
@ -41,6 +41,8 @@ Note: *hostPath* volume types created by kube play is given an SELinux shared la
|
|||||||
|
|
||||||
Note: To set userns of a pod, use the **io.podman.annotations.userns** annotation in the pod/deployment definition. This can be overridden with the `--userns` flag.
|
Note: To set userns of a pod, use the **io.podman.annotations.userns** annotation in the pod/deployment definition. This can be overridden with the `--userns` flag.
|
||||||
|
|
||||||
|
Note: Use the **io.podman.annotations.volumes-from** annotation to bind mount volumes of one container to another. You can mount volumes from multiple source containers to a target container. The source containers that belong to the same pod must be defined before the source container in the kube YAML. The annotation format is `io.podman.annotations.volumes-from/targetContainer: "sourceContainer1:mountOpts1;sourceContainer2:mountOpts2"`.
|
||||||
|
|
||||||
Note: If the `:latest` tag is used, Podman attempts to pull the image from a registry. If the image was built locally with Podman or Buildah, it has `localhost` as the domain, in that case, Podman uses the image from the local store even if it has the `:latest` tag.
|
Note: If the `:latest` tag is used, Podman attempts to pull the image from a registry. If the image was built locally with Podman or Buildah, it has `localhost` as the domain, in that case, Podman uses the image from the local store even if it has the `:latest` tag.
|
||||||
|
|
||||||
Note: The command `podman play kube` is an alias of `podman kube play`, and performs the same function.
|
Note: The command `podman play kube` is an alias of `podman kube play`, and performs the same function.
|
||||||
|
@ -53,7 +53,7 @@ func (c *Container) volumesFrom() ([]string, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if ctrs, ok := ctrSpec.Annotations[define.InspectAnnotationVolumesFrom]; ok {
|
if ctrs, ok := ctrSpec.Annotations[define.VolumesFromAnnotation]; ok {
|
||||||
return strings.Split(ctrs, ";"), nil
|
return strings.Split(ctrs, ";"), nil
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -510,7 +510,7 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
|
|||||||
if ctrSpec.Annotations[define.InspectAnnotationAutoremove] == define.InspectResponseTrue {
|
if ctrSpec.Annotations[define.InspectAnnotationAutoremove] == define.InspectResponseTrue {
|
||||||
hostConfig.AutoRemove = true
|
hostConfig.AutoRemove = true
|
||||||
}
|
}
|
||||||
if ctrs, ok := ctrSpec.Annotations[define.InspectAnnotationVolumesFrom]; ok {
|
if ctrs, ok := ctrSpec.Annotations[define.VolumesFromAnnotation]; ok {
|
||||||
hostConfig.VolumesFrom = strings.Split(ctrs, ";")
|
hostConfig.VolumesFrom = strings.Split(ctrs, ";")
|
||||||
}
|
}
|
||||||
if ctrSpec.Annotations[define.InspectAnnotationPrivileged] == define.InspectResponseTrue {
|
if ctrSpec.Annotations[define.InspectAnnotationPrivileged] == define.InspectResponseTrue {
|
||||||
|
@ -18,13 +18,6 @@ const (
|
|||||||
// the two supported boolean values (InspectResponseTrue and
|
// the two supported boolean values (InspectResponseTrue and
|
||||||
// InspectResponseFalse) it will be used in the output of Inspect().
|
// InspectResponseFalse) it will be used in the output of Inspect().
|
||||||
InspectAnnotationAutoremove = "io.podman.annotations.autoremove"
|
InspectAnnotationAutoremove = "io.podman.annotations.autoremove"
|
||||||
// InspectAnnotationVolumesFrom is used by Inspect to identify
|
|
||||||
// containers whose volumes are being used by this container.
|
|
||||||
// It is expected to be a comma-separated list of container names and/or
|
|
||||||
// IDs.
|
|
||||||
// If an annotation with this key is found in the OCI spec, it will be
|
|
||||||
// used in the output of Inspect().
|
|
||||||
InspectAnnotationVolumesFrom = "io.podman.annotations.volumes-from"
|
|
||||||
// InspectAnnotationPrivileged is used by Inspect to identify containers
|
// InspectAnnotationPrivileged is used by Inspect to identify containers
|
||||||
// which are privileged (IE, running with elevated privileges).
|
// which are privileged (IE, running with elevated privileges).
|
||||||
// It is expected to be a boolean, populated by one of
|
// It is expected to be a boolean, populated by one of
|
||||||
@ -157,6 +150,12 @@ const (
|
|||||||
// of the container
|
// of the container
|
||||||
UlimitAnnotation = "io.podman.annotations.ulimit"
|
UlimitAnnotation = "io.podman.annotations.ulimit"
|
||||||
|
|
||||||
|
// VolumesFromAnnotation is used by by play kube when playing a kube
|
||||||
|
// yaml to specify volumes-from of the container
|
||||||
|
// It is expected to be a semicolon-separated list of container names and/or
|
||||||
|
// IDs optionally with colon separated mount options.
|
||||||
|
VolumesFromAnnotation = "io.podman.annotations.volumes-from"
|
||||||
|
|
||||||
// KubeHealthCheckAnnotation is used by kube play to tell podman that any health checks should follow
|
// KubeHealthCheckAnnotation is used by kube play to tell podman that any health checks should follow
|
||||||
// the k8s behavior of waiting for the intialDelaySeconds to be over before updating the status
|
// the k8s behavior of waiting for the intialDelaySeconds to be over before updating the status
|
||||||
KubeHealthCheckAnnotation = "io.podman.annotations.kube.health.check"
|
KubeHealthCheckAnnotation = "io.podman.annotations.kube.health.check"
|
||||||
@ -169,7 +168,7 @@ const (
|
|||||||
// already reserved annotation that Podman sets during container creation.
|
// already reserved annotation that Podman sets during container creation.
|
||||||
func IsReservedAnnotation(value string) bool {
|
func IsReservedAnnotation(value string) bool {
|
||||||
switch value {
|
switch value {
|
||||||
case InspectAnnotationCIDFile, InspectAnnotationAutoremove, InspectAnnotationVolumesFrom, InspectAnnotationPrivileged, InspectAnnotationPublishAll, InspectAnnotationInit, InspectAnnotationLabel, InspectAnnotationSeccomp, InspectAnnotationApparmor, InspectResponseTrue, InspectResponseFalse:
|
case InspectAnnotationCIDFile, InspectAnnotationAutoremove, InspectAnnotationPrivileged, InspectAnnotationPublishAll, InspectAnnotationInit, InspectAnnotationLabel, InspectAnnotationSeccomp, InspectAnnotationApparmor, InspectResponseTrue, InspectResponseFalse, VolumesFromAnnotation:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -277,7 +277,7 @@ func (p *Pod) VolumesFrom() []string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if ctrs, ok := infra.config.Spec.Annotations[define.InspectAnnotationVolumesFrom]; ok {
|
if ctrs, ok := infra.config.Spec.Annotations[define.VolumesFromAnnotation]; ok {
|
||||||
return strings.Split(ctrs, ";")
|
return strings.Split(ctrs, ";")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -124,6 +124,51 @@ func (ic *ContainerEngine) createServiceContainer(ctx context.Context, name stri
|
|||||||
return ctr, nil
|
return ctr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func prepareVolumesFrom(forContainer, podName string, ctrNames, annotations map[string]string) ([]string, error) {
|
||||||
|
annotationVolsFrom := define.VolumesFromAnnotation + "/" + forContainer
|
||||||
|
|
||||||
|
volsFromCtrs, ok := annotations[annotationVolsFrom]
|
||||||
|
|
||||||
|
// No volumes-from specified
|
||||||
|
if !ok || volsFromCtrs == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// The volumes-from string is a semicolon-separated container names
|
||||||
|
// optionally with respective mount options.
|
||||||
|
volumesFrom := strings.Split(volsFromCtrs, ";")
|
||||||
|
for idx, volsFromCtr := range volumesFrom {
|
||||||
|
// Each entry is of format "container[:mount-options]"
|
||||||
|
fields := strings.Split(volsFromCtr, ":")
|
||||||
|
if len(fields) != 1 && len(fields) != 2 {
|
||||||
|
return nil, fmt.Errorf("invalid annotation %s value", annotationVolsFrom)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fields[0] == "" {
|
||||||
|
return nil, fmt.Errorf("from container name cannot be empty in annotation %s", annotationVolsFrom)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source and target containers cannot be same
|
||||||
|
if fields[0] == forContainer {
|
||||||
|
return nil, fmt.Errorf("to and from container names cannot be same in annotation %s", annotationVolsFrom)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the source container name if it belongs to the pod
|
||||||
|
// the source container must exist before the target container
|
||||||
|
// in the kube yaml. Otherwise, the source container will be
|
||||||
|
// treated as an external container. This also helps in avoiding
|
||||||
|
// cyclic dependencies between containers within the pod.
|
||||||
|
if _, ok := ctrNames[fields[0]]; ok {
|
||||||
|
volumesFrom[idx] = podName + "-" + fields[0]
|
||||||
|
if len(fields) == 2 {
|
||||||
|
volumesFrom[idx] = volumesFrom[idx] + ":" + fields[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return volumesFrom, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Creates the name for a k8s entity based on the provided content of a
|
// Creates the name for a k8s entity based on the provided content of a
|
||||||
// K8s yaml file and a given suffix.
|
// K8s yaml file and a given suffix.
|
||||||
func k8sName(content []byte, suffix string) string {
|
func k8sName(content []byte, suffix string) string {
|
||||||
@ -475,6 +520,10 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||||||
return nil, nil, fmt.Errorf("pod does not have a name")
|
return nil, nil, fmt.Errorf("pod does not have a name")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, ok := annotations[define.VolumesFromAnnotation]; ok {
|
||||||
|
return nil, nil, fmt.Errorf("annotation %s without target volume is reserved for internal use", define.VolumesFromAnnotation)
|
||||||
|
}
|
||||||
|
|
||||||
podOpt := entities.PodCreateOptions{
|
podOpt := entities.PodCreateOptions{
|
||||||
Infra: true,
|
Infra: true,
|
||||||
Net: &entities.NetOptions{NoHosts: options.NoHosts},
|
Net: &entities.NetOptions{NoHosts: options.NoHosts},
|
||||||
@ -778,6 +827,13 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||||||
initCtrType = define.OneShotInitContainer
|
initCtrType = define.OneShotInitContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var volumesFrom []string
|
||||||
|
if list, err := prepareVolumesFrom(initCtr.Name, podName, ctrNames, annotations); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
} else if list != nil {
|
||||||
|
volumesFrom = list
|
||||||
|
}
|
||||||
|
|
||||||
specgenOpts := kube.CtrSpecGenOptions{
|
specgenOpts := kube.CtrSpecGenOptions{
|
||||||
Annotations: annotations,
|
Annotations: annotations,
|
||||||
ConfigMaps: configMaps,
|
ConfigMaps: configMaps,
|
||||||
@ -798,6 +854,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||||||
SecretsManager: secretsManager,
|
SecretsManager: secretsManager,
|
||||||
UserNSIsHost: p.Userns.IsHost(),
|
UserNSIsHost: p.Userns.IsHost(),
|
||||||
Volumes: volumes,
|
Volumes: volumes,
|
||||||
|
VolumesFrom: volumesFrom,
|
||||||
UtsNSIsHost: p.UtsNs.IsHost(),
|
UtsNSIsHost: p.UtsNs.IsHost(),
|
||||||
}
|
}
|
||||||
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
|
specGen, err := kube.ToSpecGen(ctx, &specgenOpts)
|
||||||
@ -854,6 +911,13 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||||||
labels[k] = v
|
labels[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var volumesFrom []string
|
||||||
|
if list, err := prepareVolumesFrom(container.Name, podName, ctrNames, annotations); err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
} else if list != nil {
|
||||||
|
volumesFrom = list
|
||||||
|
}
|
||||||
|
|
||||||
specgenOpts := kube.CtrSpecGenOptions{
|
specgenOpts := kube.CtrSpecGenOptions{
|
||||||
Annotations: annotations,
|
Annotations: annotations,
|
||||||
ConfigMaps: configMaps,
|
ConfigMaps: configMaps,
|
||||||
@ -874,6 +938,7 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
|||||||
SecretsManager: secretsManager,
|
SecretsManager: secretsManager,
|
||||||
UserNSIsHost: p.Userns.IsHost(),
|
UserNSIsHost: p.Userns.IsHost(),
|
||||||
Volumes: volumes,
|
Volumes: volumes,
|
||||||
|
VolumesFrom: volumesFrom,
|
||||||
UtsNSIsHost: p.UtsNs.IsHost(),
|
UtsNSIsHost: p.UtsNs.IsHost(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -140,6 +140,8 @@ type CtrSpecGenOptions struct {
|
|||||||
IpcNSIsHost bool
|
IpcNSIsHost bool
|
||||||
// Volumes for all containers
|
// Volumes for all containers
|
||||||
Volumes map[string]*KubeVolume
|
Volumes map[string]*KubeVolume
|
||||||
|
// VolumesFrom for all containers
|
||||||
|
VolumesFrom []string
|
||||||
// PodID of the parent pod
|
// PodID of the parent pod
|
||||||
PodID string
|
PodID string
|
||||||
// PodName of the parent pod
|
// PodName of the parent pod
|
||||||
@ -566,6 +568,8 @@ func ToSpecGen(ctx context.Context, opts *CtrSpecGenOptions) (*specgen.SpecGener
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.VolumesFrom = opts.VolumesFrom
|
||||||
|
|
||||||
s.RestartPolicy = opts.RestartPolicy
|
s.RestartPolicy = opts.RestartPolicy
|
||||||
|
|
||||||
if opts.NetNSIsHost {
|
if opts.NetNSIsHost {
|
||||||
|
@ -152,7 +152,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(s.VolumesFrom) > 0 {
|
if len(s.VolumesFrom) > 0 {
|
||||||
configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ";")
|
configSpec.Annotations[define.VolumesFromAnnotation] = strings.Join(s.VolumesFrom, ";")
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.IsPrivileged() {
|
if s.IsPrivileged() {
|
||||||
|
@ -322,7 +322,7 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(s.VolumesFrom) > 0 {
|
if len(s.VolumesFrom) > 0 {
|
||||||
configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ";")
|
configSpec.Annotations[define.VolumesFromAnnotation] = strings.Join(s.VolumesFrom, ";")
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.IsPrivileged() {
|
if s.IsPrivileged() {
|
||||||
|
@ -68,6 +68,6 @@ var _ = Describe("Podman container inspect", func() {
|
|||||||
data := podmanTest.InspectContainer(ctr2)
|
data := podmanTest.InspectContainer(ctr2)
|
||||||
Expect(data).To(HaveLen(1))
|
Expect(data).To(HaveLen(1))
|
||||||
Expect(data[0].HostConfig.VolumesFrom).To(Equal([]string{volsctr}))
|
Expect(data[0].HostConfig.VolumesFrom).To(Equal([]string{volsctr}))
|
||||||
Expect(data[0].Config.Annotations[define.InspectAnnotationVolumesFrom]).To(Equal(volsctr))
|
Expect(data[0].Config.Annotations[define.VolumesFromAnnotation]).To(Equal(volsctr))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -1696,7 +1696,7 @@ USER test1`
|
|||||||
pod := new(v1.Pod)
|
pod := new(v1.Pod)
|
||||||
err = yaml.Unmarshal(kube.Out.Contents(), pod)
|
err = yaml.Unmarshal(kube.Out.Contents(), pod)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(pod.Annotations).To(HaveKeyWithValue(define.InspectAnnotationVolumesFrom+"/"+ctr2, ctr1))
|
Expect(pod.Annotations).To(HaveKeyWithValue(define.VolumesFromAnnotation+"/"+ctr2, ctr1))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("pod volumes-from annotation with semicolon as field separator", func() {
|
It("pod volumes-from annotation with semicolon as field separator", func() {
|
||||||
@ -1734,7 +1734,7 @@ USER test1`
|
|||||||
pod := new(v1.Pod)
|
pod := new(v1.Pod)
|
||||||
err3 := yaml.Unmarshal(kube.Out.Contents(), pod)
|
err3 := yaml.Unmarshal(kube.Out.Contents(), pod)
|
||||||
Expect(err3).ToNot(HaveOccurred())
|
Expect(err3).ToNot(HaveOccurred())
|
||||||
Expect(pod.Annotations).To(HaveKeyWithValue(define.InspectAnnotationVolumesFrom+"/"+tgtctr, frmopt1+";"+frmopt2))
|
Expect(pod.Annotations).To(HaveKeyWithValue(define.VolumesFromAnnotation+"/"+tgtctr, frmopt1+";"+frmopt2))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("--podman-only on container with --rm", func() {
|
It("--podman-only on container with --rm", func() {
|
||||||
|
@ -492,6 +492,34 @@ spec:
|
|||||||
status: {}
|
status: {}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
var volumesFromPodYaml = `
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Pod
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
io.podman.annotations.volumes-from/tgtctr: srcctr:ro
|
||||||
|
name: volspod
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: srcctr
|
||||||
|
image: ` + CITEST_IMAGE + `
|
||||||
|
command:
|
||||||
|
- sleep
|
||||||
|
- inf
|
||||||
|
volumeMounts:
|
||||||
|
- mountPath: /mnt/vol
|
||||||
|
name: testing
|
||||||
|
- name: tgtctr
|
||||||
|
image: ` + CITEST_IMAGE + `
|
||||||
|
command:
|
||||||
|
- sleep
|
||||||
|
- inf
|
||||||
|
volumes:
|
||||||
|
- name: testing
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: testvol
|
||||||
|
`
|
||||||
|
|
||||||
var configMapYamlTemplate = `
|
var configMapYamlTemplate = `
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ConfigMap
|
kind: ConfigMap
|
||||||
@ -5909,7 +5937,7 @@ spec:
|
|||||||
Expect(session).Should(Exit(125))
|
Expect(session).Should(Exit(125))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("test with reserved volumes-from annotation in yaml", func() {
|
It("test pod with volumes-from annotation in yaml", func() {
|
||||||
ctr1 := "ctr1"
|
ctr1 := "ctr1"
|
||||||
ctr2 := "ctr2"
|
ctr2 := "ctr2"
|
||||||
ctrNameInKubePod := ctr2 + "-pod-" + ctr2
|
ctrNameInKubePod := ctr2 + "-pod-" + ctr2
|
||||||
@ -5927,7 +5955,7 @@ spec:
|
|||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
Expect(session).Should(ExitCleanly())
|
Expect(session).Should(ExitCleanly())
|
||||||
|
|
||||||
kube := podmanTest.Podman([]string{"kube", "generate", "--podman-only", "-f", outputFile, ctr2})
|
kube := podmanTest.Podman([]string{"kube", "generate", "-f", outputFile, ctr2})
|
||||||
kube.WaitWithDefaultTimeout()
|
kube.WaitWithDefaultTimeout()
|
||||||
Expect(kube).Should(ExitCleanly())
|
Expect(kube).Should(ExitCleanly())
|
||||||
|
|
||||||
@ -5952,6 +5980,114 @@ spec:
|
|||||||
Expect(podrm).Should(ExitCleanly())
|
Expect(podrm).Should(ExitCleanly())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("test volumes-from annotation with source containers external", func() {
|
||||||
|
// Assert that volumes of multiple source containers, listed in
|
||||||
|
// volumes-from annotation, running outside the pod are
|
||||||
|
// getting mounted inside the target container.
|
||||||
|
|
||||||
|
srcctr1, srcctr2, tgtctr := "srcctr1", "srcctr2", "tgtctr"
|
||||||
|
frmopt1, frmopt2 := srcctr1+":ro", srcctr2+":ro"
|
||||||
|
vol1 := filepath.Join(podmanTest.TempDir, "vol-test1")
|
||||||
|
vol2 := filepath.Join(podmanTest.TempDir, "vol-test2")
|
||||||
|
|
||||||
|
volsFromAnnotaton := define.VolumesFromAnnotation + "/" + tgtctr
|
||||||
|
volsFromValue := frmopt1 + ";" + frmopt2
|
||||||
|
|
||||||
|
err1 := os.MkdirAll(vol1, 0755)
|
||||||
|
Expect(err1).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
err2 := os.MkdirAll(vol2, 0755)
|
||||||
|
Expect(err2).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
session := podmanTest.Podman([]string{"create", "--name", srcctr1, "-v", vol1, CITEST_IMAGE})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(ExitCleanly())
|
||||||
|
|
||||||
|
session = podmanTest.Podman([]string{"create", "--name", srcctr2, "-v", vol2, CITEST_IMAGE})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(ExitCleanly())
|
||||||
|
|
||||||
|
podName := tgtctr
|
||||||
|
pod := getPod(
|
||||||
|
withPodName(podName),
|
||||||
|
withCtr(getCtr(withName(tgtctr))),
|
||||||
|
withAnnotation(volsFromAnnotaton, volsFromValue))
|
||||||
|
|
||||||
|
err3 := generateKubeYaml("pod", pod, kubeYaml)
|
||||||
|
Expect(err3).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
kube := podmanTest.Podman([]string{"kube", "play", kubeYaml})
|
||||||
|
kube.WaitWithDefaultTimeout()
|
||||||
|
Expect(kube).Should(ExitCleanly())
|
||||||
|
|
||||||
|
// Assert volumes are accessible inside the target container
|
||||||
|
ctrNameInKubePod := podName + "-" + tgtctr
|
||||||
|
|
||||||
|
inspect := podmanTest.InspectContainer(ctrNameInKubePod)
|
||||||
|
Expect(inspect).To(HaveLen(1))
|
||||||
|
|
||||||
|
exec := podmanTest.Podman([]string{"exec", ctrNameInKubePod, "ls", "-d", vol1, vol2})
|
||||||
|
exec.WaitWithDefaultTimeout()
|
||||||
|
Expect(exec).Should(ExitCleanly())
|
||||||
|
Expect(exec.OutputToString()).To(ContainSubstring(vol1))
|
||||||
|
Expect(exec.OutputToString()).To(ContainSubstring(vol2))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("test volumes-from annotation with source container in pod", func() {
|
||||||
|
// Assert that volume of source container, member of the pod,
|
||||||
|
// listed in volumes-from annotation is getting mounted inside
|
||||||
|
// the target container.
|
||||||
|
|
||||||
|
srcctr, tgtctr, podName := "srcctr", "tgtctr", "volspod"
|
||||||
|
vol := "/mnt/vol"
|
||||||
|
|
||||||
|
srcctrInKubePod := podName + "-" + srcctr
|
||||||
|
tgtctrInKubePod := podName + "-" + tgtctr
|
||||||
|
|
||||||
|
err := writeYaml(volumesFromPodYaml, kubeYaml)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
kube := podmanTest.Podman([]string{"kube", "play", kubeYaml})
|
||||||
|
kube.WaitWithDefaultTimeout()
|
||||||
|
Expect(kube).Should(ExitCleanly())
|
||||||
|
|
||||||
|
inspect := podmanTest.InspectContainer(tgtctrInKubePod)
|
||||||
|
Expect(inspect).To(HaveLen(1))
|
||||||
|
|
||||||
|
// Assert volume is accessible inside the target container
|
||||||
|
// by creating contents in the volume and accessing that from
|
||||||
|
// the target container.
|
||||||
|
volFile := filepath.Join(vol, RandomString(10)+".txt")
|
||||||
|
|
||||||
|
exec := podmanTest.Podman([]string{"exec", srcctrInKubePod, "touch", volFile})
|
||||||
|
exec.WaitWithDefaultTimeout()
|
||||||
|
Expect(exec).Should(ExitCleanly())
|
||||||
|
|
||||||
|
exec = podmanTest.Podman([]string{"exec", srcctrInKubePod, "ls", volFile})
|
||||||
|
exec.WaitWithDefaultTimeout()
|
||||||
|
Expect(exec).Should(ExitCleanly())
|
||||||
|
Expect(exec.OutputToString()).To(ContainSubstring(volFile))
|
||||||
|
|
||||||
|
exec = podmanTest.Podman([]string{"exec", tgtctrInKubePod, "ls", volFile})
|
||||||
|
exec.WaitWithDefaultTimeout()
|
||||||
|
Expect(exec).Should(ExitCleanly())
|
||||||
|
Expect(exec.OutputToString()).To(ContainSubstring(volFile))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("test with reserved volumes-from annotation in yaml", func() {
|
||||||
|
// Assert that volumes-from annotation without target container
|
||||||
|
// errors out.
|
||||||
|
|
||||||
|
pod := getPod(withAnnotation(define.VolumesFromAnnotation, "reserved"))
|
||||||
|
err := generateKubeYaml("pod", pod, kubeYaml)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
kube := podmanTest.Podman([]string{"kube", "play", kubeYaml})
|
||||||
|
kube.WaitWithDefaultTimeout()
|
||||||
|
Expect(kube).Should(Exit(125))
|
||||||
|
Expect(kube.ErrorToString()).To(ContainSubstring("annotation " + define.VolumesFromAnnotation + " without target volume is reserved for internal use"))
|
||||||
|
})
|
||||||
|
|
||||||
It("test with reserved autoremove annotation in yaml", func() {
|
It("test with reserved autoremove annotation in yaml", func() {
|
||||||
ctr := "ctr"
|
ctr := "ctr"
|
||||||
ctrNameInKubePod := ctr + "-pod-" + ctr
|
ctrNameInKubePod := ctr + "-pod-" + ctr
|
||||||
|
Reference in New Issue
Block a user