azure_client 0.1.1 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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