fog-google 1.19.0 → 1.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/integration-compute-core.yml +54 -0
  3. data/.github/workflows/integration-compute-instance_groups.yml +55 -0
  4. data/.github/workflows/integration-compute-loadbalancing.yml +54 -0
  5. data/.github/workflows/integration-compute-networking.yml +53 -0
  6. data/.github/workflows/integration-monitoring.yml +54 -0
  7. data/.github/workflows/integration-pubsub.yml +54 -0
  8. data/.github/workflows/integration-sql.yml +54 -0
  9. data/.github/workflows/integration-storage.yml +55 -0
  10. data/.github/workflows/stale.yml +2 -2
  11. data/.github/workflows/unit.yml +7 -2
  12. data/.ruby-version +1 -0
  13. data/CHANGELOG.md +112 -0
  14. data/README.md +3 -3
  15. data/examples/create_instance.rb +1 -1
  16. data/examples/create_instance_and_attach_disk_later.rb +86 -0
  17. data/examples/create_instance_with_attached_disk.rb +1 -1
  18. data/examples/get_list_images.rb +1 -1
  19. data/examples/load-balance.rb +1 -1
  20. data/examples/metadata.rb +1 -1
  21. data/examples/network.rb +1 -1
  22. data/fog-google.gemspec +12 -10
  23. data/lib/fog/compute/google/models/addresses.rb +17 -8
  24. data/lib/fog/compute/google/models/disk_types.rb +17 -7
  25. data/lib/fog/compute/google/models/disks.rb +23 -8
  26. data/lib/fog/compute/google/models/firewalls.rb +11 -2
  27. data/lib/fog/compute/google/models/forwarding_rules.rb +16 -10
  28. data/lib/fog/compute/google/models/global_addresses.rb +11 -2
  29. data/lib/fog/compute/google/models/global_forwarding_rules.rb +11 -2
  30. data/lib/fog/compute/google/models/http_health_checks.rb +12 -3
  31. data/lib/fog/compute/google/models/images.rb +15 -8
  32. data/lib/fog/compute/google/models/instance_group_managers.rb +17 -9
  33. data/lib/fog/compute/google/models/instance_groups.rb +16 -8
  34. data/lib/fog/compute/google/models/instance_templates.rb +12 -3
  35. data/lib/fog/compute/google/models/machine_types.rb +17 -8
  36. data/lib/fog/compute/google/models/networks.rb +12 -3
  37. data/lib/fog/compute/google/models/operations.rb +22 -8
  38. data/lib/fog/compute/google/models/regions.rb +12 -3
  39. data/lib/fog/compute/google/models/routes.rb +12 -3
  40. data/lib/fog/compute/google/models/server.rb +4 -4
  41. data/lib/fog/compute/google/models/servers.rb +23 -9
  42. data/lib/fog/compute/google/models/ssl_certificates.rb +12 -3
  43. data/lib/fog/compute/google/models/subnetworks.rb +16 -8
  44. data/lib/fog/compute/google/models/target_http_proxies.rb +12 -3
  45. data/lib/fog/compute/google/models/target_https_proxies.rb +12 -3
  46. data/lib/fog/compute/google/models/target_instances.rb +16 -8
  47. data/lib/fog/compute/google/models/target_pools.rb +16 -8
  48. data/lib/fog/compute/google/models/url_maps.rb +12 -3
  49. data/lib/fog/compute/google/models/zones.rb +12 -3
  50. data/lib/fog/compute/google/requests/insert_server.rb +1 -1
  51. data/lib/fog/compute/google/requests/stop_server.rb +2 -2
  52. data/lib/fog/google/shared.rb +9 -8
  53. data/lib/fog/google/version.rb +1 -1
  54. data/lib/fog/storage/google_json/models/file.rb +7 -2
  55. data/lib/fog/storage/google_json/real.rb +16 -7
  56. data/lib/fog/storage/google_json/utils.rb +3 -4
  57. data/lib/fog/storage/google_json.rb +1 -1
  58. data/test/helpers/integration_test_helper.rb +3 -3
  59. data/test/integration/compute/core_compute/test_servers.rb +86 -0
  60. data/test/integration/monitoring/test_timeseries.rb +29 -14
  61. data/test/integration/storage/test_objects.rb +1 -0
  62. data/test/unit/compute/test_common_collections.rb +1 -1
  63. data/test/unit/compute/test_common_models.rb +1 -1
  64. data/test/unit/compute/test_disk.rb +26 -0
  65. data/test/unit/compute/test_server.rb +1 -1
  66. data/test/unit/dns/test_common_collections.rb +1 -1
  67. data/test/unit/monitoring/test_comon_collections.rb +1 -1
  68. data/test/unit/pubsub/test_common_collections.rb +1 -1
  69. data/test/unit/sql/test_common_collections.rb +1 -1
  70. data/test/unit/storage/test_common_json_collections.rb +1 -1
  71. data/test/unit/storage/test_common_xml_collections.rb +1 -1
  72. data/test/unit/storage/test_json_requests.rb +9 -1
  73. data/test/unit/storage/test_xml_requests.rb +1 -1
  74. metadata +63 -26
  75. data/.github/workflows/integration.yml +0 -225
