Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for persistent volume claims in kube files #8497

Merged
merged 2 commits into from
Nov 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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