package kube import ( "os" "github.com/containers/common/pkg/parse" "github.com/containers/podman/v3/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 ) // nolint:golint type KubeVolumeType int const ( KubeVolumeTypeBindMount KubeVolumeType = iota KubeVolumeTypeNamed KubeVolumeType = iota ) // nolint:golint 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 only 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, errors.Wrapf(err, "failed to create volume %q", specVolume.Name) } volumes[specVolume.Name] = volume } return volumes, nil }