github.com/hanks177/podman/v4@v4.1.3-0.20220613032544-16d90015bc83/pkg/specgen/generate/kube/volume.go (about) 1 package kube 2 3 import ( 4 "os" 5 6 "github.com/containers/common/pkg/parse" 7 "github.com/hanks177/podman/v4/libpod" 8 v1 "github.com/hanks177/podman/v4/pkg/k8s.io/api/core/v1" 9 "github.com/pkg/errors" 10 "github.com/sirupsen/logrus" 11 ) 12 13 const ( 14 // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath 15 kubeDirectoryPermission = 0755 16 // https://kubernetes.io/docs/concepts/storage/volumes/#hostpath 17 kubeFilePermission = 0644 18 ) 19 20 //nolint:revive 21 type KubeVolumeType int 22 23 const ( 24 KubeVolumeTypeBindMount KubeVolumeType = iota 25 KubeVolumeTypeNamed 26 KubeVolumeTypeConfigMap 27 KubeVolumeTypeBlockDevice 28 KubeVolumeTypeCharDevice 29 ) 30 31 //nolint:revive 32 type KubeVolume struct { 33 // Type of volume to create 34 Type KubeVolumeType 35 // Path for bind mount or volume name for named volume 36 Source string 37 // Items to add to a named volume created where the key is the file name and the value is the data 38 // This is only used when there are volumes in the yaml that refer to a configmap 39 // Example: if configmap has data "SPECIAL_LEVEL: very" then the file name is "SPECIAL_LEVEL" and the 40 // data in that file is "very". 41 Items map[string]string 42 // If the volume is optional, we can move on if it is not found 43 // Only used when there are volumes in a yaml that refer to a configmap 44 Optional bool 45 } 46 47 // Create a KubeVolume from an HostPathVolumeSource 48 func VolumeFromHostPath(hostPath *v1.HostPathVolumeSource) (*KubeVolume, error) { 49 if hostPath.Type != nil { 50 switch *hostPath.Type { 51 case v1.HostPathDirectoryOrCreate: 52 if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { 53 if err := os.Mkdir(hostPath.Path, kubeDirectoryPermission); err != nil { 54 return nil, err 55 } 56 } 57 // Label a newly created volume 58 if err := libpod.LabelVolumePath(hostPath.Path); err != nil { 59 return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) 60 } 61 case v1.HostPathFileOrCreate: 62 if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) { 63 f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission) 64 if err != nil { 65 return nil, errors.Wrap(err, "error creating HostPath") 66 } 67 if err := f.Close(); err != nil { 68 logrus.Warnf("Error in closing newly created HostPath file: %v", err) 69 } 70 } 71 // unconditionally label a newly created volume 72 if err := libpod.LabelVolumePath(hostPath.Path); err != nil { 73 return nil, errors.Wrapf(err, "error giving %s a label", hostPath.Path) 74 } 75 case v1.HostPathSocket: 76 st, err := os.Stat(hostPath.Path) 77 if err != nil { 78 return nil, errors.Wrap(err, "error checking HostPathSocket") 79 } 80 if st.Mode()&os.ModeSocket != os.ModeSocket { 81 return nil, errors.Errorf("checking HostPathSocket: path %s is not a socket", hostPath.Path) 82 } 83 case v1.HostPathBlockDev: 84 dev, err := os.Stat(hostPath.Path) 85 if err != nil { 86 return nil, errors.Wrap(err, "error checking HostPathBlockDevice") 87 } 88 if dev.Mode()&os.ModeCharDevice == os.ModeCharDevice { 89 return nil, errors.Errorf("checking HostPathDevice: path %s is not a block device", hostPath.Path) 90 } 91 return &KubeVolume{ 92 Type: KubeVolumeTypeBlockDevice, 93 Source: hostPath.Path, 94 }, nil 95 case v1.HostPathCharDev: 96 dev, err := os.Stat(hostPath.Path) 97 if err != nil { 98 return nil, errors.Wrap(err, "error checking HostPathCharDevice") 99 } 100 if dev.Mode()&os.ModeCharDevice != os.ModeCharDevice { 101 return nil, errors.Errorf("checking HostPathCharDevice: path %s is not a character device", hostPath.Path) 102 } 103 return &KubeVolume{ 104 Type: KubeVolumeTypeCharDevice, 105 Source: hostPath.Path, 106 }, nil 107 case v1.HostPathDirectory: 108 case v1.HostPathFile: 109 case v1.HostPathUnset: 110 // do nothing here because we will verify the path exists in validateVolumeHostDir 111 break 112 default: 113 return nil, errors.Errorf("Invalid HostPath type %v", hostPath.Type) 114 } 115 } 116 117 if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil { 118 return nil, errors.Wrapf(err, "error in parsing HostPath in YAML") 119 } 120 121 return &KubeVolume{ 122 Type: KubeVolumeTypeBindMount, 123 Source: hostPath.Path, 124 }, nil 125 } 126 127 // Create a KubeVolume from a PersistentVolumeClaimVolumeSource 128 func VolumeFromPersistentVolumeClaim(claim *v1.PersistentVolumeClaimVolumeSource) (*KubeVolume, error) { 129 return &KubeVolume{ 130 Type: KubeVolumeTypeNamed, 131 Source: claim.ClaimName, 132 }, nil 133 } 134 135 func VolumeFromConfigMap(configMapVolumeSource *v1.ConfigMapVolumeSource, configMaps []v1.ConfigMap) (*KubeVolume, error) { 136 var configMap *v1.ConfigMap 137 kv := &KubeVolume{Type: KubeVolumeTypeConfigMap, Items: map[string]string{}} 138 for _, cm := range configMaps { 139 if cm.Name == configMapVolumeSource.Name { 140 matchedCM := cm 141 // Set the source to the config map name 142 kv.Source = cm.Name 143 configMap = &matchedCM 144 break 145 } 146 } 147 148 if configMap == nil { 149 // If the volumeSource was optional, move on even if a matching configmap wasn't found 150 if configMapVolumeSource.Optional != nil && *configMapVolumeSource.Optional { 151 kv.Source = configMapVolumeSource.Name 152 kv.Optional = *configMapVolumeSource.Optional 153 return kv, nil 154 } 155 return nil, errors.Errorf("no such ConfigMap %q", configMapVolumeSource.Name) 156 } 157 158 // If there are Items specified in the volumeSource, that overwrites the Data from the configmap 159 if len(configMapVolumeSource.Items) > 0 { 160 for _, item := range configMapVolumeSource.Items { 161 if val, ok := configMap.Data[item.Key]; ok { 162 kv.Items[item.Path] = val 163 } 164 } 165 } else { 166 for k, v := range configMap.Data { 167 kv.Items[k] = v 168 } 169 } 170 return kv, nil 171 } 172 173 // Create a KubeVolume from one of the supported VolumeSource 174 func VolumeFromSource(volumeSource v1.VolumeSource, configMaps []v1.ConfigMap) (*KubeVolume, error) { 175 switch { 176 case volumeSource.HostPath != nil: 177 return VolumeFromHostPath(volumeSource.HostPath) 178 case volumeSource.PersistentVolumeClaim != nil: 179 return VolumeFromPersistentVolumeClaim(volumeSource.PersistentVolumeClaim) 180 case volumeSource.ConfigMap != nil: 181 return VolumeFromConfigMap(volumeSource.ConfigMap, configMaps) 182 default: 183 return nil, errors.Errorf("HostPath, ConfigMap, and PersistentVolumeClaim are currently the only supported VolumeSource") 184 } 185 } 186 187 // Create a map of volume name to KubeVolume 188 func InitializeVolumes(specVolumes []v1.Volume, configMaps []v1.ConfigMap) (map[string]*KubeVolume, error) { 189 volumes := make(map[string]*KubeVolume) 190 191 for _, specVolume := range specVolumes { 192 volume, err := VolumeFromSource(specVolume.VolumeSource, configMaps) 193 if err != nil { 194 return nil, errors.Wrapf(err, "failed to create volume %q", specVolume.Name) 195 } 196 197 volumes[specVolume.Name] = volume 198 } 199 200 return volumes, nil 201 }