Skip to content
Draft
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/hashicorp/vault v1.14.8
github.com/iancoleman/strcase v0.3.0
github.com/int128/kubelogin v1.28.0
github.com/muesli/termenv v0.16.0
github.com/pkg/errors v0.9.1
github.com/samber/lo v1.47.0
github.com/sirupsen/logrus v1.9.3
Expand Down Expand Up @@ -419,7 +420,6 @@ require (
github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect
github.com/montanaflynn/stats v0.7.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/natefinch/atomic v1.0.1 // indirect
Expand Down
83 changes: 2 additions & 81 deletions go.sum

Large diffs are not rendered by default.

16 changes: 14 additions & 2 deletions internal/dataexport/cmd/download/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@ func recursiveDownload(ctx context.Context, sClient *safeClient.SafeClient, log
}

if srcPath != "" && srcPath[len(srcPath)-1:] == "/" {
// Ensure destination root directory exists
if dstPath != "" {
if err := os.MkdirAll(dstPath, os.ModePerm); err != nil {
return fmt.Errorf("create destination root dir error: %s", err.Error())
}
}
var wg sync.WaitGroup
var mu sync.Mutex
var firstErr error
Expand Down Expand Up @@ -221,7 +227,10 @@ func recursiveDownload(ctx context.Context, sClient *safeClient.SafeClient, log
return firstErr
} else {
if dstPath != "" {
// Create out file
// Ensure parent directory exists and create output file
if err := os.MkdirAll(filepath.Dir(dstPath), os.ModePerm); err != nil {
return fmt.Errorf("create destination dir error: %s", err.Error())
}
out, err := os.Create(dstPath)
if err != nil {
return err
Expand Down Expand Up @@ -267,7 +276,7 @@ func Run(ctx context.Context, log *slog.Logger, cmd *cobra.Command, args []strin
return err
}

deName, err := util.CreateDataExporterIfNeededFunc(ctx, log, dataName, namespace, publish, ttl, rtClient)
deName, volumeKind, err := util.CreateDataExporterIfNeededFunc(ctx, log, dataName, namespace, publish, ttl, rtClient)
if err != nil {
return err
}
Expand All @@ -281,6 +290,9 @@ func Run(ctx context.Context, log *slog.Logger, cmd *cobra.Command, args []strin

switch volumeMode {
case "Filesystem":
if (srcPath == "" || srcPath == "/") && (volumeKind == util.VirtualDiskKind || volumeKind == util.VirtualDiskSnapshotKind) {
srcPath = "/" + util.VirtualDiskImage
}
if srcPath == "" {
return fmt.Errorf("invalid source path: '%s'", srcPath)
}
Expand Down
16 changes: 8 additions & 8 deletions internal/dataexport/cmd/download/download_http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ func TestDownloadFilesystem_OK(t *testing.T) {
util.PrepareDownloadFunc = func(_ context.Context, _ *slog.Logger, _, _ string, _ bool, _ *safereq.SafeClient) (string, string, *safereq.SafeClient, error) {
return srv.URL + "/api/v1/files", "Filesystem", newNoAuthSafe(), nil
}
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
return de, nil
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, string, error) {
return de, "", nil
}
defer func() {
util.PrepareDownloadFunc = origPrep
Expand Down Expand Up @@ -78,8 +78,8 @@ func TestDownloadFilesystem_BadPath(t *testing.T) {
util.PrepareDownloadFunc = func(_ context.Context, _ *slog.Logger, _, _ string, _ bool, _ *safereq.SafeClient) (string, string, *safereq.SafeClient, error) {
return srv.URL + "/api/v1/files", "Block", newNoAuthSafe(), nil
}
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
return de, nil
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, string, error) {
return de, "", nil
}
defer func() { util.PrepareDownloadFunc = origPrep; util.CreateDataExporterIfNeededFunc = origCreate }()

Expand All @@ -102,8 +102,8 @@ func TestDownloadBlock_OK(t *testing.T) {
util.PrepareDownloadFunc = func(_ context.Context, _ *slog.Logger, _, _ string, _ bool, _ *safereq.SafeClient) (string, string, *safereq.SafeClient, error) {
return srv.URL + "/api/v1/block", "Block", newNoAuthSafe(), nil
}
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
return de, nil
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, string, error) {
return de, "", nil
}
defer func() {
util.PrepareDownloadFunc = origPrep
Expand Down Expand Up @@ -132,8 +132,8 @@ func TestDownloadBlock_WrongEndpoint(t *testing.T) {
util.PrepareDownloadFunc = func(_ context.Context, _ *slog.Logger, _, _ string, _ bool, _ *safereq.SafeClient) (string, string, *safereq.SafeClient, error) {
return srv.URL + "/api/v1/block", "Filesystem", newNoAuthSafe(), nil
}
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
return de, nil
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, string, error) {
return de, "", nil
}
defer func() { util.PrepareDownloadFunc = origPrep; util.CreateDataExporterIfNeededFunc = origCreate }()

Expand Down
14 changes: 12 additions & 2 deletions internal/dataexport/cmd/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,13 @@ func downloadFunc(
var req *http.Request
switch volumeMode {
case "Filesystem":
if srcPath == "" || srcPath[len(srcPath)-1:] != "/" {
if srcPath == "" {
return fmt.Errorf("invalid source path: '%s'", srcPath)
}
// ensure trailing slash for directory listing
if srcPath[len(srcPath)-1:] != "/" {
srcPath += "/"
}
dataURL, err := neturl.JoinPath(url, srcPath)
if err != nil {
return err
Expand Down Expand Up @@ -179,13 +183,19 @@ func Run(ctx context.Context, log *slog.Logger, cmd *cobra.Command, args []strin
if err != nil {
return err
}
deName, err := util.CreateDataExporterIfNeededFunc(ctx, log, dataName, namespace, publish, ttl, rtClient)
deName, volumeKind, err := util.CreateDataExporterIfNeededFunc(ctx, log, dataName, namespace, publish, ttl, rtClient)
if err != nil {
return err
}

log.Info("DataExport created", slog.String("name", deName), slog.String("namespace", namespace))

if srcPath == "" {
if volumeKind == util.VirtualDiskKind || volumeKind == util.VirtualDiskSnapshotKind {
srcPath = "/"
}
}

err = downloadFunc(ctx, log, namespace, deName, srcPath, publish, sClient, func(body io.Reader) error {
_, err := io.Copy(os.Stdout, body)
if err == io.EOF {
Expand Down
12 changes: 6 additions & 6 deletions internal/dataexport/cmd/list/list_http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ func TestListFilesystem_OK(t *testing.T) {
safereq.SupportNoAuth = true
return srv.URL + "/api/v1/files", "Filesystem", newSafe(), nil
}
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
return de, nil
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, string, error) {
return de, "", nil
}
defer func() { util.PrepareDownloadFunc = origPrep; util.CreateDataExporterIfNeededFunc = origCreate }()

Expand Down Expand Up @@ -74,8 +74,8 @@ func TestListBlock_OK(t *testing.T) {
safereq.SupportNoAuth = true
return srv.URL + "/api/v1/block", "Block", newSafe(), nil
}
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
return de, nil
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, string, error) {
return de, "", nil
}
defer func() { util.PrepareDownloadFunc = origPrep; util.CreateDataExporterIfNeededFunc = origCreate }()

Expand Down Expand Up @@ -109,8 +109,8 @@ func TestListFilesystem_NotDir(t *testing.T) {
safereq.SupportNoAuth = true
return srv.URL + "/api/v1/files", "Filesystem", newSafe(), nil
}
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, error) {
return de, nil
util.CreateDataExporterIfNeededFunc = func(_ context.Context, _ *slog.Logger, de, _ string, _ bool, _ string, _ ctrlclient.Client) (string, string, error) {
return de, "", nil
}
defer func() { util.PrepareDownloadFunc = origPrep; util.CreateDataExporterIfNeededFunc = origCreate }()

Expand Down
4 changes: 4 additions & 0 deletions internal/dataexport/util/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package util

// VirtualDiskImage is a default image file name used for VirtualDisk and VirtualDiskSnapshot when volumeMode=Filesystem.
const VirtualDiskImage = "disk.img"
32 changes: 17 additions & 15 deletions internal/dataexport/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,62 +131,64 @@ func GetDataExportWithRestart(ctx context.Context, deName, namespace string, rtC
return deObj, nil
}

func CreateDataExporterIfNeeded(ctx context.Context, log *slog.Logger, deName, namespace string, publish bool, ttl string, rtClient ctrlrtclient.Client) (string, error) {
func CreateDataExporterIfNeeded(ctx context.Context, log *slog.Logger, dataName, namespace string, publish bool, ttl string, rtClient ctrlrtclient.Client) (string, string, error) {
deName := dataName
var volumeKind, volumeName string
lowerCaseDeName := strings.ToLower(deName)
lowerCaseDeName := strings.ToLower(dataName)

switch {
// PVC / PersistentVolumeClaim
case strings.HasPrefix(lowerCaseDeName, "pvc/"):
volumeKind = PersistentVolumeClaimKind
volumeName = deName[4:]
volumeName = dataName[4:]
deName = "de-pvc-" + volumeName
case strings.HasPrefix(lowerCaseDeName, "persistentvolumeclaim/"):
volumeKind = PersistentVolumeClaimKind
volumeName = deName[len("persistentvolumeclaim/"):]
volumeName = dataName[len("persistentvolumeclaim/"):]
deName = "de-pvc-" + volumeName

// VS / VolumeSnapshot
case strings.HasPrefix(lowerCaseDeName, "vs/"):
volumeKind = VolumeSnapshotKind
volumeName = deName[3:]
volumeName = dataName[3:]
deName = "de-vs-" + volumeName
case strings.HasPrefix(lowerCaseDeName, "volumesnapshot/"):
volumeKind = VolumeSnapshotKind
volumeName = deName[len("volumesnapshot/"):]
volumeName = dataName[len("volumesnapshot/"):]
deName = "de-vs-" + volumeName

// VD / VirtualDisk
case strings.HasPrefix(lowerCaseDeName, "vd/"):
volumeKind = VirtualDiskKind
volumeName = deName[3:]
volumeName = dataName[3:]
deName = "de-vd-" + volumeName
case strings.HasPrefix(lowerCaseDeName, "virtualdisk/"):
volumeKind = VirtualDiskKind
volumeName = deName[len("virtualdisk/"):]
volumeName = dataName[len("virtualdisk/"):]
deName = "de-vd-" + volumeName

// VDS / VirtualDiskSnapshot
case strings.HasPrefix(lowerCaseDeName, "vds/"):
volumeKind = VirtualDiskSnapshotKind
volumeName = deName[4:]
volumeName = dataName[4:]
deName = "de-vds-" + volumeName
case strings.HasPrefix(lowerCaseDeName, "virtualdisksnapshot/"):
volumeKind = VirtualDiskSnapshotKind
volumeName = deName[len("virtualdisksnapshot/"):]
volumeName = dataName[len("virtualdisksnapshot/"):]
deName = "de-vds-" + volumeName

default:
return deName, nil
// Assume user provided existing DataExport name; don't validate kind here.
return deName, "", nil
}

err := CreateDataExport(ctx, deName, namespace, ttl, volumeKind, volumeName, publish, rtClient)
if err != nil {
return deName, err
if err := CreateDataExport(ctx, deName, namespace, ttl, volumeKind, volumeName, publish, rtClient); err != nil {
return deName, "", err
}
log.Info("DataExport creating", slog.String("name", deName), slog.String("namespace", namespace))

return deName, nil
// Build minimal object to propagate kind information further.
return deName, volumeKind, nil
}

func CreateDataExport(ctx context.Context, deName, namespace, ttl, volumeKind, volumeName string, publish bool, rtClient ctrlrtclient.Client) error {
Expand Down
6 changes: 5 additions & 1 deletion internal/dataexport/util/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,13 @@ func TestCreateDataExporterIfNeeded(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
c := fake.NewClientBuilder().WithScheme(scheme).Build()

returnedName, err := CreateDataExporterIfNeeded(ctx, logger, tt.input, "test-ns", false, "2m", c)
returnedName, kind, err := CreateDataExporterIfNeeded(ctx, logger, tt.input, "test-ns", false, "2m", c)
require.NoError(t, err)
require.Equal(t, tt.expectName, returnedName)
// kind may be empty when DataExport already exists
if tt.expectKind != "" {
require.Equal(t, tt.expectKind, kind)
}

var de v1alpha1.DataExport
getErr := c.Get(ctx, ctrlclient.ObjectKey{Name: tt.expectName, Namespace: "test-ns"}, &de)
Expand Down