@@ -4,9 +4,18 @@ module Fog
4
4
  class Regions < Fog::Collection
5
5
  model Fog::Compute::Google::Region
6
6
 
7
- def all
8
- data = service.list_regions.to_h
9
- load(data[:items] || [])
7
+ def all(opts = {})
8
+ items = []
9
+ next_page_token = nil
10
+ loop do
11
+ data = service.list_regions(**opts)
12
+ next_items = data.to_h[:items] || []
13
+ items.concat(next_items)
14
+ next_page_token = data.next_page_token
15
+ break if next_page_token.nil? || next_page_token.empty?
16
+ opts[:page_token] = next_page_token
17
+ end
18
+ load(items)
10
19
  end
11
20
 
12
21
  def get(identity)
@@ -4,9 +4,18 @@ module Fog
4
4
  class Routes < Fog::Collection
5
5
  model Fog::Compute::Google::Route
6
6
 
7
- def all
8
- data = service.list_routes.to_h
9
- load(data[:items] || [])
7
+ def all(opts = {})
8
+ items = []
9
+ next_page_token = nil
10
+ loop do
11
+ data = service.list_routes(**opts)
12
+ next_items = data.to_h[:items] || []
13
+ items.concat(next_items)
14
+ next_page_token = data.next_page_token
15
+ break if next_page_token.nil? || next_page_token.empty?
16
+ opts[:page_token] = next_page_token
17
+ end
18
+ load(items)
10
19
  end
11
20
 
12
21
  def get(identity)
@@ -27,7 +27,7 @@ module Fog
27
27
  # [
28
28
  # {
29
29
  # :initialize_params => {
30
- # :source_image => "projects/debian-cloud/global/images/family/debian-9"
30
+ # :source_image => "projects/debian-cloud/global/images/family/debian-11"
31
31
  # }
32
32
  # }
33
33
  # ]
@@ -279,7 +279,7 @@ module Fog
279
279
  if disk.is_a? Disk
280
280
  disk_obj = disk.get_attached_disk
281
281
  elsif disk.is_a? String
282
- disk_obj = service.disks.attached_disk_obj(disk, attached_disk_options)
282
+ disk_obj = service.disks.attached_disk_obj(disk, **attached_disk_options)
283
283
  end
284
284
 
285
285
  data = service.attach_disk(identity, zone_name, disk_obj)
@@ -339,10 +339,10 @@ module Fog
339
339
  operation
340
340
  end
341
341
 
342
- def stop(async = true)
342
+ def stop(async = true, discard_local_ssd=false)
343
343
  requires :identity, :zone
