Skip to content

Instantly share code, notes, and snippets.

@liangrog
Last active May 9, 2018 22:12
Show Gist options
  • Save liangrog/3210939df1c3ea3dd0a9df1eb97fee32 to your computer and use it in GitHub Desktop.
Save liangrog/3210939df1c3ea3dd0a9df1eb97fee32 to your computer and use it in GitHub Desktop.
pseudo codes for implementing external-storage provisioner interface used in Medium article
package main
import (
"flag"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"github.com/kubernetes-incubator/external-storage/lib/controller"
)
// Main func
func main() {
flag.Parse()
flag.Set("logtostderr", "true")
// 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)
}
provisioner := NewAwesomeProvisioner(clientset, "awesome-no-1")
// Start the provision controller which will dynamically
// provision awesome volume PVs
pc := controller.NewProvisionController(
clientset,
"storage-class-name",
provisioner,
serverVersion.GitVersion,
)
pc.Run(wait.NeverStop)
}
// awesomeProvisioner
package main
import (
"errors"
"github.com/golang/glog"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
"github.com/kubernetes-incubator/external-storage/lib/controller"
"github.com/kubernetes-incubator/external-storage/lib/util"
)
// Provisioner struct
type awesomeProvisioner struct {
// kubernetes api client
client kubernetes.Interface
// Provisioner identity so you can
// run multiple provisioners. Each
// is responsible for separate sets of PVs
id string
}
// Constructor
func NewAwesomeProvisioner(c kubernetes.Interface, id string) controller.Provisioner {
return &awesomeProvisioner{client: c, id: id}
}
// Calling other APIs to provision a persistent volume
// and return the volume ID
func createAwesomeVolume(size string) string {
/* Do your magic here */
}
// Calling other APIs to delete persistent
// volume by given id
func deleteAwesomeVolume(id string) error {
/* Do your magic here */
}
// Implementing external-storage controller required interface
func (p *awesomeProvisioner) Provision(options controller.VolumeOptions) (*v1.PersistentVolume, error) {
// Get volume size requirement from PVC
volumeSize := util.RoundUpSize(options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)].Value(), util.GiB)
// Provisioning the physical volume
volumeId := createAwesomeVolume(util.RoundUpSize(volumeSize.Value(), util.GiB))
// This is the drive your created in "Part 1"
driver := "vendor/driver"
// You can change to any file type as long as
// your drive can deal with it
fsType := "xfs"
// PV spec, similar to a PV manifest
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: options.PVName,
Annotations: map[string]string{
"awesomeProvisionerIdentity": p.id,
},
},
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{
FlexVolume: &v1.FlexPersistentVolumeSource{
Driver: driver,
FSType: fsType,
// Provide the name of the secret
// if you are using one
SecretRef: &v1.SecretReference{
Name: os.Getenv("secret-name"),
Namespace: options.PVC.Namespace,
},
ReadOnly: false,
Options: map[string]string{"volumeId": volumeId},
},
},
},
}
return pv, nil
}
// Implementing external-storage controller required interface
func (p *awesomeProvisioner) Delete(volume *v1.PersistentVolume) error {
// Check if requested PV should be processed by this provisioner
ann, ok := volume.Annotations["awesomeProvisionerIdentity"]
if !ok {
return errors.New("identity annotation not found on PV")
}
if ann != p.id {
return &controller.IgnoredError{"identity annotation on PV does not match"}
}
// Get volumeId from PV that created by this provisioner
volumeId := volume.Spec.PersistentVolumeSource.FlexVolume.Options["volumeId"]
glog.Infof("Received request to delete awesome volume (ID: %s)\n", volumeId)
// delete the physical volume
err := DeleteAwesomeVolume(volumeId)
if err == nil {
glog.Infof("Successfully deleted awesome volume (ID: %s)\n", volumeId)
}
return err
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment