You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@deltacloud.apache.org by ma...@redhat.com on 2011/11/08 16:04:37 UTC

Unit tests Google Storage - v3

v3 of google storage unit tests - here the vcr cassette names are 'normalized' (issue
with hash ordering in ruby) to prevent 'real' requests being made when this is not
specified in /server/tests/drivers/google/setup.rb

Patch 1/3 contains fixes for google storage driver that came up when writing the tests$

Patch 2/3 contains the unit tests and vcr recordings. Invoke the tests by 'cd 
/path/to/deltacloud/server; rake test:google'

Patch 3/3 contains the monkey patch for VCR body normalisation
(we need to keep this for now - see https://github.com/myronmarston/vcr/issues/101)

marios

[PATCH 2/3] Unit tests for google storage driver

Posted by ma...@redhat.com.
From: marios <ma...@redhat.com>


Signed-off-by: marios <ma...@redhat.com>
---
 server/Rakefile                                    |    4 +-
 server/tests/common.rb                             |   44 ++++++++
 .../common_fixtures/deltacloud_blob_test.png       |  Bin 0 -> 24436 bytes
 server/tests/drivers/google/api_test.rb            |   35 ++++++
 server/tests/drivers/google/buckets_test.rb        |  116 ++++++++++++++++++++
 ...te-997d394df9ccfde81ea3073a68d674f173faa56f.yml |   28 +++++
 ...te-fdd72e5f5a3576ca37a34483de5f623c1c08a7f9.yml |   28 +++++
 ...et-4c5647f476f144847319d51b3c5cebe0d82b5995.yml |   30 +++++
 ...et-ff2e867e891fe52fd64cd797d594ac100e15b61a.yml |   40 +++++++
 ...ad-997d394df9ccfde81ea3073a68d674f173faa56f.yml |   40 +++++++
 ...st-9543e39820e2be0b739d4bbdf2a4dd3cb7e4e411.yml |   42 +++++++
 ...st-98ac1bc0a4fd1a04833547a50daed7382fc45ee3.yml |   32 ++++++
 ...st-997d394df9ccfde81ea3073a68d674f173faa56f.yml |   42 +++++++
 server/tests/drivers/google/setup.rb               |   13 ++
 14 files changed, 492 insertions(+), 2 deletions(-)
 create mode 100644 server/tests/drivers/common_fixtures/deltacloud_blob_test.png
 create mode 100644 server/tests/drivers/google/api_test.rb
 create mode 100644 server/tests/drivers/google/buckets_test.rb
 create mode 100644 server/tests/drivers/google/fixtures/delete-997d394df9ccfde81ea3073a68d674f173faa56f.yml
 create mode 100644 server/tests/drivers/google/fixtures/delete-fdd72e5f5a3576ca37a34483de5f623c1c08a7f9.yml
 create mode 100644 server/tests/drivers/google/fixtures/get-4c5647f476f144847319d51b3c5cebe0d82b5995.yml
 create mode 100644 server/tests/drivers/google/fixtures/get-ff2e867e891fe52fd64cd797d594ac100e15b61a.yml
 create mode 100644 server/tests/drivers/google/fixtures/head-997d394df9ccfde81ea3073a68d674f173faa56f.yml
 create mode 100644 server/tests/drivers/google/fixtures/post-9543e39820e2be0b739d4bbdf2a4dd3cb7e4e411.yml
 create mode 100644 server/tests/drivers/google/fixtures/post-98ac1bc0a4fd1a04833547a50daed7382fc45ee3.yml
 create mode 100644 server/tests/drivers/google/fixtures/post-997d394df9ccfde81ea3073a68d674f173faa56f.yml
 create mode 100644 server/tests/drivers/google/setup.rb

diff --git a/server/Rakefile b/server/Rakefile
index f07a6ce..0ec0f34 100644
--- a/server/Rakefile
+++ b/server/Rakefile
@@ -53,7 +53,7 @@ rescue LoadError
 end
 
 namespace :test do
-  %w(mock rackspace rhevm openstack).each do |driver|
+  %w(mock rackspace rhevm openstack google).each do |driver|
     desc "Run #{driver} unit tests"
     Rake::TestTask.new(driver) { |t|
       t.test_files = ['tests/common.rb', "tests/drivers/#{driver}/setup.rb"] + FileList.new("tests/drivers/#{driver}/*_test.rb") + FileList.new('tests/rabbit_test.rb')
@@ -75,7 +75,7 @@ end
 
 desc "Call our Test::Unit suite"
 task :test do
-  %w(mock rackspace rhevm).each do |driver|
+  %w(mock rackspace rhevm openstack google).each do |driver|
    Rake::Task["test:#{driver}"].reenable
    Rake::Task["test:#{driver}"].invoke
   end
diff --git a/server/tests/common.rb b/server/tests/common.rb
index fc39819..045033a 100644
--- a/server/tests/common.rb
+++ b/server/tests/common.rb
@@ -229,6 +229,50 @@ module DeltacloudTestCommon
     $created_instances << id
   end
 
+ #common variables used by the bucket/blob unit tests across clouds
+  @@created_bucket_name="testbucki2rpux3wdelme"
+  @@created_blob_name="testblobk1ds91kVdelme"
+  @@created_blob_local_file="#{File.dirname(__FILE__)}/drivers/common_fixtures/deltacloud_blob_test.png"
+
+  def check_bucket_basics(bucket, cloud)
+    (bucket/'bucket/name').first.text.should == "#{@@created_bucket_name}#{cloud}"
+    (bucket/'bucket').attribute("id").text.should == "#{@@created_bucket_name}#{cloud}"
+    (bucket/'bucket').length.should > 0
+    (bucket/'bucket/name').first.text.should_not == nil
+    (bucket/'bucket').attribute("href").text.should_not == nil
+  end
+
+  def check_blob_basics(blob, cloud)
+    (blob/'blob').length.should == 1
+    (blob/'blob').attribute("id").text.should_not == nil
+    (blob/'blob').attribute("href").text.should_not==nil
+    (blob/'blob/bucket').text.should_not == nil
+    (blob/'blob/content_length').text.should_not == nil
+    (blob/'blob/content_type').text.should_not == nil
+    (blob/'blob').attribute("id").text.should == "#{@@created_blob_name}#{cloud}"
+    (blob/'blob/bucket').text.should == "#{@@created_bucket_name}#{cloud}"
+    (blob/'blob/content_length').text.to_i.should == File.size(@@created_blob_local_file)
+  end
+
+  def check_blob_metadata(blob, metadata_hash)
+    meta_from_blob = {}
+    #extract metadata from nokogiri blob xml
+    (0.. (((blob/'blob/user_metadata').first).elements.size - 1) ).each do |i|
+      meta_from_blob[(((blob/'blob/user_metadata').first).elements[i].attribute("key").value)] =
+                                  (((blob/'blob/user_metadata').first).elements[i].children[1].text)
+    end
+    #remove any 'x-goog-meta-' prefixes (problem for google blobs and vcr...)
+    meta_from_blob.gsub_keys(/x-.*-meta-/i, "")
+    meta_from_blob.eql?(metadata_hash).should == true
+  end
+
+  #hash ordering is unpredictable - sort the params hash
+  #so we get same vcr cassette name each time
+  def stable_vcr_cassette_name(method, uri, params)
+    digest = Digest::SHA1.hexdigest("#{uri}-#{params.sort_by {|k,v| k.to_s}}")
+    return "#{method}-#{digest}"
+  end
+
 end
 
 include DeltacloudTestCommon
diff --git a/server/tests/drivers/common_fixtures/deltacloud_blob_test.png b/server/tests/drivers/common_fixtures/deltacloud_blob_test.png
new file mode 100644
index 0000000000000000000000000000000000000000..bf1b766689138e64236b7f8341001ba05abbef6e
GIT binary patch
literal 24436
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...@6tv>Vl)?e8ZQ-m;
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
ZU>U5AycFJhfJXnGytIl`rR4jd{{@1D8m|BV

literal 0
HcmV?d00001

diff --git a/server/tests/drivers/google/api_test.rb b/server/tests/drivers/google/api_test.rb
new file mode 100644
index 0000000..bd86d95
--- /dev/null
+++ b/server/tests/drivers/google/api_test.rb
@@ -0,0 +1,35 @@
+$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..')
+require 'tests/common'
+
+module GoogleTest
+
+  class ApiTest < Test::Unit::TestCase
+    include Rack::Test::Methods
+
+    def app
+      Sinatra::Application
+    end
+
+    def test_01_it_returns_entry_points
+      get_auth_url '/api;driver=google/?force_auth=1'
+      (last_xml_response/'/api').first[:driver].should == 'google'
+      (last_xml_response/'/api/link').length.should > 0
+    end
+
+    def test_02_it_has_google_features
+      get_url '/api;driver=google'
+      features = (last_xml_response/'/api/link[@rel="buckets"]/feature').collect { |f| f[:name] }
+      features.include?('bucket_location').should == true
+      features.length.should == 1
+    end
+
+    def test_03_it_has_google_collections
+      get_url '/api;driver=google'
+      collections = (last_xml_response/'/api/link').collect { |f| f[:rel] }
+      collections.include?('buckets').should == true
+      collections.include?('drivers').should == true
+      collections.length.should == 2
+    end
+
+  end
+end
diff --git a/server/tests/drivers/google/buckets_test.rb b/server/tests/drivers/google/buckets_test.rb
new file mode 100644
index 0000000..dd4e065
--- /dev/null
+++ b/server/tests/drivers/google/buckets_test.rb
@@ -0,0 +1,116 @@
+$:.unshift File.join(File.dirname(__FILE__), '..', '..', '..')
+require 'tests/common'
+#require 'webmock/test_unit'
+module GoogleTest
+
+  class BucketsTest < Test::Unit::TestCase
+    include Rack::Test::Methods
+
+    def app
+      Sinatra::Application
+    end
+
+    @@bucket_name_google="#{@@created_bucket_name}googel"
+    @@blob_name_google="#{@@created_blob_name}googel"
+#intentional typos here - bucket names cannot contain 'google'
+#see http://code.google.com/apis/storage/docs/reference/v1/developer-guidev1.html
+
+   def test_01_it_can_create_new_bucket
+      params = {
+        :name => @@bucket_name_google,
+        :'api[driver]' => 'google'
+      }
+      vcr_cassette = stable_vcr_cassette_name('post', '/api/buckets', params)
+      post_url '/api/buckets', params, {'vcr_cassette'=>vcr_cassette}
+      last_response.status.should == 201 # Created
+      bucket = last_xml_response
+      check_bucket_basics(bucket, "googel")
+      (bucket/'bucket/size').first.text.should == "0"
+    end
+
+    def test_02_it_can_post_new_blob
+      temp_file=File.open(@@created_blob_local_file)
+      params = {
+        'blob_id' => @@blob_name_google,
+        :meta_params=>"2",
+        :meta_name1=>"Author",
+        :meta_value1=>"deltacloud",
+        :meta_name2=>"foo",
+        :meta_value2=>"bar",
+        :'api[driver]' => 'google'
+      }
+      uri="/api/buckets/#{@@bucket_name_google}"
+      vcr_cassette = stable_vcr_cassette_name('post', uri, params)
+      params.merge!({'blob_data' => Rack::Test::UploadedFile.new(temp_file.path, "text/html")})
+      post_url uri, params, {'vcr_cassette'=>vcr_cassette}
+      last_response.status.should == 200
+      blob= last_xml_response
+      check_blob_basics(blob, "googel")
+    end
+
+    def test_03_it_can_retrieve_named_bucket_details
+       params = {
+        :'api[driver]' => 'google'
+      }
+      get_url "/api/buckets/#{@@bucket_name_google}", params
+      last_response.status.should == 200
+      bucket = last_xml_response
+      check_bucket_basics(bucket, "googel")
+      (bucket/'bucket/size').first.text.should == "1" #assuming blob created succesfully right?
+    end
+
+    def test_04_it_can_retrieve_named_blob_details
+      params = {
+                :'api[driver]' => 'google'
+               }
+      get_url "/api/buckets/#{@@bucket_name_google}/#{@@blob_name_google}", params
+      last_response.status.should == 200
+      blob = last_xml_response
+      check_blob_basics(blob, "googel")
+      check_blob_metadata(blob, {"author"=>"deltacloud", "foo"=>"bar"})
+    end
+
+    def test_05_it_can_retrieve_blob_metadata
+      params = {
+                :'api[driver]' => 'google'
+               }
+      head_url "/api/buckets/#{@@bucket_name_google}/#{@@blob_name_google}", params
+      last_response.status.should == 204
+      blob_meta_hash = last_response.headers.inject({}){|result, (k,v)| result[k]=v if k=~/^X-Deltacloud-Blobmeta-/i ; result}
+      blob_meta_hash.gsub_keys(/x-.*meta-/i, "")
+      ({"author"=>"deltacloud", "foo"=>"bar"}.eql?(blob_meta_hash)).should == true
+    end
+
+    def test_06_it_can_update_blob_metadata
+      params = {
+                :'api[driver]' => 'google'
+               }
+      new_meta = {"X-Deltacloud-Blobmeta-author" => "ApacheDeltacloud", "X-Deltacloud-Blobmeta-oof" => "rab"}
+      new_meta.each_pair do |k,v|
+        header k, v
+      end
+      post_url "/api/buckets/#{@@bucket_name_google}/#{@@blob_name_google}", params
+      last_response.status.should == 204
+      new_meta.each_pair do |k,v|
+        (last_response.headers[k]==v).should == true
+      end
+    end
+
+    def test_07_it_can_delete_blob
+      params = {
+                :'api[driver]' => 'google'
+               }
+      delete_url "/api/buckets/#{@@bucket_name_google}/#{@@blob_name_google}", params
+      last_response.status.should == 204
+    end
+
+    def test_08_it_can_delete_bucket
+      params = {
+                :'api[driver]' => 'google'
+               }
+      delete_url "/api/buckets/#{@@bucket_name_google}", params
+      last_response.status.should == 204
+    end
+
+  end
+end
diff --git a/server/tests/drivers/google/fixtures/delete-997d394df9ccfde81ea3073a68d674f173faa56f.yml b/server/tests/drivers/google/fixtures/delete-997d394df9ccfde81ea3073a68d674f173faa56f.yml
new file mode 100644
index 0000000..6d2d2fd
--- /dev/null
+++ b/server/tests/drivers/google/fixtures/delete-997d394df9ccfde81ea3073a68d674f173faa56f.yml
@@ -0,0 +1,28 @@
+--- 
+- !ruby/struct:VCR::HTTPInteraction 
+  request: !ruby/struct:VCR::Request 
+    method: DELETE
+    uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/testblobk1ds91kVdelmegoogel
+    body: 
+    headers: 
+      date: 
+      - Tue, 08 Nov 2011 14:45:26 +0000
+      authorization: 
+      - GOOG1 GOOGK7JXLS6UEYS6AYVO:gw0VDJ07/dqbPXtC67B53t7H/fk=
+  response: !ruby/struct:VCR::Response 
+    status: !ruby/struct:VCR::ResponseStatus 
+      code: 204
+      message: 
+    headers: 
+      expires: 
+      - Tue, 08 Nov 2011 14:45:27 GMT
+      server: 
+      - HTTP Upload Server Built on Nov 3 2011 13:52:28 (1320353548)
+      date: 
+      - Tue, 08 Nov 2011 14:45:27 GMT
+      content-length: 
+      - "0"
+      cache-control: 
+      - private, max-age=0
+    body: 
+    http_version: 
diff --git a/server/tests/drivers/google/fixtures/delete-fdd72e5f5a3576ca37a34483de5f623c1c08a7f9.yml b/server/tests/drivers/google/fixtures/delete-fdd72e5f5a3576ca37a34483de5f623c1c08a7f9.yml
new file mode 100644
index 0000000..aa147c5
--- /dev/null
+++ b/server/tests/drivers/google/fixtures/delete-fdd72e5f5a3576ca37a34483de5f623c1c08a7f9.yml
@@ -0,0 +1,28 @@
+--- 
+- !ruby/struct:VCR::HTTPInteraction 
+  request: !ruby/struct:VCR::Request 
+    method: DELETE
+    uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/
+    body: 
+    headers: 
+      date: 
+      - Tue, 08 Nov 2011 14:45:27 +0000
+      authorization: 
+      - GOOG1 GOOGK7JXLS6UEYS6AYVO:0KUpBpjywaMjTWti4lP0xs1w4aI=
+  response: !ruby/struct:VCR::Response 
+    status: !ruby/struct:VCR::ResponseStatus 
+      code: 204
+      message: 
+    headers: 
+      expires: 
+      - Tue, 08 Nov 2011 14:45:29 GMT
+      server: 
+      - HTTP Upload Server Built on Nov 3 2011 13:52:28 (1320353548)
+      date: 
+      - Tue, 08 Nov 2011 14:45:29 GMT
+      content-length: 
+      - "0"
+      cache-control: 
+      - private, max-age=0
+    body: 
+    http_version: 
diff --git a/server/tests/drivers/google/fixtures/get-4c5647f476f144847319d51b3c5cebe0d82b5995.yml b/server/tests/drivers/google/fixtures/get-4c5647f476f144847319d51b3c5cebe0d82b5995.yml
new file mode 100644
index 0000000..f2ab003
--- /dev/null
+++ b/server/tests/drivers/google/fixtures/get-4c5647f476f144847319d51b3c5cebe0d82b5995.yml
@@ -0,0 +1,30 @@
+--- 
+- !ruby/struct:VCR::HTTPInteraction 
+  request: !ruby/struct:VCR::Request 
+    method: GET
+    uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/
+    body: 
+    headers: 
+      date: 
+      - Tue, 08 Nov 2011 14:45:20 +0000
+      authorization: 
+      - GOOG1 GOOGK7JXLS6UEYS6AYVO:L+LQ0Sr12zsqp0vdXpaSh1NRb8o=
+  response: !ruby/struct:VCR::Response 
+    status: !ruby/struct:VCR::ResponseStatus 
+      code: 200
+      message: 
+    headers: 
+      expires: 
+      - Tue, 08 Nov 2011 14:45:21 GMT
+      content-type: 
+      - application/xml; charset=UTF-8
+      server: 
+      - HTTP Upload Server Built on Nov 3 2011 13:52:28 (1320353548)
+      date: 
+      - Tue, 08 Nov 2011 14:45:21 GMT
+      content-length: 
+      - "530"
+      cache-control: 
+      - private, max-age=0
+    body: <?xml version='1.0' encoding='UTF-8'?><ListBucketResult xmlns='http://doc.s3.amazonaws.com/2006-03-01'><Name>testbucki2rpux3wdelmegoogel</Name><Prefix></Prefix><Marker></Marker><IsTruncated>false</IsTruncated><Contents><Key>testblobk1ds91kVdelmegoogel</Key><LastModified>2011-11-08T14:45:19.826Z</LastModified><ETag>"6051b502c4281855ff32dc3e924fe3b0"</ETag><Size>24436</Size><StorageClass>STANDARD</StorageClass><Owner><ID>00b4903a9772c5a80c691066edef0ac07b267f576a2d11bbfc5bf1d2c7a2b8fd</ID></Owner></Contents></ListBucketResult>
+    http_version: 
diff --git a/server/tests/drivers/google/fixtures/get-ff2e867e891fe52fd64cd797d594ac100e15b61a.yml b/server/tests/drivers/google/fixtures/get-ff2e867e891fe52fd64cd797d594ac100e15b61a.yml
new file mode 100644
index 0000000..6f58cf2
--- /dev/null
+++ b/server/tests/drivers/google/fixtures/get-ff2e867e891fe52fd64cd797d594ac100e15b61a.yml
@@ -0,0 +1,40 @@
+--- 
+- !ruby/struct:VCR::HTTPInteraction 
+  request: !ruby/struct:VCR::Request 
+    method: HEAD
+    uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/testblobk1ds91kVdelmegoogel
+    body: 
+    headers: 
+      date: 
+      - Tue, 08 Nov 2011 14:45:21 +0000
+      authorization: 
+      - GOOG1 GOOGK7JXLS6UEYS6AYVO:KMG8vTcruwrTS06NrYb4gal1sYE=
+  response: !ruby/struct:VCR::Response 
+    status: !ruby/struct:VCR::ResponseStatus 
+      code: 200
+      message: 
+    headers: 
+      x-goog-meta-foo: 
+      - bar
+      etag: 
+      - "\"6051b502c4281855ff32dc3e924fe3b0\""
+      expires: 
+      - Fri, 01 Jan 1990 00:00:00 GMT
+      last-modified: 
+      - Tue, 08 Nov 2011 14:45:19 GMT
+      content-type: 
+      - text/html
+      server: 
+      - HTTP Upload Server Built on Nov 3 2011 13:52:28 (1320353548)
+      date: 
+      - "1320763522378"
+      x-goog-meta-author: 
+      - deltacloud
+      content-length: 
+      - "24436"
+      cache-control: 
+      - no-cache,no-store,max-age=0,must-revalidate
+      pragma: 
+      - no-cache
+    body: 
+    http_version: 
diff --git a/server/tests/drivers/google/fixtures/head-997d394df9ccfde81ea3073a68d674f173faa56f.yml b/server/tests/drivers/google/fixtures/head-997d394df9ccfde81ea3073a68d674f173faa56f.yml
new file mode 100644
index 0000000..b0e6301
--- /dev/null
+++ b/server/tests/drivers/google/fixtures/head-997d394df9ccfde81ea3073a68d674f173faa56f.yml
@@ -0,0 +1,40 @@
+--- 
+- !ruby/struct:VCR::HTTPInteraction 
+  request: !ruby/struct:VCR::Request 
+    method: HEAD
+    uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/testblobk1ds91kVdelmegoogel
+    body: 
+    headers: 
+      date: 
+      - Tue, 08 Nov 2011 14:45:22 +0000
+      authorization: 
+      - GOOG1 GOOGK7JXLS6UEYS6AYVO:a6itGMEz19GukbOEwaXcDLIdhoE=
+  response: !ruby/struct:VCR::Response 
+    status: !ruby/struct:VCR::ResponseStatus 
+      code: 200
+      message: 
+    headers: 
+      x-goog-meta-foo: 
+      - bar
+      etag: 
+      - "\"6051b502c4281855ff32dc3e924fe3b0\""
+      expires: 
+      - Fri, 01 Jan 1990 00:00:00 GMT
+      last-modified: 
+      - Tue, 08 Nov 2011 14:45:19 GMT
+      content-type: 
+      - text/html
+      server: 
+      - HTTP Upload Server Built on Nov 3 2011 13:52:28 (1320353548)
+      date: 
+      - "1320763523366"
+      x-goog-meta-author: 
+      - deltacloud
+      content-length: 
+      - "24436"
+      cache-control: 
+      - no-cache,no-store,max-age=0,must-revalidate
+      pragma: 
+      - no-cache
+    body: 
+    http_version: 
diff --git a/server/tests/drivers/google/fixtures/post-9543e39820e2be0b739d4bbdf2a4dd3cb7e4e411.yml b/server/tests/drivers/google/fixtures/post-9543e39820e2be0b739d4bbdf2a4dd3cb7e4e411.yml
new file mode 100644
index 0000000..9522d73
--- /dev/null
+++ b/server/tests/drivers/google/fixtures/post-9543e39820e2be0b739d4bbdf2a4dd3cb7e4e411.yml
@@ -0,0 +1,42 @@
+--- 
+- !ruby/struct:VCR::HTTPInteraction 
+  request: !ruby/struct:VCR::Request 
+    method: PUT
+    uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/testblobk1ds91kVdelmegoogel
+    body: 
+    headers: 
+      x-goog-meta-foo: 
+      - bar
+      content-type: 
+      - text/html
+      x-goog-meta-author: 
+      - deltacloud
+      date: 
+      - Tue, 08 Nov 2011 14:45:17 +0000
+      authorization: 
+      - GOOG1 GOOGK7JXLS6UEYS6AYVO:OkY5lr02ACmmaHJxPyWaiehWVjA=
+      content-length: 
+      - 24436
+  response: !ruby/struct:VCR::Response 
+    status: !ruby/struct:VCR::ResponseStatus 
+      code: 200
+      message: 
+    headers: 
+      etag: 
+      - "\"6051b502c4281855ff32dc3e924fe3b0\""
+      expires: 
+      - Fri, 01 Jan 1990 00:00:00 GMT
+      content-type: 
+      - text/html
+      server: 
+      - HTTP Upload Server Built on Nov 3 2011 13:52:28 (1320353548)
+      date: 
+      - Tue, 08 Nov 2011 14:45:20 GMT
+      content-length: 
+      - "0"
+      cache-control: 
+      - no-cache, no-store, must-revalidate
+      pragma: 
+      - no-cache
+    body: 
+    http_version: 
diff --git a/server/tests/drivers/google/fixtures/post-98ac1bc0a4fd1a04833547a50daed7382fc45ee3.yml b/server/tests/drivers/google/fixtures/post-98ac1bc0a4fd1a04833547a50daed7382fc45ee3.yml
new file mode 100644
index 0000000..597e228
--- /dev/null
+++ b/server/tests/drivers/google/fixtures/post-98ac1bc0a4fd1a04833547a50daed7382fc45ee3.yml
@@ -0,0 +1,32 @@
+--- 
+- !ruby/struct:VCR::HTTPInteraction 
+  request: !ruby/struct:VCR::Request 
+    method: PUT
+    uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/
+    body: 
+    headers: 
+      date: 
+      - Tue, 08 Nov 2011 14:45:14 +0000
+      authorization: 
+      - GOOG1 GOOGK7JXLS6UEYS6AYVO:P4NxwWRpcfP1oNOeg6pw+nEV5iw=
+  response: !ruby/struct:VCR::Response 
+    status: !ruby/struct:VCR::ResponseStatus 
+      code: 200
+      message: 
+    headers: 
+      expires: 
+      - Fri, 01 Jan 1990 00:00:00 GMT
+      content-type: 
+      - text/html
+      server: 
+      - HTTP Upload Server Built on Nov 3 2011 13:52:28 (1320353548)
+      date: 
+      - Tue, 08 Nov 2011 14:45:16 GMT
+      content-length: 
+      - "0"
+      cache-control: 
+      - no-cache, no-store, must-revalidate
+      pragma: 
+      - no-cache
+    body: 
+    http_version: 
diff --git a/server/tests/drivers/google/fixtures/post-997d394df9ccfde81ea3073a68d674f173faa56f.yml b/server/tests/drivers/google/fixtures/post-997d394df9ccfde81ea3073a68d674f173faa56f.yml
new file mode 100644
index 0000000..c9280bd
--- /dev/null
+++ b/server/tests/drivers/google/fixtures/post-997d394df9ccfde81ea3073a68d674f173faa56f.yml
@@ -0,0 +1,42 @@
+--- 
+- !ruby/struct:VCR::HTTPInteraction 
+  request: !ruby/struct:VCR::Request 
+    method: PUT
+    uri: https://testbucki2rpux3wdelmegoogel.commondatastorage.googleapis.com:443/testblobk1ds91kVdelmegoogel
+    body: 
+    headers: 
+      x-goog-meta-oof: 
+      - rab
+      x-goog-copy-source: 
+      - /testbucki2rpux3wdelmegoogel/testblobk1ds91kVdelmegoogel
+      x-goog-meta-author: 
+      - ApacheDeltacloud
+      date: 
+      - Tue, 08 Nov 2011 14:45:23 +0000
+      authorization: 
+      - GOOG1 GOOGK7JXLS6UEYS6AYVO:9YmU6qjJ1RpIQANCrfy6mAUSNv8=
+      x-goog-metadata-directive: 
+      - REPLACE
+  response: !ruby/struct:VCR::Response 
+    status: !ruby/struct:VCR::ResponseStatus 
+      code: 200
+      message: 
+    headers: 
+      etag: 
+      - "\"6051b502c4281855ff32dc3e924fe3b0\""
+      expires: 
+      - Fri, 01 Jan 1990 00:00:00 GMT
+      content-type: 
+      - text/html
+      server: 
+      - HTTP Upload Server Built on Nov 3 2011 13:52:28 (1320353548)
+      date: 
+      - Tue, 08 Nov 2011 14:45:26 GMT
+      content-length: 
+      - "175"
+      cache-control: 
+      - no-cache, no-store, must-revalidate
+      pragma: 
+      - no-cache
+    body: <?xml version='1.0' encoding='UTF-8'?><CopyObjectResult><LastModified>2011-11-08T14:45:19.826Z</LastModified><ETag>"6051b502c4281855ff32dc3e924fe3b0"</ETag></CopyObjectResult>
+    http_version: 
diff --git a/server/tests/drivers/google/setup.rb b/server/tests/drivers/google/setup.rb
new file mode 100644
index 0000000..09aea62
--- /dev/null
+++ b/server/tests/drivers/google/setup.rb
@@ -0,0 +1,13 @@
+ENV['API_DRIVER']   = "google"
+ENV['API_USER']     = 'GOOGK7JXLS6UEYS6AYVO'
+ENV['API_PASSWORD'] = 'QjxUunLgszKhBGn/LISQajGR82CfwvraxA9lqnkg'
+
+require 'vcr'
+
+DeltacloudTestCommon::record!
+
+VCR.config do |c|
+  c.cassette_library_dir = "#{File.dirname(__FILE__)}/fixtures/"
+  c.stub_with :excon
+  c.default_cassette_options = { :record => :new_episodes}
+end
-- 
1.7.6.4


Re: [PATCH 3/3] Monkey patch fix for VCR normalisation code

Posted by Michal Fojtik <mf...@redhat.com>.
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi,

Finished in 4.764091 seconds.
13 tests, 0 assertions, 0 failures, 0 errors

ACK! :-)

 -- Michal

marios@redhat.com wrote:
> From: marios <ma...@redhat.com>
> 
> Needed in order to run google unit tests - see
> https://github.com/myronmarston/vcr/issues/101
> 
> Signed-off-by: marios <ma...@redhat.com> --- 
> server/tests/drivers/google/setup.rb |   25
> +++++++++++++++++++++++++ 1 files changed, 25 insertions(+), 0
> deletions(-)
> 
> diff --git a/server/tests/drivers/google/setup.rb
> b/server/tests/drivers/google/setup.rb index 09aea62..95924e5 100644 
> --- a/server/tests/drivers/google/setup.rb +++
> b/server/tests/drivers/google/setup.rb @@ -11,3 +11,28 @@ VCR.config
> do |c| c.stub_with :excon c.default_cassette_options = { :record =>
> :new_episodes} end + +#monkey patch fix for VCR normalisation code: 
> +#see https://github.com/myronmarston/vcr/issues/4 +#when body is a
> tempfile, like when creating new blob +#this method of normalisation
> fails and excon throws errors +#(Excon::Errors::SocketError:can't
> convert Tempfile into String) +# +#RELEVANT:
> https://github.com/myronmarston/vcr/issues/101 +#(will need
> revisiting when vcr 2 comes along) + +module VCR +  module
> Normalizers +    module Body + +    private +    def normalize_body +
> self.body = case body +          when nil, ''; nil +          else +
> String.new(body) unless body.is_a?(Tempfile) +        end +      end 
> +    end +  end +end

- -- 
- --
Michal Fojtik, mfojtik@redhat.com
Deltacloud API: http://deltacloud.org
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org/

iQEcBAEBAgAGBQJOunUcAAoJEEnFpmY+LvFR05QH/Ry7jZdYnb9qSovv6t0yQLCY
/MM2NVzyfUrgvbsxjpNsihB7Q4yqRoNKnrQ4Ip6Zjy0+dGghwg4MKqXoQSjCZXqc
i1T9ncoiw6xjbkYvTnaUT6YqUxLcFGuheJpB5948GDDR1ADzCpAQQHjC90xfDoIn
R88/B2F0GBHHeoxNWkNVLd+ejk3MMwsLjBuWEtX0s9HwiXKa/SOxLkaJGaLSjLk2
8NErle4LkssPaPuY3mde+LR90kjkUUxn3nOA651Y+iCcf5TYh8b+kBcB5DEKgqKZ
zg1WiixtNYG9+3JwCNwaPgbtNoK6FdXWcRScjxmRUP/evhOQwjjHUx9EkUPYDs8=
=LNQX
-----END PGP SIGNATURE-----

[PATCH 3/3] Monkey patch fix for VCR normalisation code

Posted by ma...@redhat.com.
From: marios <ma...@redhat.com>

Needed in order to run google unit tests -
see https://github.com/myronmarston/vcr/issues/101

Signed-off-by: marios <ma...@redhat.com>
---
 server/tests/drivers/google/setup.rb |   25 +++++++++++++++++++++++++
 1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/server/tests/drivers/google/setup.rb b/server/tests/drivers/google/setup.rb
index 09aea62..95924e5 100644
--- a/server/tests/drivers/google/setup.rb
+++ b/server/tests/drivers/google/setup.rb
@@ -11,3 +11,28 @@ VCR.config do |c|
   c.stub_with :excon
   c.default_cassette_options = { :record => :new_episodes}
 end
+
+#monkey patch fix for VCR normalisation code:
+#see https://github.com/myronmarston/vcr/issues/4
+#when body is a tempfile, like when creating new blob
+#this method of normalisation fails and excon throws errors
+#(Excon::Errors::SocketError:can't convert Tempfile into String)
+#
+#RELEVANT: https://github.com/myronmarston/vcr/issues/101
+#(will need revisiting when vcr 2 comes along)
+
+module VCR
+  module Normalizers
+    module Body
+
+    private
+    def normalize_body
+     self.body = case body
+          when nil, ''; nil
+          else
+            String.new(body) unless body.is_a?(Tempfile)
+        end
+      end
+    end
+  end
+end
-- 
1.7.6.4


[PATCH 1/3] Fixes for google storage driver - blob metadata listing and valid_credentials method

Posted by ma...@redhat.com.
From: marios <ma...@redhat.com>

Signed-off-by: marios <ma...@redhat.com>
---
 .../lib/deltacloud/drivers/google/google_driver.rb |   12 +++++++++++-
 1 files changed, 11 insertions(+), 1 deletions(-)

diff --git a/server/lib/deltacloud/drivers/google/google_driver.rb b/server/lib/deltacloud/drivers/google/google_driver.rb
index f804fcf..2ffb5f8 100644
--- a/server/lib/deltacloud/drivers/google/google_driver.rb
+++ b/server/lib/deltacloud/drivers/google/google_driver.rb
@@ -83,12 +83,14 @@ class GoogleDriver < Deltacloud::BaseDriver
     google_client = new_client(credentials)
     safely do
       google_blob = google_client.head_object(opts['bucket'], opts[:id]).headers
+      meta_hash = google_blob.inject({}){|result, (k,v)| result[k]=v if k=~/^x-goog-meta-/i ; result}
+      meta_hash.gsub_keys("x-goog-meta-", "")
       blobs << Blob.new({   :id => opts[:id],
                  :bucket => opts['bucket'],
                  :content_length => google_blob['Content-Length'],
                  :content_type => google_blob['Content-Type'],
                  :last_modified => google_blob['Last-Modified'],
-                 :user_metadata => google_blob.select{|k,v| k.match(/^x-goog-meta-/i)}
+                 :user_metadata => meta_hash
               })
 
     end
@@ -193,6 +195,14 @@ class GoogleDriver < Deltacloud::BaseDriver
     end
   end
 
+  def valid_credentials?(credentials)
+    begin
+      new_client(credentials)
+    rescue
+      return false
+    end
+    return true
+  end
 
   private
 
-- 
1.7.6.4