344
344
 
345
- data = service.stop_server(identity, zone_name)
345
+ data = service.stop_server(identity, zone_name, discard_local_ssd)
346
346
  operation = Fog::Compute::Google::Operations
347
347
  .new(:service => service)
348
348
  .get(data.name, data.zone)
@@ -13,17 +13,31 @@ module Fog
13
13
  :page_token => page_token
14
14
  }
15
15
 
16
- if zone
17
- data = service.list_servers(zone, **opts).to_h[:items] || []
18
- else
19
- data = []
20
- service.list_aggregated_servers(**opts).items.each_value do |scoped_lst|
21
- if scoped_lst && scoped_lst.instances
22
- data.concat(scoped_lst.instances.map(&:to_h))
16
+ items = []
17
+ next_page_token = nil
18
+
19
+ loop do
20
+ if zone
21
+ data = service.list_servers(zone, **opts)
22
+ next_items = data.to_h[:items] || []
23
+ items.concat(next_items)
24
+ next_page_token = data.next_page_token
25
+ else
26
+ data = service.list_aggregated_servers(**opts)
27
+ data.items.each_value do |scoped_lst|
28
+ if scoped_lst && scoped_lst.instances
29
+ items.concat(scoped_lst.instances.map(&:to_h))
30
+ end
23
31
  end
32
+ next_page_token = data.next_page_token
24
33
  end
34
+
35
+ break if next_page_token.nil? || next_page_token.empty?
36
+
37
+ opts[:page_token] = next_page_token
25
38
  end
26
- load(data)
39
+
40
+ load(items)
27
41
  end
28
42
 
29
43
  # TODO: This method needs to take self_links as well as names
@@ -50,7 +64,7 @@ module Fog
50
64
 
51
65
  if disks.nil? || disks.empty?
52
66
  # create the persistent boot disk
53
- source_img = service.images.get_from_family("debian-9")
67
+ source_img = service.images.get_from_family("debian-11")
54
68
  disk_defaults = {
55
69
  :name => name,
56
70
  :size_gb => 10,
@@ -14,9 +14,18 @@ module Fog
14
14
  nil
15
15
  end
16
16
 
17
- def all(_filters = {})
18
- data = service.list_ssl_certificates.to_h[:items] || []
19
- load(data)
17
+ def all(opts = {})
18
+ items = []
19
+ next_page_token = nil
20
+ loop do
21
+ data = service.list_ssl_certificates(**opts)
22
+ next_items = data.to_h[:items] || []
23
+ items.concat(next_items)
24
+ next_page_token = data.next_page_token
25
+ break if next_page_token.nil? || next_page_token.empty?
26
+ opts[:page_token] = next_page_token
27
+ end
28
+ load(items)
20
29
  end
21
30
  end
22
31
  end
@@ -11,16 +11,24 @@ module Fog
11
11
  :order_by => order_by,
12
12
  :page_token => page_token
13
13
  }
14
-
15
- if region
16
- data = service.list_subnetworks(region, **filters).to_h[:items] || []
17
- else
18
- data = []
19
- service.list_aggregated_subnetworks(**filters).to_h[:items].each_value do |region_obj|
20
- data.concat(region_obj[:subnetworks]) if region_obj[:subnetworks]
14
+ items = []
15
+ next_page_token = nil
16
+ loop do
17
+ if region
18
+ data = service.list_subnetworks(region, **filters)
19
+ next_items = data.items || []
20
+ items.concat(next_items)
21
+ next_page_token = data.next_page_token
22
+ else
23
+ data = service.list_aggregated_subnetworks(**filters)
24
+ data.items.each_value do |region_obj|
25
+ items.concat(region_obj.subnetworks) if region_obj && region_obj.subnetworks
26
+ end
21
27
  end
28
+ break if next_page_token.nil? || next_page_token.empty?
29
+ filters[:page_token] = next_page_token
22
30
  end
