azure-armrest 0.2.10 → 0.3.0

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.
@@ -0,0 +1,54 @@
1
+ module Azure
2
+ module Armrest
3
+ module Billing
4
+ class UsageService < ArmrestService
5
+ # Creates and returns a new UsageService object.
6
+ #
7
+ def initialize(configuration, options = {})
8
+ options = options.merge('api_version' => '2015-06-01-preview')
9
+ super(configuration, 'subscriptions', 'Microsoft.Commerce', options)
10
+ end
11
+
12
+ # List usage details. The +options+ hash may include the following
13
+ # filters:
14
+ #
15
+ # :reportedStartTime # e.g. 2016-05-30T00:00:00Z. Mandatory.
16
+ # :reportedEndTime # e.g. 2016-06-01T00:00:00Z. Mandatory.
17
+ # :aggregationGranularity # Either 'Daily' or 'Hourly'. Default is Daily.
18
+ # :showDetails # Either true or false. Default is true.
19
+ # :continuationToken # Token received from previous call. No default.
20
+ #
21
+ # The :reportedStartTime and :reportedEndTime values should be in
22
+ # UTC + iso8601 format. For "Daily" aggregation, the time should be set
23
+ # to midnight. For "Hourly" aggregation, only the hour should be
24
+ # set, with minutes and seconds set to "00".
25
+ #
26
+ def list(options = {})
27
+ url = build_url(options)
28
+ response = rest_get(url)
29
+ JSON.parse(response)['value'].map { |hash| Azure::Armrest::Usage.new(hash) }
30
+ end
31
+
32
+ private
33
+
34
+ def build_url(options = {})
35
+ url = File.join(
36
+ Azure::Armrest::COMMON_URI,
37
+ configuration.subscription_id,
38
+ 'providers',
39
+ @provider,
40
+ 'UsageAggregates'
41
+ )
42
+
43
+ url << "?api-version=#{@api_version}"
44
+
45
+ options.each do |key, value|
46
+ url << "&#{key}=#{value}"
47
+ end
48
+
49
+ url
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,258 @@
1
+ module Azure
2
+ module Armrest
3
+ class Configuration
4
+ # Clear all class level caches. Typically used for testing only.
5
+ def self.clear_caches
6
+ # Used to store unique token information.
7
+ @token_cache = Hash.new { |h, k| h[k] = [] }
8
+ end
9
+
10
+ clear_caches # Clear caches at load time.
11
+
12
+ # Retrieve the cached token for a configuration.
13
+ # Return both the token and its expiration date, or nil if not cached
14
+ def self.retrieve_token(configuration)
15
+ @token_cache[configuration.hash]
16
+ end
17
+
18
+ # Cache the token for a configuration that a token has been fetched from Azure
19
+ def self.cache_token(configuration)
20
+ raise ArgumentError, "Configuration does not have a token" if configuration.token.nil?
21
+ @token_cache[configuration.hash] = [configuration.token, configuration.token_expiration]
22
+ end
23
+
24
+ # The api-version string
25
+ attr_accessor :api_version
26
+
27
+ # The client ID used to gather token information.
28
+ attr_accessor :client_id
29
+
30
+ # The client key used to gather token information.
31
+ attr_accessor :client_key
32
+
33
+ # The tenant ID used to gather token information.
34
+ attr_accessor :tenant_id
35
+
36
+ # The subscription ID used for each http request.
37
+ attr_accessor :subscription_id
38
+
39
+ # The resource group used for http requests.
40
+ attr_accessor :resource_group
41
+
42
+ # The grant type. The default is client_credentials.
43
+ attr_accessor :grant_type
44
+
45
+ # The content type specified for http requests. The default is 'application/json'
46
+ attr_accessor :content_type
47
+
48
+ # The accept type specified for http request results. The default is 'application/json'
49
+ attr_accessor :accept
50
+
51
+ # Proxy to be used for all http requests.
52
+ attr_accessor :proxy
53
+
54
+ # SSL version to be used for all http requests.
55
+ attr_accessor :ssl_version
56
+
57
+ # SSL verify mode for all http requests.
58
+ attr_accessor :ssl_verify
59
+
60
+ # Namespace providers, their resource types, locations and supported api-version strings.
61
+ attr_reader :providers
62
+
63
+ # Yields a new Azure::Armrest::Configuration objects. Note that you must
64
+ # specify a client_id, client_key, tenant_id and subscription_id. All other
65
+ # parameters are optional.
66
+ #
67
+ # Example:
68
+ #
69
+ # config = Azure::Armrest::Configuration.new(
70
+ # :client_id => 'xxxx',
71
+ # :client_key => 'yyyy',
72
+ # :tenant_id => 'zzzz',
73
+ # :subscription_id => 'abcd'
74
+ # )
75
+ #
76
+ # If you specify a :resource_group, that group will be used for resource
77
+ # group based service class requests. Otherwise, you will need to specify
78
+ # a resource group for most service methods.
79
+ #
80
+ # Although you can specify an :api_version, it is typically overridden
81
+ # by individual service classes.
82
+ #
83
+ def initialize(args)
84
+ # Use defaults, and override with provided arguments
85
+ options = {
86
+ :api_version => '2015-01-01',
87
+ :accept => 'application/json',
88
+ :content_type => 'application/json',
89
+ :grant_type => 'client_credentials',
90
+ :proxy => ENV['http_proxy'],
91
+ :ssl_version => 'TLSv1',
92
+ }.merge(args.symbolize_keys)
93
+
94
+ user_token = options.delete(:token)
95
+ user_token_expiration = options.delete(:token_expiration)
96
+
97
+ options.each { |key, value| send("#{key}=", value) }
98
+
99
+ unless client_id && client_key && tenant_id && subscription_id
100
+ raise ArgumentError, "client_id, client_key, tenant_id and subscription_id must all be specified"
101
+ end
102
+
103
+ if user_token && user_token_expiration
104
+ set_token(user_token, user_token_expiration)
105
+ elsif user_token || user_token_expiration
106
+ raise ArgumentError, "token and token_expiration must be both specified"
107
+ end
108
+
109
+ # Allows for URI objects or Strings.
110
+ @proxy = @proxy.to_s if @proxy
111
+
112
+ @providers = fetch_providers
113
+ set_provider_api_versions
114
+ end
115
+
116
+ def hash
117
+ [tenant_id, client_id, client_key].join('_').hash
118
+ end
119
+
120
+ def eql?(other)
121
+ return true if equal?(other)
122
+ return false unless self.class == other.class
123
+ tenant_id == other.tenant_id && client_id == other.client_id && client_key == other.client_key
124
+ end
125
+
126
+ # Returns the token for the current cache key, or sets it if it does not
127
+ # exist or it has expired.
128
+ #
129
+ def token
130
+ ensure_token
131
+ @token
132
+ end
133
+
134
+ # Set the token value and expiration time.
135
+ #
136
+ def set_token(token, token_expiration)
137
+ validate_token_time(token_expiration)
138
+
139
+ @token, @token_expiration = token, token_expiration.utc
140
+ self.class.cache_token(self)
141
+ end
142
+
143
+ # Returns the expiration datetime of the current token
144
+ #
145
+ def token_expiration
146
+ ensure_token
147
+ @token_expiration
148
+ end
149
+
150
+ # Return the default api version for the given provider and service
151
+ def provider_default_api_version(provider, service)
152
+ @provider_api_versions[provider.downcase][service.downcase]
153
+ end
154
+
155
+ # The name of the file or handle used to log http requests.
156
+ #--
157
+ # We have to do a little extra work here to convert a possible
158
+ # file handle to a file name.
159
+ #
160
+ def self.log
161
+ file = RestClient.log.instance_variable_get("@target_file")
162
+ file || RestClient.log
163
+ end
164
+
165
+ # Sets the log to +output+, which can be a file or a handle.
166
+ #
167
+ def self.log=(output)
168
+ RestClient.log = output
169
+ end
170
+
171
+ private
172
+
173
+ def ensure_token
174
+ @token, @token_expiration = self.class.retrieve_token(self) if @token.nil?
175
+ fetch_token if @token.nil? || Time.now.utc > @token_expiration
176
+ end
177
+
178
+ # Don't allow tokens from the past to be set.
179
+ #
180
+ def validate_token_time(time)
181
+ if time.utc < Time.now.utc
182
+ raise ArgumentError, 'token_expiration date invalid'
183
+ end
184
+ end
185
+
186
+ # Build a one-time lookup hash that sets the appropriate api-version
187
+ # string for a given provider and resource type. If possible, select
188
+ # a non-preview version that is not set in the future. Otherwise, just
189
+ # just the most recent one.
190
+ #
191
+ def set_provider_api_versions
192
+ # A lookup table for getting api-version strings per provider and service.
193
+ @provider_api_versions = Hash.new { |hash, key| hash[key] = {} }
194
+
195
+ providers.each do |rp|
196
+ rp.resource_types.each do |rt|
197
+ if rt.api_versions.any? { |v| v !~ /preview/i && Time.parse(v).utc <= Time.now.utc }
198
+ api_version = rt.api_versions.reject do |version|
199
+ version =~ /preview/i || Time.parse(version).utc > Time.now.utc
200
+ end.first
201
+ else
202
+ api_version = rt.api_versions.first
203
+ end
204
+
205
+ namespace = rp['namespace'].downcase # Avoid name collision
206
+ resource_type = rt.resource_type.downcase
207
+
208
+ @provider_api_versions[namespace][resource_type] = api_version
209
+ end
210
+ end
211
+ end
212
+
213
+ def fetch_providers
214
+ uri = Addressable::URI.join(Azure::Armrest::RESOURCE, 'providers')
215
+ uri.query = "api-version=#{api_version}"
216
+
217
+ response = ArmrestService.send(
218
+ :rest_get,
219
+ :url => uri.to_s,
220
+ :proxy => proxy,
221
+ :ssl_version => ssl_version,
222
+ :ssl_verify => ssl_verify,
223
+ :headers => {
224
+ :content_type => content_type,
225
+ :authorization => token
226
+ }
227
+ )
228
+
229
+ JSON.parse(response.body)['value'].map { |hash| Azure::Armrest::ResourceProvider.new(hash) }
230
+ end
231
+
232
+ def fetch_token
233
+ token_url = File.join(Azure::Armrest::AUTHORITY, tenant_id, 'oauth2/token')
234
+
235
+ response = JSON.parse(
236
+ ArmrestService.send(
237
+ :rest_post,
238
+ :url => token_url,
239
+ :proxy => proxy,
240
+ :ssl_version => ssl_version,
241
+ :ssl_verify => ssl_verify,
242
+ :payload => {
243
+ :grant_type => grant_type,
244
+ :client_id => client_id,
245
+ :client_secret => client_key,
246
+ :resource => Azure::Armrest::RESOURCE
247
+ }
248
+ )
249
+ )
250
+
251
+ @token = 'Bearer ' + response['access_token']
252
+ @token_expiration = Time.now.utc + response['expires_in'].to_i
253
+
254
+ self.class.cache_token(self)
255
+ end
256
+ end
257
+ end
258
+ end
@@ -4,30 +4,15 @@ module Azure
4
4
  attr_accessor :cause
