azure-armrest 0.3.5 → 0.3.6

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: 5f4ef3f0022820f1bff78cb13d0be5e2704e1b60
4
- data.tar.gz: 1be8e6dc7474255e856886a5f9ad280f69f64e6a
3
+ metadata.gz: d581e8606ff8fb662506a034bd0ba4dca0d5ce02
4
+ data.tar.gz: f5b4a1c3e4b474db88bab04a40a5b73e395ee2e2
5
5
  SHA512:
6
- metadata.gz: ecd8bde54c64287d360291fe01e832f76a4328bdcfb162ab07d66afe93ff46df27521eb37e9960f206e31a2a8675054aa4f29fc78e5e24d1c4527381b79a4195
7
- data.tar.gz: 51c49f5d53aedd4bf340034352a7abcf389bb703692bae4834bbb8324f1e130cadbacf847525cba6530a110eaa7acf406c533c1c99dfdb39e24e68db03a923a8
6
+ metadata.gz: b2a57e5c9b36b7c1bf9d28525cd4d3b3be4772c89b1b9d1342d6773b434846634b458fa8efefbc6f3549f6b6aac469dea7489c9ca44f0b80d45d4f5263cd5863
7
+ data.tar.gz: 411f85036c00fea64f13ff0c494e2d27172f6fcf67ad029e98b4a3f705f61966179485034c982de7598a6f642c24edd2e4429fbd13f0457ec80ac4d70f556f70
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ /futz/
data/CHANGES CHANGED
@@ -1,3 +1,14 @@
1
+ = 0.3.6 - 13-Sep-2016
2
+ * Added the poll and wait methods to the ArmrestService base class. These
3
+ are meant for use with asynchronous operations, e.g. create and delete.
4
+ * All methods that returned a plain array now return an ArmrestCollection
5
+ object instead. This is a subclass of Array, but includes header and
6
+ skip token information.
7
+ * Added the ArmrestCollection.create_from_response method. This creates
8
+ and returns a collection based on a JSON response and a model type.
9
+ * Minor update to the ApiException#to_s method so that includes a bit
10
+ more information.
11
+
1
12
  = 0.3.5 - 11-Aug-2016
2
13
  * When we added subscription ID validation in 0.3.2 we forgot to set proxy
3
14
  information first. That has been fixed.
@@ -4,6 +4,34 @@ module Azure
4
4
  module Armrest
5
5
  class ArmrestCollection < Array
6
6
  attr_accessor :continuation_token
7
+ attr_accessor :response_headers
8
+
9
+ alias skip_token continuation_token
10
+ alias skip_token= continuation_token=
11
+
12
+ class << self
13
+ # Creates and returns a ArmrestCollection object based on JSON input,
14
+ # using +klass+ to generate the list elements. In addition, both the
15
+ # response headers and continuation token are set.
16
+ #
17
+ def create_from_response(response, klass = nil)
18
+ json_response = JSON.parse(response)
19
+ array = new(json_response['value'].map { |hash| klass.new(hash) })
20
+
21
+ array.response_headers = response.headers
22
+ array.continuation_token = parse_skip_token(json_response)
23
+
24
+ array
25
+ end
26
+
27
+ private
28
+
29
+ # Parse the skip token value out of the nextLink attribute from a response.
30
+ def parse_skip_token(json)
31
+ return nil unless json['nextLink']
32
+ json['nextLink'][/.*?skipToken=(.*?)$/i, 1]
33
+ end
34
+ end
7
35
  end
8
36
  end
9
37
  end
@@ -157,6 +157,44 @@ module Azure
157
157
  JSON.parse(resp.body)['value'].map{ |hash| Azure::Armrest::Tenant.new(hash) }
158
158
  end
159
159
 
160
+ # Poll a resource and return its current operations status. The
161
+ # +response+ argument should be a ResponseHeaders object that
162
+ # contains the :azure_asyncoperation header. It may optionally
163
+ # be an object that returns a URL from a .to_s method.
164
+ #
165
+ # This is meant to check the status of asynchronous operations,
166
+ # such as create or delete.
167
+ #
168
+ def poll(response)
169
+ url = response.respond_to?(:azure_asyncoperation) ? response.azure_asyncoperation : response.to_s
170
+ JSON.parse(rest_get(url))['status']
171
+ end
172
+
173
+ # Wait for the given +response+ to return a status of 'Succeeded', up
174
+ # to a maximum of +max_time+ seconds, and return the operations status.
175
+ # The first argument must be a ResponseHeaders object that contains
176
+ # the azure_asyncoperation header.
177
+ #
178
+ # Internally this will poll the response header every :retry_after
179
+ # seconds (or 10 seconds if that header isn't found), up to a maximum of
180
+ # 60 seconds by default.
181
+ #
182
+ # For most resources the +max_time+ argument should be more than sufficient.
183
+ # Certain resources, such as virtual machines, could take longer.
184
+ #
185
+ def wait(response, max_time = 60)
186
+ sleep_time = response.respond_to?(:retry_after) ? response.retry_after.to_i : 10
187
+ total_time = 0
188
+
189
+ while (status = poll(response)).casecmp('Succeeded') != 0
190
+ total_time += sleep_time
191
+ break if total_time >= max_time
192
+ sleep sleep_time
193
+ end
194
+
195
+ status
196
+ end
197
+
160
198
  class << self
161
199
  private
162
200
 
@@ -4,15 +4,30 @@ 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
+ #
7
13
  def initialize(message = nil, cause_exception = nil)
8
14
  @message = message
9
15
  @cause = cause_exception
10
16
  end
11
17
 
18
+ # The stringified version (message) of the exception.
19
+ #
12
20
  def to_s
13
- message
21
+ if cause
22
+ "#{message} (cause: #{cause})"
23
+ else
24
+ message
25
+ end
14
26
  end
15
27
 
28
+ # The error message or, if the message is not set, the name of the
29
+ # exception class.
30
+ #
16
31
  def message
17
32
  @message || self.class.name
18
33
  end
@@ -21,16 +36,24 @@ module Azure
21
36
  class ApiException < Exception
22
37
  attr_accessor :code
23
38
 
39
+ # Create a new ApiException class. The +code+ is the error code.
40
+ #
41
+ # This class serves as the parent
24
42
  def initialize(code, message, cause_exception)
25
43
  @code = code
26
44
  super(message, cause_exception)
27
45
  end
28
46
 
47
+ # A stringified version of the error. If self is a plain ApiException,
48
+ # then the cause is included to aid in debugging.
49
+ #
29
50
  def to_s
30
- "[#{code}] #{message}"
51
+ "[#{code}] #{super}"
31
52
  end
32
53
  end
33
54
 
55
+ # A list of predefined exceptions that we wrap around RestClient exceptions.
56
+
34
57
  class ResourceNotFoundException < ApiException; end
35
58
 
36
59
  class BadRequestException < ApiException; end
@@ -42,6 +65,5 @@ module Azure
42
65
  class GatewayTimeoutException < ApiException; end
43
66
 
44
67
  class TooManyRequestsException < ApiException; end
45
-
46
68
  end
47
69
  end
@@ -44,22 +44,16 @@ module Azure
44
44
  # filter = "eventTimestamp ge #{date} and eventChannels eq 'Admin, Operation'"
45
45
  # select = "resourceGroupName, operationName"
46
46
  #
47
- # ies.list(:filter => filter, :select => select, :all => true).events.each{ |event|
47
+ # ies.list(:filter => filter, :select => select, :all => true).each{ |event|
48
48
  # p event
49
49
  # }
50
50
  #
51
51
  def list(options = {})
52
52
  url = build_url(options)
53
53
  response = rest_get(url)
54
- json_response = JSON.parse(response.body)
55
54
 
56
- events = ArmrestCollection.new(
57
- json_response['value'].map do |hash|
58
- Azure::Armrest::Insights::Event.new(hash)
59
- end
60
- )
61
-
62
- events.continuation_token = parse_skip_token(json_response)
55
+ klass = Azure::Armrest::Insights::Event
56
+ events = Azure::Armrest::ArmrestCollection.create_from_response(response, klass)
63
57
 
64
58
  if options[:all] && events.continuation_token
65
59
  events.push(*list(options.merge(:skip_token => events.continuation_token)))
@@ -22,6 +22,8 @@ module Azure
22
22
 
23
23
  attr_hash :tags
24
24
 