23
- load(data)
31
+ load(items.map(&:to_h))
24
32
  end
25
33
 
26
34
  def get(identity, region = nil)
@@ -4,9 +4,18 @@ module Fog
4
4
  class TargetHttpProxies < Fog::Collection
5
5
  model Fog::Compute::Google::TargetHttpProxy
6
6
 
7
- def all(_filters = {})
8
- data = service.list_target_http_proxies.to_h[:items] || []
9
- load(data)
7
+ def all(opts = {})
8
+ items = []
9
+ next_page_token = nil
10
+ loop do
11
+ data = service.list_target_http_proxies(*opts)
12
+ next_items = data.to_h[:items] || []
13
+ items.concat(next_items)
14
+ next_page_token = data.next_page_token
15
+ break if next_page_token.nil? || next_page_token.empty?
16
+ opts[:page_token] = next_page_token
17
+ end
18
+ load(items)
10
19
  end
11
20
 
12
21
  def get(identity)
@@ -4,9 +4,18 @@ module Fog
4
4
  class TargetHttpsProxies < Fog::Collection
5
5
  model Fog::Compute::Google::TargetHttpsProxy
6
6
 
7
- def all(_filters = {})
8
- data = service.list_target_https_proxies.to_h[:items] || []
9
- load(data)
7
+ def all(opts = {})
8
+ items = []
9
+ next_page_token = nil
10
+ loop do
11
+ data = service.list_target_https_proxies(**opts)
12
+ next_items = data.to_h[:items] || []
13
+ items.concat(next_items)
14
+ next_page_token = data.next_page_token
15
+ break if next_page_token.nil? || next_page_token.empty?
16
+ opts[:page_token] = next_page_token
17
+ end
18
+ load(items)
10
19
  end
11
20
 
12
21
  def get(identity)
@@ -13,17 +13,25 @@ module Fog
13
13
  :page_token => page_token
14
14
  }
15
15
 
16
- if zone
17
- data = service.list_target_instances(zone, **opts).to_h[:items] || []
18
- else
19
- data = []
20
- service.list_aggregated_target_instances(**opts).items.each_value do |scoped_list|
21
- unless scoped_list.nil? || scoped_list.target_instances.nil?
22
- data += scoped_list.target_instances.map(&:to_h)
16
+ items = []
17
+ next_page_token = nil
18
+ loop do
19
+ if zone
20
+ data = service.list_target_instances(zone, **opts)
21
+ next_items = data.to_h[:items] || []
22
+ items.concat(next_items)
23
+ next_page_token = data.next_page_token
24
+ else
25
+ data = service.list_aggregated_target_instances(**opts)
26
+ data.items.each_value do |scoped_list|
27
+ items.concat(scoped_list.target_instances.map(&:to_h)) if scoped_list && scoped_list.target_instances
23
28
  end
29
+ next_page_token = data.next_page_token
24
30
  end
31
+ break if next_page_token.nil? || next_page_token.empty?
32
+ opts[:page_token] = next_page_token
25
33
  end
26
- load(data)
34
+ load(items)
27
35
  end
28
36
 
29
37
  def get(identity, zone = nil)
@@ -11,17 +11,25 @@ module Fog
11
11
  :order_by => order_by,
12
12
  :page_token => page_token
13
13
  }
14
- if region.nil?
15
- data = []
16
- service.list_aggregated_target_pools(**opts).items.each_value do |lst|
17
- unless lst.nil? || lst.target_pools.nil?
18
- data += lst.to_h[:target_pools]
14
+ items = []
15
+ next_page_token = nil
16
+ loop do
17
+ if region.nil?
18
+ data = service.list_aggregated_target_pools(**opts)
19
+ data.items.each_value do |lst|
20
+ items.concat(lst.to_h[:target_pools]) if lst && lst.target_pools
19
21
  end
