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  }