Skip to content

Commit c83c131

Browse files
authored
Merge pull request #483 from jingxu97/Feb/csiproxy
Support GCEPD driver for Windows
2 parents a7bd47a + 9ba1f1c commit c83c131

12 files changed

+610
-118
lines changed

Makefile

+10-2
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,23 @@
1717

1818
STAGINGIMAGE=${GCE_PD_CSI_STAGING_IMAGE}
1919
STAGINGVERSION=${GCE_PD_CSI_STAGING_VERSION}
20+
DRIVERBINARY=gce-pd-csi-driver
21+
DRIVERWINDOWSBINARY=${DRIVERBINARY}.exe
2022

2123
all: gce-pd-driver
22-
2324
gce-pd-driver:
2425
mkdir -p bin
2526
ifndef GCE_PD_CSI_STAGING_VERSION
2627
$(error "Must set environment variable GCE_PD_CSI_STAGING_VERSION to staging version")
2728
endif
28-
go build -ldflags "-X main.vendorVersion=${STAGINGVERSION}" -o bin/gce-pd-csi-driver ./cmd/
29+
go build -ldflags "-X main.vendorVersion=${STAGINGVERSION}" -o bin/${DRIVERBINARY} ./cmd/
30+
31+
build-windows:
32+
mkdir -p bin
33+
ifndef GCE_PD_CSI_STAGING_VERSION
34+
$(error "Must set environment variable GCE_PD_CSI_STAGING_VERSION to staging version")
35+
endif
36+
GOOS=windows go build -ldflags "-X main.vendorVersion=${STAGINGVERSION}" -o bin/${DRIVERWINDOWSBINARY} ./cmd/
2937

3038
build-container:
3139
ifndef GCE_PD_CSI_STAGING_IMAGE

cmd/main.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,10 @@ func handle() {
9090
//Initialize requirements for the node service
9191
var nodeServer *driver.GCENodeServer
9292
if *runNodeService {
93-
mounter := mountmanager.NewSafeMounter()
93+
mounter, err := mountmanager.NewSafeMounter()
94+
if err != nil {
95+
klog.Fatalf("Failed to get safe mounter: %v", err)
96+
}
9497
deviceUtils := mountmanager.NewDeviceUtils()
9598
statter := mountmanager.NewStatter()
9699
meta, err := metadataservice.NewMetadataService()

go.mod

+4-2
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,19 @@ require (
66
cloud.google.com/go v0.45.1
77
github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190612171043-2e19bb35a278
88
github.com/container-storage-interface/spec v1.2.0
9-
github.com/golang/protobuf v1.3.2
9+
github.com/golang/protobuf v1.3.4
1010
github.com/google/uuid v1.1.1
1111
github.com/hashicorp/go-multierror v1.0.0 // indirect
12+
github.com/kubernetes-csi/csi-proxy/client v0.0.0-20200330215040-9eff16441b2a
1213
github.com/kubernetes-csi/csi-test/v3 v3.0.0
1314
github.com/onsi/ginkgo v1.10.3
1415
github.com/onsi/gomega v1.7.1
1516
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
1617
golang.org/x/sys v0.0.0-20191113165036-4c7a9d0fe056
18+
golang.org/x/tools/gopls v0.3.3 // indirect
1719
google.golang.org/api v0.10.0
1820
google.golang.org/genproto v0.0.0-20191114150713-6bbd007550de
19-
google.golang.org/grpc v1.25.1
21+
google.golang.org/grpc v1.27.1
2022
gopkg.in/gcfg.v1 v1.2.3
2123
gopkg.in/warnings.v0 v0.1.2 // indirect
2224
k8s.io/apimachinery v0.17.1

go.sum

+44
Large diffs are not rendered by default.

pkg/gce-pd-csi-driver/node.go

+30-50
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ package gceGCEDriver
1717
import (
1818
"fmt"
1919
"os"
20+
"runtime"
2021
"strconv"
2122
"strings"
2223

@@ -57,10 +58,20 @@ var _ csi.NodeServer = &GCENodeServer{}
5758
// node boot disk is considered an attachable disk so effective attach limit is
5859
// one less.
5960
const (
60-
volumeLimitSmall int64 = 15
61-
volumeLimitBig int64 = 127
61+
volumeLimitSmall int64 = 15
62+
volumeLimitBig int64 = 127
63+
defaultLinuxFsType = "ext4"
64+
defaultWindowsFsType = "ntfs"
6265
)
6366

67+
func getDefaultFsType() string {
68+
if runtime.GOOS == "windows" {
69+
return defaultWindowsFsType
70+
} else {
71+
return defaultLinuxFsType
72+
}
73+
}
74+
6475
func (ns *GCENodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
6576
// Validate Arguments
6677
targetPath := req.GetTargetPath()
@@ -129,10 +140,10 @@ func (ns *GCENodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePub
129140
}
130141

131142
sourcePath = stagingTargetPath
132-
133-
if err := os.MkdirAll(targetPath, 0750); err != nil {
143+
if err := preparePublishPath(targetPath, ns.Mounter); err != nil {
134144
return nil, status.Error(codes.Internal, fmt.Sprintf("mkdir failed on disk %s (%v)", targetPath, err))
135145
}
146+
136147
} else if blk := volumeCapability.GetBlock(); blk != nil {
137148
klog.V(4).Infof("NodePublishVolume with block volume mode")
138149

@@ -141,7 +152,7 @@ func (ns *GCENodeServer) NodePublishVolume(ctx context.Context, req *csi.NodePub
141152
partition = part
142153
}
143154

144-
sourcePath, err = ns.getDevicePath(volumeID, partition)
155+
sourcePath, err = getDevicePath(ns, volumeID, partition)
145156
if err != nil {
146157
return nil, status.Error(codes.Internal, fmt.Sprintf("Error when getting device path: %v", err))
147158
}
@@ -218,11 +229,9 @@ func (ns *GCENodeServer) NodeUnpublishVolume(ctx context.Context, req *csi.NodeU
218229
}
219230
defer ns.volumeLocks.Release(volumeID)
220231

221-
err := mount.CleanupMountPoint(targetPath, ns.Mounter.Interface, false /* bind mount */)
222-
if err != nil {
232+
if err := cleanupPublishPath(targetPath, ns.Mounter); err != nil {
223233
return nil, status.Error(codes.Internal, fmt.Sprintf("Unmount failed: %v\nUnmounting arguments: %s\n", err, targetPath))
224234
}
225-
226235
klog.V(4).Infof("NodeUnpublishVolume succeded on %v from %s", volumeID, targetPath)
227236
return &csi.NodeUnpublishVolumeResponse{}, nil
228237
}
@@ -264,27 +273,19 @@ func (ns *GCENodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStage
264273
if part, ok := req.GetVolumeContext()[common.VolumeAttributePartition]; ok {
265274
partition = part
266275
}
276+
devicePath, err := getDevicePath(ns, volumeID, partition)
267277

268-
devicePath, err := ns.getDevicePath(volumeID, partition)
269278
if err != nil {
270279
return nil, status.Error(codes.Internal, fmt.Sprintf("Error when getting device path: %v", err))
271280
}
272281

273282
klog.V(4).Infof("Successfully found attached GCE PD %q at device path %s.", volumeKey.Name, devicePath)
274283

275-
// Part 2: Check if mount already exists at targetpath
284+
// Part 2: Check if mount already exists at stagingTargetPath
276285
notMnt, err := ns.Mounter.Interface.IsLikelyNotMountPoint(stagingTargetPath)
277-
if err != nil {
278-
if os.IsNotExist(err) {
279-
if err := os.MkdirAll(stagingTargetPath, 0750); err != nil {
280-
return nil, status.Error(codes.Internal, fmt.Sprintf("Failed to create directory (%q): %v", stagingTargetPath, err))
281-
}
282-
notMnt = true
283-
} else {
284-
return nil, status.Error(codes.Internal, fmt.Sprintf("Unknown error when checking mount point (%q): %v", stagingTargetPath, err))
285-
}
286+
if err != nil && !os.IsNotExist(err) {
287+
return nil, status.Error(codes.Internal, fmt.Sprintf("cannot validate mount point: %s %v", stagingTargetPath, err))
286288
}
287-
288289
if !notMnt {
289290
// TODO(#95): Check who is mounted here. No error if its us
290291
/*
@@ -293,15 +294,17 @@ func (ns *GCENodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStage
293294
3) Readonly MUST match
294295
295296
*/
296-
297297
klog.V(4).Infof("NodeStageVolume succeded on %v to %s, mount already exists.", volumeID, stagingTargetPath)
298298
return &csi.NodeStageVolumeResponse{}, nil
299299

300300
}
301+
if err := prepareStagePath(stagingTargetPath, ns.Mounter); err != nil {
302+
return nil, status.Error(codes.Internal, fmt.Sprintf("mkdir failed on disk %s (%v)", stagingTargetPath, err))
303+
}
301304

302305
// Part 3: Mount device to stagingTargetPath
303-
// Default fstype is ext4
304-
fstype := "ext4"
306+
fstype := getDefaultFsType()
307+
305308
options := []string{}
306309
if mnt := volumeCapability.GetMount(); mnt != nil {
307310
if mnt.FsType != "" {
@@ -316,7 +319,7 @@ func (ns *GCENodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStage
316319
return &csi.NodeStageVolumeResponse{}, nil
317320
}
318321

319-
err = ns.Mounter.FormatAndMount(devicePath, stagingTargetPath, fstype, options)
322+
err = formatAndMount(devicePath, stagingTargetPath, fstype, options, ns.Mounter)
320323
if err != nil {
321324
return nil, status.Error(codes.Internal,
322325
fmt.Sprintf("Failed to format and mount device from (%q) to (%q) with fstype (%q) and options (%q): %v",
@@ -343,9 +346,8 @@ func (ns *GCENodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUns
343346
}
344347
defer ns.volumeLocks.Release(volumeID)
345348

346-
err := mount.CleanupMountPoint(stagingTargetPath, ns.Mounter.Interface, false /* bind mount */)
347-
if err != nil {
348-
return nil, status.Error(codes.Internal, fmt.Sprintf("NodeUnstageVolume failed to unmount at path %s: %v", stagingTargetPath, err))
349+
if err := cleanupStagePath(stagingTargetPath, ns.Mounter); err != nil {
350+
return nil, status.Error(codes.Internal, fmt.Sprintf("NodeUnstageVolume failed: %v\nUnmounting arguments: %s\n", err, stagingTargetPath))
349351
}
350352

351353
klog.V(4).Infof("NodeUnstageVolume succeded on %v from %s", volumeID, stagingTargetPath)
@@ -454,7 +456,7 @@ func (ns *GCENodeServer) NodeExpandVolume(ctx context.Context, req *csi.NodeExpa
454456
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("volume ID is invalid: %v", err))
455457
}
456458

457-
devicePath, err := ns.getDevicePath(volumeID, "")
459+
devicePath, err := getDevicePath(ns, volumeID, "")
458460
if err != nil {
459461
return nil, status.Error(codes.Internal, fmt.Sprintf("error when getting device path for %s: %v", volumeID, err))
460462
}
@@ -517,28 +519,6 @@ func (ns *GCENodeServer) GetVolumeLimits() (int64, error) {
517519
return volumeLimitBig, nil
518520
}
519521

520-
func (ns *GCENodeServer) getDevicePath(volumeID string, partition string) (string, error) {
521-
volumeKey, err := common.VolumeIDToKey(volumeID)
522-
if err != nil {
523-
return "", err
524-
}
525-
deviceName, err := common.GetDeviceName(volumeKey)
526-
if err != nil {
527-
return "", fmt.Errorf("error getting device name: %v", err)
528-
}
529-
530-
devicePaths := ns.DeviceUtils.GetDiskByIdPaths(deviceName, partition)
531-
devicePath, err := ns.DeviceUtils.VerifyDevicePath(devicePaths, deviceName)
532-
533-
if err != nil {
534-
return "", fmt.Errorf("error verifying GCE PD (%q) is attached: %v", volumeKey.Name, err)
535-
}
536-
if devicePath == "" {
537-
return "", fmt.Errorf("unable to find device path out of attempted paths: %v", devicePaths)
538-
}
539-
return devicePath, nil
540-
}
541-
542522
func (ns *GCENodeServer) getBlockSizeBytes(devicePath string) (int64, error) {
543523
output, err := ns.Mounter.Exec.Command("blockdev", "--getsize64", devicePath).CombinedOutput()
544524
if err != nil {

pkg/gce-pd-csi-driver/utils_linux.go

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// +build !windows
2+
3+
/*
4+
Copyright 2020 The Kubernetes Authors.
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
package gceGCEDriver
17+
18+
import (
19+
"fmt"
20+
"os"
21+
22+
"google.golang.org/grpc/codes"
23+
"google.golang.org/grpc/status"
24+
"k8s.io/utils/mount"
25+
"sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/common"
26+
)
27+
28+
func getDevicePath(ns *GCENodeServer, volumeID, partition string) (string, error) {
29+
volumeKey, err := common.VolumeIDToKey(volumeID)
30+
if err != nil {
31+
return "", err
32+
}
33+
deviceName, err := common.GetDeviceName(volumeKey)
34+
if err != nil {
35+
return "", fmt.Errorf("error getting device name: %v", err)
36+
}
37+
devicePaths := ns.DeviceUtils.GetDiskByIdPaths(deviceName, partition)
38+
devicePath, err := ns.DeviceUtils.VerifyDevicePath(devicePaths, deviceName)
39+
if err != nil {
40+
return "", status.Error(codes.Internal, fmt.Sprintf("error verifying GCE PD (%q) is attached: %v", deviceName, err))
41+
}
42+
if devicePath == "" {
43+
return "", status.Error(codes.Internal, fmt.Sprintf("Unable to find device path out of attempted paths: %v", devicePaths))
44+
}
45+
return devicePath, nil
46+
}
47+
48+
func formatAndMount(source, target, fstype string, options []string, m *mount.SafeFormatAndMount) error {
49+
return m.FormatAndMount(source, target, fstype, options)
50+
}
51+
52+
func preparePublishPath(path string, m *mount.SafeFormatAndMount) error {
53+
return os.MkdirAll(path, 0750)
54+
}
55+
56+
func prepareStagePath(path string, m *mount.SafeFormatAndMount) error {
57+
return os.MkdirAll(path, 0750)
58+
}
59+
60+
func cleanupPublishPath(path string, m *mount.SafeFormatAndMount) error {
61+
return mount.CleanupMountPoint(path, m, false /* bind mount */)
62+
}
63+
64+
func cleanupStagePath(path string, m *mount.SafeFormatAndMount) error {
65+
return cleanupPublishPath(path, m)
66+
}
+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// +build windows
2+
3+
/*
4+
Copyright 2020 The Kubernetes Authors.
5+
Licensed under the Apache License, Version 2.0 (the "License");
6+
you may not use this file except in compliance with the License.
7+
You may obtain a copy of the License at
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
Unless required by applicable law or agreed to in writing, software
10+
distributed under the License is distributed on an "AS IS" BASIS,
11+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
See the License for the specific language governing permissions and
13+
limitations under the License.
14+
*/
15+
16+
package gceGCEDriver
17+
18+
import (
19+
"fmt"
20+
"k8s.io/utils/mount"
21+
"sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/common"
22+
mounter "sigs.k8s.io/gcp-compute-persistent-disk-csi-driver/pkg/mount-manager"
23+
)
24+
25+
func formatAndMount(source, target, fstype string, options []string, m *mount.SafeFormatAndMount) error {
26+
proxy, ok := m.Interface.(*mounter.CSIProxyMounter)
27+
if !ok {
28+
return fmt.Errorf("could not cast to csi proxy class")
29+
}
30+
return proxy.FormatAndMount(source, target, fstype, options)
31+
}
32+
33+
// Before mounting (which means creating symlink) in Windows, the targetPath should
34+
// not exist. Currently kubelet creates the path beforehand, this is a workaround to
35+
// remove the path first.
36+
func preparePublishPath(path string, m *mount.SafeFormatAndMount) error {
37+
proxy, ok := m.Interface.(*mounter.CSIProxyMounter)
38+
if !ok {
39+
return fmt.Errorf("could not cast to csi proxy class")
40+
}
41+
exists, err := proxy.ExistsPath(path)
42+
if err != nil {
43+
return err
44+
}
45+
if exists {
46+
return proxy.RemovePodDir(path)
47+
}
48+
return nil
49+
}
50+
51+
// Before staging (which means creating symlink) in Windows, the targetPath should
52+
// not exist.
53+
func prepareStagePath(path string, m *mount.SafeFormatAndMount) error {
54+
return nil
55+
}
56+
57+
func cleanupPublishPath(path string, m *mount.SafeFormatAndMount) error {
58+
proxy, ok := m.Interface.(*mounter.CSIProxyMounter)
59+
if !ok {
60+
return fmt.Errorf("could not cast to csi proxy class")
61+
}
62+
return proxy.RemovePodDir(path)
63+
}
64+
65+
func cleanupStagePath(path string, m *mount.SafeFormatAndMount) error {
66+
proxy, ok := m.Interface.(*mounter.CSIProxyMounter)
67+
if !ok {
68+
return fmt.Errorf("could not cast to csi proxy class")
69+
}
70+
return proxy.RemovePluginDir(path)
71+
}
72+
73+
// search Windows disk number by volumeID
74+
func getDevicePath(ns *GCENodeServer, volumeID, partition string) (string, error) {
75+
volumeKey, err := common.VolumeIDToKey(volumeID)
76+
if err != nil {
77+
return "", err
78+
}
79+
deviceName, err := common.GetDeviceName(volumeKey)
80+
if err != nil {
81+
return "", fmt.Errorf("error getting device name: %v", err)
82+
}
83+
proxy, ok := ns.Mounter.Interface.(*mounter.CSIProxyMounter)
84+
if !ok {
85+
return "", fmt.Errorf("could not cast to csi proxy class")
86+
}
87+
return proxy.GetDevicePath(deviceName, partition, volumeKey.Name)
88+
}

0 commit comments

Comments
 (0)