mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 12:18:16 +00:00 
			
		
		
		
	ScaleIO - API source code update
This commit tracks all human-generated code for API source updates.
This commit is contained in:
		@@ -410,14 +410,16 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		func(sio *api.ScaleIOVolumeSource, c fuzz.Continue) {
 | 
			
		||||
			sio.ProtectionDomain = c.RandString()
 | 
			
		||||
			if sio.ProtectionDomain == "" {
 | 
			
		||||
				sio.ProtectionDomain = "default"
 | 
			
		||||
			sio.StorageMode = c.RandString()
 | 
			
		||||
			if sio.StorageMode == "" {
 | 
			
		||||
				sio.StorageMode = "ThinProvisioned"
 | 
			
		||||
			}
 | 
			
		||||
			sio.StoragePool = c.RandString()
 | 
			
		||||
			if sio.StoragePool == "" {
 | 
			
		||||
				sio.StoragePool = "default"
 | 
			
		||||
			sio.FSType = c.RandString()
 | 
			
		||||
			if sio.FSType == "" {
 | 
			
		||||
				sio.FSType = "xfs"
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		func(sio *api.ScaleIOPersistentVolumeSource, c fuzz.Continue) {
 | 
			
		||||
			sio.StorageMode = c.RandString()
 | 
			
		||||
			if sio.StorageMode == "" {
 | 
			
		||||
				sio.StorageMode = "ThinProvisioned"
 | 
			
		||||
 
 | 
			
		||||
@@ -76,8 +76,14 @@ func VisitPVSecretNames(pv *api.PersistentVolume, visitor Visitor) bool {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case source.ScaleIO != nil:
 | 
			
		||||
		if source.ScaleIO.SecretRef != nil && !visitor(getClaimRefNamespace(pv), source.ScaleIO.SecretRef.Name) {
 | 
			
		||||
			return false
 | 
			
		||||
		if source.ScaleIO.SecretRef != nil {
 | 
			
		||||
			ns := getClaimRefNamespace(pv)
 | 
			
		||||
			if source.ScaleIO.SecretRef != nil && len(source.ScaleIO.SecretRef.Namespace) > 0 {
 | 
			
		||||
				ns = source.ScaleIO.SecretRef.Namespace
 | 
			
		||||
			}
 | 
			
		||||
			if !visitor(ns, source.ScaleIO.SecretRef.Name) {
 | 
			
		||||
				return false
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	case source.ISCSI != nil:
 | 
			
		||||
		if source.ISCSI.SecretRef != nil && !visitor(getClaimRefNamespace(pv), source.ISCSI.SecretRef.Name) {
 | 
			
		||||
 
 | 
			
		||||
@@ -78,9 +78,16 @@ func TestPVSecrets(t *testing.T) {
 | 
			
		||||
		{Spec: api.PersistentVolumeSpec{
 | 
			
		||||
			ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
 | 
			
		||||
			PersistentVolumeSource: api.PersistentVolumeSource{
 | 
			
		||||
				ScaleIO: &api.ScaleIOVolumeSource{
 | 
			
		||||
					SecretRef: &api.LocalObjectReference{
 | 
			
		||||
				ScaleIO: &api.ScaleIOPersistentVolumeSource{
 | 
			
		||||
					SecretRef: &api.SecretReference{
 | 
			
		||||
						Name: "Spec.PersistentVolumeSource.ScaleIO.SecretRef"}}}}},
 | 
			
		||||
		{Spec: api.PersistentVolumeSpec{
 | 
			
		||||
			ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
 | 
			
		||||
			PersistentVolumeSource: api.PersistentVolumeSource{
 | 
			
		||||
				ScaleIO: &api.ScaleIOPersistentVolumeSource{
 | 
			
		||||
					SecretRef: &api.SecretReference{
 | 
			
		||||
						Name:      "Spec.PersistentVolumeSource.ScaleIO.SecretRef",
 | 
			
		||||
						Namespace: "scaleions"}}}}},
 | 
			
		||||
		{Spec: api.PersistentVolumeSpec{
 | 
			
		||||
			ClaimRef: &api.ObjectReference{Namespace: "claimrefns", Name: "claimrefname"},
 | 
			
		||||
			PersistentVolumeSource: api.PersistentVolumeSource{
 | 
			
		||||
@@ -150,6 +157,7 @@ func TestPVSecrets(t *testing.T) {
 | 
			
		||||
		"claimrefns/Spec.PersistentVolumeSource.RBD.SecretRef",
 | 
			
		||||
		"rbdns/Spec.PersistentVolumeSource.RBD.SecretRef",
 | 
			
		||||
		"claimrefns/Spec.PersistentVolumeSource.ScaleIO.SecretRef",
 | 
			
		||||
		"scaleions/Spec.PersistentVolumeSource.ScaleIO.SecretRef",
 | 
			
		||||
		"claimrefns/Spec.PersistentVolumeSource.ISCSI.SecretRef",
 | 
			
		||||
		"storageosns/Spec.PersistentVolumeSource.StorageOS.SecretRef",
 | 
			
		||||
	)
 | 
			
		||||
 
 | 
			
		||||
@@ -383,7 +383,7 @@ type PersistentVolumeSource struct {
 | 
			
		||||
	PortworxVolume *PortworxVolumeSource
 | 
			
		||||
	// ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
 | 
			
		||||
	// +optional
 | 
			
		||||
	ScaleIO *ScaleIOVolumeSource
 | 
			
		||||
	ScaleIO *ScaleIOPersistentVolumeSource
 | 
			
		||||
	// Local represents directly-attached storage with node affinity
 | 
			
		||||
	// +optional
 | 
			
		||||
	Local *LocalVolumeSource
 | 
			
		||||
@@ -1285,13 +1285,13 @@ type ScaleIOVolumeSource struct {
 | 
			
		||||
	// Flag to enable/disable SSL communication with Gateway, default false
 | 
			
		||||
	// +optional
 | 
			
		||||
	SSLEnabled bool
 | 
			
		||||
	// The name of the Protection Domain for the configured storage (defaults to "default").
 | 
			
		||||
	// The name of the ScaleIO Protection Domain for the configured storage.
 | 
			
		||||
	// +optional
 | 
			
		||||
	ProtectionDomain string
 | 
			
		||||
	// The Storage Pool associated with the protection domain (defaults to "default").
 | 
			
		||||
	// The ScaleIO Storage Pool associated with the protection domain.
 | 
			
		||||
	// +optional
 | 
			
		||||
	StoragePool string
 | 
			
		||||
	// Indicates whether the storage for a volume should be thick or thin (defaults to "thin").
 | 
			
		||||
	// Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned.
 | 
			
		||||
	// +optional
 | 
			
		||||
	StorageMode string
 | 
			
		||||
	// The name of a volume already created in the ScaleIO system
 | 
			
		||||
@@ -1308,6 +1308,42 @@ type ScaleIOVolumeSource struct {
 | 
			
		||||
	ReadOnly bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScaleIOPersistentVolumeSource represents a persistent ScaleIO volume that can be defined
 | 
			
		||||
// by a an admin via a storage class, for instance.
 | 
			
		||||
type ScaleIOPersistentVolumeSource struct {
 | 
			
		||||
	// The host address of the ScaleIO API Gateway.
 | 
			
		||||
	Gateway string
 | 
			
		||||
	// The name of the storage system as configured in ScaleIO.
 | 
			
		||||
	System string
 | 
			
		||||
	// SecretRef references to the secret for ScaleIO user and other
 | 
			
		||||
	// sensitive information. If this is not provided, Login operation will fail.
 | 
			
		||||
	SecretRef *SecretReference
 | 
			
		||||
	// Flag to enable/disable SSL communication with Gateway, default false
 | 
			
		||||
	// +optional
 | 
			
		||||
	SSLEnabled bool
 | 
			
		||||
	// The name of the ScaleIO Protection Domain for the configured storage.
 | 
			
		||||
	// +optional
 | 
			
		||||
	ProtectionDomain string
 | 
			
		||||
	// The ScaleIO Storage Pool associated with the protection domain.
 | 
			
		||||
	// +optional
 | 
			
		||||
	StoragePool string
 | 
			
		||||
	// Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned.
 | 
			
		||||
	// +optional
 | 
			
		||||
	StorageMode string
 | 
			
		||||
	// The name of a volume created in the ScaleIO system
 | 
			
		||||
	// that is associated with this volume source.
 | 
			
		||||
	VolumeName string
 | 
			
		||||
	// Filesystem type to mount.
 | 
			
		||||
	// Must be a filesystem type supported by the host operating system.
 | 
			
		||||
	// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
 | 
			
		||||
	// +optional
 | 
			
		||||
	FSType string
 | 
			
		||||
	// Defaults to false (read/write). ReadOnly here will force
 | 
			
		||||
	// the ReadOnly setting in VolumeMounts.
 | 
			
		||||
	// +optional
 | 
			
		||||
	ReadOnly bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Represents a StorageOS persistent volume resource.
 | 
			
		||||
type StorageOSVolumeSource struct {
 | 
			
		||||
	// VolumeName is the human-readable name of the StorageOS volume.  Volume
 | 
			
		||||
 
 | 
			
		||||
@@ -381,12 +381,15 @@ func SetDefaults_RBDPersistentVolumeSource(obj *v1.RBDPersistentVolumeSource) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SetDefaults_ScaleIOVolumeSource(obj *v1.ScaleIOVolumeSource) {
 | 
			
		||||
	if obj.ProtectionDomain == "" {
 | 
			
		||||
		obj.ProtectionDomain = "default"
 | 
			
		||||
	if obj.StorageMode == "" {
 | 
			
		||||
		obj.StorageMode = "ThinProvisioned"
 | 
			
		||||
	}
 | 
			
		||||
	if obj.StoragePool == "" {
 | 
			
		||||
		obj.StoragePool = "default"
 | 
			
		||||
	if obj.FSType == "" {
 | 
			
		||||
		obj.FSType = "xfs"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func SetDefaults_ScaleIOPersistentVolumeSource(obj *v1.ScaleIOPersistentVolumeSource) {
 | 
			
		||||
	if obj.StorageMode == "" {
 | 
			
		||||
		obj.StorageMode = "ThinProvisioned"
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1245,6 +1245,20 @@ func validateScaleIOVolumeSource(sio *api.ScaleIOVolumeSource, fldPath *field.Pa
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateScaleIOPersistentVolumeSource(sio *api.ScaleIOPersistentVolumeSource, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
	allErrs := field.ErrorList{}
 | 
			
		||||
	if sio.Gateway == "" {
 | 
			
		||||
		allErrs = append(allErrs, field.Required(fldPath.Child("gateway"), ""))
 | 
			
		||||
	}
 | 
			
		||||
	if sio.System == "" {
 | 
			
		||||
		allErrs = append(allErrs, field.Required(fldPath.Child("system"), ""))
 | 
			
		||||
	}
 | 
			
		||||
	if sio.VolumeName == "" {
 | 
			
		||||
		allErrs = append(allErrs, field.Required(fldPath.Child("volumeName"), ""))
 | 
			
		||||
	}
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateLocalVolumeSource(ls *api.LocalVolumeSource, fldPath *field.Path) field.ErrorList {
 | 
			
		||||
	allErrs := field.ErrorList{}
 | 
			
		||||
	if ls.Path == "" {
 | 
			
		||||
@@ -1489,7 +1503,7 @@ func ValidatePersistentVolume(pv *api.PersistentVolume) field.ErrorList {
 | 
			
		||||
			allErrs = append(allErrs, field.Forbidden(specPath.Child("scaleIO"), "may not specify more than 1 volume type"))
 | 
			
		||||
		} else {
 | 
			
		||||
			numVolumes++
 | 
			
		||||
			allErrs = append(allErrs, validateScaleIOVolumeSource(pv.Spec.ScaleIO, specPath.Child("scaleIO"))...)
 | 
			
		||||
			allErrs = append(allErrs, validateScaleIOPersistentVolumeSource(pv.Spec.ScaleIO, specPath.Child("scaleIO"))...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if pv.Spec.Local != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -970,6 +970,26 @@ func printScaleIOVolumeSource(sio *api.ScaleIOVolumeSource, w PrefixWriter) {
 | 
			
		||||
		sio.Gateway, sio.System, sio.ProtectionDomain, sio.StoragePool, sio.StorageMode, sio.VolumeName, sio.FSType, sio.ReadOnly)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func printScaleIOPersistentVolumeSource(sio *api.ScaleIOPersistentVolumeSource, w PrefixWriter) {
 | 
			
		||||
	var secretNS, secretName string
 | 
			
		||||
	if sio.SecretRef != nil {
 | 
			
		||||
		secretName = sio.SecretRef.Name
 | 
			
		||||
		secretNS = sio.SecretRef.Namespace
 | 
			
		||||
	}
 | 
			
		||||
	w.Write(LEVEL_2, "Type:\tScaleIO (a persistent volume backed by a block device in ScaleIO)\n"+
 | 
			
		||||
		"    Gateway:\t%v\n"+
 | 
			
		||||
		"    System:\t%v\n"+
 | 
			
		||||
		"    Protection Domain:\t%v\n"+
 | 
			
		||||
		"    Storage Pool:\t%v\n"+
 | 
			
		||||
		"    Storage Mode:\t%v\n"+
 | 
			
		||||
		"    VolumeName:\t%v\n"+
 | 
			
		||||
		"    SecretName:\t%v\n"+
 | 
			
		||||
		"    SecretNamespace:\t%v\n"+
 | 
			
		||||
		"    FSType:\t%v\n"+
 | 
			
		||||
		"    ReadOnly:\t%v\n",
 | 
			
		||||
		sio.Gateway, sio.System, sio.ProtectionDomain, sio.StoragePool, sio.StorageMode, sio.VolumeName, secretName, secretNS, sio.FSType, sio.ReadOnly)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func printLocalVolumeSource(ls *api.LocalVolumeSource, w PrefixWriter) {
 | 
			
		||||
	w.Write(LEVEL_2, "Type:\tLocalVolume (a persistent volume backed by local storage on a node)\n"+
 | 
			
		||||
		"    Path:\t%v\n",
 | 
			
		||||
@@ -1135,7 +1155,7 @@ func describePersistentVolume(pv *api.PersistentVolume, events *api.EventList) (
 | 
			
		||||
		case pv.Spec.PortworxVolume != nil:
 | 
			
		||||
			printPortworxVolumeSource(pv.Spec.PortworxVolume, w)
 | 
			
		||||
		case pv.Spec.ScaleIO != nil:
 | 
			
		||||
			printScaleIOVolumeSource(pv.Spec.ScaleIO, w)
 | 
			
		||||
			printScaleIOPersistentVolumeSource(pv.Spec.ScaleIO, w)
 | 
			
		||||
		case pv.Spec.Local != nil:
 | 
			
		||||
			printLocalVolumeSource(pv.Spec.Local, w)
 | 
			
		||||
		case pv.Spec.CephFS != nil:
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,7 @@ var (
 | 
			
		||||
		confKey.sslEnabled: "false",
 | 
			
		||||
		confKey.system:     "scaleio",
 | 
			
		||||
		confKey.volumeName: "sio-0001",
 | 
			
		||||
		confKey.secretRef:  "sio-secret",
 | 
			
		||||
		confKey.secretName: "sio-secret",
 | 
			
		||||
		confKey.username:   "c2lvdXNlcgo=",     // siouser
 | 
			
		||||
		confKey.password:   "c2lvcGFzc3dvcmQK", // siopassword
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -60,12 +60,11 @@ func (p *sioPlugin) GetPluginName() string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *sioPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
 | 
			
		||||
	source, err := getVolumeSourceFromSpec(spec)
 | 
			
		||||
	attribs, err := getVolumeSourceAttribs(spec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return source.VolumeName, nil
 | 
			
		||||
	return attribs.volName, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *sioPlugin) CanSupport(spec *volume.Spec) bool {
 | 
			
		||||
@@ -81,29 +80,33 @@ func (p *sioPlugin) NewMounter(
 | 
			
		||||
	spec *volume.Spec,
 | 
			
		||||
	pod *api.Pod,
 | 
			
		||||
	_ volume.VolumeOptions) (volume.Mounter, error) {
 | 
			
		||||
	sioSource, err := getVolumeSourceFromSpec(spec)
 | 
			
		||||
 | 
			
		||||
	// extract source info from either ScaleIOVolumeSource or ScaleIOPersistentVolumeSource type
 | 
			
		||||
	attribs, err := getVolumeSourceAttribs(spec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		glog.Error(log("failed to extract ScaleIOVolumeSource from spec: %v", err))
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return nil, errors.New(log("mounter failed to extract volume attributes from spec: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	secretName, secretNS, err := getSecretAndNamespaceFromSpec(spec, pod)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.New(log("failed to get secret name or secretNamespace: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &sioVolume{
 | 
			
		||||
		pod:         pod,
 | 
			
		||||
		spec:        spec,
 | 
			
		||||
		source:      sioSource,
 | 
			
		||||
		namespace:   pod.Namespace,
 | 
			
		||||
		volSpecName: spec.Name(),
 | 
			
		||||
		volName:     sioSource.VolumeName,
 | 
			
		||||
		podUID:      pod.UID,
 | 
			
		||||
		readOnly:    sioSource.ReadOnly,
 | 
			
		||||
		fsType:      sioSource.FSType,
 | 
			
		||||
		plugin:      p,
 | 
			
		||||
		pod:             pod,
 | 
			
		||||
		spec:            spec,
 | 
			
		||||
		secretName:      secretName,
 | 
			
		||||
		secretNamespace: secretNS,
 | 
			
		||||
		volSpecName:     spec.Name(),
 | 
			
		||||
		volName:         attribs.volName,
 | 
			
		||||
		podUID:          pod.UID,
 | 
			
		||||
		readOnly:        attribs.readOnly,
 | 
			
		||||
		fsType:          attribs.fsType,
 | 
			
		||||
		plugin:          p,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewUnmounter creates a representation of the volume to unmount
 | 
			
		||||
// The specName param can be used to carry the namespace value (if needed) using format:
 | 
			
		||||
// specName = [<namespace>nsSep]<somevalue> where the specname is pre-pended with the namespace
 | 
			
		||||
func (p *sioPlugin) NewUnmounter(specName string, podUID types.UID) (volume.Unmounter, error) {
 | 
			
		||||
	glog.V(4).Info(log("Unmounter for %s", specName))
 | 
			
		||||
 | 
			
		||||
@@ -156,22 +159,25 @@ func (p *sioPlugin) GetAccessModes() []api.PersistentVolumeAccessMode {
 | 
			
		||||
var _ volume.DeletableVolumePlugin = &sioPlugin{}
 | 
			
		||||
 | 
			
		||||
func (p *sioPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
 | 
			
		||||
	sioSource, err := getVolumeSourceFromSpec(spec)
 | 
			
		||||
	attribs, err := getVolumeSourceAttribs(spec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		glog.Error(log("deleter failed to extract source from spec: %v", err))
 | 
			
		||||
		glog.Error(log("deleter failed to extract volume attributes from spec: %v", err))
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namespace := spec.PersistentVolume.Spec.ClaimRef.Namespace
 | 
			
		||||
	secretName, secretNS, err := getSecretAndNamespaceFromSpec(spec, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, errors.New(log("failed to get secret name or secretNamespace: %v", err))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &sioVolume{
 | 
			
		||||
		spec:        spec,
 | 
			
		||||
		source:      sioSource,
 | 
			
		||||
		namespace:   namespace,
 | 
			
		||||
		volSpecName: spec.Name(),
 | 
			
		||||
		volName:     sioSource.VolumeName,
 | 
			
		||||
		plugin:      p,
 | 
			
		||||
		readOnly:    sioSource.ReadOnly,
 | 
			
		||||
		spec:            spec,
 | 
			
		||||
		secretName:      secretName,
 | 
			
		||||
		secretNamespace: secretNS,
 | 
			
		||||
		volSpecName:     spec.Name(),
 | 
			
		||||
		volName:         attribs.volName,
 | 
			
		||||
		plugin:          p,
 | 
			
		||||
		readOnly:        attribs.readOnly,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -189,13 +195,26 @@ func (p *sioPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisi
 | 
			
		||||
		return nil, errors.New("option parameters missing")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namespace := options.PVC.Namespace
 | 
			
		||||
	// Supports ref of name of secret a couple of ways:
 | 
			
		||||
	// options.Parameters["secretRef"] for backward compat, or
 | 
			
		||||
	// options.Parameters["secretName"]
 | 
			
		||||
	secretName := configData[confKey.secretName]
 | 
			
		||||
	if secretName == "" {
 | 
			
		||||
		secretName = configData["secretName"]
 | 
			
		||||
		configData[confKey.secretName] = secretName
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	secretNS := configData[confKey.secretNamespace]
 | 
			
		||||
	if secretNS == "" {
 | 
			
		||||
		secretNS = options.PVC.Namespace
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &sioVolume{
 | 
			
		||||
		configData:  configData,
 | 
			
		||||
		plugin:      p,
 | 
			
		||||
		options:     options,
 | 
			
		||||
		namespace:   namespace,
 | 
			
		||||
		volSpecName: options.PVName,
 | 
			
		||||
		configData:      configData,
 | 
			
		||||
		plugin:          p,
 | 
			
		||||
		options:         options,
 | 
			
		||||
		secretName:      secretName,
 | 
			
		||||
		secretNamespace: secretNS,
 | 
			
		||||
		volSpecName:     options.PVName,
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,11 +31,17 @@ import (
 | 
			
		||||
	volutil "k8s.io/kubernetes/pkg/volume/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type volSourceAttribs struct {
 | 
			
		||||
	volName,
 | 
			
		||||
	fsType string
 | 
			
		||||
	readOnly bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	confKey = struct {
 | 
			
		||||
		gateway,
 | 
			
		||||
		sslEnabled,
 | 
			
		||||
		secretRef,
 | 
			
		||||
		secretName,
 | 
			
		||||
		system,
 | 
			
		||||
		protectionDomain,
 | 
			
		||||
		storagePool,
 | 
			
		||||
@@ -47,12 +53,13 @@ var (
 | 
			
		||||
		readOnly,
 | 
			
		||||
		username,
 | 
			
		||||
		password,
 | 
			
		||||
		namespace,
 | 
			
		||||
		secretNamespace,
 | 
			
		||||
		sdcGuid string
 | 
			
		||||
	}{
 | 
			
		||||
		gateway:          "gateway",
 | 
			
		||||
		sslEnabled:       "sslEnabled",
 | 
			
		||||
		secretRef:        "secretRef",
 | 
			
		||||
		secretName:       "secretRef",
 | 
			
		||||
		secretNamespace:  "secretNamespace",
 | 
			
		||||
		system:           "system",
 | 
			
		||||
		protectionDomain: "protectionDomain",
 | 
			
		||||
		storagePool:      "storagePool",
 | 
			
		||||
@@ -64,7 +71,6 @@ var (
 | 
			
		||||
		readOnly:         "readOnly",
 | 
			
		||||
		username:         "username",
 | 
			
		||||
		password:         "password",
 | 
			
		||||
		namespace:        "namespace",
 | 
			
		||||
		sdcGuid:          "sdcGuid",
 | 
			
		||||
	}
 | 
			
		||||
	sdcGuidLabelName = "scaleio.sdcGuid"
 | 
			
		||||
@@ -79,23 +85,32 @@ var (
 | 
			
		||||
	protectionDomainNotProvidedErr = errors.New("ScaleIO protection domain not provided")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// mapScaleIOVolumeSource maps attributes from a ScaleIOVolumeSource to config
 | 
			
		||||
func mapVolumeSource(config map[string]string, source *api.ScaleIOVolumeSource) {
 | 
			
		||||
	config[confKey.gateway] = source.Gateway
 | 
			
		||||
	config[confKey.secretRef] = func() string {
 | 
			
		||||
		if source.SecretRef != nil {
 | 
			
		||||
			return string(source.SecretRef.Name)
 | 
			
		||||
		}
 | 
			
		||||
		return ""
 | 
			
		||||
	}()
 | 
			
		||||
	config[confKey.system] = source.System
 | 
			
		||||
	config[confKey.volumeName] = source.VolumeName
 | 
			
		||||
	config[confKey.sslEnabled] = strconv.FormatBool(source.SSLEnabled)
 | 
			
		||||
	config[confKey.protectionDomain] = source.ProtectionDomain
 | 
			
		||||
	config[confKey.storagePool] = source.StoragePool
 | 
			
		||||
	config[confKey.storageMode] = source.StorageMode
 | 
			
		||||
	config[confKey.fsType] = source.FSType
 | 
			
		||||
	config[confKey.readOnly] = strconv.FormatBool(source.ReadOnly)
 | 
			
		||||
// mapVolumeSpec maps attributes from either ScaleIOVolumeSource  or ScaleIOPersistentVolumeSource to config
 | 
			
		||||
func mapVolumeSpec(config map[string]string, spec *volume.Spec) {
 | 
			
		||||
 | 
			
		||||
	if source, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
 | 
			
		||||
		config[confKey.gateway] = source.Gateway
 | 
			
		||||
		config[confKey.system] = source.System
 | 
			
		||||
		config[confKey.volumeName] = source.VolumeName
 | 
			
		||||
		config[confKey.sslEnabled] = strconv.FormatBool(source.SSLEnabled)
 | 
			
		||||
		config[confKey.protectionDomain] = source.ProtectionDomain
 | 
			
		||||
		config[confKey.storagePool] = source.StoragePool
 | 
			
		||||
		config[confKey.storageMode] = source.StorageMode
 | 
			
		||||
		config[confKey.fsType] = source.FSType
 | 
			
		||||
		config[confKey.readOnly] = strconv.FormatBool(source.ReadOnly)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if source, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
 | 
			
		||||
		config[confKey.gateway] = source.Gateway
 | 
			
		||||
		config[confKey.system] = source.System
 | 
			
		||||
		config[confKey.volumeName] = source.VolumeName
 | 
			
		||||
		config[confKey.sslEnabled] = strconv.FormatBool(source.SSLEnabled)
 | 
			
		||||
		config[confKey.protectionDomain] = source.ProtectionDomain
 | 
			
		||||
		config[confKey.storagePool] = source.StoragePool
 | 
			
		||||
		config[confKey.storageMode] = source.StorageMode
 | 
			
		||||
		config[confKey.fsType] = source.FSType
 | 
			
		||||
		config[confKey.readOnly] = strconv.FormatBool(source.ReadOnly)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	//optionals
 | 
			
		||||
	applyConfigDefaults(config)
 | 
			
		||||
@@ -105,7 +120,7 @@ func validateConfigs(config map[string]string) error {
 | 
			
		||||
	if config[confKey.gateway] == "" {
 | 
			
		||||
		return gatewayNotProvidedErr
 | 
			
		||||
	}
 | 
			
		||||
	if config[confKey.secretRef] == "" {
 | 
			
		||||
	if config[confKey.secretName] == "" {
 | 
			
		||||
		return secretRefNotProvidedErr
 | 
			
		||||
	}
 | 
			
		||||
	if config[confKey.system] == "" {
 | 
			
		||||
@@ -202,7 +217,7 @@ func saveConfig(configName string, data map[string]string) error {
 | 
			
		||||
// attachSecret loads secret object and attaches to configData
 | 
			
		||||
func attachSecret(plug *sioPlugin, namespace string, configData map[string]string) error {
 | 
			
		||||
	// load secret
 | 
			
		||||
	secretRefName := configData[confKey.secretRef]
 | 
			
		||||
	secretRefName := configData[confKey.secretName]
 | 
			
		||||
	kubeClient := plug.host.GetKubeClient()
 | 
			
		||||
	secretMap, err := volutil.GetSecretForPV(namespace, secretRefName, sioPluginName, kubeClient)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -244,8 +259,8 @@ func getSdcGuidLabel(plug *sioPlugin) (string, error) {
 | 
			
		||||
	return label, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getVolumeSourceFromSpec safely extracts ScaleIOVolumeSource from spec
 | 
			
		||||
func getVolumeSourceFromSpec(spec *volume.Spec) (*api.ScaleIOVolumeSource, error) {
 | 
			
		||||
// getVolumeSourceFromSpec safely extracts ScaleIOVolumeSource or ScaleIOPersistentVolumeSource from spec
 | 
			
		||||
func getVolumeSourceFromSpec(spec *volume.Spec) (interface{}, error) {
 | 
			
		||||
	if spec.Volume != nil && spec.Volume.ScaleIO != nil {
 | 
			
		||||
		return spec.Volume.ScaleIO, nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -257,6 +272,66 @@ func getVolumeSourceFromSpec(spec *volume.Spec) (*api.ScaleIOVolumeSource, error
 | 
			
		||||
	return nil, fmt.Errorf("ScaleIO not defined in spec")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getVolumeSourceAttribs(spec *volume.Spec) (*volSourceAttribs, error) {
 | 
			
		||||
	attribs := new(volSourceAttribs)
 | 
			
		||||
	if pvSource, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
 | 
			
		||||
		attribs.volName = pvSource.VolumeName
 | 
			
		||||
		attribs.fsType = pvSource.FSType
 | 
			
		||||
		attribs.readOnly = pvSource.ReadOnly
 | 
			
		||||
	} else if pSource, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
 | 
			
		||||
		attribs.volName = pSource.VolumeName
 | 
			
		||||
		attribs.fsType = pSource.FSType
 | 
			
		||||
		attribs.readOnly = pSource.ReadOnly
 | 
			
		||||
	} else {
 | 
			
		||||
		msg := log("failed to get ScaleIOVolumeSource or ScaleIOPersistentVolumeSource from spec")
 | 
			
		||||
		glog.Error(msg)
 | 
			
		||||
		return nil, errors.New(msg)
 | 
			
		||||
	}
 | 
			
		||||
	return attribs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getScaleIOPersistentVolumeSourceFromSpec(spec *volume.Spec) (*api.ScaleIOPersistentVolumeSource, error) {
 | 
			
		||||
	source, err := getVolumeSourceFromSpec(spec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if val, ok := source.(*api.ScaleIOPersistentVolumeSource); ok {
 | 
			
		||||
		return val, nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("spec is not a valid ScaleIOPersistentVolume type")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getScaleIOVolumeSourceFromSpec(spec *volume.Spec) (*api.ScaleIOVolumeSource, error) {
 | 
			
		||||
	source, err := getVolumeSourceFromSpec(spec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if val, ok := source.(*api.ScaleIOVolumeSource); ok {
 | 
			
		||||
		return val, nil
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("spec is not a valid ScaleIOVolume type")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getSecretAndNamespaceFromSpec(spec *volume.Spec, pod *api.Pod) (secretName string, secretNS string, err error) {
 | 
			
		||||
	if source, err := getScaleIOVolumeSourceFromSpec(spec); err == nil {
 | 
			
		||||
		secretName = source.SecretRef.Name
 | 
			
		||||
		if pod != nil {
 | 
			
		||||
			secretNS = pod.Namespace
 | 
			
		||||
		}
 | 
			
		||||
	} else if source, err := getScaleIOPersistentVolumeSourceFromSpec(spec); err == nil {
 | 
			
		||||
		if source.SecretRef != nil {
 | 
			
		||||
			secretName = source.SecretRef.Name
 | 
			
		||||
			secretNS = source.SecretRef.Namespace
 | 
			
		||||
			if secretNS == "" && pod != nil {
 | 
			
		||||
				secretNS = pod.Namespace
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		return "", "", errors.New("failed to get ScaleIOVolumeSource or ScaleIOPersistentVolumeSource")
 | 
			
		||||
	}
 | 
			
		||||
	return secretName, secretNS, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func log(msg string, parts ...interface{}) string {
 | 
			
		||||
	return fmt.Sprintf(fmt.Sprintf("scaleio: %s", msg), parts...)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	api "k8s.io/api/core/v1"
 | 
			
		||||
	utiltesting "k8s.io/client-go/util/testing"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/volume"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
@@ -48,7 +49,7 @@ var (
 | 
			
		||||
		confKey.gateway:          "http://sio/",
 | 
			
		||||
		confKey.volSpecName:      testSioVolName,
 | 
			
		||||
		confKey.volumeName:       "sio-vol",
 | 
			
		||||
		confKey.secretRef:        "sio-secret",
 | 
			
		||||
		confKey.secretName:       "sio-secret",
 | 
			
		||||
		confKey.protectionDomain: "defaultPD",
 | 
			
		||||
		confKey.storagePool:      "deraultSP",
 | 
			
		||||
		confKey.fsType:           "xfs",
 | 
			
		||||
@@ -60,7 +61,7 @@ var (
 | 
			
		||||
 | 
			
		||||
func TestUtilMapVolumeSource(t *testing.T) {
 | 
			
		||||
	data := make(map[string]string)
 | 
			
		||||
	mapVolumeSource(data, vol.VolumeSource.ScaleIO)
 | 
			
		||||
	mapVolumeSpec(data, volume.NewSpecFromVolume(vol))
 | 
			
		||||
	if data[confKey.gateway] != "http://test.scaleio:1111" {
 | 
			
		||||
		t.Error("Unexpected gateway value")
 | 
			
		||||
	}
 | 
			
		||||
@@ -79,9 +80,6 @@ func TestUtilMapVolumeSource(t *testing.T) {
 | 
			
		||||
	if data[confKey.fsType] != "ext4" {
 | 
			
		||||
		t.Error("Unexpected fstype value")
 | 
			
		||||
	}
 | 
			
		||||
	if data[confKey.secretRef] != "test-secret" {
 | 
			
		||||
		t.Error("Unexpected secret ref value")
 | 
			
		||||
	}
 | 
			
		||||
	if data[confKey.sslEnabled] != "false" {
 | 
			
		||||
		t.Error("Unexpected sslEnabled value")
 | 
			
		||||
	}
 | 
			
		||||
@@ -92,8 +90,8 @@ func TestUtilMapVolumeSource(t *testing.T) {
 | 
			
		||||
 | 
			
		||||
func TestUtilValidateConfigs(t *testing.T) {
 | 
			
		||||
	data := map[string]string{
 | 
			
		||||
		confKey.secretRef: "sio-secret",
 | 
			
		||||
		confKey.system:    "sio",
 | 
			
		||||
		confKey.secretName: "sio-secret",
 | 
			
		||||
		confKey.system:     "sio",
 | 
			
		||||
	}
 | 
			
		||||
	if err := validateConfigs(data); err != gatewayNotProvidedErr {
 | 
			
		||||
		t.Error("Expecting error for missing gateway, but did not get it")
 | 
			
		||||
@@ -105,7 +103,7 @@ func TestUtilApplyConfigDefaults(t *testing.T) {
 | 
			
		||||
		confKey.system:     "sio",
 | 
			
		||||
		confKey.gateway:    "http://sio/",
 | 
			
		||||
		confKey.volumeName: "sio-vol",
 | 
			
		||||
		confKey.secretRef:  "test-secret",
 | 
			
		||||
		confKey.secretName: "test-secret",
 | 
			
		||||
	}
 | 
			
		||||
	applyConfigDefaults(data)
 | 
			
		||||
 | 
			
		||||
@@ -130,7 +128,7 @@ func TestUtilApplyConfigDefaults(t *testing.T) {
 | 
			
		||||
	if data[confKey.storageMode] != "ThinProvisioned" {
 | 
			
		||||
		t.Error("Unexpected storage mode value")
 | 
			
		||||
	}
 | 
			
		||||
	if data[confKey.secretRef] != "test-secret" {
 | 
			
		||||
	if data[confKey.secretName] != "test-secret" {
 | 
			
		||||
		t.Error("Unexpected secret ref value")
 | 
			
		||||
	}
 | 
			
		||||
	if data[confKey.sslEnabled] != "false" {
 | 
			
		||||
@@ -157,7 +155,7 @@ func TestUtilSaveConfig(t *testing.T) {
 | 
			
		||||
	config := path.Join(tmpDir, testConfigFile)
 | 
			
		||||
	data := map[string]string{
 | 
			
		||||
		confKey.gateway:    "https://test-gateway/",
 | 
			
		||||
		confKey.secretRef:  "sio-secret",
 | 
			
		||||
		confKey.secretName: "sio-secret",
 | 
			
		||||
		confKey.sslEnabled: "false",
 | 
			
		||||
	}
 | 
			
		||||
	if err := saveConfig(config, data); err != nil {
 | 
			
		||||
@@ -178,7 +176,7 @@ func TestUtilSaveConfig(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUtilAttachSecret(t *testing.T) {
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t)
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
 | 
			
		||||
	defer os.RemoveAll(tmpDir)
 | 
			
		||||
 | 
			
		||||
	plug, err := plugMgr.FindPluginByName(sioPluginName)
 | 
			
		||||
 
 | 
			
		||||
@@ -37,19 +37,19 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type sioVolume struct {
 | 
			
		||||
	sioMgr      *sioMgr
 | 
			
		||||
	plugin      *sioPlugin
 | 
			
		||||
	pod         *api.Pod
 | 
			
		||||
	podUID      types.UID
 | 
			
		||||
	spec        *volume.Spec
 | 
			
		||||
	source      *api.ScaleIOVolumeSource
 | 
			
		||||
	namespace   string
 | 
			
		||||
	volSpecName string
 | 
			
		||||
	volName     string
 | 
			
		||||
	readOnly    bool
 | 
			
		||||
	fsType      string
 | 
			
		||||
	options     volume.VolumeOptions
 | 
			
		||||
	configData  map[string]string
 | 
			
		||||
	sioMgr          *sioMgr
 | 
			
		||||
	plugin          *sioPlugin
 | 
			
		||||
	pod             *api.Pod
 | 
			
		||||
	podUID          types.UID
 | 
			
		||||
	spec            *volume.Spec
 | 
			
		||||
	secretName      string
 | 
			
		||||
	secretNamespace string
 | 
			
		||||
	volSpecName     string
 | 
			
		||||
	volName         string
 | 
			
		||||
	readOnly        bool
 | 
			
		||||
	fsType          string
 | 
			
		||||
	options         volume.VolumeOptions
 | 
			
		||||
	configData      map[string]string
 | 
			
		||||
 | 
			
		||||
	volume.MetricsNil
 | 
			
		||||
}
 | 
			
		||||
@@ -59,7 +59,6 @@ type sioVolume struct {
 | 
			
		||||
var _ volume.Volume = &sioVolume{}
 | 
			
		||||
 | 
			
		||||
// GetPath returns the path where the volume will be mounted.
 | 
			
		||||
// The volumeName is prefixed with the pod's namespace a <pod.Namespace>-<volumeName>
 | 
			
		||||
func (v *sioVolume) GetPath() string {
 | 
			
		||||
	return v.plugin.host.GetPodVolumeDir(
 | 
			
		||||
		v.podUID,
 | 
			
		||||
@@ -128,11 +127,11 @@ func (v *sioVolume) SetUpAt(dir string, fsGroup *int64) error {
 | 
			
		||||
	switch {
 | 
			
		||||
	default:
 | 
			
		||||
		options = append(options, "rw")
 | 
			
		||||
	case isROM && !v.source.ReadOnly:
 | 
			
		||||
	case isROM && !v.readOnly:
 | 
			
		||||
		options = append(options, "rw")
 | 
			
		||||
	case isROM:
 | 
			
		||||
		options = append(options, "ro")
 | 
			
		||||
	case v.source.ReadOnly:
 | 
			
		||||
	case v.readOnly:
 | 
			
		||||
		options = append(options, "ro")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -327,10 +326,10 @@ func (v *sioVolume) Provision() (*api.PersistentVolume, error) {
 | 
			
		||||
				),
 | 
			
		||||
			},
 | 
			
		||||
			PersistentVolumeSource: api.PersistentVolumeSource{
 | 
			
		||||
				ScaleIO: &api.ScaleIOVolumeSource{
 | 
			
		||||
				ScaleIO: &api.ScaleIOPersistentVolumeSource{
 | 
			
		||||
					Gateway:          v.configData[confKey.gateway],
 | 
			
		||||
					SSLEnabled:       sslEnabled,
 | 
			
		||||
					SecretRef:        &api.LocalObjectReference{Name: v.configData[confKey.secretRef]},
 | 
			
		||||
					SecretRef:        &api.SecretReference{Name: v.secretName, Namespace: v.secretNamespace},
 | 
			
		||||
					System:           v.configData[confKey.system],
 | 
			
		||||
					ProtectionDomain: v.configData[confKey.protectionDomain],
 | 
			
		||||
					StoragePool:      v.configData[confKey.storagePool],
 | 
			
		||||
@@ -366,13 +365,17 @@ func (v *sioVolume) setSioMgr() error {
 | 
			
		||||
			glog.V(4).Info(log("previous config file not found, creating new one"))
 | 
			
		||||
			// prepare config data
 | 
			
		||||
			configData = make(map[string]string)
 | 
			
		||||
			mapVolumeSource(configData, v.source)
 | 
			
		||||
			mapVolumeSpec(configData, v.spec)
 | 
			
		||||
 | 
			
		||||
			// additional config data
 | 
			
		||||
			configData[confKey.secretNamespace] = v.secretNamespace
 | 
			
		||||
			configData[confKey.secretName] = v.secretName
 | 
			
		||||
			configData[confKey.volSpecName] = v.volSpecName
 | 
			
		||||
 | 
			
		||||
			if err := validateConfigs(configData); err != nil {
 | 
			
		||||
				glog.Error(log("config setup failed: %s", err))
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			configData[confKey.namespace] = v.namespace
 | 
			
		||||
			configData[confKey.volSpecName] = v.volSpecName
 | 
			
		||||
 | 
			
		||||
			// persist config
 | 
			
		||||
			if err := saveConfig(configName, configData); err != nil {
 | 
			
		||||
@@ -381,7 +384,7 @@ func (v *sioVolume) setSioMgr() error {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		// merge in secret
 | 
			
		||||
		if err := attachSecret(v.plugin, v.namespace, configData); err != nil {
 | 
			
		||||
		if err := attachSecret(v.plugin, v.secretNamespace, configData); err != nil {
 | 
			
		||||
			glog.Error(log("failed to load secret: %v", err))
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
@@ -414,12 +417,13 @@ func (v *sioVolume) resetSioMgr() error {
 | 
			
		||||
			glog.Error(log("failed to load config data: %v", err))
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		v.namespace = configData[confKey.namespace]
 | 
			
		||||
		v.secretName = configData[confKey.secretName]
 | 
			
		||||
		v.secretNamespace = configData[confKey.secretNamespace]
 | 
			
		||||
		v.volName = configData[confKey.volumeName]
 | 
			
		||||
		v.volSpecName = configData[confKey.volSpecName]
 | 
			
		||||
 | 
			
		||||
		// attach secret
 | 
			
		||||
		if err := attachSecret(v.plugin, v.namespace, configData); err != nil {
 | 
			
		||||
		if err := attachSecret(v.plugin, v.secretNamespace, configData); err != nil {
 | 
			
		||||
			glog.Error(log("failed to load secret: %v", err))
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
@@ -446,21 +450,22 @@ func (v *sioVolume) resetSioMgr() error {
 | 
			
		||||
func (v *sioVolume) setSioMgrFromConfig() error {
 | 
			
		||||
	glog.V(4).Info(log("setting scaleio mgr from available config"))
 | 
			
		||||
	if v.sioMgr == nil {
 | 
			
		||||
		configData := v.configData
 | 
			
		||||
		applyConfigDefaults(configData)
 | 
			
		||||
		if err := validateConfigs(configData); err != nil {
 | 
			
		||||
		applyConfigDefaults(v.configData)
 | 
			
		||||
 | 
			
		||||
		v.configData[confKey.volSpecName] = v.volSpecName
 | 
			
		||||
 | 
			
		||||
		if err := validateConfigs(v.configData); err != nil {
 | 
			
		||||
			glog.Error(log("config data setup failed: %s", err))
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		configData[confKey.namespace] = v.namespace
 | 
			
		||||
		configData[confKey.volSpecName] = v.volSpecName
 | 
			
		||||
 | 
			
		||||
		// copy config and attach secret
 | 
			
		||||
		data := map[string]string{}
 | 
			
		||||
		for k, v := range configData {
 | 
			
		||||
		for k, v := range v.configData {
 | 
			
		||||
			data[k] = v
 | 
			
		||||
		}
 | 
			
		||||
		if err := attachSecret(v.plugin, v.namespace, data); err != nil {
 | 
			
		||||
 | 
			
		||||
		if err := attachSecret(v.plugin, v.secretNamespace, data); err != nil {
 | 
			
		||||
			glog.Error(log("failed to load secret: %v", err))
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
@@ -483,16 +488,20 @@ func (v *sioVolume) setSioMgrFromSpec() error {
 | 
			
		||||
	if v.sioMgr == nil {
 | 
			
		||||
		// get config data form spec volume source
 | 
			
		||||
		configData := map[string]string{}
 | 
			
		||||
		mapVolumeSource(configData, v.source)
 | 
			
		||||
		mapVolumeSpec(configData, v.spec)
 | 
			
		||||
 | 
			
		||||
		// additional config
 | 
			
		||||
		configData[confKey.secretNamespace] = v.secretNamespace
 | 
			
		||||
		configData[confKey.secretName] = v.secretName
 | 
			
		||||
		configData[confKey.volSpecName] = v.volSpecName
 | 
			
		||||
 | 
			
		||||
		if err := validateConfigs(configData); err != nil {
 | 
			
		||||
			glog.Error(log("config setup failed: %s", err))
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		configData[confKey.namespace] = v.namespace
 | 
			
		||||
		configData[confKey.volSpecName] = v.volSpecName
 | 
			
		||||
 | 
			
		||||
		// attach secret object to config data
 | 
			
		||||
		if err := attachSecret(v.plugin, v.namespace, configData); err != nil {
 | 
			
		||||
		if err := attachSecret(v.plugin, v.secretNamespace, configData); err != nil {
 | 
			
		||||
			glog.Error(log("failed to load secret: %v", err))
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,6 +27,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	api "k8s.io/api/core/v1"
 | 
			
		||||
	meta "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
	fakeclient "k8s.io/client-go/kubernetes/fake"
 | 
			
		||||
	utiltesting "k8s.io/client-go/util/testing"
 | 
			
		||||
@@ -39,29 +40,18 @@ var (
 | 
			
		||||
	testSioPD      = "default"
 | 
			
		||||
	testSioVol     = "vol-0001"
 | 
			
		||||
	testns         = "default"
 | 
			
		||||
	testSecret     = "sio-secret"
 | 
			
		||||
	testSioVolName = fmt.Sprintf("%s%s%s", testns, "-", testSioVol)
 | 
			
		||||
	podUID         = types.UID("sio-pod")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newPluginMgr(t *testing.T) (*volume.VolumePluginMgr, string) {
 | 
			
		||||
func newPluginMgr(t *testing.T, apiObject runtime.Object) (*volume.VolumePluginMgr, string) {
 | 
			
		||||
	tmpDir, err := utiltesting.MkTmpdir("scaleio-test")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("can't make a temp dir: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	config := &api.Secret{
 | 
			
		||||
		ObjectMeta: meta.ObjectMeta{
 | 
			
		||||
			Name:      "sio-secret",
 | 
			
		||||
			Namespace: testns,
 | 
			
		||||
			UID:       "1234567890",
 | 
			
		||||
		},
 | 
			
		||||
		Type: api.SecretType("kubernetes.io/scaleio"),
 | 
			
		||||
		Data: map[string][]byte{
 | 
			
		||||
			"username": []byte("username"),
 | 
			
		||||
			"password": []byte("password"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fakeClient := fakeclient.NewSimpleClientset(config)
 | 
			
		||||
	fakeClient := fakeclient.NewSimpleClientset(apiObject)
 | 
			
		||||
	host := volumetest.NewFakeVolumeHostWithNodeLabels(
 | 
			
		||||
		tmpDir,
 | 
			
		||||
		fakeClient,
 | 
			
		||||
@@ -74,8 +64,23 @@ func newPluginMgr(t *testing.T) (*volume.VolumePluginMgr, string) {
 | 
			
		||||
	return plugMgr, tmpDir
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func makeScaleIOSecret(name, namespace string) *api.Secret {
 | 
			
		||||
	return &api.Secret{
 | 
			
		||||
		ObjectMeta: meta.ObjectMeta{
 | 
			
		||||
			Name:      name,
 | 
			
		||||
			Namespace: namespace,
 | 
			
		||||
			UID:       "1234567890",
 | 
			
		||||
		},
 | 
			
		||||
		Type: api.SecretType("kubernetes.io/scaleio"),
 | 
			
		||||
		Data: map[string][]byte{
 | 
			
		||||
			"username": []byte("username"),
 | 
			
		||||
			"password": []byte("password"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVolumeCanSupport(t *testing.T) {
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t)
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
 | 
			
		||||
	defer os.RemoveAll(tmpDir)
 | 
			
		||||
	plug, err := plugMgr.FindPluginByName(sioPluginName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -100,7 +105,7 @@ func TestVolumeCanSupport(t *testing.T) {
 | 
			
		||||
			PersistentVolume: &api.PersistentVolume{
 | 
			
		||||
				Spec: api.PersistentVolumeSpec{
 | 
			
		||||
					PersistentVolumeSource: api.PersistentVolumeSource{
 | 
			
		||||
						ScaleIO: &api.ScaleIOVolumeSource{},
 | 
			
		||||
						ScaleIO: &api.ScaleIOPersistentVolumeSource{},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
@@ -111,7 +116,7 @@ func TestVolumeCanSupport(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVolumeGetAccessModes(t *testing.T) {
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t)
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
 | 
			
		||||
	defer os.RemoveAll(tmpDir)
 | 
			
		||||
	plug, err := plugMgr.FindPersistentPluginByName(sioPluginName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -131,7 +136,7 @@ func containsMode(modes []api.PersistentVolumeAccessMode, mode api.PersistentVol
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVolumeMounterUnmounter(t *testing.T) {
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t)
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
 | 
			
		||||
	defer os.RemoveAll(tmpDir)
 | 
			
		||||
 | 
			
		||||
	plug, err := plugMgr.FindPluginByName(sioPluginName)
 | 
			
		||||
@@ -153,7 +158,7 @@ func TestVolumeMounterUnmounter(t *testing.T) {
 | 
			
		||||
				StoragePool:      "default",
 | 
			
		||||
				VolumeName:       testSioVol,
 | 
			
		||||
				FSType:           "ext4",
 | 
			
		||||
				SecretRef:        &api.LocalObjectReference{Name: "sio-secret"},
 | 
			
		||||
				SecretRef:        &api.LocalObjectReference{Name: testSecret},
 | 
			
		||||
				ReadOnly:         false,
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
@@ -245,7 +250,7 @@ func TestVolumeMounterUnmounter(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVolumeProvisioner(t *testing.T) {
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t)
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
 | 
			
		||||
	defer os.RemoveAll(tmpDir)
 | 
			
		||||
 | 
			
		||||
	plug, err := plugMgr.FindPluginByName(sioPluginName)
 | 
			
		||||
@@ -274,7 +279,7 @@ func TestVolumeProvisioner(t *testing.T) {
 | 
			
		||||
		confKey.system:           "sio",
 | 
			
		||||
		confKey.protectionDomain: testSioPD,
 | 
			
		||||
		confKey.storagePool:      "default",
 | 
			
		||||
		confKey.secretRef:        "sio-secret",
 | 
			
		||||
		confKey.secretName:       testSecret,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	provisioner, err := sioPlug.NewProvisioner(options)
 | 
			
		||||
@@ -296,6 +301,17 @@ func TestVolumeProvisioner(t *testing.T) {
 | 
			
		||||
		t.Fatalf("call to Provision() failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if spec.Namespace != testns {
 | 
			
		||||
		t.Fatalf("unexpected namespace %v", spec.Namespace)
 | 
			
		||||
	}
 | 
			
		||||
	if spec.Spec.ScaleIO.SecretRef == nil {
 | 
			
		||||
		t.Fatalf("unexpected nil value for spec.SecretRef")
 | 
			
		||||
	}
 | 
			
		||||
	if spec.Spec.ScaleIO.SecretRef.Name != testSecret ||
 | 
			
		||||
		spec.Spec.ScaleIO.SecretRef.Namespace != testns {
 | 
			
		||||
		t.Fatalf("spec.SecretRef is not being set properly")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spec.Spec.ClaimRef = &api.ObjectReference{Namespace: testns}
 | 
			
		||||
 | 
			
		||||
	// validate provision
 | 
			
		||||
@@ -379,7 +395,7 @@ func TestVolumeProvisioner(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVolumeProvisionerWithIncompleteConfig(t *testing.T) {
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t)
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
 | 
			
		||||
	defer os.RemoveAll(tmpDir)
 | 
			
		||||
 | 
			
		||||
	plug, err := plugMgr.FindPluginByName(sioPluginName)
 | 
			
		||||
@@ -411,7 +427,7 @@ func TestVolumeProvisionerWithIncompleteConfig(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVolumeProvisionerWithZeroCapacity(t *testing.T) {
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t)
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret(testSecret, testns))
 | 
			
		||||
	defer os.RemoveAll(tmpDir)
 | 
			
		||||
 | 
			
		||||
	plug, err := plugMgr.FindPluginByName(sioPluginName)
 | 
			
		||||
@@ -440,7 +456,7 @@ func TestVolumeProvisionerWithZeroCapacity(t *testing.T) {
 | 
			
		||||
		confKey.system:           "sio",
 | 
			
		||||
		confKey.protectionDomain: testSioPD,
 | 
			
		||||
		confKey.storagePool:      "default",
 | 
			
		||||
		confKey.secretRef:        "sio-secret",
 | 
			
		||||
		confKey.secretName:       "sio-secret",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	provisioner, _ := sioPlug.NewProvisioner(options)
 | 
			
		||||
@@ -457,3 +473,63 @@ func TestVolumeProvisionerWithZeroCapacity(t *testing.T) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestVolumeProvisionerWithSecretNamespace(t *testing.T) {
 | 
			
		||||
	plugMgr, tmpDir := newPluginMgr(t, makeScaleIOSecret("sio-sec", "sio-ns"))
 | 
			
		||||
	defer os.RemoveAll(tmpDir)
 | 
			
		||||
 | 
			
		||||
	plug, err := plugMgr.FindPluginByName(sioPluginName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Can't find the plugin %v", sioPluginName)
 | 
			
		||||
	}
 | 
			
		||||
	sioPlug, ok := plug.(*sioPlugin)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		t.Fatal("Cannot assert plugin to be type sioPlugin")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	options := volume.VolumeOptions{
 | 
			
		||||
		ClusterName: "testcluster",
 | 
			
		||||
		PVName:      "pvc-sio-dynamic-vol",
 | 
			
		||||
		PVC:         volumetest.CreateTestPVC("100Mi", []api.PersistentVolumeAccessMode{api.ReadWriteOnce}),
 | 
			
		||||
		PersistentVolumeReclaimPolicy: api.PersistentVolumeReclaimDelete,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	options.PVC.Spec.AccessModes = []api.PersistentVolumeAccessMode{
 | 
			
		||||
		api.ReadWriteOnce,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	options.PVC.Namespace = "pvc-ns"
 | 
			
		||||
	options.Parameters = map[string]string{
 | 
			
		||||
		confKey.gateway:          "http://test.scaleio:11111",
 | 
			
		||||
		confKey.system:           "sio",
 | 
			
		||||
		confKey.protectionDomain: testSioPD,
 | 
			
		||||
		confKey.storagePool:      "default",
 | 
			
		||||
		confKey.secretName:       "sio-sec",
 | 
			
		||||
		confKey.secretNamespace:  "sio-ns",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	provisioner, _ := sioPlug.NewProvisioner(options)
 | 
			
		||||
	sio := newFakeSio()
 | 
			
		||||
	sioVol := provisioner.(*sioVolume)
 | 
			
		||||
	if err := sioVol.setSioMgrFromConfig(); err != nil {
 | 
			
		||||
		t.Fatalf("failed to create scaleio mgr from config: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	sioVol.sioMgr.client = sio
 | 
			
		||||
 | 
			
		||||
	spec, err := sioVol.Provision()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("call to Provision() failed: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if spec.GetObjectMeta().GetNamespace() != "pvc-ns" {
 | 
			
		||||
		t.Fatalf("unexpected spec.namespace %s", spec.GetObjectMeta().GetNamespace())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if spec.Spec.ScaleIO.SecretRef.Name != "sio-sec" {
 | 
			
		||||
		t.Fatalf("unexpected spec.ScaleIOPersistentVolume.SecretRef.Name %v", spec.Spec.ScaleIO.SecretRef.Name)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if spec.Spec.ScaleIO.SecretRef.Namespace != "sio-ns" {
 | 
			
		||||
		t.Fatalf("unexpected spec.ScaleIOPersistentVolume.SecretRef.Namespace %v", spec.Spec.ScaleIO.SecretRef.Namespace)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -440,7 +440,7 @@ type PersistentVolumeSource struct {
 | 
			
		||||
	PortworxVolume *PortworxVolumeSource `json:"portworxVolume,omitempty" protobuf:"bytes,18,opt,name=portworxVolume"`
 | 
			
		||||
	// ScaleIO represents a ScaleIO persistent volume attached and mounted on Kubernetes nodes.
 | 
			
		||||
	// +optional
 | 
			
		||||
	ScaleIO *ScaleIOVolumeSource `json:"scaleIO,omitempty" protobuf:"bytes,19,opt,name=scaleIO"`
 | 
			
		||||
	ScaleIO *ScaleIOPersistentVolumeSource `json:"scaleIO,omitempty" protobuf:"bytes,19,opt,name=scaleIO"`
 | 
			
		||||
	// Local represents directly-attached storage with node affinity
 | 
			
		||||
	// +optional
 | 
			
		||||
	Local *LocalVolumeSource `json:"local,omitempty" protobuf:"bytes,20,opt,name=local"`
 | 
			
		||||
@@ -1396,13 +1396,48 @@ type ScaleIOVolumeSource struct {
 | 
			
		||||
	// Flag to enable/disable SSL communication with Gateway, default false
 | 
			
		||||
	// +optional
 | 
			
		||||
	SSLEnabled bool `json:"sslEnabled,omitempty" protobuf:"varint,4,opt,name=sslEnabled"`
 | 
			
		||||
	// The name of the Protection Domain for the configured storage (defaults to "default").
 | 
			
		||||
	// The name of the ScaleIO Protection Domain for the configured storage.
 | 
			
		||||
	// +optional
 | 
			
		||||
	ProtectionDomain string `json:"protectionDomain,omitempty" protobuf:"bytes,5,opt,name=protectionDomain"`
 | 
			
		||||
	// The Storage Pool associated with the protection domain (defaults to "default").
 | 
			
		||||
	// The ScaleIO Storage Pool associated with the protection domain.
 | 
			
		||||
	// +optional
 | 
			
		||||
	StoragePool string `json:"storagePool,omitempty" protobuf:"bytes,6,opt,name=storagePool"`
 | 
			
		||||
	// Indicates whether the storage for a volume should be thick or thin (defaults to "thin").
 | 
			
		||||
	// Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned.
 | 
			
		||||
	// +optional
 | 
			
		||||
	StorageMode string `json:"storageMode,omitempty" protobuf:"bytes,7,opt,name=storageMode"`
 | 
			
		||||
	// The name of a volume already created in the ScaleIO system
 | 
			
		||||
	// that is associated with this volume source.
 | 
			
		||||
	VolumeName string `json:"volumeName,omitempty" protobuf:"bytes,8,opt,name=volumeName"`
 | 
			
		||||
	// Filesystem type to mount.
 | 
			
		||||
	// Must be a filesystem type supported by the host operating system.
 | 
			
		||||
	// Ex. "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified.
 | 
			
		||||
	// +optional
 | 
			
		||||
	FSType string `json:"fsType,omitempty" protobuf:"bytes,9,opt,name=fsType"`
 | 
			
		||||
	// Defaults to false (read/write). ReadOnly here will force
 | 
			
		||||
	// the ReadOnly setting in VolumeMounts.
 | 
			
		||||
	// +optional
 | 
			
		||||
	ReadOnly bool `json:"readOnly,omitempty" protobuf:"varint,10,opt,name=readOnly"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ScaleIOPersistentVolumeSource represents a persistent ScaleIO volume
 | 
			
		||||
type ScaleIOPersistentVolumeSource struct {
 | 
			
		||||
	// The host address of the ScaleIO API Gateway.
 | 
			
		||||
	Gateway string `json:"gateway" protobuf:"bytes,1,opt,name=gateway"`
 | 
			
		||||
	// The name of the storage system as configured in ScaleIO.
 | 
			
		||||
	System string `json:"system" protobuf:"bytes,2,opt,name=system"`
 | 
			
		||||
	// SecretRef references to the secret for ScaleIO user and other
 | 
			
		||||
	// sensitive information. If this is not provided, Login operation will fail.
 | 
			
		||||
	SecretRef *SecretReference `json:"secretRef" protobuf:"bytes,3,opt,name=secretRef"`
 | 
			
		||||
	// Flag to enable/disable SSL communication with Gateway, default false
 | 
			
		||||
	// +optional
 | 
			
		||||
	SSLEnabled bool `json:"sslEnabled,omitempty" protobuf:"varint,4,opt,name=sslEnabled"`
 | 
			
		||||
	// The name of the ScaleIO Protection Domain for the configured storage.
 | 
			
		||||
	// +optional
 | 
			
		||||
	ProtectionDomain string `json:"protectionDomain,omitempty" protobuf:"bytes,5,opt,name=protectionDomain"`
 | 
			
		||||
	// The ScaleIO Storage Pool associated with the protection domain.
 | 
			
		||||
	// +optional
 | 
			
		||||
	StoragePool string `json:"storagePool,omitempty" protobuf:"bytes,6,opt,name=storagePool"`
 | 
			
		||||
	// Indicates whether the storage for a volume should be ThickProvisioned or ThinProvisioned.
 | 
			
		||||
	// +optional
 | 
			
		||||
	StorageMode string `json:"storageMode,omitempty" protobuf:"bytes,7,opt,name=storageMode"`
 | 
			
		||||
	// The name of a volume already created in the ScaleIO system
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user