Repository: fsouza/fake-gcs-server (go)

Issue:
Error when attempting to delete a missing bucket
When attempting to delete a missing bucket, the server panics:

```DELETE https://localhost:4443/storage/v1/b/my_bucket```

```
2023-01-09 10:26:13 2023/01/09 08:26:13 http: panic serving 172.17.0.1:45708: runtime error: invalid memory address or nil pointer dereference
2023-01-09 10:26:13 goroutine 22 [running]:
2023-01-09 10:26:13 net/http.(*conn).serve.func1()
2023-01-09 10:26:13     /usr/local/go/src/net/http/server.go:1850 +0xbf
2023-01-09 10:26:13 panic({0xabdaa0, 0x112e340})
2023-01-09 10:26:13     /usr/local/go/src/runtime/panic.go:890 +0x262
2023-01-09 10:26:13 github.com/fsouza/fake-gcs-server/internal/backend.(*storageFS).ListObjects.func1({0xc000038138?, 0x12}, {0x0, 0x0}, {0x13?, 0x2?})
2023-01-09 10:26:13     /code/internal/backend/fs.go:237 +0xca
2023-01-09 10:26:13 path/filepath.Walk({0xc000038138, 0x12}, 0xc00010f0b8)
2023-01-09 10:26:13     /usr/local/go/src/path/filepath/path.go:518 +0x50
2023-01-09 10:26:13 github.com/fsouza/fake-gcs-server/internal/backend.(*storageFS).ListObjects(0xc00012acc0, {0xc0000aa555, 0x9}, {0x0, 0x0}, 0xba?)
2023-01-09 10:26:13     /code/internal/backend/fs.go:233 +0x1fe
2023-01-09 10:26:13 github.com/fsouza/fake-gcs-server/internal/backend.(*storageFS).DeleteBucket(0xc00012acc0, {0xc0000aa555, 0x9})
2023-01-09 10:26:13     /code/internal/backend/fs.go:131 +0x65
2023-01-09 10:26:13 github.com/fsouza/fake-gcs-server/fakestorage.(*Server).deleteBucket(0xc000102d00, 0x17?)
2023-01-09 10:26:13     /code/fakestorage/bucket.go:118 +0x76
2023-01-09 10:26:13 github.com/fsouza/fake-gcs-server/fakestorage.jsonToHTTPHandler.func1({0xcba270, 0xc000524ba0}, 0xc000216600?)
2023-01-09 10:26:13     /code/fakestorage/json_response.go:24 +0x45
2023-01-09 10:26:13 net/http.HandlerFunc.ServeHTTP(0xc00048c400?, {0xcba270?, 0xc000524ba0?}, 0xcb9160?)
2023-01-09 10:26:13     /usr/local/go/src/net/http/server.go:2109 +0x2f
2023-01-09 10:26:13 github.com/gorilla/mux.(*Router).ServeHTTP(0xc0003ac480, {0xcba270, 0xc000524ba0}, 0xc00048c300)
2023-01-09 10:26:13     /go/pkg/mod/github.com/gorilla/mux@v1.8.0/mux.go:210 +0x1cf
2023-01-09 10:26:13 github.com/gorilla/handlers.(*cors).ServeHTTP(0xc0000ecf30, {0xcba270, 0xc000524ba0}, 0xc00048c300)
2023-01-09 10:26:13     /go/pkg/mod/github.com/gorilla/handlers@v1.5.1/cors.go:54 +0x370
2023-01-09 10:26:13 github.com/gorilla/handlers.loggingHandler.ServeHTTP({{0xcb5540, 0xc000386190}, {0xcb3c20, 0xc0000ecf30}, 0xbf3088}, {0xcb9880, 0xc000284000}, 0xc00048c300)
2023-01-09 10:26:13     /go/pkg/mod/github.com/gorilla/handlers@v1.5.1/logging.go:47 +0xef
2023-01-09 10:26:13 github.com/fsouza/fake-gcs-server/fakestorage.requestCompressHandler.func1({0xcb9880, 0xc000284000}, 0xc00048c300)
2023-01-09 10:26:13     /code/fakestorage/server.go:432 +0x116
2023-01-09 10:26:13 net/http.HandlerFunc.ServeHTTP(0x0?, {0xcb9880?, 0xc000284000?}, 0x4648ae?)
2023-01-09 10:26:13     /usr/local/go/src/net/http/server.go:2109 +0x2f
2023-01-09 10:26:13 net/http.serverHandler.ServeHTTP({0xc000216480?}, {0xcb9880, 0xc000284000}, 0xc00048c300)
2023-01-09 10:26:13     /usr/local/go/src/net/http/server.go:2947 +0x30c
2023-01-09 10:26:13 net/http.(*conn).serve(0xc00024a000, {0xcba870, 0xc0002160c0})
2023-01-09 10:26:13     /usr/local/go/src/net/http/server.go:1991 +0x607
2023-01-09 10:26:13 created by net/http.(*Server).Serve
2023-01-09 10:26:13     /usr/local/go/src/net/http/server.go:3102 +0x4db
```

