From 56b04254371d43c060878c97bec2deb514c40cc2 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Wed, 21 Jan 2026 17:27:41 +0000 Subject: [PATCH 01/18] wip-- create bucket with ip filter working --- .../lib/google/cloud/storage/bucket.rb | 9 + .../lib/google/cloud/storage/project.rb | 8 +- .../samples/acceptance/buckets_test.rb | 1163 +++++++++-------- .../storage_create_bucket_with_ip_filter.rb | 129 ++ 4 files changed, 746 insertions(+), 563 deletions(-) create mode 100644 google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket.rb b/google-cloud-storage/lib/google/cloud/storage/bucket.rb index ca8461354bb7..2be3ade74b38 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket.rb @@ -1410,6 +1410,15 @@ def delete if_metageneration_match: nil, if_metageneration_not_match: nil user_project: user_project end + def ip_filter + @gapi.ip_filter + end + + def ip_filter= new_ip_filter + @gapi.ip_filter = new_ip_filter || {} + patch_gapi! :ip_filter + end + ## # Retrieves a list of files matching the criteria. # diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index 47e4ffc32da4..3d971a53af50 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -477,12 +477,15 @@ def create_bucket bucket_name, user_project: nil, autoclass_enabled: false, enable_object_retention: nil, - hierarchical_namespace: nil + hierarchical_namespace: nil, + ip_filter: nil + params = { name: bucket_name, location: location, custom_placement_config: custom_placement_config, - hierarchical_namespace: hierarchical_namespace + hierarchical_namespace: hierarchical_namespace, + ip_filter: ip_filter }.delete_if { |_, v| v.nil? } new_bucket = Google::Apis::StorageV1::Bucket.new(**params) storage_class = storage_class_for storage_class @@ -496,6 +499,7 @@ def create_bucket bucket_name, b.versioning = versioning unless versioning.nil? b.requester_pays = requester_pays unless requester_pays.nil? b.hierarchical_namespace = hierarchical_namespace unless hierarchical_namespace.nil? + b.ip_filter = ip_filter unless ip_filter.nil? end yield updater if block_given? updater.check_for_changed_labels! diff --git a/google-cloud-storage/samples/acceptance/buckets_test.rb b/google-cloud-storage/samples/acceptance/buckets_test.rb index bb78ba9bbe44..e29644d5d196 100644 --- a/google-cloud-storage/samples/acceptance/buckets_test.rb +++ b/google-cloud-storage/samples/acceptance/buckets_test.rb @@ -21,6 +21,7 @@ require_relative "../storage_create_bucket_class_location" require_relative "../storage_create_bucket_dual_region" require_relative "../storage_create_bucket_hierarchical_namespace" +require_relative "../storage_create_bucket_with_ip_filter" require_relative "../storage_create_bucket_with_object_retention" require_relative "../storage_define_bucket_website_configuration" require_relative "../storage_delete_bucket" @@ -55,6 +56,7 @@ require_relative "../storage_get_autoclass" require_relative "../storage_set_autoclass" require_relative "../storage_move_object" +require 'pry' describe "Buckets Snippets" do let(:storage_client) { Google::Cloud::Storage.new } @@ -62,577 +64,616 @@ let(:retention_period) { rand 1..99 } let(:bucket) { fixture_bucket } - describe "bucket lifecycle" do - it "create_bucket, create_bucket_class_location, list_buckets, get_bucket_metadata, delete_bucket" do - # create_bucket - bucket_name = random_bucket_name - refute storage_client.bucket bucket_name + # describe "bucket lifecycle" do + # it "create_bucket, create_bucket_class_location, list_buckets, get_bucket_metadata, delete_bucket" do + # # create_bucket + # bucket_name = random_bucket_name + # refute storage_client.bucket bucket_name + + # retry_resource_exhaustion do + # assert_output "Created bucket: #{bucket_name}\n" do + # create_bucket bucket_name: bucket_name + # end + # end + + # refute_nil storage_client.bucket bucket_name + + # # create_bucket_class_location + + # secondary_bucket_name = random_bucket_name + # location = "ASIA" + # storage_class = "COLDLINE" + # refute storage_client.bucket secondary_bucket_name + + # retry_resource_exhaustion do + # assert_output "Created bucket #{secondary_bucket_name} in #{location} with #{storage_class} class\n" do + # create_bucket_class_location bucket_name: secondary_bucket_name + # end + # end + + # secondary_bucket = storage_client.bucket secondary_bucket_name + # refute_nil secondary_bucket + # assert_equal location, secondary_bucket.location + # assert_equal storage_class, secondary_bucket.storage_class + + # # list_buckets + # out, _err = capture_io do + # list_buckets + # end + + # assert_includes out, "ruby-storage-samples-" + + # # get_bucket_metadata + # out, _err = capture_io do + # get_bucket_metadata bucket_name: bucket_name + # end + + # assert_includes out, bucket_name + + # # delete_bucket + # assert_output "Deleted bucket: #{bucket_name}\n" do + # delete_bucket bucket_name: bucket_name + # end + + + # refute storage_client.bucket bucket_name + + # delete_bucket_helper bucket_name + # delete_bucket_helper secondary_bucket_name + # end + # end + + # describe "storage_create_bucket_dual_region" do + # it "creates dual region bucket" do + # location = "US" + # region_1 = "US-EAST1" + # region_2 = "US-WEST1" + # location_type = "dual-region" + # bucket_name = random_bucket_name + # refute storage_client.bucket bucket_name + + # expected = "Bucket #{bucket_name} created:\n" + # expected += "- location: #{location}\n" + # expected += "- location_type: #{location_type}\n" + # expected += "- custom_placement_config:\n" + # expected += " - data_locations: #{[region_1, region_2]}\n" + + # retry_resource_exhaustion do + # assert_output expected do + # StorageCreateBucketDualRegion.new.storage_create_bucket_dual_region bucket_name: bucket_name, + # region_1: region_1, + # region_2: region_2 + # end + # end + + # refute_nil storage_client.bucket bucket_name + + # delete_bucket_helper bucket_name + # end + # end + + # describe "storage_create_bucket_hierarchical_namespace" do + # it "creates hierarchical namespace enabled bucket" do + # bucket_name = random_bucket_name + # refute storage_client.bucket bucket_name + + # expected = "Created bucket #{bucket_name} with Hierarchical Namespace enabled.\n" + + # retry_resource_exhaustion do + # assert_output expected do + # create_bucket_hierarchical_namespace bucket_name: bucket_name + # end + # end + + # refute_nil storage_client.bucket bucket_name + + # delete_bucket_helper bucket_name + # end + # end + + describe "storage_create_bucket_ip_filter" do - retry_resource_exhaustion do - assert_output "Created bucket: #{bucket_name}\n" do - create_bucket bucket_name: bucket_name - end - end - - refute_nil storage_client.bucket bucket_name - - # create_bucket_class_location - - secondary_bucket_name = random_bucket_name - location = "ASIA" - storage_class = "COLDLINE" - refute storage_client.bucket secondary_bucket_name - - retry_resource_exhaustion do - assert_output "Created bucket #{secondary_bucket_name} in #{location} with #{storage_class} class\n" do - create_bucket_class_location bucket_name: secondary_bucket_name - end - end - - secondary_bucket = storage_client.bucket secondary_bucket_name - refute_nil secondary_bucket - assert_equal location, secondary_bucket.location - assert_equal storage_class, secondary_bucket.storage_class - - # list_buckets - out, _err = capture_io do - list_buckets - end - - assert_includes out, "ruby-storage-samples-" - - # get_bucket_metadata - out, _err = capture_io do - get_bucket_metadata bucket_name: bucket_name - end - - assert_includes out, bucket_name - - # delete_bucket - assert_output "Deleted bucket: #{bucket_name}\n" do - delete_bucket bucket_name: bucket_name - end - - - refute storage_client.bucket bucket_name - - delete_bucket_helper bucket_name - delete_bucket_helper secondary_bucket_name - end - end - - describe "storage_create_bucket_dual_region" do - it "creates dual region bucket" do - location = "US" - region_1 = "US-EAST1" - region_2 = "US-WEST1" - location_type = "dual-region" - bucket_name = random_bucket_name - refute storage_client.bucket bucket_name - - expected = "Bucket #{bucket_name} created:\n" - expected += "- location: #{location}\n" - expected += "- location_type: #{location_type}\n" - expected += "- custom_placement_config:\n" - expected += " - data_locations: #{[region_1, region_2]}\n" - - retry_resource_exhaustion do - assert_output expected do - StorageCreateBucketDualRegion.new.storage_create_bucket_dual_region bucket_name: bucket_name, - region_1: region_1, - region_2: region_2 - end - end - - refute_nil storage_client.bucket bucket_name - - delete_bucket_helper bucket_name + before :all do + @bucket_name = random_bucket_name end - end - - describe "storage_create_bucket_hierarchical_namespace" do - it "creates hierarchical namespace enabled bucket" do - bucket_name = random_bucket_name - refute storage_client.bucket bucket_name - - expected = "Created bucket #{bucket_name} with Hierarchical Namespace enabled.\n" - - retry_resource_exhaustion do - assert_output expected do - create_bucket_hierarchical_namespace bucket_name: bucket_name - end - end - - refute_nil storage_client.bucket bucket_name - delete_bucket_helper bucket_name + after :all do + binding.pry + # delete_bucket_helper @bucket_name end - end - - describe "storage_create_bucket_with_object_retention" do - it "creates a bucket with object retention enabled." do - bucket_name = random_bucket_name - refute storage_client.bucket bucket_name - - expected = "Created bucket #{bucket_name} with object retention setting: Enabled\n" + # it "creates IP filter enabled bucket" do + # expected = "Created bucket #{@bucket_name} with IP filter enabled.\n" + # retry_resource_exhaustion do + # assert_output expected do + # create_bucket_with_ip_filter bucket_name: @bucket_name + # end + # end + # end + + # it "removes IP filter of an existing bucket" do + # expected = "Updated IP filter for bucket #{@bucket_name}.\n" + # retry_resource_exhaustion do + # assert_output expected do + # removes_bucket_ip_filter bucket_name: @bucket_name + # end + # end + # end + + it "Updates IP filter of an existing bucket" do + expected = "Updated bucket #{@bucket_name+"-1"} with IP filter enabled.\n" retry_resource_exhaustion do assert_output expected do - create_bucket_with_object_retention bucket_name: bucket_name + update_bucket_with_ip_filter bucket_name: @bucket_name+"-1" end end - - refute_nil storage_client.bucket bucket_name - - file_name = "test_object_retention" - - bucket = storage_client.bucket bucket_name - - out, _err = capture_io do - set_object_retention_policy bucket_name: bucket.name, - content: "hello world", - destination_file_name: file_name - end - - assert_includes out, "Retention policy for file #{file_name}" - - file = bucket.file file_name - file.retention = { - mode: nil, - retain_until_time: nil, - override_unlocked_retention: true - } - delete_bucket_helper bucket_name end end - describe "autoclass" do - it "get_autoclass, set_autoclass" do - bucket_name = random_bucket_name - refute storage_client.bucket bucket_name - - storage_client.create_bucket bucket_name, autoclass_enabled: true - - assert_output(/autoclass config set to true./) do - get_autoclass bucket_name: bucket_name - end - - assert_output(/autoclass terminal storage class set to NEARLINE./) do - get_autoclass bucket_name: bucket_name - end - - assert_output(/autoclass terminal storage class set to ARCHIVE./) do - set_autoclass bucket_name: bucket_name, toggle: true, terminal_storage_class: "ARCHIVE" - end - - assert_output(/autoclass config set to false./) do - set_autoclass bucket_name: bucket_name, toggle: false - end - - delete_bucket_helper bucket_name - end - end - - describe "cors" do - it "cors_configuration, remove_cors_configuration" do - bucket.cors { |c| c.clear } - assert bucket.cors.empty? - - # cors_configuration - assert_output "Set CORS policies for bucket #{bucket.name}\n" do - cors_configuration bucket_name: bucket.name - end - - bucket.refresh! - assert_equal 1, bucket.cors.count - rule = bucket.cors.first - assert_equal ["*"], rule.origin - assert_equal ["PUT", "POST"], rule.methods - assert_equal ["Content-Type", "x-goog-resumable"], rule.headers - assert_equal 3600, rule.max_age - - # remove_cors_configuration - assert_output "Remove CORS policies for bucket #{bucket.name}\n" do - remove_cors_configuration bucket_name: bucket.name - end - bucket.refresh! - assert bucket.cors.empty? - end - end - - describe "requester_pays" do - it "enable_requester_pays, disable_requester_pays, get_requester_pays_status" do - # enable_requester_pays - bucket.requester_pays = false - - assert_output "Requester pays has been enabled for #{bucket.name}\n" do - enable_requester_pays bucket_name: bucket.name - end - bucket.refresh! - assert bucket.requester_pays? - - # get_requester_pays_status - assert_output "Requester pays status is enabled for #{bucket.name}\n" do - get_requester_pays_status bucket_name: bucket.name - end - assert bucket.requester_pays? - - # disable_requester_pays - assert_output "Requester pays has been disabled for #{bucket.name}\n" do - disable_requester_pays bucket_name: bucket.name - end - bucket.refresh! - refute bucket.requester_pays? - - # get_requester_pays_status - assert_output "Requester pays status is disabled for #{bucket.name}\n" do - get_requester_pays_status bucket_name: bucket.name - end - refute bucket.requester_pays? - end - end - - describe "uniform_bucket_level_access" do - it "enable_uniform_bucket_level_access, get_uniform_bucket_level_access, disable_uniform_bucket_level_access" do - # enable_uniform_bucket_level_access - bucket.uniform_bucket_level_access = false - - assert_output "Uniform bucket-level access was enabled for #{bucket.name}.\n" do - enable_uniform_bucket_level_access bucket_name: bucket.name - end - - bucket.refresh! - assert bucket.uniform_bucket_level_access? - - # get_uniform_bucket_level_access - assert_output "Uniform bucket-level access is enabled for #{bucket.name}.\nBucket " \ - "will be locked on #{bucket.uniform_bucket_level_access_locked_at}.\n" do - get_uniform_bucket_level_access bucket_name: bucket.name - end - assert bucket.uniform_bucket_level_access? - - # disable_uniform_bucket_level_access - assert_output "Uniform bucket-level access was disabled for #{bucket.name}.\n" do - disable_uniform_bucket_level_access bucket_name: bucket.name - end - - bucket.refresh! - refute bucket.uniform_bucket_level_access? - - # get_uniform_bucket_level_access - assert_output "Uniform bucket-level access is disabled for #{bucket.name}.\n" do - get_uniform_bucket_level_access bucket_name: bucket.name - end - refute bucket.uniform_bucket_level_access? - - bucket.uniform_bucket_level_access = false - end - end - - describe "default Cloud KMS encryption key" do - it "set_bucket_default_kms_key, bucket_delete_default_kms_key" do - refute bucket.default_kms_key - - # set_bucket_default_kms_key - assert_output "Default KMS key for #{bucket.name} was set to #{kms_key}\n" do - set_bucket_default_kms_key bucket_name: bucket.name, - default_kms_key: kms_key - end - - bucket.refresh! - assert_equal bucket.default_kms_key, kms_key - - # bucket_delete_default_kms_key - assert_output "Default KMS key was removed from #{bucket.name}\n" do - bucket_delete_default_kms_key bucket_name: bucket.name - end - - bucket.refresh! - refute bucket.default_kms_key - end - end - - describe "get bucket class and location data" do - bucket_name = random_bucket_name - location = "US" - storage_class = "COLDLINE" - - it "get_bucket_class_and_location" do - storage_client.create_bucket bucket_name, - location: location, - storage_class: storage_class - expected_output = "Bucket #{bucket_name} storage class is " \ - "#{storage_class}, and the location is #{location}\n" - assert_output expected_output do - get_bucket_class_and_location bucket_name: bucket_name - end - end - end - - describe "labels" do - it "add_bucket_label, remove_bucket_label" do - # add_bucket_label - label_key = "label_key" - label_value = "label_value" - - assert_output "Added label #{label_key} with value #{label_value} to #{bucket.name}\n" do - add_bucket_label bucket_name: bucket.name, - label_value: label_value, - label_key: label_key - end - - bucket.refresh! - assert_equal bucket.labels[label_key], label_value - - # remove_bucket_label - assert_output "Deleted label #{label_key} from #{bucket.name}\n" do - remove_bucket_label bucket_name: bucket.name, - label_key: label_key - end - - bucket.refresh! - assert bucket.labels[label_key].empty? - end - end - - describe "lifecycle management" do - let(:bucket) { create_bucket_helper random_bucket_name } - after { delete_bucket_helper bucket.name } - - it "enable_bucket_lifecycle_management, disable_bucket_lifecycle_management" do - # enable_bucket_lifecycle_management - out, _err = capture_io do - enable_bucket_lifecycle_management bucket_name: bucket.name - end - - assert_includes out, "Lifecycle management is enabled" - - # disable_bucket_lifecycle_management - out, _err = capture_io do - disable_bucket_lifecycle_management bucket_name: bucket.name - end - - assert_includes out, "Lifecycle management is disabled" - end - end - - describe "retention policy" do - let(:bucket) { create_bucket_helper random_bucket_name } - after { delete_bucket_helper bucket.name } - - it "set_retention_policy, get_retention_policy, remove_retention_policy" do - # set_retention_policy - assert_output "Retention period for #{bucket.name} is now #{retention_period} seconds.\n" do - set_retention_policy bucket_name: bucket.name, - retention_period: retention_period - end - - bucket.refresh! - assert_equal bucket.retention_period, retention_period - - # get_retention_policy - out, _err = capture_io do - get_retention_policy bucket_name: bucket.name - end - - assert_includes out, "period: #{retention_period}\n" - - # remove_retention_policy - assert_equal bucket.retention_period, retention_period - assert_output "Retention policy for #{bucket.name} has been removed.\n" do - remove_retention_policy bucket_name: bucket.name - end - - bucket.refresh! - refute bucket.retention_period - - # lock_retention_policy - bucket.retention_period = retention_period - out, _err = capture_io do - lock_retention_policy bucket_name: bucket.name - end - - assert_includes out, "Retention policy for #{bucket.name} is now locked." - bucket.refresh! - assert bucket.retention_policy_locked? - - # remove_retention_policy - assert_output "Policy is locked and retention policy can't be removed.\n" do - remove_retention_policy bucket_name: bucket.name - end - end - end - - describe "default_event_based_hold" do - it "enable_default_event_based_hold, get_default_event_based_hold, disable_default_event_based_hold" do - # enable_default_event_based_hold - assert_output "Default event-based hold was enabled for #{bucket.name}.\n" do - enable_default_event_based_hold bucket_name: bucket.name - end - - bucket.refresh! - assert bucket.default_event_based_hold? - - # get_default_event_based_hold - assert_output "Default event-based hold is enabled for #{bucket.name}.\n" do - get_default_event_based_hold bucket_name: bucket.name - end - - # disable_default_event_based_hold - bucket.update do |b| - b.default_event_based_hold = true - end - - assert_output "Default event-based hold was disabled for #{bucket.name}.\n" do - disable_default_event_based_hold bucket_name: bucket.name - end - - bucket.refresh! - refute bucket.default_event_based_hold? - - # get_default_event_based_hold - assert_output "Default event-based hold is not enabled for #{bucket.name}.\n" do - get_default_event_based_hold bucket_name: bucket.name - end - end - end - - describe "storage_class" do - it "change_default_storage_class" do - assert_equal "STANDARD", bucket.storage_class - - assert_output "Default storage class for bucket #{bucket.name} has been set to COLDLINE\n" do - change_default_storage_class bucket_name: bucket.name - end - - bucket.refresh! - assert_equal "COLDLINE", bucket.storage_class - # teardown - bucket.storage_class = "STANDARD" - end - end - - describe "versioning" do - it "enable_versioning, disable_versioning" do - # enable_versioning - bucket.versioning = false - - assert_output "Versioning was enabled for bucket #{bucket.name}\n" do - enable_versioning bucket_name: bucket.name - end - bucket.refresh! - assert bucket.versioning? - - # disable_versioning - assert_output "Versioning was disabled for bucket #{bucket.name}\n" do - disable_versioning bucket_name: bucket.name - end - bucket.refresh! - refute bucket.versioning? - end - end - - describe "website_configuration" do - let(:main_page_suffix) { "index.html" } - let(:not_found_page) { "404.html" } - - it "define_bucket_website_configuration" do - expected_out = "Static website bucket #{bucket.name} is set up to use #{main_page_suffix} as the index page " \ - "and #{not_found_page} as the 404 page\n" - - assert_output expected_out do - define_bucket_website_configuration bucket_name: bucket.name, - main_page_suffix: main_page_suffix, - not_found_page: not_found_page - end - - bucket.refresh! - assert_equal main_page_suffix, bucket.website_main - assert_equal not_found_page, bucket.website_404 - end - end - - describe "public_access_prevention" do - it "set_public_access_prevention_enforced, get_public_access_prevention, " \ - "set_public_access_prevention_inherited" do - bucket.public_access_prevention = :inherited - bucket.refresh! - _(bucket.public_access_prevention).must_equal "inherited" - - # set_public_access_prevention_enforced - assert_output "Public access prevention is set to enforced for #{bucket.name}.\n" do - set_public_access_prevention_enforced bucket_name: bucket.name - end - - bucket.refresh! - _(bucket.public_access_prevention).must_equal "enforced" - - # get_public_access_prevention - assert_output "Public access prevention is 'enforced' for #{bucket.name}.\n" do - get_public_access_prevention bucket_name: bucket.name - end - _(bucket.public_access_prevention).must_equal "enforced" - - # set_public_access_prevention_inherited - assert_output "Public access prevention is 'inherited' for #{bucket.name}.\n" do - set_public_access_prevention_inherited bucket_name: bucket.name - end - - bucket.refresh! - _(bucket.public_access_prevention).must_equal "inherited" - bucket.public_access_prevention = :inherited - end - end - - describe "storage move file" do - let(:source_file) { "file_1_name_#{SecureRandom.hex}.txt" } - let(:destination_file) { "file_2_name_#{SecureRandom.hex}.txt" } - let :hns_bucket do - hierarchical_namespace = Google::Apis::StorageV1::Bucket::HierarchicalNamespace.new enabled: true - storage_client.create_bucket random_bucket_name do |b| - b.uniform_bucket_level_access = true - b.hierarchical_namespace = hierarchical_namespace - end - end - let :create_source_file do - file_content = "A" * (3 * 1024 * 1024) # 3 MB of 'A' characters - file = StringIO.new file_content - hns_bucket.create_file file, source_file - end - it "file is moved and old file is deleted" do - create_source_file - out, _err = capture_io do - move_object bucket_name: hns_bucket.name, source_file_name: source_file, destination_file_name: destination_file - end - assert_includes out, "New File #{destination_file} created\n" - refute_nil(hns_bucket.file(destination_file)) - assert_nil(hns_bucket.file(source_file)) - end - - it "raises error if source and destination are having same filename" do - create_source_file - exception = assert_raises Google::Cloud::InvalidArgumentError do - move_object bucket_name: hns_bucket.name, source_file_name: source_file, destination_file_name: source_file - end - assert_equal "invalid: Source and destination object names must be different.", exception.message - end - end - - describe "list buckets with partial success" do - it 'returns a list of bucket names if return_partial_success_flag is true' do - result = list_buckets_with_partial_success return_partial_success_flag: true - assert_kind_of Array, result - assert result.all? { |n| n.is_a? String }, "expected all items to be String" - end - - it 'returns nil for unreachable if return_partial_success_flag is false' do - result = list_buckets_with_partial_success return_partial_success_flag: false - assert_nil result - end - - it 'returns nil for unreachable if return_partial_success_flag is not passed' do - result = list_buckets_with_partial_success return_partial_success_flag: nil - assert_nil result - end - end + # describe "storage_create_bucket_with_object_retention" do + # it "creates a bucket with object retention enabled." do + # bucket_name = random_bucket_name + # refute storage_client.bucket bucket_name + + # expected = "Created bucket #{bucket_name} with object retention setting: Enabled\n" + + # retry_resource_exhaustion do + # assert_output expected do + # create_bucket_with_object_retention bucket_name: bucket_name + # end + # end + + # refute_nil storage_client.bucket bucket_name + + # file_name = "test_object_retention" + + # bucket = storage_client.bucket bucket_name + + # out, _err = capture_io do + # set_object_retention_policy bucket_name: bucket.name, + # content: "hello world", + # destination_file_name: file_name + # end + + # assert_includes out, "Retention policy for file #{file_name}" + + # file = bucket.file file_name + # file.retention = { + # mode: nil, + # retain_until_time: nil, + # override_unlocked_retention: true + # } + # delete_bucket_helper bucket_name + # end + # end + + # describe "autoclass" do + # it "get_autoclass, set_autoclass" do + # bucket_name = random_bucket_name + # refute storage_client.bucket bucket_name + + # storage_client.create_bucket bucket_name, autoclass_enabled: true + + # assert_output(/autoclass config set to true./) do + # get_autoclass bucket_name: bucket_name + # end + + # assert_output(/autoclass terminal storage class set to NEARLINE./) do + # get_autoclass bucket_name: bucket_name + # end + + # assert_output(/autoclass terminal storage class set to ARCHIVE./) do + # set_autoclass bucket_name: bucket_name, toggle: true, terminal_storage_class: "ARCHIVE" + # end + + # assert_output(/autoclass config set to false./) do + # set_autoclass bucket_name: bucket_name, toggle: false + # end + + # delete_bucket_helper bucket_name + # end + # end + + # describe "cors" do + # it "cors_configuration, remove_cors_configuration" do + # bucket.cors { |c| c.clear } + # assert bucket.cors.empty? + + # # cors_configuration + # assert_output "Set CORS policies for bucket #{bucket.name}\n" do + # cors_configuration bucket_name: bucket.name + # end + + # bucket.refresh! + # assert_equal 1, bucket.cors.count + # rule = bucket.cors.first + # assert_equal ["*"], rule.origin + # assert_equal ["PUT", "POST"], rule.methods + # assert_equal ["Content-Type", "x-goog-resumable"], rule.headers + # assert_equal 3600, rule.max_age + + # # remove_cors_configuration + # assert_output "Remove CORS policies for bucket #{bucket.name}\n" do + # remove_cors_configuration bucket_name: bucket.name + # end + # bucket.refresh! + # assert bucket.cors.empty? + # end + # end + + # describe "requester_pays" do + # it "enable_requester_pays, disable_requester_pays, get_requester_pays_status" do + # # enable_requester_pays + # bucket.requester_pays = false + + # assert_output "Requester pays has been enabled for #{bucket.name}\n" do + # enable_requester_pays bucket_name: bucket.name + # end + # bucket.refresh! + # assert bucket.requester_pays? + + # # get_requester_pays_status + # assert_output "Requester pays status is enabled for #{bucket.name}\n" do + # get_requester_pays_status bucket_name: bucket.name + # end + # assert bucket.requester_pays? + + # # disable_requester_pays + # assert_output "Requester pays has been disabled for #{bucket.name}\n" do + # disable_requester_pays bucket_name: bucket.name + # end + # bucket.refresh! + # refute bucket.requester_pays? + + # # get_requester_pays_status + # assert_output "Requester pays status is disabled for #{bucket.name}\n" do + # get_requester_pays_status bucket_name: bucket.name + # end + # refute bucket.requester_pays? + # end + # end + + # describe "uniform_bucket_level_access" do + # it "enable_uniform_bucket_level_access, get_uniform_bucket_level_access, disable_uniform_bucket_level_access" do + # # enable_uniform_bucket_level_access + # bucket.uniform_bucket_level_access = false + + # assert_output "Uniform bucket-level access was enabled for #{bucket.name}.\n" do + # enable_uniform_bucket_level_access bucket_name: bucket.name + # end + + # bucket.refresh! + # assert bucket.uniform_bucket_level_access? + + # # get_uniform_bucket_level_access + # assert_output "Uniform bucket-level access is enabled for #{bucket.name}.\nBucket " \ + # "will be locked on #{bucket.uniform_bucket_level_access_locked_at}.\n" do + # get_uniform_bucket_level_access bucket_name: bucket.name + # end + # assert bucket.uniform_bucket_level_access? + + # # disable_uniform_bucket_level_access + # assert_output "Uniform bucket-level access was disabled for #{bucket.name}.\n" do + # disable_uniform_bucket_level_access bucket_name: bucket.name + # end + + # bucket.refresh! + # refute bucket.uniform_bucket_level_access? + + # # get_uniform_bucket_level_access + # assert_output "Uniform bucket-level access is disabled for #{bucket.name}.\n" do + # get_uniform_bucket_level_access bucket_name: bucket.name + # end + # refute bucket.uniform_bucket_level_access? + + # bucket.uniform_bucket_level_access = false + # end + # end + + # describe "default Cloud KMS encryption key" do + # it "set_bucket_default_kms_key, bucket_delete_default_kms_key" do + # refute bucket.default_kms_key + + # # set_bucket_default_kms_key + # assert_output "Default KMS key for #{bucket.name} was set to #{kms_key}\n" do + # set_bucket_default_kms_key bucket_name: bucket.name, + # default_kms_key: kms_key + # end + + # bucket.refresh! + # assert_equal bucket.default_kms_key, kms_key + + # # bucket_delete_default_kms_key + # assert_output "Default KMS key was removed from #{bucket.name}\n" do + # bucket_delete_default_kms_key bucket_name: bucket.name + # end + + # bucket.refresh! + # refute bucket.default_kms_key + # end + # end + + # describe "get bucket class and location data" do + # bucket_name = random_bucket_name + # location = "US" + # storage_class = "COLDLINE" + + # it "get_bucket_class_and_location" do + # storage_client.create_bucket bucket_name, + # location: location, + # storage_class: storage_class + # expected_output = "Bucket #{bucket_name} storage class is " \ + # "#{storage_class}, and the location is #{location}\n" + # assert_output expected_output do + # get_bucket_class_and_location bucket_name: bucket_name + # end + # end + # end + + # describe "labels" do + # it "add_bucket_label, remove_bucket_label" do + # # add_bucket_label + # label_key = "label_key" + # label_value = "label_value" + + # assert_output "Added label #{label_key} with value #{label_value} to #{bucket.name}\n" do + # add_bucket_label bucket_name: bucket.name, + # label_value: label_value, + # label_key: label_key + # end + + # bucket.refresh! + # assert_equal bucket.labels[label_key], label_value + + # # remove_bucket_label + # assert_output "Deleted label #{label_key} from #{bucket.name}\n" do + # remove_bucket_label bucket_name: bucket.name, + # label_key: label_key + # end + + # bucket.refresh! + # assert bucket.labels[label_key].empty? + # end + # end + + # describe "lifecycle management" do + # let(:bucket) { create_bucket_helper random_bucket_name } + # after { delete_bucket_helper bucket.name } + + # it "enable_bucket_lifecycle_management, disable_bucket_lifecycle_management" do + # # enable_bucket_lifecycle_management + # out, _err = capture_io do + # enable_bucket_lifecycle_management bucket_name: bucket.name + # end + + # assert_includes out, "Lifecycle management is enabled" + + # # disable_bucket_lifecycle_management + # out, _err = capture_io do + # disable_bucket_lifecycle_management bucket_name: bucket.name + # end + + # assert_includes out, "Lifecycle management is disabled" + # end + # end + + # describe "retention policy" do + # let(:bucket) { create_bucket_helper random_bucket_name } + # after { delete_bucket_helper bucket.name } + + # it "set_retention_policy, get_retention_policy, remove_retention_policy" do + # # set_retention_policy + # assert_output "Retention period for #{bucket.name} is now #{retention_period} seconds.\n" do + # set_retention_policy bucket_name: bucket.name, + # retention_period: retention_period + # end + + # bucket.refresh! + # assert_equal bucket.retention_period, retention_period + + # # get_retention_policy + # out, _err = capture_io do + # get_retention_policy bucket_name: bucket.name + # end + + # assert_includes out, "period: #{retention_period}\n" + + # # remove_retention_policy + # assert_equal bucket.retention_period, retention_period + # assert_output "Retention policy for #{bucket.name} has been removed.\n" do + # remove_retention_policy bucket_name: bucket.name + # end + + # bucket.refresh! + # refute bucket.retention_period + + # # lock_retention_policy + # bucket.retention_period = retention_period + # out, _err = capture_io do + # lock_retention_policy bucket_name: bucket.name + # end + + # assert_includes out, "Retention policy for #{bucket.name} is now locked." + # bucket.refresh! + # assert bucket.retention_policy_locked? + + # # remove_retention_policy + # assert_output "Policy is locked and retention policy can't be removed.\n" do + # remove_retention_policy bucket_name: bucket.name + # end + # end + # end + + # describe "default_event_based_hold" do + # it "enable_default_event_based_hold, get_default_event_based_hold, disable_default_event_based_hold" do + # # enable_default_event_based_hold + # assert_output "Default event-based hold was enabled for #{bucket.name}.\n" do + # enable_default_event_based_hold bucket_name: bucket.name + # end + + # bucket.refresh! + # assert bucket.default_event_based_hold? + + # # get_default_event_based_hold + # assert_output "Default event-based hold is enabled for #{bucket.name}.\n" do + # get_default_event_based_hold bucket_name: bucket.name + # end + + # # disable_default_event_based_hold + # bucket.update do |b| + # b.default_event_based_hold = true + # end + + # assert_output "Default event-based hold was disabled for #{bucket.name}.\n" do + # disable_default_event_based_hold bucket_name: bucket.name + # end + + # bucket.refresh! + # refute bucket.default_event_based_hold? + + # # get_default_event_based_hold + # assert_output "Default event-based hold is not enabled for #{bucket.name}.\n" do + # get_default_event_based_hold bucket_name: bucket.name + # end + # end + # end + + # describe "storage_class" do + # it "change_default_storage_class" do + # assert_equal "STANDARD", bucket.storage_class + + # assert_output "Default storage class for bucket #{bucket.name} has been set to COLDLINE\n" do + # change_default_storage_class bucket_name: bucket.name + # end + + # bucket.refresh! + # assert_equal "COLDLINE", bucket.storage_class + # # teardown + # bucket.storage_class = "STANDARD" + # end + # end + + # describe "versioning" do + # it "enable_versioning, disable_versioning" do + # # enable_versioning + # bucket.versioning = false + + # assert_output "Versioning was enabled for bucket #{bucket.name}\n" do + # enable_versioning bucket_name: bucket.name + # end + # bucket.refresh! + # assert bucket.versioning? + + # # disable_versioning + # assert_output "Versioning was disabled for bucket #{bucket.name}\n" do + # disable_versioning bucket_name: bucket.name + # end + # bucket.refresh! + # refute bucket.versioning? + # end + # end + + # describe "website_configuration" do + # let(:main_page_suffix) { "index.html" } + # let(:not_found_page) { "404.html" } + + # it "define_bucket_website_configuration" do + # expected_out = "Static website bucket #{bucket.name} is set up to use #{main_page_suffix} as the index page " \ + # "and #{not_found_page} as the 404 page\n" + + # assert_output expected_out do + # define_bucket_website_configuration bucket_name: bucket.name, + # main_page_suffix: main_page_suffix, + # not_found_page: not_found_page + # end + + # bucket.refresh! + # assert_equal main_page_suffix, bucket.website_main + # assert_equal not_found_page, bucket.website_404 + # end + # end + + # describe "public_access_prevention" do + # it "set_public_access_prevention_enforced, get_public_access_prevention, " \ + # "set_public_access_prevention_inherited" do + # bucket.public_access_prevention = :inherited + # bucket.refresh! + # _(bucket.public_access_prevention).must_equal "inherited" + + # # set_public_access_prevention_enforced + # assert_output "Public access prevention is set to enforced for #{bucket.name}.\n" do + # set_public_access_prevention_enforced bucket_name: bucket.name + # end + + # bucket.refresh! + # _(bucket.public_access_prevention).must_equal "enforced" + + # # get_public_access_prevention + # assert_output "Public access prevention is 'enforced' for #{bucket.name}.\n" do + # get_public_access_prevention bucket_name: bucket.name + # end + # _(bucket.public_access_prevention).must_equal "enforced" + + # # set_public_access_prevention_inherited + # assert_output "Public access prevention is 'inherited' for #{bucket.name}.\n" do + # set_public_access_prevention_inherited bucket_name: bucket.name + # end + + # bucket.refresh! + # _(bucket.public_access_prevention).must_equal "inherited" + # bucket.public_access_prevention = :inherited + # end + # end + + # describe "storage move file" do + # let(:source_file) { "file_1_name_#{SecureRandom.hex}.txt" } + # let(:destination_file) { "file_2_name_#{SecureRandom.hex}.txt" } + # let :hns_bucket do + # hierarchical_namespace = Google::Apis::StorageV1::Bucket::HierarchicalNamespace.new enabled: true + # storage_client.create_bucket random_bucket_name do |b| + # b.uniform_bucket_level_access = true + # b.hierarchical_namespace = hierarchical_namespace + # end + # end + # let :create_source_file do + # file_content = "A" * (3 * 1024 * 1024) # 3 MB of 'A' characters + # file = StringIO.new file_content + # hns_bucket.create_file file, source_file + # end + # it "file is moved and old file is deleted" do + # create_source_file + # out, _err = capture_io do + # move_object bucket_name: hns_bucket.name, source_file_name: source_file, destination_file_name: destination_file + # end + # assert_includes out, "New File #{destination_file} created\n" + # refute_nil(hns_bucket.file(destination_file)) + # assert_nil(hns_bucket.file(source_file)) + # end + + # it "raises error if source and destination are having same filename" do + # create_source_file + # exception = assert_raises Google::Cloud::InvalidArgumentError do + # move_object bucket_name: hns_bucket.name, source_file_name: source_file, destination_file_name: source_file + # end + # assert_equal "invalid: Source and destination object names must be different.", exception.message + # end + # end + + # describe "list buckets with partial success" do + # it 'returns a list of bucket names if return_partial_success_flag is true' do + # result = list_buckets_with_partial_success return_partial_success_flag: true + # assert_kind_of Array, result + # assert result.all? { |n| n.is_a? String }, "expected all items to be String" + # end + + # it 'returns nil for unreachable if return_partial_success_flag is false' do + # result = list_buckets_with_partial_success return_partial_success_flag: false + # assert_nil result + # end + + # it 'returns nil for unreachable if return_partial_success_flag is not passed' do + # result = list_buckets_with_partial_success return_partial_success_flag: nil + # assert_nil result + # end + # end end diff --git a/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb new file mode 100644 index 000000000000..02deb4ebe75c --- /dev/null +++ b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb @@ -0,0 +1,129 @@ +# 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 +# +# http://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. + +# [START storage_create_bucket_with_ip_filter] +def create_bucket_with_ip_filter bucket_name: + # The ID to give your GCS bucket + # bucket_name = "your-unique-bucket-name" + + require "google/cloud/storage" + + storage = Google::Cloud::Storage.new + + ip_filter = Google::Apis::StorageV1::Bucket::IpFilter.new( + mode: "Enabled", + public_network_source: Google::Apis::StorageV1::Bucket::IpFilter::PublicNetworkSource.new( + allowed_ip_cidr_ranges: [ + "0.0.0.0/0", "::/0" + ] + ), + allow_all_service_agent_access: false + ) + bucket = storage.create_bucket bucket_name do |b| + b.ip_filter = ip_filter + b.uniform_bucket_level_access = true + end + + puts "Created bucket #{bucket_name} with IP filter enabled." +end + +def removes_bucket_ip_filter bucket_name: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + require "google/cloud/storage" + + storage = Google::Cloud::Storage.new + bucket = storage.bucket bucket_name + + ip_filter = Google::Apis::StorageV1::Bucket::IpFilter.new( + mode: "Disabled" + ) + bucket.update do |b| + b.ip_filter = ip_filter + end + + puts "Updated IP filter for bucket #{bucket_name}." +end + +def update_bucket_with_ip_filter bucket_name: + # The ID to give your GCS bucket + # bucket_name = "your-unique-bucket-name" + + require "google/cloud/resource_manager/v3" + storage = Google::Cloud::Storage.new + + ip_filter = Google::Apis::StorageV1::Bucket::IpFilter.new( + mode: "Enabled", + public_network_source: Google::Apis::StorageV1::Bucket::IpFilter::PublicNetworkSource.new( + allowed_ip_cidr_ranges: [ + "0.0.0.0/0", "::/0" + ] + ), + allow_all_service_agent_access: false + ) + # require "google/cloud/iam" + # Initialize the IAM Admin Client (Not Credentials Client) + # iam_client = Google::Cloud::Iam::Service.new + client = Google::Cloud::ResourceManager::V3::Projects::Client.new + + + project_id = "storage-sdk-vendor" + role_id = "GcsIpExemptRole" + + # Define the Custom Role + # role = Google::Cloud::Iam::V1::Role.new( + # title: "GCS IP Exemption Role", + # included_permissions: ["storage.buckets.exemptFromIpFilter"], + # stage: :GA + # ) + resource = "projects/#{project_id}" + + # Call test_iam_permissions. + # In Ruby, you can pass a Hash that matches the request structure. + response = client.test_iam_permissions( + resource: resource, + permissions: ["storage.buckets.exemptFromIpFilter"] + ) + + # The response object contains a 'permissions' array of granted permissions + puts "Granted permissions: #{response.permissions}" + + response.permissions + binding.pry + + # role = "projects/#{storage.project}/roles/GcsIpExemptRole" + member = "serviceAccount:insecure-cloudtop-shared-user@cloudtop-prod-asia-east.iam.gserviceaccount.com" + + bucket.policy requested_policy_version: 3 do |policy| + policy.bindings.insert role: role, members: [member] + end + # bucket = storage.create_bucket bucket_name + # bucket.policy do |p| + # p.add "roles/storage.buckets.exemptFromIpFilter", "serviceAccount:insecure-cloudtop-shared-user@cloudtop-prod-asia-east.iam.gserviceaccount.com" + # end + # binding.pry + + bucket.update do |b| + b.ip_filter = ip_filter + end + + + puts "Updated bucket #{bucket_name} with IP filter enabled." +end +# [END storage_create_bucket_with_ip_filter] + +if $PROGRAM_NAME == __FILE__ + create_bucket_with_ip_filter bucket_name: ARGV.shift +end From 6c1fe37b3a27387776896c1789ca21259df74157 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Tue, 27 Jan 2026 10:37:13 +0000 Subject: [PATCH 02/18] wip --- .../storage_create_bucket_with_ip_filter.rb | 102 ++++++++++++------ 1 file changed, 70 insertions(+), 32 deletions(-) diff --git a/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb index 02deb4ebe75c..6215e7ba102d 100644 --- a/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb +++ b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb @@ -30,11 +30,14 @@ def create_bucket_with_ip_filter bucket_name: ), allow_all_service_agent_access: false ) + bucket = storage.create_bucket bucket_name do |b| b.ip_filter = ip_filter b.uniform_bucket_level_access = true end + binding.pry + puts "Created bucket #{bucket_name} with IP filter enabled." end @@ -73,47 +76,82 @@ def update_bucket_with_ip_filter bucket_name: ), allow_all_service_agent_access: false ) - # require "google/cloud/iam" - # Initialize the IAM Admin Client (Not Credentials Client) - # iam_client = Google::Cloud::Iam::Service.new - client = Google::Cloud::ResourceManager::V3::Projects::Client.new + +# require "google/cloud/resource_manager" + +# Direct client initialization +client = Google::Cloud::ResourceManager::V3::Projects::Client.new + +project_id = storage.project +resource_name = "projects/#{project_id}" + +# 1. Get the current polGoogle::Cloud::Iam::V1::IAM::Client.newicy +# binding.pry +policy = client.get_iam_policy resource: resource_name +# binding.pry + +custom_role = "storage.buckets.exemptFromIpFilter" +custom_role_path = "#{resource_name}/roles/#{custom_role}" +service_account = "serviceAccount:#{storage.service_account_email}" +#{serviceAccount:storage-sdk-vendor@storage-sdk-vendor.iam.gserviceaccount.com} +binding.pry +custom_role_binding = Google::Iam::V1::Binding.new( + role: custom_role_path, + members: [service_account] +) +policy.bindings << custom_role_binding - project_id = "storage-sdk-vendor" - role_id = "GcsIpExemptRole" +client.set_iam_policy resource: resource_name, policy: policy +# Google::Cloud::PermissionDeniedError: 7:Permission 'resourcemanager.projects.setIamPolicy' denied on resource '//cloudresourcemanager.googleapis.com/projects/storage-sdk-vendor' (or it may not exist).. debug_error_string:{UNKNOWN:Error received from peer ipv4:74.125.23.95:443 {grpc_message:"Permission \'resourcemanager.projects.setIamPolicy\' denied on resource \'//cloudresourcemanager.googleapis.com/projects/storage-sdk-vendor\' (or it may not exist).", grpc_status:7}} (Google::Cloud::PermissionDeniedError) - # Define the Custom Role + +# # Optional: Log the change +# puts "Updating project policy to include #{custom_role} for #{service_account}" +# end + +puts "Project policy updated successfully." + + # project_id = "storage-sdk-vendor" + # role_id = "GcsIpExemptRole" + # binding.pry + + # # Define the Custom Role # role = Google::Cloud::Iam::V1::Role.new( # title: "GCS IP Exemption Role", # included_permissions: ["storage.buckets.exemptFromIpFilter"], # stage: :GA # ) - resource = "projects/#{project_id}" - - # Call test_iam_permissions. - # In Ruby, you can pass a Hash that matches the request structure. - response = client.test_iam_permissions( - resource: resource, - permissions: ["storage.buckets.exemptFromIpFilter"] - ) - - # The response object contains a 'permissions' array of granted permissions - puts "Granted permissions: #{response.permissions}" + # resource = "projects/#{project_id}" + # storage_project = storage.project + # resource = "projects/#{storage_project}/roles/#{role_id}" + # storage_project.policy do |p| + # p.add "roles/#{role}", member + # end + + # # Call test_iam_permissions. + # # In Ruby, you can pass a Hash that matches the request structure. + # response = client.test_iam_permissions( + # resource: resource, + # permissions: ["storage.buckets.exemptFromIpFilter"] + # ) + + # # The response object contains a 'permissions' array of granted permissions + # puts "Granted permissions: #{response.permissions}" - response.permissions - binding.pry - - # role = "projects/#{storage.project}/roles/GcsIpExemptRole" - member = "serviceAccount:insecure-cloudtop-shared-user@cloudtop-prod-asia-east.iam.gserviceaccount.com" - - bucket.policy requested_policy_version: 3 do |policy| - policy.bindings.insert role: role, members: [member] - end - # bucket = storage.create_bucket bucket_name - # bucket.policy do |p| - # p.add "roles/storage.buckets.exemptFromIpFilter", "serviceAccount:insecure-cloudtop-shared-user@cloudtop-prod-asia-east.iam.gserviceaccount.com" - # end - # binding.pry + # response.permissions + + # # role = "projects/#{storage.project}/roles/GcsIpExemptRole" + # member = "serviceAccount:insecure-cloudtop-shared-user@cloudtop-prod-asia-east.iam.gserviceaccount.com" + + # bucket.policy requested_policy_version: 3 do |policy| + # policy.bindings.insert role: "roles/storage.buckets.exemptFromIpFilter", members: ["serviceAccount:insecure-cloudtop-shared-user@cloudtop-prod-asia-east.iam.gserviceaccount.com"] + # end + # # bucket = storage.create_bucket bucket_name + # # bucket.policy do |p| + # # p.add "roles/storage.buckets.exemptFromIpFilter", "serviceAccount:insecure-cloudtop-shared-user@cloudtop-prod-asia-east.iam.gserviceaccount.com" + # # end + # # binding.pry bucket.update do |b| b.ip_filter = ip_filter From dcfdafb2026cf97c56d3e99e67c9644e6a061bb2 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Thu, 21 May 2026 10:56:17 +0000 Subject: [PATCH 03/18] wip --- .../samples/acceptance/buckets_test.rb | 22 ++++++------ .../storage_create_bucket_with_ip_filter.rb | 36 +++++++++++++------ 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/google-cloud-storage/samples/acceptance/buckets_test.rb b/google-cloud-storage/samples/acceptance/buckets_test.rb index e29644d5d196..1b41b6033049 100644 --- a/google-cloud-storage/samples/acceptance/buckets_test.rb +++ b/google-cloud-storage/samples/acceptance/buckets_test.rb @@ -178,18 +178,20 @@ end after :all do - binding.pry - # delete_bucket_helper @bucket_name + # removes_bucket_ip_filter bucket_name: @bucket_name + binding.pry + + delete_bucket_helper @bucket_name end - # it "creates IP filter enabled bucket" do - # expected = "Created bucket #{@bucket_name} with IP filter enabled.\n" - # retry_resource_exhaustion do - # assert_output expected do - # create_bucket_with_ip_filter bucket_name: @bucket_name - # end - # end - # end + it "creates IP filter enabled bucket" do + expected = "Created bucket #{@bucket_name} with IP filter enabled.\n" + retry_resource_exhaustion do + assert_output expected do + create_bucket_with_ip_filter bucket_name: @bucket_name + end + end + end # it "removes IP filter of an existing bucket" do # expected = "Updated IP filter for bucket #{@bucket_name}.\n" diff --git a/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb index 6215e7ba102d..43f45362b76a 100644 --- a/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb +++ b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb @@ -20,16 +20,33 @@ def create_bucket_with_ip_filter bucket_name: require "google/cloud/storage" storage = Google::Cloud::Storage.new + binding.pry - ip_filter = Google::Apis::StorageV1::Bucket::IpFilter.new( - mode: "Enabled", - public_network_source: Google::Apis::StorageV1::Bucket::IpFilter::PublicNetworkSource.new( + # ip_filter = Google::Apis::StorageV1::Bucket::IpFilter.new( + # mode: "Enabled", + # public_network_source: Google::Apis::StorageV1::Bucket::IpFilter::PublicNetworkSource.new( + # allowed_ip_cidr_ranges: [ + # "0.0.0.0/0", "::/0" + # ], + # vpc_network_sources: [ + # {} + # network: "projects/storage-sdk-vendor/global/networks/default", + # allowed_ip_cidr_ranges: [ + # "10.0.0.0/8" + # ] + # } + # ] + # ), + # allow_all_service_agent_access: true + # ) + ip_filter = { + mode: "Disabled", + public_network_source: { allowed_ip_cidr_ranges: [ "0.0.0.0/0", "::/0" ] - ), - allow_all_service_agent_access: false - ) + } +} bucket = storage.create_bucket bucket_name do |b| b.ip_filter = ip_filter @@ -68,14 +85,11 @@ def update_bucket_with_ip_filter bucket_name: storage = Google::Cloud::Storage.new ip_filter = Google::Apis::StorageV1::Bucket::IpFilter.new( - mode: "Enabled", public_network_source: Google::Apis::StorageV1::Bucket::IpFilter::PublicNetworkSource.new( allowed_ip_cidr_ranges: [ - "0.0.0.0/0", "::/0" + "0.0.0.3/0", "::/0" ] - ), - allow_all_service_agent_access: false - ) + )) # require "google/cloud/resource_manager" From 4d2bbe7fafb712d0fdc18faa3fa39dcead8b61cf Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Thu, 4 Jun 2026 04:05:49 +0000 Subject: [PATCH 04/18] check samples --- .../samples/acceptance/buckets_test.rb | 22 ++-- .../storage_create_bucket_with_ip_filter.rb | 119 ++---------------- 2 files changed, 21 insertions(+), 120 deletions(-) diff --git a/google-cloud-storage/samples/acceptance/buckets_test.rb b/google-cloud-storage/samples/acceptance/buckets_test.rb index 1b41b6033049..f71a408b3ae7 100644 --- a/google-cloud-storage/samples/acceptance/buckets_test.rb +++ b/google-cloud-storage/samples/acceptance/buckets_test.rb @@ -179,7 +179,7 @@ after :all do # removes_bucket_ip_filter bucket_name: @bucket_name - binding.pry + # binding.pry delete_bucket_helper @bucket_name end @@ -193,20 +193,20 @@ end end - # it "removes IP filter of an existing bucket" do - # expected = "Updated IP filter for bucket #{@bucket_name}.\n" - # retry_resource_exhaustion do - # assert_output expected do - # removes_bucket_ip_filter bucket_name: @bucket_name - # end - # end - # end + it "removes IP filter of an existing bucket" do + expected = "Updated IP filter for bucket #{@bucket_name}.\n" + retry_resource_exhaustion do + assert_output expected do + removes_bucket_ip_filter bucket_name: @bucket_name + end + end + end it "Updates IP filter of an existing bucket" do - expected = "Updated bucket #{@bucket_name+"-1"} with IP filter enabled.\n" + expected = "Updated bucket #{@bucket_name} with IP filter enabled.\n" retry_resource_exhaustion do assert_output expected do - update_bucket_with_ip_filter bucket_name: @bucket_name+"-1" + update_bucket_with_ip_filter bucket_name: @bucket_name end end end diff --git a/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb index 43f45362b76a..c31d03a076d7 100644 --- a/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb +++ b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb @@ -20,41 +20,19 @@ def create_bucket_with_ip_filter bucket_name: require "google/cloud/storage" storage = Google::Cloud::Storage.new - binding.pry - - # ip_filter = Google::Apis::StorageV1::Bucket::IpFilter.new( - # mode: "Enabled", - # public_network_source: Google::Apis::StorageV1::Bucket::IpFilter::PublicNetworkSource.new( - # allowed_ip_cidr_ranges: [ - # "0.0.0.0/0", "::/0" - # ], - # vpc_network_sources: [ - # {} - # network: "projects/storage-sdk-vendor/global/networks/default", - # allowed_ip_cidr_ranges: [ - # "10.0.0.0/8" - # ] - # } - # ] - # ), - # allow_all_service_agent_access: true - # ) ip_filter = { mode: "Disabled", public_network_source: { allowed_ip_cidr_ranges: [ "0.0.0.0/0", "::/0" ] + } } -} bucket = storage.create_bucket bucket_name do |b| b.ip_filter = ip_filter b.uniform_bucket_level_access = true end - - binding.pry - puts "Created bucket #{bucket_name} with IP filter enabled." end @@ -81,97 +59,20 @@ def update_bucket_with_ip_filter bucket_name: # The ID to give your GCS bucket # bucket_name = "your-unique-bucket-name" - require "google/cloud/resource_manager/v3" storage = Google::Cloud::Storage.new + bucket = storage.bucket bucket_name - ip_filter = Google::Apis::StorageV1::Bucket::IpFilter.new( - public_network_source: Google::Apis::StorageV1::Bucket::IpFilter::PublicNetworkSource.new( - allowed_ip_cidr_ranges: [ - "0.0.0.3/0", "::/0" - ] - )) - -# require "google/cloud/resource_manager" - -# Direct client initialization -client = Google::Cloud::ResourceManager::V3::Projects::Client.new - -project_id = storage.project -resource_name = "projects/#{project_id}" - -# 1. Get the current polGoogle::Cloud::Iam::V1::IAM::Client.newicy -# binding.pry -policy = client.get_iam_policy resource: resource_name -# binding.pry - -custom_role = "storage.buckets.exemptFromIpFilter" -custom_role_path = "#{resource_name}/roles/#{custom_role}" -service_account = "serviceAccount:#{storage.service_account_email}" -#{serviceAccount:storage-sdk-vendor@storage-sdk-vendor.iam.gserviceaccount.com} - -binding.pry -custom_role_binding = Google::Iam::V1::Binding.new( - role: custom_role_path, - members: [service_account] -) -policy.bindings << custom_role_binding - -client.set_iam_policy resource: resource_name, policy: policy -# Google::Cloud::PermissionDeniedError: 7:Permission 'resourcemanager.projects.setIamPolicy' denied on resource '//cloudresourcemanager.googleapis.com/projects/storage-sdk-vendor' (or it may not exist).. debug_error_string:{UNKNOWN:Error received from peer ipv4:74.125.23.95:443 {grpc_message:"Permission \'resourcemanager.projects.setIamPolicy\' denied on resource \'//cloudresourcemanager.googleapis.com/projects/storage-sdk-vendor\' (or it may not exist).", grpc_status:7}} (Google::Cloud::PermissionDeniedError) - - -# # Optional: Log the change -# puts "Updating project policy to include #{custom_role} for #{service_account}" -# end - -puts "Project policy updated successfully." - - # project_id = "storage-sdk-vendor" - # role_id = "GcsIpExemptRole" - # binding.pry - - # # Define the Custom Role - # role = Google::Cloud::Iam::V1::Role.new( - # title: "GCS IP Exemption Role", - # included_permissions: ["storage.buckets.exemptFromIpFilter"], - # stage: :GA - # ) - # resource = "projects/#{project_id}" - # storage_project = storage.project - # resource = "projects/#{storage_project}/roles/#{role_id}" - # storage_project.policy do |p| - # p.add "roles/#{role}", member - # end - - # # Call test_iam_permissions. - # # In Ruby, you can pass a Hash that matches the request structure. - # response = client.test_iam_permissions( - # resource: resource, - # permissions: ["storage.buckets.exemptFromIpFilter"] - # ) - - # # The response object contains a 'permissions' array of granted permissions - # puts "Granted permissions: #{response.permissions}" - - # response.permissions - - # # role = "projects/#{storage.project}/roles/GcsIpExemptRole" - # member = "serviceAccount:insecure-cloudtop-shared-user@cloudtop-prod-asia-east.iam.gserviceaccount.com" - - # bucket.policy requested_policy_version: 3 do |policy| - # policy.bindings.insert role: "roles/storage.buckets.exemptFromIpFilter", members: ["serviceAccount:insecure-cloudtop-shared-user@cloudtop-prod-asia-east.iam.gserviceaccount.com"] - # end - # # bucket = storage.create_bucket bucket_name - # # bucket.policy do |p| - # # p.add "roles/storage.buckets.exemptFromIpFilter", "serviceAccount:insecure-cloudtop-shared-user@cloudtop-prod-asia-east.iam.gserviceaccount.com" - # # end - # # binding.pry - + ip_filter = { + mode: "Disabled", + public_network_source: { + allowed_ip_cidr_ranges: [ + "0.0.0.4/0", "::/0" + ] + } + } bucket.update do |b| b.ip_filter = ip_filter end - - puts "Updated bucket #{bucket_name} with IP filter enabled." end # [END storage_create_bucket_with_ip_filter] From e4877d3e67417723945cd2ffab4b2580e588db7a Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Sun, 7 Jun 2026 16:32:27 +0000 Subject: [PATCH 05/18] adding tests and samples --- .../storage/bucket_ip_filter_test.rb | 79 ++ .../lib/google/cloud/storage/bucket.rb | 61 + .../lib/google/cloud/storage/project.rb | 12 + .../samples/acceptance/buckets_test.rb | 1203 +++++++++-------- .../storage_create_bucket_with_ip_filter.rb | 52 +- .../storage_delete_bucket_ip_filter.rb | 41 + .../storage_enable_bucket_ip_filter.rb | 44 + .../samples/storage_get_bucket_ip_filter.rb | 40 + .../samples/storage_list_bucket_ip_filters.rb | 41 + .../storage_update_bucket_with_ip_filter.rb | 44 + .../cloud/storage/bucket_ip_filter_test.rb | 151 +++ .../test/google/cloud/storage/project_test.rb | 32 + 12 files changed, 1165 insertions(+), 635 deletions(-) create mode 100644 google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb create mode 100644 google-cloud-storage/samples/storage_delete_bucket_ip_filter.rb create mode 100644 google-cloud-storage/samples/storage_enable_bucket_ip_filter.rb create mode 100644 google-cloud-storage/samples/storage_get_bucket_ip_filter.rb create mode 100644 google-cloud-storage/samples/storage_list_bucket_ip_filters.rb create mode 100644 google-cloud-storage/samples/storage_update_bucket_with_ip_filter.rb create mode 100644 google-cloud-storage/test/google/cloud/storage/bucket_ip_filter_test.rb diff --git a/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb b/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb new file mode 100644 index 000000000000..ca45ce2538a5 --- /dev/null +++ b/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb @@ -0,0 +1,79 @@ +# 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. + +require "storage_helper" + +describe Google::Cloud::Storage::Bucket, :storage do + let(:ip_filter_disabled) do + { + mode: "Disabled", + public_network_source: { + allowed_ip_cidr_ranges: ["0.0.0.0/0", "::/0"] + } + } + end + + let(:ip_filter_disabled_update) do + { + mode: "Disabled", + public_network_source: { + allowed_ip_cidr_ranges: ["8.8.8.8/32"] + } + } + end + + let(:bucket_name) { "#{$bucket_names[2]}-ip-filter" } + + it "creates, gets, updates, and deletes a bucket with ip_filter" do + # Create a bucket with ip_filter + bucket = storage.bucket(bucket_name) || + safe_gcs_execute {storage.create_bucket bucket_name, ip_filter: ip_filter_disabled} + + # _(bucket.ip_filter).wont_be_nil + _(bucket.ip_filter.mode).must_equal "Disabled" + _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["0.0.0.0/0", "::/0"] + + # Get the bucket and verify ip_filter + bucket = storage.bucket bucket_name + _(bucket.ip_filter).wont_be_nil + _(bucket.ip_filter.mode).must_equal "Disabled" + _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["0.0.0.0/0", "::/0"] + + # Update the ip_filter + safe_gcs_execute do + bucket.update do |b| + b.ip_filter = ip_filter_disabled_update + end + end + + _(bucket.ip_filter.mode).must_equal "Disabled" + _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["8.8.8.8/32"] + + # Disable ip_filter + safe_gcs_execute do + bucket.ip_filter = { + mode: "Disabled", + public_network_source: { + allowed_ip_cidr_ranges: [] + } + } + end + + _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_be_nil + + safe_gcs_execute { bucket.delete } + _(storage.bucket(bucket_name)).must_be :nil? + + end +end diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket.rb b/google-cloud-storage/lib/google/cloud/storage/bucket.rb index 2be3ade74b38..f8665b203250 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket.rb @@ -1410,10 +1410,71 @@ def delete if_metageneration_match: nil, if_metageneration_not_match: nil user_project: user_project end + ## + # The bucket's IP filter configuration. + # This value can be modified by calling {#ip_filter=}. + # + # @return [Google::Apis::StorageV1::Bucket::IpFilter, nil] The bucket's IP filter configuration, + # or `nil` if not configured. + # + # @example + # require "google/cloud/storage" + # + # storage = Google::Cloud::Storage.new + # + # bucket = storage.bucket "my-bucket" + # ip_filter = bucket.ip_filter + # if ip_filter + # puts "Mode: #{ip_filter.mode}" + # puts "Public CIDR: #{ip_filter.public_network_source&.allowed_ip_cidr_ranges}" + # end + # def ip_filter @gapi.ip_filter end + ## + # Sets the value for IP filter in the bucket. This value can + # be queried by calling {#ip_filter}. + # + # @param [Google::Apis::StorageV1::Bucket::IpFilter, Hash] new_ip_filter The bucket's new IP filter. + # Acceptable Hash structure: + # - :mode - [String] The mode of the IP filter. Acceptable values are: "Disabled", "Enforced", "Preview" + # - :public_network_source - [Hash] The public network source configuration: + # - :allowed_ip_cidr_ranges - [Array] Array of IP CIDR ranges allowed for public access. + # - :vpc_network_sources - [Array] The VPC network sources configuration: + # - :network - [String] The VPC network resource path, e.g. "projects/PROJECT_ID/global/networks/NETWORK_NAME". + # - :allowed_ip_cidr_ranges - [Array] Array of IP CIDR ranges allowed for VPC access. + # - :allow_cross_org_vpcs - [Boolean] Whether to allow cross-org VPC access. + # - :allow_all_service_agent_access - [Boolean] Whether to allow all service agent access. + # + # @example Enable IP filter with Hash: + # require "google/cloud/storage" + # + # storage = Google::Cloud::Storage.new + # + # bucket = storage.bucket "my-bucket" + # bucket.ip_filter = { + # mode: "Enabled", + # allow_all_service_agent_access: true, + # public_network_source: { + # allowed_ip_cidr_ranges: ["0.0.0.0/0", "::/0"] + # } + # } + # + # @example Clear/delete IP filter: + # require "google/cloud/storage" + # + # storage = Google::Cloud::Storage.new + # + # bucket = storage.bucket "my-bucket" + # bucket.ip_filter = { + # mode: "Disabled", + # public_network_source: { + # allowed_ip_cidr_ranges: [] + # } + # } + # def ip_filter= new_ip_filter @gapi.ip_filter = new_ip_filter || {} patch_gapi! :ip_filter diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index 3d971a53af50..c5ef54193d10 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -424,6 +424,18 @@ def bucket bucket_name, # @param [Boolean] enable_object_retention # When set to true, object retention is enabled for this bucket. # + # @param [Hash] ip_filter The bucket's IP filter configuration. + # Acceptable values are: + # - {Google::Apis::StorageV1::Bucket::IpFilter} object + # - Hash that can be converted to a {Google::Apis::StorageV1::Bucket::IpFilter} object + # - :mode - [String] The mode of the IP filter. Acceptable values are: "Disabled", "Enforced", "Preview" + # - :public_network_source - [Hash] The public network source configuration: + # - :allowed_ip_cidr_ranges - [Array] Array of IP CIDR ranges allowed for public access. + # - :vpc_network_sources - [Array] The VPC network sources configuration: + # - :network - [String] The VPC network resource path, e.g. "projects/PROJECT_ID/global/networks/NETWORK_NAME". + # - :allowed_ip_cidr_ranges - [Array] Array of IP CIDR ranges allowed for VPC access. + # - :allow_cross_org_vpcs - [Boolean] Whether to allow cross-org VPC access. + # - :allow_all_service_agent_access - [Boolean] Whether to allow all service agent access. # @yield [bucket] a block for configuring the bucket before it is # created # @yieldparam [Bucket] bucket the bucket object to be configured diff --git a/google-cloud-storage/samples/acceptance/buckets_test.rb b/google-cloud-storage/samples/acceptance/buckets_test.rb index f71a408b3ae7..7fbaf9995e7e 100644 --- a/google-cloud-storage/samples/acceptance/buckets_test.rb +++ b/google-cloud-storage/samples/acceptance/buckets_test.rb @@ -22,6 +22,11 @@ require_relative "../storage_create_bucket_dual_region" require_relative "../storage_create_bucket_hierarchical_namespace" require_relative "../storage_create_bucket_with_ip_filter" +require_relative "../storage_get_bucket_ip_filter" +require_relative "../storage_update_bucket_with_ip_filter" +require_relative "../storage_delete_bucket_ip_filter" +require_relative "../storage_enable_bucket_ip_filter" +require_relative "../storage_list_bucket_ip_filters" require_relative "../storage_create_bucket_with_object_retention" require_relative "../storage_define_bucket_website_configuration" require_relative "../storage_delete_bucket" @@ -64,618 +69,638 @@ let(:retention_period) { rand 1..99 } let(:bucket) { fixture_bucket } - # describe "bucket lifecycle" do - # it "create_bucket, create_bucket_class_location, list_buckets, get_bucket_metadata, delete_bucket" do - # # create_bucket - # bucket_name = random_bucket_name - # refute storage_client.bucket bucket_name - - # retry_resource_exhaustion do - # assert_output "Created bucket: #{bucket_name}\n" do - # create_bucket bucket_name: bucket_name - # end - # end - - # refute_nil storage_client.bucket bucket_name - - # # create_bucket_class_location - - # secondary_bucket_name = random_bucket_name - # location = "ASIA" - # storage_class = "COLDLINE" - # refute storage_client.bucket secondary_bucket_name - - # retry_resource_exhaustion do - # assert_output "Created bucket #{secondary_bucket_name} in #{location} with #{storage_class} class\n" do - # create_bucket_class_location bucket_name: secondary_bucket_name - # end - # end - - # secondary_bucket = storage_client.bucket secondary_bucket_name - # refute_nil secondary_bucket - # assert_equal location, secondary_bucket.location - # assert_equal storage_class, secondary_bucket.storage_class - - # # list_buckets - # out, _err = capture_io do - # list_buckets - # end - - # assert_includes out, "ruby-storage-samples-" - - # # get_bucket_metadata - # out, _err = capture_io do - # get_bucket_metadata bucket_name: bucket_name - # end - - # assert_includes out, bucket_name - - # # delete_bucket - # assert_output "Deleted bucket: #{bucket_name}\n" do - # delete_bucket bucket_name: bucket_name - # end - - - # refute storage_client.bucket bucket_name - - # delete_bucket_helper bucket_name - # delete_bucket_helper secondary_bucket_name - # end - # end - - # describe "storage_create_bucket_dual_region" do - # it "creates dual region bucket" do - # location = "US" - # region_1 = "US-EAST1" - # region_2 = "US-WEST1" - # location_type = "dual-region" - # bucket_name = random_bucket_name - # refute storage_client.bucket bucket_name - - # expected = "Bucket #{bucket_name} created:\n" - # expected += "- location: #{location}\n" - # expected += "- location_type: #{location_type}\n" - # expected += "- custom_placement_config:\n" - # expected += " - data_locations: #{[region_1, region_2]}\n" - - # retry_resource_exhaustion do - # assert_output expected do - # StorageCreateBucketDualRegion.new.storage_create_bucket_dual_region bucket_name: bucket_name, - # region_1: region_1, - # region_2: region_2 - # end - # end - - # refute_nil storage_client.bucket bucket_name - - # delete_bucket_helper bucket_name - # end - # end - - # describe "storage_create_bucket_hierarchical_namespace" do - # it "creates hierarchical namespace enabled bucket" do - # bucket_name = random_bucket_name - # refute storage_client.bucket bucket_name - - # expected = "Created bucket #{bucket_name} with Hierarchical Namespace enabled.\n" - - # retry_resource_exhaustion do - # assert_output expected do - # create_bucket_hierarchical_namespace bucket_name: bucket_name - # end - # end - - # refute_nil storage_client.bucket bucket_name - - # delete_bucket_helper bucket_name - # end - # end - - describe "storage_create_bucket_ip_filter" do + describe "bucket lifecycle" do + it "create_bucket, create_bucket_class_location, list_buckets, get_bucket_metadata, delete_bucket" do + # create_bucket + bucket_name = random_bucket_name + refute storage_client.bucket bucket_name - before :all do - @bucket_name = random_bucket_name + retry_resource_exhaustion do + assert_output "Created bucket: #{bucket_name}\n" do + create_bucket bucket_name: bucket_name + end + end + + refute_nil storage_client.bucket bucket_name + + # create_bucket_class_location + + secondary_bucket_name = random_bucket_name + location = "ASIA" + storage_class = "COLDLINE" + refute storage_client.bucket secondary_bucket_name + + retry_resource_exhaustion do + assert_output "Created bucket #{secondary_bucket_name} in #{location} with #{storage_class} class\n" do + create_bucket_class_location bucket_name: secondary_bucket_name + end + end + + secondary_bucket = storage_client.bucket secondary_bucket_name + refute_nil secondary_bucket + assert_equal location, secondary_bucket.location + assert_equal storage_class, secondary_bucket.storage_class + + # list_buckets + out, _err = capture_io do + list_buckets + end + + assert_includes out, "ruby-storage-samples-" + + # get_bucket_metadata + out, _err = capture_io do + get_bucket_metadata bucket_name: bucket_name + end + + assert_includes out, bucket_name + + # delete_bucket + assert_output "Deleted bucket: #{bucket_name}\n" do + delete_bucket bucket_name: bucket_name + end + + + refute storage_client.bucket bucket_name + + delete_bucket_helper bucket_name + delete_bucket_helper secondary_bucket_name end + end - after :all do - # removes_bucket_ip_filter bucket_name: @bucket_name - # binding.pry + describe "storage_create_bucket_dual_region" do + it "creates dual region bucket" do + location = "US" + region_1 = "US-EAST1" + region_2 = "US-WEST1" + location_type = "dual-region" + bucket_name = random_bucket_name + refute storage_client.bucket bucket_name + + expected = "Bucket #{bucket_name} created:\n" + expected += "- location: #{location}\n" + expected += "- location_type: #{location_type}\n" + expected += "- custom_placement_config:\n" + expected += " - data_locations: #{[region_1, region_2]}\n" + + retry_resource_exhaustion do + assert_output expected do + StorageCreateBucketDualRegion.new.storage_create_bucket_dual_region bucket_name: bucket_name, + region_1: region_1, + region_2: region_2 + end + end - delete_bucket_helper @bucket_name + refute_nil storage_client.bucket bucket_name + + delete_bucket_helper bucket_name end + end + + describe "storage_create_bucket_hierarchical_namespace" do + it "creates hierarchical namespace enabled bucket" do + bucket_name = random_bucket_name + refute storage_client.bucket bucket_name + + expected = "Created bucket #{bucket_name} with Hierarchical Namespace enabled.\n" - it "creates IP filter enabled bucket" do - expected = "Created bucket #{@bucket_name} with IP filter enabled.\n" retry_resource_exhaustion do assert_output expected do - create_bucket_with_ip_filter bucket_name: @bucket_name + create_bucket_hierarchical_namespace bucket_name: bucket_name end end + + refute_nil storage_client.bucket bucket_name + + delete_bucket_helper bucket_name end + end + + describe "storage_bucket_ip_filter" do + let(:bucket_name) { random_bucket_name } - it "removes IP filter of an existing bucket" do - expected = "Updated IP filter for bucket #{@bucket_name}.\n" + after :all do + delete_bucket_helper bucket_name + end + + it "creates, updates, gets, lists, and removes IP filter config" do + # Creates IP filter enabled bucket + expected = "Created bucket #{bucket_name} with IP filter.\n" retry_resource_exhaustion do assert_output expected do - removes_bucket_ip_filter bucket_name: @bucket_name + create_bucket_with_ip_filter bucket_name: bucket_name + end + end + + # Updates IP filter of an existing bucket + expected = "Updated bucket #{bucket_name} with IP filter.\n" + retry_resource_exhaustion do + assert_output expected do + update_bucket_with_ip_filter bucket_name: bucket_name + end + end + + # Gets IP filter of an existing bucket + expected = "Bucket #{bucket_name} has IP filter mode: Disabled.\n" \ + "Allowed public network CIDR ranges: 8.8.8.8/32.\n" + retry_resource_exhaustion do + assert_output expected do + get_bucket_ip_filter bucket_name: bucket_name + end + end + + # Lists IP filter allowed ranges + expected = "IP filter mode: Disabled\n" \ + "Allowed range: 8.8.8.8/32\n" + retry_resource_exhaustion do + assert_output expected do + list_bucket_ip_filters bucket_name: bucket_name + end + end + + # Deletes IP filter of an existing bucket + expected = "Deleted IP filter for bucket #{bucket_name}.\n" + retry_resource_exhaustion do + assert_output expected do + delete_bucket_ip_filter bucket_name: bucket_name + end + end + + # Enables IP filter of an existing bucket (SKIPPED) + skip "SKIPPED : If Ip filter is enabled, we cannot access the bucket" + expected = "Enabled IP filter for bucket #{bucket_name}.\n" + retry_resource_exhaustion do + assert_output expected do + enable_bucket_ip_filter bucket_name: bucket_name end end end + end + + describe "storage_create_bucket_with_object_retention" do + it "creates a bucket with object retention enabled." do + bucket_name = random_bucket_name + refute storage_client.bucket bucket_name + + expected = "Created bucket #{bucket_name} with object retention setting: Enabled\n" - it "Updates IP filter of an existing bucket" do - expected = "Updated bucket #{@bucket_name} with IP filter enabled.\n" retry_resource_exhaustion do assert_output expected do - update_bucket_with_ip_filter bucket_name: @bucket_name + create_bucket_with_object_retention bucket_name: bucket_name end end + + refute_nil storage_client.bucket bucket_name + + file_name = "test_object_retention" + + bucket = storage_client.bucket bucket_name + + out, _err = capture_io do + set_object_retention_policy bucket_name: bucket.name, + content: "hello world", + destination_file_name: file_name + end + + assert_includes out, "Retention policy for file #{file_name}" + + file = bucket.file file_name + file.retention = { + mode: nil, + retain_until_time: nil, + override_unlocked_retention: true + } + delete_bucket_helper bucket_name end end - # describe "storage_create_bucket_with_object_retention" do - # it "creates a bucket with object retention enabled." do - # bucket_name = random_bucket_name - # refute storage_client.bucket bucket_name - - # expected = "Created bucket #{bucket_name} with object retention setting: Enabled\n" - - # retry_resource_exhaustion do - # assert_output expected do - # create_bucket_with_object_retention bucket_name: bucket_name - # end - # end - - # refute_nil storage_client.bucket bucket_name - - # file_name = "test_object_retention" - - # bucket = storage_client.bucket bucket_name - - # out, _err = capture_io do - # set_object_retention_policy bucket_name: bucket.name, - # content: "hello world", - # destination_file_name: file_name - # end - - # assert_includes out, "Retention policy for file #{file_name}" - - # file = bucket.file file_name - # file.retention = { - # mode: nil, - # retain_until_time: nil, - # override_unlocked_retention: true - # } - # delete_bucket_helper bucket_name - # end - # end - - # describe "autoclass" do - # it "get_autoclass, set_autoclass" do - # bucket_name = random_bucket_name - # refute storage_client.bucket bucket_name - - # storage_client.create_bucket bucket_name, autoclass_enabled: true - - # assert_output(/autoclass config set to true./) do - # get_autoclass bucket_name: bucket_name - # end - - # assert_output(/autoclass terminal storage class set to NEARLINE./) do - # get_autoclass bucket_name: bucket_name - # end - - # assert_output(/autoclass terminal storage class set to ARCHIVE./) do - # set_autoclass bucket_name: bucket_name, toggle: true, terminal_storage_class: "ARCHIVE" - # end - - # assert_output(/autoclass config set to false./) do - # set_autoclass bucket_name: bucket_name, toggle: false - # end - - # delete_bucket_helper bucket_name - # end - # end - - # describe "cors" do - # it "cors_configuration, remove_cors_configuration" do - # bucket.cors { |c| c.clear } - # assert bucket.cors.empty? - - # # cors_configuration - # assert_output "Set CORS policies for bucket #{bucket.name}\n" do - # cors_configuration bucket_name: bucket.name - # end - - # bucket.refresh! - # assert_equal 1, bucket.cors.count - # rule = bucket.cors.first - # assert_equal ["*"], rule.origin - # assert_equal ["PUT", "POST"], rule.methods - # assert_equal ["Content-Type", "x-goog-resumable"], rule.headers - # assert_equal 3600, rule.max_age - - # # remove_cors_configuration - # assert_output "Remove CORS policies for bucket #{bucket.name}\n" do - # remove_cors_configuration bucket_name: bucket.name - # end - # bucket.refresh! - # assert bucket.cors.empty? - # end - # end - - # describe "requester_pays" do - # it "enable_requester_pays, disable_requester_pays, get_requester_pays_status" do - # # enable_requester_pays - # bucket.requester_pays = false - - # assert_output "Requester pays has been enabled for #{bucket.name}\n" do - # enable_requester_pays bucket_name: bucket.name - # end - # bucket.refresh! - # assert bucket.requester_pays? - - # # get_requester_pays_status - # assert_output "Requester pays status is enabled for #{bucket.name}\n" do - # get_requester_pays_status bucket_name: bucket.name - # end - # assert bucket.requester_pays? - - # # disable_requester_pays - # assert_output "Requester pays has been disabled for #{bucket.name}\n" do - # disable_requester_pays bucket_name: bucket.name - # end - # bucket.refresh! - # refute bucket.requester_pays? - - # # get_requester_pays_status - # assert_output "Requester pays status is disabled for #{bucket.name}\n" do - # get_requester_pays_status bucket_name: bucket.name - # end - # refute bucket.requester_pays? - # end - # end - - # describe "uniform_bucket_level_access" do - # it "enable_uniform_bucket_level_access, get_uniform_bucket_level_access, disable_uniform_bucket_level_access" do - # # enable_uniform_bucket_level_access - # bucket.uniform_bucket_level_access = false - - # assert_output "Uniform bucket-level access was enabled for #{bucket.name}.\n" do - # enable_uniform_bucket_level_access bucket_name: bucket.name - # end - - # bucket.refresh! - # assert bucket.uniform_bucket_level_access? - - # # get_uniform_bucket_level_access - # assert_output "Uniform bucket-level access is enabled for #{bucket.name}.\nBucket " \ - # "will be locked on #{bucket.uniform_bucket_level_access_locked_at}.\n" do - # get_uniform_bucket_level_access bucket_name: bucket.name - # end - # assert bucket.uniform_bucket_level_access? - - # # disable_uniform_bucket_level_access - # assert_output "Uniform bucket-level access was disabled for #{bucket.name}.\n" do - # disable_uniform_bucket_level_access bucket_name: bucket.name - # end - - # bucket.refresh! - # refute bucket.uniform_bucket_level_access? - - # # get_uniform_bucket_level_access - # assert_output "Uniform bucket-level access is disabled for #{bucket.name}.\n" do - # get_uniform_bucket_level_access bucket_name: bucket.name - # end - # refute bucket.uniform_bucket_level_access? - - # bucket.uniform_bucket_level_access = false - # end - # end - - # describe "default Cloud KMS encryption key" do - # it "set_bucket_default_kms_key, bucket_delete_default_kms_key" do - # refute bucket.default_kms_key - - # # set_bucket_default_kms_key - # assert_output "Default KMS key for #{bucket.name} was set to #{kms_key}\n" do - # set_bucket_default_kms_key bucket_name: bucket.name, - # default_kms_key: kms_key - # end - - # bucket.refresh! - # assert_equal bucket.default_kms_key, kms_key - - # # bucket_delete_default_kms_key - # assert_output "Default KMS key was removed from #{bucket.name}\n" do - # bucket_delete_default_kms_key bucket_name: bucket.name - # end - - # bucket.refresh! - # refute bucket.default_kms_key - # end - # end - - # describe "get bucket class and location data" do - # bucket_name = random_bucket_name - # location = "US" - # storage_class = "COLDLINE" - - # it "get_bucket_class_and_location" do - # storage_client.create_bucket bucket_name, - # location: location, - # storage_class: storage_class - # expected_output = "Bucket #{bucket_name} storage class is " \ - # "#{storage_class}, and the location is #{location}\n" - # assert_output expected_output do - # get_bucket_class_and_location bucket_name: bucket_name - # end - # end - # end - - # describe "labels" do - # it "add_bucket_label, remove_bucket_label" do - # # add_bucket_label - # label_key = "label_key" - # label_value = "label_value" - - # assert_output "Added label #{label_key} with value #{label_value} to #{bucket.name}\n" do - # add_bucket_label bucket_name: bucket.name, - # label_value: label_value, - # label_key: label_key - # end - - # bucket.refresh! - # assert_equal bucket.labels[label_key], label_value - - # # remove_bucket_label - # assert_output "Deleted label #{label_key} from #{bucket.name}\n" do - # remove_bucket_label bucket_name: bucket.name, - # label_key: label_key - # end - - # bucket.refresh! - # assert bucket.labels[label_key].empty? - # end - # end - - # describe "lifecycle management" do - # let(:bucket) { create_bucket_helper random_bucket_name } - # after { delete_bucket_helper bucket.name } - - # it "enable_bucket_lifecycle_management, disable_bucket_lifecycle_management" do - # # enable_bucket_lifecycle_management - # out, _err = capture_io do - # enable_bucket_lifecycle_management bucket_name: bucket.name - # end - - # assert_includes out, "Lifecycle management is enabled" - - # # disable_bucket_lifecycle_management - # out, _err = capture_io do - # disable_bucket_lifecycle_management bucket_name: bucket.name - # end - - # assert_includes out, "Lifecycle management is disabled" - # end - # end - - # describe "retention policy" do - # let(:bucket) { create_bucket_helper random_bucket_name } - # after { delete_bucket_helper bucket.name } - - # it "set_retention_policy, get_retention_policy, remove_retention_policy" do - # # set_retention_policy - # assert_output "Retention period for #{bucket.name} is now #{retention_period} seconds.\n" do - # set_retention_policy bucket_name: bucket.name, - # retention_period: retention_period - # end - - # bucket.refresh! - # assert_equal bucket.retention_period, retention_period - - # # get_retention_policy - # out, _err = capture_io do - # get_retention_policy bucket_name: bucket.name - # end - - # assert_includes out, "period: #{retention_period}\n" - - # # remove_retention_policy - # assert_equal bucket.retention_period, retention_period - # assert_output "Retention policy for #{bucket.name} has been removed.\n" do - # remove_retention_policy bucket_name: bucket.name - # end - - # bucket.refresh! - # refute bucket.retention_period - - # # lock_retention_policy - # bucket.retention_period = retention_period - # out, _err = capture_io do - # lock_retention_policy bucket_name: bucket.name - # end - - # assert_includes out, "Retention policy for #{bucket.name} is now locked." - # bucket.refresh! - # assert bucket.retention_policy_locked? - - # # remove_retention_policy - # assert_output "Policy is locked and retention policy can't be removed.\n" do - # remove_retention_policy bucket_name: bucket.name - # end - # end - # end - - # describe "default_event_based_hold" do - # it "enable_default_event_based_hold, get_default_event_based_hold, disable_default_event_based_hold" do - # # enable_default_event_based_hold - # assert_output "Default event-based hold was enabled for #{bucket.name}.\n" do - # enable_default_event_based_hold bucket_name: bucket.name - # end - - # bucket.refresh! - # assert bucket.default_event_based_hold? - - # # get_default_event_based_hold - # assert_output "Default event-based hold is enabled for #{bucket.name}.\n" do - # get_default_event_based_hold bucket_name: bucket.name - # end - - # # disable_default_event_based_hold - # bucket.update do |b| - # b.default_event_based_hold = true - # end - - # assert_output "Default event-based hold was disabled for #{bucket.name}.\n" do - # disable_default_event_based_hold bucket_name: bucket.name - # end - - # bucket.refresh! - # refute bucket.default_event_based_hold? - - # # get_default_event_based_hold - # assert_output "Default event-based hold is not enabled for #{bucket.name}.\n" do - # get_default_event_based_hold bucket_name: bucket.name - # end - # end - # end - - # describe "storage_class" do - # it "change_default_storage_class" do - # assert_equal "STANDARD", bucket.storage_class - - # assert_output "Default storage class for bucket #{bucket.name} has been set to COLDLINE\n" do - # change_default_storage_class bucket_name: bucket.name - # end - - # bucket.refresh! - # assert_equal "COLDLINE", bucket.storage_class - # # teardown - # bucket.storage_class = "STANDARD" - # end - # end - - # describe "versioning" do - # it "enable_versioning, disable_versioning" do - # # enable_versioning - # bucket.versioning = false - - # assert_output "Versioning was enabled for bucket #{bucket.name}\n" do - # enable_versioning bucket_name: bucket.name - # end - # bucket.refresh! - # assert bucket.versioning? - - # # disable_versioning - # assert_output "Versioning was disabled for bucket #{bucket.name}\n" do - # disable_versioning bucket_name: bucket.name - # end - # bucket.refresh! - # refute bucket.versioning? - # end - # end - - # describe "website_configuration" do - # let(:main_page_suffix) { "index.html" } - # let(:not_found_page) { "404.html" } - - # it "define_bucket_website_configuration" do - # expected_out = "Static website bucket #{bucket.name} is set up to use #{main_page_suffix} as the index page " \ - # "and #{not_found_page} as the 404 page\n" - - # assert_output expected_out do - # define_bucket_website_configuration bucket_name: bucket.name, - # main_page_suffix: main_page_suffix, - # not_found_page: not_found_page - # end - - # bucket.refresh! - # assert_equal main_page_suffix, bucket.website_main - # assert_equal not_found_page, bucket.website_404 - # end - # end - - # describe "public_access_prevention" do - # it "set_public_access_prevention_enforced, get_public_access_prevention, " \ - # "set_public_access_prevention_inherited" do - # bucket.public_access_prevention = :inherited - # bucket.refresh! - # _(bucket.public_access_prevention).must_equal "inherited" - - # # set_public_access_prevention_enforced - # assert_output "Public access prevention is set to enforced for #{bucket.name}.\n" do - # set_public_access_prevention_enforced bucket_name: bucket.name - # end - - # bucket.refresh! - # _(bucket.public_access_prevention).must_equal "enforced" - - # # get_public_access_prevention - # assert_output "Public access prevention is 'enforced' for #{bucket.name}.\n" do - # get_public_access_prevention bucket_name: bucket.name - # end - # _(bucket.public_access_prevention).must_equal "enforced" - - # # set_public_access_prevention_inherited - # assert_output "Public access prevention is 'inherited' for #{bucket.name}.\n" do - # set_public_access_prevention_inherited bucket_name: bucket.name - # end - - # bucket.refresh! - # _(bucket.public_access_prevention).must_equal "inherited" - # bucket.public_access_prevention = :inherited - # end - # end - - # describe "storage move file" do - # let(:source_file) { "file_1_name_#{SecureRandom.hex}.txt" } - # let(:destination_file) { "file_2_name_#{SecureRandom.hex}.txt" } - # let :hns_bucket do - # hierarchical_namespace = Google::Apis::StorageV1::Bucket::HierarchicalNamespace.new enabled: true - # storage_client.create_bucket random_bucket_name do |b| - # b.uniform_bucket_level_access = true - # b.hierarchical_namespace = hierarchical_namespace - # end - # end - # let :create_source_file do - # file_content = "A" * (3 * 1024 * 1024) # 3 MB of 'A' characters - # file = StringIO.new file_content - # hns_bucket.create_file file, source_file - # end - # it "file is moved and old file is deleted" do - # create_source_file - # out, _err = capture_io do - # move_object bucket_name: hns_bucket.name, source_file_name: source_file, destination_file_name: destination_file - # end - # assert_includes out, "New File #{destination_file} created\n" - # refute_nil(hns_bucket.file(destination_file)) - # assert_nil(hns_bucket.file(source_file)) - # end - - # it "raises error if source and destination are having same filename" do - # create_source_file - # exception = assert_raises Google::Cloud::InvalidArgumentError do - # move_object bucket_name: hns_bucket.name, source_file_name: source_file, destination_file_name: source_file - # end - # assert_equal "invalid: Source and destination object names must be different.", exception.message - # end - # end - - # describe "list buckets with partial success" do - # it 'returns a list of bucket names if return_partial_success_flag is true' do - # result = list_buckets_with_partial_success return_partial_success_flag: true - # assert_kind_of Array, result - # assert result.all? { |n| n.is_a? String }, "expected all items to be String" - # end - - # it 'returns nil for unreachable if return_partial_success_flag is false' do - # result = list_buckets_with_partial_success return_partial_success_flag: false - # assert_nil result - # end - - # it 'returns nil for unreachable if return_partial_success_flag is not passed' do - # result = list_buckets_with_partial_success return_partial_success_flag: nil - # assert_nil result - # end - # end + describe "autoclass" do + it "get_autoclass, set_autoclass" do + bucket_name = random_bucket_name + refute storage_client.bucket bucket_name + + storage_client.create_bucket bucket_name, autoclass_enabled: true + + assert_output(/autoclass config set to true./) do + get_autoclass bucket_name: bucket_name + end + + assert_output(/autoclass terminal storage class set to NEARLINE./) do + get_autoclass bucket_name: bucket_name + end + + assert_output(/autoclass terminal storage class set to ARCHIVE./) do + set_autoclass bucket_name: bucket_name, toggle: true, terminal_storage_class: "ARCHIVE" + end + + assert_output(/autoclass config set to false./) do + set_autoclass bucket_name: bucket_name, toggle: false + end + + delete_bucket_helper bucket_name + end + end + + describe "cors" do + it "cors_configuration, remove_cors_configuration" do + bucket.cors { |c| c.clear } + assert bucket.cors.empty? + + # cors_configuration + assert_output "Set CORS policies for bucket #{bucket.name}\n" do + cors_configuration bucket_name: bucket.name + end + + bucket.refresh! + assert_equal 1, bucket.cors.count + rule = bucket.cors.first + assert_equal ["*"], rule.origin + assert_equal ["PUT", "POST"], rule.methods + assert_equal ["Content-Type", "x-goog-resumable"], rule.headers + assert_equal 3600, rule.max_age + + # remove_cors_configuration + assert_output "Remove CORS policies for bucket #{bucket.name}\n" do + remove_cors_configuration bucket_name: bucket.name + end + bucket.refresh! + assert bucket.cors.empty? + end + end + + describe "requester_pays" do + it "enable_requester_pays, disable_requester_pays, get_requester_pays_status" do + # enable_requester_pays + bucket.requester_pays = false + + assert_output "Requester pays has been enabled for #{bucket.name}\n" do + enable_requester_pays bucket_name: bucket.name + end + bucket.refresh! + assert bucket.requester_pays? + + # get_requester_pays_status + assert_output "Requester pays status is enabled for #{bucket.name}\n" do + get_requester_pays_status bucket_name: bucket.name + end + assert bucket.requester_pays? + + # disable_requester_pays + assert_output "Requester pays has been disabled for #{bucket.name}\n" do + disable_requester_pays bucket_name: bucket.name + end + bucket.refresh! + refute bucket.requester_pays? + + # get_requester_pays_status + assert_output "Requester pays status is disabled for #{bucket.name}\n" do + get_requester_pays_status bucket_name: bucket.name + end + refute bucket.requester_pays? + end + end + + describe "uniform_bucket_level_access" do + it "enable_uniform_bucket_level_access, get_uniform_bucket_level_access, disable_uniform_bucket_level_access" do + # enable_uniform_bucket_level_access + bucket.uniform_bucket_level_access = false + + assert_output "Uniform bucket-level access was enabled for #{bucket.name}.\n" do + enable_uniform_bucket_level_access bucket_name: bucket.name + end + + bucket.refresh! + assert bucket.uniform_bucket_level_access? + + # get_uniform_bucket_level_access + assert_output "Uniform bucket-level access is enabled for #{bucket.name}.\nBucket " \ + "will be locked on #{bucket.uniform_bucket_level_access_locked_at}.\n" do + get_uniform_bucket_level_access bucket_name: bucket.name + end + assert bucket.uniform_bucket_level_access? + + # disable_uniform_bucket_level_access + assert_output "Uniform bucket-level access was disabled for #{bucket.name}.\n" do + disable_uniform_bucket_level_access bucket_name: bucket.name + end + + bucket.refresh! + refute bucket.uniform_bucket_level_access? + + # get_uniform_bucket_level_access + assert_output "Uniform bucket-level access is disabled for #{bucket.name}.\n" do + get_uniform_bucket_level_access bucket_name: bucket.name + end + refute bucket.uniform_bucket_level_access? + + bucket.uniform_bucket_level_access = false + end + end + + describe "default Cloud KMS encryption key" do + it "set_bucket_default_kms_key, bucket_delete_default_kms_key" do + refute bucket.default_kms_key + + # set_bucket_default_kms_key + assert_output "Default KMS key for #{bucket.name} was set to #{kms_key}\n" do + set_bucket_default_kms_key bucket_name: bucket.name, + default_kms_key: kms_key + end + + bucket.refresh! + assert_equal bucket.default_kms_key, kms_key + + # bucket_delete_default_kms_key + assert_output "Default KMS key was removed from #{bucket.name}\n" do + bucket_delete_default_kms_key bucket_name: bucket.name + end + + bucket.refresh! + refute bucket.default_kms_key + end + end + + describe "get bucket class and location data" do + bucket_name = random_bucket_name + location = "US" + storage_class = "COLDLINE" + + it "get_bucket_class_and_location" do + storage_client.create_bucket bucket_name, + location: location, + storage_class: storage_class + expected_output = "Bucket #{bucket_name} storage class is " \ + "#{storage_class}, and the location is #{location}\n" + assert_output expected_output do + get_bucket_class_and_location bucket_name: bucket_name + end + end + end + + describe "labels" do + it "add_bucket_label, remove_bucket_label" do + # add_bucket_label + label_key = "label_key" + label_value = "label_value" + + assert_output "Added label #{label_key} with value #{label_value} to #{bucket.name}\n" do + add_bucket_label bucket_name: bucket.name, + label_value: label_value, + label_key: label_key + end + + bucket.refresh! + assert_equal bucket.labels[label_key], label_value + + # remove_bucket_label + assert_output "Deleted label #{label_key} from #{bucket.name}\n" do + remove_bucket_label bucket_name: bucket.name, + label_key: label_key + end + + bucket.refresh! + assert bucket.labels[label_key].empty? + end + end + + describe "lifecycle management" do + let(:bucket) { create_bucket_helper random_bucket_name } + after { delete_bucket_helper bucket.name } + + it "enable_bucket_lifecycle_management, disable_bucket_lifecycle_management" do + # enable_bucket_lifecycle_management + out, _err = capture_io do + enable_bucket_lifecycle_management bucket_name: bucket.name + end + + assert_includes out, "Lifecycle management is enabled" + + # disable_bucket_lifecycle_management + out, _err = capture_io do + disable_bucket_lifecycle_management bucket_name: bucket.name + end + + assert_includes out, "Lifecycle management is disabled" + end + end + + describe "retention policy" do + let(:bucket) { create_bucket_helper random_bucket_name } + after { delete_bucket_helper bucket.name } + + it "set_retention_policy, get_retention_policy, remove_retention_policy" do + # set_retention_policy + assert_output "Retention period for #{bucket.name} is now #{retention_period} seconds.\n" do + set_retention_policy bucket_name: bucket.name, + retention_period: retention_period + end + + bucket.refresh! + assert_equal bucket.retention_period, retention_period + + # get_retention_policy + out, _err = capture_io do + get_retention_policy bucket_name: bucket.name + end + + assert_includes out, "period: #{retention_period}\n" + + # remove_retention_policy + assert_equal bucket.retention_period, retention_period + assert_output "Retention policy for #{bucket.name} has been removed.\n" do + remove_retention_policy bucket_name: bucket.name + end + + bucket.refresh! + refute bucket.retention_period + + # lock_retention_policy + bucket.retention_period = retention_period + out, _err = capture_io do + lock_retention_policy bucket_name: bucket.name + end + + assert_includes out, "Retention policy for #{bucket.name} is now locked." + bucket.refresh! + assert bucket.retention_policy_locked? + + # remove_retention_policy + assert_output "Policy is locked and retention policy can't be removed.\n" do + remove_retention_policy bucket_name: bucket.name + end + end + end + + describe "default_event_based_hold" do + it "enable_default_event_based_hold, get_default_event_based_hold, disable_default_event_based_hold" do + # enable_default_event_based_hold + assert_output "Default event-based hold was enabled for #{bucket.name}.\n" do + enable_default_event_based_hold bucket_name: bucket.name + end + + bucket.refresh! + assert bucket.default_event_based_hold? + + # get_default_event_based_hold + assert_output "Default event-based hold is enabled for #{bucket.name}.\n" do + get_default_event_based_hold bucket_name: bucket.name + end + + # disable_default_event_based_hold + bucket.update do |b| + b.default_event_based_hold = true + end + + assert_output "Default event-based hold was disabled for #{bucket.name}.\n" do + disable_default_event_based_hold bucket_name: bucket.name + end + + bucket.refresh! + refute bucket.default_event_based_hold? + + # get_default_event_based_hold + assert_output "Default event-based hold is not enabled for #{bucket.name}.\n" do + get_default_event_based_hold bucket_name: bucket.name + end + end + end + + describe "storage_class" do + it "change_default_storage_class" do + assert_equal "STANDARD", bucket.storage_class + + assert_output "Default storage class for bucket #{bucket.name} has been set to COLDLINE\n" do + change_default_storage_class bucket_name: bucket.name + end + + bucket.refresh! + assert_equal "COLDLINE", bucket.storage_class + # teardown + bucket.storage_class = "STANDARD" + end + end + + describe "versioning" do + it "enable_versioning, disable_versioning" do + # enable_versioning + bucket.versioning = false + + assert_output "Versioning was enabled for bucket #{bucket.name}\n" do + enable_versioning bucket_name: bucket.name + end + bucket.refresh! + assert bucket.versioning? + + # disable_versioning + assert_output "Versioning was disabled for bucket #{bucket.name}\n" do + disable_versioning bucket_name: bucket.name + end + bucket.refresh! + refute bucket.versioning? + end + end + + describe "website_configuration" do + let(:main_page_suffix) { "index.html" } + let(:not_found_page) { "404.html" } + + it "define_bucket_website_configuration" do + expected_out = "Static website bucket #{bucket.name} is set up to use #{main_page_suffix} as the index page " \ + "and #{not_found_page} as the 404 page\n" + + assert_output expected_out do + define_bucket_website_configuration bucket_name: bucket.name, + main_page_suffix: main_page_suffix, + not_found_page: not_found_page + end + + bucket.refresh! + assert_equal main_page_suffix, bucket.website_main + assert_equal not_found_page, bucket.website_404 + end + end + + describe "public_access_prevention" do + it "set_public_access_prevention_enforced, get_public_access_prevention, " \ + "set_public_access_prevention_inherited" do + bucket.public_access_prevention = :inherited + bucket.refresh! + _(bucket.public_access_prevention).must_equal "inherited" + + # set_public_access_prevention_enforced + assert_output "Public access prevention is set to enforced for #{bucket.name}.\n" do + set_public_access_prevention_enforced bucket_name: bucket.name + end + + bucket.refresh! + _(bucket.public_access_prevention).must_equal "enforced" + + # get_public_access_prevention + assert_output "Public access prevention is 'enforced' for #{bucket.name}.\n" do + get_public_access_prevention bucket_name: bucket.name + end + _(bucket.public_access_prevention).must_equal "enforced" + + # set_public_access_prevention_inherited + assert_output "Public access prevention is 'inherited' for #{bucket.name}.\n" do + set_public_access_prevention_inherited bucket_name: bucket.name + end + + bucket.refresh! + _(bucket.public_access_prevention).must_equal "inherited" + bucket.public_access_prevention = :inherited + end + end + + describe "storage move file" do + let(:source_file) { "file_1_name_#{SecureRandom.hex}.txt" } + let(:destination_file) { "file_2_name_#{SecureRandom.hex}.txt" } + let :hns_bucket do + hierarchical_namespace = Google::Apis::StorageV1::Bucket::HierarchicalNamespace.new enabled: true + storage_client.create_bucket random_bucket_name do |b| + b.uniform_bucket_level_access = true + b.hierarchical_namespace = hierarchical_namespace + end + end + let :create_source_file do + file_content = "A" * (3 * 1024 * 1024) # 3 MB of 'A' characters + file = StringIO.new file_content + hns_bucket.create_file file, source_file + end + it "file is moved and old file is deleted" do + create_source_file + out, _err = capture_io do + move_object bucket_name: hns_bucket.name, source_file_name: source_file, destination_file_name: destination_file + end + assert_includes out, "New File #{destination_file} created\n" + refute_nil(hns_bucket.file(destination_file)) + assert_nil(hns_bucket.file(source_file)) + end + + it "raises error if source and destination are having same filename" do + create_source_file + exception = assert_raises Google::Cloud::InvalidArgumentError do + move_object bucket_name: hns_bucket.name, source_file_name: source_file, destination_file_name: source_file + end + assert_equal "invalid: Source and destination object names must be different.", exception.message + end + end + + describe "list buckets with partial success" do + it 'returns a list of bucket names if return_partial_success_flag is true' do + result = list_buckets_with_partial_success return_partial_success_flag: true + assert_kind_of Array, result + assert result.all? { |n| n.is_a? String }, "expected all items to be String" + end + + it 'returns nil for unreachable if return_partial_success_flag is false' do + result = list_buckets_with_partial_success return_partial_success_flag: false + assert_nil result + end + + it 'returns nil for unreachable if return_partial_success_flag is not passed' do + result = list_buckets_with_partial_success return_partial_success_flag: nil + assert_nil result + end + end end diff --git a/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb index c31d03a076d7..b155ad7fe391 100644 --- a/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb +++ b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb @@ -1,4 +1,4 @@ -# Copyright 2024 Google LLC +# 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. @@ -20,60 +20,20 @@ def create_bucket_with_ip_filter bucket_name: require "google/cloud/storage" storage = Google::Cloud::Storage.new - ip_filter = { - mode: "Disabled", - public_network_source: { - allowed_ip_cidr_ranges: [ - "0.0.0.0/0", "::/0" - ] - } - } - - bucket = storage.create_bucket bucket_name do |b| - b.ip_filter = ip_filter - b.uniform_bucket_level_access = true - end - puts "Created bucket #{bucket_name} with IP filter enabled." -end - -def removes_bucket_ip_filter bucket_name: - # The ID of your GCS bucket - # bucket_name = "your-unique-bucket-name" - - require "google/cloud/storage" - - storage = Google::Cloud::Storage.new - bucket = storage.bucket bucket_name - - ip_filter = Google::Apis::StorageV1::Bucket::IpFilter.new( - mode: "Disabled" - ) - bucket.update do |b| - b.ip_filter = ip_filter - end - - puts "Updated IP filter for bucket #{bucket_name}." -end - -def update_bucket_with_ip_filter bucket_name: - # The ID to give your GCS bucket - # bucket_name = "your-unique-bucket-name" - - storage = Google::Cloud::Storage.new - bucket = storage.bucket bucket_name - ip_filter = { mode: "Disabled", public_network_source: { allowed_ip_cidr_ranges: [ - "0.0.0.4/0", "::/0" + "0.0.0.0/0", "::/0" ] } } - bucket.update do |b| + + bucket = storage.create_bucket bucket_name do |b| b.ip_filter = ip_filter + b.uniform_bucket_level_access = true end - puts "Updated bucket #{bucket_name} with IP filter enabled." + puts "Created bucket #{bucket_name} with IP filter." end # [END storage_create_bucket_with_ip_filter] diff --git a/google-cloud-storage/samples/storage_delete_bucket_ip_filter.rb b/google-cloud-storage/samples/storage_delete_bucket_ip_filter.rb new file mode 100644 index 000000000000..f5366527769b --- /dev/null +++ b/google-cloud-storage/samples/storage_delete_bucket_ip_filter.rb @@ -0,0 +1,41 @@ +# 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 +# +# http://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. + +# [START storage_delete_ip_filtering_rules] +def delete_bucket_ip_filter bucket_name: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + require "google/cloud/storage" + + storage = Google::Cloud::Storage.new + bucket = storage.bucket bucket_name + + # Clear IP filter configuration by setting it to empty/disabled + bucket.update do |b| + b.ip_filter = { + mode: "Disabled", + public_network_source: { + allowed_ip_cidr_ranges: [] + } + } + end + + puts "Deleted IP filter for bucket #{bucket_name}." +end +# [END storage_delete_ip_filtering_rules] + +if $PROGRAM_NAME == __FILE__ + delete_bucket_ip_filter bucket_name: ARGV.shift +end diff --git a/google-cloud-storage/samples/storage_enable_bucket_ip_filter.rb b/google-cloud-storage/samples/storage_enable_bucket_ip_filter.rb new file mode 100644 index 000000000000..6e96767479f1 --- /dev/null +++ b/google-cloud-storage/samples/storage_enable_bucket_ip_filter.rb @@ -0,0 +1,44 @@ +# 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 +# +# http://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. + +# [START storage_enable_bucket_ip_filter] +def enable_bucket_ip_filter bucket_name: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + require "google/cloud/storage" + + storage = Google::Cloud::Storage.new + bucket = storage.bucket bucket_name + + # Enable the IP filter configuration. + ip_filter = { + mode: "Enabled", + allow_all_service_agent_access: true, + public_network_source: { + allowed_ip_cidr_ranges: ["0.0.0.0/0", "::/0"] + } + } + + bucket.update do |b| + b.ip_filter = ip_filter + end + + puts "Enabled IP filter for bucket #{bucket_name}." +end +# [END storage_enable_bucket_ip_filter] + +if $PROGRAM_NAME == __FILE__ + enable_bucket_ip_filter bucket_name: ARGV.shift +end diff --git a/google-cloud-storage/samples/storage_get_bucket_ip_filter.rb b/google-cloud-storage/samples/storage_get_bucket_ip_filter.rb new file mode 100644 index 000000000000..62f872a3fa43 --- /dev/null +++ b/google-cloud-storage/samples/storage_get_bucket_ip_filter.rb @@ -0,0 +1,40 @@ +# 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 +# +# http://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. + +# [START storage_get_bucket_ip_filter] +def get_bucket_ip_filter bucket_name: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + require "google/cloud/storage" + + storage = Google::Cloud::Storage.new + bucket = storage.bucket bucket_name + + ip_filter = bucket.ip_filter + + if ip_filter + puts "Bucket #{bucket_name} has IP filter mode: #{ip_filter.mode}." + if ip_filter.public_network_source + puts "Allowed public network CIDR ranges: #{ip_filter.public_network_source.allowed_ip_cidr_ranges.join(', ')}." + end + else + puts "Bucket #{bucket_name} does not have an IP filter configuration." + end +end +# [END storage_get_bucket_ip_filter] + +if $PROGRAM_NAME == __FILE__ + get_bucket_ip_filter bucket_name: ARGV.shift +end diff --git a/google-cloud-storage/samples/storage_list_bucket_ip_filters.rb b/google-cloud-storage/samples/storage_list_bucket_ip_filters.rb new file mode 100644 index 000000000000..e0d294cfc436 --- /dev/null +++ b/google-cloud-storage/samples/storage_list_bucket_ip_filters.rb @@ -0,0 +1,41 @@ +# 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 +# +# http://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. + +# [START storage_list_bucket_ip_filters] +def list_bucket_ip_filters bucket_name: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + require "google/cloud/storage" + + storage = Google::Cloud::Storage.new + bucket = storage.bucket bucket_name + + ip_filter = bucket.ip_filter + if ip_filter + puts "IP filter mode: #{ip_filter.mode}" + if ip_filter.public_network_source + ip_filter.public_network_source.allowed_ip_cidr_ranges.each do |range| + puts "Allowed range: #{range}" + end + end + else + puts "No IP filter configured on bucket #{bucket_name}." + end +end +# [END storage_list_bucket_ip_filters] + +if $PROGRAM_NAME == __FILE__ + list_bucket_ip_filters bucket_name: ARGV.shift +end diff --git a/google-cloud-storage/samples/storage_update_bucket_with_ip_filter.rb b/google-cloud-storage/samples/storage_update_bucket_with_ip_filter.rb new file mode 100644 index 000000000000..dd616d1b1001 --- /dev/null +++ b/google-cloud-storage/samples/storage_update_bucket_with_ip_filter.rb @@ -0,0 +1,44 @@ +# 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 +# +# http://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. + +# [START storage_update_bucket_with_ip_filter] +def update_bucket_with_ip_filter bucket_name: + # The ID of your GCS bucket + # bucket_name = "your-unique-bucket-name" + + require "google/cloud/storage" + + storage = Google::Cloud::Storage.new + bucket = storage.bucket bucket_name + + ip_filter = { + mode: "Disabled", + public_network_source: { + allowed_ip_cidr_ranges: [ + "8.8.8.8/32" + ] + } + } + + bucket.update do |b| + b.ip_filter = ip_filter + end + + puts "Updated bucket #{bucket_name} with IP filter." +end +# [END storage_update_bucket_with_ip_filter] + +if $PROGRAM_NAME == __FILE__ + update_bucket_with_ip_filter bucket_name: ARGV.shift +end diff --git a/google-cloud-storage/test/google/cloud/storage/bucket_ip_filter_test.rb b/google-cloud-storage/test/google/cloud/storage/bucket_ip_filter_test.rb new file mode 100644 index 000000000000..9706f05c8b74 --- /dev/null +++ b/google-cloud-storage/test/google/cloud/storage/bucket_ip_filter_test.rb @@ -0,0 +1,151 @@ +# 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. + +require "helper" + +describe Google::Cloud::Storage::Bucket, :ip_filter, :mock_storage do + let(:bucket_name) { "new-bucket-#{Time.now.to_i}" } + let(:bucket_hash) { random_bucket_hash name: bucket_name } + let(:bucket_gapi) { Google::Apis::StorageV1::Bucket.from_json bucket_hash.to_json } + let(:bucket) { Google::Cloud::Storage::Bucket.from_gapi bucket_gapi, storage.service } + + let(:ip_filter_hash) do + { + "mode" => "Disabled", + "publicNetworkSource" => { + "allowedIpCidrRanges" => ["0.0.0.0/0", "::/0"] + } + } + end + let(:ip_filter_gapi) { Google::Apis::StorageV1::Bucket::IpFilter.from_json ip_filter_hash.to_json } + + it "knows its ip_filter value" do + _(bucket.ip_filter).must_be_nil + + bucket_gapi.ip_filter = ip_filter_gapi + _(bucket.ip_filter).wont_be_nil + _(bucket.ip_filter.mode).must_equal "Disabled" + _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["0.0.0.0/0", "::/0"] + end + + + it "updates its ip_filter" do + mock = Minitest::Mock.new + mock.expect :update_bucket, resp_bucket_gapi(bucket_hash, ip_filter: ip_filter_gapi), + [bucket_name, patch_bucket_gapi(ip_filter: ip_filter_gapi)], **update_bucket_args(options: {retries: 0}) + + bucket.service.mocked_service = mock + + _(bucket.ip_filter).must_be_nil + + bucket.update do |b| + b.ip_filter = ip_filter_gapi + end + + _(bucket.ip_filter).wont_be_nil + _(bucket.ip_filter.mode).must_equal "Disabled" + _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["0.0.0.0/0", "::/0"] + + mock.verify + end + + it "enables its ip_filter" do + enable_ip_filter_hash = { + "mode" => "Enabled", + "allowAllServiceAgentAccess" => true, + "publicNetworkSource" => { + "allowedIpCidrRanges" => ["0.0.0.0/0", "::/0"] + } + } + enable_ip_filter_gapi = Google::Apis::StorageV1::Bucket::IpFilter.from_json enable_ip_filter_hash.to_json + + mock = Minitest::Mock.new + mock.expect :update_bucket, resp_bucket_gapi(bucket_hash, ip_filter: enable_ip_filter_gapi), + [bucket_name, patch_bucket_gapi(ip_filter: enable_ip_filter_gapi)], **update_bucket_args(options: {retries: 0}) + + bucket.service.mocked_service = mock + + bucket.update do |b| + b.ip_filter = { + mode: "Enabled", + allow_all_service_agent_access: true, + public_network_source: { + allowed_ip_cidr_ranges: ["0.0.0.0/0", "::/0"] + } + } + end + + _(bucket.ip_filter).wont_be_nil + _(bucket.ip_filter.mode).must_equal "Enabled" + _(bucket.ip_filter.allow_all_service_agent_access).must_equal true + _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["0.0.0.0/0", "::/0"] + + mock.verify + end + + it "deletes its ip_filter" do + delete_ip_filter_hash = { + "mode" => "Disabled", + "publicNetworkSource" => { + "allowedIpCidrRanges" => [] + } + } + delete_ip_filter_gapi = Google::Apis::StorageV1::Bucket::IpFilter.from_json delete_ip_filter_hash.to_json + + mock = Minitest::Mock.new + mock.expect :update_bucket, resp_bucket_gapi(bucket_hash, ip_filter: delete_ip_filter_gapi), + [bucket_name, patch_bucket_gapi(ip_filter: delete_ip_filter_gapi)], **update_bucket_args(options: {retries: 0}) + + bucket.service.mocked_service = mock + + bucket.update do |b| + b.ip_filter = { + mode: "Disabled", + public_network_source: { + allowed_ip_cidr_ranges: [] + } + } + end + + _(bucket.ip_filter).wont_be_nil + _(bucket.ip_filter.mode).must_equal "Disabled" + _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_be_empty + + mock.verify + end + + it "clears its ip_filter with nil" do + mock = Minitest::Mock.new + mock.expect :patch_bucket, resp_bucket_gapi(bucket_hash, ip_filter: Google::Apis::StorageV1::Bucket::IpFilter.new), + [bucket_name, patch_bucket_gapi(ip_filter: Google::Apis::StorageV1::Bucket::IpFilter.new)], **patch_bucket_args(options: {retries: 0}) + + bucket.service.mocked_service = mock + + bucket.ip_filter = nil + + mock.verify + end + + def patch_bucket_gapi ip_filter: nil + Google::Apis::StorageV1::Bucket.new( + ip_filter: ip_filter + ) + end + + def resp_bucket_gapi bucket_hash, ip_filter: nil + b = Google::Apis::StorageV1::Bucket.from_json bucket_hash.to_json + b.ip_filter = ip_filter + b + end +end diff --git a/google-cloud-storage/test/google/cloud/storage/project_test.rb b/google-cloud-storage/test/google/cloud/storage/project_test.rb index 05724226ca34..344bd8c88378 100644 --- a/google-cloud-storage/test/google/cloud/storage/project_test.rb +++ b/google-cloud-storage/test/google/cloud/storage/project_test.rb @@ -597,6 +597,38 @@ def creds.is_a? target _(bucket.hierarchical_namespace[:enabled]).must_equal false end + it "creates a bucket with block ip_filter" do + mock = Minitest::Mock.new + created_bucket = create_bucket_gapi bucket_name + ip_filter_hash = { + "mode" => "Disabled", + "publicNetworkSource" => { + "allowedIpCidrRanges" => ["0.0.0.0/0", "::/0"] + } + } + ip_filter_gapi = Google::Apis::StorageV1::Bucket::IpFilter.from_json ip_filter_hash.to_json + created_bucket.ip_filter = ip_filter_gapi + resp_bucket = bucket_with_location created_bucket + + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + storage.service.mocked_service = mock + + bucket = storage.create_bucket bucket_name do |b| + b.ip_filter = { + mode: "Disabled", + public_network_source: { + allowed_ip_cidr_ranges: ["0.0.0.0/0", "::/0"] + } + } + end + mock.verify + + _(bucket).must_be_kind_of Google::Cloud::Storage::Bucket + _(bucket.ip_filter).wont_be_nil + _(bucket.ip_filter.mode).must_equal "Disabled" + _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["0.0.0.0/0", "::/0"] + end + it "raises when creating a bucket with a blank name" do bucket_name = "" From e6ccd0c2594c0a28a140474dccfc3ed784e185aa Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Mon, 8 Jun 2026 11:00:21 +0000 Subject: [PATCH 06/18] remove pry --- google-cloud-storage/samples/acceptance/buckets_test.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/google-cloud-storage/samples/acceptance/buckets_test.rb b/google-cloud-storage/samples/acceptance/buckets_test.rb index 7fbaf9995e7e..c2a93c518022 100644 --- a/google-cloud-storage/samples/acceptance/buckets_test.rb +++ b/google-cloud-storage/samples/acceptance/buckets_test.rb @@ -61,7 +61,6 @@ require_relative "../storage_get_autoclass" require_relative "../storage_set_autoclass" require_relative "../storage_move_object" -require 'pry' describe "Buckets Snippets" do let(:storage_client) { Google::Cloud::Storage.new } From 6e8951c91f7cb33ac4bbbc2f4a6dd520dc9bc0df Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Thu, 11 Jun 2026 10:25:13 +0000 Subject: [PATCH 07/18] updating comments --- google-cloud-storage/lib/google/cloud/storage/bucket.rb | 2 +- google-cloud-storage/lib/google/cloud/storage/project.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket.rb b/google-cloud-storage/lib/google/cloud/storage/bucket.rb index f8665b203250..aaf4c44fb3e4 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket.rb @@ -1439,7 +1439,7 @@ def ip_filter # # @param [Google::Apis::StorageV1::Bucket::IpFilter, Hash] new_ip_filter The bucket's new IP filter. # Acceptable Hash structure: - # - :mode - [String] The mode of the IP filter. Acceptable values are: "Disabled", "Enforced", "Preview" + # - :mode - [String] The mode of the IP filter. Acceptable values are: "Disabled", "Enabled" # - :public_network_source - [Hash] The public network source configuration: # - :allowed_ip_cidr_ranges - [Array] Array of IP CIDR ranges allowed for public access. # - :vpc_network_sources - [Array] The VPC network sources configuration: diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index c5ef54193d10..18b8dd83b35b 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -428,7 +428,7 @@ def bucket bucket_name, # Acceptable values are: # - {Google::Apis::StorageV1::Bucket::IpFilter} object # - Hash that can be converted to a {Google::Apis::StorageV1::Bucket::IpFilter} object - # - :mode - [String] The mode of the IP filter. Acceptable values are: "Disabled", "Enforced", "Preview" + # - :mode - [String] The mode of the IP filter. Acceptable values are: "Disabled", "Enabled" # - :public_network_source - [Hash] The public network source configuration: # - :allowed_ip_cidr_ranges - [Array] Array of IP CIDR ranges allowed for public access. # - :vpc_network_sources - [Array] The VPC network sources configuration: From febdecf0f46afd0ce7cfe9aea8ec3fb114b48c6d Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Fri, 12 Jun 2026 12:00:35 +0000 Subject: [PATCH 08/18] fix syntax --- .../acceptance/storage/bucket_ip_filter_test.rb | 10 +++++++--- .../samples/acceptance/buckets_test.rb | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb b/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb index ca45ce2538a5..39add6700fe1 100644 --- a/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb +++ b/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -require "storage_helper" - +require_relative "../storage_helper" +require 'pry' describe Google::Cloud::Storage::Bucket, :storage do let(:ip_filter_disabled) do { @@ -33,10 +33,14 @@ } end - let(:bucket_name) { "#{$bucket_names[2]}-ip-filter" } + let(:bucket_name) { "#{$bucket_names.first}-ip-filter" } it "creates, gets, updates, and deletes a bucket with ip_filter" do # Create a bucket with ip_filter + puts "*************************Bucket Name**************************" + puts bucket_name + puts "*************************Bucket Name**************************" + bucket = storage.bucket(bucket_name) || safe_gcs_execute {storage.create_bucket bucket_name, ip_filter: ip_filter_disabled} diff --git a/google-cloud-storage/samples/acceptance/buckets_test.rb b/google-cloud-storage/samples/acceptance/buckets_test.rb index eb682cd8980a..dd34cee780a0 100644 --- a/google-cloud-storage/samples/acceptance/buckets_test.rb +++ b/google-cloud-storage/samples/acceptance/buckets_test.rb @@ -237,6 +237,8 @@ end end end + + end describe "storage_bucket_encryption_enforcement_config" do bucket_name = random_bucket_name From 447bd869a5faaeb2d01b9488cecd34b30e418ad8 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Mon, 15 Jun 2026 05:35:05 +0000 Subject: [PATCH 09/18] remove pry --- google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb b/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb index 39add6700fe1..1e3d38747222 100644 --- a/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb +++ b/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb @@ -13,7 +13,6 @@ # limitations under the License. require_relative "../storage_helper" -require 'pry' describe Google::Cloud::Storage::Bucket, :storage do let(:ip_filter_disabled) do { From 49f3eb3cdc8c5624b5b6e52687242c0ec1b621c5 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Mon, 15 Jun 2026 07:09:28 +0000 Subject: [PATCH 10/18] debugging kokoro --- .../storage/bucket_ip_filter_test.rb | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb b/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb index 1e3d38747222..18472ce66b9c 100644 --- a/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb +++ b/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb @@ -31,17 +31,24 @@ } } end - let(:bucket_name) { "#{$bucket_names.first}-ip-filter" } + let :bucket do + storage.bucket(bucket_name) || + storage.create_bucket(bucket_name, ip_filter: ip_filter_disabled) + end + + after(:all) do + safe_gcs_execute { bucket.delete } + end it "creates, gets, updates, and deletes a bucket with ip_filter" do # Create a bucket with ip_filter puts "*************************Bucket Name**************************" - puts bucket_name + puts bucket.ip_filter puts "*************************Bucket Name**************************" - bucket = storage.bucket(bucket_name) || - safe_gcs_execute {storage.create_bucket bucket_name, ip_filter: ip_filter_disabled} + # bucket = storage.bucket(bucket_name) || + # safe_gcs_execute {storage.create_bucket bucket_name, ip_filter: ip_filter_disabled} # _(bucket.ip_filter).wont_be_nil _(bucket.ip_filter.mode).must_equal "Disabled" @@ -75,8 +82,5 @@ _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_be_nil - safe_gcs_execute { bucket.delete } - _(storage.bucket(bucket_name)).must_be :nil? - end end From a098c2d3fc899c47bc45fb9c9b84f7e04e664ee5 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Mon, 22 Jun 2026 18:21:37 +0530 Subject: [PATCH 11/18] Simplify documentation for uniform bucket-level access Removed redundant explanation about uniform bucket-level access. --- google-cloud-storage/lib/google/cloud/storage/bucket.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket.rb b/google-cloud-storage/lib/google/cloud/storage/bucket.rb index afb4f33a82cf..90bd4edd7cb0 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket.rb @@ -1115,11 +1115,7 @@ def uniform_bucket_level_access? end ## - # Sets whether uniform bucket-level access is enabled for this bucket. When this is enabled, access to the - # bucket will be configured through IAM, and legacy ACL policies will not work. When it is first enabled, - # {#uniform_bucket_level_access_locked_at} will be set by the API automatically. The uniform bucket-level access - # can then be disabled until the time specified, after which it will become immutable and calls to change it - # will fail. If uniform bucket-level access is enabled, calls to access legacy ACL information will fail. + # Sets whether uniform bucket-level access is enabled for this bucket. # # Before enabling uniform bucket-level access please review [uniform bucket-level # access](https://cloud.google.com/storage/docs/uniform-bucket-level-access). From 8dbd4ce320947b7ba966330032b1eb94b2b49343 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Mon, 22 Jun 2026 18:37:42 +0530 Subject: [PATCH 12/18] Clean up code by removing extra line break Removed unnecessary line break before accessing the IP filter. --- google-cloud-storage/samples/storage_get_bucket_ip_filter.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/google-cloud-storage/samples/storage_get_bucket_ip_filter.rb b/google-cloud-storage/samples/storage_get_bucket_ip_filter.rb index 62f872a3fa43..1b12ef69be9e 100644 --- a/google-cloud-storage/samples/storage_get_bucket_ip_filter.rb +++ b/google-cloud-storage/samples/storage_get_bucket_ip_filter.rb @@ -21,7 +21,6 @@ def get_bucket_ip_filter bucket_name: storage = Google::Cloud::Storage.new bucket = storage.bucket bucket_name - ip_filter = bucket.ip_filter if ip_filter From 24f955ae63f4327467b9908625f843cfa5807f41 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Mon, 22 Jun 2026 13:28:40 +0000 Subject: [PATCH 13/18] updating samples --- .../samples/acceptance/buckets_test.rb | 9 +++++---- ...th_ip_filter.rb => storage_disable_ip_filtering.rb} | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) rename google-cloud-storage/samples/{storage_update_bucket_with_ip_filter.rb => storage_disable_ip_filtering.rb} (80%) diff --git a/google-cloud-storage/samples/acceptance/buckets_test.rb b/google-cloud-storage/samples/acceptance/buckets_test.rb index dd34cee780a0..bff234141f2b 100644 --- a/google-cloud-storage/samples/acceptance/buckets_test.rb +++ b/google-cloud-storage/samples/acceptance/buckets_test.rb @@ -23,7 +23,7 @@ require_relative "../storage_create_bucket_hierarchical_namespace" require_relative "../storage_create_bucket_with_ip_filter" require_relative "../storage_get_bucket_ip_filter" -require_relative "../storage_update_bucket_with_ip_filter" +require_relative "../storage_disable_ip_filtering" require_relative "../storage_delete_bucket_ip_filter" require_relative "../storage_enable_bucket_ip_filter" require_relative "../storage_list_bucket_ip_filters" @@ -194,11 +194,11 @@ end end - # Updates IP filter of an existing bucket - expected = "Updated bucket #{bucket_name} with IP filter.\n" + # Disables IP filter of an existing bucket + expected = "Disabled IP filtering for bucket #{bucket_name}.\n" retry_resource_exhaustion do assert_output expected do - update_bucket_with_ip_filter bucket_name: bucket_name + disable_ip_filtering bucket_name: bucket_name end end @@ -239,6 +239,7 @@ end end + describe "storage_bucket_encryption_enforcement_config" do bucket_name = random_bucket_name diff --git a/google-cloud-storage/samples/storage_update_bucket_with_ip_filter.rb b/google-cloud-storage/samples/storage_disable_ip_filtering.rb similarity index 80% rename from google-cloud-storage/samples/storage_update_bucket_with_ip_filter.rb rename to google-cloud-storage/samples/storage_disable_ip_filtering.rb index dd616d1b1001..c0afbc530783 100644 --- a/google-cloud-storage/samples/storage_update_bucket_with_ip_filter.rb +++ b/google-cloud-storage/samples/storage_disable_ip_filtering.rb @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -# [START storage_update_bucket_with_ip_filter] -def update_bucket_with_ip_filter bucket_name: +# [START storage_disable_ip_filtering] +def disable_ip_filtering bucket_name: # The ID of your GCS bucket # bucket_name = "your-unique-bucket-name" @@ -35,10 +35,10 @@ def update_bucket_with_ip_filter bucket_name: b.ip_filter = ip_filter end - puts "Updated bucket #{bucket_name} with IP filter." + puts "Disabled IP filtering for bucket #{bucket_name}." end -# [END storage_update_bucket_with_ip_filter] +# [END storage_disable_ip_filtering] if $PROGRAM_NAME == __FILE__ - update_bucket_with_ip_filter bucket_name: ARGV.shift + disable_ip_filtering bucket_name: ARGV.shift end From fe371992501b4db09a6fdef42bd70deed872960d Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Thu, 16 Jul 2026 08:36:20 +0000 Subject: [PATCH 14/18] modifying samples and addng projection filter --- .../storage/bucket_ip_filter_test.rb | 51 ++++++++++++++----- .../lib/google/cloud/storage/bucket.rb | 4 +- .../lib/google/cloud/storage/bucket/list.rb | 11 ++-- .../lib/google/cloud/storage/project.rb | 18 ++++--- .../lib/google/cloud/storage/service.rb | 8 ++- .../samples/acceptance/buckets_test.rb | 14 ++--- .../storage_create_bucket_with_ip_filter.rb | 2 +- .../storage_enable_bucket_ip_filter.rb | 6 +-- .../samples/storage_get_bucket_ip_filter.rb | 6 +-- .../samples/storage_list_bucket_ip_filters.rb | 26 ++++------ 10 files changed, 91 insertions(+), 55 deletions(-) diff --git a/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb b/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb index 18472ce66b9c..ba8d2a37b46a 100644 --- a/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb +++ b/google-cloud-storage/acceptance/storage/bucket_ip_filter_test.rb @@ -28,12 +28,18 @@ mode: "Disabled", public_network_source: { allowed_ip_cidr_ranges: ["8.8.8.8/32"] - } + }, + vpc_network_sources: [ + { + network: "projects/#{storage.project}/global/networks/default", + allowed_ip_cidr_ranges: ["0.0.0.0/0"] + } + ] } end let(:bucket_name) { "#{$bucket_names.first}-ip-filter" } let :bucket do - storage.bucket(bucket_name) || + storage.bucket(bucket_name, projection: "full") || storage.create_bucket(bucket_name, ip_filter: ip_filter_disabled) end @@ -41,25 +47,42 @@ safe_gcs_execute { bucket.delete } end + it "returns a 400 error when provided an invalid IP CIDR range" do + invalid_ip_filter = { + mode: "Enabled", + public_network_source: { + allowed_ip_cidr_ranges: ["invalid-ip-range"] + } + } + + # Verify that creating a bucket with an invalid CIDR raises an error + err = expect { + storage.create_bucket "#{bucket_name}-invalid", ip_filter: invalid_ip_filter + }.must_raise Google::Cloud::InvalidArgumentError + + _(err.message).must_match /invalid/i + end + it "creates, gets, updates, and deletes a bucket with ip_filter" do - # Create a bucket with ip_filter - puts "*************************Bucket Name**************************" - puts bucket.ip_filter - puts "*************************Bucket Name**************************" - # bucket = storage.bucket(bucket_name) || - # safe_gcs_execute {storage.create_bucket bucket_name, ip_filter: ip_filter_disabled} - # _(bucket.ip_filter).wont_be_nil + _(bucket.ip_filter).wont_be_nil _(bucket.ip_filter.mode).must_equal "Disabled" _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["0.0.0.0/0", "::/0"] # Get the bucket and verify ip_filter - bucket = storage.bucket bucket_name + bucket = storage.bucket bucket_name, projection: "full" _(bucket.ip_filter).wont_be_nil _(bucket.ip_filter.mode).must_equal "Disabled" _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["0.0.0.0/0", "::/0"] + # list_bucket_ip_filters + found = false + storage.buckets(projection: "full").all do |b| + found = true if b.name == bucket_name + end + _(found).must_equal true + # Update the ip_filter safe_gcs_execute do bucket.update do |b| @@ -69,18 +92,22 @@ _(bucket.ip_filter.mode).must_equal "Disabled" _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_equal ["8.8.8.8/32"] + _(bucket.ip_filter.vpc_network_sources.first.network).must_equal "projects/#{storage.project}/global/networks/default" + _(bucket.ip_filter.vpc_network_sources.first.allowed_ip_cidr_ranges).must_equal ["0.0.0.0/0"] - # Disable ip_filter + # Disable ip_filter and clear network sources safe_gcs_execute do bucket.ip_filter = { mode: "Disabled", public_network_source: { allowed_ip_cidr_ranges: [] - } + }, + vpc_network_sources: [] } end _(bucket.ip_filter.public_network_source.allowed_ip_cidr_ranges).must_be_nil + _(bucket.ip_filter.vpc_network_sources).must_be_nil end end diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket.rb b/google-cloud-storage/lib/google/cloud/storage/bucket.rb index 16be941c2186..8b17a36871ca 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket.rb @@ -3403,9 +3403,9 @@ def create_notification topic, custom_attrs: nil, event_types: nil, ## # Reloads the bucket with current data from the Storage service. # - def reload! + def reload! projection: nil ensure_service! - @gapi = service.get_bucket name, user_project: user_project + @gapi = service.get_bucket name, user_project: user_project, projection: projection # If NotFound then lazy will never be unset @lazy = nil self diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb index 2378ed0034a9..6b3e7710f0f8 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb @@ -81,9 +81,13 @@ def next ensure_service! gapi = @service.list_buckets prefix: @prefix, token: @token, max: @max, user_project: @user_project, - soft_deleted: @soft_deleted + soft_deleted: @soft_deleted, + return_partial_success: !@unreachable.nil?, + projection: @projection Bucket::List.from_gapi gapi, @service, @prefix, @max, - user_project: @user_project + user_project: @user_project, soft_deleted: @soft_deleted, + return_partial_success: !@unreachable.nil?, + projection: @projection end ## @@ -155,7 +159,7 @@ def all request_limit: nil, &block # @private New Bucket::List from a Google API Client # Google::Apis::StorageV1::Buckets object. def self.from_gapi gapi_list, service, prefix = nil, max = nil, - user_project: nil, soft_deleted: nil, return_partial_success: nil + user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil buckets = new(Array(gapi_list.items).map do |gapi_object| Bucket.from_gapi gapi_object, service, user_project: user_project end) @@ -165,6 +169,7 @@ def self.from_gapi gapi_list, service, prefix = nil, max = nil, buckets.instance_variable_set :@max, max buckets.instance_variable_set :@user_project, user_project buckets.instance_variable_set :@soft_deleted, soft_deleted + buckets.instance_variable_set :@projection, projection buckets.instance_variable_set :@unreachable, Array(gapi_list.unreachable) if return_partial_success buckets end diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index 18b8dd83b35b..352c4be3e2d0 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -214,11 +214,11 @@ def add_custom_header header_name, header_value # puts unreachable_bucket_name # end # - def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil + def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil gapi = service.list_buckets \ - prefix: prefix, token: token, max: max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success + prefix: prefix, token: token, max: max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success, projection: projection Bucket::List.from_gapi \ - gapi, service, prefix, max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success + gapi, service, prefix, max, user_project: user_project, soft_deleted: soft_deleted, return_partial_success: return_partial_success, projection: projection end alias find_buckets buckets @@ -298,7 +298,8 @@ def bucket bucket_name, soft_deleted: nil, if_metageneration_match: nil, if_metageneration_not_match: nil, - user_project: nil + user_project: nil, + projection: nil if skip_lookup return Bucket.new_lazy bucket_name, service, user_project: user_project @@ -308,7 +309,8 @@ def bucket bucket_name, if_metageneration_not_match: if_metageneration_not_match, user_project: user_project, soft_deleted: soft_deleted, - generation: generation + generation: generation, + projection: projection Bucket.from_gapi gapi, service, user_project: user_project rescue Google::Cloud::NotFoundError @@ -490,7 +492,8 @@ def create_bucket bucket_name, autoclass_enabled: false, enable_object_retention: nil, hierarchical_namespace: nil, - ip_filter: nil + ip_filter: nil, + projection: nil params = { name: bucket_name, @@ -520,7 +523,8 @@ def create_bucket bucket_name, gapi = service.insert_bucket \ new_bucket, acl: acl_rule(acl), default_acl: acl_rule(default_acl), user_project: user_project, - enable_object_retention: enable_object_retention + enable_object_retention: enable_object_retention, + projection: projection Bucket.from_gapi gapi, service, user_project: user_project end # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity diff --git a/google-cloud-storage/lib/google/cloud/storage/service.rb b/google-cloud-storage/lib/google/cloud/storage/service.rb index fd87490a5c62..25bef1b4c7cd 100644 --- a/google-cloud-storage/lib/google/cloud/storage/service.rb +++ b/google-cloud-storage/lib/google/cloud/storage/service.rb @@ -96,13 +96,14 @@ def project_service_account ## # Retrieves a list of buckets for the given project. - def list_buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + def list_buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} execute do service.list_buckets \ @project, prefix: prefix, page_token: token, max_results: max, user_project: user_project(user_project), soft_deleted: soft_deleted, return_partial_success: return_partial_success, + projection: projection, options: options end end @@ -116,6 +117,7 @@ def get_bucket bucket_name, user_project: nil, soft_deleted: nil, generation: nil, + projection: nil, options: {} execute do service.get_bucket bucket_name, @@ -124,6 +126,7 @@ def get_bucket bucket_name, user_project: user_project(user_project), soft_deleted: soft_deleted, generation: generation, + projection: projection, options: options end end @@ -133,13 +136,14 @@ def get_bucket bucket_name, # Returns Google::Apis::StorageV1::Bucket. def insert_bucket bucket_gapi, acl: nil, default_acl: nil, user_project: nil, enable_object_retention: nil, - options: {} + projection: nil, options: {} execute do service.insert_bucket \ @project, bucket_gapi, predefined_acl: acl, predefined_default_object_acl: default_acl, user_project: user_project(user_project), + projection: projection, options: options, enable_object_retention: enable_object_retention end diff --git a/google-cloud-storage/samples/acceptance/buckets_test.rb b/google-cloud-storage/samples/acceptance/buckets_test.rb index bff234141f2b..6abb319c40b5 100644 --- a/google-cloud-storage/samples/acceptance/buckets_test.rb +++ b/google-cloud-storage/samples/acceptance/buckets_test.rb @@ -211,13 +211,14 @@ end end - # Lists IP filter allowed ranges - expected = "IP filter mode: Disabled\n" \ - "Allowed range: 8.8.8.8/32\n" + # Lists IP filter configurations for buckets in the project retry_resource_exhaustion do - assert_output expected do - list_bucket_ip_filters bucket_name: bucket_name + out, _err = capture_io do + list_bucket_ip_filters bucket_name end + # Assert that the specific bucket we created in this test suite + # appears in the list with its IP filter mode + assert_includes out, "Bucket Name: #{bucket_name}, IP Filtering Mode: Disabled" end # Deletes IP filter of an existing bucket @@ -229,11 +230,10 @@ end # Enables IP filter of an existing bucket (SKIPPED) - skip "SKIPPED : If Ip filter is enabled, we cannot access the bucket" expected = "Enabled IP filter for bucket #{bucket_name}.\n" retry_resource_exhaustion do assert_output expected do - enable_bucket_ip_filter bucket_name: bucket_name + enable_bucket_ip_filter bucket_name: bucket_name, mode: "Disabled" end end end diff --git a/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb index b155ad7fe391..9c56446db9f2 100644 --- a/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb +++ b/google-cloud-storage/samples/storage_create_bucket_with_ip_filter.rb @@ -33,7 +33,7 @@ def create_bucket_with_ip_filter bucket_name: b.ip_filter = ip_filter b.uniform_bucket_level_access = true end - puts "Created bucket #{bucket_name} with IP filter." + puts "Created bucket #{bucket.name} with IP filter." end # [END storage_create_bucket_with_ip_filter] diff --git a/google-cloud-storage/samples/storage_enable_bucket_ip_filter.rb b/google-cloud-storage/samples/storage_enable_bucket_ip_filter.rb index 6e96767479f1..ed61043b7251 100644 --- a/google-cloud-storage/samples/storage_enable_bucket_ip_filter.rb +++ b/google-cloud-storage/samples/storage_enable_bucket_ip_filter.rb @@ -13,7 +13,7 @@ # limitations under the License. # [START storage_enable_bucket_ip_filter] -def enable_bucket_ip_filter bucket_name: +def enable_bucket_ip_filter bucket_name:, mode: "Enabled" # The ID of your GCS bucket # bucket_name = "your-unique-bucket-name" @@ -24,7 +24,7 @@ def enable_bucket_ip_filter bucket_name: # Enable the IP filter configuration. ip_filter = { - mode: "Enabled", + mode: mode, allow_all_service_agent_access: true, public_network_source: { allowed_ip_cidr_ranges: ["0.0.0.0/0", "::/0"] @@ -40,5 +40,5 @@ def enable_bucket_ip_filter bucket_name: # [END storage_enable_bucket_ip_filter] if $PROGRAM_NAME == __FILE__ - enable_bucket_ip_filter bucket_name: ARGV.shift + enable_bucket_ip_filter bucket_name: ARGV.shift, mode: ARGV.shift end diff --git a/google-cloud-storage/samples/storage_get_bucket_ip_filter.rb b/google-cloud-storage/samples/storage_get_bucket_ip_filter.rb index 1b12ef69be9e..6787caaee7f8 100644 --- a/google-cloud-storage/samples/storage_get_bucket_ip_filter.rb +++ b/google-cloud-storage/samples/storage_get_bucket_ip_filter.rb @@ -20,16 +20,16 @@ def get_bucket_ip_filter bucket_name: require "google/cloud/storage" storage = Google::Cloud::Storage.new - bucket = storage.bucket bucket_name + bucket = storage.bucket bucket_name, projection: "full" ip_filter = bucket.ip_filter if ip_filter - puts "Bucket #{bucket_name} has IP filter mode: #{ip_filter.mode}." + puts "Bucket #{bucket.name} has IP filter mode: #{ip_filter.mode}." if ip_filter.public_network_source puts "Allowed public network CIDR ranges: #{ip_filter.public_network_source.allowed_ip_cidr_ranges.join(', ')}." end else - puts "Bucket #{bucket_name} does not have an IP filter configuration." + puts "Bucket #{bucket.name} does not have an IP filter configuration." end end # [END storage_get_bucket_ip_filter] diff --git a/google-cloud-storage/samples/storage_list_bucket_ip_filters.rb b/google-cloud-storage/samples/storage_list_bucket_ip_filters.rb index e0d294cfc436..28ea66549a8e 100644 --- a/google-cloud-storage/samples/storage_list_bucket_ip_filters.rb +++ b/google-cloud-storage/samples/storage_list_bucket_ip_filters.rb @@ -13,25 +13,21 @@ # limitations under the License. # [START storage_list_bucket_ip_filters] -def list_bucket_ip_filters bucket_name: - # The ID of your GCS bucket - # bucket_name = "your-unique-bucket-name" +def list_bucket_ip_filters bucket_name + # The ID of your GCP project + # project_id = "your-project-id" require "google/cloud/storage" storage = Google::Cloud::Storage.new - bucket = storage.bucket bucket_name - - ip_filter = bucket.ip_filter - if ip_filter - puts "IP filter mode: #{ip_filter.mode}" - if ip_filter.public_network_source - ip_filter.public_network_source.allowed_ip_cidr_ranges.each do |range| - puts "Allowed range: #{range}" - end - end - else - puts "No IP filter configured on bucket #{bucket_name}." + + puts "Buckets:" + # Use projection: "full" to ensure IP filter metadata is returned + storage.buckets(projection: "full").all do |bucket| + ip_filter = bucket.ip_filter + mode = ip_filter ? ip_filter.mode : "Not Configured" + + puts "Bucket Name: #{bucket.name}, IP Filtering Mode: #{mode}" end end # [END storage_list_bucket_ip_filters] From 9332501de303ff452394fcf4266b960f8fbc68e6 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Fri, 17 Jul 2026 13:29:41 +0000 Subject: [PATCH 15/18] updating scenarios and docs --- .../lib/google/cloud/storage/bucket.rb | 37 +++++++++++++------ .../lib/google/cloud/storage/project.rb | 36 ++++++++++++------ .../samples/acceptance/buckets_test.rb | 6 ++- .../samples/storage_list_bucket_ip_filters.rb | 4 +- 4 files changed, 55 insertions(+), 28 deletions(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket.rb b/google-cloud-storage/lib/google/cloud/storage/bucket.rb index 8b17a36871ca..c311aa302a63 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket.rb @@ -1115,7 +1115,11 @@ def uniform_bucket_level_access? end ## - # Sets whether uniform bucket-level access is enabled for this bucket. + # Sets whether uniform bucket-level access is enabled for this bucket. When this is enabled, access to the + # bucket will be configured through IAM, and legacy ACL policies will not work. When it is first enabled, + # {#uniform_bucket_level_access_locked_at} will be set by the API automatically. The uniform bucket-level access + # can then be disabled until the time specified, after which it will become immutable and calls to change it + # will fail. If uniform bucket-level access is enabled, calls to access legacy ACL information will fail. # # Before enabling uniform bucket-level access please review [uniform bucket-level # access](https://cloud.google.com/storage/docs/uniform-bucket-level-access). @@ -1576,6 +1580,10 @@ def delete if_metageneration_match: nil, if_metageneration_not_match: nil # The bucket's IP filter configuration. # This value can be modified by calling {#ip_filter=}. # + # @note The IP filter metadata is not returned by the API by default. + # You must retrieve the bucket with the `projection: :full` (or `"full"`) + # option to access this configuration. See {Project#bucket}. + # # @return [Google::Apis::StorageV1::Bucket::IpFilter, nil] The bucket's IP filter configuration, # or `nil` if not configured. # @@ -1584,7 +1592,7 @@ def delete if_metageneration_match: nil, if_metageneration_not_match: nil # # storage = Google::Cloud::Storage.new # - # bucket = storage.bucket "my-bucket" + # bucket = storage.bucket "my-bucket", projection: "full" # ip_filter = bucket.ip_filter # if ip_filter # puts "Mode: #{ip_filter.mode}" @@ -1601,21 +1609,21 @@ def ip_filter # # @param [Google::Apis::StorageV1::Bucket::IpFilter, Hash] new_ip_filter The bucket's new IP filter. # Acceptable Hash structure: - # - :mode - [String] The mode of the IP filter. Acceptable values are: "Disabled", "Enabled" - # - :public_network_source - [Hash] The public network source configuration: - # - :allowed_ip_cidr_ranges - [Array] Array of IP CIDR ranges allowed for public access. - # - :vpc_network_sources - [Array] The VPC network sources configuration: - # - :network - [String] The VPC network resource path, e.g. "projects/PROJECT_ID/global/networks/NETWORK_NAME". - # - :allowed_ip_cidr_ranges - [Array] Array of IP CIDR ranges allowed for VPC access. - # - :allow_cross_org_vpcs - [Boolean] Whether to allow cross-org VPC access. - # - :allow_all_service_agent_access - [Boolean] Whether to allow all service agent access. + # * `:mode` (String) - The mode of the IP filter. Acceptable values are: "Disabled", "Enabled" + # * `:public_network_source` (Hash) - The public network source configuration: + # * `:allowed_ip_cidr_ranges` (Array) - Array of IP CIDR ranges allowed for public access. + # * `:vpc_network_sources` (Array) - The VPC network sources configuration: + # * `:network` (String) - The VPC network resource path, e.g. "projects/PROJECT_ID/global/networks/NETWORK_NAME". + # * `:allowed_ip_cidr_ranges` (Array) - Array of IP CIDR ranges allowed for VPC access. + # * `:allow_cross_org_vpcs` (Boolean) - Whether to allow cross-org VPC access. + # * `:allow_all_service_agent_access` (Boolean) - Whether to allow all service agent access. # # @example Enable IP filter with Hash: # require "google/cloud/storage" # # storage = Google::Cloud::Storage.new # - # bucket = storage.bucket "my-bucket" + # bucket = storage.bucket "my-bucket", projection: "full" # bucket.ip_filter = { # mode: "Enabled", # allow_all_service_agent_access: true, @@ -1629,7 +1637,7 @@ def ip_filter # # storage = Google::Cloud::Storage.new # - # bucket = storage.bucket "my-bucket" + # bucket = storage.bucket "my-bucket", projection: "full" # bucket.ip_filter = { # mode: "Disabled", # public_network_source: { @@ -3403,6 +3411,11 @@ def create_notification topic, custom_attrs: nil, event_types: nil, ## # Reloads the bucket with current data from the Storage service. # + # @param [String] projection Set of properties to return. Accepted values + # are `noAcl` and `full`. The default value is `noAcl`. If set to `full`, + # the bucket will include additional metadata, such as ACL policies and + # IP filter settings. + # def reload! projection: nil ensure_service! @gapi = service.get_bucket name, user_project: user_project, projection: projection diff --git a/google-cloud-storage/lib/google/cloud/storage/project.rb b/google-cloud-storage/lib/google/cloud/storage/project.rb index 352c4be3e2d0..024005b853fb 100644 --- a/google-cloud-storage/lib/google/cloud/storage/project.rb +++ b/google-cloud-storage/lib/google/cloud/storage/project.rb @@ -159,8 +159,12 @@ def add_custom_header header_name, header_value # # See also {Bucket#requester_pays=} and {Bucket#requester_pays}. # @param [Boolean] return_partial_success - # If true, the response will contain a list of unreachable buckets. - # If false, ListBuckets will throw an error if there are any unreachable buckets. + # If true, the response will contain a list of unreachable buckets. + # If false, ListBuckets will throw an error if there are any unreachable buckets. + # @param [String] projection Set of properties to return. Accepted values + # are `noAcl` and `full`. The default value is `noAcl`. If set to `full`, + # the bucket will include additional metadata, such as ACL policies and + # IP filter settings. # # @return [Array] (See # {Google::Cloud::Storage::Bucket::List}) @@ -249,6 +253,10 @@ def buckets prefix: nil, token: nil, max: nil, user_project: nil, soft_deleted: # on whether the bucket's current metageneration matches the given value. # @param [Boolean] soft_deleted If true, returns the soft-deleted bucket. # This parameter is required if generation is specified. + # @param [String] projection Set of properties to return. Accepted values + # are `noAcl` and `full`. The default value is `noAcl`. If set to `full`, + # the bucket will include additional metadata, such as ACL policies and + # IP filter settings. # # The value provided will be applied to all operations on the returned # bucket instance and its files. @@ -425,19 +433,23 @@ def bucket bucket_name, # See also {Bucket#requester_pays=} and {Bucket#requester_pays}. # @param [Boolean] enable_object_retention # When set to true, object retention is enabled for this bucket. + # @param [String] projection Set of properties to return. Accepted values + # are `noAcl` and `full`. The default value is `noAcl`. If set to `full`, + # the bucket will include additional metadata, such as ACL policies and + # IP filter settings. # # @param [Hash] ip_filter The bucket's IP filter configuration. # Acceptable values are: - # - {Google::Apis::StorageV1::Bucket::IpFilter} object - # - Hash that can be converted to a {Google::Apis::StorageV1::Bucket::IpFilter} object - # - :mode - [String] The mode of the IP filter. Acceptable values are: "Disabled", "Enabled" - # - :public_network_source - [Hash] The public network source configuration: - # - :allowed_ip_cidr_ranges - [Array] Array of IP CIDR ranges allowed for public access. - # - :vpc_network_sources - [Array] The VPC network sources configuration: - # - :network - [String] The VPC network resource path, e.g. "projects/PROJECT_ID/global/networks/NETWORK_NAME". - # - :allowed_ip_cidr_ranges - [Array] Array of IP CIDR ranges allowed for VPC access. - # - :allow_cross_org_vpcs - [Boolean] Whether to allow cross-org VPC access. - # - :allow_all_service_agent_access - [Boolean] Whether to allow all service agent access. + # * A {Google::Apis::StorageV1::Bucket::IpFilter} object. + # * A Hash that can be converted to a {Google::Apis::StorageV1::Bucket::IpFilter} object: + # * `:mode` (String) - The mode of the IP filter. Acceptable values are: "Disabled", "Enabled" + # * `:public_network_source` (Hash) - The public network source configuration: + # * `:allowed_ip_cidr_ranges` (Array) - Array of IP CIDR ranges allowed for public access. + # * `:vpc_network_sources` (Array) - The VPC network sources configuration: + # * `:network` (String) - The VPC network resource path, e.g. "projects/PROJECT_ID/global/networks/NETWORK_NAME". + # * `:allowed_ip_cidr_ranges` (Array) - Array of IP CIDR ranges allowed for VPC access. + # * `:allow_cross_org_vpcs` (Boolean) - Whether to allow cross-org VPC access. + # * `:allow_all_service_agent_access` (Boolean) - Whether to allow all service agent access. # @yield [bucket] a block for configuring the bucket before it is # created # @yieldparam [Bucket] bucket the bucket object to be configured diff --git a/google-cloud-storage/samples/acceptance/buckets_test.rb b/google-cloud-storage/samples/acceptance/buckets_test.rb index 6abb319c40b5..1635cff21f46 100644 --- a/google-cloud-storage/samples/acceptance/buckets_test.rb +++ b/google-cloud-storage/samples/acceptance/buckets_test.rb @@ -214,7 +214,7 @@ # Lists IP filter configurations for buckets in the project retry_resource_exhaustion do out, _err = capture_io do - list_bucket_ip_filters bucket_name + list_bucket_ip_filters end # Assert that the specific bucket we created in this test suite # appears in the list with its IP filter mode @@ -229,7 +229,9 @@ end end - # Enables IP filter of an existing bucket (SKIPPED) + # Enables IP filter of an existing bucket + # We are intentionally setting the mode to "Disabled" to test the update functionality + # In real use-case, user would be setting it to "Enabled" expected = "Enabled IP filter for bucket #{bucket_name}.\n" retry_resource_exhaustion do assert_output expected do diff --git a/google-cloud-storage/samples/storage_list_bucket_ip_filters.rb b/google-cloud-storage/samples/storage_list_bucket_ip_filters.rb index 28ea66549a8e..fb2f632b4af0 100644 --- a/google-cloud-storage/samples/storage_list_bucket_ip_filters.rb +++ b/google-cloud-storage/samples/storage_list_bucket_ip_filters.rb @@ -13,7 +13,7 @@ # limitations under the License. # [START storage_list_bucket_ip_filters] -def list_bucket_ip_filters bucket_name +def list_bucket_ip_filters # The ID of your GCP project # project_id = "your-project-id" @@ -33,5 +33,5 @@ def list_bucket_ip_filters bucket_name # [END storage_list_bucket_ip_filters] if $PROGRAM_NAME == __FILE__ - list_bucket_ip_filters bucket_name: ARGV.shift + list_bucket_ip_filters end From 1c9975d174c2febeb2523dc3e197e7a8269626f5 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Mon, 20 Jul 2026 12:35:05 +0000 Subject: [PATCH 16/18] fixing unit test --- .../test/google/cloud/storage/project_test.rb | 104 +++++++++--------- google-cloud-storage/test/helper.rb | 2 + 2 files changed, 54 insertions(+), 52 deletions(-) diff --git a/google-cloud-storage/test/google/cloud/storage/project_test.rb b/google-cloud-storage/test/google/cloud/storage/project_test.rb index 6cbbd4deebca..19905377a496 100644 --- a/google-cloud-storage/test/google/cloud/storage/project_test.rb +++ b/google-cloud-storage/test/google/cloud/storage/project_test.rb @@ -156,7 +156,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name @@ -172,7 +172,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name, location: bucket_location resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, location: bucket_location @@ -189,7 +189,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name, autoclass_enabled: bucket_autoclass_enabled resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, autoclass_enabled: bucket_autoclass_enabled @@ -205,7 +205,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name, storage_class: bucket_storage_class resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, storage_class: bucket_storage_class @@ -221,7 +221,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name, versioning: Google::Apis::StorageV1::Bucket::Versioning.new(enabled: true) resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, versioning: true @@ -237,7 +237,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name, logging: Google::Apis::StorageV1::Bucket::Logging.new(log_bucket: bucket_logging_bucket, log_object_prefix: bucket_logging_prefix) resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, logging_bucket: bucket_logging_bucket, logging_prefix: bucket_logging_prefix @@ -254,7 +254,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name, website: Google::Apis::StorageV1::Bucket::Website.new(main_page_suffix: bucket_website_main, not_found_page: bucket_website_404) resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, website_main: bucket_website_main, website_404: bucket_website_404 @@ -271,7 +271,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name, billing: Google::Apis::StorageV1::Bucket::Billing.new(requester_pays: bucket_requester_pays) resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name do |b| @@ -289,7 +289,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name, billing: Google::Apis::StorageV1::Bucket::Billing.new(requester_pays: bucket_requester_pays) resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: "test", enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: "test", enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, user_project: true do |b| @@ -308,7 +308,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name, billing: Google::Apis::StorageV1::Bucket::Billing.new(requester_pays: bucket_requester_pays) resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: "my-other-project", enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: "my-other-project", enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, user_project: "my-other-project" do |b| @@ -327,7 +327,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name, cors: bucket_cors_gapi resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -349,7 +349,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name, lifecycle: lifecycle_gapi(lifecycle_rule_gapi("SetStorageClass", storage_class: "NEARLINE", age: 32)) resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -369,7 +369,7 @@ def creds.is_a? target created_bucket = create_bucket_gapi bucket_name created_bucket.labels = { "env" => "production", "foo" => "bar" } resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -391,7 +391,7 @@ def creds.is_a? target created_bucket = create_bucket_gapi bucket_name created_bucket.encryption = encryption_gapi(key_name: kms_key) resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -411,7 +411,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: "private", predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: "private", predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, acl: "private" @@ -426,7 +426,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: "publicRead", predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: "publicRead", predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, acl: :public @@ -441,7 +441,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: "private", user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: "private", user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, default_acl: :private @@ -456,7 +456,7 @@ def creds.is_a? target mock = Minitest::Mock.new created_bucket = create_bucket_gapi bucket_name resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: "publicRead", user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: "publicRead", user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, default_acl: "public" @@ -480,7 +480,7 @@ def creds.is_a? target retention_period: bucket_retention_period, effective_time: bucket_retention_effective_at ) - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -506,7 +506,7 @@ def creds.is_a? target mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: bucket_enable_object_retention, - options: {} + projection: nil, options: {} storage.service.mocked_service = mock @@ -523,7 +523,7 @@ def creds.is_a? target created_bucket = create_bucket_gapi bucket_name created_bucket.default_event_based_hold = true resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -545,7 +545,7 @@ def creds.is_a? target created_bucket.rpo = "ASYNC_TURBO" resp_bucket = bucket_with_location created_bucket, location_type: "dual-region" - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name do |b| @@ -566,7 +566,7 @@ def creds.is_a? target created_bucket.hierarchical_namespace = hierarchical_namespace_object resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name do |b| @@ -586,7 +586,7 @@ def creds.is_a? target created_bucket.hierarchical_namespace = { enabled: false } resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name, hierarchical_namespace: { enabled: false } @@ -610,7 +610,7 @@ def creds.is_a? target created_bucket.ip_filter = ip_filter_gapi resp_bucket = bucket_with_location created_bucket - mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, options: {} + mock.expect :insert_bucket, resp_bucket, [project, created_bucket], predefined_acl: nil, predefined_default_object_acl: nil, user_project: nil, enable_object_retention: nil, projection: nil, options: {} storage.service.mocked_service = mock bucket = storage.create_bucket bucket_name do |b| @@ -647,7 +647,7 @@ def stub.insert_bucket *args num_buckets = 3 mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -665,7 +665,7 @@ def stub.insert_bucket *args num_buckets = 3 mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil,return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(num_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil,return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -683,7 +683,7 @@ def stub.insert_bucket *args mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(num_buckets,"next_page_token",soft_deleted), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: true, - return_partial_success: nil, options: {} + return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock buckets = storage.buckets soft_deleted: true @@ -700,8 +700,8 @@ def stub.insert_bucket *args it "paginates buckets" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -720,8 +720,8 @@ def stub.insert_bucket *args it "paginates buckets with max set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil,return_partial_success: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil,return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil,return_partial_success: nil, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil,return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -742,7 +742,7 @@ def stub.insert_bucket *args num_buckets = 3 mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -759,8 +759,8 @@ def stub.insert_bucket *args it "paginates buckets with next? and next" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, projection: nil, options: {} storage.service.mocked_service = mock @@ -778,8 +778,8 @@ def stub.insert_bucket *args it "paginates buckets with next? and next and max set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, projection: nil, options: {} storage.service.mocked_service = mock @@ -797,8 +797,8 @@ def stub.insert_bucket *args it "paginates buckets with all" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, projection: nil, options: {} storage.service.mocked_service = mock @@ -811,8 +811,8 @@ def stub.insert_bucket *args it "paginates buckets with all and max set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, projection: nil, options: {} storage.service.mocked_service = mock @@ -825,8 +825,8 @@ def stub.insert_bucket *args it "iterates buckets with all using Enumerator" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, projection: nil, options: {} storage.service.mocked_service = mock @@ -839,8 +839,8 @@ def stub.insert_bucket *args it "iterates buckets with all and request_limit set" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil,soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil,soft_deleted: nil, return_partial_success: false, projection: nil, options: {} storage.service.mocked_service = mock @@ -853,8 +853,8 @@ def stub.insert_bucket *args it "iterates buckets with all and user_project set to true" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: false, projection: nil, options: {} storage.service.mocked_service = mock @@ -868,8 +868,8 @@ def stub.insert_bucket *args it "iterates buckets with all and user_project set to another project ID" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: false, projection: nil, options: {} storage.service.mocked_service = mock @@ -1081,7 +1081,7 @@ def stub.insert_bucket *args mock = Minitest::Mock.new - mock.expect :list_buckets, list_unreachable_buckets_gapi(2,nil,nil,unreachable_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil,return_partial_success: true, options: {} + mock.expect :list_buckets, list_unreachable_buckets_gapi(2,nil,nil,unreachable_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil,return_partial_success: true, projection: nil, options: {} storage.service.mocked_service = mock @@ -1097,7 +1097,7 @@ def stub.insert_bucket *args mock = Minitest::Mock.new - mock.expect :list_buckets, list_unreachable_buckets_gapi(2,nil,nil,unreachable_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil,return_partial_success: true, options: {} + mock.expect :list_buckets, list_unreachable_buckets_gapi(2,nil,nil,unreachable_buckets), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil,return_partial_success: true, projection: nil, options: {} storage.service.mocked_service = mock @@ -1111,7 +1111,7 @@ def stub.insert_bucket *args it "returns nil array for unreachable buckets if return_partial_success is passed as false" do mock = Minitest::Mock.new - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, projection: nil, options: {} storage.service.mocked_service = mock diff --git a/google-cloud-storage/test/helper.rb b/google-cloud-storage/test/helper.rb index 36a314e6222e..f81015ef4a98 100644 --- a/google-cloud-storage/test/helper.rb +++ b/google-cloud-storage/test/helper.rb @@ -347,6 +347,7 @@ def get_bucket_args if_metageneration_match: nil, user_project: nil, generation: nil, soft_deleted: nil, + projection: nil, options: {} { if_metageneration_match: if_metageneration_match, @@ -354,6 +355,7 @@ def get_bucket_args if_metageneration_match: nil, user_project: user_project, generation: generation, soft_deleted: soft_deleted, + projection: projection, options: options } end From 1d038b8e715ed4c6b2f4eeef047d3c7f7a48ac37 Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Mon, 20 Jul 2026 15:56:58 +0000 Subject: [PATCH 17/18] fix tests --- .../lib/google/cloud/storage/bucket/list.rb | 2 -- .../test/google/cloud/storage/project_test.rb | 18 +++++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb index 6b3e7710f0f8..b90dea759258 100644 --- a/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb +++ b/google-cloud-storage/lib/google/cloud/storage/bucket/list.rb @@ -82,11 +82,9 @@ def next gapi = @service.list_buckets prefix: @prefix, token: @token, max: @max, user_project: @user_project, soft_deleted: @soft_deleted, - return_partial_success: !@unreachable.nil?, projection: @projection Bucket::List.from_gapi gapi, @service, @prefix, @max, user_project: @user_project, soft_deleted: @soft_deleted, - return_partial_success: !@unreachable.nil?, projection: @projection end diff --git a/google-cloud-storage/test/google/cloud/storage/project_test.rb b/google-cloud-storage/test/google/cloud/storage/project_test.rb index 19905377a496..4f3b5fd78fbc 100644 --- a/google-cloud-storage/test/google/cloud/storage/project_test.rb +++ b/google-cloud-storage/test/google/cloud/storage/project_test.rb @@ -760,7 +760,7 @@ def stub.insert_bucket *args it "paginates buckets with next? and next" do mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -779,7 +779,7 @@ def stub.insert_bucket *args it "paginates buckets with next? and next and max set" do mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -798,7 +798,7 @@ def stub.insert_bucket *args it "paginates buckets with all" do mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -812,7 +812,7 @@ def stub.insert_bucket *args it "paginates buckets with all and max set" do mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: false, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(2), [project], prefix: nil, page_token: "next_page_token", max_results: 3, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -826,7 +826,7 @@ def stub.insert_bucket *args it "iterates buckets with all using Enumerator" do mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: false, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -840,7 +840,7 @@ def stub.insert_bucket *args it "iterates buckets with all and request_limit set" do mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: nil, soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil,soft_deleted: nil, return_partial_success: false, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: nil,soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -854,7 +854,7 @@ def stub.insert_bucket *args it "iterates buckets with all and user_project set to true" do mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: false, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "test", soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -869,7 +869,7 @@ def stub.insert_bucket *args it "iterates buckets with all and user_project set to another project ID" do mock = Minitest::Mock.new mock.expect :list_buckets, list_buckets_gapi(3, "next_page_token"), [project], prefix: nil, page_token: nil, max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} - mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: false, projection: nil, options: {} + mock.expect :list_buckets, list_buckets_gapi(3, "second_page_token"), [project], prefix: nil, page_token: "next_page_token", max_results: nil, user_project: "my-other-project", soft_deleted: nil, return_partial_success: nil, projection: nil, options: {} storage.service.mocked_service = mock @@ -1178,4 +1178,4 @@ def restored_bucket_gapi name def object_retention_param enable_object_retention enable_object_retention ? Google::Apis::StorageV1::Bucket::ObjectRetention.new(mode: "Enabled") : nil end -end +end \ No newline at end of file From a8cc1fb262e9200600432539e5e2f7969658381c Mon Sep 17 00:00:00 2001 From: Shubhangi Singh Date: Mon, 20 Jul 2026 16:00:02 +0000 Subject: [PATCH 18/18] fix indentation --- google-cloud-storage/test/google/cloud/storage/project_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/google-cloud-storage/test/google/cloud/storage/project_test.rb b/google-cloud-storage/test/google/cloud/storage/project_test.rb index 4f3b5fd78fbc..089b1bc25522 100644 --- a/google-cloud-storage/test/google/cloud/storage/project_test.rb +++ b/google-cloud-storage/test/google/cloud/storage/project_test.rb @@ -1178,4 +1178,4 @@ def restored_bucket_gapi name def object_retention_param enable_object_retention enable_object_retention ? Google::Apis::StorageV1::Bucket::ObjectRetention.new(mode: "Enabled") : nil end -end \ No newline at end of file +end