Skip to content

Commit e8dc75e

Browse files
committed
feat: support NFS
feat: add Dockerfile change feat: add nodeserver change to support NFS fix test failure fix: rename fsType to protocol
1 parent 504e867 commit e8dc75e

File tree

8 files changed

+114
-17
lines changed

8 files changed

+114
-17
lines changed
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
apiVersion: storage.k8s.io/v1
3+
kind: StorageClass
4+
metadata:
5+
name: blob
6+
provisioner: blobfuse.csi.azure.com
7+
parameters:
8+
storageAccount: EXISTING_STORAGE_ACCOUNT_NAME
9+
protocol: nfs
10+
volumeBindingMode: Immediate

pkg/blobfuse/blobfuse.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ const (
4545
defaultVers = "3.0"
4646
serverNameField = "server"
4747
tagsField = "tags"
48+
protocolField = "protocol"
49+
fuse = "fuse"
50+
nfs = "nfs"
4851

4952
// See https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata#container-names
5053
containerNameMinLength = 3
@@ -58,7 +61,8 @@ const (
5861
)
5962

6063
var (
61-
retriableErrors = []string{accountNotProvisioned, tooManyRequests, shareNotFound, shareBeingDeleted, clientThrottled}
64+
supportedProtocolList = []string{fuse, nfs}
65+
retriableErrors = []string{accountNotProvisioned, tooManyRequests, shareNotFound, shareBeingDeleted, clientThrottled}
6266
)
6367

6468
// Driver implements all interfaces of CSI drivers
@@ -417,3 +421,15 @@ func isRetriableError(err error) bool {
417421
}
418422
return false
419423
}
424+
425+
func isSupportedProtocol(protocol string) bool {
426+
if protocol == "" {
427+
return true
428+
}
429+
for _, v := range supportedProtocolList {
430+
if protocol == v {
431+
return true
432+
}
433+
}
434+
return false
435+
}

pkg/blobfuse/blobfuse_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,3 +314,34 @@ func TestIsCorruptedDir(t *testing.T) {
314314
assert.Equal(t, test.expectedResult, isCorruptedDir, "TestCase[%d]: %s", i, test.desc)
315315
}
316316
}
317+
318+
func TestIsSupportedProtocol(t *testing.T) {
319+
tests := []struct {
320+
protocol string
321+
expectedResult bool
322+
}{
323+
{
324+
protocol: "",
325+
expectedResult: true,
326+
},
327+
{
328+
protocol: "fuse",
329+
expectedResult: true,
330+
},
331+
{
332+
protocol: "nfs",
333+
expectedResult: true,
334+
},
335+
{
336+
protocol: "invalid",
337+
expectedResult: false,
338+
},
339+
}
340+
341+
for _, test := range tests {
342+
result := isSupportedProtocol(test.protocol)
343+
if result != test.expectedResult {
344+
t.Errorf("isSupportedProtocol(%s) returned with %v, not equal to %v", test.protocol, result, test.expectedResult)
345+
}
346+
}
347+
}

pkg/blobfuse/controllerserver.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
5555
requestGiB := int(util.RoundUpGiB(volSizeBytes))
5656

5757
parameters := req.GetParameters()
58-
var storageAccountType, resourceGroup, location, account, containerName, customTags string
58+
var storageAccountType, resourceGroup, location, account, containerName, protocol, customTags string
5959

6060
// Apply ProvisionerParameters (case-insensitive). We leave validation of
6161
// the values to the cloud provider.
@@ -73,6 +73,8 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
7373
resourceGroup = v
7474
case "containername":
7575
containerName = v
76+
case protocolField:
77+
protocol = v
7678
case tagsField:
7779
customTags = v
7880
}
@@ -82,6 +84,16 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
8284
resourceGroup = d.cloud.ResourceGroup
8385
}
8486

