azure-armrest 0.0.6 → 0.0.7

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: 4759cde171ba44bc90f91439ddc0dcc7eedf318e
4
- data.tar.gz: a3fe9d9e1262b89008aadb48fc253aa0f492e979
3
+ metadata.gz: defac8d47729576388ef8ee13366182f5d2ba617
4
+ data.tar.gz: 226dd08875b1bcee85ce4ada88c0d31653a7c0d5
5
5
  SHA512:
6
- metadata.gz: 6f07e07d0567b32068495e0795ff78cf0d695ac4b157bcf835bfd75a6d5fba9f657c5f04d7170161a387e8bb82a05ecd6081c612b4905fa866a1c7798ee3b9c9
7
- data.tar.gz: 30d9530160d22508227a01a9902097ba25eab6ad42a9e7caf5bb7f2cd93a81c64fca7dfa866d7bf5348b9d2c0ea310228e992abba90e00708b1eaa70a66a038e
6
+ metadata.gz: a111aa4f81c3a43699a5f39f78cbbadbc84361d18a58d40e4e5f803ad1ab2b90daa22d261ce3f58241ad787cd5c01c2678b91ec70ef977c6078d93bc74359ad1
7
+ data.tar.gz: 90cc8169495c37470eee3bc75b93fb907b93aa15a4a8f2c67c6cae34af5543b353a576f337f207f8c5da040a2ca4c4bf16e37a5bf92bb6821030c3669ad6b6bd
data/CHANGES CHANGED
@@ -1,3 +1,9 @@
1
+ = 0.0.7 - 5-Nov-2015
2
+ * Refactored our BaseModel class so that it no longer uses Delegate or OpenStruct.
3
+ * Added more methods to the StorageAccount model, and modified methods so that
4
+ any options are always the last argument.
5
+ * Bug fix for the StorageAccount#all_blobs method.
6
+
1
7
  = 0.0.6 - 23-Oct-2015
2
8
  * Defined a custom == method for the BaseModel class.
3
9
  * The TemplateDeployment#properties.outputs now returns a hash.
@@ -1,22 +1,21 @@
1
- require 'delegate'
2
- require 'ostruct'
3
-
4
1
  module Azure
5
2
  module Armrest
6
3
  # Base class for JSON wrapper classes. Each Service class should have
7
4
  # a corresponding class that wraps the JSON it collects, and each of
8
5
  # them should subclass this base class.
9
- class BaseModel < Delegator
6
+ class BaseModel
7
+ # Initially inherit the exclusion list from parent class or create an empty Set.
10
8
  def self.excl_list
11
- # initially inherit the exclusion list from parent class or create an empty Set
12
9
  @excl_list ||= superclass.respond_to?(:excl_list, true) ? superclass.send(:excl_list) : Set.new
13
10
  end
11
+
14
12
  private_class_method :excl_list
15
13
 
14
+ # Merge the declared exclusive attributes to the existing list.
16
15
  def self.attr_hash(*attrs)
17
- # merge the declared exclusive attributes to the existing list
18
16
  @excl_list = excl_list | Set.new(attrs.map(&:to_s))
19
17
  end
18
+
20
19
  private_class_method :attr_hash
21
20
 
22
21
  attr_hash :tags
@@ -62,8 +61,7 @@ module Azure
62
61
  @json = json
63
62
  end
64
63
 
65
- @ostruct = OpenStruct.new(hash)
66
- super(@ostruct)
64
+ __setobj__(hash)
67
65
  end
68
66
 
69
67
  def resource_group
@@ -103,37 +101,54 @@ module Azure
103
101
  __getobj__.eql?(other.__getobj__)
104
102
  end
105
103
 
104
+ # Support hash style accessors
105
+ def [](key)
106
+ __getobj__[key]
107
+ end
108
+
109
+ def []=(key, val)
110
+ key_exists = __getobj__.include?(key)
111
+ __getobj__[key] = val
112
+
113
+ return if key_exists
114
+ add_accessor_methods(snake_case(key), key)
115
+ end
116
+
106
117
  protected
107
118
 
108
- # Interface method required to make delegation work. Do
109
- # not use this method directly.
119
+ # Do not use this method directly.
110
120
  def __getobj__
111
- @ostruct
121
+ @hashobj
112
122
  end
113
123
 
