131 lines
3.6 KiB
Go
131 lines
3.6 KiB
Go
package http
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"mime"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/influxdata/influxdb/v2"
|
|
influxdbcontext "github.com/influxdata/influxdb/v2/context"
|
|
kithttp "github.com/influxdata/influxdb/v2/kit/transport/http"
|
|
"github.com/influxdata/influxdb/v2/mock"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap/zaptest"
|
|
)
|
|
|
|
func TestBackupMetaService(t *testing.T) {
|
|
ctrlr := gomock.NewController(t)
|
|
backupSvc := mock.NewMockBackupService(ctrlr)
|
|
sqlBackupSvc := mock.NewMockSqlBackupRestoreService(ctrlr)
|
|
bucketManifestWriter := mock.NewMockBucketManifestWriter(ctrlr)
|
|
|
|
b := &BackupBackend{
|
|
BackupService: backupSvc,
|
|
SqlBackupRestoreService: sqlBackupSvc,
|
|
BucketManifestWriter: bucketManifestWriter,
|
|
}
|
|
h := NewBackupHandler(b)
|
|
|
|
rr := httptest.NewRecorder()
|
|
r, err := http.NewRequest(http.MethodGet, "/", nil)
|
|
require.NoError(t, err)
|
|
|
|
backupSvc.EXPECT().
|
|
BackupKVStore(gomock.Any(), gomock.Any()).
|
|
Return(nil)
|
|
|
|
backupSvc.EXPECT().RLockKVStore()
|
|
backupSvc.EXPECT().UnlockKVStore()
|
|
|
|
sqlBackupSvc.EXPECT().
|
|
BackupSqlStore(gomock.Any(), gomock.Any()).
|
|
Return(nil)
|
|
|
|
sqlBackupSvc.EXPECT().RLockSqlStore()
|
|
sqlBackupSvc.EXPECT().RUnlockSqlStore()
|
|
|
|
bucketManifestWriter.EXPECT().
|
|
WriteManifest(gomock.Any(), gomock.Any()).
|
|
Return(nil)
|
|
|
|
h.handleBackupMetadata(rr, r)
|
|
rs := rr.Result()
|
|
require.Equal(t, rs.StatusCode, http.StatusOK)
|
|
|
|
// Parse the multi-part response
|
|
// First get the boundary from the header
|
|
_, params, err := mime.ParseMediaType(rs.Header.Get("Content-Type"))
|
|
require.NoError(t, err)
|
|
mr := multipart.NewReader(rs.Body, params["boundary"])
|
|
|
|
// Go through the parts of the response and verify the part names appear in the correct order
|
|
wantContentTypes := []string{"application/octet-stream", "application/octet-stream", "application/json; charset=utf-8"}
|
|
wantPartNames := []string{"kv", "sql", "buckets"}
|
|
for i := 0; ; i++ {
|
|
p, err := mr.NextPart()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
require.NoError(t, err)
|
|
require.Equal(t, wantContentTypes[i], p.Header.Get("Content-Type"))
|
|
|
|
_, params, err := mime.ParseMediaType(p.Header.Get("Content-Disposition"))
|
|
require.NoError(t, err)
|
|
require.Equal(t, wantPartNames[i], params["name"])
|
|
}
|
|
}
|
|
|
|
func TestRequireOperPermissions(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
permList []influxdb.Permission
|
|
wantStatus int
|
|
wantContentType string
|
|
}{
|
|
{
|
|
"authorized to do the backup",
|
|
influxdb.OperPermissions(),
|
|
http.StatusOK,
|
|
"text/plain; charset=utf-8",
|
|
},
|
|
{
|
|
"not authorized to do the backup",
|
|
influxdb.ReadAllPermissions(),
|
|
http.StatusUnauthorized,
|
|
"application/json; charset=utf-8",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// next will only be called if the authorization allows it. this is a dummy function
|
|
// that will set the content-type header to something other than application/json
|
|
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
|
|
w.Write([]byte("OK"))
|
|
})
|
|
|
|
rr := httptest.NewRecorder()
|
|
|
|
r, err := http.NewRequest(http.MethodGet, "/", nil)
|
|
require.NoError(t, err)
|
|
ctx := influxdbcontext.SetAuthorizer(context.Background(), mock.NewMockAuthorizer(false, tt.permList))
|
|
r = r.WithContext(ctx)
|
|
|
|
h := BackupHandler{
|
|
HTTPErrorHandler: kithttp.NewErrorHandler(zaptest.NewLogger(t)),
|
|
}
|
|
h.requireOperPermissions(next).ServeHTTP(rr, r)
|
|
rs := rr.Result()
|
|
|
|
require.Equal(t, tt.wantStatus, rs.StatusCode)
|
|
require.Equal(t, tt.wantContentType, rs.Header.Get("Content-Type"))
|
|
})
|
|
}
|
|
}
|