storagedriver/s3: Added Delete tests to s3_test
(cherry picked from commit 1e3b6b67a8e6d7f01307518370f0731212935d05) Signed-off-by: Collin Shoop <cshoop@digitalocean.com>master
							parent
							
								
									38ab4c606e
								
							
						
					
					
						commit
						05a258e711
					
				| 
						 | 
					@ -2,11 +2,12 @@ package s3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"math/rand"
 | 
						"math/rand"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"reflect"
 | 
					 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/check.v1"
 | 
						"gopkg.in/check.v1"
 | 
				
			||||||
| 
						 | 
					@ -271,6 +272,276 @@ func TestStorageClass(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestPopulate(t *testing.T) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if skipS3() != "" {
 | 
				
			||||||
 | 
							t.Skip(skipS3())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rootDir, err := ioutil.TempDir("", "driver-")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error creating temporary directory: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.Remove(rootDir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						driver, err := s3DriverConstructor(rootDir, s3.StorageClassStandard)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error creating driver with standard storage: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var objs = []string{
 | 
				
			||||||
 | 
							"/file1",
 | 
				
			||||||
 | 
							"/file1-2",
 | 
				
			||||||
 | 
							"/file1/2",
 | 
				
			||||||
 | 
							"/folder1/file1",
 | 
				
			||||||
 | 
							"/folder2/file1",
 | 
				
			||||||
 | 
							"/folder3/file1",
 | 
				
			||||||
 | 
							"/folder3/subfolder1/subfolder1/file1",
 | 
				
			||||||
 | 
							"/folder3/subfolder2/subfolder1/file1",
 | 
				
			||||||
 | 
							"/folder4/file1",
 | 
				
			||||||
 | 
							"/folder1-v2/file1",
 | 
				
			||||||
 | 
							"/folder1-v2/subfolder1/file1",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						init := func() []string {
 | 
				
			||||||
 | 
							// init file structure matching objs above
 | 
				
			||||||
 | 
							var created []string
 | 
				
			||||||
 | 
							for _, path := range objs {
 | 
				
			||||||
 | 
								err := driver.PutContent(context.Background(), path, []byte("content "+path))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									fmt.Printf("unable to init file %s: %s\n", path, err)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								created = append(created, path)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return created
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						init()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestDelete(t *testing.T) {
 | 
				
			||||||
 | 
						if skipS3() != "" {
 | 
				
			||||||
 | 
							t.Skip(skipS3())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rootDir, err := ioutil.TempDir("", "driver-")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error creating temporary directory: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer os.Remove(rootDir)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						driver, err := s3DriverConstructor(rootDir, s3.StorageClassStandard)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("unexpected error creating driver with standard storage: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var objs = []string{
 | 
				
			||||||
 | 
							"/file1",
 | 
				
			||||||
 | 
							"/file1-2",
 | 
				
			||||||
 | 
							"/file1/2",
 | 
				
			||||||
 | 
							"/folder1/file1",
 | 
				
			||||||
 | 
							"/folder2/file1",
 | 
				
			||||||
 | 
							"/folder3/file1",
 | 
				
			||||||
 | 
							"/folder3/subfolder1/subfolder1/file1",
 | 
				
			||||||
 | 
							"/folder3/subfolder2/subfolder1/file1",
 | 
				
			||||||
 | 
							"/folder4/file1",
 | 
				
			||||||
 | 
							"/folder1-v2/file1",
 | 
				
			||||||
 | 
							"/folder1-v2/subfolder1/file1",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// objects to skip auto-created test case
 | 
				
			||||||
 | 
						var skipCase = map[string]bool{
 | 
				
			||||||
 | 
							// special case where deleting "/file1" also deletes "/file1/2" is tested explicitly
 | 
				
			||||||
 | 
							"/file1": true,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						type errFn func(error) bool
 | 
				
			||||||
 | 
						type testCase struct {
 | 
				
			||||||
 | 
							name     string
 | 
				
			||||||
 | 
							delete   string
 | 
				
			||||||
 | 
							expected []string
 | 
				
			||||||
 | 
							// error validation function
 | 
				
			||||||
 | 
							err errFn
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errPathNotFound := func(err error) bool {
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							switch err.(type) {
 | 
				
			||||||
 | 
							case storagedriver.PathNotFoundError:
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						errInvalidPath := func(err error) bool {
 | 
				
			||||||
 | 
							if err == nil {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							switch err.(type) {
 | 
				
			||||||
 | 
							case storagedriver.InvalidPathError:
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tcs := []testCase{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								// special case where a given path is a file and has subpaths
 | 
				
			||||||
 | 
								name:   "delete file1",
 | 
				
			||||||
 | 
								delete: "/file1",
 | 
				
			||||||
 | 
								expected: []string{
 | 
				
			||||||
 | 
									"/file1",
 | 
				
			||||||
 | 
									"/file1/2",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:   "delete folder1",
 | 
				
			||||||
 | 
								delete: "/folder1",
 | 
				
			||||||
 | 
								expected: []string{
 | 
				
			||||||
 | 
									"/folder1/file1",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:   "delete folder2",
 | 
				
			||||||
 | 
								delete: "/folder2",
 | 
				
			||||||
 | 
								expected: []string{
 | 
				
			||||||
 | 
									"/folder2/file1",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:   "delete folder3",
 | 
				
			||||||
 | 
								delete: "/folder3",
 | 
				
			||||||
 | 
								expected: []string{
 | 
				
			||||||
 | 
									"/folder3/file1",
 | 
				
			||||||
 | 
									"/folder3/subfolder1/subfolder1/file1",
 | 
				
			||||||
 | 
									"/folder3/subfolder2/subfolder1/file1",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "delete path that doesn't exist",
 | 
				
			||||||
 | 
								delete:   "/path/does/not/exist",
 | 
				
			||||||
 | 
								expected: []string{},
 | 
				
			||||||
 | 
								err:      errPathNotFound,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "delete path invalid: trailing slash",
 | 
				
			||||||
 | 
								delete:   "/path/is/invalid/",
 | 
				
			||||||
 | 
								expected: []string{},
 | 
				
			||||||
 | 
								err:      errInvalidPath,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name:     "delete path invalid: trailing special character",
 | 
				
			||||||
 | 
								delete:   "/path/is/invalid*",
 | 
				
			||||||
 | 
								expected: []string{},
 | 
				
			||||||
 | 
								err:      errInvalidPath,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// init a test case for each file
 | 
				
			||||||
 | 
						for _, path := range objs {
 | 
				
			||||||
 | 
							if skipCase[path] {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							tcs = append(tcs, testCase{
 | 
				
			||||||
 | 
								name:     fmt.Sprintf("delete path:'%s'", path),
 | 
				
			||||||
 | 
								delete:   path,
 | 
				
			||||||
 | 
								expected: []string{path},
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						init := func() []string {
 | 
				
			||||||
 | 
							// init file structure matching objs
 | 
				
			||||||
 | 
							var created []string
 | 
				
			||||||
 | 
							for _, path := range objs {
 | 
				
			||||||
 | 
								err := driver.PutContent(context.Background(), path, []byte("content "+path))
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									fmt.Printf("unable to init file %s: %s\n", path, err)
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								created = append(created, path)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return created
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						cleanup := func(objs []string) {
 | 
				
			||||||
 | 
							var lastErr error
 | 
				
			||||||
 | 
							for _, path := range objs {
 | 
				
			||||||
 | 
								err := driver.Delete(context.Background(), path)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									switch err.(type) {
 | 
				
			||||||
 | 
									case storagedriver.PathNotFoundError:
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									lastErr = err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if lastErr != nil {
 | 
				
			||||||
 | 
								t.Fatalf("cleanup failed: %s", lastErr)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range tcs {
 | 
				
			||||||
 | 
							t.Run(tc.name, func(t *testing.T) {
 | 
				
			||||||
 | 
								objs := init()
 | 
				
			||||||
 | 
								defer cleanup(objs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err := driver.Delete(context.Background(), tc.delete)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if tc.err != nil {
 | 
				
			||||||
 | 
									if err == nil {
 | 
				
			||||||
 | 
										t.Fatalf("expected error")
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if !tc.err(err) {
 | 
				
			||||||
 | 
										t.Fatalf("error does not match expected: %s", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if tc.err == nil && err != nil {
 | 
				
			||||||
 | 
									t.Fatalf("unexpected error: %s", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var issues []string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// validate all files expected to be deleted are deleted
 | 
				
			||||||
 | 
								// and all files not marked for deletion still remain
 | 
				
			||||||
 | 
								expected := tc.expected
 | 
				
			||||||
 | 
								isExpected := func(path string) bool {
 | 
				
			||||||
 | 
									for _, epath := range expected {
 | 
				
			||||||
 | 
										if epath == path {
 | 
				
			||||||
 | 
											return true
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return false
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								for _, path := range objs {
 | 
				
			||||||
 | 
									stat, err := driver.Stat(context.Background(), path)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										switch err.(type) {
 | 
				
			||||||
 | 
										case storagedriver.PathNotFoundError:
 | 
				
			||||||
 | 
											if !isExpected(path) {
 | 
				
			||||||
 | 
												issues = append(issues, fmt.Sprintf("unexpected path was deleted: %s", path))
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											// path was deleted & was supposed to be
 | 
				
			||||||
 | 
											continue
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										t.Fatalf("stat: %s", err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if stat.IsDir() {
 | 
				
			||||||
 | 
										// for special cases where an object path has subpaths (eg /file1)
 | 
				
			||||||
 | 
										// once /file1 is deleted it's now a directory according to stat
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if isExpected(path) {
 | 
				
			||||||
 | 
										issues = append(issues, fmt.Sprintf("expected path was not deleted: %s", path))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if len(issues) > 0 {
 | 
				
			||||||
 | 
									t.Fatalf(strings.Join(issues, "; "))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestOverThousandBlobs(t *testing.T) {
 | 
					func TestOverThousandBlobs(t *testing.T) {
 | 
				
			||||||
	if skipS3() != "" {
 | 
						if skipS3() != "" {
 | 
				
			||||||
		t.Skip(skipS3())
 | 
							t.Skip(skipS3())
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue