github.com/matrixorigin/matrixone@v0.7.0/pkg/fileservice/s3_fs_test.go (about) 1 // Copyright 2022 Matrix Origin 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package fileservice 16 17 import ( 18 "context" 19 "encoding/csv" 20 "encoding/json" 21 "errors" 22 "os" 23 "os/exec" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/aws/aws-sdk-go-v2/aws" 29 "github.com/aws/aws-sdk-go-v2/config" 30 "github.com/aws/aws-sdk-go-v2/service/s3" 31 "github.com/stretchr/testify/assert" 32 ) 33 34 type _TestS3Config struct { 35 Endpoint string `json:"s3-test-endpoint"` 36 Region string `json:"s3-test-region"` 37 APIKey string `json:"s3-test-key"` 38 APISecret string `json:"s3-test-secret"` 39 Bucket string `json:"s3-test-bucket"` 40 RoleARN string `json:"role-arn"` 41 } 42 43 func loadS3TestConfig() (config _TestS3Config, err error) { 44 45 // load from s3.json 46 content, err := os.ReadFile("s3.json") 47 if err != nil { 48 if os.IsNotExist(err) { 49 err = nil 50 } else { 51 return config, err 52 } 53 } 54 if len(content) > 0 { 55 err := json.Unmarshal(content, &config) 56 if err != nil { 57 return config, err 58 } 59 } 60 61 // load from env 62 loadEnv := func(name string, ptr *string) { 63 if *ptr != "" { 64 return 65 } 66 if value := os.Getenv(name); value != "" { 67 *ptr = value 68 } 69 } 70 loadEnv("endpoint", &config.Endpoint) 71 loadEnv("region", &config.Region) 72 loadEnv("apikey", &config.APIKey) 73 loadEnv("apisecret", &config.APISecret) 74 loadEnv("bucket", &config.Bucket) 75 76 return 77 } 78 79 func TestS3FS(t *testing.T) { 80 config, err := loadS3TestConfig() 81 assert.Nil(t, err) 82 if config.Endpoint == "" { 83 // no config 84 t.Skip() 85 } 86 87 t.Setenv("AWS_REGION", config.Region) 88 t.Setenv("AWS_ACCESS_KEY_ID", config.APIKey) 89 t.Setenv("AWS_SECRET_ACCESS_KEY", config.APISecret) 90 91 t.Run("file service", func(t *testing.T) { 92 cacheDir := t.TempDir() 93 testFileService(t, func(name string) FileService { 94 95 fs, err := NewS3FS( 96 "", 97 name, 98 config.Endpoint, 99 config.Bucket, 100 time.Now().Format("2006-01-02.15:04:05.000000"), 101 -1, 102 -1, 103 cacheDir, 104 ) 105 assert.Nil(t, err) 106 107 return fs 108 }) 109 }) 110 111 t.Run("list root", func(t *testing.T) { 112 cacheDir := t.TempDir() 113 fs, err := NewS3FS( 114 "", 115 "s3", 116 config.Endpoint, 117 config.Bucket, 118 "", 119 -1, 120 -1, 121 cacheDir, 122 ) 123 assert.Nil(t, err) 124 ctx := context.Background() 125 entries, err := fs.List(ctx, "") 126 assert.Nil(t, err) 127 assert.True(t, len(entries) > 0) 128 }) 129 130 t.Run("caching file service", func(t *testing.T) { 131 cacheDir := t.TempDir() 132 testCachingFileService(t, func() CachingFileService { 133 fs, err := NewS3FS( 134 "", 135 "s3", 136 config.Endpoint, 137 config.Bucket, 138 time.Now().Format("2006-01-02.15:04:05.000000"), 139 128*1024, 140 128*1024, 141 cacheDir, 142 ) 143 assert.Nil(t, err) 144 return fs 145 }) 146 }) 147 148 } 149 150 func TestDynamicS3(t *testing.T) { 151 config, err := loadS3TestConfig() 152 assert.Nil(t, err) 153 if config.Endpoint == "" { 154 // no config 155 t.Skip() 156 } 157 testFileService(t, func(name string) FileService { 158 buf := new(strings.Builder) 159 w := csv.NewWriter(buf) 160 err := w.Write([]string{ 161 "s3", 162 config.Endpoint, 163 config.Region, 164 config.Bucket, 165 config.APIKey, 166 config.APISecret, 167 time.Now().Format("2006-01-02.15:04:05.000000"), 168 name, 169 }) 170 assert.Nil(t, err) 171 w.Flush() 172 fs, path, err := GetForETL(nil, JoinPath( 173 buf.String(), 174 "foo/bar/baz", 175 )) 176 assert.Nil(t, err) 177 assert.Equal(t, path, "foo/bar/baz") 178 return fs 179 }) 180 } 181 182 func TestDynamicS3NoKey(t *testing.T) { 183 config, err := loadS3TestConfig() 184 assert.Nil(t, err) 185 if config.Endpoint == "" { 186 // no config 187 t.Skip() 188 } 189 t.Setenv("AWS_REGION", config.Region) 190 t.Setenv("AWS_ACCESS_KEY_ID", config.APIKey) 191 t.Setenv("AWS_SECRET_ACCESS_KEY", config.APISecret) 192 testFileService(t, func(name string) FileService { 193 buf := new(strings.Builder) 194 w := csv.NewWriter(buf) 195 err := w.Write([]string{ 196 "s3-no-key", 197 config.Endpoint, 198 config.Region, 199 config.Bucket, 200 time.Now().Format("2006-01-02.15:04:05.000000"), 201 name, 202 }) 203 assert.Nil(t, err) 204 w.Flush() 205 fs, path, err := GetForETL(nil, JoinPath( 206 buf.String(), 207 "foo/bar/baz", 208 )) 209 assert.Nil(t, err) 210 assert.Equal(t, path, "foo/bar/baz") 211 return fs 212 }) 213 } 214 215 func TestDynamicS3Opts(t *testing.T) { 216 config, err := loadS3TestConfig() 217 assert.Nil(t, err) 218 if config.Endpoint == "" { 219 // no config 220 t.Skip() 221 } 222 t.Setenv("AWS_REGION", config.Region) 223 t.Setenv("AWS_ACCESS_KEY_ID", config.APIKey) 224 t.Setenv("AWS_SECRET_ACCESS_KEY", config.APISecret) 225 testFileService(t, func(name string) FileService { 226 buf := new(strings.Builder) 227 w := csv.NewWriter(buf) 228 err := w.Write([]string{ 229 "s3-opts", 230 "endpoint=" + config.Endpoint, 231 "region=" + config.Region, 232 "bucket=" + config.Bucket, 233 "prefix=" + time.Now().Format("2006-01-02.15:04:05.000000"), 234 "name=" + name, 235 }) 236 assert.Nil(t, err) 237 w.Flush() 238 fs, path, err := GetForETL(nil, JoinPath( 239 buf.String(), 240 "foo/bar/baz", 241 )) 242 assert.Nil(t, err) 243 assert.Equal(t, path, "foo/bar/baz") 244 return fs 245 }) 246 } 247 248 func TestDynamicS3OptsRoleARN(t *testing.T) { 249 config, err := loadS3TestConfig() 250 assert.Nil(t, err) 251 if config.Endpoint == "" { 252 // no config 253 t.Skip() 254 } 255 t.Setenv("AWS_REGION", config.Region) 256 t.Setenv("AWS_ACCESS_KEY_ID", config.APIKey) 257 t.Setenv("AWS_SECRET_ACCESS_KEY", config.APISecret) 258 testFileService(t, func(name string) FileService { 259 buf := new(strings.Builder) 260 w := csv.NewWriter(buf) 261 err := w.Write([]string{ 262 "s3-opts", 263 "endpoint=" + config.Endpoint, 264 "bucket=" + config.Bucket, 265 "prefix=" + time.Now().Format("2006-01-02.15:04:05.000000"), 266 "name=" + name, 267 "role-arn=" + config.RoleARN, 268 }) 269 assert.Nil(t, err) 270 w.Flush() 271 fs, path, err := GetForETL(nil, JoinPath( 272 buf.String(), 273 "foo/bar/baz", 274 )) 275 if err != nil { 276 t.Fatal(err) 277 } 278 assert.Equal(t, path, "foo/bar/baz") 279 return fs 280 }) 281 } 282 283 func TestDynamicS3OptsNoRegion(t *testing.T) { 284 config, err := loadS3TestConfig() 285 assert.Nil(t, err) 286 if config.Endpoint == "" { 287 // no config 288 t.Skip() 289 } 290 t.Setenv("AWS_REGION", "") 291 t.Setenv("AWS_ACCESS_KEY_ID", config.APIKey) 292 t.Setenv("AWS_SECRET_ACCESS_KEY", config.APISecret) 293 testFileService(t, func(name string) FileService { 294 buf := new(strings.Builder) 295 w := csv.NewWriter(buf) 296 err := w.Write([]string{ 297 "s3-opts", 298 "bucket=" + config.Bucket, 299 "prefix=" + time.Now().Format("2006-01-02.15:04:05.000000"), 300 "name=" + name, 301 "role-arn=" + config.RoleARN, 302 }) 303 assert.Nil(t, err) 304 w.Flush() 305 fs, path, err := GetForETL(nil, JoinPath( 306 buf.String(), 307 "foo/bar/baz", 308 )) 309 if err != nil { 310 t.Fatal(err) 311 } 312 assert.Equal(t, path, "foo/bar/baz") 313 return fs 314 }) 315 } 316 317 func TestS3FSMinioServer(t *testing.T) { 318 319 // find minio executable 320 exePath, err := exec.LookPath("minio") 321 if errors.Is(err, exec.ErrNotFound) { 322 // minio not found in machine 323 return 324 } 325 326 // start minio 327 ctx, cancel := context.WithCancel(context.Background()) 328 defer cancel() 329 cmd := exec.CommandContext(ctx, 330 exePath, 331 "server", 332 t.TempDir(), 333 //"--certs-dir", filepath.Join("testdata", "minio-certs"), 334 ) 335 cmd.Env = append(os.Environ(), 336 "MINIO_SITE_NAME=test", 337 "MINIO_SITE_REGION=test", 338 ) 339 //cmd.Stderr = os.Stderr 340 //cmd.Stdout = os.Stdout 341 err = cmd.Start() 342 assert.Nil(t, err) 343 344 // set s3 credentials 345 t.Setenv("AWS_REGION", "test") 346 t.Setenv("AWS_ACCESS_KEY_ID", "minioadmin") 347 t.Setenv("AWS_SECRET_ACCESS_KEY", "minioadmin") 348 349 endpoint := "http://localhost:9000" 350 351 // create bucket 352 ctx, cancel = context.WithTimeout(ctx, time.Second*59) 353 defer cancel() 354 cfg, err := config.LoadDefaultConfig(ctx) 355 assert.Nil(t, err) 356 client := s3.NewFromConfig(cfg, 357 s3.WithEndpointResolver( 358 s3.EndpointResolverFunc( 359 func( 360 region string, 361 options s3.EndpointResolverOptions, 362 ) ( 363 ep aws.Endpoint, 364 err error, 365 ) { 366 _ = options 367 ep.URL = endpoint 368 ep.Source = aws.EndpointSourceCustom 369 ep.HostnameImmutable = true 370 ep.SigningRegion = region 371 return 372 }, 373 ), 374 ), 375 ) 376 _, err = client.CreateBucket(ctx, &s3.CreateBucketInput{ 377 Bucket: ptrTo("test"), 378 }) 379 assert.Nil(t, err) 380 381 // run test 382 t.Run("file service", func(t *testing.T) { 383 cacheDir := t.TempDir() 384 testFileService(t, func(name string) FileService { 385 386 fs, err := NewS3FSOnMinio( 387 "", 388 name, 389 endpoint, 390 "test", 391 time.Now().Format("2006-01-02.15:04:05.000000"), 392 -1, 393 -1, 394 cacheDir, 395 ) 396 assert.Nil(t, err) 397 398 return fs 399 }) 400 }) 401 402 } 403 404 func BenchmarkS3FS(b *testing.B) { 405 config, err := loadS3TestConfig() 406 assert.Nil(b, err) 407 if config.Endpoint == "" { 408 // no config 409 b.Skip() 410 } 411 412 b.Setenv("AWS_REGION", config.Region) 413 b.Setenv("AWS_ACCESS_KEY_ID", config.APIKey) 414 b.Setenv("AWS_SECRET_ACCESS_KEY", config.APISecret) 415 416 cacheDir := b.TempDir() 417 418 b.ResetTimer() 419 420 benchmarkFileService(b, func() FileService { 421 fs, err := NewS3FS( 422 "", 423 "s3", 424 config.Endpoint, 425 config.Bucket, 426 time.Now().Format("2006-01-02.15:04:05.000000"), 427 -1, 428 -1, 429 cacheDir, 430 ) 431 assert.Nil(b, err) 432 return fs 433 }) 434 }