87+
if !isSupportedProtocol(protocol) {
88+
return nil, status.Errorf(codes.InvalidArgument, "protocol(%s) is not supported, supported protocol list: %v", protocol, supportedProtocolList)
89+
}
90+
91+
if protocol == nfs {
92+
if account == "" {
93+
return nil, status.Errorf(codes.InvalidArgument, "storage account must be specified when provisioning nfs file share")
94+
}
95+
}
96+
8597
accountKind := string(storage.StorageV2)
8698
if strings.HasPrefix(strings.ToLower(storageAccountType), "premium") {
8799
accountKind = string(storage.BlockBlobStorage)

pkg/blobfuse/controllerserver_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func TestCreateVolume(t *testing.T) {
135135
}
136136
mockStorageAccountsClient.EXPECT().ListByResourceGroup(gomock.Any(), gomock.Any()).Return(nil, rerr).AnyTimes()
137137
_, err := d.CreateVolume(context.Background(), req)
138-
expectedErr := fmt.Errorf("could not get storage key for storage account : could not list storage accounts for account type : Retriable: false, RetryAfter: 0s, HTTPStatusCode: 0, RawError: test")
138+
expectedErr := status.Errorf(codes.Internal, "failed to ensure storage account: could not list storage accounts for account type : Retriable: false, RetryAfter: 0s, HTTPStatusCode: 0, RawError: test")
139139
if !reflect.DeepEqual(err, expectedErr) {
140140
t.Errorf("actualErr: (%v), expectedErr: (%v)", err, expectedErr)
141141
}

pkg/blobfuse/nodeserver.go

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ import (
2222
"os"
2323
"os/exec"
2424
"strings"
25+
"time"
2526

2627
volumehelper "sigs.k8s.io/blobfuse-csi-driver/pkg/util"
2728

2829
"github.com/container-storage-interface/spec/lib/go/csi"
2930

31+
"k8s.io/apimachinery/pkg/util/wait"
3032
"k8s.io/klog/v2"
3133
"k8s.io/kubernetes/pkg/volume/util"
3234
"k8s.io/utils/mount"
@@ -119,7 +121,6 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
119121
}
120122

121123
volumeID := req.GetVolumeId()
122-
fsType := req.GetVolumeCapability().GetMount().GetFsType()
123124
mountFlags := req.GetVolumeCapability().GetMount().GetMountFlags()
124125
attrib := req.GetVolumeContext()
125126
secrets := req.GetSecrets()
@@ -129,29 +130,53 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
129130
return nil, err
130131
}
131132

132-
// Get mountOptions that the volume will be formatted and mounted with
133-
options := []string{"--use-https=true"}
134-
mountOptions := util.JoinMountOptions(mountFlags, options)
135-
136-
args := targetPath + " " + "--tmp-path=/mnt/" + volumeID + " " + "--container-name=" + containerName
137-
for _, opt := range mountOptions {
138-
args = args + " " + opt
139-
}
140-
141-
var blobStorageEndPoint string
133+
var blobStorageEndPoint, protocol string
142134
for k, v := range attrib {
143135
switch strings.ToLower(k) {
144136
case serverNameField:
145137
blobStorageEndPoint = v
138+
case protocolField:
139+
protocol = v
146140
}
147141
}
148142
if strings.TrimSpace(blobStorageEndPoint) == "" {
149143
// server address is "accountname.blob.core.windows.net" by default
150144
blobStorageEndPoint = fmt.Sprintf("%s.blob.%s", accountName, d.cloud.Environment.StorageEndpointSuffix)
151145
}
152146

