summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Williams <jordan@jwillikers.com>2021-03-17 13:39:33 -0500
committerMatthew Heon <mheon@redhat.com>2021-03-29 13:41:19 -0400
commit633ae014e6945670676e4118356d09418c678138 (patch)
tree825741084f9eb93565b62fa3f3404b5967b49960
parentc9640bab79865a5fe731e0aaf27f12036ba9fbd5 (diff)
downloadpodman-633ae014e6945670676e4118356d09418c678138.tar.gz
podman-633ae014e6945670676e4118356d09418c678138.tar.bz2
podman-633ae014e6945670676e4118356d09418c678138.zip
Generate Kubernetes PersistentVolumeClaims from named volumes
Fixes #5788 This commit adds support for named volumes in podman-generate-kube. Named volumes are output in the YAML as PersistentVolumeClaims. To avoid naming conflicts, the volume name is suffixed with "-pvc". This commit adds a corresponding suffix for host path mounts. Host path volumes are suffixed with "-host". Signed-off-by: Jordan Williams <jordan@jwillikers.com>
-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--libpod/kube.go32
-rw-r--r--test/e2e/generate_kube_test.go30
4 files changed, 174 insertions, 7 deletions
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/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/test/e2e/generate_kube_test.go b/test/e2e/generate_kube_test.go
index e74192746..1c53307bd 100644
--- a/test/e2e/generate_kube_test.go
+++ b/test/e2e/generate_kube_test.go
@@ -495,6 +495,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"})