5
5
  attr_writer :message
6
6
 
7
- # Create a new Armrest::Exception object. The +message+ should be an
8
- # error string, while +cause_exception+ is typically set to the
9
- # raw RestClient exception.
10
- #
11
- # You will not typically use this object directly.
12
- #
13
7
  def initialize(message = nil, cause_exception = nil)
14
8
  @message = message
15
9
  @cause = cause_exception
16
10
  end
17
11
 
18
- # The stringified version (message) of the exception.
19
- #
20
12
  def to_s
21
- if cause
22
- "#{message} (cause: #{cause})"
23
- else
24
- message
25
- end
13
+ message
26
14
  end
27
15
 
28
- # The error message or, if the message is not set, the name of the
29
- # exception class.
30
- #
31
16
  def message
32
17
  @message || self.class.name
33
18
  end
@@ -36,24 +21,16 @@ module Azure
36
21
  class ApiException < Exception
37
22
  attr_accessor :code
38
23
 
39
- # Create a new ApiException class. The +code+ is the error code.
40
- #
41
- # This class serves as the parent
42
24
  def initialize(code, message, cause_exception)
43
25
  @code = code
44
26
  super(message, cause_exception)
45
27
  end
46
28
 
47
- # A stringified version of the error. If self is a plain ApiException,
48
- # then the cause is included to aid in debugging.
49
- #
50
29
  def to_s
51
- "[#{code}] #{super}"
30
+ "[#{code}] #{message}"
52
31
  end
53
32
  end
54
33
 
55
- # A list of predefined exceptions that we wrap around RestClient exceptions.
56
-
57
34
  class ResourceNotFoundException < ApiException; end
58
35
 
59
36
  class BadRequestException < ApiException; end
@@ -64,5 +41,7 @@ module Azure
64
41
 
65
42
  class GatewayTimeoutException < ApiException; end
66
43
 
44
+ class TooManyRequestsException < ApiException; end
45
+
67
46
  end
68
47
  end
@@ -89,11 +89,30 @@ module Azure
89
89
  @json
90
90
  end
91
91
 
92
+ def inspect_method_list
93
+ methods(false).reject { |m| m.to_s.end_with?('=') }
94
+ end
95
+ private :inspect_method_list
96
+
92
97
  def inspect
93
- string = "<#{self.class} "
94
- method_list = methods(false).select { |m| !m.to_s.include?('=') }
95
- string << method_list.map { |m| "#{m}=#{send(m).inspect}" }.join(', ')
96
- string << '>'
98
+ Kernel.instance_method(:to_s).bind(self).call.chomp!('>') <<
99
+ ' ' <<
100
+ inspect_method_list.map { |m| "#{m}=#{send(m).inspect}" }.join(', ') <<
101
+ '>'
102
+ end
103
+
104
+ def pretty_print(q)
105
+ q.object_address_group(self) {
106
+ q.seplist(inspect_method_list, lambda { q.text ',' }) {|v|
107
+ q.breakable
108
+ q.text v.to_s
109
+ q.text '='
110
+ q.group(1) {
111
+ q.breakable ''
112
+ q.pp(send(v))
113
+ }
114
+ }
115
+ }
97
116
  end
