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.
- checksums.yaml +4 -4
- data/.travis.yml +3 -2
- data/CHANGES +16 -10
- data/README.md +4 -3
- data/azure-armrest.gemspec +1 -0
- data/lib/azure/armrest.rb +3 -1
- data/lib/azure/armrest/armrest_service.rb +111 -364
- data/lib/azure/armrest/billing/usage_service.rb +54 -0
- data/lib/azure/armrest/configuration.rb +258 -0
- data/lib/azure/armrest/exception.rb +4 -25
- data/lib/azure/armrest/model/base_model.rb +24 -4
- data/lib/azure/armrest/model/storage_account.rb +18 -9
- data/lib/azure/armrest/resource_group_based_service.rb +1 -1
- data/lib/azure/armrest/resource_group_service.rb +3 -3
- data/lib/azure/armrest/resource_service.rb +35 -26
- data/lib/azure/armrest/storage_account_service.rb +2 -24
- data/lib/azure/armrest/version.rb +1 -1
- data/lib/azure/armrest/virtual_machine_service.rb +6 -5
- metadata +18 -2
@@ -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
|
-
|
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}] #{
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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.
|
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.
|
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.
|
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
|
-
|
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.
|
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.
|
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.
|
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.
|
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.
|
519
|
+
ArmrestService.send(
|
520
|
+
:rest_get,
|
512
521
|
:url => url,
|
513
522
|
:headers => headers,
|
514
523
|
:proxy => proxy,
|