From d8c8fdf085052e4944463e80d1dbde46f21ba1c4 Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Thu, 11 Jun 2026 10:55:03 +0000 Subject: [PATCH 1/5] feat(c++): add deleteFolderRecursive sample This adds a sample demonstrating how to recursively delete a folder in a hierarchical namespace bucket. Fixes: b/521168740 --- .../samples/storage_control_folder_samples.cc | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc b/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc index 43e8050f65f4c..0f6315100d3cd 100644 --- a/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc +++ b/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc @@ -76,6 +76,24 @@ void DeleteFolder(google::cloud::storagecontrol_v2::StorageControlClient client, (std::move(client), argv.at(0), argv.at(1)); } +void DeleteFolderRecursive(google::cloud::storagecontrol_v2::StorageControlClient client, + std::vector const& argv) { + // [START storage_control_delete_folder_recursive] + namespace storagecontrol = google::cloud::storagecontrol_v2; + [](storagecontrol::StorageControlClient client, + std::string const& bucket_name, std::string const& folder_id) { + // Set project to "_" to signify globally scoped bucket + auto const name = std::string{"projects/_/buckets/"} + bucket_name + + "/folders/" + folder_id; + auto deleted = client.DeleteFolderRecursive(name).get(); + if (!deleted) throw std::move(deleted).status(); + + std::cout << "Deleted folder: " << name << "\n"; + } + // [END storage_control_delete_folder_recursive] + (std::move(client), argv.at(0), argv.at(1)); +} + void GetFolder(google::cloud::storagecontrol_v2::StorageControlClient client, std::vector const& argv) { // [START storage_control_get_folder] @@ -176,6 +194,10 @@ void AutoRun(std::vector const& argv) { std::cout << "\nRunning DeleteFolder() example" << std::endl; DeleteFolder(client, {bucket_name, dest_folder_id}); + + std::cout << "\nRunning DeleteFolderRecursive() example" << std::endl; + CreateFolder(client, {bucket_name, folder_id}); + DeleteFolderRecursive(client, {bucket_name, folder_id}); } } // namespace @@ -207,6 +229,8 @@ int main(int argc, char* argv[]) { // NOLINT(bugprone-exception-escape) Example example({ make_entry("create-folder", {"bucket-name", "folder-id"}, CreateFolder), make_entry("delete-folder", {"bucket-name", "folder-id"}, DeleteFolder), + make_entry("delete-folder-recursive", {"bucket-name", "folder-id"}, + DeleteFolderRecursive), make_entry("get-folder", {"bucket-name", "folder-id"}, GetFolder), make_entry("list-folders", {"bucket-name"}, ListFolders), make_entry("rename-folder", From ea2fc55814a1e830a2a5d479f339c361b80a57f3 Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Thu, 11 Jun 2026 11:39:51 +0000 Subject: [PATCH 2/5] fix(storagecontrol): resolve PR comments for DeleteFolderRecursive [Generated-by: AI] --- commit_msg.txt | 5 +++++ .../v2/samples/storage_control_folder_samples.cc | 1 + 2 files changed, 6 insertions(+) create mode 100644 commit_msg.txt diff --git a/commit_msg.txt b/commit_msg.txt new file mode 100644 index 0000000000000..7b75b8214f9d0 --- /dev/null +++ b/commit_msg.txt @@ -0,0 +1,5 @@ +feat(c++): add deleteFolderRecursive sample + +This adds a sample demonstrating how to recursively delete a folder in a hierarchical namespace bucket. + +Fixes: b/521168740 diff --git a/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc b/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc index 0f6315100d3cd..19c332bee60ae 100644 --- a/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc +++ b/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc @@ -197,6 +197,7 @@ void AutoRun(std::vector const& argv) { std::cout << "\nRunning DeleteFolderRecursive() example" << std::endl; CreateFolder(client, {bucket_name, folder_id}); + CreateFolder(client, {bucket_name, folder_id + "/subfolder"}); DeleteFolderRecursive(client, {bucket_name, folder_id}); } From 433d4e2e44a6244563d20b543a36499f87746b3f Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Thu, 11 Jun 2026 11:42:15 +0000 Subject: [PATCH 3/5] style(storagecontrol): format DeleteFolderRecursive signature [Generated-by: AI] --- .../v2/samples/storage_control_folder_samples.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc b/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc index 19c332bee60ae..00b29b56a0bc4 100644 --- a/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc +++ b/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc @@ -76,8 +76,9 @@ void DeleteFolder(google::cloud::storagecontrol_v2::StorageControlClient client, (std::move(client), argv.at(0), argv.at(1)); } -void DeleteFolderRecursive(google::cloud::storagecontrol_v2::StorageControlClient client, - std::vector const& argv) { +void DeleteFolderRecursive( + google::cloud::storagecontrol_v2::StorageControlClient client, + std::vector const& argv) { // [START storage_control_delete_folder_recursive] namespace storagecontrol = google::cloud::storagecontrol_v2; [](storagecontrol::StorageControlClient client, From 14cf93f010788eabb8d32a07b566a3ea8410e6c3 Mon Sep 17 00:00:00 2001 From: Nidhi Date: Mon, 15 Jun 2026 11:44:41 +0000 Subject: [PATCH 4/5] fix(ci): address MSVC compiler errors and update samples to use trailing slashes for HNS folders --- ...2_regional_access_boundary_token_manager.h | 13 ++++++------ google/cloud/internal/rest_opentelemetry.cc | 4 ++-- .../samples/storage_control_folder_samples.cc | 20 +++++++++---------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/google/cloud/internal/oauth2_regional_access_boundary_token_manager.h b/google/cloud/internal/oauth2_regional_access_boundary_token_manager.h index 09b48a2ee5e2b..9764474fcc722 100644 --- a/google/cloud/internal/oauth2_regional_access_boundary_token_manager.h +++ b/google/cloud/internal/oauth2_regional_access_boundary_token_manager.h @@ -178,15 +178,16 @@ class RegionalAccessBoundaryTokenManager (void)failed_lookup_cooldown_.get(); } - promise pending_refresh; - pending_refresh_ = pending_refresh.get_future(); + promise pr; + pending_refresh_ = pr.get_future(); + auto p = std::make_shared>(std::move(pr)); auto constexpr kLocation = __func__; - auto pending_refresh_fn = [p = std::move(pending_refresh), + auto pending_refresh_fn = [p, weak = weak_from_this(), request, stub = iam_stub_, retry_policy = retry_policy_->clone(), backoff_policy = backoff_policy_->clone(), - options = options_]() mutable { + options = options_]() { auto refresh_attempt_fn = [stub](rest_internal::RestContext&, Options const&, Request const& request) { return stub->AllowedLocations(request); @@ -209,7 +210,7 @@ class RegionalAccessBoundaryTokenManager self->allowed_locations_ = *allowed_locations; self->expire_time_ = self->clock_->Now() + TokenTtl(); self->failed_lookup_backoff_policy_.reset(); - p.set_value(Status{}); + p->set_value(Status{}); } else { self->allowed_locations_ = AllowedLocationsResponse{}; if (!self->failed_lookup_backoff_policy_) { @@ -219,7 +220,7 @@ class RegionalAccessBoundaryTokenManager self->failed_lookup_cooldown_ = self->background_->cq().MakeRelativeTimer( self->failed_lookup_backoff_policy_->OnCompletion()); - p.set_value(allowed_locations.status()); + p->set_value(allowed_locations.status()); } self->refresh_in_progress_ = false; }; diff --git a/google/cloud/internal/rest_opentelemetry.cc b/google/cloud/internal/rest_opentelemetry.cc index 4d5d1d839b194..fc4f523cbe67e 100644 --- a/google/cloud/internal/rest_opentelemetry.cc +++ b/google/cloud/internal/rest_opentelemetry.cc @@ -17,8 +17,8 @@ #include "google/cloud/internal/rest_context.h" #include "google/cloud/internal/trace_propagator.h" #include "google/cloud/options.h" +#include "google/cloud/internal/absl_str_cat_quiet.h" #include "absl/strings/match.h" -#include "absl/strings/str_cat.h" #include #include #include @@ -81,7 +81,7 @@ opentelemetry::nostd::shared_ptr MakeSpanHttp( {/*sc::kUrlFull=*/"url.full", request.path()}}, options); for (auto const& kv : request.headers()) { - auto const name = "http.request.header." + std::string{kv.first}; + auto const name = "http.request.header." + kv.first.name(); if (kv.second.EmptyValues()) { span->SetAttribute(name, ""); continue; diff --git a/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc b/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc index 00b29b56a0bc4..a0fff4065719e 100644 --- a/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc +++ b/google/cloud/storagecontrol/v2/samples/storage_control_folder_samples.cc @@ -30,11 +30,11 @@ void RemoveStaleFolders( google::cloud::storagecontrol_v2::StorageControlClient client, std::string const& bucket_name, std::string const& prefix, std::chrono::system_clock::time_point created_time_limit) { - std::regex re(prefix + R"re(-[a-z]{32})re"); + std::regex re(prefix + R"re(-[a-z]{32}/?)re"); auto const parent = std::string{"projects/_/buckets/"} + bucket_name; for (auto folder : client.ListFolders(parent)) { if (!folder) throw std::move(folder).status(); - if (!std::regex_match(folder->name(), re)) continue; + if (!std::regex_search(folder->name(), re)) continue; auto const create_time = google::cloud::internal::ToChronoTimePoint(folder->create_time()); if (create_time > created_time_limit) continue; @@ -170,9 +170,6 @@ void AutoRun(std::vector const& argv) { auto const folder_id = prefix + "-" + google::cloud::internal::Sample( generator, 32, "abcdefghijklmnopqrstuvwxyz"); - auto const dest_folder_id = prefix + "-" + - google::cloud::internal::Sample( - generator, 32, "abcdefghijklmnopqrstuvwxyz"); auto const create_time_limit = std::chrono::system_clock::now() - std::chrono::hours(48); // This is the only example that cleans up stale folders. The examples run in @@ -182,24 +179,25 @@ void AutoRun(std::vector const& argv) { RemoveStaleFolders(client, bucket_name, prefix, create_time_limit); std::cout << "\nRunning CreateFolder() example" << std::endl; - CreateFolder(client, {bucket_name, folder_id}); + CreateFolder(client, {bucket_name, folder_id + "/"}); std::cout << "\nRunning GetFolder() example" << std::endl; - GetFolder(client, {bucket_name, folder_id}); + GetFolder(client, {bucket_name, folder_id + "/"}); std::cout << "\nRunning ListFolders() example" << std::endl; ListFolders(client, {bucket_name}); + auto const dest_folder_id = folder_id + "-dest/"; std::cout << "\nRunning RenameFolder() example" << std::endl; - RenameFolder(client, {bucket_name, folder_id, dest_folder_id}); + RenameFolder(client, {bucket_name, folder_id + "/", dest_folder_id}); std::cout << "\nRunning DeleteFolder() example" << std::endl; DeleteFolder(client, {bucket_name, dest_folder_id}); std::cout << "\nRunning DeleteFolderRecursive() example" << std::endl; - CreateFolder(client, {bucket_name, folder_id}); - CreateFolder(client, {bucket_name, folder_id + "/subfolder"}); - DeleteFolderRecursive(client, {bucket_name, folder_id}); + CreateFolder(client, {bucket_name, folder_id + "/"}); + CreateFolder(client, {bucket_name, folder_id + "/subfolder/"}); + DeleteFolderRecursive(client, {bucket_name, folder_id + "/"}); } } // namespace From abd114179b8f389b5aa126d46cfa1447e57ecef3 Mon Sep 17 00:00:00 2001 From: Nidhi Nandwani Date: Tue, 16 Jun 2026 07:02:55 +0000 Subject: [PATCH 5/5] fix(ci): apply style fixes and add missing includes --- .../oauth2_regional_access_boundary_token_manager.h | 6 ++++-- google/cloud/internal/rest_opentelemetry.cc | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/google/cloud/internal/oauth2_regional_access_boundary_token_manager.h b/google/cloud/internal/oauth2_regional_access_boundary_token_manager.h index 9764474fcc722..82329abb73052 100644 --- a/google/cloud/internal/oauth2_regional_access_boundary_token_manager.h +++ b/google/cloud/internal/oauth2_regional_access_boundary_token_manager.h @@ -16,6 +16,7 @@ #define GOOGLE_CLOUD_CPP_GOOGLE_CLOUD_INTERNAL_OAUTH2_REGIONAL_ACCESS_BOUNDARY_TOKEN_MANAGER_H #include "google/cloud/backoff_policy.h" +#include "google/cloud/future.h" #include "google/cloud/internal/clock.h" #include "google/cloud/internal/http_header.h" #include "google/cloud/internal/oauth2_minimal_iam_credentials_rest.h" @@ -28,6 +29,8 @@ #include "google/cloud/version.h" #include "absl/strings/match.h" #include +#include +#include #include namespace google { @@ -182,8 +185,7 @@ class RegionalAccessBoundaryTokenManager pending_refresh_ = pr.get_future(); auto p = std::make_shared>(std::move(pr)); auto constexpr kLocation = __func__; - auto pending_refresh_fn = [p, - weak = weak_from_this(), request, + auto pending_refresh_fn = [p, weak = weak_from_this(), request, stub = iam_stub_, retry_policy = retry_policy_->clone(), backoff_policy = backoff_policy_->clone(), diff --git a/google/cloud/internal/rest_opentelemetry.cc b/google/cloud/internal/rest_opentelemetry.cc index fc4f523cbe67e..6aef44a3900ff 100644 --- a/google/cloud/internal/rest_opentelemetry.cc +++ b/google/cloud/internal/rest_opentelemetry.cc @@ -13,11 +13,11 @@ // limitations under the License. #include "google/cloud/internal/rest_opentelemetry.h" +#include "google/cloud/internal/absl_str_cat_quiet.h" #include "google/cloud/internal/opentelemetry.h" #include "google/cloud/internal/rest_context.h" #include "google/cloud/internal/trace_propagator.h" #include "google/cloud/options.h" -#include "google/cloud/internal/absl_str_cat_quiet.h" #include "absl/strings/match.h" #include #include