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  }