22
+ next_page_token = data.next_page_token
23
+ else
24
+ data = service.list_target_pools(region, **opts)
25
+ next_items = data.to_h[:items] || []
26
+ items.concat(next_items)
27
+ next_page_token = data.next_page_token
20
28
  end
21
- else
22
- data = service.list_target_pools(region, **opts).to_h[:items]
29
+ break if next_page_token.nil? || next_page_token.empty?
30
+ opts[:page_token] = next_page_token
23
31
  end
24
- load(data)
32
+ load(items)
25
33
  end
26
34
 
27
35
  def get(identity, region = nil)
@@ -4,9 +4,18 @@ module Fog
4
4
  class UrlMaps < Fog::Collection
5
5
  model Fog::Compute::Google::UrlMap
6
6
 
7
- def all
8
- data = service.list_url_maps.to_h[:items] || []
9
- load(data)
7
+ def all(opts = {})
8
+ items = []
9
+ next_page_token = nil
10
+ loop do
11
+ data = service.list_url_maps(**opts)
12
+ next_items = data.to_h[:items] || []
13
+ items.concat(next_items)
14
+ next_page_token = data.next_page_token
15
+ break if next_page_token.nil? || next_page_token.empty?
16
+ opts[:page_token] = next_page_token
17
+ end
18
+ load(items)
10
19
  end
11
20
 
12
21
  def get(identity)
@@ -4,9 +4,18 @@ module Fog
4
4
  class Zones < Fog::Collection
5
5
  model Fog::Compute::Google::Zone
6
6
 
7
- def all
8
- data = service.list_zones.to_h[:items] || []
9
- load(data)
7
+ def all(opts = {})
8
+ items = []
9
+ next_page_token = nil
10
+ loop do
11
+ data = service.list_zones
12
+ next_items = data.to_h[:items] || []
13
+ items.concat(next_items)
14
+ next_page_token = data.next_page_token
15
+ break if next_page_token.nil? || next_page_token.empty?
16
+ opts[:page_token] = next_page_token
17
+ end
18
+ load(items)
10
19
  end
11
20
 
12
21
  def get(identity)
@@ -52,7 +52,7 @@ module Fog
52
52
  # :disks => [
53
53
  # {
54
54
  # :initialize_params => {
55
- # :source_image => "projects/debian-cloud/global/images/family/debian-9"
55
+ # :source_image => "projects/debian-cloud/global/images/family/debian-11"
56
56
  # }
57
57
  # }
58
58
  # ]
@@ -10,8 +10,8 @@ module Fog
10
10
  end
11
11
 
12
12
  class Real
13
- def stop_server(identity, zone)
14
- @compute.stop_instance(@project, zone.split("/")[-1], identity)
13
+ def stop_server(identity, zone, discard_local_ssd=false)
14
+ @compute.stop_instance(@project, zone.split("/")[-1], identity, discard_local_ssd: discard_local_ssd)
15
15
  end
16
16
  end
17
17
  end
@@ -67,20 +67,21 @@ module Fog
67
67
  ::Google::Apis.logger.level = ::Logger::DEBUG
68
68
  end
69
69
 
70
- auth = nil
70
+ initialize_auth(options).tap do |auth|
71
+ ::Google::Apis::RequestOptions.default.authorization = auth
72
+ end
73
+ end
71
74
 
75
+ def initialize_auth(options)
72
76
  if options[:google_json_key_location] || options[:google_json_key_string]
73
- auth = process_key_auth(options)
77
+ process_key_auth(options)
74
78
  elsif options[:google_auth]
75
- auth = options[:google_auth]
79
+ options[:google_auth]
76
80
  elsif options[:google_application_default]
77
- auth = process_application_default_auth(options)
81
+ process_application_default_auth(options)
78
82
  else
79
- auth = process_fallback_auth(options)
83
+ process_fallback_auth(options)
80
84
  end
81
-
82
- ::Google::Apis::RequestOptions.default.authorization = auth
83
- auth
84
85
  end
85
86
 
