azure-armrest 0.4.0 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 27744a66e69a816479bb6dc1d7a2bb5070d42717
4
- data.tar.gz: b1ded209d9ff4e9c8bde0981e55c5b95586a4511
3
+ metadata.gz: 0a888f86dfd49210ca1ce4232968d2449f8ee1a2
4
+ data.tar.gz: 8a19fb58fe560e115b0153e843f0eaaf8329c61d
5
5
  SHA512:
6
- metadata.gz: 6aaa5ae7fd87a32a0727e0965e84ece73ae426159ce9f49c54c6cd90e23ddc6f4939df8eda1f45ee5a8543f0fce7399dd5870fbddf87f556aa38ea3ec93fe5d3
7
- data.tar.gz: 8dc378aa589e06f5d45c387356d83f23ed489796bab3289c9443344514d3726d467b6085047c37260235bee908857280739f3efa9bae59f0e340d141cda7199b
6
+ metadata.gz: cc6c79041f6c46dcad609fd72e2e5c719c2b84cea7e0fc2cf10fd3da4bc789f92bfcdcddf69748fbfe1e9e837fc001ab607c1d6bc38d2ce0752639284f7acbb7
7
+ data.tar.gz: 55fd98f1b9af93b7e84b6eaef6b98d5814a31531518ceb00c5c617e2242c547d74ab40c3f4d073bab98d2feb0ca5e09cf1292f45a982bf49532f60817426cc90
data/CHANGES CHANGED
@@ -1,3 +1,25 @@
1
+ = 0.4.1 - 16-Dec-2016
2
+ * The Configuration#environment accessor was added. As of this release you
3
+ can specify 'usgov' as an option to the constructor, and the appropriate
4
+ resource and authority endpoints will be used instead of the public ones.
5
+ * Added the #authority_url and #resource_url accessors to the Configuration
6
+ class. Use wisely.
7
+ * The Armrest::COMMON constant was removed because the resource isn't actually
8
+ constant. Instead use the authority_url or resource_url methods. This was
9
+ really only meant for internal use anyway.
10
+ * Added the Armrest::USGOV_RESOURCE and Armrest::USGOV_AUTHORITY constants.
11
+ * Fixed a bug in the VirtualMachineService#delete_associated_resources where
12
+ the method would fail if you requested network security group deletion but
13
+ there was no associated network security group.
14
+ * Fixed an issue in the ArmrestService#poll method where a 202 response might
15
+ not actually have a body. In that case, it is treated as success.
16
+ * Fixed a logic bug in the ArmrestService#wait method, and added the option
17
+ to specify 0 (infinity) for the wait time.
18
+ * Added the ArmrestService#log method, and the Configuration.log= method now
19
+ automatically converts the argument to a Logger instance if it's not already.
20
+ * The ArmrestService#base_url method was altered to include subscription
21
+ information.
22
+
1
23
  = 0.4.0 - 8-Dec-2016
2
24
  * The Configuration constructor no longer requires a subscription ID. However,
3
25
  the presence of a subscription ID is still required for almost all Service
data/README.md CHANGED
@@ -43,9 +43,13 @@ end
43
43
 
44
44
  ## Subscriptions
45
45
 
46
- As of version 0.3.0 you must provide a subscription ID. In previous versions,
47
- if you did not provide a subscription ID in your configuration object, then the
48
- first subscription ID returned from a REST call would be used.
46
+ As of version 0.4.0 you a subscription ID is not longer strictly necessary in
47
+ the Configuration constructor, but almost all service classes require it in
48
+ their own constructor. Only the SubscriptionService class does not.
49
+
50
+ In version 0.3.x the subscription ID was mandatory. Prior to 0.3.x, if you did
51
+ not provide a subscription ID in your configuration object, then the first
52
+ subscription ID returned from a REST call would be used.
49
53
 
50
54
  ## Notes
51
55
 
data/lib/azure/armrest.rb CHANGED
@@ -11,14 +11,20 @@ module Azure
11
11
  # The Armrest module mostly serves as a namespace, but also contains any
12
12
  # common constants shared by subclasses.
13
13
  module Armrest
14
- # The default Azure resource
14
+ # The default (public) Azure resource
15
15
  RESOURCE = "https://management.azure.com/"
16
16
 
17
- # The default authority resource
18
- AUTHORITY = "https://login.windows.net/"
17
+ # The resource for US Government clients
18
+ USGOV_RESOURCE = "https://management.core.usgovcloudapi.net/"
19
19
 