25
+ attr_accessor :response_headers
26
+
25
27
  # Constructs and returns a new JSON wrapper class. Pass in a plain
26
28
  # JSON string and it will automatically give you accessor methods
27
29
  # that make it behave like a typical Ruby object. You may also pass
@@ -21,7 +21,7 @@ module Azure
21
21
  class TableData < BaseModel; end
22
22
 
23
23
  # The version string used in headers sent as part any internal http
24
- # request. The default is 2015-02-21.
24
+ # request. The default is 2015-12-11.
25
25
  attr_accessor :storage_api_version
26
26
 
27
27
  # An http proxy to use per request. Defaults to ENV['http_proxy'] if set.
@@ -35,7 +35,7 @@ module Azure
35
35
 
36
36
  def initialize(json)
37
37
  super
38
- @storage_api_version = '2015-02-21'
38
+ @storage_api_version = '2015-12-11'
39
39
  @proxy = ENV['http_proxy']
40
40
  @ssl_version = 'TLSv1'
41
41
  @ssl_verify = nil
@@ -99,11 +99,12 @@ module Azure
99
99
  key ||= properties.key1
100
100
 
101
101
  query = build_query(options)
102
-
103
102
  response = table_response(key, query, name)
104
- json_response = JSON.parse(response.body)
105
103
 
106
- data = ArmrestCollection.new(json_response['value'].map { |t| TableData.new(t) })
104
+ klass = Azure::Armrest::StorageAccount::TableData
105
+ data = Azure::Armrest::ArmrestCollection.create_from_response(response, klass)
106
+
107
+ # Continuation tokens are parsed differently for storage
107
108
  data.continuation_token = parse_continuation_tokens(response)
108
109
 
109
110
  if options[:all] && data.continuation_token
@@ -2,6 +2,17 @@ module Azure
2
2
  module Armrest
3
3
  # Base class for services that need to run in a resource group
4
4
  class ResourceGroupBasedService < ArmrestService
5
+ # Create a resource +name+ within the resource group +rgroup+, or the
6
+ # resource group that was specified in the configuration, along with
7
+ # a hash of appropriate +options+.
8
+ #
9
+ # Returns an instance of the object that was created if possible,
10
+ # otherwise nil is returned.
11
+ #
12
+ # Note that this is an asynchronous operation. You can check the current
13
+ # status of the resource by inspecting the :response_headers instance and
14
+ # polling either the :azure_asyncoperation or :location URL.
15
+ #
5
16
  def create(name, rgroup = configuration.resource_group, options = {})
6
17
  validate_resource_group(rgroup)
7
18
  validate_resource(name)
@@ -9,18 +20,33 @@ module Azure
9
20
  url = build_url(rgroup, name)
10
21
  url = yield(url) || url if block_given?
11
22
  response = rest_put(url, options.to_json)
12
- model_class.new(response) unless response.empty?
23
+
24
+ obj = nil
25
+
26
+ unless response.empty?
27
+ obj = model_class.new(response.body)
28
+ obj.response_headers = Azure::Armrest::ResponseHeaders.new(response.headers)
29
+ end
30
+
31
+ obj
13
32
  end
14
33
 
15
34
  alias update create
16
35
 
36
+ # List all resources within the resource group +rgroup+, or the
37
+ # resource group that was specified in the configuration.
38
+ #
39
+ # Returns an ArmrestCollection, with the response headers set
40
+ # for the operation as a whole.
41
+ #
17
42
  def list(rgroup = configuration.resource_group)
18
43
  validate_resource_group(rgroup)
19
44
 
20
45
  url = build_url(rgroup)
21
46
  url = yield(url) || url if block_given?
22
47
  response = rest_get(url)
23
- JSON.parse(response)['value'].map { |hash| model_class.new(hash) }
48
+
49
+ Azure::Armrest::ArmrestCollection.create_from_response(response, model_class)
24
50
  end
25
51
 
26
52
  # Use a single call to get all resources for the service. You may
@@ -35,11 +61,16 @@ module Azure
35
61
  def list_all(filter = {})
36
62
  url = build_url
37
63
  url = yield(url) || url if block_given?
64
+
38
65
  response = rest_get(url)