86
87
  ##
@@ -1,5 +1,5 @@
1
1
  module Fog
2
2
  module Google
3
- VERSION = "1.19.0".freeze
3
+ VERSION = "1.24.1".freeze
4
4
  end
5
5
  end
@@ -5,6 +5,7 @@ module Fog
5
5
  identity :key, :aliases => ["Key", :name]
6
6
 
7
7
  attribute :acl
8
+ attribute :uniform
8
9
  attribute :predefined_acl, :aliases => ["predefinedAcl", :predefined_acl]
9
10
  attribute :cache_control, :aliases => ["cacheControl", :cache_control]
10
11
  attribute :content_disposition, :aliases => ["contentDisposition", :content_disposition]
@@ -37,6 +38,10 @@ module Fog
37
38
  "publicReadWrite"
38
39
  ].freeze
39
40
 
41
+ def uniform=(enable)
42
+ @uniform=enable
43
+ end
44
+
40
45
  def predefined_acl=(new_predefined_acl)
41
46
  unless VALID_PREDEFINED_ACLS.include?(new_predefined_acl)
42
47
  raise ArgumentError.new("acl must be one of [#{VALID_PREDEFINED_ACLS.join(', ')}]")
@@ -111,8 +116,8 @@ module Fog
111
116
  FILE_INSERTABLE_FIELDS.map { |k| [k, attributes[k]] }
112
117
  .reject { |pair| pair[1].nil? }
113
118
  ]
114
-
115
- options[:predefined_acl] ||= @predefined_acl
119
+
120
+ options[:predefined_acl] ||= @predefined_acl unless @uniform
116
121
 
117
122
  service.put_object(directory.key, key, body, **options)
118
123
  self.content_length = Fog::Storage.get_body_size(body)
@@ -10,16 +10,12 @@ module Fog
10
10
 
11
11
  def initialize(options = {})
12
12
  shared_initialize(options[:google_project], GOOGLE_STORAGE_JSON_API_VERSION, GOOGLE_STORAGE_JSON_BASE_URL)
13
+ @options = options.dup
13
14
  options[:google_api_scope_url] = GOOGLE_STORAGE_JSON_API_SCOPE_URLS.join(" ")
14
15
  @host = options[:host] || "storage.googleapis.com"
15
16
 
16
17
  # TODO(temikus): Do we even need this client?
17
18
  @client = initialize_google_client(options)
18
- # IAM client used for SignBlob API
19
- @iam_service = ::Google::Apis::IamcredentialsV1::IAMCredentialsService.new
20
- apply_client_options(@iam_service, {
21
- google_api_scope_url: GOOGLE_STORAGE_JSON_IAM_API_SCOPE_URLS.join(" ")
22
- })
23
19
 
24
20
  @storage_json = ::Google::Apis::StorageV1::StorageService.new
25
21
  apply_client_options(@storage_json, options)
@@ -135,11 +131,24 @@ DATA
135
131
  # See https://cloud.google.com/storage/docs/access-control/signed-urls-v2
136
132
  # @return [String] Signature binary blob
137
133
  def default_signer(string_to_sign)
138
- key = OpenSSL::PKey::RSA.new(@storage_json.authorization.signing_key)
134
+ key = @storage_json.authorization.signing_key
135
+ key = OpenSSL::PKey::RSA.new(@storage_json.authorization.signing_key) unless key.respond_to?(:sign)
139
136
  digest = OpenSSL::Digest::SHA256.new
140
137
  return key.sign(digest, string_to_sign)
141
138
  end
142
139
 
140
+ # IAM client used for SignBlob API.
141
+ # Lazily initialize this since it requires another authorization request.
142
+ def iam_service
143
+ return @iam_service if defined?(@iam_service)
144
+
145
+ @iam_service = ::Google::Apis::IamcredentialsV1::IAMCredentialsService.new
146
+ apply_client_options(@iam_service, @options)
147
+ iam_options = @options.merge(google_api_scope_url: GOOGLE_STORAGE_JSON_IAM_API_SCOPE_URLS.join(" "))
148
+ @iam_service.authorization = initialize_auth(iam_options)
149
+ @iam_service
150
+ end
151
+
143
152
  ##
