azure-armrest 0.0.6 → 0.0.7

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