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 2010/08/18 12:12:24 UTC

Blobstore functions - PUT and DELETE /api/containers

This patch implements the PUT /api/containers and DELETE /api/containers operations (create and delete an empty container) for S3 and Cloudfiles. There is a feature/bug which has been causing me problems: after the DELETE operation, for some reason Sinatra then tries to do a GET on the deleted resource... not sure what I'm doing wrong so Michal/David anyone else have you come across this before?

I have added a feature for the Amazon EC2/S3 driver so when you create a container you can optionally specify its location. Valid values are: 'EU'|'us-west1'|'ap-southeast-1E' (europe, US west coast, asia-pacific respectively). If you do not specify a location then the default is used which is 'us-east' (though weirldy if you specify 'us-east' as a location things blow up @ Amazon's end). 

I will keep adding new blobstore functions and sending them out incrementally for comments. Once everything is done I will send out one big patch with everything included. 

marios

Re: Blobstore functions - PUT and DELETE /api/containers

Posted by Michal Fojtik <mf...@redhat.com>.
Hi Marios,

On 18/08/10 11:12 +0100, mandreou@redhat.com wrote:
>
>This patch implements the PUT /api/containers and DELETE /api/containers operations (create and delete an empty container) for S3 and Cloudfiles. There is a feature/bug which has been causing me problems: after the DELETE operation, for some reason Sinatra then tries to do a GET on the deleted resource... not sure what I'm doing wrong so Michal/David anyone else have you come across this before?

I tried your patch and it's working perfectly. After I delete container it
redirects me to a list of containers, which as I suppose is the correct way
how to do it.

Anyway I found a few issues:

1. When you want to create a container, with some common name (like 'Test')
    it throws 'Internal Server Error'. Please catch these exceptions using
    safely..end block.
    I know that I'm sharing my bucket names with other users, but we need to
    throw something 'useful' for a client.
    Also please consider using this block for all 'backend-specific' operations.
    For this case returning '409 - Conflict' should be fine. Something like:

     safely do
       begin
       if container_location
         container = RightAws::S3::Bucket.create(s3_client, name, true, nil, :location => container_location)
       else
         container = RightAws::S3::Bucket.create(s3_client, name, true)
       end
       rescue RightAws::AwsError => e
         raise e unless e.message =~ /BucketAlreadyExists/
         raise Deltacloud::BackendError.new(409, e.class.to_s, e.message, e.backtrace)
       end
     end

2. When I tried your patch for the first time I got 'no-method-error' when
    I accessed some blob item inside container. This error was caused by
    missing 'content_type' method in RightAws::S3::Key class.
    Workaround is to add 'require "mime/types"' on top of EC2 driver and  then
    instead of using:

481:   :content_type => s3_object.content_type,
   
    use:

481:   :content_type => "#{MIME::Types.type_for(s3_object.name)}",

3. Create operation is always POST and not PUT. Should be great if we could
    use 'PUT' method for 'putting' new blobs inside container (but I'm not
    sure if this HTTP method like uploading ;)

>I have added a feature for the Amazon EC2/S3 driver so when you create a container you can optionally specify its location. Valid values are: 'EU'|'us-west1'|'ap-southeast-1E' (europe, US west coast, asia-pacific respectively). If you do not specify a location then the default is used which is 'us-east' (though weirldy if you specify 'us-east' as a location things blow up @ Amazon's end).

Nice! I tried it and it works as expected.

  -- Michal

-- 
--------------------------------------------------------
Michal Fojtik, mfojtik@redhat.com, +420 532 294 4307
Ruby / Ruby On Rails Developer
Deltacloud API: http://deltacloud.org
--------------------------------------------------------

['PATCH'] New blobstore functions: PUT /api/containers and DELETE /api/containers/:id.

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

---
 server/lib/deltacloud/base_driver/base_driver.rb   |    6 +++
 server/lib/deltacloud/base_driver/features.rb      |    8 ++++
 server/lib/deltacloud/drivers/ec2/ec2_driver.rb    |   26 +++++++++++++
 .../drivers/rackspace/rackspace_driver.rb          |   13 +++++++
 .../lib/deltacloud/helpers/application_helper.rb   |    7 ++++
 server/server.rb                                   |   38 +++++++++++++++++++-
 server/views/containers/index.html.haml            |   12 +++++-
 server/views/containers/new.html.haml              |   13 +++++++
 8 files changed, 120 insertions(+), 3 deletions(-)
 create mode 100644 server/views/containers/new.html.haml

diff --git a/server/lib/deltacloud/base_driver/base_driver.rb b/server/lib/deltacloud/base_driver/base_driver.rb
index c21ccfa..a2d1ee3 100644
--- a/server/lib/deltacloud/base_driver/base_driver.rb
+++ b/server/lib/deltacloud/base_driver/base_driver.rb
@@ -197,6 +197,12 @@ module Deltacloud
       nil
     end
     
+    def create_container(credentials, name, opts=nil)
+    end
+    
+    def delete_container(credentials, name, opts=nil)
+    end
+    
     def blobs(credentials, opts = nil)
       []
     end
diff --git a/server/lib/deltacloud/base_driver/features.rb b/server/lib/deltacloud/base_driver/features.rb
index 3ed4085..af3975e 100644
--- a/server/lib/deltacloud/base_driver/features.rb
+++ b/server/lib/deltacloud/base_driver/features.rb
@@ -162,5 +162,13 @@ module Deltacloud
       description "Size instances according to changes to a hardware profile"
       # The parameters are filled in from the hardware profiles
     end
+    
+    declare_feature :containers, :container_location do
+      description "Take extra location parameter for Container creation (e.g. S3, 'eu' or 'us-west-1')"
+      operation :create do
+        param :location, :string, :optional
+      end
+    end
+    
   end
 end
diff --git a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
index 313872c..a47e6d8 100644
--- a/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
+++ b/server/lib/deltacloud/drivers/ec2/ec2_driver.rb
@@ -43,6 +43,7 @@ class EC2Driver < Deltacloud::BaseDriver
   feature :instances, :user_data
   feature :instances, :authentication_key
   feature :images, :owner_id
+  feature :containers, :container_location 
 
   define_hardware_profile('m1.small') do
     cpu                1
@@ -292,6 +293,31 @@ class EC2Driver < Deltacloud::BaseDriver
     container_list
   end
 
+
+#
+#
+#valid values for bucket location: 'EU'|'us-west1'|'ap-southeast-1' - if you
+#don't specify a location then by default buckets are created in 'us-east'
+#[if you specify 'us-east' things blow up] 
+  def create_container(credentials, name, opts={})
+    container = nil
+    s3_client = s3_client(credentials)
+    container_location = opts['location']
+    if container_location
+      container = RightAws::S3::Bucket.create(s3_client, name, true, nil, :location => container_location)
+    else
+      container = RightAws::S3::Bucket.create(s3_client, name, true)
+    end
+    convert_storage_container(container)
+  end
+
+#--
+#delete_container
+#--
+  def delete_container(credentials, name, opts={})
+    s3_client = s3_client(credentials)
+    s3_client.interface.delete_bucket(name)
+  end
 #--
 # BLOBS get a list of blobs for a given bucket
 #--
diff --git a/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb b/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb
index e6fbf97..d15ec68 100644
--- a/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb
+++ b/server/lib/deltacloud/drivers/rackspace/rackspace_driver.rb
@@ -168,6 +168,19 @@ class RackspaceDriver < Deltacloud::BaseDriver
     container_list
   end
 
+  def create_container(credentials, name, opts)
+    container = nil
+    cf = cloudfiles_client(credentials)
+    new_container = cf.create_container(name)
+    container = convert_container(new_container)
+    container
+  end
+  
+  def delete_container(credentials, name, opts)
+    cf = cloudfiles_client(credentials)
+    cf.delete_container(name)
+  end
+
   def blobs(credentials, opts)
     unless opts['container'] then
       raise Deltacloud::Validation::Failure.new(Deltacloud::Validation::Param.new(["container"]), "Error - need container name to retrieve the blob list. You said bucket->#{opts['container']}.")
diff --git a/server/lib/deltacloud/helpers/application_helper.rb b/server/lib/deltacloud/helpers/application_helper.rb
index 9a9dfdc..348e5d6 100644
--- a/server/lib/deltacloud/helpers/application_helper.rb
+++ b/server/lib/deltacloud/helpers/application_helper.rb
@@ -54,6 +54,13 @@ module ApplicationHelper
     return 'password' if driver_has_feature?(:authentication_password)
   end
 
+  def driver_has_container_location_feature?
+    driver.features(:containers).each do |feat|
+      return true if feat.name == :container_location
+    end
+    false
+  end
+
   def filter_all(model)
       filter = {}
       filter.merge!(:id => params[:id]) if params[:id]
diff --git a/server/server.rb b/server/server.rb
index e0a61c1..75e63b4 100644
--- a/server/server.rb
+++ b/server/server.rb
@@ -374,6 +374,13 @@ get '/api/containers/:container/:blob' do
   end
 end
 
+get '/api/containers/new' do
+  respond_to do |format|
+    format.html { haml :"containers/new" }
+  end
+end
+
+
 collection :containers do
   description "Cloud Storage Containers - aka buckets|directories|folders"
 
@@ -390,4 +397,33 @@ collection :containers do
     param :id,        :string
     control { show(:container) }
   end
-end
\ No newline at end of file
+  
+  operation :create do
+    description "Create a new container (PUT /api/containers)"
+    param :name,      :string,    :required
+    control do
+      @container = driver.create_container(credentials, params[:name], params)
+      respond_to do |format|
+        format.xml do
+          response.status = 201  # Created
+          response['Location'] = container_url(@container.id)
+          haml :"containers/show"
+        end
+        format.html do
+          redirect container_url(@container.id) if @container and @container.id
+          redirect containers_url
+        end
+      end
+    end
+  end
+  
+  operation :destroy do
+    description "Delete a container by name - container must be empty"
+    param :id,    :string,    :required
+    control do
+      driver.delete_container(credentials, params[:id], params)
+      redirect(containers_url)
+    end
+  end
+
+end
diff --git a/server/views/containers/index.html.haml b/server/views/containers/index.html.haml
index a199ace..9c2c31a 100644
--- a/server/views/containers/index.html.haml
+++ b/server/views/containers/index.html.haml
@@ -1,5 +1,8 @@
 %h1
   Containers
+%br
+%p
+  =link_to 'Create new container', "/api/containers/new"
 
 %table.display
   %thead
@@ -12,7 +15,8 @@
         Size
       %th
         Blob List
-
+      %th
+         
   %tbody
     - @containers.each do |container|
       %tr
@@ -24,4 +28,8 @@
           = container.size
         %td
           -container.blob_list.each do |blob|
-            = blob 
\ No newline at end of file
+            = blob 
+        %td
+          =link_to 'Delete', destroy_container_url(container.name), :class => 'delete'
+
+
diff --git a/server/views/containers/new.html.haml b/server/views/containers/new.html.haml
new file mode 100644
index 0000000..4c92e00
--- /dev/null
+++ b/server/views/containers/new.html.haml
@@ -0,0 +1,13 @@
+%h1 New Container
+
+%form{ :action => containers_url, :method => :post }
+  %label
+    Container Name:
+    %input{ :name => 'name', :size => 250}/
+    %br
+  -if driver_has_container_location_feature?
+    %p
+      %label
+        Location: (optional)
+        %input{ :name => 'location', :size => 20 }/
+  %input{ :type => :submit, :name => "commit", :value => "create"}/
\ No newline at end of file
-- 
1.7.2.1