144
153
  # Fallback URL signer using the IAM SignServiceAccountBlob API, see
145
154
  # Google::Apis::IamcredentialsV1::IAMCredentialsService#sign_service_account_blob
@@ -161,7 +170,7 @@ DATA
161
170
  )
162
171
 
163
172
  resource = "projects/-/serviceAccounts/#{google_access_id}"
164
- response = @iam_service.sign_service_account_blob(resource, request)
173
+ response = iam_service.sign_service_account_blob(resource, request)
165
174
 
166
175
  return response.signed_blob
167
176
  end
@@ -1,3 +1,5 @@
1
+ require 'addressable'
2
+
1
3
  module Fog
2
4
  module Storage
3
5
  class GoogleJSON
@@ -19,10 +21,7 @@ module Fog
19
21
 
20
22
  def host_path_query(params, expires)
21
23
  params[:headers]["Date"] = expires.to_i
22
- # implementation from CGI.escape, but without ' ' to '+' conversion
23
- params[:path] = params[:path].b.gsub(/([^a-zA-Z0-9_.\-~]+)/) { |m|
24
- '%' + m.unpack('H2' * m.bytesize).join('%').upcase
25
- }.gsub("%2F", "/")
24
+ params[:path] = ::Addressable::URI.encode_component(params[:path], ::Addressable::URI::CharacterClasses::PATH)
26
25
 
27
26
  query = []
28
27
 
@@ -29,7 +29,7 @@ module Fog
29
29
 
30
30
  # Version of IAM API used for blob signing, see Fog::Storage::GoogleJSON::Real#iam_signer
31
31
  GOOGLE_STORAGE_JSON_IAM_API_VERSION = "v1".freeze
