| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | package s3_test | 
					
						
							| 
									
										
										
										
											2016-01-02 14:38:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 	"context" | 
					
						
							|  |  |  | 	"crypto/rand" | 
					
						
							|  |  |  | 	"encoding/hex" | 
					
						
							| 
									
										
										
										
											2017-05-11 22:48:46 +02:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 	"io" | 
					
						
							|  |  |  | 	"net" | 
					
						
							| 
									
										
										
										
											2023-06-17 15:25:08 +02:00
										 |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 	"os/exec" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-08-21 17:46:23 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-01 11:40:12 +02:00
										 |  |  | 	"github.com/restic/restic/internal/backend" | 
					
						
							| 
									
										
										
										
											2023-06-17 15:25:08 +02:00
										 |  |  | 	"github.com/restic/restic/internal/backend/location" | 
					
						
							| 
									
										
										
										
											2017-07-23 14:21:03 +02:00
										 |  |  | 	"github.com/restic/restic/internal/backend/s3" | 
					
						
							|  |  |  | 	"github.com/restic/restic/internal/backend/test" | 
					
						
							| 
									
										
										
										
											2021-08-04 22:56:18 +02:00
										 |  |  | 	"github.com/restic/restic/internal/options" | 
					
						
							| 
									
										
										
										
											2017-10-02 15:06:39 +02:00
										 |  |  | 	rtest "github.com/restic/restic/internal/test" | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2016-01-02 14:38:45 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | func mkdir(t testing.TB, dir string) { | 
					
						
							|  |  |  | 	err := os.MkdirAll(dir, 0700) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | func runMinio(ctx context.Context, t testing.TB, dir, key, secret string) func() { | 
					
						
							|  |  |  | 	mkdir(t, filepath.Join(dir, "config")) | 
					
						
							|  |  |  | 	mkdir(t, filepath.Join(dir, "root")) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cmd := exec.CommandContext(ctx, "minio", | 
					
						
							|  |  |  | 		"server", | 
					
						
							|  |  |  | 		"--address", "127.0.0.1:9000", | 
					
						
							|  |  |  | 		"--config-dir", filepath.Join(dir, "config"), | 
					
						
							|  |  |  | 		filepath.Join(dir, "root")) | 
					
						
							|  |  |  | 	cmd.Env = append(os.Environ(), | 
					
						
							|  |  |  | 		"MINIO_ACCESS_KEY="+key, | 
					
						
							|  |  |  | 		"MINIO_SECRET_KEY="+secret, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	cmd.Stderr = os.Stderr | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err := cmd.Start() | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 		t.Fatal(err) | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 	// wait until the TCP port is reachable | 
					
						
							|  |  |  | 	var success bool | 
					
						
							| 
									
										
										
										
											2017-06-08 20:50:56 +02:00
										 |  |  | 	for i := 0; i < 100; i++ { | 
					
						
							| 
									
										
										
										
											2017-05-12 21:45:18 +02:00
										 |  |  | 		time.Sleep(200 * time.Millisecond) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 		c, err := net.Dial("tcp", "localhost:9000") | 
					
						
							| 
									
										
										
										
											2017-06-11 14:38:16 +02:00
										 |  |  | 		if err == nil { | 
					
						
							|  |  |  | 			success = true | 
					
						
							|  |  |  | 			if err := c.Close(); err != nil { | 
					
						
							|  |  |  | 				t.Fatal(err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			break | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 	if !success { | 
					
						
							|  |  |  | 		t.Fatal("unable to connect to minio server") | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 	return func() { | 
					
						
							|  |  |  | 		err = cmd.Process.Kill() | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 			t.Fatal(err) | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-05-11 22:40:39 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// ignore errors, we've killed the process | 
					
						
							|  |  |  | 		_ = cmd.Wait() | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-13 21:44:19 +02:00
										 |  |  | func newRandomCredentials(t testing.TB) (key, secret string) { | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 	buf := make([]byte, 10) | 
					
						
							|  |  |  | 	_, err := io.ReadFull(rand.Reader, buf) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	key = hex.EncodeToString(buf) | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 	_, err = io.ReadFull(rand.Reader, buf) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	secret = hex.EncodeToString(buf) | 
					
						
							| 
									
										
										
										
											2016-01-23 18:46:04 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 	return key, secret | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 19:35:20 +02:00
										 |  |  | func newMinioTestSuite(t testing.TB) (*test.Suite[s3.Config], func()) { | 
					
						
							| 
									
										
										
										
											2023-06-08 16:53:55 +02:00
										 |  |  | 	ctx, cancel := context.WithCancel(context.Background()) | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 16:53:55 +02:00
										 |  |  | 	tempdir := rtest.TempDir(t) | 
					
						
							|  |  |  | 	key, secret := newRandomCredentials(t) | 
					
						
							|  |  |  | 	cleanup := runMinio(ctx, t, tempdir, key, secret) | 
					
						
							| 
									
										
										
										
											2017-05-11 21:54:04 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 19:35:20 +02:00
										 |  |  | 	return &test.Suite[s3.Config]{ | 
					
						
							|  |  |  | 			// NewConfig returns a config for a new temporary backend that will be used in tests. | 
					
						
							|  |  |  | 			NewConfig: func() (*s3.Config, error) { | 
					
						
							|  |  |  | 				cfg := s3.NewConfig() | 
					
						
							|  |  |  | 				cfg.Endpoint = "localhost:9000" | 
					
						
							|  |  |  | 				cfg.Bucket = "restictestbucket" | 
					
						
							|  |  |  | 				cfg.Prefix = fmt.Sprintf("test-%d", time.Now().UnixNano()) | 
					
						
							|  |  |  | 				cfg.UseHTTP = true | 
					
						
							|  |  |  | 				cfg.KeyID = key | 
					
						
							|  |  |  | 				cfg.Secret = options.NewSecretString(secret) | 
					
						
							|  |  |  | 				return &cfg, nil | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-01 11:40:12 +02:00
										 |  |  | 			Factory: location.NewHTTPBackendFactory("s3", s3.ParseConfig, location.NoPassword, func(ctx context.Context, cfg s3.Config, rt http.RoundTripper) (be backend.Backend, err error) { | 
					
						
							| 
									
										
										
										
											2025-03-22 13:52:17 +01:00
										 |  |  | 				for i := 0; i < 50; i++ { | 
					
						
							| 
									
										
										
										
											2023-06-17 15:25:08 +02:00
										 |  |  | 					be, err = s3.Create(ctx, cfg, rt) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						t.Logf("s3 open: try %d: error %v", i, err) | 
					
						
							|  |  |  | 						time.Sleep(500 * time.Millisecond) | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					break | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return be, err | 
					
						
							|  |  |  | 			}, s3.Open), | 
					
						
							| 
									
										
										
										
											2023-06-08 19:35:20 +02:00
										 |  |  | 		}, func() { | 
					
						
							|  |  |  | 			defer cancel() | 
					
						
							|  |  |  | 			defer cleanup() | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-01-02 14:38:45 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-05-11 22:38:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-13 21:44:19 +02:00
										 |  |  | func TestBackendMinio(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2017-05-13 20:41:04 +02:00
										 |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if t.Skipped() { | 
					
						
							| 
									
										
										
										
											2017-10-02 15:06:39 +02:00
										 |  |  | 			rtest.SkipDisallowed(t, "restic/backend/s3.TestBackendMinio") | 
					
						
							| 
									
										
										
										
											2017-05-13 20:41:04 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-13 21:44:19 +02:00
										 |  |  | 	// try to find a minio binary | 
					
						
							|  |  |  | 	_, err := exec.LookPath("minio") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Skip(err) | 
					
						
							|  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2017-05-11 22:38:25 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 19:35:20 +02:00
										 |  |  | 	suite, cleanup := newMinioTestSuite(t) | 
					
						
							| 
									
										
										
										
											2023-06-08 16:53:55 +02:00
										 |  |  | 	defer cleanup() | 
					
						
							| 
									
										
										
										
											2017-05-13 21:44:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 16:53:55 +02:00
										 |  |  | 	suite.RunTests(t) | 
					
						
							| 
									
										
										
										
											2017-05-13 21:44:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func BenchmarkBackendMinio(t *testing.B) { | 
					
						
							|  |  |  | 	// try to find a minio binary | 
					
						
							|  |  |  | 	_, err := exec.LookPath("minio") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Skip(err) | 
					
						
							|  |  |  | 		return | 
					
						
							| 
									
										
										
										
											2017-05-11 22:38:25 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 19:35:20 +02:00
										 |  |  | 	suite, cleanup := newMinioTestSuite(t) | 
					
						
							| 
									
										
										
										
											2023-06-08 16:53:55 +02:00
										 |  |  | 	defer cleanup() | 
					
						
							| 
									
										
										
										
											2017-05-13 21:44:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 16:53:55 +02:00
										 |  |  | 	suite.RunBenchmarks(t) | 
					
						
							| 
									
										
										
										
											2017-05-13 21:44:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 19:35:20 +02:00
										 |  |  | func newS3TestSuite() *test.Suite[s3.Config] { | 
					
						
							| 
									
										
										
										
											2023-04-21 21:06:56 +02:00
										 |  |  | 	return &test.Suite[s3.Config]{ | 
					
						
							| 
									
										
										
										
											2017-05-11 22:38:25 +02:00
										 |  |  | 		// do not use excessive data | 
					
						
							|  |  |  | 		MinimalData: true, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// NewConfig returns a config for a new temporary backend that will be used in tests. | 
					
						
							| 
									
										
										
										
											2023-04-21 21:35:34 +02:00
										 |  |  | 		NewConfig: func() (*s3.Config, error) { | 
					
						
							| 
									
										
										
										
											2023-04-20 22:40:21 +02:00
										 |  |  | 			cfg, err := s3.ParseConfig(os.Getenv("RESTIC_TEST_S3_REPOSITORY")) | 
					
						
							| 
									
										
										
										
											2017-05-11 22:38:25 +02:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2023-04-21 21:35:34 +02:00
										 |  |  | 				return nil, err | 
					
						
							| 
									
										
										
										
											2017-05-11 22:38:25 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			cfg.KeyID = os.Getenv("RESTIC_TEST_S3_KEY") | 
					
						
							| 
									
										
										
										
											2021-08-04 22:56:18 +02:00
										 |  |  | 			cfg.Secret = options.NewSecretString(os.Getenv("RESTIC_TEST_S3_SECRET")) | 
					
						
							| 
									
										
										
										
											2017-05-11 22:48:46 +02:00
										 |  |  | 			cfg.Prefix = fmt.Sprintf("test-%d", time.Now().UnixNano()) | 
					
						
							| 
									
										
										
										
											2017-05-11 22:38:25 +02:00
										 |  |  | 			return cfg, nil | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-08 16:53:55 +02:00
										 |  |  | 		Factory: s3.NewFactory(), | 
					
						
							| 
									
										
										
										
											2017-05-11 22:38:25 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-13 21:44:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestBackendS3(t *testing.T) { | 
					
						
							|  |  |  | 	defer func() { | 
					
						
							|  |  |  | 		if t.Skipped() { | 
					
						
							| 
									
										
										
										
											2017-10-02 15:06:39 +02:00
										 |  |  | 			rtest.SkipDisallowed(t, "restic/backend/s3.TestBackendS3") | 
					
						
							| 
									
										
										
										
											2017-05-13 21:44:19 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := []string{ | 
					
						
							|  |  |  | 		"RESTIC_TEST_S3_KEY", | 
					
						
							|  |  |  | 		"RESTIC_TEST_S3_SECRET", | 
					
						
							|  |  |  | 		"RESTIC_TEST_S3_REPOSITORY", | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, v := range vars { | 
					
						
							|  |  |  | 		if os.Getenv(v) == "" { | 
					
						
							|  |  |  | 			t.Skipf("environment variable %v not set", v) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	t.Logf("run tests") | 
					
						
							| 
									
										
										
										
											2023-06-08 19:35:20 +02:00
										 |  |  | 	newS3TestSuite().RunTests(t) | 
					
						
							| 
									
										
										
										
											2017-05-13 21:44:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func BenchmarkBackendS3(t *testing.B) { | 
					
						
							|  |  |  | 	vars := []string{ | 
					
						
							|  |  |  | 		"RESTIC_TEST_S3_KEY", | 
					
						
							|  |  |  | 		"RESTIC_TEST_S3_SECRET", | 
					
						
							|  |  |  | 		"RESTIC_TEST_S3_REPOSITORY", | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, v := range vars { | 
					
						
							|  |  |  | 		if os.Getenv(v) == "" { | 
					
						
							|  |  |  | 			t.Skipf("environment variable %v not set", v) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-05-11 22:38:25 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	t.Logf("run tests") | 
					
						
							| 
									
										
										
										
											2023-06-08 19:35:20 +02:00
										 |  |  | 	newS3TestSuite().RunBenchmarks(t) | 
					
						
							| 
									
										
										
										
											2017-05-11 22:38:25 +02:00
										 |  |  | } |