Skip to content
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

feat: support archiveOnDelete when DeleteVolume #722

Merged
merged 1 commit into from
Jan 15, 2024
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
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
Loading