I am using the default docker backend (filesystem), the OS is windows though docker.

Pull request:
Fix deletion of non-existent buckets in the filesystem backend
Also enables the filesystem backend in more tests.

Closes #1034.

Patch:
diff --git a/internal/backend/fs.go b/internal/backend/fs.go
index dc70cfb..d3fde1a 100644
--- a/internal/backend/fs.go
+++ b/internal/backend/fs.go
@@ -228,12 +228,13 @@ func (s *storageFS) ListObjects(bucketName string, prefix string, versions bool)
 	defer s.mtx.RUnlock()
 
 	objects := []ObjectAttrs{}
-	// TODO: WalkDir more efficient?
 	bucketPath := filepath.Join(s.rootDir, url.PathEscape(bucketName))
-	if err := filepath.Walk(bucketPath, func(path string, info fs.FileInfo, _ error) error {
-		// Rel() should never return error since path always descend from bucketPath
+	if err := filepath.Walk(bucketPath, func(path string, info fs.FileInfo, err error) error {
+		if err != nil {
+			return err
+		}
+
 		objName, _ := filepath.Rel(bucketPath, path)
-		// TODO: should this check path?
 		if s.mh.isSpecialFile(info.Name()) {
 			return nil
 		}


Test patch:
diff --git a/fakestorage/bucket_test.go b/fakestorage/bucket_test.go
index 2146a98..ff16623 100644
--- a/fakestorage/bucket_test.go
+++ b/fakestorage/bucket_test.go
@@ -97,7 +97,7 @@ func TestServerClientBucketAttrsAfterCreateBucket(t *testing.T) {
 func TestServerClientDeleteBucket(t *testing.T) {
 	t.Run("it deletes empty buckets", func(t *testing.T) {
 		const bucketName = "bucket-to-delete"
-		runServersTest(t, runServersOptions{}, func(t *testing.T, server *Server) {
+		runServersTest(t, runServersOptions{enableFSBackend: true}, func(t *testing.T, server *Server) {
 			server.CreateBucketWithOpts(CreateBucketOpts{Name: bucketName})
 			client := server.Client()
 			err := client.Bucket(bucketName).Delete(context.Background())
@@ -117,7 +117,7 @@ func TestServerClientDeleteBucket(t *testing.T) {
 	t.Run("it returns an error for non-empty buckets", func(t *testing.T) {
 		const bucketName = "non-empty-bucket"
 		objs := []Object{{ObjectAttrs: ObjectAttrs{BucketName: bucketName, Name: "static/js/app.js"}}}
-		runServersTest(t, runServersOptions{objs: objs}, func(t *testing.T, server *Server) {
+		runServersTest(t, runServersOptions{objs: objs, enableFSBackend: true}, func(t *testing.T, server *Server) {
 			client := server.Client()
 			err := client.Bucket(bucketName).Delete(context.Background())
 			if err == nil {
@@ -128,7 +128,7 @@ func TestServerClientDeleteBucket(t *testing.T) {
 
 	t.Run("it returns an error for unknown buckets", func(t *testing.T) {
 		const bucketName = "non-existent-bucket"
-		runServersTest(t, runServersOptions{}, func(t *testing.T, server *Server) {
+		runServersTest(t, runServersOptions{enableFSBackend: true}, func(t *testing.T, server *Server) {
 			client := server.Client()
 			err := client.Bucket(bucketName).Delete(context.Background())
 			if err == nil {