github.com/jingruilea/kubeedge@v1.2.0-beta.0.0.20200410162146-4bb8902b3879/edge/pkg/edged/volume/csi/csi_util.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 16 KubeEdge Authors: To create mini-kubelet for edge deployment scenario, 17 this file is derived from kubernetes v1.15.3, 18 and the full file path is k8s.io/kubernetes/pkg/volume/csi/csi_util.go 19 */ 20 21 package csi 22 23 import ( 24 "encoding/json" 25 "fmt" 26 "os" 27 "path/filepath" 28 "time" 29 30 api "k8s.io/api/core/v1" 31 meta "k8s.io/apimachinery/pkg/apis/meta/v1" 32 utilfeature "k8s.io/apiserver/pkg/util/feature" 33 "k8s.io/client-go/kubernetes" 34 "k8s.io/klog" 35 "k8s.io/kubernetes/pkg/features" 36 "k8s.io/kubernetes/pkg/volume" 37 utilstrings "k8s.io/utils/strings" 38 ) 39 40 const ( 41 testInformerSyncPeriod = 100 * time.Millisecond 42 testInformerSyncTimeout = 30 * time.Second 43 ) 44 45 func getCredentialsFromSecret(k8s kubernetes.Interface, secretRef *api.SecretReference) (map[string]string, error) { 46 credentials := map[string]string{} 47 secret, err := k8s.CoreV1().Secrets(secretRef.Namespace).Get(secretRef.Name, meta.GetOptions{}) 48 if err != nil { 49 klog.Errorf("failed to find the secret %s in the namespace %s with error: %v\n", secretRef.Name, secretRef.Namespace, err) 50 return credentials, err 51 } 52 for key, value := range secret.Data { 53 credentials[key] = string(value) 54 } 55 56 return credentials, nil 57 } 58 59 // saveVolumeData persists parameter data as json file at the provided location 60 func saveVolumeData(dir string, fileName string, data map[string]string) error { 61 dataFilePath := filepath.Join(dir, fileName) 62 klog.V(4).Info(log("saving volume data file [%s]", dataFilePath)) 63 file, err := os.Create(dataFilePath) 64 if err != nil { 65 klog.Error(log("failed to save volume data file %s: %v", dataFilePath, err)) 66 return err 67 } 68 defer file.Close() 69 if err := json.NewEncoder(file).Encode(data); err != nil { 70 klog.Error(log("failed to save volume data file %s: %v", dataFilePath, err)) 71 return err 72 } 73 klog.V(4).Info(log("volume data file saved successfully [%s]", dataFilePath)) 74 return nil 75 } 76 77 // loadVolumeData loads volume info from specified json file/location 78 func loadVolumeData(dir string, fileName string) (map[string]string, error) { 79 // remove /mount at the end 80 dataFileName := filepath.Join(dir, fileName) 81 klog.V(4).Info(log("loading volume data file [%s]", dataFileName)) 82 83 file, err := os.Open(dataFileName) 84 if err != nil { 85 klog.Error(log("failed to open volume data file [%s]: %v", dataFileName, err)) 86 return nil, err 87 } 88 defer file.Close() 89 data := map[string]string{} 90 if err := json.NewDecoder(file).Decode(&data); err != nil { 91 klog.Error(log("failed to parse volume data file [%s]: %v", dataFileName, err)) 92 return nil, err 93 } 94 95 return data, nil 96 } 97 98 func getCSISourceFromSpec(spec *volume.Spec) (*api.CSIPersistentVolumeSource, error) { 99 return getPVSourceFromSpec(spec) 100 } 101 102 func getReadOnlyFromSpec(spec *volume.Spec) (bool, error) { 103 if spec.PersistentVolume != nil && 104 spec.PersistentVolume.Spec.CSI != nil { 105 return spec.ReadOnly, nil 106 } 107 108 return false, fmt.Errorf("CSIPersistentVolumeSource not defined in spec") 109 } 110 111 // log prepends log string with `kubernetes.io/csi` 112 func log(msg string, parts ...interface{}) string { 113 return fmt.Sprintf(fmt.Sprintf("%s: %s", CSIPluginName, msg), parts...) 114 } 115 116 // getVolumeDevicePluginDir returns the path where the CSI plugin keeps the 117 // symlink for a block device associated with a given specVolumeID. 118 // path: plugins/kubernetes.io/csi/volumeDevices/{specVolumeID}/dev 119 func getVolumeDevicePluginDir(specVolID string, host volume.VolumeHost) string { 120 sanitizedSpecVolID := utilstrings.EscapeQualifiedName(specVolID) 121 return filepath.Join(host.GetVolumeDevicePluginDir(CSIPluginName), sanitizedSpecVolID, "dev") 122 } 123 124 // getVolumeDeviceDataDir returns the path where the CSI plugin keeps the 125 // volume data for a block device associated with a given specVolumeID. 126 // path: plugins/kubernetes.io/csi/volumeDevices/{specVolumeID}/data 127 func getVolumeDeviceDataDir(specVolID string, host volume.VolumeHost) string { 128 sanitizedSpecVolID := utilstrings.EscapeQualifiedName(specVolID) 129 return filepath.Join(host.GetVolumeDevicePluginDir(CSIPluginName), sanitizedSpecVolID, "data") 130 } 131 132 // hasReadWriteOnce returns true if modes contains v1.ReadWriteOnce 133 func hasReadWriteOnce(modes []api.PersistentVolumeAccessMode) bool { 134 if modes == nil { 135 return false 136 } 137 for _, mode := range modes { 138 if mode == api.ReadWriteOnce { 139 return true 140 } 141 } 142 return false 143 } 144 145 // getSourceFromSpec returns either CSIVolumeSource or CSIPersistentVolumeSource, but not both 146 func getSourceFromSpec(spec *volume.Spec) (*api.CSIVolumeSource, *api.CSIPersistentVolumeSource, error) { 147 if spec == nil { 148 return nil, nil, fmt.Errorf("volume.Spec nil") 149 } 150 if spec.Volume != nil && spec.PersistentVolume != nil { 151 return nil, nil, fmt.Errorf("volume.Spec has both volume and persistent volume sources") 152 } 153 if spec.Volume != nil && spec.Volume.CSI != nil && utilfeature.DefaultFeatureGate.Enabled(features.CSIInlineVolume) { 154 return spec.Volume.CSI, nil, nil 155 } 156 if spec.PersistentVolume != nil && 157 spec.PersistentVolume.Spec.CSI != nil { 158 return nil, spec.PersistentVolume.Spec.CSI, nil 159 } 160 161 return nil, nil, fmt.Errorf("volume source not found in volume.Spec") 162 } 163 164 // getPVSourceFromSpec ensures only CSIPersistentVolumeSource is present in volume.Spec 165 func getPVSourceFromSpec(spec *volume.Spec) (*api.CSIPersistentVolumeSource, error) { 166 volSrc, pvSrc, err := getSourceFromSpec(spec) 167 if err != nil { 168 return nil, err 169 } 170 if volSrc != nil { 171 return nil, fmt.Errorf("unexpected api.CSIVolumeSource found in volume.Spec") 172 } 173 return pvSrc, nil 174 }