@@ -32,6 +32,7 @@ import (
32
32
"google.golang.org/grpc/status"
33
33
34
34
"github.com/container-storage-interface/spec/lib/go/csi"
35
+ "k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
35
36
utilexec "k8s.io/utils/exec"
36
37
)
37
38
@@ -42,6 +43,13 @@ const (
42
43
maxStorageCapacity = tib
43
44
)
44
45
46
+ type accessType int
47
+
48
+ const (
49
+ mountAccess accessType = iota
50
+ blockAccess
51
+ )
52
+
45
53
type controllerServer struct {
46
54
caps []* csi.ControllerServiceCapability
47
55
}
@@ -67,9 +75,41 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
67
75
if len (req .GetName ()) == 0 {
68
76
return nil , status .Error (codes .InvalidArgument , "Name missing in request" )
69
77
}
70
- if req .GetVolumeCapabilities () == nil {
78
+ caps := req .GetVolumeCapabilities ()
79
+ if caps == nil {
71
80
return nil , status .Error (codes .InvalidArgument , "Volume Capabilities missing in request" )
72
81
}
82
+
83
+ // Keep a record of the requested access types.
84
+ var accessTypeMount , accessTypeBlock bool
85
+
86
+ for _ , cap := range caps {
87
+ if cap .GetBlock () != nil {
88
+ accessTypeBlock = true
89
+ }
90
+ if cap .GetMount () != nil {
91
+ accessTypeMount = true
92
+ }
93
+ }
94
+ // A real driver would also need to check that the other
95
+ // fields in VolumeCapabilities are sane. The check above is
96
+ // just enough to pass the "[Testpattern: Dynamic PV (block
97
+ // volmode)] volumeMode should fail in binding dynamic
98
+ // provisioned PV to PVC" storage E2E test.
99
+
100
+ if accessTypeBlock && accessTypeMount {
101
+ return nil , status .Error (codes .InvalidArgument , "cannot have both block and mount access type" )
102
+ }
103
+
104
+ var requestedAccessType accessType
105
+
106
+ if accessTypeBlock {
107
+ requestedAccessType = blockAccess
108
+ } else {
109
+ // Default to mount.
110
+ requestedAccessType = mountAccess
111
+ }
112
+
73
113
// Need to check for already existing volume name, and if found
74
114
// check for the requested capacity and already allocated capacity
75
115
if exVol , err := getVolumeByName (req .GetName ()); err == nil {
@@ -94,13 +134,35 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
94
134
if capacity >= maxStorageCapacity {
95
135
return nil , status .Errorf (codes .OutOfRange , "Requested capacity %d exceeds maximum allowed %d" , capacity , maxStorageCapacity )
96
136
}
137
+
97
138
volumeID := uuid .NewUUID ().String ()
98
139
path := provisionRoot + volumeID
99
- err := os .MkdirAll (path , 0777 )
100
- if err != nil {
101
- glog .V (3 ).Infof ("failed to create volume: %v" , err )
102
- return nil , err
140
+
141
+ switch requestedAccessType {
142
+ case blockAccess :
143
+ executor := utilexec .New ()
144
+ size := fmt .Sprintf ("%dM" , capacity / mib )
145
+ // Create a block file.
146
+ out , err := executor .Command ("fallocate" , "-l" , size , path ).CombinedOutput ()
147
+ if err != nil {
148
+ glog .V (3 ).Infof ("failed to create block device: %v" , string (out ))
149
+ return nil , err
150
+ }
151
+
152
+ // Associate block file with the loop device.
153
+ volPathHandler := volumepathhandler.VolumePathHandler {}
154
+ _ , err = volPathHandler .AttachFileDevice (path )
155
+ if err != nil {
156
+ return nil , status .Error (codes .Internal , fmt .Sprintf ("failed to attach device: %v" , err ))
157
+ }
158
+ case mountAccess :
159
+ err := os .MkdirAll (path , 0777 )
160
+ if err != nil {
161
+ glog .V (3 ).Infof ("failed to create volume: %v" , err )
162
+ return nil , err
163
+ }
103
164
}
165
+
104
166
if req .GetVolumeContentSource () != nil {
105
167
contentSource := req .GetVolumeContentSource ()
106
168
if contentSource .GetSnapshot () != nil {
@@ -127,6 +189,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
127
189
hostPathVol .VolID = volumeID
128
190
hostPathVol .VolSize = capacity
129
191
hostPathVol .VolPath = path
192
+ hostPathVol .VolAccessType = requestedAccessType
130
193
hostPathVolumes [volumeID ] = hostPathVol
131
194
return & csi.CreateVolumeResponse {
132
195
Volume : & csi.Volume {
@@ -148,11 +211,34 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
148
211
glog .V (3 ).Infof ("invalid delete volume req: %v" , req )
149
212
return nil , err
150
213
}
151
- volumeID := req .VolumeId
152
- glog .V (4 ).Infof ("deleting volume %s" , volumeID )
153
- path := provisionRoot + volumeID
154
- os .RemoveAll (path )
155
- delete (hostPathVolumes , volumeID )
214
+
215
+ vol , err := getVolumeByID (req .GetVolumeId ())
216
+ if err != nil {
217
+ // Return OK if the volume is not found.
218
+ return & csi.DeleteVolumeResponse {}, nil
219
+ }
220
+ glog .V (4 ).Infof ("deleting volume %s" , vol .VolID )
221
+
222
+ if vol .VolAccessType == blockAccess {
223
+
224
+ volPathHandler := volumepathhandler.VolumePathHandler {}
225
+ // Get the associated loop device.
226
+ device , err := volPathHandler .GetLoopDevice (provisionRoot + vol .VolID )
227
+ if err != nil {
228
+ return nil , status .Error (codes .Internal , fmt .Sprintf ("failed to get the loop device: %v" , err ))
229
+ }
230
+
231
+ if device != "" {
232
+ // Remove any associated loop device.
233
+ glog .V (4 ).Infof ("deleting loop device %s" , device )
234
+ if err := volPathHandler .RemoveLoopDevice (device ); err != nil {
235
+ return nil , status .Error (codes .Internal , fmt .Sprintf ("failed to remove loop device: %v" , err ))
236
+ }
237
+ }
238
+ }
239
+
240
+ os .RemoveAll (vol .VolPath )
241
+ delete (hostPathVolumes , vol .VolID )
156
242
return & csi.DeleteVolumeResponse {}, nil
157
243
}
158
244
0 commit comments