Configurable mode and ownership via PVC annotations and provider wide defaults
This commit is contained in:
		
							parent
							
								
									59a3ca2cd1
								
							
						
					
					
						commit
						0f7a89aab9
					
				
							
								
								
									
										12
									
								
								README.md
								
								
								
								
							
							
						
						
									
										12
									
								
								README.md
								
								
								
								
							|  | @ -293,6 +293,18 @@ spec: | |||
|       storage: 1Mi | ||||
| ``` | ||||
| 
 | ||||
| **Step 8: Controlling the permissions and ownership of subdirs** | ||||
| 
 | ||||
| By default new directories will be created with `root:root` ownership, and `0777` permissions in most environments. If you have a need to control this, you can do so by providing the `NFS_DEFAULT_MODE`, `NFS_DEFAULT_UID` and `NFS_DEFAULT_GID` environment variables (or the appropriate configuration in the Helm chart values). The mode must be an octal representation of a file mode, for example `777`, `0755` etc. The uid and gid must be the numeric ids of your desired user and group, so `1000` not `my_user`.  | ||||
| 
 | ||||
| If your usecase requires per-PVC ownership and/or mode, this can be done via annotations on your PVC: | ||||
| 
 | ||||
| - `k8s-sigs.io/nfs-directory-mode` | ||||
| - `k8s-sigs.io/nfs-directory-uid` | ||||
| - `k8s-sigs.io/nfs-directory-gid` | ||||
| 
 | ||||
| The order of precedence is PVC annotations, ENV vars, then root:root 0777 if nothing else has been specified.  | ||||
| 
 | ||||
| # Build and publish your own container image | ||||
| 
 | ||||
| To build your own custom container image from this repository, you will have to build and push the nfs-subdir-external-provisioner image using the following instructions. | ||||
|  |  | |||
|  | @ -57,6 +57,12 @@ spec: | |||
|               value: {{ .Values.nfs.server }} | ||||
|             - name: NFS_PATH | ||||
|               value: {{ .Values.nfs.path }} | ||||
|             - name: NFS_DEFAULT_MODE | ||||
|               value: {{ .Values.nfs.defaultMode}} | ||||
|             - name: NFS_DEFAULT_UID | ||||
|               value: {{ .Values.nfs.defaultUid }} | ||||
|             - name: NFS_DEFAULT_GID | ||||
|               value: {{ .Values.nfs.defaultGid }} | ||||
|             {{- if eq .Values.leaderElection.enabled false }} | ||||
|             - name: ENABLE_LEADER_ELECTION | ||||
|               value: "false" | ||||
|  |  | |||
|  | @ -12,6 +12,9 @@ nfs: | |||
|   path: /nfs-storage | ||||
|   mountOptions: | ||||
|   volumeName: nfs-subdir-external-provisioner-root | ||||
|   defaultMode: "777" | ||||
|   defaultUid: "0" | ||||
|   defaultGid: "0" | ||||
|   # Reclaim policy for the main nfs volume | ||||
|   reclaimPolicy: Retain | ||||
| 
 | ||||
|  |  | |||
|  | @ -47,6 +47,9 @@ type nfsProvisioner struct { | |||
| 	client      kubernetes.Interface | ||||
| 	server      string | ||||
| 	path        string | ||||
| 	defaultMode os.FileMode | ||||
| 	defaultUid  int | ||||
| 	defaultGid  int | ||||
| } | ||||
| 
 | ||||
| type pvcMetadata struct { | ||||
|  | @ -75,6 +78,7 @@ func (meta *pvcMetadata) stringParser(str string) string { | |||
| 
 | ||||
| const ( | ||||
| 	mountPath        = "/persistentvolumes" | ||||
| 	annotationPrefix = "k8s-sigs.io" | ||||
| ) | ||||
| 
 | ||||
| var _ controller.Provisioner = &nfsProvisioner{} | ||||
|  | @ -111,15 +115,54 @@ func (p *nfsProvisioner) Provision(ctx context.Context, options controller.Provi | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if the PVC has an annotation requesting a specific mode. Fallback to defaults if not.
 | ||||
| 	mode := p.defaultMode | ||||
| 	pvcMode := metadata.annotations[annotationPrefix+"/nfs-directory-mode"] | ||||
| 	if pvcMode != "" { | ||||
| 		var err error | ||||
| 		mode, err = getModeFromString(pvcMode) | ||||
| 		if err != nil { | ||||
| 			return nil, controller.ProvisioningFinished, fmt.Errorf("invalid directoryMode %s: %v", pvcMode, err) | ||||
| 		} | ||||
| 	} | ||||
| 	glog.V(4).Infof("creating path %s", fullPath) | ||||
| 	if err := os.MkdirAll(fullPath, 0o777); err != nil { | ||||
| 	if err := os.MkdirAll(fullPath, mode); err != nil { | ||||
| 		return nil, controller.ProvisioningFinished, errors.New("unable to create directory to provision new pv: " + err.Error()) | ||||
| 	} | ||||
| 	err := os.Chmod(fullPath, 0o777) | ||||
| 	err := os.Chmod(fullPath, mode) | ||||
| 	if err != nil { | ||||
| 		return nil, "", err | ||||
| 	} | ||||
| 
 | ||||
| 	// Check if the PVC has an annotation requesting a specific UID and GID. Again, fallback to defaults if not.
 | ||||
| 	uid := p.defaultUid | ||||
| 	pvcUid := metadata.annotations[annotationPrefix+"/nfs-directory-uid"] | ||||
| 	if pvcUid != "" { | ||||
| 		var err error | ||||
| 		uid, err = getIdFromString(pvcUid) | ||||
| 		if err != nil { | ||||
| 			// No real point in returning an error here as the dir will have already been created as root:root
 | ||||
| 			// log the error and continue with the default uid
 | ||||
| 			glog.Errorf("invalid directoryUid %s: %v", pvcUid, err) | ||||
| 			uid = p.defaultUid | ||||
| 		} | ||||
| 	} | ||||
| 	gid := p.defaultGid | ||||
| 	pvcGid := metadata.annotations[annotationPrefix+"/nfs-directory-gid"] | ||||
| 	if pvcGid != "" { | ||||
| 		var err error | ||||
| 		gid, err = getIdFromString(pvcGid) | ||||
| 		if err != nil { | ||||
| 			// No real point in returning an error here as the dir will have already been created as root:root
 | ||||
| 			// log the error and continue with the default gid
 | ||||
| 			glog.Errorf("invalid directoryGid %s: %v", pvcGid, err) | ||||
| 			gid = p.defaultGid | ||||
| 		} | ||||
| 	} | ||||
| 	err = os.Chown(fullPath, uid, gid) | ||||
| 	if err != nil { | ||||
| 		return nil, "", err | ||||
| 	} | ||||
| 	pv := &v1.PersistentVolume{ | ||||
| 		ObjectMeta: metav1.ObjectMeta{ | ||||
| 			Name: options.PVName, | ||||
|  | @ -205,6 +248,36 @@ func (p *nfsProvisioner) getClassForVolume(ctx context.Context, pv *v1.Persisten | |||
| 	return class, nil | ||||
| } | ||||
| 
 | ||||
| func getModeFromString(mode string) (os.FileMode, error) { | ||||
| 	if mode == "" { | ||||
| 		return os.FileMode(0o777), nil // Default to 0777, per current behavior
 | ||||
| 	} | ||||
| 	var modeInt int64 | ||||
| 	var err error | ||||
| 	modeInt, err = strconv.ParseInt(mode, 8, 64) | ||||
| 	if err != nil { | ||||
| 		return 0, fmt.Errorf("invalid mode %s: %v", mode, err) | ||||
| 	} | ||||
| 	if modeInt < 0 || modeInt > 0o777 { | ||||
| 		return 0, fmt.Errorf("mode must be between 0 and 0777, got %s", mode) | ||||
| 	} | ||||
| 	return os.FileMode(modeInt), nil | ||||
| } | ||||
| 
 | ||||
| func getIdFromString(id string) (int, error) { | ||||
| 	if id == "" { | ||||
| 		return 0, nil // Default to 0 aka root, per current behavior
 | ||||
| 	} | ||||
| 	idInt, err := strconv.Atoi(id) | ||||
| 	if err != nil { | ||||
| 		return 0, fmt.Errorf("invalid id %s: %v", id, err) | ||||
| 	} | ||||
| 	if idInt < 0 || idInt > 65535 { | ||||
| 		return 0, fmt.Errorf("id must be between 0 and 65535, got %s", id) | ||||
| 	} | ||||
| 	return idInt, nil | ||||
| } | ||||
| 
 | ||||
| func main() { | ||||
| 	flag.Parse() | ||||
| 	flag.Set("logtostderr", "true") | ||||
|  | @ -221,6 +294,19 @@ func main() { | |||
| 	if provisionerName == "" { | ||||
| 		glog.Fatalf("environment variable %s is not set! Please set it.", provisionerNameKey) | ||||
| 	} | ||||
| 	// Get the default mode, uid, and gid from environment variables
 | ||||
| 	mode, err := getModeFromString(os.Getenv("NFS_DEFAULT_MODE")) | ||||
| 	if err != nil { | ||||
| 		glog.Fatalf("Failed to parse NFS_DEFAULT_MODE: %v", err) | ||||
| 	} | ||||
| 	uid, err := getIdFromString(os.Getenv("NFS_DEFAULT_UID")) | ||||
| 	if err != nil { | ||||
| 		glog.Fatalf("Failed to parse NFS_DEFAULT_UID: %v", err) | ||||
| 	} | ||||
| 	gid, err := getIdFromString(os.Getenv("NFS_DEFAULT_GID")) | ||||
| 	if err != nil { | ||||
| 		glog.Fatalf("Failed to parse NFS_DEFAULT_GID: %v", err) | ||||
| 	} | ||||
| 	kubeconfig := os.Getenv("KUBECONFIG") | ||||
| 	var config *rest.Config | ||||
| 	if kubeconfig != "" { | ||||
|  | @ -265,6 +351,9 @@ func main() { | |||
| 		client:      clientset, | ||||
| 		server:      server, | ||||
| 		path:        path, | ||||
| 		defaultMode: mode, | ||||
| 		defaultUid:  uid, | ||||
| 		defaultGid:  gid, | ||||
| 	} | ||||
| 	// Start the provision controller which will dynamically provision efs NFS
 | ||||
| 	// PVs
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue