diff --git a/storage/control/anywhere_cache_test.go b/storage/control/anywhere_cache_test.go new file mode 100644 index 0000000000..ac599e36bf --- /dev/null +++ b/storage/control/anywhere_cache_test.go @@ -0,0 +1,148 @@ +// Copyright 2026 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" + "os" + "strings" + "testing" + "time" + + "cloud.google.com/go/storage" + "github.com/GoogleCloudPlatform/golang-samples/internal/testutil" +) + +func TestAnywhereCache(t *testing.T) { + tc := testutil.SystemTest(t) + ctx := context.Background() + + zone := os.Getenv("GOOGLE_SAMPLES_ZONE") + if zone == "" { + zone = "us-central1-a" + } + + // Create bucket with UBLA enabled as required for Anywhere Cache. + bucketName := testutil.UniqueBucketName(testPrefix) + b := client.Bucket(bucketName) + attrs := &storage.BucketAttrs{ + 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() { + testutil.DeleteBucketIfExists(ctx, client, bucketName) + }) + + cacheName := fmt.Sprintf("buckets/%v/anywhereCaches/%v", bucketName, zone) + + // Create Anywhere Cache. + // Using partial match for assertions as per memory guidelines. + if ok := testutil.Retry(t, 5, time.Second, func(r *testutil.R) { + buf := &bytes.Buffer{} + if err := createAnywhereCache(buf, bucketName, zone); err != nil { + r.Errorf("createAnywhereCache: %v", err) + } + if got, want := buf.String(), cacheName; !strings.Contains(got, want) { + r.Errorf("createAnywhereCache: got %q, want to contain %q", got, want) + } + }); !ok { + t.Fatalf("failed to create anywhere cache; can't continue") + } + + // Get Anywhere Cache. + fullCacheName := fmt.Sprintf("projects/_/buckets/%v/anywhereCaches/%v", bucketName, zone) + if ok := testutil.Retry(t, 5, time.Second, func(r *testutil.R) { + buf := &bytes.Buffer{} + if err := getAnywhereCache(buf, fullCacheName); err != nil { + r.Errorf("getAnywhereCache: %v", err) + } + if got, want := buf.String(), cacheName; !strings.Contains(got, want) { + r.Errorf("getAnywhereCache: got %q, want to contain %q", got, want) + } + }); !ok { + t.Fatalf("failed to get anywhere cache; can't continue") + } + + // List Anywhere Caches. + if ok := testutil.Retry(t, 5, time.Second, func(r *testutil.R) { + buf := &bytes.Buffer{} + if err := listAnywhereCaches(buf, bucketName); err != nil { + r.Errorf("listAnywhereCaches: %v", err) + } + if got, want := buf.String(), cacheName; !strings.Contains(got, want) { + r.Errorf("listAnywhereCaches: got %q, want to contain %q", got, want) + } + }); !ok { + t.Fatalf("failed to list anywhere caches; can't continue") + } + + // Update Anywhere Cache. + if ok := testutil.Retry(t, 5, time.Second, func(r *testutil.R) { + buf := &bytes.Buffer{} + if err := updateAnywhereCache(buf, fullCacheName, "admit-on-second-miss"); err != nil { + r.Errorf("updateAnywhereCache: %v", err) + } + if got, want := buf.String(), cacheName; !strings.Contains(got, want) { + r.Errorf("updateAnywhereCache: got %q, want to contain %q", got, want) + } + }); !ok { + t.Fatalf("failed to update anywhere cache; can't continue") + } + + // Pause Anywhere Cache. + if ok := testutil.Retry(t, 5, time.Second, func(r *testutil.R) { + buf := &bytes.Buffer{} + if err := pauseAnywhereCache(buf, fullCacheName); err != nil { + r.Errorf("pauseAnywhereCache: %v", err) + } + if got, want := buf.String(), cacheName; !strings.Contains(got, want) { + r.Errorf("pauseAnywhereCache: got %q, want to contain %q", got, want) + } + }); !ok { + t.Fatalf("failed to pause anywhere cache; can't continue") + } + + // Resume Anywhere Cache. + if ok := testutil.Retry(t, 5, time.Second, func(r *testutil.R) { + buf := &bytes.Buffer{} + if err := resumeAnywhereCache(buf, fullCacheName); err != nil { + r.Errorf("resumeAnywhereCache: %v", err) + } + if got, want := buf.String(), cacheName; !strings.Contains(got, want) { + r.Errorf("resumeAnywhereCache: got %q, want to contain %q", got, want) + } + }); !ok { + t.Fatalf("failed to resume anywhere cache; can't continue") + } + + // Disable Anywhere Cache. + if ok := testutil.Retry(t, 5, time.Second, func(r *testutil.R) { + buf := &bytes.Buffer{} + if err := disableAnywhereCache(buf, fullCacheName); err != nil { + r.Errorf("disableAnywhereCache: %v", err) + } + if got, want := buf.String(), cacheName; !strings.Contains(got, want) { + r.Errorf("disableAnywhereCache: got %q, want to contain %q", got, want) + } + }); !ok { + t.Fatalf("failed to disable anywhere cache") + } +} diff --git a/storage/control/create_anywhere_cache.go b/storage/control/create_anywhere_cache.go new file mode 100644 index 0000000000..0f17f22395 --- /dev/null +++ b/storage/control/create_anywhere_cache.go @@ -0,0 +1,66 @@ +// Copyright 2026 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_anywhere_cache] +import ( + "context" + "fmt" + "io" + "time" + + storagecontrol "cloud.google.com/go/storage/control/apiv2" + "cloud.google.com/go/storage/control/apiv2/controlpb" +) + +// createAnywhereCache creates an anywhere cache for the given bucket and zone. +func createAnywhereCache(w io.Writer, bucketName, zone string) error { + // bucketName := "bucket-name" + // zone := "us-central1-a" + + ctx := context.Background() + client, err := storagecontrol.NewStorageControlClient(ctx) + if err != nil { + return fmt.Errorf("NewStorageControlClient: %w", err) + } + defer client.Close() + + ctx, cancel := context.WithTimeout(ctx, time.Minute*20) + defer cancel() + + req := &controlpb.CreateAnywhereCacheRequest{ + Parent: fmt.Sprintf("projects/_/buckets/%v", bucketName), + AnywhereCache: &controlpb.AnywhereCache{ + Zone: zone, + }, + } + + // Start a create/update operation and block until it completes. + // Real applications may want to setup a callback, wait on a coroutine, or poll until it completes. + op, err := client.CreateAnywhereCache(ctx, req) + if err != nil { + return fmt.Errorf("CreateAnywhereCache(%q): %w", zone, err) + } + + anywhereCache, err := op.Wait(ctx) + if err != nil { + return fmt.Errorf("Wait: %w", err) + } + + fmt.Fprintf(w, "Created anywhere cache: %v\n", anywhereCache.GetName()) + return nil +} + +// [END storage_control_create_anywhere_cache] diff --git a/storage/control/disable_anywhere_cache.go b/storage/control/disable_anywhere_cache.go new file mode 100644 index 0000000000..e222185319 --- /dev/null +++ b/storage/control/disable_anywhere_cache.go @@ -0,0 +1,55 @@ +// Copyright 2026 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_disable_anywhere_cache] +import ( + "context" + "fmt" + "io" + "time" + + storagecontrol "cloud.google.com/go/storage/control/apiv2" + "cloud.google.com/go/storage/control/apiv2/controlpb" +) + +// disableAnywhereCache disables an anywhere cache. +func disableAnywhereCache(w io.Writer, cacheName string) error { + // cacheName := "projects/_/buckets/bucket-name/anywhereCaches/us-central1-a" + + ctx := context.Background() + client, err := storagecontrol.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.DisableAnywhereCacheRequest{ + Name: cacheName, + } + + anywhereCache, err := client.DisableAnywhereCache(ctx, req) + if err != nil { + return fmt.Errorf("DisableAnywhereCache(%q): %w", cacheName, err) + } + + fmt.Fprintf(w, "Disabled anywhere cache: %v\n", anywhereCache.GetName()) + return nil +} + +// [END storage_control_disable_anywhere_cache] diff --git a/storage/control/get_anywhere_cache.go b/storage/control/get_anywhere_cache.go new file mode 100644 index 0000000000..1dc4e74e0f --- /dev/null +++ b/storage/control/get_anywhere_cache.go @@ -0,0 +1,55 @@ +// Copyright 2026 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_anywhere_cache] +import ( + "context" + "fmt" + "io" + "time" + + storagecontrol "cloud.google.com/go/storage/control/apiv2" + "cloud.google.com/go/storage/control/apiv2/controlpb" +) + +// getAnywhereCache gets an anywhere cache for the given name. +func getAnywhereCache(w io.Writer, cacheName string) error { + // cacheName := "projects/_/buckets/bucket-name/anywhereCaches/us-central1-a" + + ctx := context.Background() + client, err := storagecontrol.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.GetAnywhereCacheRequest{ + Name: cacheName, + } + + anywhereCache, err := client.GetAnywhereCache(ctx, req) + if err != nil { + return fmt.Errorf("GetAnywhereCache(%q): %w", cacheName, err) + } + + fmt.Fprintf(w, "Got anywhere cache: %v\n", anywhereCache.GetName()) + return nil +} + +// [END storage_control_get_anywhere_cache] diff --git a/storage/control/list_anywhere_caches.go b/storage/control/list_anywhere_caches.go new file mode 100644 index 0000000000..88a5b1c1ee --- /dev/null +++ b/storage/control/list_anywhere_caches.go @@ -0,0 +1,62 @@ +// Copyright 2026 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_anywhere_caches] +import ( + "context" + "fmt" + "io" + "time" + + storagecontrol "cloud.google.com/go/storage/control/apiv2" + "cloud.google.com/go/storage/control/apiv2/controlpb" + "google.golang.org/api/iterator" +) + +// listAnywhereCaches lists anywhere caches for the given bucket. +func listAnywhereCaches(w io.Writer, bucketName string) error { + // bucketName := "bucket-name" + + ctx := context.Background() + client, err := storagecontrol.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.ListAnywhereCachesRequest{ + Parent: fmt.Sprintf("projects/_/buckets/%v", bucketName), + } + + it := client.ListAnywhereCaches(ctx, req) + for { + anywhereCache, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + return fmt.Errorf("it.Next: %w", err) + } + fmt.Fprintf(w, "%v\n", anywhereCache.GetName()) + } + + return nil +} + +// [END storage_control_list_anywhere_caches] diff --git a/storage/control/pause_anywhere_cache.go b/storage/control/pause_anywhere_cache.go new file mode 100644 index 0000000000..c77eb03f03 --- /dev/null +++ b/storage/control/pause_anywhere_cache.go @@ -0,0 +1,55 @@ +// Copyright 2026 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_pause_anywhere_cache] +import ( + "context" + "fmt" + "io" + "time" + + storagecontrol "cloud.google.com/go/storage/control/apiv2" + "cloud.google.com/go/storage/control/apiv2/controlpb" +) + +// pauseAnywhereCache pauses an anywhere cache. +func pauseAnywhereCache(w io.Writer, cacheName string) error { + // cacheName := "projects/_/buckets/bucket-name/anywhereCaches/us-central1-a" + + ctx := context.Background() + client, err := storagecontrol.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.PauseAnywhereCacheRequest{ + Name: cacheName, + } + + anywhereCache, err := client.PauseAnywhereCache(ctx, req) + if err != nil { + return fmt.Errorf("PauseAnywhereCache(%q): %w", cacheName, err) + } + + fmt.Fprintf(w, "Paused anywhere cache: %v\n", anywhereCache.GetName()) + return nil +} + +// [END storage_control_pause_anywhere_cache] diff --git a/storage/control/resume_anywhere_cache.go b/storage/control/resume_anywhere_cache.go new file mode 100644 index 0000000000..13cfca3f09 --- /dev/null +++ b/storage/control/resume_anywhere_cache.go @@ -0,0 +1,55 @@ +// Copyright 2026 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_resume_anywhere_cache] +import ( + "context" + "fmt" + "io" + "time" + + storagecontrol "cloud.google.com/go/storage/control/apiv2" + "cloud.google.com/go/storage/control/apiv2/controlpb" +) + +// resumeAnywhereCache resumes an anywhere cache. +func resumeAnywhereCache(w io.Writer, cacheName string) error { + // cacheName := "projects/_/buckets/bucket-name/anywhereCaches/us-central1-a" + + ctx := context.Background() + client, err := storagecontrol.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.ResumeAnywhereCacheRequest{ + Name: cacheName, + } + + anywhereCache, err := client.ResumeAnywhereCache(ctx, req) + if err != nil { + return fmt.Errorf("ResumeAnywhereCache(%q): %w", cacheName, err) + } + + fmt.Fprintf(w, "Resumed anywhere cache: %v\n", anywhereCache.GetName()) + return nil +} + +// [END storage_control_resume_anywhere_cache] diff --git a/storage/control/update_anywhere_cache.go b/storage/control/update_anywhere_cache.go new file mode 100644 index 0000000000..316bb86e68 --- /dev/null +++ b/storage/control/update_anywhere_cache.go @@ -0,0 +1,70 @@ +// Copyright 2026 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_update_anywhere_cache] +import ( + "context" + "fmt" + "io" + "time" + + storagecontrol "cloud.google.com/go/storage/control/apiv2" + "cloud.google.com/go/storage/control/apiv2/controlpb" + "google.golang.org/genproto/protobuf/field_mask" +) + +// updateAnywhereCache updates an anywhere cache. +func updateAnywhereCache(w io.Writer, cacheName, admissionPolicy string) error { + // cacheName := "projects/_/buckets/bucket-name/anywhereCaches/us-central1-a" + // admissionPolicy := "admit-on-second-miss" + + ctx := context.Background() + client, err := storagecontrol.NewStorageControlClient(ctx) + if err != nil { + return fmt.Errorf("NewStorageControlClient: %w", err) + } + defer client.Close() + + ctx, cancel := context.WithTimeout(ctx, time.Minute*20) + defer cancel() + + req := &controlpb.UpdateAnywhereCacheRequest{ + AnywhereCache: &controlpb.AnywhereCache{ + Name: cacheName, + AdmissionPolicy: admissionPolicy, + }, + UpdateMask: &field_mask.FieldMask{ + Paths: []string{"admission_policy"}, + }, + } + + // Start a create/update operation and block until it completes. + // Real applications may want to setup a callback, wait on a coroutine, or poll until it completes. + op, err := client.UpdateAnywhereCache(ctx, req) + if err != nil { + return fmt.Errorf("UpdateAnywhereCache(%q): %w", cacheName, err) + } + + anywhereCache, err := op.Wait(ctx) + if err != nil { + return fmt.Errorf("Wait: %w", err) + } + + fmt.Fprintf(w, "Updated anywhere cache: %v\n", anywhereCache.GetName()) + return nil +} + +// [END storage_control_update_anywhere_cache]