Skip to content
This repository has been archived by the owner on Aug 14, 2021. It is now read-only.

Commit

Permalink
Add mountOptions StorageClass parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
wongma7 committed Apr 26, 2017
1 parent c4a2f44 commit 35cf999
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 17 deletions.
1 change: 1 addition & 0 deletions nfs/docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Edit the `provisioner` field in `deploy/kubernetes/class.yaml` to be the provisi

### Parameters
* `gid`: `"none"` or a [supplemental group](http://kubernetes.io/docs/user-guide/security-context/) like `"1001"`. NFS shares will be created with permissions such that only pods running with the supplemental group can read & write to the share. Or if `"none"`, anybody can write to the share. This will only work in conjunction with the `root-squash` flag set true. Default (if omitted) `"none"`.
* `mountOptions`: a comma separated list of [mount options](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#mount-options) for every PV of this class to be mounted with. The list is inserted directly into every PV's mount options annotation/field without any validation. Default blank `""`.

Name the `StorageClass` however you like; the name is how claims will request this class. Create the class.

Expand Down
32 changes: 22 additions & 10 deletions nfs/pkg/volume/provision.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ const (
// object that specifies a supplemental GID.
VolumeGidAnnotationKey = "pv.beta.kubernetes.io/gid"

// MountOptionAnnotation is the annotation on a PV object that specifies a
// comma separated list of mount options
MountOptionAnnotation = "volume.beta.kubernetes.io/mount-options"

// A PV annotation for the identity of the nfsProvisioner that provisioned it
annProvisionerID = "Provisioner_Id"

Expand Down Expand Up @@ -186,6 +190,9 @@ func (p *nfsProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis
if volume.supGroup != 0 {
annotations[VolumeGidAnnotationKey] = strconv.FormatUint(volume.supGroup, 10)
}
if volume.mountOptions != "" {
annotations[MountOptionAnnotation] = volume.mountOptions
}
annotations[annProvisionerID] = string(p.identity)

pv := &v1.PersistentVolume{
Expand Down Expand Up @@ -216,11 +223,12 @@ func (p *nfsProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis
type volume struct {
server string
path string
supGroup uint64
exportBlock string
exportID uint16
projectBlock string
projectID uint16
supGroup uint64
mountOptions string
}

// createVolume creates a volume i.e. the storage asset. It creates a unique
Expand All @@ -229,7 +237,7 @@ type volume struct {
// config or /etc/exports, and the exportID
// TODO return values
func (p *nfsProvisioner) createVolume(options controller.VolumeOptions) (volume, error) {
gid, err := p.validateOptions(options)
gid, mountOptions, err := p.validateOptions(options)
if err != nil {
return volume{}, fmt.Errorf("error validating options for volume: %v", err)
}
Expand Down Expand Up @@ -261,16 +269,18 @@ func (p *nfsProvisioner) createVolume(options controller.VolumeOptions) (volume,
return volume{
server: server,
path: path,
supGroup: 0,
exportBlock: exportBlock,
exportID: exportID,
projectBlock: projectBlock,
projectID: projectID,
supGroup: 0,
mountOptions: mountOptions,
}, nil
}

func (p *nfsProvisioner) validateOptions(options controller.VolumeOptions) (string, error) {
func (p *nfsProvisioner) validateOptions(options controller.VolumeOptions) (string, string, error) {
gid := "none"
mountOptions := ""
for k, v := range options.Parameters {
switch strings.ToLower(k) {
case "gid":
Expand All @@ -279,32 +289,34 @@ func (p *nfsProvisioner) validateOptions(options controller.VolumeOptions) (stri
} else if i, err := strconv.ParseUint(v, 10, 64); err == nil && i != 0 {
gid = v
} else {
return "", fmt.Errorf("invalid value for parameter gid: %v. valid values are: 'none' or a non-zero integer", v)
return "", "", fmt.Errorf("invalid value for parameter gid: %v. valid values are: 'none' or a non-zero integer", v)
}
case "mountoptions":
mountOptions = v
default:
return "", fmt.Errorf("invalid parameter: %q", k)
return "", "", fmt.Errorf("invalid parameter: %q", k)
}
}

// TODO implement options.ProvisionerSelector parsing
// pv.Labels MUST be set to match claim.spec.selector
// gid selector? with or without pv annotation?
if options.PVC.Spec.Selector != nil {
return "", fmt.Errorf("claim.Spec.Selector is not supported")
return "", "", fmt.Errorf("claim.Spec.Selector is not supported")
}

var stat syscall.Statfs_t
if err := syscall.Statfs(p.exportDir, &stat); err != nil {
return "", fmt.Errorf("error calling statfs on %v: %v", p.exportDir, err)
return "", "", fmt.Errorf("error calling statfs on %v: %v", p.exportDir, err)
}
capacity := options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)]
requestBytes := capacity.Value()
available := int64(stat.Bavail) * int64(stat.Bsize)
if requestBytes > available {
return "", fmt.Errorf("insufficient available space %v bytes to satisfy claim for %v bytes", available, requestBytes)
return "", "", fmt.Errorf("insufficient available space %v bytes to satisfy claim for %v bytes", available, requestBytes)
}

return gid, nil
return gid, mountOptions, nil
}

// getServer gets the server IP to put in a provisioned PV's spec.
Expand Down
23 changes: 16 additions & 7 deletions nfs/pkg/volume/provision_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,13 @@ func TestCreateVolume(t *testing.T) {
for _, test := range tests {
os.Setenv(test.envKey, "1.1.1.1")

server, path, supGroup, block, exportID, _, _, err := p.createVolume(test.options)
volume, err := p.createVolume(test.options)

evaluate(t, test.name, test.expectError, err, test.expectedServer, server, "server")
evaluate(t, test.name, test.expectError, err, test.expectedPath, path, "path")
evaluate(t, test.name, test.expectError, err, test.expectedGroup, supGroup, "group")
evaluate(t, test.name, test.expectError, err, test.expectedBlock, block, "block")
evaluate(t, test.name, test.expectError, err, test.expectedExportID, exportID, "export id")
evaluate(t, test.name, test.expectError, err, test.expectedServer, volume.server, "server")
evaluate(t, test.name, test.expectError, err, test.expectedPath, volume.path, "path")
evaluate(t, test.name, test.expectError, err, test.expectedGroup, volume.supGroup, "group")
evaluate(t, test.name, test.expectError, err, test.expectedBlock, volume.exportBlock, "block")
evaluate(t, test.name, test.expectError, err, test.expectedExportID, volume.exportID, "export id")

os.Unsetenv(test.envKey)
}
Expand Down Expand Up @@ -234,6 +234,15 @@ func TestValidateOptions(t *testing.T) {
expectedGid: "",
expectError: true,
},
{
name: "mount options parameter key",
options: controller.VolumeOptions{
Parameters: map[string]string{"mountOptions": "asdf"},
PVC: newClaim(resource.MustParse("1Ki"), nil, nil),
},
expectedGid: "none",
expectError: false,
},
// TODO implement options.ProvisionerSelector parsing
{
name: "non-nil selector",
Expand All @@ -257,7 +266,7 @@ func TestValidateOptions(t *testing.T) {
p := newNFSProvisionerInternal(tmpDir+"/", client, false, &testExporter{}, newDummyQuotaer(), "")

for _, test := range tests {
gid, err := p.validateOptions(test.options)
gid, _, err := p.validateOptions(test.options)

evaluate(t, test.name, test.expectError, err, test.expectedGid, gid, "gid")
}
Expand Down

0 comments on commit 35cf999

Please sign in to comment.