github.com/weaviate/weaviate@v1.24.6/test/modules/backup-gcs/backup_backend_test.go (about)

     1  //                           _       _
     2  // __      _____  __ ___   ___  __ _| |_ ___
     3  // \ \ /\ / / _ \/ _` \ \ / / |/ _` | __/ _ \
     4  //  \ V  V /  __/ (_| |\ V /| | (_| | ||  __/
     5  //   \_/\_/ \___|\__,_| \_/ |_|\__,_|\__\___|
     6  //
     7  //  Copyright © 2016 - 2024 Weaviate B.V. All rights reserved.
     8  //
     9  //  CONTACT: hello@weaviate.io
    10  //
    11  
    12  package test
    13  
    14  import (
    15  	"context"
    16  	"encoding/json"
    17  	"fmt"
    18  	"os"
    19  	"path/filepath"
    20  	"testing"
    21  	"time"
    22  
    23  	"github.com/pkg/errors"
    24  	"github.com/sirupsen/logrus"
    25  	logrustest "github.com/sirupsen/logrus/hooks/test"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  	"github.com/weaviate/weaviate/entities/backup"
    29  	"github.com/weaviate/weaviate/entities/moduletools"
    30  	mod "github.com/weaviate/weaviate/modules/backup-gcs"
    31  	"github.com/weaviate/weaviate/test/docker"
    32  	moduleshelper "github.com/weaviate/weaviate/test/helper/modules"
    33  	ubak "github.com/weaviate/weaviate/usecases/backup"
    34  	"github.com/weaviate/weaviate/usecases/config"
    35  )
    36  
    37  func Test_GCSBackend_Backup(t *testing.T) {
    38  	ctx := context.Background()
    39  	compose, err := docker.New().WithGCS().Start(ctx)
    40  	if err != nil {
    41  		t.Fatal(errors.Wrapf(err, "cannot start"))
    42  	}
    43  
    44  	t.Setenv(envGCSEndpoint, compose.GetGCS().URI())
    45  
    46  	t.Run("store backup meta", moduleLevelStoreBackupMeta)
    47  	t.Run("copy objects", moduleLevelCopyObjects)
    48  	t.Run("copy files", moduleLevelCopyFiles)
    49  
    50  	if err := compose.Terminate(ctx); err != nil {
    51  		t.Fatal(errors.Wrapf(err, "failed to terminate test containers"))
    52  	}
    53  }
    54  
    55  func moduleLevelStoreBackupMeta(t *testing.T) {
    56  	testCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    57  	defer cancel()
    58  
    59  	dataDir := t.TempDir()
    60  	className := "BackupClass"
    61  	backupID := "backup_id"
    62  	bucketName := "bucket"
    63  	projectID := "project-id"
    64  	endpoint := os.Getenv(envGCSEndpoint)
    65  	metadataFilename := "backup.json"
    66  	gcsUseAuth := "false"
    67  
    68  	t.Log("setup env")
    69  	t.Setenv(envGCSEndpoint, endpoint)
    70  	t.Setenv(envGCSStorageEmulatorHost, endpoint)
    71  	t.Setenv(envGCSCredentials, "")
    72  	t.Setenv(envGCSProjectID, projectID)
    73  	t.Setenv(envGCSBucket, bucketName)
    74  	t.Setenv(envGCSUseAuth, gcsUseAuth)
    75  	moduleshelper.CreateGCSBucket(testCtx, t, projectID, bucketName)
    76  
    77  	t.Run("store backup meta in gcs", func(t *testing.T) {
    78  		t.Setenv("BACKUP_GCS_BUCKET", bucketName)
    79  		gcs := mod.New()
    80  		err := gcs.Init(testCtx, newFakeModuleParams(dataDir))
    81  		require.Nil(t, err)
    82  
    83  		t.Run("access permissions", func(t *testing.T) {
    84  			err := gcs.Initialize(testCtx, backupID)
    85  			assert.Nil(t, err)
    86  		})
    87  
    88  		t.Run("backup meta does not exist yet", func(t *testing.T) {
    89  			meta, err := gcs.GetObject(testCtx, backupID, metadataFilename)
    90  			assert.Nil(t, meta)
    91  			assert.NotNil(t, err)
    92  			assert.IsType(t, backup.ErrNotFound{}, err)
    93  		})
    94  
    95  		t.Run("put backup meta on backend", func(t *testing.T) {
    96  			desc := &backup.BackupDescriptor{
    97  				StartedAt:   time.Now(),
    98  				CompletedAt: time.Time{},
    99  				ID:          backupID,
   100  				Classes: []backup.ClassDescriptor{
   101  					{
   102  						Name: className,
   103  					},
   104  				},
   105  				Status:  string(backup.Started),
   106  				Version: ubak.Version,
   107  			}
   108  
   109  			b, err := json.Marshal(desc)
   110  			require.Nil(t, err)
   111  
   112  			err = gcs.PutObject(testCtx, backupID, metadataFilename, b)
   113  			require.Nil(t, err)
   114  
   115  			dest := gcs.HomeDir(backupID)
   116  			expected := fmt.Sprintf("gs://%s/%s", bucketName, backupID)
   117  			assert.Equal(t, expected, dest)
   118  		})
   119  
   120  		t.Run("assert backup meta contents", func(t *testing.T) {
   121  			obj, err := gcs.GetObject(testCtx, backupID, metadataFilename)
   122  			require.Nil(t, err)
   123  
   124  			var meta backup.BackupDescriptor
   125  			err = json.Unmarshal(obj, &meta)
   126  			require.Nil(t, err)
   127  			assert.NotEmpty(t, meta.StartedAt)
   128  			assert.Empty(t, meta.CompletedAt)
   129  			assert.Equal(t, meta.Status, string(backup.Started))
   130  			assert.Empty(t, meta.Error)
   131  			assert.Len(t, meta.Classes, 1)
   132  			assert.Equal(t, meta.Classes[0].Name, className)
   133  			assert.Equal(t, meta.Version, ubak.Version)
   134  			assert.Nil(t, meta.Classes[0].Error)
   135  		})
   136  	})
   137  }
   138  
   139  func moduleLevelCopyObjects(t *testing.T) {
   140  	testCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   141  	defer cancel()
   142  
   143  	dataDir := t.TempDir()
   144  	key := "moduleLevelCopyObjects"
   145  	backupID := "backup_id"
   146  	bucketName := "bucket"
   147  	projectID := "project-id"
   148  	endpoint := os.Getenv(envGCSEndpoint)
   149  	gcsUseAuth := "false"
   150  
   151  	t.Log("setup env")
   152  	t.Setenv(envGCSEndpoint, endpoint)
   153  	t.Setenv(envGCSStorageEmulatorHost, endpoint)
   154  	t.Setenv(envGCSCredentials, "")
   155  	t.Setenv(envGCSProjectID, projectID)
   156  	t.Setenv(envGCSBucket, bucketName)
   157  	t.Setenv(envGCSUseAuth, gcsUseAuth)
   158  	moduleshelper.CreateGCSBucket(testCtx, t, projectID, bucketName)
   159  
   160  	t.Run("copy objects", func(t *testing.T) {
   161  		t.Setenv("BACKUP_GCS_BUCKET", bucketName)
   162  		gcs := mod.New()
   163  		err := gcs.Init(testCtx, newFakeModuleParams(dataDir))
   164  		require.Nil(t, err)
   165  
   166  		t.Run("put object to bucket", func(t *testing.T) {
   167  			err := gcs.PutObject(testCtx, backupID, key, []byte("hello"))
   168  			assert.Nil(t, err)
   169  		})
   170  
   171  		t.Run("get object from bucket", func(t *testing.T) {
   172  			meta, err := gcs.GetObject(testCtx, backupID, key)
   173  			assert.Nil(t, err)
   174  			assert.Equal(t, []byte("hello"), meta)
   175  		})
   176  	})
   177  }
   178  
   179  func moduleLevelCopyFiles(t *testing.T) {
   180  	testCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
   181  	defer cancel()
   182  
   183  	dataDir := t.TempDir()
   184  	key := "moduleLevelCopyFiles"
   185  	backupID := "backup_id"
   186  	bucketName := "bucket"
   187  	projectID := "project-id"
   188  	endpoint := os.Getenv(envGCSEndpoint)
   189  	gcsUseAuth := "false"
   190  
   191  	t.Log("setup env")
   192  	t.Setenv(envGCSEndpoint, endpoint)
   193  	t.Setenv(envGCSStorageEmulatorHost, endpoint)
   194  	t.Setenv(envGCSCredentials, "")
   195  	t.Setenv(envGCSProjectID, projectID)
   196  	t.Setenv(envGCSBucket, bucketName)
   197  	t.Setenv(envGCSUseAuth, gcsUseAuth)
   198  	moduleshelper.CreateGCSBucket(testCtx, t, projectID, bucketName)
   199  
   200  	t.Run("copy files", func(t *testing.T) {
   201  		fpaths := moduleshelper.CreateTestFiles(t, dataDir)
   202  		fpath := fpaths[0]
   203  		expectedContents, err := os.ReadFile(fpath)
   204  		require.Nil(t, err)
   205  		require.NotNil(t, expectedContents)
   206  
   207  		t.Setenv("BACKUP_GCS_BUCKET", bucketName)
   208  		gcs := mod.New()
   209  		err = gcs.Init(testCtx, newFakeModuleParams(dataDir))
   210  		require.Nil(t, err)
   211  
   212  		t.Run("verify source data path", func(t *testing.T) {
   213  			assert.Equal(t, dataDir, gcs.SourceDataPath())
   214  		})
   215  
   216  		t.Run("copy file to backend", func(t *testing.T) {
   217  			srcPath, _ := filepath.Rel(dataDir, fpath)
   218  			err := gcs.PutFile(testCtx, backupID, key, srcPath)
   219  			require.Nil(t, err)
   220  
   221  			contents, err := gcs.GetObject(testCtx, backupID, key)
   222  			require.Nil(t, err)
   223  			assert.Equal(t, expectedContents, contents)
   224  		})
   225  
   226  		t.Run("fetch file from backend", func(t *testing.T) {
   227  			destPath := dataDir + "/file_0.copy.db"
   228  
   229  			err := gcs.WriteToFile(testCtx, backupID, key, destPath)
   230  			require.Nil(t, err)
   231  
   232  			contents, err := os.ReadFile(destPath)
   233  			require.Nil(t, err)
   234  			assert.Equal(t, expectedContents, contents)
   235  		})
   236  	})
   237  }
   238  
   239  type fakeModuleParams struct {
   240  	logger   logrus.FieldLogger
   241  	provider fakeStorageProvider
   242  	config   config.Config
   243  }
   244  
   245  func newFakeModuleParams(dataPath string) *fakeModuleParams {
   246  	logger, _ := logrustest.NewNullLogger()
   247  	return &fakeModuleParams{
   248  		logger:   logger,
   249  		provider: fakeStorageProvider{dataPath: dataPath},
   250  	}
   251  }
   252  
   253  func (f *fakeModuleParams) GetStorageProvider() moduletools.StorageProvider {
   254  	return &f.provider
   255  }
   256  
   257  func (f *fakeModuleParams) GetAppState() interface{} {
   258  	return nil
   259  }
   260  
   261  func (f *fakeModuleParams) GetLogger() logrus.FieldLogger {
   262  	return f.logger
   263  }
   264  
   265  func (f *fakeModuleParams) GetConfig() config.Config {
   266  	return f.config
   267  }
   268  
   269  type fakeStorageProvider struct {
   270  	dataPath string
   271  }
   272  
   273  func (f *fakeStorageProvider) Storage(name string) (moduletools.Storage, error) {
   274  	return nil, nil
   275  }
   276  
   277  func (f *fakeStorageProvider) DataPath() string {
   278  	return f.dataPath
   279  }