39
- results = JSON.parse(response)['value'].map { |hash| model_class.new(hash) }
66
+ results = Azure::Armrest::ArmrestCollection.create_from_response(response, model_class)
67
+
40
68
  filter.empty? ? results : results.select { |obj| filter.all? { |k, v| obj.public_send(k) == v } }
41
69
  end
42
70
 
71
+ # Get information about a single resource +name+ within resource group
72
+ # +rgroup+, or the resource group that was set in the configuration.
73
+ #
43
74
  def get(name, rgroup = configuration.resource_group)
44
75
  validate_resource_group(rgroup)
45
76
  validate_resource(name)
@@ -47,7 +78,11 @@ module Azure
47
78
  url = build_url(rgroup, name)
48
79
  url = yield(url) || url if block_given?
49
80
  response = rest_get(url)
50
- model_class.new(response)
81
+
82
+ obj = model_class.new(response.body)
83
+ obj.response_headers = Azure::Armrest::ResponseHeaders.new(response.headers)
84
+
85
+ obj
51
86
  end
52
87
 
53
88
  # Delete the resource with the given +name+ for the provided +resource_group+,
@@ -101,19 +136,27 @@ module Azure
101
136
 
102
137
  # Aggregate resources from all resource groups.
103
138
  #
104
- # To be used in the cases where the API does not support list_all with one call.
139
+ # To be used in the cases where the API does not support list_all with
140
+ # one call. Note that this does not set the skip token because we're
141
+ # actually collating the results of multiple calls internally.
105
142
  #
106
143
  def list_in_all_groups
107
- array = []
108
- mutex = Mutex.new
144
+ array = []
145
+ mutex = Mutex.new
146
+ headers = nil
109
147
 
110
148
  Parallel.each(list_resource_groups, :in_threads => configuration.max_threads) do |rg|
111
149
  response = rest_get(build_url(rg.name))
112
- results = JSON.parse(response)['value'].map { |hash| model_class.new(hash) }
150
+ json_response = JSON.parse(response.body)['value']
151
+ headers = Azure::Armrest::ResponseHeaders.new(response.headers)
152
+ results = json_response.map { |hash| model_class.new(hash) }
113
153
  mutex.synchronize { array << results } unless results.blank?
114
154
  end
115
155
 
116
- array.flatten
156
+ array = ArmrestCollection.new(array.flatten)
157
+ array.response_headers = headers # Use the last set of headers for the overall result
158
+
159
+ array
117
160
  end
118
161
  end
119
162
  end
@@ -26,8 +26,7 @@ module Azure
26
26
  url << "&$filter=#{options[:filter]}" if options[:filter]
27
27
 
28
28
  response = rest_get(url)
29
-
30
- JSON.parse(response)['value'].map { |hash| Azure::Armrest::ResourceGroup.new(hash) }
29
+ Azure::Armrest::ArmrestCollection.create_from_response(response, Azure::Armrest::ResourceGroup)
31
30
  end
32
31
 
33
32
  # Creates a new +group+ in +location+ for the current subscription.
@@ -23,7 +23,7 @@ module Azure
23
23
  def list(resource_group, options = {})
24
24
  url = build_url(resource_group, options)
25
25
  response = rest_get(url)
26
- JSON.parse(response)['value'].map { |hash| Azure::Armrest::Resource.new(hash) }
26
+ Azure::Armrest::ArmrestCollection.create_from_response(response, Azure::Armrest::Resource)
27
27
  end
28
28
 
29
29
  # Same as Azure::Armrest::ResourceService#list but returns all resources
@@ -32,7 +32,7 @@ module Azure
32
32
  def list_all(options = {})
33
33
  url = build_url(nil, options)
34
34
  response = rest_get(url)
35
- JSON.parse(response)['value'].map { |hash| Azure::Armrest::Resource.new(hash) }
35
+ Azure::Armrest::ArmrestCollection.create_from_response(response, Azure::Armrest::Resource)
36
36
  end
37
37
 
38
38
  # Move the resources from +source_group+ under +source_subscription+,
@@ -1,5 +1,5 @@
1
1
  module Azure
2
2
  module Armrest
3
- VERSION = '0.3.5'.freeze
3
+ VERSION = '0.3.6'.freeze
4
4
  end
5
5
  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.3.5
4
+ version: 0.3.6
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-08-11 00:00:00.000000000 Z
14
+ date: 2016-09-13 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: json