azure_client 0.1.1 → 0.1.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.
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- azure_client (0.0.9)
4
+ azure_client (0.1.6)
5
5
  rest-client
6
6
 
7
7
  GEM
@@ -1,6 +1,7 @@
1
1
  # standard
2
2
  require 'json'
3
3
  require 'base64'
4
+ require "zlib"
4
5
 
5
6
  # azure
6
7
  require 'azure'
@@ -10,6 +11,7 @@ require 'azure_client/buffered_queue'
10
11
  require 'azure_client/buffered_queue_factory'
11
12
  require 'azure_client/buffered_queue_message'
12
13
  require 'azure_client/client'
14
+ require 'azure_client/compression'
13
15
  require 'azure_client/container'
14
16
  require 'azure_client/exponential_retry_policy'
15
17
  require 'azure_client/linear_retry_policy'
@@ -11,6 +11,10 @@ module AzureClient
11
11
  end
12
12
 
13
13
  def get_content
14
+ content_encoding = @blob_service.get_blob_properties(@container_name, @name).properties[:content_encoding]
15
+ if (content_encoding && content_encoding.downcase == "gzip")
16
+ return Compression.decompress(@content)
17
+ end
14
18
  @content
15
19
  end
16
20
 
@@ -9,7 +9,7 @@ module AzureClient
9
9
  @retry_policy = retry_policy
10
10
  end
11
11
 
12
- def add_message(content, metadata = "", retry_policy = @retry_policy)
12
+ def add_message(content, metadata = "", options = {}, retry_policy = @retry_policy)
13
13
  payload = {"content" => content, "metadata" => metadata}.to_json
14
14
  queue = select_queue
15
15
  if is_allowed_payload_size(payload)
@@ -18,7 +18,7 @@ module AzureClient
18
18
  #pick random blob name
19
19
  blob_name = name + (0...9).map{ ('a'..'z').to_a[rand(26)] }.join
20
20
  reference = {"type" => "azure_blob_reference", "name" => blob_name, "metadata" => metadata}
21
- @container.store_blob(blob_name, content, {}, retry_policy)
21
+ @container.store_blob(blob_name, content, options, retry_policy)
22
22
  queue.add_message(reference.to_json, retry_policy)
23
23
  end
24
24
  end
@@ -14,16 +14,32 @@ module AzureClient
14
14
  end
15
15
 
16
16
  def get_container(name, retry_policy = ExponentialRetryPolicy.new(5,1,2))
17
- if !(blob_service.list_containers.map {|container| container.name}.include?(name))
18
- blob_service.create_container(name)
19
- end
17
+ retry_policy.retry {
18
+ begin
19
+ blob_service.get_container_properties(name)
20
+ rescue Exception => e
21
+ if e.status_code == 404
22
+ blob_service.create_container(name)
23
+ else
24
+ raise e
25
+ end
26
+ end
27
+ }
20
28
  return Container.new(name, blob_service, retry_policy)
21
29
  end
22
30
 
23
31
  def get_table(name, retry_policy = ExponentialRetryPolicy.new(2,1,2))
24
- if !(table_service.query_tables.map {|hash| hash[:properties]["TableName"]}.include?(name))
25
- table_service.create_table(name)
26
- end
32
+ retry_policy.retry {
33
+ begin
34
+ table_service.get_table(name)
35
+ rescue StandardError => e
36
+ if e.status_code == 404
37
+ table_service.create_table(name)
38
+ else
39
+ raise e
40
+ end
41
+ end
42
+ }
27
43
  return Table.new(name, table_service, retry_policy)
28
44
  end
29
45
 
@@ -0,0 +1,18 @@
1
+ module AzureClient
2
+ class Compression
3
+
4
+ def self.compress(content)
5
+ stream = StringIO.new("w")
6
+ gz = Zlib::GzipWriter.new(stream)
7
+ gz.write(content)
8
+ gz.close
9
+ compressed = stream.string
10
+ end
11
+
12
+ def self.decompress(compressed)
13
+ gz = Zlib::GzipReader.new(StringIO.new(compressed))
14
+ content = gz.read
15
+ end
16
+
17
+ end
18
+ end
@@ -1,5 +1,8 @@
1
1
  module AzureClient
2
2
  class Container
3
+ BLOB_SIZE_LIMIT = 1024 * 1024 * 60 # actual limit is 64 MB
4
+ BLOCK_SIZE_LIMIT = 1024 * 1024 * 3 # actual limit is 4 MB
5
+
3
6
  attr_reader :name
