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) }