Skip to content

Commit

Permalink
Merge pull request #8497 from AlbanBedel/kube-volume
Browse files Browse the repository at this point in the history
Add support for persistent volume claims in kube files
  • Loading branch information
openshift-merge-robot authored Nov 28, 2020
2 parents 3110308 + 66944ba commit f0d48aa
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 96 deletions.
67 changes: 3 additions & 64 deletions pkg/domain/infra/abi/play.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"os"
"strings"

"github.com/containers/buildah/pkg/parse"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/libpod/image"
Expand All @@ -24,13 +23,6 @@ import (
v1 "k8s.io/api/core/v1"
)

const (
// https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
kubeDirectoryPermission = 0755
// https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
kubeFilePermission = 0644
)

func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
var (
kubeObject v1.ObjectReference
Expand Down Expand Up @@ -168,62 +160,9 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
DockerInsecureSkipTLSVerify: options.SkipTLSVerify,
}

// map from name to mount point
volumes := make(map[string]string)
for _, volume := range podYAML.Spec.Volumes {
hostPath := volume.VolumeSource.HostPath
if hostPath == nil {
return nil, errors.Errorf("HostPath is currently the only supported VolumeSource")
}
if hostPath.Type != nil {
switch *hostPath.Type {
case v1.HostPathDirectoryOrCreate:
if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) {
if err := os.Mkdir(hostPath.Path, kubeDirectoryPermission); err != nil {
return nil, err
}
}
// Label a newly created volume
if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path)
}
case v1.HostPathFileOrCreate:
if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) {
f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission)
if err != nil {
return nil, errors.Wrap(err, "error creating HostPath")
}
if err := f.Close(); err != nil {
logrus.Warnf("Error in closing newly created HostPath file: %v", err)
}
}
// unconditionally label a newly created volume
if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path)
}
case v1.HostPathSocket:
st, err := os.Stat(hostPath.Path)
if err != nil {
return nil, errors.Wrap(err, "error checking HostPathSocket")
}
if st.Mode()&os.ModeSocket != os.ModeSocket {
return nil, errors.Errorf("error checking HostPathSocket: path %s is not a socket", hostPath.Path)
}

case v1.HostPathDirectory:
case v1.HostPathFile:
case v1.HostPathUnset:
// do nothing here because we will verify the path exists in validateVolumeHostDir
break
default:
return nil, errors.Errorf("Invalid HostPath type %v", hostPath.Type)
}
}

if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil {
return nil, errors.Wrapf(err, "error in parsing HostPath in YAML")
}
volumes[volume.Name] = hostPath.Path
volumes, err := kube.InitializeVolumes(podYAML.Spec.Volumes)
if err != nil {
return nil, err
}

seccompPaths, err := kube.InitializeSeccompPaths(podYAML.ObjectMeta.Annotations, options.SeccompProfileRoot)
Expand Down
40 changes: 27 additions & 13 deletions pkg/specgen/generate/kube/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec)
return p, nil
}

