-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
samples(storage): add folder samples (#4178)
Adds samples to create, get, list, rename, and delete folders. Closes googleapis/google-cloud-go#10294 ## Checklist - [x] I have followed [Contributing Guidelines from CONTRIBUTING.MD](https://togithub.com/GoogleCloudPlatform/golang-samples/blob/main/CONTRIBUTING.md) - [x] **Tests** pass: `go test -v ./..` (see [Testing](https://togithub.com/GoogleCloudPlatform/golang-samples/blob/main/CONTRIBUTING.md#testing)) - [x] **Code formatted**: `gofmt` (see [Formatting](https://togithub.com/GoogleCloudPlatform/golang-samples/blob/main/CONTRIBUTING.md#formatting)) - [x] **Vetting** pass: `go vet` (see [Formatting](https://togithub.com/GoogleCloudPlatform/golang-samples/blob/main/CONTRIBUTING.md#formatting)) - [ ] These samples need a new **API enabled** in testing projects to pass (let us know which ones) - [ ] These samples need a new/updated **env vars** in testing projects set to pass (let us know which ones) - [ ] This sample adds a new sample directory, and I updated the [CODEOWNERS file](https://togithub.com/GoogleCloudPlatform/golang-samples/blob/main/.github/CODEOWNERS) with the codeowners for this sample - [ ] This sample adds a new **Product API**, and I updated the [Blunderbuss issue/PR auto-assigner](https://togithub.com/GoogleCloudPlatform/golang-samples/blob/main/.github/blunderbuss.yml) with the codeowners for this sample - [ ] Please **merge** this PR for me once it is approved
- Loading branch information
Showing
6 changed files
with
449 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// Copyright 2024 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package control | ||
|
||
// [START storage_control_create_folder] | ||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"time" | ||
|
||
control "cloud.google.com/go/storage/control/apiv2" | ||
"cloud.google.com/go/storage/control/apiv2/controlpb" | ||
) | ||
|
||
// createFolder creates a folder in the bucket with the given name. | ||
func createFolder(w io.Writer, bucket, folder string) error { | ||
// bucket := "bucket-name" | ||
// folder := "folder-name" | ||
|
||
ctx := context.Background() | ||
client, err := control.NewStorageControlClient(ctx) | ||
if err != nil { | ||
return fmt.Errorf("NewStorageControlClient: %w", err) | ||
} | ||
defer client.Close() | ||
|
||
ctx, cancel := context.WithTimeout(ctx, time.Second*30) | ||
defer cancel() | ||
|
||
req := &controlpb.CreateFolderRequest{ | ||
Parent: fmt.Sprintf("projects/_/buckets/%v", bucket), | ||
FolderId: folder, | ||
} | ||
f, err := client.CreateFolder(ctx, req) | ||
if err != nil { | ||
return fmt.Errorf("CreateFolder(%q): %w", folder, err) | ||
} | ||
|
||
fmt.Fprintf(w, "created folder with path %q", f.Name) | ||
return nil | ||
} | ||
|
||
// [END storage_control_create_folder] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
// Copyright 2024 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package control | ||
|
||
// [START storage_control_delete_folder] | ||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"time" | ||
|
||
control "cloud.google.com/go/storage/control/apiv2" | ||
"cloud.google.com/go/storage/control/apiv2/controlpb" | ||
) | ||
|
||
// deleteFolder deletes the folder with the given name. | ||
func deleteFolder(w io.Writer, bucket, folder string) error { | ||
// bucket := "bucket-name" | ||
// folder := "folder-name" | ||
|
||
ctx := context.Background() | ||
client, err := control.NewStorageControlClient(ctx) | ||
if err != nil { | ||
return fmt.Errorf("NewStorageControlClient: %w", err) | ||
} | ||
defer client.Close() | ||
|
||
ctx, cancel := context.WithTimeout(ctx, time.Second*30) | ||
defer cancel() | ||
|
||
// Construct folder path including the bucket name. | ||
folderPath := fmt.Sprintf("projects/_/buckets/%v/folders/%v", bucket, folder) | ||
|
||
req := &controlpb.DeleteFolderRequest{ | ||
Name: folderPath, | ||
} | ||
if err := client.DeleteFolder(ctx, req); err != nil { | ||
return fmt.Errorf("DeleteFolder(%q): %w", folderPath, err) | ||
} | ||
|
||
fmt.Fprintf(w, "deleted folder %q", folderPath) | ||
return nil | ||
} | ||
|
||
// [END storage_control_delete_folder] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
// Copyright 2024 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package control | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"log" | ||
"os" | ||
"strings" | ||
"testing" | ||
"time" | ||
|
||
"cloud.google.com/go/storage" | ||
"github.com/GoogleCloudPlatform/golang-samples/internal/testutil" | ||
) | ||
|
||
const ( | ||
testPrefix = "storage-control-test" | ||
bucketExpiryAge = time.Hour * 24 | ||
) | ||
|
||
var ( | ||
client *storage.Client | ||
) | ||
|
||
func TestMain(m *testing.M) { | ||
// Create shared storage client | ||
ctx := context.Background() | ||
c, err := storage.NewClient(ctx) | ||
if err != nil { | ||
log.Fatalf("storage.NewClient: %v", err) | ||
} | ||
defer c.Close() | ||
client = c | ||
|
||
// Run tests | ||
exit := m.Run() | ||
|
||
// Delete old buckets whose name begins with our test prefix | ||
tc, _ := testutil.ContextMain(m) | ||
|
||
if err := testutil.DeleteExpiredBuckets(c, tc.ProjectID, testPrefix, bucketExpiryAge); err != nil { | ||
// Don't fail the test if cleanup fails | ||
log.Printf("Post-test cleanup failed: %v", err) | ||
} | ||
os.Exit(exit) | ||
} | ||
|
||
func TestFolders(t *testing.T) { | ||
tc := testutil.SystemTest(t) | ||
ctx := context.Background() | ||
|
||
// Create HNS bucket. | ||
bucketName := testutil.UniqueBucketName(testPrefix) | ||
b := client.Bucket(bucketName) | ||
attrs := &storage.BucketAttrs{ | ||
HierarchicalNamespace: &storage.HierarchicalNamespace{ | ||
Enabled: true, | ||
}, | ||
UniformBucketLevelAccess: storage.UniformBucketLevelAccess{ | ||
Enabled: true, | ||
}, | ||
} | ||
if err := b.Create(ctx, tc.ProjectID, attrs); err != nil { | ||
t.Fatalf("Bucket.Create(%q): %v", bucketName, err) | ||
} | ||
t.Cleanup(func() { | ||
if err := testutil.DeleteBucketIfExists(ctx, client, bucketName); err != nil { | ||
log.Printf("Bucket.Delete(%q): %v", bucketName, err) | ||
} | ||
}) | ||
|
||
folderName := "foo" | ||
folderPath := fmt.Sprintf("projects/_/buckets/%v/folders/%v", bucketName, folderName) | ||
newFolderName := "bar" | ||
newFolderPath := fmt.Sprintf("projects/_/buckets/%v/folders/%v", bucketName, newFolderName) | ||
|
||
// Create folder. Retry because there is no automatic retry in the client | ||
// for this op. | ||
if ok := testutil.Retry(t, 5, time.Second, func(r *testutil.R) { | ||
buf := &bytes.Buffer{} | ||
if err := createFolder(buf, bucketName, folderName); err != nil { | ||
r.Errorf("createFolder: %v", err) | ||
} | ||
if got, want := buf.String(), folderPath; !strings.Contains(got, want) { | ||
r.Errorf("createFolder: got %q, want to contain %q", got, want) | ||
} | ||
}); !ok { | ||
t.Fatalf("failed to create folder; can't continue") | ||
} | ||
|
||
// Get folder. Retry because there is no automatic retry in the client | ||
// for this op. | ||
if ok := testutil.Retry(t, 5, time.Second, func(r *testutil.R) { | ||
buf := &bytes.Buffer{} | ||
if err := getFolder(buf, bucketName, folderName); err != nil { | ||
r.Errorf("getFolder: %v", err) | ||
} | ||
if got, want := buf.String(), folderPath; !strings.Contains(got, want) { | ||
r.Errorf("getFolder: got %q, want to contain %q", got, want) | ||
} | ||
}); !ok { | ||
t.Fatalf("failed to get folder; can't continue") | ||
} | ||
|
||
// List folders. | ||
buf := &bytes.Buffer{} | ||
if err := listFolders(buf, bucketName); err != nil { | ||
t.Fatalf("listFolders: %v", err) | ||
} | ||
if got, want := buf.String(), folderPath; !strings.Contains(got, want) { | ||
t.Errorf("listFolders: got %q, want to contain %q", got, want) | ||
} | ||
|
||
// Rename folder. | ||
buf = &bytes.Buffer{} | ||
if err := renameFolder(buf, bucketName, folderName, newFolderName); err != nil { | ||
t.Fatalf("renameFolder: %v", err) | ||
} | ||
if got, want := buf.String(), newFolderPath; !strings.Contains(got, want) { | ||
t.Errorf("listFolders: got %q, want to contain %q", got, want) | ||
} | ||
|
||
// Delete folder. | ||
buf = &bytes.Buffer{} | ||
if err := deleteFolder(buf, bucketName, newFolderName); err != nil { | ||
t.Fatalf("deleteFolder: %v", err) | ||
} | ||
if got, want := buf.String(), newFolderPath; !strings.Contains(got, want) { | ||
t.Errorf("deleteFolder: got %q, want to contain %q", got, want) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// Copyright 2024 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package control | ||
|
||
// [START storage_control_get_folder] | ||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"time" | ||
|
||
control "cloud.google.com/go/storage/control/apiv2" | ||
"cloud.google.com/go/storage/control/apiv2/controlpb" | ||
) | ||
|
||
// getFolder gets metadata for the folder with the given name. | ||
func getFolder(w io.Writer, bucket, folder string) error { | ||
// bucket := "bucket-name" | ||
// folder := "folder-name" | ||
|
||
ctx := context.Background() | ||
client, err := control.NewStorageControlClient(ctx) | ||
if err != nil { | ||
return fmt.Errorf("NewStorageControlClient: %w", err) | ||
} | ||
defer client.Close() | ||
|
||
ctx, cancel := context.WithTimeout(ctx, time.Second*30) | ||
defer cancel() | ||
|
||
// Construct folder path including the bucket name. | ||
folderPath := fmt.Sprintf("projects/_/buckets/%v/folders/%v", bucket, folder) | ||
|
||
req := &controlpb.GetFolderRequest{ | ||
Name: folderPath, | ||
} | ||
f, err := client.GetFolder(ctx, req) | ||
if err != nil { | ||
return fmt.Errorf("GetFolder(%q): %w", folderPath, err) | ||
} | ||
|
||
fmt.Fprintf(w, "got folder metadata: %+v", f) | ||
return nil | ||
} | ||
|
||
// [END storage_control_get_folder] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// Copyright 2024 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
package control | ||
|
||
// [START storage_control_list_folders] | ||
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"time" | ||
|
||
control "cloud.google.com/go/storage/control/apiv2" | ||
"cloud.google.com/go/storage/control/apiv2/controlpb" | ||
"google.golang.org/api/iterator" | ||
) | ||
|
||
// listFolders lists all folders present in the bucket. | ||
func listFolders(w io.Writer, bucket string) error { | ||
// bucket := "bucket-name" | ||
// folder := "folder-name" | ||
|
||
ctx := context.Background() | ||
client, err := control.NewStorageControlClient(ctx) | ||
if err != nil { | ||
return fmt.Errorf("NewStorageControlClient: %w", err) | ||
} | ||
defer client.Close() | ||
|
||
ctx, cancel := context.WithTimeout(ctx, time.Second*30) | ||
defer cancel() | ||
|
||
// Construct bucket path for a bucket containing folders. | ||
bucketPath := fmt.Sprintf("projects/_/buckets/%v", bucket) | ||
|
||
// List all folders present. | ||
req := &controlpb.ListFoldersRequest{ | ||
Parent: bucketPath, | ||
} | ||
it := client.ListFolders(ctx, req) | ||
for { | ||
f, err := it.Next() | ||
if err == iterator.Done { | ||
break | ||
} | ||
if err != nil { | ||
return fmt.Errorf("ListFolders(%q): %w", bucketPath, err) | ||
} | ||
fmt.Fprintf(w, "got folder %v\n", f.Name) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// [END storage_control_list_folders] |
Oops, something went wrong.