20
- # A common URI for all subclasses
21
- COMMON_URI = RESOURCE + "subscriptions/"
20
+ # The default (public) authority resource
21
+ AUTHORITY = "https://login.microsoftonline.com/"
22
+
23
+ # The authority for US Government clients
24
+ USGOV_AUTHORITY = "https://login-us.microsoftonline.com/"
25
+
26
+ # Environment string used to indicate US Government
27
+ USGOV_ENVIRONMENT = 'usgov'
22
28
  end
23
29
  end
24
30
 
@@ -12,7 +12,7 @@ module Azure
12
12
 
13
13
  alias configuration armrest_configuration
14
14
 
15
- # Base url used for REST calls.
15
+ # Base url with subscription information used for most REST calls.
16
16
  attr_accessor :base_url
17
17
 
18
18
  # Provider for service specific API calls
@@ -46,7 +46,7 @@ module Azure
46
46
  end
47
47
 
48
48
  # Base URL used for REST calls. Modify within method calls as needed.
49
- @base_url = Azure::Armrest::RESOURCE
49
+ @base_url = File.join(configuration.resource_url, 'subscriptions', configuration.subscription_id)
50
50
 
51
51
  set_service_api_version(options, service_name)
52
52
  end
@@ -165,7 +165,11 @@ module Azure
165
165
  def poll(response)
166
166
  return 'Succeeded' if [200, 201].include?(response.response_code)
167
167
  url = response.try(:azure_asyncoperation) || response.try(:location)
168
- JSON.parse(rest_get(url))['status']
168
+ response = rest_get(url).body
169
+ unless response.blank?
170
+ status = JSON.parse(response)['status']
171
+ end
172
+ status || 'Succeeded' # assume succeeded otherwise the wait method may hang
169
173
  end
170
174
 
171
175
  # Wait for the given +response+ to return a status of 'Succeeded', up
@@ -175,7 +179,7 @@ module Azure
175
179
  #
176
180
  # Internally this will poll the response header every :retry_after
177
181
  # seconds (or 10 seconds if that header isn't found), up to a maximum of
178
- # 60 seconds by default.
182
+ # 60 seconds by default. There is no timeout limit if +max_time+ is 0.
179
183
  #
180
184
  # For most resources the +max_time+ argument should be more than sufficient.
181
185
  # Certain resources, such as virtual machines, could take longer.
@@ -184,9 +188,9 @@ module Azure
184
188
  sleep_time = response.respond_to?(:retry_after) ? response.retry_after.to_i : 10
185
189
  total_time = 0
186
190
 
187
- while (status = poll(response)).casecmp('Succeeded') != 0
191
+ until (status = poll(response)) =~ /^succe/i # success or succeeded
188
192
  total_time += sleep_time
189
- break if total_time >= max_time
193
+ break if max_time > 0 && total_time >= max_time
190
194
  sleep sleep_time
191
195
  end
192
196
 
@@ -353,6 +357,10 @@ module Azure
353
357
  def model_class
354
358
  @model_class ||= Object.const_get(self.class.to_s.sub(/Service$/, ''))
355
359
  end
360
+
361
+ def log(level = "info", msg)
362
+ RestClient.log.try(level, msg)
363
+ end
356
364
  end # ArmrestService
357
365
  end # Armrest
358
366
  end # Azure
@@ -59,14 +59,7 @@ module Azure
59
59
  private
60
60
 
61
61
  def build_url(options = {})
62
- url = File.join(
63
- Azure::Armrest::COMMON_URI,
64
- configuration.subscription_id,
65
- 'providers',
66
- @provider,
67
- 'UsageAggregates'
68
- )
69
-
62
+ url = File.join(base_url, 'providers', @provider, 'UsageAggregates')
70
63
  url << "?api-version=#{@api_version}"
71
64
 
72
65
  options.each do |key, value|
@@ -64,6 +64,15 @@ module Azure
64
64
  # Maximum number of threads to use within methods that use Parallel for thread pooling.
65
65
  attr_accessor :max_threads
66
66
 
67
+ # The environment in which to acquire your token.
68
+ attr_reader :environment
69
+
70
+ # The authority URL used to acquire a valid token.
71
+ attr_accessor :resource_url
72
+
73
+ # The resource URL used to acquire a valid token.
74
+ attr_accessor :authority_url
75
+
67
76
  # Yields a new Azure::Armrest::Configuration objects. Note that you must
68
77
  # specify a client_id, client_key, tenant_id. The subscription_id is optional
69
78
  # but should be specified in most cases. All other parameters are optional.
@@ -90,13 +99,15 @@ module Azure
90
99
  def initialize(args)
91
100
  # Use defaults, and override with provided arguments