153-
klog.V(2).Infof("target %v\nfstype %v\n\nvolumeId %v\ncontext %v\nmountflags %v\nmountOptions %v\nargs %v\nblobStorageEndPoint %v",
154-
targetPath, fsType, volumeID, attrib, mountFlags, mountOptions, args, blobStorageEndPoint)
147+
if protocol == nfs {
148+
klog.V(2).Infof("target %v\nprotocol %v\n\nvolumeId %v\ncontext %v\nmountflags %v\nblobStorageEndPoint %v",
149+
targetPath, protocol, volumeID, attrib, mountFlags, blobStorageEndPoint)
150+
151+
source := fmt.Sprintf("%s:/%s/%s", blobStorageEndPoint, accountName, containerName)
152+
mountOptions := util.JoinMountOptions(mountFlags, []string{"sec=sys,vers=3,nolock"})
153+
mountComplete := false
154+
err := wait.PollImmediate(1*time.Second, 2*time.Minute, func() (bool, error) {
155+
err := d.mounter.MountSensitive(source, targetPath, nfs, mountOptions, []string{})
156+
mountComplete = true
157+
return true, err
158+
})
159+
if !mountComplete {
160+
return nil, status.Error(codes.Internal, fmt.Sprintf("volume(%s) mount %q on %q failed with timeout(2m)", volumeID, source, targetPath))
161+
}
162+
if err != nil {
163+
return nil, status.Error(codes.Internal, fmt.Sprintf("volume(%s) mount %q on %q failed with %v", volumeID, source, targetPath, err))
164+
}
165+
klog.V(2).Infof("volume(%s) mount %q on %q succeeded", volumeID, source, targetPath)
166+
167+
return &csi.NodeStageVolumeResponse{}, nil
168+
}
169+
170+
// Get mountOptions that the volume will be formatted and mounted with
171+
mountOptions := util.JoinMountOptions(mountFlags, []string{"--use-https=true"})
172+
173+
args := targetPath + " " + "--tmp-path=/mnt/" + volumeID + " " + "--container-name=" + containerName
174+
for _, opt := range mountOptions {
175+
args = args + " " + opt
176+
}
177+
178+
klog.V(2).Infof("target %v\nprotocol %v\n\nvolumeId %v\ncontext %v\nmountflags %v\nmountOptions %v\nargs %v\nblobStorageEndPoint %v",
179+
targetPath, protocol, volumeID, attrib, mountFlags, mountOptions, args, blobStorageEndPoint)
155180
cmd := exec.Command("blobfuse", strings.Split(args, " ")...)
156181

157182
cmd.Env = append(os.Environ(), "AZURE_STORAGE_ACCOUNT="+accountName)
@@ -187,6 +212,7 @@ func (d *Driver) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRe
187212
return nil, err
188213
}
189214

215+
klog.V(2).Infof("volume(%s) mount on %q succeeded", volumeID, targetPath)
190216
return &csi.NodeStageVolumeResponse{}, nil
191217
}
192218

pkg/blobfuseplugin/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ COPY --from=builder /go/src/sigs.k8s.io/blobfuse-csi-driver/_output/blobfuseplug
2525
RUN apt-get update && clean-install ca-certificates pkg-config libfuse-dev cmake libcurl4-gnutls-dev libgnutls28-dev uuid-dev libgcrypt20-dev wget
2626
RUN wget -O /tmp/packages-microsoft-prod.deb https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb && dpkg -i /tmp/packages-microsoft-prod.deb && apt-get update && apt install blobfuse fuse -y && rm -f /tmp/packages-microsoft-prod.deb
2727
RUN apt remove wget -y
28+
# this is a workaround to install nfs-common and don't quit with error
29+
RUN apt install nfs-common -y || true
2830
LABEL maintainers="andyzhangx"
2931
LABEL description="Blobfuse CSI Driver"
3032

pkg/blobfuseplugin/dev.Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
FROM mcr.microsoft.com/aks/fundamental/base-ubuntu:v0.0.5
1616
RUN wget -O /tmp/packages-microsoft-prod.deb https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb
17-
RUN dpkg -i /tmp/packages-microsoft-prod.deb && apt-get update && apt-get install -y ca-certificates pkg-config libfuse-dev cmake libcurl4-gnutls-dev libgnutls28-dev uuid-dev libgcrypt20-dev blobfuse
17+
RUN dpkg -i /tmp/packages-microsoft-prod.deb && apt-get update && apt-get install -y ca-certificates pkg-config libfuse-dev cmake libcurl4-gnutls-dev libgnutls28-dev uuid-dev libgcrypt20-dev blobfuse nfs-common
1818
LABEL maintainers="andyzhangx"
1919
LABEL description="Blobfuse CSI Driver"
2020

0 commit comments

Comments
 (0)