Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ var (
coreInformerResyncPeriod = flag.Duration("core-informer-resync-repriod", 15*time.Minute, "Core informer resync period.")

// Feature multishare backups enabled
featureMultishareBackups = flag.Bool("feature-multishare-backups", false, "if set to true, the multishare backups will be enabled. enable-multishare must be set to true as well")
featureMultishareBackups = flag.Bool("feature-multishare-backups", false, "if set to true, the multishare backups will be enabled. enable-multishare must be set to true as well")
featureNFSExportOptionsOnCreate = flag.Bool("feature-nfs-export-options", false, "if set to true, the driver will accpet nfs-export-options-on-create parameter and configure IP Access rules")

// Feature stateful CSI driver specific parameters
featureStateful = flag.Bool("feature-stateful-multishare", false, "if set to true, the controller will run stateful multishare controller, if set to true, enable-multishare must be set to true as well")
Expand Down Expand Up @@ -183,6 +184,9 @@ func main() {
FeatureMultishareBackups: &driver.FeatureMultishareBackups{
Enabled: *featureMultishareBackups,
},
FeatureNFSExportOptionsOnCreate: &driver.FeatureNFSExportOptionsOnCreate{
Enabled: *featureNFSExportOptionsOnCreate,
},
}

mounter := mount.New("")
Expand Down
44 changes: 12 additions & 32 deletions pkg/cloud_provider/file/fake.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,10 @@ func (manager *fakeServiceManager) CreateInstance(ctx context.Context, obj *Serv
Ip: "1.1.1.1",
ReservedIpRange: obj.Network.ReservedIpRange,
},
Labels: obj.Labels,
State: "READY",
Labels: obj.Labels,
State: "READY",
BackupSource: obj.BackupSource,
NfsExportOptions: obj.NfsExportOptions,
}

manager.createdInstances[obj.Name] = instance
Expand Down Expand Up @@ -204,29 +206,6 @@ func (manager *fakeServiceManager) GetBackup(ctx context.Context, backupUri stri
return backupInfo, nil
}

func (manager *fakeServiceManager) CreateInstanceFromBackupSource(ctx context.Context, obj *ServiceInstance, sourceSnapshotId string) (*ServiceInstance, error) {
instance := &ServiceInstance{
Project: defaultProject,
Location: defaultZone,
Name: obj.Name,
Tier: obj.Tier,
Volume: Volume{
Name: obj.Volume.Name,
SizeBytes: obj.Volume.SizeBytes,
},
Network: Network{
Name: obj.Network.Name,
Ip: "1.1.1.1",
ReservedIpRange: obj.Network.ReservedIpRange,
},
Labels: obj.Labels,
State: "READY",
}

manager.createdInstances[obj.Name] = instance
return instance, nil
}

func (m *fakeServiceManager) HasOperations(ctx context.Context, obj *ServiceInstance, operationType string, done bool) (bool, error) {
return false, nil
}
Expand Down Expand Up @@ -380,13 +359,14 @@ func (manager *fakeServiceManager) StartCreateShareOp(ctx context.Context, obj *
State: "READY",
}
share := &Share{
Name: obj.Name,
Parent: parent,
CapacityBytes: obj.CapacityBytes,
Labels: obj.Labels,
MountPointName: obj.Name,
BackupId: obj.BackupId,
State: "READY",
Name: obj.Name,
Parent: parent,
CapacityBytes: obj.CapacityBytes,
Labels: obj.Labels,
MountPointName: obj.Name,
BackupId: obj.BackupId,
State: "READY",
NfsExportOptions: obj.NfsExportOptions,
}
manager.createdMultishares[share.Name] = share

Expand Down
132 changes: 54 additions & 78 deletions pkg/cloud_provider/file/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,23 @@ type PollOpts struct {
Interval time.Duration
Timeout time.Duration
}
type NfsExportOptions struct {
AccessMode string `json:"accessMode,omitempty"`
AnonGid int64 `json:"anonGid,omitempty,string"`
AnonUid int64 `json:"anonUid,omitempty,string"`
IpRanges []string `json:"ipRanges,omitempty"`
SquashMode string `json:"squashMode,omitempty"`
}

