commit 5a51799e23260d3af3517fe993de5e387752edef Author: Jackie Li Date: Wed Apr 26 21:12:08 2017 +0100 nfs-client-provisioner fixes #5 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f66bbe54 --- /dev/null +++ b/.gitignore @@ -0,0 +1,26 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof + +nfs-client-provisioner diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..c11fce8d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,4 @@ +FROM alpine:3.5 +RUN apk update --no-cache && apk add ca-certificates +COPY nfs-client-provisioner /nfs-client-provisioner +ENTRYPOINT ["/nfs-client-provisioner"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..ecb1c1c1 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# kubernetes nfs-client-provisioner +- pv provisioned as ${namespace}-${pvcName}-${pvName} +- pv recycled as archieved-${namespace}-${pvcName}-${pvName} + +# deploy +- modify and deploy `deploy/deployment.yaml` +- modify and deploy `deploy/class.yaml` + +# test +- `kubectl create -f deploy/test-claim.yaml` +- `kubectl create -f deploy/test-pod.yaml` +- check the folder and file "SUCCESS" created +- `kubectl delete -f deploy/test-pod.yaml` +- `kubectl delete -f deploy/test-claim.yaml` +- check the folder renamed diff --git a/build.sh b/build.sh new file mode 100755 index 00000000..3f33f69c --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#!/bin/sh +CGO_ENABLED=0 go build ./cmd/nfs-client-provisioner #&& docker build -t quay.io/jackieli/nfs-client-provisioner . + diff --git a/cmd/nfs-client-provisioner/provisioner.go b/cmd/nfs-client-provisioner/provisioner.go new file mode 100644 index 00000000..ded8fae8 --- /dev/null +++ b/cmd/nfs-client-provisioner/provisioner.go @@ -0,0 +1,129 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/golang/glog" + "github.com/kubernetes-incubator/external-storage/lib/controller" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/pkg/api/v1" + "k8s.io/client-go/rest" +) + +const ( + provisionerNameKey = "PROVISIONER_NAME" +) + +type nfsProvisioner struct { + client kubernetes.Interface + server string + path string +} + +const ( + mountPath = "/persistentvolumes" +) + +var _ controller.Provisioner = &nfsProvisioner{} + +func (p *nfsProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) { + if options.PVC.Spec.Selector != nil { + return nil, fmt.Errorf("claim Selector is not supported") + } + glog.V(4).Infof("nfs provisioner: VolumeOptions %v", options) + + pvcNamespace := options.PVC.Namespace + pvcName := options.PVC.Name + + pvName := strings.Join([]string{pvcNamespace, pvcName, options.PVName}, "-") + + fullPath := filepath.Join(mountPath, pvName) + glog.V(4).Infof("creating path %s", fullPath) + if err := os.MkdirAll(fullPath, 0777); err != nil { + return nil, errors.New("unable to create directory to provision new pv: " + err.Error()) + } + + path := filepath.Join(p.path, pvName) + + pv := &v1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Name: pvName, + }, + Spec: v1.PersistentVolumeSpec{ + PersistentVolumeReclaimPolicy: options.PersistentVolumeReclaimPolicy, + AccessModes: options.PVC.Spec.AccessModes, + Capacity: v1.ResourceList{ + v1.ResourceName(v1.ResourceStorage): options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)], + }, + PersistentVolumeSource: v1.PersistentVolumeSource{ + NFS: &v1.NFSVolumeSource{ + Server: p.server, + Path: path, + ReadOnly: false, + }, + }, + }, + } + return pv, nil +} + +func (p *nfsProvisioner) Delete(volume *v1.PersistentVolume) error { + path := volume.Spec.PersistentVolumeSource.NFS.Path + pvName := filepath.Base(path) + oldPath := filepath.Join(mountPath, pvName) + archivePath := filepath.Join(mountPath, "archieved-"+pvName) + glog.V(4).Infof("archiving path %s to %s", oldPath, archivePath) + return os.Rename(oldPath, archivePath) +} + +func main() { + flag.Parse() + flag.Set("logtostderr", "true") + + server := os.Getenv("NFS_SERVER") + if server == "" { + glog.Fatal("NFS_SERVER not set") + } + path := os.Getenv("NFS_PATH") + if path == "" { + glog.Fatal("NFS_PATH not set") + } + provisionerName := os.Getenv(provisionerNameKey) + if provisionerName == "" { + glog.Fatalf("environment variable %s is not set! Please set it.", provisionerNameKey) + } + + // Create an InClusterConfig and use it to create a client for the controller + // to use to communicate with Kubernetes + config, err := rest.InClusterConfig() + if err != nil { + glog.Fatalf("Failed to create config: %v", err) + } + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + glog.Fatalf("Failed to create client: %v", err) + } + + // The controller needs to know what the server version is because out-of-tree + // provisioners aren't officially supported until 1.5 + serverVersion, err := clientset.Discovery().ServerVersion() + if err != nil { + glog.Fatalf("Error getting server version: %v", err) + } + + clientNFSProvisioner := &nfsProvisioner{ + server: server, + path: path, + } + // Start the provision controller which will dynamically provision efs NFS + // PVs + pc := controller.NewProvisionController(clientset, provisionerName, clientNFSProvisioner, serverVersion.GitVersion) + pc.Run(wait.NeverStop) +} diff --git a/deploy/class.yaml b/deploy/class.yaml new file mode 100644 index 00000000..8e09cc1a --- /dev/null +++ b/deploy/class.yaml @@ -0,0 +1,5 @@ +apiVersion: storage.k8s.io/v1beta1 +kind: StorageClass +metadata: + name: managed-nfs-storage +provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME' diff --git a/deploy/deployment.yaml b/deploy/deployment.yaml new file mode 100644 index 00000000..6595f33d --- /dev/null +++ b/deploy/deployment.yaml @@ -0,0 +1,31 @@ +kind: Deployment +apiVersion: extensions/v1beta1 +metadata: + name: nfs-client-provisioner +spec: + replicas: 1 + strategy: + type: Recreate + template: + metadata: + labels: + app: nfs-client-provisioner + spec: + containers: + - name: nfs-client-provisioner + image: quay.io/jackieli/nfs-client-provisioner:v1 + volumeMounts: + - name: nfs-client-root + mountPath: /persistentvolumes + env: + - name: PROVISIONER_NAME + value: fuseim.pri/ifs + - name: NFS_SERVER + value: 10.10.10.60 + - name: NFS_PATH + value: /ifs/kubernetes + volumes: + - name: nfs-client-root + nfs: + server: 10.10.10.60 + path: /ifs/kubernetes \ No newline at end of file diff --git a/deploy/test-claim.yaml b/deploy/test-claim.yaml new file mode 100644 index 00000000..4382e200 --- /dev/null +++ b/deploy/test-claim.yaml @@ -0,0 +1,12 @@ +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: test-claim + annotations: + volume.beta.kubernetes.io/storage-class: "managed-nfs-storage" +spec: + accessModes: + - ReadWriteMany + resources: + requests: + storage: 1Mi \ No newline at end of file diff --git a/deploy/test-pod.yaml b/deploy/test-pod.yaml new file mode 100644 index 00000000..c58da791 --- /dev/null +++ b/deploy/test-pod.yaml @@ -0,0 +1,21 @@ +kind: Pod +apiVersion: v1 +metadata: + name: test-pod +spec: + containers: + - name: test-pod + image: gcr.io/google_containers/busybox:1.24 + command: + - "/bin/sh" + args: + - "-c" + - "touch /mnt/SUCCESS && exit 0 || exit 1" + volumeMounts: + - name: nfs-pvc + mountPath: "/mnt" + restartPolicy: "Never" + volumes: + - name: nfs-pvc + persistentVolumeClaim: + claimName: test-claim \ No newline at end of file