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 }