mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
* GCS uploader: Add tests Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com> * Use go generate Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
165 lines
3.7 KiB
Go
165 lines
3.7 KiB
Go
package gcs
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"cloud.google.com/go/storage"
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/grafana/grafana/pkg/ifaces/gcsifaces"
|
|
"github.com/grafana/grafana/pkg/mocks/mock_gcsifaces"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/oauth2/google"
|
|
"golang.org/x/oauth2/jwt"
|
|
"google.golang.org/api/option"
|
|
)
|
|
|
|
const dfltExpiration = 7 * 24 * time.Hour
|
|
|
|
type testConfig struct {
|
|
signedURL string
|
|
}
|
|
|
|
func mockSDK(ctx context.Context, t *testing.T, content []byte, bucket string, signed bool) testConfig {
|
|
t.Helper()
|
|
|
|
var cfg testConfig
|
|
|
|
ctrl := gomock.NewController(t)
|
|
t.Cleanup(func() {
|
|
ctrl.Finish()
|
|
})
|
|
|
|
wm := mock_gcsifaces.NewMockStorageWriter(ctrl)
|
|
if !signed {
|
|
wm.
|
|
EXPECT().
|
|
SetACL(gomock.Eq("publicRead")).
|
|
Return()
|
|
}
|
|
wm.EXPECT().
|
|
Write(gomock.Eq(content)).
|
|
Return(len(content), nil)
|
|
wm.EXPECT().
|
|
Close()
|
|
|
|
om := mock_gcsifaces.NewMockStorageObject(ctrl)
|
|
om.
|
|
EXPECT().
|
|
NewWriter(gomock.Eq(ctx)).
|
|
Return(wm)
|
|
|
|
bm := mock_gcsifaces.NewMockStorageBucket(ctrl)
|
|
bm.
|
|
EXPECT().
|
|
Object(gomock.Any()).
|
|
Return(om)
|
|
|
|
cm := mock_gcsifaces.NewMockStorageClient(ctrl)
|
|
cm.
|
|
EXPECT().
|
|
Bucket(gomock.Eq(bucket)).
|
|
Return(bm)
|
|
|
|
if signed {
|
|
const scope = storage.ScopeReadWrite
|
|
cfg.signedURL = "https://google.com/signed"
|
|
|
|
creds := &google.Credentials{
|
|
JSON: []byte(`{}`),
|
|
}
|
|
conf := &jwt.Config{
|
|
Email: "test@grafana.com",
|
|
PrivateKey: []byte("private"),
|
|
}
|
|
suOpts := &storage.SignedURLOptions{
|
|
Scheme: storage.SigningSchemeV4,
|
|
Method: "GET",
|
|
GoogleAccessID: conf.Email,
|
|
PrivateKey: conf.PrivateKey,
|
|
Expires: time.Now().Add(dfltExpiration),
|
|
}
|
|
cm.
|
|
EXPECT().
|
|
FindDefaultCredentials(gomock.Eq(ctx), gomock.Eq(scope)).
|
|
Return(creds, nil)
|
|
cm.
|
|
EXPECT().
|
|
JWTConfigFromJSON(gomock.Eq(creds.JSON)).
|
|
Return(conf, nil)
|
|
cm.
|
|
EXPECT().
|
|
SignedURL(gomock.Eq(bucket), gomock.Any(), signedURLOptsMatcher{suOpts}).
|
|
Return(cfg.signedURL, nil)
|
|
}
|
|
|
|
origNewClient := newClient
|
|
t.Cleanup(func() {
|
|
newClient = origNewClient
|
|
})
|
|
newClient = func(ctx context.Context, options ...option.ClientOption) (gcsifaces.StorageClient, error) {
|
|
return cm, nil
|
|
}
|
|
|
|
return cfg
|
|
}
|
|
|
|
func TestUploadToGCS_DefaultCredentials(t *testing.T) {
|
|
const bucket = "test"
|
|
content := []byte("test\n")
|
|
tmpDir := t.TempDir()
|
|
fpath := filepath.Join(tmpDir, "test.png")
|
|
err := ioutil.WriteFile(fpath, content, 0600)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("Without signed URL", func(t *testing.T) {
|
|
ctx := context.Background()
|
|
mockSDK(ctx, t, content, bucket, false)
|
|
|
|
uploader, err := NewUploader("", bucket, "", false, dfltExpiration)
|
|
require.NoError(t, err)
|
|
|
|
path, err := uploader.Upload(ctx, fpath)
|
|
require.NoError(t, err)
|
|
|
|
assert.Regexp(t, fmt.Sprintf(`^https://storage.googleapis.com/%s/[^/]+\.png$`, bucket), path)
|
|
})
|
|
|
|
t.Run("With signed URL", func(t *testing.T) {
|
|
ctx := context.Background()
|
|
cfg := mockSDK(ctx, t, content, bucket, true)
|
|
|
|
uploader, err := NewUploader("", bucket, "", true, dfltExpiration)
|
|
require.NoError(t, err)
|
|
|
|
path, err := uploader.Upload(ctx, fpath)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, cfg.signedURL, path)
|
|
})
|
|
}
|
|
|
|
type signedURLOptsMatcher struct {
|
|
opts *storage.SignedURLOptions
|
|
}
|
|
|
|
func (m signedURLOptsMatcher) Matches(x interface{}) bool {
|
|
suOpts, ok := x.(*storage.SignedURLOptions)
|
|
if !ok {
|
|
return false
|
|
}
|
|
|
|
return suOpts.Scheme == m.opts.Scheme && suOpts.Method == m.opts.Method && suOpts.GoogleAccessID ==
|
|
m.opts.GoogleAccessID && bytes.Equal(suOpts.PrivateKey, m.opts.PrivateKey)
|
|
}
|
|
|
|
func (m signedURLOptsMatcher) String() string {
|
|
return "Matches two SignedURLOptions"
|
|
}
|