Merge pull request #5863 from prksu/nfs-provisioner
nfs: nfs provisioner controlled by operator
This commit is contained in:
10 files changed
+510
-255
No files matched your search
@@ -9,6 +9,54 @@ metadata:
|
||||
name: rook-nfs-server
|
||||
namespace: rook-nfs
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: rook-nfs-provisioner-runner
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumes"]
|
||||
verbs: ["get", "list", "watch", "create", "delete"]
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumeclaims"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: ["storage.k8s.io"]
|
||||
resources: ["storageclasses"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["events"]
|
||||
verbs: ["create", "update", "patch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["services", "endpoints"]
|
||||
verbs: ["get"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["podsecuritypolicies"]
|
||||
resourceNames: ["nfs-provisioner"]
|
||||
verbs: ["use"]
|
||||
- apiGroups: [""]
|
||||
resources: ["endpoints"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch"]
|
||||
- apiGroups:
|
||||
- nfs.rook.io
|
||||
resources:
|
||||
- "*"
|
||||
verbs:
|
||||
- "*"
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: rook-nfs-provisioner-runner
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: rook-nfs-server
|
||||
# replace with namespace where provisioner is deployed
|
||||
namespace: rook-nfs
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: rook-nfs-provisioner-runner
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
---
|
||||
# A default storageclass must be present
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: rook-nfs-provisioner-runner
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumes"]
|
||||
verbs: ["get", "list", "watch", "create", "delete"]
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumeclaims"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: ["storage.k8s.io"]
|
||||
resources: ["storageclasses"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["events"]
|
||||
verbs: ["create", "update", "patch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["services", "endpoints"]
|
||||
verbs: ["get"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["podsecuritypolicies"]
|
||||
resourceNames: ["nfs-provisioner"]
|
||||
verbs: ["use"]
|
||||
- apiGroups: [""]
|
||||
resources: ["endpoints"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch"]
|
||||
- apiGroups:
|
||||
- nfs.rook.io
|
||||
resources:
|
||||
- "*"
|
||||
verbs:
|
||||
- "*"
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: run-nfs-provisioner
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: rook-nfs-provisioner
|
||||
# replace with namespace where provisioner is deployed
|
||||
namespace: rook-nfs-system
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: rook-nfs-provisioner-runner
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: rook-nfs-provisioner
|
||||
namespace: rook-nfs-system
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: rook-nfs-provisioner
|
||||
namespace: rook-nfs-system
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rook-nfs-provisioner
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rook-nfs-provisioner
|
||||
spec:
|
||||
serviceAccount: rook-nfs-provisioner
|
||||
containers:
|
||||
- name: rook-nfs-provisioner
|
||||
image: rook/nfs:master
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: ["nfs", "provisioner","--provisioner=rook.io/nfs-provisioner"]
|
||||
env:
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
@@ -8,6 +8,6 @@ parameters:
|
||||
exportName: share1
|
||||
nfsServerName: rook-nfs
|
||||
nfsServerNamespace: rook-nfs
|
||||
provisioner: rook.io/nfs-provisioner
|
||||
provisioner: nfs.rook.io/rook-nfs-provisioner
|
||||
reclaimPolicy: Delete
|
||||
volumeBindingMode: Immediate
|
||||
@@ -19,6 +19,7 @@ package nfs
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -158,8 +159,8 @@ func (r *NFSServerReconciler) reconcileNFSServerConfig(ctx context.Context, cr *
|
||||
nfsGaneshaConfig := `
|
||||
EXPORT {
|
||||
Export_Id = ` + fmt.Sprintf("%v", id) + `;
|
||||
Path = /` + claimName + `;
|
||||
Pseudo = /` + claimName + `;
|
||||
Path = ` + path.Join("/", claimName) + `;
|
||||
Pseudo = ` + path.Join("/", claimName) + `;
|
||||
Protocols = 4;
|
||||
Transports = TCP;
|
||||
Sectype = sys;
|
||||
@@ -287,12 +288,16 @@ func (r *NFSServerReconciler) reconcileNFSServer(ctx context.Context, cr *nfsv1a
|
||||
|
||||
volumeMounts = append(volumeMounts, corev1.VolumeMount{
|
||||
Name: shareName,
|
||||
MountPath: "/" + claimName,
|
||||
MountPath: path.Join("/", claimName),
|
||||
})
|
||||
}
|
||||
|
||||
sts.Spec.Template.Spec.Volumes = volumes
|
||||
sts.Spec.Template.Spec.Containers[0].VolumeMounts = volumeMounts
|
||||
for i, container := range sts.Spec.Template.Spec.Containers {
|
||||
if container.Name == "nfs-server" || container.Name == "nfs-provisioner" {
|
||||
sts.Spec.Template.Spec.Containers[i].VolumeMounts = volumeMounts
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
@@ -17,6 +17,8 @@ limitations under the License.
|
||||
package nfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
@@ -92,7 +94,7 @@ func (r *resource) Generate() *nfsv1alpha1.NFSServer {
|
||||
}
|
||||
|
||||
func TestNFSServerReconciler_Reconcile(t *testing.T) {
|
||||
expectedStatefulSet := func(scheme *runtime.Scheme, cr *nfsv1alpha1.NFSServer) *appsv1.StatefulSet {
|
||||
expectedServerFunc := func(scheme *runtime.Scheme, cr *nfsv1alpha1.NFSServer) *appsv1.StatefulSet {
|
||||
sts := newStatefulSetForNFSServer(cr)
|
||||
sts.Spec.Selector = &metav1.LabelSelector{
|
||||
MatchLabels: newLabels(cr),
|
||||
@@ -137,16 +139,26 @@ func TestNFSServerReconciler_Reconcile(t *testing.T) {
|
||||
|
||||
volumeMounts = append(volumeMounts, corev1.VolumeMount{
|
||||
Name: shareName,
|
||||
MountPath: "/" + claimName,
|
||||
MountPath: path.Join("/", claimName),
|
||||
})
|
||||
}
|
||||
sts.Status.ReadyReplicas = int32(cr.Spec.Replicas)
|
||||
sts.Spec.Template.Spec.Volumes = volumes
|
||||
sts.Spec.Template.Spec.Containers[0].VolumeMounts = volumeMounts
|
||||
for i, container := range sts.Spec.Template.Spec.Containers {
|
||||
if container.Name == "nfs-server" || container.Name == "nfs-provisioner" {
|
||||
sts.Spec.Template.Spec.Containers[i].VolumeMounts = volumeMounts
|
||||
}
|
||||
}
|
||||
|
||||
return sts
|
||||
}
|
||||
|
||||
expectedServerServiceFunc := func(scheme *runtime.Scheme, cr *nfsv1alpha1.NFSServer) *corev1.Service {
|
||||
svc := newServiceForNFSServer(cr)
|
||||
_ = controllerutil.SetControllerReference(cr, svc, scheme)
|
||||
return svc
|
||||
}
|
||||
|
||||
rr := reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: "nfs-server",
|
||||
@@ -208,11 +220,18 @@ func TestNFSServerReconciler_Reconcile(t *testing.T) {
|
||||
scheme := clientgoscheme.Scheme
|
||||
scheme.AddKnownTypes(nfsv1alpha1.SchemeGroupVersion, tt.cr)
|
||||
|
||||
expectedServer := expectedServerFunc(scheme, tt.cr)
|
||||
expectedServerService := expectedServerServiceFunc(scheme, tt.cr)
|
||||
|
||||
objs := []runtime.Object{
|
||||
tt.cr,
|
||||
expectedStatefulSet(scheme, tt.cr),
|
||||
expectedServer,
|
||||
expectedServerService,
|
||||
}
|
||||
|
||||
expectedServer.GetObjectKind().SetGroupVersionKind(appsv1.SchemeGroupVersion.WithKind("StatefulSet"))
|
||||
expectedServerService.GetObjectKind().SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("Service"))
|
||||
|
||||
fc := fake.NewFakeClient(objs...)
|
||||
fr := record.NewFakeRecorder(2)
|
||||
|
||||
@@ -230,6 +249,24 @@ func TestNFSServerReconciler_Reconcile(t *testing.T) {
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("NFSServerReconciler.Reconcile() = %v, want %v", got, tt.want)
|
||||
}
|
||||
|
||||
gotServer := &appsv1.StatefulSet{}
|
||||
if err := fc.Get(context.Background(), tt.args.req.NamespacedName, gotServer); err != nil {
|
||||
t.Errorf("NFSServerReconciler.Reconcile() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(gotServer, expectedServer) {
|
||||
t.Errorf("NFSServerReconciler.Reconcile() = %v, want %v", gotServer, expectedServer)
|
||||
}
|
||||
|
||||
gotServerService := &corev1.Service{}
|
||||
if err := fc.Get(context.Background(), tt.args.req.NamespacedName, gotServerService); err != nil {
|
||||
t.Errorf("NFSServerReconciler.Reconcile() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(gotServerService, expectedServerService) {
|
||||
t.Errorf("NFSServerReconciler.Reconcile() = %v, want %v", gotServerService, expectedServerService)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
+126
-66
@@ -18,11 +18,14 @@ package nfs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
rookclient "github.com/rook/rook/pkg/client/clientset/versioned"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
storage "k8s.io/api/storage/v1"
|
||||
storagev1 "k8s.io/api/storage/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
"sigs.k8s.io/sig-storage-lib-external-provisioner/controller"
|
||||
@@ -30,50 +33,87 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type nfsProvisioner struct {
|
||||
const (
|
||||
nfsServerNameSCParam = "nfsServerName"
|
||||
nfsServerNamespaceSCParam = "nfsServerNamespace"
|
||||
exportNameSCParam = "exportName"
|
||||
)
|
||||
|
||||
var (
|
||||
mountPath = "/"
|
||||
)
|
||||
|
||||
type Provisioner struct {
|
||||
client kubernetes.Interface
|
||||
rookClient rookclient.Interface
|
||||
}
|
||||
|
||||
var _ controller.Provisioner = &nfsProvisioner{}
|
||||
var _ controller.Provisioner = &Provisioner{}
|
||||
|
||||
// NewNFSProvisioner returns an instance of nfsProvisioner
|
||||
func NewNFSProvisioner(clientset kubernetes.Interface, rookClientset rookclient.Interface) *nfsProvisioner {
|
||||
return &nfsProvisioner{clientset, rookClientset}
|
||||
func NewNFSProvisioner(clientset kubernetes.Interface, rookClientset rookclient.Interface) *Provisioner {
|
||||
return &Provisioner{clientset, rookClientset}
|
||||
}
|
||||
|
||||
func (p *nfsProvisioner) Provision(options controller.ProvisionOptions) (*v1.PersistentVolume, error) {
|
||||
func (p *Provisioner) Provision(options controller.ProvisionOptions) (*v1.PersistentVolume, error) {
|
||||
logger.Infof("nfs provisioner: ProvisionOptions %v", options)
|
||||
|
||||
// Get the storage class for this volume.
|
||||
storageClass, err := p.getClassForPVC(options.PVC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options.PVC.Spec.Selector != nil {
|
||||
return nil, fmt.Errorf("claim Selector is not supported")
|
||||
}
|
||||
|
||||
nfsServerName, present := storageClass.Parameters["nfsServerName"]
|
||||
if !present {
|
||||
return nil, errors.Errorf("NFS share Path not found in the storageclass: %v", storageClass.GetName())
|
||||
}
|
||||
|
||||
nfsNamespace, present := storageClass.Parameters["nfsServerNamespace"]
|
||||
if !present {
|
||||
return nil, errors.Errorf("NFS share Path not found in the storageclass: %v", storageClass.GetName())
|
||||
}
|
||||
|
||||
exportName, present := storageClass.Parameters["exportName"]
|
||||
if !present {
|
||||
return nil, errors.Errorf("NFS share Path not found in the storageclass: %v", storageClass.GetName())
|
||||
}
|
||||
|
||||
nfsVolumeSource, err := p.getNFSVolumeSource(nfsServerName, nfsNamespace, exportName)
|
||||
sc, err := p.storageClassForPVC(options.PVC)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
serverName, present := sc.Parameters[nfsServerNameSCParam]
|
||||
if !present {
|
||||
return nil, errors.Errorf("NFS share Path not found in the storageclass: %v", sc.GetName())
|
||||
}
|
||||
|
||||
serverNamespace, present := sc.Parameters[nfsServerNamespaceSCParam]
|
||||
if !present {
|
||||
return nil, errors.Errorf("NFS share Path not found in the storageclass: %v", sc.GetName())
|
||||
}
|
||||
|
||||
exportName, present := sc.Parameters[exportNameSCParam]
|
||||
if !present {
|
||||
return nil, errors.Errorf("NFS share Path not found in the storageclass: %v", sc.GetName())
|
||||
}
|
||||
|
||||
nfsserver, err := p.rookClient.NfsV1alpha1().NFSServers(serverNamespace).Get(serverName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nfsserversvc, err := p.client.CoreV1().Services(serverNamespace).Get(serverName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var (
|
||||
exportPath string
|
||||
found bool
|
||||
)
|
||||
|
||||
for _, export := range nfsserver.Spec.Exports {
|
||||
if export.Name == exportName {
|
||||
exportPath = path.Join(mountPath, export.PersistentVolumeClaim.ClaimName)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil, fmt.Errorf("No export name from storageclass is match with NFSServer %s in namespace %s", nfsserver.Name, nfsserver.Namespace)
|
||||
}
|
||||
|
||||
pvName := strings.Join([]string{options.PVC.Namespace, options.PVC.Name, options.PVName}, "-")
|
||||
fullPath := path.Join(exportPath, pvName)
|
||||
if err := os.MkdirAll(fullPath, 0700); err != nil {
|
||||
return nil, errors.New("unable to create directory to provision new pv: " + err.Error())
|
||||
}
|
||||
|
||||
pv := &v1.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: options.PVName,
|
||||
@@ -86,19 +126,68 @@ func (p *nfsProvisioner) Provision(options controller.ProvisionOptions) (*v1.Per
|
||||
v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
|
||||
},
|
||||
PersistentVolumeSource: v1.PersistentVolumeSource{
|
||||
NFS: nfsVolumeSource,
|
||||
NFS: &v1.NFSVolumeSource{
|
||||
Server: nfsserversvc.Spec.ClusterIP,
|
||||
Path: fullPath,
|
||||
ReadOnly: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return pv, nil
|
||||
}
|
||||
|
||||
func (p *nfsProvisioner) Delete(volume *v1.PersistentVolume) error {
|
||||
return nil
|
||||
func (p *Provisioner) Delete(volume *v1.PersistentVolume) error {
|
||||
nfsPath := volume.Spec.PersistentVolumeSource.NFS.Path
|
||||
pvName := path.Base(nfsPath)
|
||||
|
||||
sc, err := p.storageClassForPV(volume)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serverName, present := sc.Parameters[nfsServerNameSCParam]
|
||||
if !present {
|
||||
return errors.Errorf("NFS share Path not found in the storageclass: %v", sc.GetName())
|
||||
}
|
||||
|
||||
serverNamespace, present := sc.Parameters[nfsServerNamespaceSCParam]
|
||||
if !present {
|
||||
return errors.Errorf("NFS share Path not found in the storageclass: %v", sc.GetName())
|
||||
}
|
||||
|
||||
exportName, present := sc.Parameters[exportNameSCParam]
|
||||
if !present {
|
||||
return errors.Errorf("NFS share Path not found in the storageclass: %v", sc.GetName())
|
||||
}
|
||||
|
||||
nfsserver, err := p.rookClient.NfsV1alpha1().NFSServers(serverNamespace).Get(serverName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
exportPath string
|
||||
found bool
|
||||
)
|
||||
|
||||
for _, export := range nfsserver.Spec.Exports {
|
||||
if export.Name == exportName {
|
||||
exportPath = path.Join(mountPath, export.PersistentVolumeClaim.ClaimName)
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return fmt.Errorf("No export name from storageclass is match with NFSServer %s in namespace %s", nfsserver.Name, nfsserver.Namespace)
|
||||
}
|
||||
|
||||
fullPath := path.Join(exportPath, pvName)
|
||||
return os.RemoveAll(fullPath)
|
||||
}
|
||||
|
||||
// getClassForPV returns StorageClass
|
||||
func (p *nfsProvisioner) getClassForPV(pv *v1.PersistentVolume) (*storage.StorageClass, error) {
|
||||
func (p *Provisioner) storageClassForPV(pv *v1.PersistentVolume) (*storagev1.StorageClass, error) {
|
||||
if p.client == nil {
|
||||
return nil, fmt.Errorf("Cannot get kube client")
|
||||
}
|
||||
@@ -106,15 +195,11 @@ func (p *nfsProvisioner) getClassForPV(pv *v1.PersistentVolume) (*storage.Storag
|
||||
if className == "" {
|
||||
return nil, fmt.Errorf("Volume has no storage class")
|
||||
}
|
||||
class, err := p.client.StorageV1().StorageClasses().Get(className, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return class, nil
|
||||
|
||||
return p.client.StorageV1().StorageClasses().Get(className, metav1.GetOptions{})
|
||||
}
|
||||
|
||||
// getClassForPVC returns StorageClass
|
||||
func (p *nfsProvisioner) getClassForPVC(pvc *v1.PersistentVolumeClaim) (*storage.StorageClass, error) {
|
||||
func (p *Provisioner) storageClassForPVC(pvc *v1.PersistentVolumeClaim) (*storagev1.StorageClass, error) {
|
||||
if p.client == nil {
|
||||
return nil, fmt.Errorf("Cannot get kube client")
|
||||
}
|
||||
@@ -122,31 +207,6 @@ func (p *nfsProvisioner) getClassForPVC(pvc *v1.PersistentVolumeClaim) (*storage
|
||||
if className == "" {
|
||||
return nil, fmt.Errorf("Volume has no storage class")
|
||||
}
|
||||
class, err := p.client.StorageV1().StorageClasses().Get(className, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return class, nil
|
||||
}
|
||||
|
||||
// getNFSVolumeSource returns the nfs source configuration for the pv
|
||||
func (p *nfsProvisioner) getNFSVolumeSource(nfsServerName, namespace, exportName string) (*v1.NFSVolumeSource, error) {
|
||||
nfsServer, err := p.rookClient.NfsV1alpha1().NFSServers(namespace).Get(nfsServerName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nfsService, err := p.client.CoreV1().Services(namespace).Get(nfsServerName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, export := range nfsServer.Spec.Exports {
|
||||
if export.Name == exportName {
|
||||
return &v1.NFSVolumeSource{
|
||||
ReadOnly: false,
|
||||
Server: nfsService.Spec.ClusterIP,
|
||||
Path: "/" + export.PersistentVolumeClaim.ClaimName,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Errorf("exportName not Found")
|
||||
return p.client.StorageV1().StorageClasses().Get(className, metav1.GetOptions{})
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
Copyright 2019 The Rook Authors. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package nfs
|
||||
|
||||
import (
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
rookclient "github.com/rook/rook/pkg/client/clientset/versioned"
|
||||
rookclientfake "github.com/rook/rook/pkg/client/clientset/versioned/fake"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
storagev1 "k8s.io/api/storage/v1"
|
||||
apiresource "k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
k8sclientfake "k8s.io/client-go/kubernetes/fake"
|
||||
"sigs.k8s.io/sig-storage-lib-external-provisioner/controller"
|
||||
)
|
||||
|
||||
func init() {
|
||||
mountPath = "/tmp/test-rook-nfs"
|
||||
}
|
||||
|
||||
func newDummyStorageClass(name string, nfsServerNamespacedName types.NamespacedName, reclaimPolicy corev1.PersistentVolumeReclaimPolicy) *storagev1.StorageClass {
|
||||
return &storagev1.StorageClass{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Parameters: map[string]string{
|
||||
nfsServerNameSCParam: nfsServerNamespacedName.Name,
|
||||
nfsServerNamespaceSCParam: nfsServerNamespacedName.Namespace,
|
||||
exportNameSCParam: name,
|
||||
},
|
||||
ReclaimPolicy: &reclaimPolicy,
|
||||
}
|
||||
}
|
||||
|
||||
func newDummyPVC(name, namespace string, capacity apiresource.Quantity, storageClassName string) *corev1.PersistentVolumeClaim {
|
||||
return &corev1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: corev1.PersistentVolumeClaimSpec{
|
||||
Resources: corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceName(corev1.ResourceStorage): capacity,
|
||||
},
|
||||
},
|
||||
StorageClassName: &storageClassName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newDummyPV(name, scName, expectedPath string, expectedCapacity apiresource.Quantity, expectedReclaimPolicy corev1.PersistentVolumeReclaimPolicy) *corev1.PersistentVolume {
|
||||
return &corev1.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
},
|
||||
Spec: corev1.PersistentVolumeSpec{
|
||||
PersistentVolumeReclaimPolicy: expectedReclaimPolicy,
|
||||
Capacity: corev1.ResourceList{
|
||||
corev1.ResourceName(corev1.ResourceStorage): expectedCapacity,
|
||||
},
|
||||
PersistentVolumeSource: corev1.PersistentVolumeSource{
|
||||
NFS: &corev1.NFSVolumeSource{
|
||||
Path: expectedPath,
|
||||
},
|
||||
},
|
||||
StorageClassName: scName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisioner_Provision(t *testing.T) {
|
||||
if err := os.MkdirAll(mountPath, 0755); err != nil {
|
||||
t.Error("error creating test provisioner directory")
|
||||
}
|
||||
|
||||
defer os.RemoveAll(mountPath)
|
||||
|
||||
nfsserver := newCustomResource(types.NamespacedName{Name: "test-nfsserver", Namespace: "test-nfsserver"}).WithExports("share-1", "ReadWrite", "none", "test-claim").Generate()
|
||||
|
||||
type fields struct {
|
||||
client kubernetes.Interface
|
||||
rookClient rookclient.Interface
|
||||
}
|
||||
type args struct {
|
||||
options controller.ProvisionOptions
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *corev1.PersistentVolume
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success create volume",
|
||||
fields: fields{
|
||||
client: k8sclientfake.NewSimpleClientset(
|
||||
newServiceForNFSServer(nfsserver),
|
||||
newDummyStorageClass("share-1", types.NamespacedName{Name: nfsserver.Name, Namespace: nfsserver.Namespace}, corev1.PersistentVolumeReclaimDelete),
|
||||
),
|
||||
rookClient: rookclientfake.NewSimpleClientset(
|
||||
nfsserver,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
options: controller.ProvisionOptions{
|
||||
StorageClass: newDummyStorageClass("share-1", types.NamespacedName{Name: nfsserver.Name, Namespace: nfsserver.Namespace}, corev1.PersistentVolumeReclaimDelete),
|
||||
PVName: "share-1-pvc",
|
||||
PVC: newDummyPVC("share-1-pvc", "default", apiresource.MustParse("1Mi"), "share-1"),
|
||||
},
|
||||
},
|
||||
want: newDummyPV("share-1-pvc", "", "/tmp/test-rook-nfs/test-claim/default-share-1-pvc-share-1-pvc", apiresource.MustParse("1Mi"), corev1.PersistentVolumeReclaimDelete),
|
||||
},
|
||||
{
|
||||
name: "no matching export",
|
||||
fields: fields{
|
||||
client: k8sclientfake.NewSimpleClientset(
|
||||
newServiceForNFSServer(nfsserver),
|
||||
newDummyStorageClass("foo", types.NamespacedName{Name: nfsserver.Name, Namespace: nfsserver.Namespace}, corev1.PersistentVolumeReclaimDelete),
|
||||
),
|
||||
rookClient: rookclientfake.NewSimpleClientset(
|
||||
nfsserver,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
options: controller.ProvisionOptions{
|
||||
StorageClass: newDummyStorageClass("foo", types.NamespacedName{Name: nfsserver.Name, Namespace: nfsserver.Namespace}, corev1.PersistentVolumeReclaimDelete),
|
||||
PVName: "share-1-pvc",
|
||||
PVC: newDummyPVC("share-1-pvc", "default", apiresource.MustParse("1Mi"), "foo"),
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := &Provisioner{
|
||||
client: tt.fields.client,
|
||||
rookClient: tt.fields.rookClient,
|
||||
}
|
||||
got, err := p.Provision(tt.args.options)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("Provisioner.Provision() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("Provisioner.Provision() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProvisioner_Delete(t *testing.T) {
|
||||
if err := os.MkdirAll(mountPath, 0755); err != nil {
|
||||
t.Error("error creating test provisioner directory")
|
||||
}
|
||||
|
||||
defer os.RemoveAll(mountPath)
|
||||
|
||||
nfsserver := newCustomResource(types.NamespacedName{Name: "test-nfsserver", Namespace: "test-nfsserver"}).WithExports("share-1", "ReadWrite", "none", "test-claim").Generate()
|
||||
type fields struct {
|
||||
client kubernetes.Interface
|
||||
rookClient rookclient.Interface
|
||||
}
|
||||
type args struct {
|
||||
volume *corev1.PersistentVolume
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "success delete volume",
|
||||
fields: fields{
|
||||
client: k8sclientfake.NewSimpleClientset(
|
||||
newServiceForNFSServer(nfsserver),
|
||||
newDummyStorageClass("share-1", types.NamespacedName{Name: nfsserver.Name, Namespace: nfsserver.Namespace}, corev1.PersistentVolumeReclaimDelete),
|
||||
),
|
||||
rookClient: rookclientfake.NewSimpleClientset(
|
||||
nfsserver,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
volume: newDummyPV("share-1-pvc", "share-1", "/tmp/test-rook-nfs/test-claim/default-share-1-pvc-share-1-pvc", apiresource.MustParse("1Mi"), corev1.PersistentVolumeReclaimDelete),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := &Provisioner{
|
||||
client: tt.fields.client,
|
||||
rookClient: tt.fields.rookClient,
|
||||
}
|
||||
if err := p.Delete(tt.args.volume); (err != nil) != tt.wantErr {
|
||||
t.Errorf("Provisioner.Delete() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ func newServiceForNFSServer(cr *nfsv1alpha1.NFSServer) *corev1.Service {
|
||||
}
|
||||
|
||||
func newStatefulSetForNFSServer(cr *nfsv1alpha1.NFSServer) *appsv1.StatefulSet {
|
||||
privilaged := true
|
||||
replicas := int32(cr.Spec.Replicas)
|
||||
return &appsv1.StatefulSet{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -87,10 +88,9 @@ func newStatefulSetForNFSServer(cr *nfsv1alpha1.NFSServer) *appsv1.StatefulSet {
|
||||
ServiceAccountName: "rook-nfs-server",
|
||||
Containers: []corev1.Container{
|
||||
{
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
Name: cr.Name,
|
||||
Image: "rook/nfs:master",
|
||||
Args: []string{"nfs", "server", "--ganeshaConfigPath=" + nfsConfigMapPath + "/" + cr.Name},
|
||||
Name: "nfs-server",
|
||||
Image: "rook/nfs:master",
|
||||
Args: []string{"nfs", "server", "--ganeshaConfigPath=" + nfsConfigMapPath + "/" + cr.Name},
|
||||
Ports: []corev1.ContainerPort{
|
||||
{
|
||||
Name: "nfs-port",
|
||||
@@ -110,6 +110,16 @@ func newStatefulSetForNFSServer(cr *nfsv1alpha1.NFSServer) *appsv1.StatefulSet {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "nfs-provisioner",
|
||||
Image: "rook/nfs:master",
|
||||
Args: []string{"nfs", "provisioner", "--provisioner=" + "nfs.rook.io/" + cr.Name + "-provisioner"},
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
TerminationMessagePolicy: corev1.TerminationMessageReadFile,
|
||||
SecurityContext: &corev1.SecurityContext{
|
||||
Privileged: &privilaged,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -320,93 +320,6 @@ spec:
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: rook-nfs-provisioner-runner
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumes"]
|
||||
verbs: ["get", "list", "watch", "create", "delete"]
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumeclaims"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: ["storage.k8s.io"]
|
||||
resources: ["storageclasses"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["events"]
|
||||
verbs: ["create", "update", "patch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["services", "endpoints"]
|
||||
verbs: ["get"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["podsecuritypolicies"]
|
||||
resourceNames: ["nfs-provisioner"]
|
||||
verbs: ["use"]
|
||||
- apiGroups: [""]
|
||||
resources: ["endpoints"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch"]
|
||||
- apiGroups:
|
||||
- nfs.rook.io
|
||||
resources:
|
||||
- "*"
|
||||
verbs:
|
||||
- "*"
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: run-nfs-provisioner
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: rook-nfs-provisioner
|
||||
# replace with namespace where provisioner is deployed
|
||||
namespace: ` + namespace + `
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: rook-nfs-provisioner-runner
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: rook-nfs-provisioner
|
||||
namespace: ` + namespace + `
|
||||
---
|
||||
kind: Deployment
|
||||
apiVersion: apps/v1
|
||||
metadata:
|
||||
name: rook-nfs-provisioner
|
||||
namespace: ` + namespace + `
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rook-nfs-provisioner
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: rook-nfs-provisioner
|
||||
spec:
|
||||
serviceAccount: rook-nfs-provisioner
|
||||
containers:
|
||||
- name: rook-nfs-provisioner
|
||||
image: rook/nfs:master
|
||||
imagePullPolicy: IfNotPresent
|
||||
args: ["nfs", "provisioner","--provisioner=rook.io/nfs-provisioner"]
|
||||
env:
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
`
|
||||
}
|
||||
|
||||
@@ -462,7 +375,7 @@ parameters:
|
||||
exportName: nfs-share
|
||||
nfsServerName: ` + namespace + `
|
||||
nfsServerNamespace: ` + namespace + `
|
||||
provisioner: rook.io/nfs-provisioner
|
||||
provisioner: nfs.rook.io/` + namespace + `-provisioner
|
||||
reclaimPolicy: Delete
|
||||
volumeBindingMode: Immediate
|
||||
---
|
||||
@@ -476,7 +389,7 @@ parameters:
|
||||
exportName: nfs-share1
|
||||
nfsServerName: ` + namespace + `
|
||||
nfsServerNamespace: ` + namespace + `
|
||||
provisioner: rook.io/nfs-provisioner
|
||||
provisioner: nfs.rook.io/` + namespace + `-provisioner
|
||||
reclaimPolicy: Delete
|
||||
volumeBindingMode: Immediate
|
||||
---
|
||||
@@ -515,6 +428,53 @@ metadata:
|
||||
name: rook-nfs-server
|
||||
namespace: ` + namespace + `
|
||||
---
|
||||
kind: ClusterRole
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: rook-nfs-provisioner-runner
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumes"]
|
||||
verbs: ["get", "list", "watch", "create", "delete"]
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumeclaims"]
|
||||
verbs: ["get", "list", "watch", "update"]
|
||||
- apiGroups: ["storage.k8s.io"]
|
||||
resources: ["storageclasses"]
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["events"]
|
||||
verbs: ["create", "update", "patch"]
|
||||
- apiGroups: [""]
|
||||
resources: ["services", "endpoints"]
|
||||
verbs: ["get"]
|
||||
- apiGroups: ["extensions"]
|
||||
resources: ["podsecuritypolicies"]
|
||||
resourceNames: ["nfs-provisioner"]
|
||||
verbs: ["use"]
|
||||
- apiGroups: [""]
|
||||
resources: ["endpoints"]
|
||||
verbs: ["get", "list", "watch", "create", "update", "patch"]
|
||||
- apiGroups:
|
||||
- nfs.rook.io
|
||||
resources:
|
||||
- "*"
|
||||
verbs:
|
||||
- "*"
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: run-nfs-provisioner
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: rook-nfs-server
|
||||
namespace: ` + namespace + `
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: rook-nfs-provisioner-runner
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
|
||||
@@ -68,7 +68,7 @@ func (suite *NfsSuite) TearDownSuite() {
|
||||
}
|
||||
|
||||
func (suite *NfsSuite) Setup() {
|
||||
suite.namespace = "nfs-ns"
|
||||
suite.namespace = "rook-nfs"
|
||||
suite.systemNamespace = installer.SystemNamespace(suite.namespace)
|
||||
suite.instanceCount = 1
|
||||
|
||||
|
||||
Reference in new issue
Block a user