Skip to content

Commit b862d4c

Browse files
authored
Merge pull request #6 from darkowlzz/hostpath-block-support
hostpath: Add block volume support
2 parents b2a219c + ea76c5b commit b862d4c

File tree

15 files changed

+797
-49
lines changed

15 files changed

+797
-49
lines changed

Dockerfile

+2
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@ FROM alpine
22
LABEL maintainers="Kubernetes Authors"
33
LABEL description="HostPath Driver"
44

5+
# Add util-linux to get a new version of losetup.
6+
RUN apk add util-linux
57
COPY ./bin/hostpathplugin /hostpathplugin
68
ENTRYPOINT ["/hostpathplugin"]

Gopkg.lock

+8-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

deploy/hostpath/csi-hostpath-plugin.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ spec:
4747
- --v=5
4848
- --csi-address=/csi/csi.sock
4949
- --kubelet-registration-path=/var/lib/kubelet/plugins/csi-hostpath/csi.sock
50+
securityContext:
51+
privileged: true
5052
env:
5153
- name: KUBE_NODE_NAME
5254
valueFrom:
@@ -82,6 +84,9 @@ spec:
8284
- mountPath: /var/lib/kubelet/pods
8385
mountPropagation: Bidirectional
8486
name: mountpoint-dir
87+
- mountPath: /var/lib/kubelet/plugins
88+
mountPropagation: Bidirectional
89+
name: plugins-dir
8590
volumes:
8691
- hostPath:
8792
path: /var/lib/kubelet/plugins/csi-hostpath
@@ -95,3 +100,7 @@ spec:
95100
path: /var/lib/kubelet/plugins_registry
96101
type: Directory
97102
name: registration-dir
103+
- hostPath:
104+
path: /var/lib/kubelet/plugins
105+
type: Directory
106+
name: plugins-dir

pkg/hostpath/controllerserver.go

+96-10
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"google.golang.org/grpc/status"
3333

3434
"github.com/container-storage-interface/spec/lib/go/csi"
35+
"k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
3536
utilexec "k8s.io/utils/exec"
3637
)
3738

@@ -42,6 +43,13 @@ const (
4243
maxStorageCapacity = tib
4344
)
4445

46+
type accessType int
47+
48+
const (
49+
mountAccess accessType = iota
50+
blockAccess
51+
)
52+
4553
type controllerServer struct {
4654
caps []*csi.ControllerServiceCapability
4755
}
@@ -67,9 +75,41 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
6775
if len(req.GetName()) == 0 {
6876
return nil, status.Error(codes.InvalidArgument, "Name missing in request")
6977
}
70-
if req.GetVolumeCapabilities() == nil {
78+
caps := req.GetVolumeCapabilities()
79+
if caps == nil {
7180
return nil, status.Error(codes.InvalidArgument, "Volume Capabilities missing in request")
7281
}
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+
73113
// Need to check for already existing volume name, and if found
74114
// check for the requested capacity and already allocated capacity
75115
if exVol, err := getVolumeByName(req.GetName()); err == nil {
@@ -94,13 +134,35 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
94134
if capacity >= maxStorageCapacity {
95135
return nil, status.Errorf(codes.OutOfRange, "Requested capacity %d exceeds maximum allowed %d", capacity, maxStorageCapacity)
96136
}
137+
97138
volumeID := uuid.NewUUID().String()
98139
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+
}
103164
}
165+
104166
if req.GetVolumeContentSource() != nil {
105167
contentSource := req.GetVolumeContentSource()
106168
if contentSource.GetSnapshot() != nil {
@@ -127,6 +189,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol
127189
hostPathVol.VolID = volumeID
128190
hostPathVol.VolSize = capacity
129191
hostPathVol.VolPath = path
192+
hostPathVol.VolAccessType = requestedAccessType
130193
hostPathVolumes[volumeID] = hostPathVol
131194
return &csi.CreateVolumeResponse{
132195
Volume: &csi.Volume{
@@ -148,11 +211,34 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol
148211
glog.V(3).Infof("invalid delete volume req: %v", req)
149212
return nil, err
150213
}
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)
156242
return &csi.DeleteVolumeResponse{}, nil
157243
}
158244

pkg/hostpath/hostpath.go

+5-4
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ type hostPath struct {
4545
}
4646

4747
type hostPathVolume struct {
48-
VolName string `json:"volName"`
49-
VolID string `json:"volID"`
50-
VolSize int64 `json:"volSize"`
51-
VolPath string `json:"volPath"`
48+
VolName string `json:"volName"`
49+
VolID string `json:"volID"`
50+
VolSize int64 `json:"volSize"`
51+
VolPath string `json:"volPath"`
52+
VolAccessType accessType `json:"volAccessType"`
5253
}
5354

5455
type hostPathSnapshot struct {

0 commit comments

Comments
 (0)