114
- # A custom Delegator interface method that creates snake_case
115
- # versions of the camelCase delegate methods.
124
+ # Create snake_case accessor methods for all hash attributes
125
+ # Use _alias if an accessor conflicts with existing methods
116
126
  def __setobj__(obj)
127
+ @hashobj = obj
117
128
  excl_list = self.class.send(:excl_list)
118
- obj.methods(false).each{ |m|
119
- if m.to_s[-1] != '=' && !excl_list.include?(m.to_s) # Must deal with nested models
120
- res = obj.send(m)
121
- if res.is_a?(Array)
122
- newval = res.map { |elem| elem.is_a?(Hash) ? @embedModel.new(elem) : elem }
123
- obj.send("#{m}=", newval)
124
- elsif res.is_a?(Hash)
125
- obj.send("#{m}=", @embedModel.new(res))
129
+ obj.each do |key, value|
130
+ snake = snake_case(key)
131
+ unless excl_list.include?(snake) # Must deal with nested models
132
+ if value.is_a?(Array)
133
+ newval = value.map { |elem| elem.is_a?(Hash) ? @embedModel.new(elem) : elem }
134
+ obj[key] = newval
135
+ elsif value.is_a?(Hash)
136
+ obj[key] = @embedModel.new(value)
126
137
  end
127
138
  end
128
139
 
129
- snake = m.to_s.gsub(/(.)([A-Z])/,'\1_\2').downcase.to_sym
140
+ add_accessor_methods(snake, key)
141
+ end
142
+ end
143
+
144
+ def add_accessor_methods(method, key)
145
+ method.prepend('_') if methods.include?(method.to_sym)
146
+ instance_eval { define_singleton_method(method) { __getobj__[key] } }
147
+ instance_eval { define_singleton_method("#{method}=") { |val| __getobj__[key] = val } }
148
+ end
130
149
 
131
- begin
132
- obj.instance_eval("alias #{snake} #{m}; undef :#{m}") unless snake == m
133
- rescue SyntaxError
134
- next
135
- end
136
- }
150
+ def snake_case(name)
151
+ name.to_s.gsub(/(.)([A-Z])/,'\1_\2').downcase
137
152
  end
138
153
  end
139
154
 
@@ -169,4 +184,4 @@ module Azure
169
184
  end
170
185
  end
171
186
 
172
- require_relative 'storage_account'
187
+ require_relative 'storage_account'
@@ -7,7 +7,9 @@ module Azure
7
7
  class StorageAccount < BaseModel
8
8
  # Classes used to wrap container and blob information.
9
9
  class Container < BaseModel; end
10
+ class ContainerProperty < BaseModel; end
10
11
  class Blob < BaseModel; end
12
+ class BlobProperty < BaseModel; end
11
13
  class BlobServiceProperty < BaseModel; end
12
14
  class BlobServiceStat < BaseModel; end
13
15
  class BlobMetadata < BaseModel; end
@@ -35,6 +37,50 @@ module Azure
35
37
  end
36
38
  end
37
39
 
40
+ # Returns the properties for the given container +name+ using account +key+.
41
+ # If no key is provided, it is assumed that the StorageAccount object
42
+ # includes the key1 property.
43
+ #
44
+ def container_properties(name, key = nil)
45
+ key ||= properties.key1
46
+
47
+ response = blob_response(key, "restype=container", name)
48
+
49
+ ContainerProperty.new(response.headers)
50
+ end
51
+
52
+ # Returns the properties for the given container +name+ using account +key+.
53
+ # If no key is provided, it is assumed that the StorageAccount object
54
+ # includes the key1 property.
55
+ #
56
+ # If the returned object does not contain x_ms_blob_public_access then
57
+ # the container is private to the account owner. You can also use the
58
+ # :private? method to determine if the account is public or private.
59
+ #
60
+ def container_acl(name, key = nil)
61
+ key ||= properties.key1
62
+
63
+ response = blob_response(key, "restype=container&comp=acl", name)
64
+ response.headers[:private?] = response.headers.include?(:x_ms_blob_public_access) ? false : true
65
+
66
+ ContainerProperty.new(response.headers)
67
+ end
68
+
69
+ # Return the blob properties for the given +blob+ found in +container+. You may
70
+ # optionally provide a date to get information for a snapshot.
71
+ #
72
+ def blob_properties(container, blob, key = nil, options = {})
73
+ key ||= properties.key1
74
+
75
+ url = File.join(properties.primary_endpoints.blob, container, blob)
76
+ url += "?snapshot=" + options[:date] if options[:date]
77
+
78
+ headers = build_headers(url, key, :verb => 'HEAD')
79
+ response = RestClient.head(url, headers)
80
+
81
+ BlobProperty.new(response.headers)
82
+ end
83
+
38
84
  # Return a list of blobs for the given +container+ using the given +key+
39
85
  # or the key1 property of the StorageAccount object.
40
86
  #
@@ -49,7 +95,9 @@ module Azure
49
95
  doc = Nokogiri::XML(response.body)
50
96
 
51
97
  doc.xpath('//Blobs/Blob').map do |node|
52
- Blob.new(Hash.from_xml(node.to_s)['Blob'])
98
+ blob = Blob.new(Hash.from_xml(node.to_s)['Blob'])
99
+ blob[:container] = container
100
+ blob
53
101
  end
54
102
  end
55
103
 
@@ -60,8 +108,8 @@ module Azure
60
108
  array = []
61
109
  threads = []
62
110
 
63
- containers.each do |container|
64
- threads << Thread.new(container, key){ |c,k| array << blobs(c.name, k) }
111
+ containers(key).each do |container|
112
+ threads << Thread.new(container, key) { |c, k| array << blobs(c.name, k) }
65
113
  end
66
114
 
67
115
  threads.each(&:join)
@@ -71,7 +119,7 @@ module Azure
71
119
 
72
120
  # Returns the blob service properties for the current storage account.
73
121
  #
74
- def blob_properties(key = nil)
122
+ def blob_service_properties(key = nil)
75
123
  key ||= properties.key1
76
124
 
77
125
  response = blob_response(key, "restype=service&comp=properties")
@@ -84,11 +132,11 @@ module Azure
84
132
  # Return metadata for the given +blob+ within +container+. You may
85
133
  # specify a +date+ to retrieve metadata for a specific snapshot.
86
134
  #
87
- def blob_metadata(container, blob, date = nil, key = nil)
135
+ def blob_metadata(container, blob, key = nil, options = {})
88
136
  key ||= properties.key1
89
137
 
90
138
  query = "comp=metadata"
91
- query << "&snapshot=#{date}" if date
139
+ query << "&snapshot=" + options[:date] if options[:date]
92
140
 
93
141
  response = blob_response(key, query, container, blob)
94
142
 
@@ -109,6 +157,48 @@ module Azure
109
157
  BlobServiceStat.new(Hash.from_xml(doc.to_s)[toplevel])
110
158
  end
111
159
 
160
+ # Copy the blob from the source container/blob to the destination container/blob.
161
+ # If no destination blob name is provided, it will use the same name as the source.
162
+ #
163
+ # Example:
164
+ #
165
+ # source = "Microsoft.Compute/Images/your_container/your-img-osDisk.123xyz.vhd"
166
+ # storage_acct.copy_blob('system', source, 'vhds', nil, your_key)
167
+ #
168
+ def copy_blob(src_container, src_blob, dst_container, dst_blob = nil, key = nil)
169
+ key ||= properties.key1
170
+ dst_blob ||= File.basename(src_blob)
171
+
172
+ dst_url = File.join(properties.primary_endpoints.blob, dst_container, dst_blob)
173
+ src_url = File.join(properties.primary_endpoints.blob, src_container, src_blob)
174
+
175
+ options = {'x-ms-copy-source' => src_url, 'If-None-Match' => '*', :verb => 'PUT'}
176
+
177
+ headers = build_headers(dst_url, key, options)
178
+
179
+ # RestClient will set the Content-Type to application/x-www-form-urlencoded.
180
+ # We must override this setting or the request will fail.
181
+ headers['Content-Type'] = ''
182
+
183
+ response = RestClient.put(dst_url, '', headers)
184
+
185
+ Blob.new(response.headers)
186
+ end
187
+
188
+ # Delete the given +blob+ found in +container+.
189
+ #
190
+ def delete_blob(container, blob, key = nil, options = {})
191
+ key ||= properties.key1
192
+
193
+ url = File.join(properties.primary_endpoints.blob, container, blob)
194
+ url += "?snapshot=" + options[:date] if options[:date]
195
+
196
+ headers = build_headers(url, key, :verb => 'DELETE')
197
+ response = RestClient.delete(url, headers)
198
+
199
+ true
200
+ end
201
+
112
202
  private
