azure-armrest 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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