Skip to content

Commit

Permalink
feat: support archiveOnDelete
Browse files Browse the repository at this point in the history
fix typo

fix golint
  • Loading branch information
andyzhangx committed Jan 15, 2024
1 parent d5dd4ac commit 202ed96
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 22 deletions.
2 changes: 2 additions & 0 deletions cmd/smbplugin/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ var (
volStatsCacheExpireInMinutes = flag.Int("vol-stats-cache-expire-in-minutes", 10, "The cache expire time in minutes for volume stats cache")
krb5CacheDirectory = flag.String("krb5-cache-directory", smb.DefaultKrb5CacheDirectory, "The directory for kerberos cache")
krb5Prefix = flag.String("krb5-prefix", smb.DefaultKrb5CCName, "The prefix for kerberos cache")
defaultOnDeletePolicy = flag.String("default-ondelete-policy", "", "default policy for deleting subdirectory when deleting a volume")
)

func main() {
Expand Down Expand Up @@ -78,6 +79,7 @@ func handle() {
VolStatsCacheExpireInMinutes: *volStatsCacheExpireInMinutes,
Krb5CacheDirectory: *krb5CacheDirectory,
Krb5Prefix: *krb5Prefix,
DefaultOnDeletePolicy: *defaultOnDeletePolicy,
}
driver := smb.NewDriver(&driverOptions)
driver.Run(*endpoint, *kubeconfig, false)
Expand Down
5 changes: 1 addition & 4 deletions docs/driver-parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Name | Meaning | Available Value | Mandatory | Default value
--- | --- | --- | --- | ---
source | Samba Server address | `//smb-server-address/sharename` </br>([Azure File](https://docs.microsoft.com/en-us/azure/storage/files/storage-files-introduction) format: `//accountname.file.core.windows.net/filesharename`) | Yes |
subDir | sub directory under smb share | | No | if sub directory does not exist, this driver would create a new one
onDelete | when volume is deleted, keep the directory if it's `retain` | `delete`(default), `retain`, `archive` | No | `delete`
csi.storage.k8s.io/provisioner-secret-name | secret name that stores `username`, `password`(`domain` is optional); if secret is provided, driver will create a sub directory with PV name under `source` | existing secret name | No |
csi.storage.k8s.io/provisioner-secret-namespace | namespace where the secret is | existing secret namespace | No |
csi.storage.k8s.io/node-stage-secret-name | secret name that stores `username`, `password`(`domain` is optional) | existing secret name | Yes |
Expand Down Expand Up @@ -35,10 +36,6 @@ kubectl create secret generic smbcreds --from-literal username=USERNAME --from-l
```

### Kerberos ticket support for Linux




#### These are the conditions that must be met:
- Kerberos support should be set up and cifs-utils must be installed on every node.
- The directory /var/lib/kubelet/kerberos/ needs to exist, and it will hold kerberos credential cache files for various users.
Expand Down
54 changes: 44 additions & 10 deletions pkg/smb/controllerserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ type smbVolume struct {
size int64
// pv name when subDir is not empty
uuid string
// on delete action
onDelete string
}

// Ordering of elements in the CSI volume id.
Expand All @@ -55,6 +57,7 @@ const (
idSource = iota
idSubDir
idUUID
idOnDelete
totalIDElements // Always last
)

Expand All @@ -74,7 +77,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
if parameters == nil {
parameters = make(map[string]string)
}
smbVol, err := newSMBVolume(name, reqCapacity, parameters)
smbVol, err := newSMBVolume(name, reqCapacity, parameters, d.defaultOnDeletePolicy)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
Expand Down Expand Up @@ -158,9 +161,13 @@ func (d *Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest)
}
}

if len(req.GetSecrets()) > 0 {
klog.V(2).Infof("begin to delete subdirectory since secret is provided")
// Mount smb base share so we can delete the subdirectory
if smbVol.onDelete == "" {
smbVol.onDelete = d.defaultOnDeletePolicy
}

if len(req.GetSecrets()) > 0 || !strings.EqualFold(smbVol.onDelete, retain) {
klog.V(2).Infof("begin to delete or archive subdirectory since secret is provided")
// mount smb base share so we can delete or archive the subdirectory
if err = d.internalMount(ctx, smbVol, volCap, secrets); err != nil {
return nil, status.Errorf(codes.Internal, "failed to mount smb server: %v", err.Error())
}
Expand All @@ -170,11 +177,20 @@ func (d *Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest)
}
}()

// Delete subdirectory under base-dir
internalVolumePath := getInternalVolumePath(d.workingMountDir, smbVol)
klog.V(2).Infof("Removing subdirectory at %v", internalVolumePath)
if err = os.RemoveAll(internalVolumePath); err != nil {
return nil, status.Errorf(codes.Internal, "failed to delete subdirectory: %v", err.Error())
if strings.EqualFold(smbVol.onDelete, archive) {
archivedInternalVolumePath := filepath.Join(getInternalMountPath(d.workingMountDir, smbVol), "archived-"+smbVol.subDir)

// archive subdirectory under base-dir
klog.V(2).Infof("archiving subdirectory %s --> %s", internalVolumePath, archivedInternalVolumePath)
if err = os.Rename(internalVolumePath, archivedInternalVolumePath); err != nil {
return nil, status.Errorf(codes.Internal, "archive subdirectory(%s, %s) failed with %v", internalVolumePath, archivedInternalVolumePath, err.Error())
}
} else {
klog.V(2).Infof("Removing subdirectory at %v", internalVolumePath)
if err = os.RemoveAll(internalVolumePath); err != nil {
return nil, status.Errorf(codes.Internal, "failed to delete subdirectory: %v", err.Error())
}
}
} else {
klog.V(2).Infof("DeleteVolume(%s) does not delete subdirectory", volumeID)
Expand Down Expand Up @@ -345,6 +361,9 @@ func getVolumeIDFromSmbVol(vol *smbVolume) string {
idElements[idSource] = strings.Trim(vol.source, "/")
idElements[idSubDir] = strings.Trim(vol.subDir, "/")
idElements[idUUID] = vol.uuid
if strings.EqualFold(vol.onDelete, retain) || strings.EqualFold(vol.onDelete, archive) {
idElements[idOnDelete] = vol.onDelete
}
return strings.Join(idElements, separator)
}

Expand All @@ -361,8 +380,8 @@ func getInternalMountPath(workingMountDir string, vol *smbVolume) string {
}

// Convert VolumeCreate parameters to an smbVolume
func newSMBVolume(name string, size int64, params map[string]string) (*smbVolume, error) {
var source, subDir string
func newSMBVolume(name string, size int64, params map[string]string, defaultOnDeletePolicy string) (*smbVolume, error) {
var source, subDir, onDelete string
subDirReplaceMap := map[string]string{}

// validate parameters (case-insensitive).
Expand All @@ -372,6 +391,8 @@ func newSMBVolume(name string, size int64, params map[string]string) (*smbVolume
source = v
case subDirField:
subDir = v
case paramOnDelete:
onDelete = v
case pvcNamespaceKey:
subDirReplaceMap[pvcNamespaceMetadata] = v
case pvcNameKey:
Expand Down Expand Up @@ -400,6 +421,16 @@ func newSMBVolume(name string, size int64, params map[string]string) (*smbVolume
// make volume id unique if subDir is provided
vol.uuid = name
}

if err := validateOnDeleteValue(onDelete); err != nil {
return nil, err
}

vol.onDelete = defaultOnDeletePolicy
if onDelete != "" {
vol.onDelete = onDelete
}

vol.id = getVolumeIDFromSmbVol(vol)
return vol, nil
}
Expand Down Expand Up @@ -447,6 +478,9 @@ func getSmbVolFromID(id string) (*smbVolume, error) {
if len(segments) >= 3 {
vol.uuid = segments[2]
}
if len(segments) >= 4 {
vol.onDelete = segments[3]
}
return vol, nil
}

Expand Down
46 changes: 38 additions & 8 deletions pkg/smb/controllerserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import (
const (
testServer = "test-server/baseDir"
testCSIVolume = "test-csi"
testVolumeID = "test-server/baseDir#test-csi#"
testVolumeID = "test-server/baseDir#test-csi##"
)

func TestControllerGetCapabilities(t *testing.T) {
Expand Down Expand Up @@ -448,6 +448,7 @@ func TestGetSmbVolFromID(t *testing.T) {
source string
subDir string
uuid string
onDelete string
expectErr bool
}{
{
Expand Down Expand Up @@ -486,6 +487,24 @@ func TestGetSmbVolFromID(t *testing.T) {
uuid: "pvc-4729891a-f57e-4982-9c60-e9884af1be2f",
expectErr: false,
},
{
desc: "valid request nested ondelete retain",
volumeID: "smb-server.default.svc.cluster.local/share#subdir#pvc-4729891a-f57e-4982-9c60-e9884af1be2f#retain",
source: "//smb-server.default.svc.cluster.local/share",
subDir: "subdir",
uuid: "pvc-4729891a-f57e-4982-9c60-e9884af1be2f",
onDelete: "retain",
expectErr: false,
},
{
desc: "valid request nested ondelete archive",
volumeID: "smb-server.default.svc.cluster.local/share#subdir#pvc-4729891a-f57e-4982-9c60-e9884af1be2f#archive",
source: "//smb-server.default.svc.cluster.local/share",
subDir: "subdir",
uuid: "pvc-4729891a-f57e-4982-9c60-e9884af1be2f",
onDelete: "archive",
expectErr: false,
},
{
desc: "incorrect volume id",
volumeID: "smb-server.default.svc.cluster.local/share",
Expand All @@ -503,6 +522,7 @@ func TestGetSmbVolFromID(t *testing.T) {
assert.Equal(t, smbVolume.source, test.source)
assert.Equal(t, smbVolume.subDir, test.subDir)
assert.Equal(t, smbVolume.uuid, test.uuid)
assert.Equal(t, smbVolume.onDelete, test.onDelete)
assert.Nil(t, err)
} else {
assert.NotNil(t, err)
Expand All @@ -523,7 +543,7 @@ func TestGetVolumeIDFromSmbVol(t *testing.T) {
source: "//smb-server.default.svc.cluster.local/share",
subDir: "subdir",
},
result: "smb-server.default.svc.cluster.local/share#subdir#",
result: "smb-server.default.svc.cluster.local/share#subdir##",
},
{
desc: "volume with uuid",
Expand All @@ -532,14 +552,24 @@ func TestGetVolumeIDFromSmbVol(t *testing.T) {
subDir: "subdir",
uuid: "uuid",
},
result: "smb-server.default.svc.cluster.local/share#subdir#uuid",
result: "smb-server.default.svc.cluster.local/share#subdir#uuid#",
},
{
desc: "volume without subdir",
vol: &smbVolume{
source: "//smb-server.default.svc.cluster.local/share",
},
result: "smb-server.default.svc.cluster.local/share##",
result: "smb-server.default.svc.cluster.local/share###",
},
{
desc: "volume with nested onDelete retain",
vol: &smbVolume{
source: "//smb-server.default.svc.cluster.local/share",
subDir: "subdir",
uuid: "uuid",
onDelete: "retain",
},
result: "smb-server.default.svc.cluster.local/share#subdir#uuid#retain",
},
}

Expand Down Expand Up @@ -605,7 +635,7 @@ func TestNewSMBVolume(t *testing.T) {
"subDir": "subdir",
},
expectVol: &smbVolume{
id: "smb-server.default.svc.cluster.local/share#subdir#pv-name",
id: "smb-server.default.svc.cluster.local/share#subdir#pv-name#",
source: "//smb-server.default.svc.cluster.local/share",
subDir: "subdir",
size: 100,
Expand All @@ -624,7 +654,7 @@ func TestNewSMBVolume(t *testing.T) {
pvNameKey: "pvname",
},
expectVol: &smbVolume{
id: "smb-server.default.svc.cluster.local/share#subdir-pvcname-pvcnamespace-pvname#pv-name",
id: "smb-server.default.svc.cluster.local/share#subdir-pvcname-pvcnamespace-pvname#pv-name#",
source: "//smb-server.default.svc.cluster.local/share",
subDir: "subdir-pvcname-pvcnamespace-pvname",
size: 100,
Expand All @@ -639,7 +669,7 @@ func TestNewSMBVolume(t *testing.T) {
"source": "//smb-server.default.svc.cluster.local/share",
},
expectVol: &smbVolume{
id: "smb-server.default.svc.cluster.local/share#pv-name#",
id: "smb-server.default.svc.cluster.local/share#pv-name##",
source: "//smb-server.default.svc.cluster.local/share",
subDir: "pv-name",
size: 200,
Expand All @@ -661,7 +691,7 @@ func TestNewSMBVolume(t *testing.T) {
}

for _, test := range cases {
vol, err := newSMBVolume(test.name, test.size, test.params)
vol, err := newSMBVolume(test.name, test.size, test.params, "")
if !reflect.DeepEqual(err, test.expectErr) {
t.Errorf("[test: %s] Unexpected error: %v, expected error: %v", test.desc, err, test.expectErr)
}
Expand Down
18 changes: 18 additions & 0 deletions pkg/smb/smb.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ limitations under the License.
package smb

import (
"fmt"
"strings"
"time"

Expand All @@ -39,6 +40,7 @@ const (
subDirField = "subdir"
domainField = "domain"
mountOptionsField = "mountoptions"
paramOnDelete = "ondelete"
defaultDomainName = "AZURE"
pvcNameKey = "csi.storage.k8s.io/pvc/name"
pvcNamespaceKey = "csi.storage.k8s.io/pvc/namespace"
Expand All @@ -48,8 +50,12 @@ const (
pvNameMetadata = "${pv.metadata.name}"
DefaultKrb5CCName = "krb5cc_"
DefaultKrb5CacheDirectory = "/var/lib/kubelet/kerberos/"
retain = "retain"
archive = "archive"
)

var supportedOnDeleteValues = []string{"", "delete", retain, archive}

// DriverOptions defines driver parameters specified in driver deployment
type DriverOptions struct {
NodeID string
Expand All @@ -61,6 +67,7 @@ type DriverOptions struct {
VolStatsCacheExpireInMinutes int
Krb5CacheDirectory string
Krb5Prefix string
DefaultOnDeletePolicy string
}

// Driver implements all interfaces of CSI drivers
Expand All @@ -78,6 +85,7 @@ type Driver struct {
removeSMBMappingDuringUnmount bool
krb5CacheDirectory string
krb5Prefix string
defaultOnDeletePolicy string
}

// NewDriver Creates a NewCSIDriver object. Assumes vendor version is equal to driver version &
Expand Down Expand Up @@ -208,3 +216,13 @@ func replaceWithMap(str string, m map[string]string) string {
}
return str
}

func validateOnDeleteValue(onDelete string) error {
for _, v := range supportedOnDeleteValues {
if strings.EqualFold(v, onDelete) {
return nil
}
}

return fmt.Errorf("invalid value %s for OnDelete, supported values are %v", onDelete, supportedOnDeleteValues)
}
Loading

0 comments on commit 202ed96

Please sign in to comment.