113
203
 
114
204
  # Using the blob primary endpoint as a base, join any arguments to the
@@ -122,7 +212,7 @@ module Azure
122
212
 
123
213
  # Set the headers needed, including the Authorization header.
124
214
  #
125
- def build_headers(url, key)
215
+ def build_headers(url, key, additional_headers = {})
126
216
  sig = Signature.new(url, key)
127
217
 
128
218
  headers = {
@@ -131,6 +221,7 @@ module Azure
131
221
  :auth_string => true
132
222
  }
133
223
 
224
+ headers.merge!(additional_headers)
134
225
  headers['Authorization'] = sig.blob_signature(headers)
135
226
 
136
227
  headers
@@ -13,16 +13,14 @@ module Azure
13
13
  end
14
14
 
15
15
  # List all the resources for the current subscription. You can optionally
16
- # pass :top or :filter options as well to restrict returned results.
17
- #
18
- # If you pass a :resource_group option, then only resources for that
19
- # resource group are returned.
16
+ # pass :top or :filter options as well to restrict returned results. The
17
+ # :filter option only applies to tags.
20
18
  #
21
19
  # Examples:
22
20
  #
23
21
  # rgs = ResourceGroupService.new
24
22
  # rgs.list(:top => 2)
25
- # rgs.list(:filter => "location eq 'centralus'")
23
+ # rgs.list(:filter => "sometag=value")
26
24
  #
27
25
  def list(options = {})
28
26
  url = build_url
@@ -40,23 +40,34 @@ module Azure
40
40
  # this method are cached.
41
41
  #
42
42
  def list
43
- url = build_url
44
- response = rest_get(url)
45
- JSON.parse(response)["value"].map{ |hash| Azure::Armrest::ResourceProvider.new(hash) }
43
+ _list.map{ |hash| Azure::Armrest::ResourceProvider.new(hash) }
44
+ end
45
+
46
+ def _list
47
+ response = rest_get(build_url)
48
+ JSON.parse(response)["value"]
46
49
  end
50
+ private :_list
47
51
 
48
- cache_method(:list, cache_time)
52
+ # Cannot directly cache list method, because Marshal used by chche_method
53
+ # cannot dump anonymous class, including the ones derived from Azure::Armrest::BaseModel
54
+ # Same applies to other cached methods in this class
55
+ cache_method(:_list, cache_time)
49
56
 
50
57
  # Return information about a specific +namespace+ provider. The results
51
58
  # of this method are cached.
52
59
  #
53
60
  def get(namespace)
61
+ Azure::Armrest::ResourceProvider.new(_get(namespace))
62
+ end
63
+
64
+ def _get(namespace)
54
65
  url = build_url(namespace)
55
- response = rest_get(url)
56
- Azure::Armrest::ResourceProvider.new(response)
66
+ rest_get(url).body
57
67
  end
68
+ private :_get
58
69
 
59
- cache_method(:get, cache_time)
70
+ cache_method(:_get, cache_time)
60
71
 
61
72
  # Returns an array of geo-locations for the given +namespace+ provider.
62
73
  # The results of this method are cached.
@@ -84,16 +95,16 @@ module Azure
84
95
  #
85
96
  def register(namespace)
86
97
  url = build_url(namespace, 'register')
87
- response = rest_post(url)
88
- response.return!
98
+ rest_post(url)
99
+ nil
89
100
  end
90
101
 
91
102
  # Unregister the current subscription from the +namespace+ provider.
92
103
  #
93
104
  def unregister(namespace)
94
105
  url = build_url(namespace, 'unregister')
95
- response = rest_post(url)
96
- response.return!
106
+ rest_post(url)
107
+ nil
97
108
  end
98
109
 
99
110
  private
@@ -1,5 +1,5 @@
1
1
  module Azure
2
2
  module Armrest
3
- VERSION = "0.0.6"
3
+ VERSION = "0.0.7"
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.0.6
4
+ version: 0.0.7
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: 2015-10-23 00:00:00.000000000 Z
14
+ date: 2015-11-06 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: json
@@ -232,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
232
232
  version: '0'
233
233
  requirements: []
234
234
  rubyforge_project:
235
- rubygems_version: 2.4.5.1
235
+ rubygems_version: 2.5.0
236
236
  signing_key:
237
237
  specification_version: 4
238
238
  summary: An interface for ARM/JSON Azure REST API