Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitaly.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorToon Claes <toon@gitlab.com>2023-11-06 22:48:08 +0300
committerToon Claes <toon@gitlab.com>2023-11-20 12:45:29 +0300
commit62727018760718488b88fe0d742342120745ad31 (patch)
tree93f342f7e480ac570065b0128ce2533e87317c9e
parent0b83defb571f82b40758ddc3d9692b9653b3ae19 (diff)
backup: Add SignedURL to Sink
Expose the SignedURL function of the bucket through the Sink interface. Some blob backends will support this. It's hard to test signed URLs with actual servers, so for now we can only test some backends return a "not implemented" error.
-rw-r--r--internal/backup/backup.go4
-rw-r--r--internal/backup/filesystem_sink.go7
-rw-r--r--internal/backup/sink.go19
-rw-r--r--internal/backup/sink_test.go48
4 files changed, 78 insertions, 0 deletions
diff --git a/internal/backup/backup.go b/internal/backup/backup.go
index 9f32ee22e..e58fc8718 100644
--- a/internal/backup/backup.go
+++ b/internal/backup/backup.go
@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"strings"
+ "time"
"gitlab.com/gitlab-org/gitaly/v16/internal/git"
"gitlab.com/gitlab-org/gitaly/v16/internal/git/catfile"
@@ -38,6 +39,9 @@ type Sink interface {
// GetReader returns a reader that servers the data stored by relativePath.
// If relativePath doesn't exists the ErrDoesntExist will be returned.
GetReader(ctx context.Context, relativePath string) (io.ReadCloser, error)
+ // SignedURL returns a URL that can be used to GET the blob for the duration
+ // specified in expiry.
+ SignedURL(ctx context.Context, relativePath string, expiry time.Duration) (string, error)
}
// Backup represents all the information needed to restore a backup for a repository
diff --git a/internal/backup/filesystem_sink.go b/internal/backup/filesystem_sink.go
index 6d68502db..2b4c04944 100644
--- a/internal/backup/filesystem_sink.go
+++ b/internal/backup/filesystem_sink.go
@@ -7,8 +7,10 @@ import (
"io"
"os"
"path/filepath"
+ "time"
"gitlab.com/gitlab-org/gitaly/v16/internal/helper/perm"
+ "gitlab.com/gitlab-org/gitaly/v16/internal/structerr"
)
// FilesystemSink is a sink for creating and restoring backups from the local filesystem.
@@ -59,3 +61,8 @@ func (fs *FilesystemSink) GetReader(ctx context.Context, relativePath string) (i
func (fs *FilesystemSink) Close() error {
return nil
}
+
+// SignedURL is not supported by FilesystemSink.
+func (fs *FilesystemSink) SignedURL(ctx context.Context, relativePath string, expiry time.Duration) (string, error) {
+ return "", structerr.NewUnimplemented("SignedURL not implemented for FilesystemSink")
+}
diff --git a/internal/backup/sink.go b/internal/backup/sink.go
index a02bab89d..9de909024 100644
--- a/internal/backup/sink.go
+++ b/internal/backup/sink.go
@@ -6,6 +6,7 @@ import (
"io"
"net/url"
"strings"
+ "time"
"gocloud.dev/blob"
"gocloud.dev/blob/azureblob"
@@ -98,3 +99,21 @@ func (s *StorageServiceSink) GetReader(ctx context.Context, relativePath string)
}
return reader, nil
}
+
+// SignedURL returns a URL that can be used to GET the blob for the duration
+// specified in expiry.
+func (s *StorageServiceSink) SignedURL(ctx context.Context, relativePath string, expiry time.Duration) (string, error) {
+ opt := &blob.SignedURLOptions{
+ Expiry: expiry,
+ }
+
+ signed, err := s.bucket.SignedURL(ctx, relativePath, opt)
+ if err != nil {
+ if gcerrors.Code(err) == gcerrors.NotFound {
+ err = ErrDoesntExist
+ }
+ return "", fmt.Errorf("storage service sink: signed URL for %q: %w", relativePath, err)
+ }
+
+ return signed, err
+}
diff --git a/internal/backup/sink_test.go b/internal/backup/sink_test.go
index a2d24cbe8..9ff6f3f8a 100644
--- a/internal/backup/sink_test.go
+++ b/internal/backup/sink_test.go
@@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"testing"
+ "time"
"github.com/stretchr/testify/require"
"gitlab.com/gitlab-org/gitaly/v16/internal/helper/perm"
@@ -147,3 +148,50 @@ func TestStorageServiceSink(t *testing.T) {
require.Nil(t, reader)
})
}
+
+func TestStorageServiceSink_SignedURL_notImplemented(t *testing.T) {
+ t.Parallel()
+
+ ctx := testhelper.Context(t)
+ tmpDir := testhelper.TempDir(t)
+
+ for _, tc := range []struct {
+ desc string
+ bucketURL string
+ }{
+ {
+ desc: "memory bucket",
+ bucketURL: "mem://test_bucket",
+ },
+ {
+ desc: "fs bucket",
+ bucketURL: tmpDir,
+ },
+ } {
+ tc := tc
+
+ t.Run(tc.desc, func(t *testing.T) {
+ t.Parallel()
+
+ sss, err := ResolveSink(ctx, tc.bucketURL)
+ require.NoError(t, err)
+ t.Cleanup(func() { require.NoError(t, sss.Close()) })
+
+ const relativePath = "path/to/data"
+
+ data := []byte("test")
+
+ w, err := sss.GetWriter(ctx, relativePath)
+ require.NoError(t, err)
+
+ _, err = io.Copy(w, bytes.NewReader(data))
+ require.NoError(t, err)
+
+ require.NoError(t, w.Close())
+
+ _, err = sss.SignedURL(ctx, relativePath, 10*time.Minute)
+ require.Error(t, err)
+ require.Contains(t, err.Error(), "not implemented")
+ })
+ }
+}