92
101
  options = {
93
- :api_version => '2015-01-01',
94
- :accept => 'application/json',
95
- :content_type => 'application/json',
96
- :grant_type => 'client_credentials',
97
- :proxy => ENV['http_proxy'],
98
- :ssl_version => 'TLSv1',
99
- :max_threads => 10
102
+ :api_version => '2015-01-01',
103
+ :accept => 'application/json',
104
+ :content_type => 'application/json',
105
+ :grant_type => 'client_credentials',
106
+ :proxy => ENV['http_proxy'],
107
+ :ssl_version => 'TLSv1',
108
+ :max_threads => 10,
109
+ :authority_url => Azure::Armrest::AUTHORITY,
110
+ :resource_url => Azure::Armrest::RESOURCE
100
111
  }.merge(args.symbolize_keys)
101
112
 
102
113
  # Avoid thread safety issues for VCR testing.
@@ -184,19 +195,18 @@ module Azure
184
195
  end
185
196
  end
186
197
 
187
- # The name of the file or handle used to log http requests.
188
- #--
189
- # We have to do a little extra work here to convert a possible
190
- # file handle to a file name.
198
+ # Returns the logger instance. It might be initially set through a log
199
+ # file path, file handler, or already a logger instance.
191
200
  #
192
201
  def self.log
193
- file = RestClient.log.instance_variable_get("@target_file")
194
- file || RestClient.log
202
+ RestClient.log
195
203
  end
196
204
 
197
- # Sets the log to +output+, which can be a file or a handle.
205
+ # Sets the log to +output+, which can be a file, a file handle, or
206
+ # a logger instance
198
207
  #
199
208
  def self.log=(output)
209
+ output = Logger.new(output) unless output.kind_of?(Logger)
200
210
  RestClient.log = output
201
211
  end
202
212
 
@@ -208,6 +218,28 @@ module Azure
208
218
 
209
219
  private
210
220
 
221
+ # Sets the environment to authenticate against. The environment
222
+ # must support ActiveDirectory.
223
+ #
224
+ def environment=(env)
225
+ return if env == environment
226
+ set_auth_and_resource_urls(env)
227
+ @environment = env
228
+ end
229
+
230
+ # Sets the authority_url and resource_url accessors depending on the
231
+ # environment.
232
+ #--
233
+ # Only two supported at the moment, but more likely to be added.
234
+ #
235
+ def set_auth_and_resource_urls(env)
236
+ case env.to_s.downcase
237
+ when Azure::Armrest::USGOV_ENVIRONMENT
238
+ @authority_url = Azure::Armrest::USGOV_AUTHORITY
239
+ @resource_url = Azure::Armrest::USGOV_RESOURCE
240
+ end
241
+ end
242
+
211
243
  # Validate the subscription ID for the given credentials. Returns the
212
244
  # subscription ID if valid.
213
245
  #
@@ -275,7 +307,7 @@ module Azure
275
307
  end
276
308
 
277
309
  def fetch_token
278
- token_url = File.join(Azure::Armrest::AUTHORITY, tenant_id, 'oauth2/token')
310
+ token_url = File.join(authority_url, tenant_id, 'oauth2/token')
279
311
 
280
312
  response = JSON.parse(
281
313
  ArmrestService.send(
@@ -288,7 +320,7 @@ module Azure
288
320
  :grant_type => grant_type,
289
321
  :client_id => client_id,
290
322
  :client_secret => client_key,
291
- :resource => Azure::Armrest::RESOURCE
323
+ :resource => resource_url
292
324
  }
293
325
  )
294
326
  )
@@ -80,7 +80,7 @@ module Azure
80
80
 
81
81
  def build_url(resource_id)