type Share struct {
Name string // only the share name
Parent *MultishareInstance // parent captures the project, location details.
State string
MountPointName string
Labels map[string]string
CapacityBytes int64
BackupId string
Name string // only the share name
Parent *MultishareInstance // parent captures the project, location details.
State string
MountPointName string
Labels map[string]string
CapacityBytes int64
BackupId string
NfsExportOptions []*NfsExportOptions
}

type MultishareInstance struct {
Expand Down Expand Up @@ -88,15 +96,17 @@ type ListFilter struct {
}

type ServiceInstance struct {
Project string
Name string
Location string
Tier string
Network Network
Volume Volume
Labels map[string]string
State string
KmsKeyName string
Project string
Name string
Location string
Tier string
Network Network
Volume Volume
Labels map[string]string
State string
KmsKeyName string
BackupSource string
NfsExportOptions []*NfsExportOptions
}

type Volume struct {
Expand Down Expand Up @@ -154,7 +164,6 @@ type Service interface {
GetBackup(ctx context.Context, backupUri string) (*Backup, error)
CreateBackup(ctx context.Context, backupInfo *BackupInfo) (*filev1beta1.Backup, error)
DeleteBackup(ctx context.Context, backupId string) error
CreateInstanceFromBackupSource(ctx context.Context, obj *ServiceInstance, volumeSourceSnapshotId string) (*ServiceInstance, error)
HasOperations(ctx context.Context, obj *ServiceInstance, operationType string, done bool) (bool, error)
// Multishare ops
GetMultishareInstance(ctx context.Context, obj *MultishareInstance) (*MultishareInstance, error)
Expand Down Expand Up @@ -251,64 +260,14 @@ func NewGCFSService(version string, client *http.Client, primaryFilestoreService
}

func (manager *gcfsServiceManager) CreateInstance(ctx context.Context, obj *ServiceInstance) (*ServiceInstance, error) {
betaObj := &filev1beta1.Instance{
Tier: obj.Tier,
FileShares: []*filev1beta1.FileShareConfig{
{
Name: obj.Volume.Name,
CapacityGb: util.RoundBytesToGb(obj.Volume.SizeBytes),
},
},
Networks: []*filev1beta1.NetworkConfig{
{
Network: obj.Network.Name,
Modes: []string{"MODE_IPV4"},
ReservedIpRange: obj.Network.ReservedIpRange,
ConnectMode: obj.Network.ConnectMode,
},
},
KmsKeyName: obj.KmsKeyName,
Labels: obj.Labels,
}

klog.V(4).Infof("Creating instance %q: location %q, tier %q, capacity %v, network %q, ipRange %q, connectMode %q, KmsKeyName %q, labels %v",
obj.Name,
obj.Location,
betaObj.Tier,
betaObj.FileShares[0].CapacityGb,
betaObj.Networks[0].Network,
betaObj.Networks[0].ReservedIpRange,
betaObj.Networks[0].ConnectMode,
betaObj.KmsKeyName,
betaObj.Labels)
op, err := manager.instancesService.Create(locationURI(obj.Project, obj.Location), betaObj).InstanceId(obj.Name).Context(ctx).Do()
if err != nil {
klog.Errorf("CreateInstance operation failed for instance %s: %w", obj.Name, err)
return nil, err
}

klog.V(4).Infof("For instance %v, waiting for create instance op %v to complete", obj.Name, op.Name)
err = manager.waitForOp(ctx, op)
if err != nil {
klog.Errorf("WaitFor CreateInstance op %s failed: %w", op.Name, err)
return nil, err
}
instance, err := manager.GetInstance(ctx, obj)
if err != nil {
klog.Errorf("failed to get instance after creation: %w", err)
return nil, err
}
return instance, nil
}

func (manager *gcfsServiceManager) CreateInstanceFromBackupSource(ctx context.Context, obj *ServiceInstance, sourceSnapshotId string) (*ServiceInstance, error) {
instance := &filev1beta1.Instance{
Tier: obj.Tier,
FileShares: []*filev1beta1.FileShareConfig{
{
Name: obj.Volume.Name,
CapacityGb: util.RoundBytesToGb(obj.Volume.SizeBytes),
SourceBackup: sourceSnapshotId,
Name: obj.Volume.Name,
CapacityGb: util.RoundBytesToGb(obj.Volume.SizeBytes),
SourceBackup: obj.BackupSource,
NfsExportOptions: extractNfsShareExportOptions(obj.NfsExportOptions),
},
},
Networks: []*filev1beta1.NetworkConfig{
Expand Down Expand Up @@ -394,9 +353,10 @@ func cloudInstanceToServiceInstance(instance *filev1beta1.Instance) (*ServiceIns
ReservedIpRange: instance.Networks[0].ReservedIpRange,
ConnectMode: instance.Networks[0].ConnectMode,
},
KmsKeyName: instance.KmsKeyName,
Labels: instance.Labels,
State: instance.State,
KmsKeyName: instance.KmsKeyName,
Labels: instance.Labels,
State: instance.State,
BackupSource: instance.FileShares[0].SourceBackup,
}, nil
}

Expand Down Expand Up @@ -1006,10 +966,11 @@ func (manager *gcfsServiceManager) StartResizeMultishareInstanceOp(ctx context.C
func (manager *gcfsServiceManager) StartCreateShareOp(ctx context.Context, share *Share) (*filev1beta1multishare.Operation, error) {
instanceuri := instanceURI(share.Parent.Project, share.Parent.Location, share.Parent.Name)
targetshare := &filev1beta1multishare.Share{
CapacityGb: util.BytesToGb(share.CapacityBytes),
Labels: share.Labels,
MountName: share.MountPointName,
Backup: share.BackupId,
CapacityGb: util.BytesToGb(share.CapacityBytes),
Labels: share.Labels,
MountName: share.MountPointName,
Backup: share.BackupId,
NfsExportOptions: extractNfsShareExportOptions(share.NfsExportOptions),
}

op, err := manager.multishareInstancesSharesService.Create(instanceuri, targetshare).ShareId(share.Name).Context(ctx).Do()
Expand Down Expand Up @@ -1326,3 +1287,18 @@ func GenerateShareURI(s *Share) (string, error) {
func isMultishareVolId(volId string) bool {
return strings.Contains(volId, "modeMultishare")
}

func extractNfsShareExportOptions(options []*NfsExportOptions) []*filev1beta1multishare.NfsExportOptions {
var filerOpts []*filev1beta1multishare.NfsExportOptions
for _, opt := range options {
filerOpts = append(filerOpts,
&filev1beta1multishare.NfsExportOptions{
AccessMode: opt.AccessMode,
AnonGid: opt.AnonGid,
AnonUid: opt.AnonUid,
IpRanges: opt.IpRanges,
SquashMode: opt.SquashMode,
})
}
return filerOpts
}
50 changes: 38 additions & 12 deletions pkg/csi_driver/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ limitations under the License.
package driver

import (
"bytes"
"encoding/json"
"fmt"
"strings"
"time"
Expand Down Expand Up @@ -81,6 +83,7 @@ const (
paramMultishare = "multishare"
ParamInstanceEncryptionKmsKey = "instance-encryption-kms-key"
ParamMultishareInstanceScLabel = "instance-storageclass-label"
ParamNfsExportOptions = "nfs-export-options-on-create"
paramMaxVolumeSize = "max-volume-size"

// Keys for PV and PVC parameters as reported by external-provisioner
Expand Down Expand Up @@ -218,7 +221,6 @@ func (s *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolu
}
defer s.config.volumeLocks.Release(volumeID)

sourceSnapshotId := ""
if req.GetVolumeContentSource() != nil {
if req.GetVolumeContentSource().GetVolume() != nil {
return nil, status.Error(codes.InvalidArgument, "Unsupported volume content source")
Expand All @@ -235,7 +237,7 @@ func (s *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolu
klog.Errorf("Failed to get volume %v source snapshot %v: %v", name, id, err.Error())
return nil, file.StatusError(err)
}
sourceSnapshotId = id
newFiler.BackupSource = id
}
}

Expand Down Expand Up @@ -302,17 +304,13 @@ func (s *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolu

// Create the instance
var createErr error
if sourceSnapshotId != "" {
filer, createErr = s.config.fileService.CreateInstanceFromBackupSource(ctx, newFiler, sourceSnapshotId)
} else {
filer, createErr = s.config.fileService.CreateInstance(ctx, newFiler)
}
filer, createErr = s.config.fileService.CreateInstance(ctx, newFiler)
if createErr != nil {
klog.Errorf("Create volume for volume Id %s failed: %v", volumeID, createErr.Error())
return nil, file.StatusError(createErr)
}
}
resp := &csi.CreateVolumeResponse{Volume: s.fileInstanceToCSIVolume(filer, modeInstance, sourceSnapshotId)}
resp := &csi.CreateVolumeResponse{Volume: s.fileInstanceToCSIVolume(filer, modeInstance)}
klog.Infof("CreateVolume succeeded: %+v", resp)
return resp, nil
}
Expand Down Expand Up @@ -587,6 +585,7 @@ func (s *controllerServer) generateNewFileInstance(name string, capBytes int64,

// Set default parameters
tier := defaultTier
var nfsExportOptions []*file.NfsExportOptions
network := defaultNetwork
connectMode := directPeering
kmsKeyName := ""
Expand All @@ -604,6 +603,14 @@ func (s *controllerServer) generateNewFileInstance(name string, capBytes int64,
}
location = region
}
case ParamNfsExportOptions:
if s.config.features.FeatureNFSExportOptionsOnCreate == nil || !s.config.features.FeatureNFSExportOptionsOnCreate.Enabled {
return nil, fmt.Errorf("nfsExportOptions are disabled")
}
nfsExportOptions, err = parseNfsExportOptions(v)
if err != nil {
return nil, fmt.Errorf("failed to parse nfs-export-options-on-create %s: %v", v, err)
}
case paramNetwork:
network = v
case ParamConnectMode:
Expand Down Expand Up @@ -637,12 +644,13 @@ func (s *controllerServer) generateNewFileInstance(name string, capBytes int64,
Name: newInstanceVolume,
SizeBytes: capBytes,
},
KmsKeyName: kmsKeyName,
KmsKeyName: kmsKeyName,
NfsExportOptions: nfsExportOptions,
}, nil
}

// fileInstanceToCSIVolume generates a CSI volume spec from the cloud Instance
func (s *controllerServer) fileInstanceToCSIVolume(instance *file.ServiceInstance, mode, sourceSnapshotId string) *csi.Volume {
func (s *controllerServer) fileInstanceToCSIVolume(instance *file.ServiceInstance, mode string) *csi.Volume {
resp := &csi.Volume{
VolumeId: getVolumeIDFromFileInstance(instance, mode),
CapacityBytes: instance.Volume.SizeBytes,
Expand All @@ -651,11 +659,11 @@ func (s *controllerServer) fileInstanceToCSIVolume(instance *file.ServiceInstanc
attrVolume: instance.Volume.Name,
},
}
if sourceSnapshotId != "" {
if instance.BackupSource != "" {
contentSource := &csi.VolumeContentSource{
Type: &csi.VolumeContentSource_Snapshot{
Snapshot: &csi.VolumeContentSource_SnapshotSource{
SnapshotId: sourceSnapshotId,
SnapshotId: instance.BackupSource,
},
},
}
Expand Down Expand Up @@ -1015,3 +1023,21 @@ func (s *controllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteSn

return &csi.DeleteSnapshotResponse{}, nil
}

func parseNfsExportOptions(optionsString string) ([]*file.NfsExportOptions, error) {
if optionsString == "" {
return nil, nil
}
var parsedOptions []*file.NfsExportOptions
err := strictUnmarshal([]byte(optionsString), &parsedOptions)
if err != nil {
return nil, err
}
return parsedOptions, nil
}

func strictUnmarshal(data []byte, v interface{}) error {
dec := json.NewDecoder(bytes.NewReader(data))
dec.DisallowUnknownFields()
return dec.Decode(v)
}
Loading