98
117
 
99
118
  def ==(other)
@@ -173,6 +192,7 @@ module Azure
173
192
  class ResourceGroup < BaseModel; end
174
193
  class ResourceProvider < BaseModel; end
175
194
  class Sku < BaseModel; end
195
+ class Usage < BaseModel; end
176
196
 
177
197
  class StorageAccount < BaseModel; end
178
198
  class StorageAccountKey < StorageAccount
@@ -169,7 +169,8 @@ module Azure
169
169
 
170
170
  headers = build_headers(url, key, :blob, :verb => 'HEAD')
171
171
 
172
- response = ArmrestService.rest_head(
172
+ response = ArmrestService.send(
173
+ :rest_head,
173
174
  :url => url,
174
175
  :headers => headers,
175
176
  :proxy => proxy,
@@ -192,7 +193,8 @@ module Azure
192
193
 
193
194
  headers = build_headers(url, key)
194
195
 
195
- response = ArmrestService.rest_get(
196
+ response = ArmrestService.send(
197
+ :rest_get,
196
198
  :url => url,
197
199
  :headers => headers,
198
200
  :proxy => proxy,
@@ -283,7 +285,8 @@ module Azure
283
285
 
284
286
  headers = build_headers(dst_url, key, :blob, options)
285
287
 
286
- response = ArmrestService.rest_put(
288
+ response = ArmrestService.send(
289
+ :rest_put,
287
290
  :url => dst_url,
288
291
  :payload => '',
289
292
  :headers => headers,
@@ -305,7 +308,8 @@ module Azure
305
308
 
306
309
  headers = build_headers(url, key, :blob, :verb => 'DELETE')
307
310
 
308
- response = ArmrestService.rest_delete(
311
+ ArmrestService.send(
312
+ :rest_delete,
309
313
  :url => url,
310
314
  :headers => headers,
311
315
  :proxy => proxy,
@@ -335,7 +339,8 @@ module Azure
335
339
  options = options.merge(data)
336
340
  headers = build_headers(url, key, :blob, options)
337
341
 
338
- response = ArmrestService.rest_put(
342
+ response = ArmrestService.send(
343
+ :rest_put,
339
344
  :url => url,
340
345
  :payload => '',
341
346
  :headers => headers,
@@ -355,7 +360,8 @@ module Azure
355
360
 
356
361
  headers = build_headers(url, key, :blob, :verb => 'PUT')
357
362
 
358
- response = ArmrestService.rest_put(
363
+ response = ArmrestService.send(
364
+ :rest_put,
359
365
  :url => url,
360
366
  :payload => '',
361
367
  :headers => headers,
@@ -432,7 +438,8 @@ module Azure
432
438
 
433
439
  headers = build_headers(url, key, :blob, additional_headers)
434
440
 
435
- ArmrestService.rest_get(
441
+ ArmrestService.send(
442
+ :rest_get,
436
443
  :url => url,
437
444
  :headers => headers,
438
445
  :proxy => proxy,
@@ -486,7 +493,8 @@ module Azure
486
493
  url = File.join(properties.primary_endpoints.blob, *args) + "?#{query}"
487
494
  headers = build_headers(url, key, 'blob')
488
495
 
489
- ArmrestService.rest_get(
496
+ ArmrestService.send(
497
+ :rest_get,
490
498
  :url => url,
491
499
  :headers => headers,
492
500
  :proxy => proxy,
@@ -508,7 +516,8 @@ module Azure
508
516
  url << "?#{query}"
509
517
  end
510
518
 
511
- ArmrestService.rest_get(
519
+ ArmrestService.send(
520
+ :rest_get,
512
521
  :url => url,
513
522
  :headers => headers,
514
523
  :proxy => proxy,