func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newImage *image.Image, volumes map[string]string, podID, podName, infraID string, configMaps []v1.ConfigMap, seccompPaths *KubeSeccompPaths, restartPolicy string) (*specgen.SpecGenerator, error) {
func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newImage *image.Image, volumes map[string]*KubeVolume, podID, podName, infraID string, configMaps []v1.ConfigMap, seccompPaths *KubeSeccompPaths, restartPolicy string) (*specgen.SpecGenerator, error) {
s := specgen.NewSpecGenerator(iid, false)

// podName should be non-empty for Deployment objects to be able to create
Expand Down Expand Up @@ -163,22 +163,36 @@ func ToSpecGen(ctx context.Context, containerYAML v1.Container, iid string, newI
s.Env = envs

for _, volume := range containerYAML.VolumeMounts {
hostPath, exists := volumes[volume.Name]
volumeSource, exists := volumes[volume.Name]
if !exists {
return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name)
}
if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil {
return nil, errors.Wrapf(err, "error in parsing MountPath")
}
mount := spec.Mount{
Destination: volume.MountPath,
Source: hostPath,
Type: "bind",
}
if volume.ReadOnly {
mount.Options = []string{"ro"}
switch volumeSource.Type {
case KubeVolumeTypeBindMount:
if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil {
return nil, errors.Wrapf(err, "error in parsing MountPath")
}
mount := spec.Mount{
Destination: volume.MountPath,
Source: volumeSource.Source,
Type: "bind",
}
if volume.ReadOnly {
mount.Options = []string{"ro"}
}
s.Mounts = append(s.Mounts, mount)
case KubeVolumeTypeNamed:
namedVolume := specgen.NamedVolume{
Dest: volume.MountPath,
Name: volumeSource.Source,
}
if volume.ReadOnly {
namedVolume.Options = []string{"ro"}
}
s.Volumes = append(s.Volumes, &namedVolume)
default:
return nil, errors.Errorf("Unsupported volume source type")
}
s.Mounts = append(s.Mounts, mount)
}

s.RestartPolicy = restartPolicy
Expand Down
124 changes: 124 additions & 0 deletions pkg/specgen/generate/kube/volume.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package kube

import (
"os"

"github.com/containers/buildah/pkg/parse"
"github.com/containers/podman/v2/libpod"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
v1 "k8s.io/api/core/v1"
)

const (
// https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
kubeDirectoryPermission = 0755
// https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
kubeFilePermission = 0644
)

type KubeVolumeType int

const (
KubeVolumeTypeBindMount KubeVolumeType = iota
KubeVolumeTypeNamed KubeVolumeType = iota
)

type KubeVolume struct {
// Type of volume to create
Type KubeVolumeType
// Path for bind mount or volume name for named volume
Source string
}

// Create a KubeVolume from an HostPathVolumeSource
func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) {
if hostPath.Type != nil {
switch *hostPath.Type {
case v1.HostPathDirectoryOrCreate:
if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) {
if err := os.Mkdir(hostPath.Path, kubeDirectoryPermission); err != nil {
return nil, err
}
}
// Label a newly created volume
if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path)
}
case v1.HostPathFileOrCreate:
if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) {
f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission)
if err != nil {
return nil, errors.Wrap(err, "error creating HostPath")
}
if err := f.Close(); err != nil {
logrus.Warnf("Error in closing newly created HostPath file: %v", err)
}
}
// unconditionally label a newly created volume
if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path)
}
case v1.HostPathSocket:
st, err := os.Stat(hostPath.Path)
if err != nil {
return nil, errors.Wrap(err, "error checking HostPathSocket")
}
if st.Mode()&os.ModeSocket != os.ModeSocket {
return nil, errors.Errorf("error checking HostPathSocket: path %s is not a socket", hostPath.Path)
}

case v1.HostPathDirectory:
case v1.HostPathFile:
case v1.HostPathUnset:
// do nothing here because we will verify the path exists in validateVolumeHostDir
break
default:
return nil, errors.Errorf("Invalid HostPath type %v", hostPath.Type)
}
}

if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil {
return nil, errors.Wrapf(err, "error in parsing HostPath in YAML")
}

return &KubeVolume{
Type: KubeVolumeTypeBindMount,
Source: hostPath.Path,
}, nil
}

// Create a KubeVolume from a PersistentVolumeClaimVolumeSource
func VolumeFromPersistentVolumeClaim(claim *v1.PersistentVolumeClaimVolumeSource) (*KubeVolume, error) {
return &KubeVolume{
Type: KubeVolumeTypeNamed,
Source: claim.ClaimName,
}, nil
}

// Create a KubeVolume from one of the supported VolumeSource
func VolumeFromSource(volumeSource v1.VolumeSource) (*KubeVolume, error) {
if volumeSource.HostPath != nil {
return VolumeFromHostPath(volumeSource.HostPath)
} else if volumeSource.PersistentVolumeClaim != nil {
return VolumeFromPersistentVolumeClaim(volumeSource.PersistentVolumeClaim)
} else {
return nil, errors.Errorf("HostPath and PersistentVolumeClaim are currently the conly supported VolumeSource")
}
}

// Create a map of volume name to KubeVolume
func InitializeVolumes(specVolumes []v1.Volume) (map[string]*KubeVolume, error) {
volumes := make(map[string]*KubeVolume)

for _, specVolume := range specVolumes {
volume, err := VolumeFromSource(specVolume.VolumeSource)
if err != nil {
return nil, err
}

volumes[specVolume.Name] = volume
}

return volumes, nil
}
Loading

0 comments on commit f0d48aa

Please sign in to comment.