4
7
 
5
8
  def initialize(name, blob_service, retry_policy)
@@ -7,12 +10,22 @@ module AzureClient
7
10
  @blob_service = blob_service
8
11
  @retry_policy = retry_policy
9
12
  end
13
+
14
+ def list_blobs(options = {}, retry_policy = @retry_policy)
15
+ retry_policy.retry {
16
+ @blob_service.list_blobs(name, options)
17
+ }
18
+ end
10
19
 
11
20
  #will overwrite content if blob with same name already exists
12
21
  def store_blob(blob_name, content, options = {}, retry_policy = @retry_policy)
13
- retry_policy.retry {
14
- @blob_service.create_block_blob(name, blob_name, content, options)
15
- }
22
+ if content.kind_of?(String)
23
+ store_string_to_blob(blob_name, content, options, retry_policy)
24
+ elsif content.kind_of?(IO)
25
+ store_io_to_blob(blob_name, content, options, retry_policy)
26
+ else
27
+ raise "Trying to upload unknown type #{content.class} into Azure, only String/IO allowed!"
28
+ end
16
29
  end
17
30
 
18
31
  #throws exception if no blob found
@@ -57,25 +70,73 @@ module AzureClient
57
70
  }
58
71
  end
59
72
 
60
- def poll_blob(blob_name, key, val, timeout = 10800)
61
- time = 0
73
+ def poll_blob(blob_name, key, val, polling_policy = ExponentialRetryPolicy.new(75,5,1.1))
74
+ start_time = Time.now
62
75
  #create blob if it does not exist
63
76
  begin
64
77
  blob = get_blob(blob_name)
65
78
  rescue
66
79
  store_blob(blob_name,"")
67
80
  end
68
- loop do
69
- sleep(5)
70
- time += 5;
71
- puts "*** polling Azure blob #{blob_name} for #{time} seconds"
81
+
82
+ polling_policy.retry {
83
+ puts "*** polling Azure blob #{blob_name} for #{Time.now - start_time} seconds"
72
84
  metadata = get_blob_metadata(blob_name)
73
- return val if metadata && metadata[key] == val
74
- if time > timeout
75
- raise "Polling blob #{blob_name} took more than #{timeout} seconds to run. Time out."
85
+ if metadata && metadata[key] == val then
86
+ return val
87
+ else
88
+ raise "Polled the blob #{blob_name} for #{Time.now - start_time} seconds. Time out!"
89
+ end
90
+ }
91
+ end
92
+
93
+ private
94
+
95
+ def store_string_to_blob(blob_name, content, options = {}, retry_policy = @retry_policy)
96
+ if options[:compressed]
97
+ content = Compression.compress(content)
98
+ options.merge!(:content_encoding => "gzip")
99
+ end
100
+
101
+ #use block list if content is too big
102
+ if content.length > BLOB_SIZE_LIMIT
103
+ block_number = content.length / BLOCK_SIZE_LIMIT
104
+ block_list = (0..block_number).map{|n| [pad_zero(n), :latest]}
105
+ block_list.each do |block|
106
+ retry_policy.retry {
107
+ @blob_service.create_blob_block(name, blob_name, block[0], content[n*BLOCK_SIZE_LIMIT..(n+1)*BLOCK_SIZE_LIMIT-1], options)
108
+ }
76
109
  end
110
+ retry_policy.retry {
111
+ @blob_service.commit_blob_blocks(name, blob_name, block_list, options)
112
+ }
113
+ else
114
+ retry_policy.retry {
115
+ @blob_service.create_block_blob(name, blob_name, content, options)
116
+ }
77
117
  end
78
118
  end
79
119
 
120
+ def store_io_to_blob(blob_name, io, options = {}, retry_policy = @retry_policy)
121
+ #always use block list if content is too big
122
+ block_id = 0
123
+ block_list = []
124
+ while !io.eof?
125
+ retry_policy.retry {
126
+ str = io.read(BLOCK_SIZE_LIMIT)
127
+ @blob_service.create_blob_block(name, blob_name, pad_zero(block_id), str, options)
128
+ }
129
+ block_list << [pad_zero(block_id), :latest]
130
+ block_id += 1
131
+ end
132
+
133
+ retry_policy.retry {
134
+ @blob_service.commit_blob_blocks(name, blob_name, block_list, options)
135
+ }
136
+ end
137
+
138
+ def pad_zero(number)
139
+ "%06d" % number
140
+ end
80
141
  end
