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