Skip to content
Snippets Groups Projects
Commit 1f742322 authored by Andrew Melnick's avatar Andrew Melnick Committed by Derek Su
Browse files

Begin work on support for multiple storage classes

parent 9dbcda68
No related branches found
No related tags found
No related merge requests found
......@@ -14,6 +14,9 @@ data:
{{- with .Values.sharedFileSystemPath }}
{{- $config = set $config "sharedFileSystemPath" . }}
{{- end }}
{{- with .Values.multipleStorageClasses }}
{{- $config = set $config "sharedFileSystemPath" . }}
{{- end }}
{{- $config | toPrettyJson | nindent 4 }}
setup: |-
{{- .Values.configmap.setup | nindent 4 }}
......
{{ if .Values.storageClass.create -}}
{{- $storageClasses := list }}
{{ if .Values.multipleStorageClasses }}
{{ $storageClasses = .Values.multipleStorageClasses }}
{{- else }}
{{ $storageClasses = list .Values }}
{{ end }}
{{- $dot := . }}
{{- range $values := $storageClasses }}
{{ if $values.storageClass.create -}}
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: {{ .Values.storageClass.name }}
name: {{ $values.storageClass.name }}
labels:
{{ include "local-path-provisioner.labels" . | indent 4 }}
{{ include "local-path-provisioner.labels" $dot | indent 4 }}
annotations:
storageclass.kubernetes.io/is-default-class: "{{ .Values.storageClass.defaultClass }}"
defaultVolumeType: "{{ .Values.storageClass.defaultVolumeType }}"
provisioner: {{ template "local-path-provisioner.provisionerName" . }}
volumeBindingMode: {{ .Values.storageClass.volumeBindingMode }}
reclaimPolicy: {{ .Values.storageClass.reclaimPolicy }}
storageclass.kubernetes.io/is-default-class: "{{ $values.storageClass.defaultClass }}"
defaultVolumeType: "{{ $values.storageClass.defaultVolumeType }}"
provisioner: {{ template "local-path-provisioner.provisionerName" $dot }}
volumeBindingMode: {{ $values.storageClass.volumeBindingMode }}
reclaimPolicy: {{ $values.storageClass.reclaimPolicy }}
allowVolumeExpansion: true
{{- end }}
---
{{- end }}
......@@ -73,6 +73,21 @@ nodePathMap:
# If `sharedFileSystemPath` is used, then `nodePathMap` must be set to `[]`.
# sharedFileSystemPath: ""
# `multipleStorageClasses` allows the provisioner to manage multiple independent storage classes.
# Each storage class must have a unique name, and contains the same fields as shown above for
# a single storage class setup, EXCEPT for the provisionerName, which is the same for all
# storage classes
# multipleStorageClasses:
# - storageClass:
# name: my-storage-class-name
# create: true
# defaultClass: false
# reclaimPolicy: Delete
# sharedFileSystemPath: ""
# ## OR
# # See above
# nodePathMap: {}
podAnnotations: {}
podSecurityContext: {}
......
......@@ -81,12 +81,22 @@ type NodePathMapData struct {
Paths []string `json:"paths,omitempty"`
}
type ConfigData struct {
type StorageClassConfigData struct {
NodePathMap []*NodePathMapData `json:"nodePathMap,omitempty"`
CmdTimeoutSeconds int `json:"cmdTimeoutSeconds,omitempty"`
SharedFileSystemPath string `json:"sharedFileSystemPath,omitempty"`
}
type ConfigData struct {
CmdTimeoutSeconds int `json:"cmdTimeoutSeconds,omitempty"`
SetupCommand string `json:"setupCommand,omitempty"`
TeardownCommand string `json:"teardownCommand,omitempty"`
StorageClassConfigData
StorageClassConfigs map[string]StorageClassConfigData `json:"storageClassConfigs"`
}
type StorageClassConfig struct {
NodePathMap map[string]*NodePathMap
SharedFileSystemPath string
}
type NodePathMap struct {
......@@ -94,11 +104,11 @@ type NodePathMap struct {
}
type Config struct {
NodePathMap map[string]*NodePathMap
CmdTimeoutSeconds int
SharedFileSystemPath string
SetupCommand string
TeardownCommand string
StorageClassConfig
StorageClassConfigs map[string]StorageClassConfig
}
func NewProvisioner(ctx context.Context, kubeClient *clientset.Clientset,
......@@ -177,7 +187,7 @@ func (p *LocalPathProvisioner) watchAndRefreshConfig() {
}()
}
func (p *LocalPathProvisioner) getPathOnNode(node string, requestedPath string) (string, error) {
func (p *LocalPathProvisioner) getPathOnNode(node string, requestedPath string, c *StorageClassConfig) (string, error) {
p.configMutex.RLock()
defer p.configMutex.RUnlock()
......@@ -185,8 +195,7 @@ func (p *LocalPathProvisioner) getPathOnNode(node string, requestedPath string)
return "", fmt.Errorf("no valid config available")
}
c := p.config
sharedFS, err := p.isSharedFilesystem()
sharedFS, err := p.isSharedFilesystem(c)
if err != nil {
return "", err
}
......@@ -222,7 +231,7 @@ func (p *LocalPathProvisioner) getPathOnNode(node string, requestedPath string)
return path, nil
}
func (p *LocalPathProvisioner) isSharedFilesystem() (bool, error) {
func (p *LocalPathProvisioner) isSharedFilesystem(c *StorageClassConfig) (bool, error) {
p.configMutex.RLock()
defer p.configMutex.RUnlock()
......@@ -230,7 +239,6 @@ func (p *LocalPathProvisioner) isSharedFilesystem() (bool, error) {
return false, fmt.Errorf("no valid config available")
}
c := p.config
if (c.SharedFileSystemPath != "") && (len(c.NodePathMap) != 0) {
return false, fmt.Errorf("both nodePathMap and sharedFileSystemPath are defined. Please make sure only one is in use")
}
......@@ -246,11 +254,30 @@ func (p *LocalPathProvisioner) isSharedFilesystem() (bool, error) {
return false, fmt.Errorf("both nodePathMap and sharedFileSystemPath are unconfigured")
}
func (p *LocalPathProvisioner) pickConfig(storageClassName string) (*StorageClassConfig, error) {
if len(p.config.StorageClassConfigs) == 0 {
return &p.config.StorageClassConfig, nil
}
cfg, ok := p.config.StorageClassConfigs[storageClassName]
if !ok {
return nil, fmt.Errorf("BUG: Got request for unexpected storage class %s", storageClassName)
}
return &cfg, nil
}
func (p *LocalPathProvisioner) Provision(ctx context.Context, opts pvController.ProvisionOptions) (*v1.PersistentVolume, pvController.ProvisioningState, error) {
cfg, err := p.pickConfig(opts.StorageClass.Name)
if err != nil {
return nil, pvController.ProvisioningFinished, err
}
return p.provisionFor(opts, cfg)
}
func (p *LocalPathProvisioner) provisionFor(opts pvController.ProvisionOptions, c *StorageClassConfig) (*v1.PersistentVolume, pvController.ProvisioningState, error) {
pvc := opts.PVC
node := opts.SelectedNode
storageClass := opts.StorageClass
sharedFS, err := p.isSharedFilesystem()
sharedFS, err := p.isSharedFilesystem(c)
if err != nil {
return nil, pvController.ProvisioningFinished, err
}
......@@ -279,7 +306,7 @@ func (p *LocalPathProvisioner) Provision(ctx context.Context, opts pvController.
requestedPath = storageClass.Parameters["nodePath"]
}
}
basePath, err := p.getPathOnNode(nodeName, requestedPath)
basePath, err := p.getPathOnNode(nodeName, requestedPath, c)
if err != nil {
return nil, pvController.ProvisioningFinished, err
}
......@@ -307,7 +334,7 @@ func (p *LocalPathProvisioner) Provision(ctx context.Context, opts pvController.
Mode: *pvc.Spec.VolumeMode,
SizeInBytes: storage.Value(),
Node: nodeName,
}); err != nil {
}, c); err != nil {
return nil, pvController.ProvisioningFinished, err
}
......@@ -375,10 +402,19 @@ func (p *LocalPathProvisioner) Provision(ctx context.Context, opts pvController.
}
func (p *LocalPathProvisioner) Delete(ctx context.Context, pv *v1.PersistentVolume) (err error) {
cfg, err := p.pickConfig(pv.Spec.StorageClassName)
if err != nil {
return err
}
return p.deleteFor(pv, cfg)
}
func (p *LocalPathProvisioner) deleteFor(pv *v1.PersistentVolume, c *StorageClassConfig) (err error) {
defer func() {
err = errors.Wrapf(err, "failed to delete volume %v", pv.Name)
}()
path, node, err := p.getPathAndNodeForPV(pv)
path, node, err := p.getPathAndNodeForPV(pv, c)
if err != nil {
return err
}
......@@ -401,7 +437,7 @@ func (p *LocalPathProvisioner) Delete(ctx context.Context, pv *v1.PersistentVolu
Mode: *pv.Spec.VolumeMode,
SizeInBytes: storage.Value(),
Node: node,
}); err != nil {
}, c); err != nil {
logrus.Infof("clean up volume %v failed: %v", pv.Name, err)
return err
}
......@@ -411,7 +447,7 @@ func (p *LocalPathProvisioner) Delete(ctx context.Context, pv *v1.PersistentVolu
return nil
}
func (p *LocalPathProvisioner) getPathAndNodeForPV(pv *v1.PersistentVolume) (path, node string, err error) {
func (p *LocalPathProvisioner) getPathAndNodeForPV(pv *v1.PersistentVolume, cfg *StorageClassConfig) (path, node string, err error) {
defer func() {
err = errors.Wrapf(err, "failed to delete volume %v", pv.Name)
}()
......@@ -425,7 +461,7 @@ func (p *LocalPathProvisioner) getPathAndNodeForPV(pv *v1.PersistentVolume) (pat
return "", "", fmt.Errorf("no path set")
}
sharedFS, err := p.isSharedFilesystem()
sharedFS, err := p.isSharedFilesystem(cfg)
if err != nil {
return "", "", err
}
......@@ -475,11 +511,11 @@ type volumeOptions struct {
Node string
}
func (p *LocalPathProvisioner) createHelperPod(action ActionType, cmd []string, o volumeOptions) (err error) {
func (p *LocalPathProvisioner) createHelperPod(action ActionType, cmd []string, o volumeOptions, cfg *StorageClassConfig) (err error) {
defer func() {
err = errors.Wrapf(err, "failed to %v volume %v", action, o.Name)
}()
sharedFS, err := p.isSharedFilesystem()
sharedFS, err := p.isSharedFilesystem(cfg)
if err != nil {
return err
}
......@@ -667,13 +703,39 @@ func loadConfigFile(configFile string) (cfgData *ConfigData, err error) {
}
func canonicalizeConfig(data *ConfigData) (cfg *Config, err error) {
defer func() {
err = errors.Wrapf(err, "config canonicalization failed")
}()
cfg = &Config{}
cfg.SharedFileSystemPath = data.SharedFileSystemPath
if len(data.StorageClassConfigs) == 0 {
defaultConfig, err := canonicalizeStorageClassConfig(&data.StorageClassConfigData)
if err != nil {
return nil, err
}
cfg.StorageClassConfig = *defaultConfig
} else {
cfg.StorageClassConfigs = make(map[string]StorageClassConfig, len(data.StorageClassConfigs))
for name, classData := range data.StorageClassConfigs {
classCfg, err := canonicalizeStorageClassConfig(&classData)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("config for class %s is invalid", name))
}
cfg.StorageClassConfigs[name] = *classCfg
}
}
cfg.SetupCommand = data.SetupCommand
cfg.TeardownCommand = data.TeardownCommand
if data.CmdTimeoutSeconds > 0 {
cfg.CmdTimeoutSeconds = data.CmdTimeoutSeconds
} else {
cfg.CmdTimeoutSeconds = defaultCmdTimeoutSeconds
}
return cfg, nil
}
func canonicalizeStorageClassConfig(data *StorageClassConfigData) (cfg *StorageClassConfig, err error) {
defer func() {
err = errors.Wrapf(err, "StorageClass config canonicalization failed")
}()
cfg = &StorageClassConfig{}
cfg.SharedFileSystemPath = data.SharedFileSystemPath
cfg.NodePathMap = map[string]*NodePathMap{}
for _, n := range data.NodePathMap {
if cfg.NodePathMap[n.Node] != nil {
......@@ -698,11 +760,6 @@ func canonicalizeConfig(data *ConfigData) (cfg *Config, err error) {
npMap.Paths[path] = struct{}{}
}
}
if data.CmdTimeoutSeconds > 0 {
cfg.CmdTimeoutSeconds = data.CmdTimeoutSeconds
} else {
cfg.CmdTimeoutSeconds = defaultCmdTimeoutSeconds
}
return cfg, nil
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment