Skip to content

Add GetClosestVolumeIDFromTargetPath API to the Volume API Group #189

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

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
402 changes: 293 additions & 109 deletions client/api/volume/v2alpha1/api.pb.go

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions client/api/volume/v2alpha1/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ service Volume {
// GetVolumeIDFromTargetPath gets the volume id for a given target path.
rpc GetVolumeIDFromTargetPath(GetVolumeIDFromTargetPathRequest) returns (GetVolumeIDFromTargetPathResponse) {}

// GetClosestVolumeIDFromTargetPath gets the closest volume id for a given target path
// by following symlinks and moving up in the filesystem, if after moving up in the filesystem
// we get to a DriveLetter then the volume corresponding to this drive letter is returned instead.
rpc GetClosestVolumeIDFromTargetPath(GetClosestVolumeIDFromTargetPathRequest) returns (GetClosestVolumeIDFromTargetPathResponse) {}

// WriteVolumeCache write volume cache to disk.
rpc WriteVolumeCache(WriteVolumeCacheRequest) returns (WriteVolumeCacheResponse) {}
}
Expand Down Expand Up @@ -133,6 +138,16 @@ message GetVolumeIDFromTargetPathResponse {
string volume_id = 1;
}

message GetClosestVolumeIDFromTargetPathRequest {
// The target path.
string target_path = 1;
}

message GetClosestVolumeIDFromTargetPathResponse {
// The volume device ID.
string volume_id = 1;
}

message WriteVolumeCacheRequest {
// Volume device ID of the volume to flush the cache.
string volume_id = 1;
Expand Down
2 changes: 1 addition & 1 deletion client/groups/disk/v1/client_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/groups/filesystem/v1/client_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/groups/smb/v1/client_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion client/groups/volume/v1/client_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions client/groups/volume/v2alpha1/client_generated.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions integrationtests/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,12 +201,16 @@ type VirtualHardDisk struct {
InitialSize int64
}

func diskInit(t *testing.T) (*VirtualHardDisk, func()) {
func getTestPluginPath() (string, int) {
s1 := rand.NewSource(time.Now().UTC().UnixNano())
r1 := rand.New(s1)

testId := r1.Intn(10000000)
testPluginPath := fmt.Sprintf("C:\\var\\lib\\kubelet\\plugins\\testplugin-%d.csi.io\\", testId)
testId := r1.Intn(1000000)
return fmt.Sprintf("C:\\var\\lib\\kubelet\\plugins\\testplugin-%d.csi.io\\", testId), testId
}

func diskInit(t *testing.T) (*VirtualHardDisk, func()) {
testPluginPath, testId := getTestPluginPath()
mountPath := fmt.Sprintf("%smount-%d", testPluginPath, testId)
vhdxPath := fmt.Sprintf("%sdisk-%d.vhdx", testPluginPath, testId)

Expand Down Expand Up @@ -240,7 +244,8 @@ func diskInit(t *testing.T) (*VirtualHardDisk, func()) {
if diskNumUnparsed, err = runPowershellCmd(t, cmd); err != nil {
t.Fatalf("Error: %v. Command: %s", err, cmd)
}
if diskNum, err = strconv.ParseUint(strings.TrimRight(diskNumUnparsed, "\r\n"), 10, 32); err != nil {
diskNumUnparsed = strings.TrimSpace(diskNumUnparsed)
if diskNum, err = strconv.ParseUint(diskNumUnparsed, 10, 32); err != nil {
t.Fatalf("Error: %v", err)
}

Expand Down
187 changes: 170 additions & 17 deletions integrationtests/volume_v2alpha1_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ package integrationtests
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"

diskv1 "github.com/kubernetes-csi/csi-proxy/client/api/disk/v1"
Expand All @@ -11,23 +15,10 @@ import (
v2alpha1client "github.com/kubernetes-csi/csi-proxy/client/groups/volume/v2alpha1"
)

func v2alpha1VolumeTests(t *testing.T) {
var volumeClient *v2alpha1client.Client
var diskClient *diskv1client.Client
var err error

if volumeClient, err = v2alpha1client.NewClient(); err != nil {
t.Fatalf("Client new error: %v", err)
}
defer volumeClient.Close()

if diskClient, err = diskv1client.NewClient(); err != nil {
t.Fatalf("DiskClient new error: %v", err)
}
defer diskClient.Close()

// volumeInit initializes a volume, it creates a VHD, initializes it,
// creates a partition with the max size and formats the volume corresponding to that partition
func volumeInit(volumeClient *v2alpha1client.Client, t *testing.T) (*VirtualHardDisk, string, func()) {
vhd, vhdCleanup := diskInit(t)
defer vhdCleanup()

listRequest := &v2alpha1.ListVolumesOnDiskRequest{
DiskNumber: vhd.DiskNumber,
Expand All @@ -42,6 +33,7 @@ func v2alpha1VolumeTests(t *testing.T) {
t.Fatalf("Number of volumes not equal to 1: %d", volumeIDsLen)
}
volumeID := listResponse.VolumeIds[0]
t.Logf("VolumeId %v", volumeID)

isVolumeFormattedRequest := &v2alpha1.IsVolumeFormattedRequest{
VolumeId: volumeID,
Expand Down Expand Up @@ -69,8 +61,146 @@ func v2alpha1VolumeTests(t *testing.T) {
if !isVolumeFormattedResponse.Formatted {
t.Fatal("Volume should be formatted. Unexpected !!")
}
return vhd, volumeID, vhdCleanup
}

func v2alpha1GetClosestVolumeFromTargetPathTests(diskClient *diskv1client.Client, volumeClient *v2alpha1client.Client, t *testing.T) {
t.Run("DriveLetterVolume", func(t *testing.T) {
vhd, _, vhdCleanup := volumeInit(volumeClient, t)
defer vhdCleanup()

// vhd.Mount dir exists, because there are no volumes above it should return the C:\ volume
var request *v2alpha1.GetClosestVolumeIDFromTargetPathRequest
var response *v2alpha1.GetClosestVolumeIDFromTargetPathResponse
request = &v2alpha1.GetClosestVolumeIDFromTargetPathRequest{
TargetPath: vhd.Mount,
}
response, err := volumeClient.GetClosestVolumeIDFromTargetPath(context.TODO(), request)
if err != nil {
t.Fatalf("GetClosestVolumeIDFromTargetPath request error, err=%v", err)
}

// the C drive volume
cmd := exec.Command("powershell", "/c", `(Get-Partition -DriveLetter C | Get-Volume).UniqueId`)
targetb, err := cmd.Output()
if err != nil {
t.Fatalf("Failed to get the C: drive volume")
}
cDriveVolume := strings.TrimSpace(string(targetb))

if response.VolumeId != cDriveVolume {
t.Fatalf("The volume from GetClosestVolumeIDFromTargetPath doesn't match the C: drive volume")
}
})
t.Run("AncestorVolumeFromNestedDirectory", func(t *testing.T) {
var err error
vhd, volumeID, vhdCleanup := volumeInit(volumeClient, t)
defer vhdCleanup()

// Mount the volume
mountVolumeRequest := &v2alpha1.MountVolumeRequest{
VolumeId: volumeID,
TargetPath: vhd.Mount,
}
_, err = volumeClient.MountVolume(context.TODO(), mountVolumeRequest)
if err != nil {
t.Fatalf("Volume id %s mount to path %s failed. Error: %v", volumeID, vhd.Mount, err)
}

// Unmount the volume
defer func() {
unmountVolumeRequest := &v2alpha1.UnmountVolumeRequest{
VolumeId: volumeID,
TargetPath: vhd.Mount,
}
_, err = volumeClient.UnmountVolume(context.TODO(), unmountVolumeRequest)
if err != nil {
t.Fatalf("Volume id %s mount to path %s failed. Error: %v", volumeID, vhd.Mount, err)
}
}()

nestedDirectory := filepath.Join(vhd.Mount, "foo/bar")
err = os.MkdirAll(nestedDirectory, os.ModeDir)
if err != nil {
t.Fatalf("Failed to create directory=%s", nestedDirectory)
}

// the volume returned should be the VHD volume
var request *v2alpha1.GetClosestVolumeIDFromTargetPathRequest
var response *v2alpha1.GetClosestVolumeIDFromTargetPathResponse
request = &v2alpha1.GetClosestVolumeIDFromTargetPathRequest{
TargetPath: nestedDirectory,
}
response, err = volumeClient.GetClosestVolumeIDFromTargetPath(context.TODO(), request)
if err != nil {
t.Fatalf("GetClosestVolumeIDFromTargetPath request error, err=%v", err)
}

if response.VolumeId != volumeID {
t.Fatalf("The volume from GetClosestVolumeIDFromTargetPath doesn't match the VHD volume=%s", volumeID)
}
})

t.Run("SymlinkToVolume", func(t *testing.T) {
var err error
vhd, volumeID, vhdCleanup := volumeInit(volumeClient, t)
defer vhdCleanup()

// Mount the volume
mountVolumeRequest := &v2alpha1.MountVolumeRequest{
VolumeId: volumeID,
TargetPath: vhd.Mount,
}
_, err = volumeClient.MountVolume(context.TODO(), mountVolumeRequest)
if err != nil {
t.Fatalf("Volume id %s mount to path %s failed. Error: %v", volumeID, vhd.Mount, err)
}

// Unmount the volume
defer func() {
unmountVolumeRequest := &v2alpha1.UnmountVolumeRequest{
VolumeId: volumeID,
TargetPath: vhd.Mount,
}
_, err = volumeClient.UnmountVolume(context.TODO(), unmountVolumeRequest)
if err != nil {
t.Fatalf("Volume id %s mount to path %s failed. Error: %v", volumeID, vhd.Mount, err)
}
}()

testPluginPath, _ := getTestPluginPath()
err = os.MkdirAll(testPluginPath, os.ModeDir)
if err != nil {
t.Fatalf("Failed to create directory=%s", testPluginPath)
}

sourceSymlink := filepath.Join(testPluginPath, "source")
err = os.Symlink(vhd.Mount, sourceSymlink)
if err != nil {
t.Fatalf("Failed to create the symlink=%s", sourceSymlink)
}

// the volume returned should be the VHD volume
var request *v2alpha1.GetClosestVolumeIDFromTargetPathRequest
var response *v2alpha1.GetClosestVolumeIDFromTargetPathResponse
request = &v2alpha1.GetClosestVolumeIDFromTargetPathRequest{
TargetPath: sourceSymlink,
}
response, err = volumeClient.GetClosestVolumeIDFromTargetPath(context.TODO(), request)
if err != nil {
t.Fatalf("GetClosestVolumeIDFromTargetPath request error, err=%v", err)
}

if response.VolumeId != volumeID {
t.Fatalf("The volume from GetClosestVolumeIDFromTargetPath doesn't match the VHD volume=%s", volumeID)
}
})
}

func v2alpha1MountVolumeTests(diskClient *diskv1client.Client, volumeClient *v2alpha1client.Client, t *testing.T) {
vhd, volumeID, vhdCleanup := volumeInit(volumeClient, t)
defer vhdCleanup()

t.Logf("VolumeId %v", volumeID)
volumeStatsRequest := &v2alpha1.GetVolumeStatsRequest{
VolumeId: volumeID,
}
Expand Down Expand Up @@ -167,3 +297,26 @@ func v2alpha1VolumeTests(t *testing.T) {
t.Fatalf("Volume id %s mount to path %s failed. Error: %v", volumeID, vhd.Mount, err)
}
}

func v2alpha1VolumeTests(t *testing.T) {
var volumeClient *v2alpha1client.Client
var diskClient *diskv1client.Client
var err error

if volumeClient, err = v2alpha1client.NewClient(); err != nil {
t.Fatalf("Client new error: %v", err)
}
defer volumeClient.Close()

if diskClient, err = diskv1client.NewClient(); err != nil {
t.Fatalf("DiskClient new error: %v", err)
}
defer diskClient.Close()

t.Run("MountVolume", func(t *testing.T) {
v2alpha1MountVolumeTests(diskClient, volumeClient, t)
})
t.Run("GetClosestVolumeFromTargetPath", func(t *testing.T) {
v2alpha1GetClosestVolumeFromTargetPathTests(diskClient, volumeClient, t)
})
}
Loading