azure-armrest 0.3.5 → 0.3.6

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: 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