81
142
  end
@@ -1,3 +1,3 @@
1
1
  module AzureClient
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.6"
3
3
  end
@@ -0,0 +1,75 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'spec_helper')
2
+
3
+ describe 'Container' do
4
+ before(:each) do
5
+ @storage = get_client
6
+ @container_name = (0...9).map{ ('a'..'z').to_a[rand(26)] }.join
7
+ @container = @storage.get_container(@container_name)
8
+ end
9
+
10
+ after(:each) do
11
+ @container.delete
12
+ end
13
+
14
+ it 'should add have a name' do
15
+ @container.name.should == @container_name
16
+ end
17
+
18
+ it 'stores and gets blobs' do
19
+ @container.store_blob("blob1", "blob1_content")
20
+ blob = @container.get_blob("blob1")
21
+ blob.get_content.should == "blob1_content"
22
+ end
23
+
24
+ it 'stores and gets compressed blobs' do
25
+ container = @storage.get_container("test")
26
+ container.store_blob("blob1", "blob_content", {:compressed => true})
27
+ blob = container.get_blob("blob1")
28
+ blob.get_content.should == "blob_content"
29
+ end
30
+
31
+ # it 'deletes blobs' do
32
+ # @container.store_blob("blob1", "blob1_content")
33
+ # @container.delete_blob("blob1")
34
+ # expect { @container.get_blob("blob1") }.to raise_error
35
+ # end
36
+ #
37
+ # it 'acquires leases' do
38
+ # @container.store_blob("blob1", "blob1_content")
39
+ # lease = @container.get_blob_lease('blob1')
40
+ # lease.should_not be_nil
41
+ # properties = @container.get_blob_properties('blob1')
42
+ # properties[:lease_status].should == 'locked'
43
+ # properties[:lease_state].should == 'leased'
44
+ # expect { @container.get_blob_lease('blob1') }.to raise_error
45
+ # end
46
+ #
47
+ # it 'retrieves blob properties' do
48
+ # @container.store_blob('blob1', 'blob1_content')
49
+ # properties = nil
50
+ # expect { properties = @container.get_blob_properties('blob1') }.to_not raise_error
51
+ # properties.should respond_to(:[])
52
+ # end
53
+ #
54
+ # it 'sets blob metadata' do
55
+ # @container.store_blob('blob1', 'blob1_content')
56
+ # metadata = { meta: 'data' }
57
+ # expect { @container.set_blob_metadata('blob1', metadata) }.to_not raise_error
58
+ # end
59
+ #
60
+ # it 'retrieves set metadata' do
61
+ # @container.store_blob('blob1', 'blob1_content')
62
+ # metadata = { meta: 'data' }
63
+ # @container.set_blob_metadata('blob1', metadata)
64
+ # @container.get_blob_metadata('blob1')['meta'].should == 'data'
65
+ # end
66
+ #
67
+ # it 'polls blobs' do
68
+ # res = ""
69
+ # Thread.new { res = @container.poll_blob("blob2","state","Ready") }
70
+ # sleep(6.0)
71
+ # @container.set_blob_metadata("blob2", {"state" => "Ready"})
72
+ # sleep (6.0)
73
+ # res.should == "Ready"
74
+ # end
75
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: azure_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-07-31 00:00:00.000000000 Z
12
+ date: 2013-12-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rest-client
@@ -94,6 +94,7 @@ files:
94
94
  - lib/azure_client/buffered_queue_factory.rb
95
95
  - lib/azure_client/buffered_queue_message.rb
96
96
  - lib/azure_client/client.rb
97
+ - lib/azure_client/compression.rb
97
98
  - lib/azure_client/container.rb
98
99
  - lib/azure_client/exponential_retry_policy.rb
99
100
  - lib/azure_client/linear_retry_policy.rb
@@ -102,6 +103,7 @@ files:
102
103
  - lib/azure_client/table.rb
103
104
  - lib/azure_client/table_entity.rb
104
105
  - lib/azure_client/version.rb
106
+ - spec/azure_client/container_spec.rb
105
107
  homepage: ''
106
108
  licenses: []
107
109
  post_install_message:
@@ -126,4 +128,5 @@ rubygems_version: 1.8.24
126
128
  signing_key:
127
129
  specification_version: 3
128
130
  summary: ''
129
- test_files: []
131
+ test_files:
132
+ - spec/azure_client/container_spec.rb