From ca62da0e0de1a6f58a2a893a3ecd621fe4513475 Mon Sep 17 00:00:00 2001
From: Sheng Yang <sheng@yasker.org>
Date: Wed, 26 Sep 2018 16:34:38 -0700
Subject: [PATCH] Provisioner works with DynamicProvisioningScheduling on
 Kubernetes v1.11

---
 main.go        | 130 +++++++++++++++++++++++++++++++++++++++++++++++++
 provisioner.go |  86 ++++++++++++++++++++++++++++++++
 2 files changed, 216 insertions(+)
 create mode 100644 main.go
 create mode 100644 provisioner.go

diff --git a/main.go b/main.go
new file mode 100644
index 0000000..68d9206
--- /dev/null
+++ b/main.go
@@ -0,0 +1,130 @@
+package main
+
+import (
+	"fmt"
+	"os"
+	"os/signal"
+	"syscall"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/pkg/errors"
+	"github.com/urfave/cli"
+
+	pvController "github.com/kubernetes-incubator/external-storage/lib/controller"
+	clientset "k8s.io/client-go/kubernetes"
+	"k8s.io/client-go/rest"
+)
+
+var (
+	VERSION = "0.0.1"
+
+	DefaultProvisionerName = "rancher.io/local-path"
+
+	FlagProvisionerName = "provisioner-name"
+	EnvProvisionerName  = "PROVISIONER_NAME"
+)
+
+func cmdNotFound(c *cli.Context, command string) {
+	panic(fmt.Errorf("Unrecognized command: %s", command))
+}
+
+func onUsageError(c *cli.Context, err error, isSubcommand bool) error {
+	panic(fmt.Errorf("Usage error, please check your command"))
+}
+
+func RegisterShutdownChannel(done chan struct{}) {
+	sigs := make(chan os.Signal, 1)
+	signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
+	go func() {
+		sig := <-sigs
+		logrus.Infof("Receive %v to exit", sig)
+		close(done)
+	}()
+}
+
+func StartCmd() cli.Command {
+	return cli.Command{
+		Name: "start",
+		Flags: []cli.Flag{
+			cli.StringFlag{
+				Name:   FlagProvisionerName,
+				Usage:  "Specify provisioner name",
+				EnvVar: EnvProvisionerName,
+				Value:  DefaultProvisionerName,
+			},
+		},
+		Action: func(c *cli.Context) {
+			if err := startDaemon(c); err != nil {
+				logrus.Fatalf("Error starting daemon: %v", err)
+			}
+		},
+	}
+}
+
+func startDaemon(c *cli.Context) error {
+	stopCh := make(chan struct{})
+	RegisterShutdownChannel(stopCh)
+
+	config, err := rest.InClusterConfig()
+	if err != nil {
+		return errors.Wrap(err, "unable to get client config")
+	}
+
+	kubeClient, err := clientset.NewForConfig(config)
+	if err != nil {
+		return errors.Wrap(err, "unable to get k8s client")
+	}
+
+	serverVersion, err := kubeClient.Discovery().ServerVersion()
+	if err != nil {
+		return errors.Wrap(err, "Cannot start Provisioner: failed to get Kubernetes server version")
+	}
+
+	provisionerName := c.String(FlagProvisionerName)
+	if provisionerName == "" {
+		return fmt.Errorf("invalid empty provisioner name")
+	}
+	provisioner := NewProvisioner()
+	pc := pvController.NewProvisionController(
+		kubeClient,
+		provisionerName,
+		provisioner,
+		serverVersion.GitVersion,
+	)
+	logrus.Debug("Provisioner started")
+	pc.Run(stopCh)
+	logrus.Debug("Provisioner stopped")
+	return nil
+}
+
+func main() {
+	logrus.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
+
+	a := cli.NewApp()
+	a.Version = VERSION
+	a.Usage = "Local Path Provisioner"
+
+	a.Before = func(c *cli.Context) error {
+		if c.GlobalBool("debug") {
+			logrus.SetLevel(logrus.DebugLevel)
+		}
+		return nil
+	}
+
+	a.Flags = []cli.Flag{
+		cli.BoolFlag{
+			Name:   "debug, d",
+			Usage:  "enable debug logging level",
+			EnvVar: "RANCHER_DEBUG",
+		},
+	}
+	a.Commands = []cli.Command{
+		StartCmd(),
+	}
+	a.CommandNotFound = cmdNotFound
+	a.OnUsageError = onUsageError
+
+	if err := a.Run(os.Args); err != nil {
+		logrus.Fatalf("Critical error: %v", err)
+	}
+}
diff --git a/provisioner.go b/provisioner.go
new file mode 100644
index 0000000..28e6779
--- /dev/null
+++ b/provisioner.go
@@ -0,0 +1,86 @@
+package main
+
+import (
+	"fmt"
+	"path/filepath"
+
+	"github.com/Sirupsen/logrus"
+
+	pvController "github.com/kubernetes-incubator/external-storage/lib/controller"
+	"k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+var (
+	DefaultPath = "/opt"
+)
+
+type LocalPathProvisioner struct {
+}
+
+func NewProvisioner() *LocalPathProvisioner {
+	return &LocalPathProvisioner{}
+}
+
+func (p *LocalPathProvisioner) Provision(opts pvController.VolumeOptions) (*v1.PersistentVolume, error) {
+	pvc := opts.PVC
+	if pvc.Spec.Selector != nil {
+		return nil, fmt.Errorf("claim.Spec.Selector is not supported")
+	}
+	for _, accessMode := range pvc.Spec.AccessModes {
+		if accessMode != v1.ReadWriteOnce {
+			return nil, fmt.Errorf("Only support ReadWriteOnce access mode")
+		}
+	}
+	node := opts.SelectedNode
+	if opts.SelectedNode == nil {
+		return nil, fmt.Errorf("configuration error, no node was specified")
+	}
+	name := opts.PVName
+	path := filepath.Join(DefaultPath, name)
+
+	logrus.Infof("Created volume %v", name)
+	fs := v1.PersistentVolumeFilesystem
+	hostPathType := v1.HostPathDirectoryOrCreate
+
+	return &v1.PersistentVolume{
+		ObjectMeta: metav1.ObjectMeta{
+			Name: name,
+		},
+		Spec: v1.PersistentVolumeSpec{
+			PersistentVolumeReclaimPolicy: opts.PersistentVolumeReclaimPolicy,
+			AccessModes:                   pvc.Spec.AccessModes,
+			VolumeMode:                    &fs,
+			Capacity: v1.ResourceList{
+				v1.ResourceName(v1.ResourceStorage): pvc.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)],
+			},
+			PersistentVolumeSource: v1.PersistentVolumeSource{
+				HostPath: &v1.HostPathVolumeSource{
+					Path: path,
+					Type: &hostPathType,
+				},
+			},
+			NodeAffinity: &v1.VolumeNodeAffinity{
+				Required: &v1.NodeSelector{
+					NodeSelectorTerms: []v1.NodeSelectorTerm{
+						{
+							MatchExpressions: []v1.NodeSelectorRequirement{
+								{
+									Key:      "kubernetes.io/hostname",
+									Operator: v1.NodeSelectorOpIn,
+									Values: []string{
+										node.Name,
+									},
+								},
+							},
+						},
+					},
+				},
+			},
+		},
+	}, nil
+}
+
+func (p *LocalPathProvisioner) Delete(pv *v1.PersistentVolume) error {
+	return nil
+}
-- 
GitLab