82
82
  url = File.join(
83
- Azure::Armrest::RESOURCE,
83
+ configuration.resource_url,
84
84
  resource_id,
85
85
  'providers',
86
86
  provider,
@@ -66,19 +66,7 @@ module Azure
66
66
  private
67
67
 
68
68
  def build_url(options = {})
69
- sub_id = armrest_configuration.subscription_id
70
-
71
- url =
72
- File.join(
73
- Azure::Armrest::COMMON_URI,
74
- sub_id,
75
- 'providers',
76
- provider,
77
- 'eventtypes',
78
- 'management',
79
- 'values'
80
- )
81
-
69
+ url = File.join(base_url, 'providers', provider, 'eventtypes', 'management', 'values')
82
70
  url << "?api-version=#{@api_version}"
83
71
  url << "&$filter=#{options[:filter]}" if options[:filter]
84
72
  url << "&$select=#{options[:select]}" if options[:select]
@@ -34,11 +34,8 @@ module Azure
34
34
  private
35
35
 
36
36
  def build_url(provider, resource_type, resource_name, resource_group, options)
37
- sub_id = configuration.subscription_id
38
-
39
37
  url = File.join(
40
- Azure::Armrest::COMMON_URI,
41
- sub_id,
38
+ base_url,
42
39
  'resourceGroups',
43
40
  resource_group,
44
41
  'providers',
@@ -114,7 +114,7 @@ module Azure
114
114
  api_version ||= configuration.api_version
115
115
  service_name = info['subservice_name'] || info['service_name']
116
116
 
117
- url = File.join(Azure::Armrest::RESOURCE, id_string) + "?api-version=#{api_version}"
117
+ url = File.join(configuration.resource_url, id_string) + "?api-version=#{api_version}"
118
118
 
119
119
  model_class = SERVICE_NAME_MAP.fetch(service_name.downcase) do
120
120
  raise ArgumentError, "unable to map service name #{service_name} to model"
@@ -203,7 +203,7 @@ module Azure
203
203
  # arguments provided, and appends it with the api_version.
204
204
  #
205
205
  def build_url(resource_group = nil, *args)
206
- url = File.join(Azure::Armrest::COMMON_URI, configuration.subscription_id)
206
+ url = base_url
207
207
  url = File.join(url, 'resourceGroups', resource_group) if resource_group
208
208
  url = File.join(url, 'providers', @provider, @service_name)
209
209
  url = File.join(url, *args) unless args.empty?
@@ -69,8 +69,7 @@ module Azure
69
69
  private
70
70
 
71
71
  def build_url(group = nil, *args)
72
- id = configuration.subscription_id
73
- url = File.join(Azure::Armrest::COMMON_URI, id, 'resourcegroups')
72
+ url = File.join(base_url, 'resourcegroups')
74
73
  url = File.join(url, group) if group
75
74
  url = File.join(url, *args) unless args.empty?
76
75
  url << "?api-version=#{@api_version}"
@@ -61,7 +61,7 @@ module Azure
61
61
  end
62
62
 
63
63
  def _list_all
64
- url = File.join(Azure::Armrest::RESOURCE, 'providers')
64
+ url = File.join(configuration.resource_url, 'providers')
65
65
  url << "?api-version=#{@api_version}"
66
66
  response = rest_get(url)
67
67
  JSON.parse(response)['value']
@@ -127,7 +127,7 @@ module Azure
127
127
 
128
128
  def build_url(namespace = nil, *args)
129
129
  id = configuration.subscription_id
130
- url = File.join(Azure::Armrest::COMMON_URI, id, 'providers')
130
+ url = File.join(base_url, 'providers')
131
131
  url = File.join(url, namespace) if namespace
132
132
  url = File.join(url, *args) unless args.empty?
133
133
  url << "?api-version=#{@api_version}"
@@ -56,8 +56,12 @@ module Azure
56
56
  #
57
57
  def move(source_group, source_subscription = configuration.subscription_id)
58
58
  url = File.join(
59
- Azure::Armrest::COMMON_URI, source_subscription,
60
- 'resourcegroups', source_group, 'moveresources'
59
+ configuration.resource_url,
60
+ 'subscriptions',
61
+ source_subscription,
62
+ 'resourcegroups',
63
+ source_group,
64
+ 'moveresources'
61
65
  )
62
66
 
63
67
  url << "?api-version=#{@api_version}"
@@ -90,12 +94,10 @@ module Azure
90
94
  private
91
95
 
92
96
  def build_url(resource_group = nil, options = {})
93
- url = File.join(Azure::Armrest::COMMON_URI, configuration.subscription_id)
94
-
95
97
  if resource_group
96
- url = File.join(url, 'resourceGroups', resource_group, 'resources')
98
+ url = File.join(base_url, 'resourceGroups', resource_group, 'resources')
97
99
  else
98
- url = File.join(url, 'resources')
100
+ url = File.join(base_url, 'resources')
99
101
  end
100
102
 
101
103
  url << "?api-version=#{@api_version}"
@@ -28,7 +28,7 @@ module Azure
28
28
  private
29
29
 
30
30
  def subscriptions_url
31
- File.join(Azure::Armrest::RESOURCE, 'subscriptions')
31
+ File.join(configuration.resource_url, 'subscriptions')
32
32
  end
33
33
  end
34
34
  end
@@ -1,5 +1,5 @@
1
1
  module Azure
2
2
  module Armrest
3
- VERSION = '0.4.0'.freeze
3
+ VERSION = '0.4.1'.freeze
4
4
  end
5
5
  end
@@ -117,8 +117,7 @@ module Azure
117
117
  #
118
118
  def build_url(resource_group, vm, *args)
119
119
  url = File.join(
120
- Azure::Armrest::COMMON_URI,
121
- configuration.subscription_id,
120
+ base_url,
122
121
  'resourceGroups',
123
122
  resource_group,
124
123
  'providers',
@@ -98,15 +98,7 @@ module Azure
98
98
  # arguments provided, and appends it with the api_version.
99
99
  #
100
100
  def build_url(location, *args)
101
- url = File.join(
102
- Azure::Armrest::COMMON_URI,
103
- configuration.subscription_id,
104
- 'providers',
105
- provider,
106
- 'locations',
107
- location
108
- )
109
-
101
+ url = File.join(base_url, 'providers', provider, 'locations', location)
110
102
  url = File.join(url, *args) unless args.empty?
111
103
  url << "?api-version=#{@api_version}"
112
104
  end
@@ -200,9 +200,10 @@ module Azure
200
200
  end
201
201
 
202
202
  if options[:network_security_groups]
203
- nic.properties.network_security_group
204
- nsg = get_associated_resource(nic.properties.network_security_group.id)
205
- delete_and_wait(nsgs, nsg.name, nsg.resource_group, options)
203
+ if nic.properties.respond_to?(:network_security_group)
204
+ nsg = get_associated_resource(nic.properties.network_security_group.id)
205
+ delete_and_wait(nsgs, nsg.name, nsg.resource_group, options)
206
+ end
206
207
  end
207
208
  end
208
209
  end
@@ -243,13 +244,13 @@ module Azure
243
244
 
244
245
  # In the unlikely event it did not unlock, just log and skip.
245
246
  if disk.x_ms_lease_status.casecmp('unlocked') != 0
246
- log_message("Unable to delete disk #{disk.container}/#{disk.name}", 'warn')
247
+ log('warn', "Unable to delete disk #{disk.container}/#{disk.name}")
247
248
  return
248
249
  end
249
250
  end
250
251
 
251
252
  storage_account.delete_blob(disk.container, disk.name, key)
252
- log_message("Deleted blob #{disk.container}/#{disk.name}") if options[:verbose]
253
+ log("Deleted blob #{disk.container}/#{disk.name}") if options[:verbose]
253
254
 
254
255
  begin
255
256
  status_file = File.basename(disk.name, '.vhd') + '.status'
@@ -257,7 +258,7 @@ module Azure
257
258
  rescue Azure::Armrest::NotFoundException
258
259
  # Ignore, does not always exist.
259
260
  else
260
- log_message("Deleted blob #{disk.container}/#{status_file}") if options[:verbose]
261
+ log("Deleted blob #{disk.container}/#{status_file}") if options[:verbose]
261
262
  end
262
263
  end
263
264
  end
@@ -271,20 +272,15 @@ module Azure
271
272
  def delete_and_wait(service, name, group, options)
272
273
  resource_type = service.class.to_s.sub('Service', '').split('::').last
273
274
 
274
- log_message("Deleting #{resource_type} #{name}/#{group}") if options[:verbose]
275
-
276
- headers = service.delete(name, group)
275
+ log("Deleting #{resource_type} #{name}/#{group}") if options[:verbose]
277
276
 
278
- loop do
279
- status = wait(headers)
280
- break if status.downcase.start_with?('succ') # Succeeded, Success, etc.
281
- end
277
+ wait(service.delete(name, group), 0)
282
278
 
283
- log_message("Deleted #{resource_type} #{name}/#{group}") if options[:verbose]
279
+ log("Deleted #{resource_type} #{name}/#{group}") if options[:verbose]
284
280
  rescue Azure::Armrest::BadRequestException, Azure::Armrest::PreconditionFailedException => err
285
281
  if options[:verbose]
286
282
  msg = "Unable to delete #{resource_type} #{name}/#{group}, skipping. Message: #{err.message}"
287
- log_message(msg, 'warn')
283
+ log('warn', msg)
288
284
  end
289
285
  end
290
286
 
@@ -296,15 +292,6 @@ module Azure
296
292
  rest_post(url)
297
293
  nil
298
294
  end
299
-
300
- # Simple log messager. Use the Configuration.log if defined.
301
- def log_message(msg, level = 'info')
302
- if Azure::Armrest::Configuration.log
303
- Azure::Armrest::Configuration.log.send(level.to_sym, msg)
304
- else
305
- warn msg
306
- end
307
- end
308
295
  end
309
296
  end
310
297
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: azure-armrest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel J. Berger
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2016-12-08 00:00:00.000000000 Z
14
+ date: 2016-12-16 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: json