-
Notifications
You must be signed in to change notification settings - Fork 769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Alias mode to export a share to other namespaces #47
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,17 +36,20 @@ import ( | |
"k8s.io/client-go/rest" | ||
"k8s.io/client-go/tools/clientcmd" | ||
"k8s.io/kubernetes/pkg/apis/core/v1/helper" | ||
mnt "k8s.io/mount-utils" | ||
"sigs.k8s.io/sig-storage-lib-external-provisioner/v6/controller" | ||
) | ||
|
||
const ( | ||
provisionerNameKey = "PROVISIONER_NAME" | ||
magicAliasHostname = "--alias" | ||
) | ||
|
||
type nfsProvisioner struct { | ||
client kubernetes.Interface | ||
server string | ||
path string | ||
alias bool | ||
} | ||
|
||
type pvcMetadata struct { | ||
|
@@ -79,40 +82,17 @@ const ( | |
var _ controller.Provisioner = &nfsProvisioner{} | ||
|
||
func (p *nfsProvisioner) Provision(ctx context.Context, options controller.ProvisionOptions) (*v1.PersistentVolume, controller.ProvisioningState, error) { | ||
var path string | ||
var err error | ||
|
||
if options.PVC.Spec.Selector != nil { | ||
return nil, controller.ProvisioningFinished, 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}, "-") | ||
|
||
metadata := &pvcMetadata{ | ||
data: map[string]string{ | ||
"name": pvcName, | ||
"namespace": pvcNamespace, | ||
}, | ||
labels: options.PVC.Labels, | ||
annotations: options.PVC.Annotations, | ||
} | ||
|
||
fullPath := filepath.Join(mountPath, pvName) | ||
path := filepath.Join(p.path, pvName) | ||
|
||
pathPattern, exists := options.StorageClass.Parameters["pathPattern"] | ||
if exists { | ||
customPath := metadata.stringParser(pathPattern) | ||
path = filepath.Join(p.path, customPath) | ||
fullPath = filepath.Join(mountPath, customPath) | ||
} | ||
|
||
glog.V(4).Infof("creating path %s", fullPath) | ||
if err := os.MkdirAll(fullPath, 0777); err != nil { | ||
return nil, controller.ProvisioningFinished, errors.New("unable to create directory to provision new pv: " + err.Error()) | ||
if path, err = p.getOrMakeDir(options); err != nil { | ||
return nil, controller.ProvisioningFinished, err | ||
} | ||
os.Chmod(fullPath, 0777) | ||
|
||
pv := &v1.PersistentVolume{ | ||
ObjectMeta: metav1.ObjectMeta{ | ||
|
@@ -129,14 +109,51 @@ func (p *nfsProvisioner) Provision(ctx context.Context, options controller.Provi | |
NFS: &v1.NFSVolumeSource{ | ||
Server: p.server, | ||
Path: path, | ||
ReadOnly: false, | ||
ReadOnly: false, // Pass ReadOnly through if in alias mode? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. afaik it have no real effect, you can try to set it to true and it will be writable anyway. |
||
}, | ||
}, | ||
}, | ||
} | ||
return pv, controller.ProvisioningFinished, nil | ||
} | ||
|
||
// If in alias mode, forward the server:path details from the PVC we mounted. | ||
// If not, create a new directory. | ||
func (p *nfsProvisioner) getOrMakeDir(options controller.ProvisionOptions) (string, error) { | ||
if p.alias { | ||
return p.path, nil | ||
} | ||
|
||
pvcNamespace := options.PVC.Namespace | ||
pvcName := options.PVC.Name | ||
|
||
pvName := strings.Join([]string{pvcNamespace, pvcName, options.PVName}, "-") | ||
fullPath := filepath.Join(mountPath, pvName) | ||
path := filepath.Join(p.path, pvName) | ||
|
||
pathPattern, exists := options.StorageClass.Parameters["pathPattern"] | ||
if exists { | ||
metadata := &pvcMetadata{ | ||
data: map[string]string{ | ||
"name": pvcName, | ||
"namespace": pvcNamespace, | ||
}, | ||
labels: options.PVC.Labels, | ||
annotations: options.PVC.Annotations, | ||
} | ||
customPath := metadata.stringParser(pathPattern) | ||
path = filepath.Join(p.path, customPath) | ||
fullPath = filepath.Join(mountPath, customPath) | ||
} | ||
|
||
glog.V(4).Infof("creating path %s", fullPath) | ||
if err := os.MkdirAll(fullPath, 0777); err == nil { | ||
return "", errors.New("unable to create directory to provision new pv: " + err.Error()) | ||
} | ||
os.Chmod(fullPath, 0777) | ||
return path, nil | ||
} | ||
|
||
func (p *nfsProvisioner) Delete(ctx context.Context, volume *v1.PersistentVolume) error { | ||
path := volume.Spec.PersistentVolumeSource.NFS.Path | ||
relativePath := strings.Replace(path, p.path, "", 1) | ||
|
@@ -200,6 +217,17 @@ func (p *nfsProvisioner) getClassForVolume(ctx context.Context, pv *v1.Persisten | |
return class, nil | ||
} | ||
|
||
// Return the server and path parts for the given NFS mount | ||
func getDetailsForMountPoint(m string) (server, path string, err error) { | ||
if path, _, err = mnt.GetDeviceNameFromMount(mnt.New(""), m); err == nil { | ||
if parts := strings.Split(path, ":"); len(parts) == 2 { | ||
return parts[0], parts[1], err | ||
} | ||
err = errors.New("Can't parse server:path from device string: " + path) | ||
} | ||
return | ||
} | ||
|
||
func main() { | ||
flag.Parse() | ||
flag.Set("logtostderr", "true") | ||
|
@@ -256,10 +284,24 @@ func main() { | |
} | ||
} | ||
|
||
// If $NFS_SERVER=="--alias", just pass through the server/path that is | ||
// mounted in the provisioner's pod under /persistentvolumes and never | ||
// make a new directory for each volume we are asked to provision. | ||
var alias bool | ||
if server == magicAliasHostname { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. imo its not very intuitive way and what do you think about another environment variable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was something I cooked up quickly last year as a proof of concept. I agree that supporting both modes with a class parameter is better, but if we use that to reference a volume claim rather than a server:path pair, resolving that reference might involve another round trip to the API server with potential failure modes. I'll rework the code and try both kinds of class parameters. |
||
// Figure just once and store the server/path pair | ||
if server, path, err = getDetailsForMountPoint(mountPath); err != nil { | ||
glog.Fatalf("Error getting server details for %v: %v", mountPath, err) | ||
} | ||
glog.Infof("Aliasing all new volumes to %v::%v", server, path) | ||
alias = true | ||
} | ||
|
||
clientNFSProvisioner := &nfsProvisioner{ | ||
client: clientset, | ||
server: server, | ||
path: path, | ||
alias: alias, | ||
} | ||
// Start the provision controller which will dynamically provision efs NFS | ||
// PVs | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the
fuseim.pri/ifs
soon will be changed tok8s-sigs.io/nfs-subdir-external-provisioner
(#37)