32
- GOOGLE_STORAGE_JSON_IAM_API_SCOPE_URLS = %w(https://www.googleapis.com/auth/devstorage.full_control).freeze
32
+ GOOGLE_STORAGE_JSON_IAM_API_SCOPE_URLS = %w(https://www.googleapis.com/auth/iam).freeze
33
33
 
34
34
  # TODO: Come up with a way to only request a subset of permissions.
35
35
  # https://cloud.google.com/storage/docs/json_api/v1/how-tos/authorizing
@@ -17,9 +17,9 @@ TEST_ZONE = "us-central1-f".freeze
17
17
  TEST_REGION = "us-central1".freeze
18
18
  TEST_SIZE_GB = 10
19
19
  TEST_MACHINE_TYPE = "n1-standard-1".freeze
20
- TEST_IMAGE = "debian-9-stretch-v20180611".freeze
20
+ TEST_IMAGE = "debian-11-bullseye-v20220920".freeze
21
21
  TEST_IMAGE_PROJECT = "debian-cloud".freeze
22
- TEST_IMAGE_FAMILY = "debian-9".freeze
22
+ TEST_IMAGE_FAMILY = "debian-11".freeze
23
23
 
24
24
  # XXX This depends on a public image in gs://fog-test-bucket; there may be a better way to do this
25
25
  # The image was created like so: https://cloud.google.com/compute/docs/images/export-image
@@ -93,7 +93,7 @@ KEY
93
93
  RETRIABLE_TRIES = 3
94
94
  RETRIABLE_BASE_INTERVAL = 50
95
95
 
96
- class FogIntegrationTest < MiniTest::Test
96
+ class FogIntegrationTest < Minitest::Test
97
97
  def namespaced_name
98
98
  "#{self.class}_#{name}"
99
99
  end
@@ -132,6 +132,92 @@ class TestServers < FogIntegrationTest
132
132
  assert server.ready?
133
133
  end
134
134
 
135
+ def test_start_stop_discard_local_ssd
136
+ server = @factory.create
137
+
138
+ async = true
139
+ discard_local_ssd = true
140
+
141
+ server.stop(async, discard_local_ssd)
142
+ server.wait_for { stopped? }
143
+
144
+ assert server.stopped?
145
+ end
146
+
147
+ def test_attach_disk
148
+ # Creating server
149
+ server = @factory.create
150
+ server.wait_for { ready? }
151
+
152
+ disk_name = "fog-test-1-testservers-test-attach-disk-attachable" # suffix forces disk name to differ from the existing disk
153
+ # Creating disk #{disk_name}
154
+ disk = @disks.create(
155
+ :name => disk_name,
156
+ :source_image => TEST_IMAGE,
157
+ :size_gb => 64
158
+ )
159
+ device_name = "#{disk.name}-device"
160
+
161
+ # Attaching disk #{disk.name} as device #{device_name}
162
+ self_link = "https://www.googleapis.com/compute/v1/projects/#{TEST_PROJECT}/zones/#{TEST_ZONE}/disks/#{disk.name}"
163
+ server.attach_disk(self_link, true, device_name: device_name)
164
+
165
+ # Waiting for attachment
166
+ disk.wait_for { ! users.nil? && users != []}
167
+
168
+ assert_equal "https://www.googleapis.com/compute/v1/projects/#{TEST_PROJECT}/zones/#{TEST_ZONE}/instances/#{server.name}", disk.users[0]
169
+
170
+ server.reload
171
+ server_attached_disk = server.disks.select{|d| d[:boot] == false}[0]
172
+ assert_equal device_name, server_attached_disk[:device_name]
173
+ end
174
+
175
+ def test_detach_disk
176
+ # Creating server
177
+ server = @factory.create
178
+ server.wait_for { ready? }
179
+
180
+ disk_name = "fog-test-1-testservers-test-detach-attachable" # suffix forces disk name to differ from the existing disk
181
+ # Creating disk #{disk_name}
182
+ disk = @disks.create(
183
+ :name => disk_name,
184
+ :source_image => TEST_IMAGE,
185
+ :size_gb => 64
186
+ )
187
+ device_name = "#{disk.name}-device"
188
+
189
+ # Attaching disk #{disk.name} as device #{device_name}
190
+ self_link = "https://www.googleapis.com/compute/v1/projects/#{TEST_PROJECT}/zones/#{TEST_ZONE}/disks/#{disk.name}"
191
+ server.attach_disk(self_link, true, device_name: device_name)
192
+ disk.wait_for { ! users.nil? && users != []}
193
+
194
+ server.reload
195
+ server_attached_disk = server.disks.select{|d| d[:boot] == false}[0]
196
+ assert_equal device_name, server_attached_disk[:device_name]
197
+
198
+ # Detaching (synchronous) disk #{disk.name}
199
+ server.detach_disk(device_name, false)
200
+
201
+ disk.reload
202
+ assert disk.users.nil? || disk.users == []
203
+
204
+ # Re-attaching disk #{disk.name} as device #{device_name}
205
+ server.attach_disk(self_link, true, device_name: device_name)
206
+ disk.wait_for { ! users.nil? && users != []}
207
+
208
+ server.reload
209
+ server_attached_disk = server.disks.select{|d| d[:boot] == false}[0]
210
+ assert_equal device_name, server_attached_disk[:device_name]
211
+
212
+ # Detaching (async) disk #{disk.name}
213
+ server.detach_disk(device_name, true)
214
+
215
+ # Waiting for detachment
216
+ disk.wait_for { users.nil? || users == []}
217
+
218
+ assert disk.users.nil? || disk.users == []
219
+ end
220
+
135
221
  def test_reset_windows_password
136
222
  win_disk = @disks.create(
137
223
  :name => "fog-test-1-testservers-test-reset-windows-password-2",