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/03/02 21:04:33 UTC

Etc fixes for ruby client + buckets/blobs from deltacloudc

Updates the ruby client with basic functionality for buckets and blobs. Right now implements index and show for buckets and show for blobs (index doesn't apply - blobs aren't a 'proper' collection - you get an 'index' of blobs when you get a specific bucket). Will work on adding create/delete for these later (Streaming PUT takes priority right now). Patch 2/5 is a fix for Jira issue #20. Patch 5/5 is from Michal (patch from a while back that was forgotten on the list). 

 deltacloudc buckets -l  -u 'http://user:pass@localhost:3001/api'
 deltacloudc buckets index -u 'http://user:pass@localhost:3001/api'
 deltacloudc buckets show -i BUCKETNAME -u 'http://user:pass@localhost:3001/api'
 deltacloudc blob -l -u 'http://user:pass@localhost:3001/api'
 deltacloudc blob show -b BUCKETNAME -i BLOBNAME -u 'http://user:pass@localhost:3001/api'


marios

[PATCH 2/5] Fix for deltacloudc to address jira issue #20 https://issues.apache.org/jira/browse/DTACLOUD-20

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

---
 client/bin/deltacloudc |   33 ++++++++++++++++++++++++++++++---
 1 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/client/bin/deltacloudc b/client/bin/deltacloudc
index 9408f90..1fe7874 100755
--- a/client/bin/deltacloudc
+++ b/client/bin/deltacloudc
@@ -38,6 +38,24 @@ deltacloudc collection operation [options]
 URL format:
 API_URL=http://[user]:[password]@[api_url][port][/uri]
 
+Examples:
+
+ 1. To list collections for deltacloud api on port 3333 of server deltacloud.foo
+
+        deltacloudc -l -u http://user:password@deltacloud.foo:3333/api
+
+ 2. To list the operations for the 'images' collection:
+
+        deltacloudc images -l -u http://user:password@deltacloud.foo:3333/api
+
+ 3. To list all images (i.e. call the 'index' operation of 'images'):
+
+        deltacloudc images index -u http://user:password@deltacloud.foo:3333/api
+
+ 4. To get the details of image '5':
+
+        deltacloudc images show -i 5 -u http://user:password@deltacloud.foo:3333/api
+
 Options:
 BANNER
   opts.on( '-i', '--id ID', 'ID for operation') { |id| options[:id] = id }
@@ -48,21 +66,30 @@ BANNER
   opts.on( '-s', '--state STATE', 'Instance state (RUNNING, STOPPED)') { |state| options[:state] = state }
   opts.on( '-u', '--url URL', 'API url ($API_URL variable)') { |url| options[:api_url] = url }
   opts.on( '-l', '--list', 'List collections/operations') { |id| options[:list] = true }
-  opts.on( '-h', '--help', 'Display this screen' ) { puts opts ; exit }
+  opts.on( '-h', '--help', 'Display this screen' ) { puts @optparse; Kernel.exit! }
   opts.on( '-v', '--version', 'Display API version' ) { options[:version]=true }
   opts.on( '-V', '--verbose', 'Print verbose messages' ) { options[:verbose]=true }
 end
 
 def invalid_usage(error_msg='')
-  puts "ERROR: #{error_msg}"
+  puts "\n ERROR: #{error_msg} \n\n"
+  puts @optparse
   exit(1)
 end
 
-@optparse.parse!
+begin
+    @optparse.parse!
+rescue Exception => e
+    invalid_usage(e.message)
+end
 
 # First try to get API_URL from environment
 options[:api_url] = ENV['API_URL'] if options[:api_url].nil?
 
+if(options[:api_url].nil?)
+    invalid_usage("You must supply the url to the deltacloud api; either use '-u' flag or set the 'API_URL' environment variable")
+end
+
 url = URI.parse(options[:api_url])
 api_url = "http://#{url.host}#{url.port ? ":#{url.port}" : ''}#{url.path}"
 
-- 
1.7.3.4


[PATCH 1/5] Adds some tests and a fix for plain_formatter for deltacloudc (via Michal Fojtik)

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

---
 client/Rakefile               |   10 ++
 client/lib/base_object.rb     |    1 -
 client/lib/plain_formatter.rb |    7 +-
 client/tests/cmd.rb           |  196 +++++++++++++++++++++++++++++++++++++++++
 client/tests/common.rb        |   24 +++++
 5 files changed, 235 insertions(+), 3 deletions(-)
 create mode 100644 client/tests/cmd.rb
 create mode 100644 client/tests/common.rb

diff --git a/client/Rakefile b/client/Rakefile
index 947525e..a6ba12b 100644
--- a/client/Rakefile
+++ b/client/Rakefile
@@ -17,6 +17,7 @@
 # under the License.
 
 require 'rake/gempackagetask'
+require 'rake/testtask'
 
 load 'deltacloud-client.gemspec'
 
@@ -25,6 +26,7 @@ task 'documentation' do
   load 'lib/documentation.rb'
 end
 
+
 spec = Gem::Specification.load('deltacloud-client.gemspec')
 Rake::GemPackageTask.new(spec) do |pkg|
   pkg.need_tar = true
@@ -44,6 +46,14 @@ task 'fixtures' do
   FileUtils.cp_r( File.dirname( __FILE__ ) + '/specs/fixtures', File.dirname( __FILE__ ) + '/specs/data' )
 end
 
+namespace :test do
+  Rake::TestTask.new(:cmd) do |t|
+    t.libs << "tests"
+    t.test_files = FileList['tests/cmd.rb']
+    t.verbose = true
+  end
+end
+
 desc "Clean Fixtures"
 task 'fixtures:clean' do
   FileUtils.rm_rf( File.dirname( __FILE__ ) + '/specs/data' )
diff --git a/client/lib/base_object.rb b/client/lib/base_object.rb
index 8b0f394..06db703 100644
--- a/client/lib/base_object.rb
+++ b/client/lib/base_object.rb
@@ -108,7 +108,6 @@ module DeltaCloud
         # First of all search throught array for method name
         m = search_for_method(method_name)
         if m.nil?
-          warn "[WARNING] Method unsupported by API: '#{self.class}.#{method_name}(#{args.inspect})'"
           return nil
         else
           # Call appropriate handler for method
diff --git a/client/lib/plain_formatter.rb b/client/lib/plain_formatter.rb
index 1b66c34..89e92f9 100644
--- a/client/lib/plain_formatter.rb
+++ b/client/lib/plain_formatter.rb
@@ -50,8 +50,11 @@ module DeltaCloud
 
       class HardwareProfile < Base
         def format
+          architecture = @obj.architecture ? @obj.architecture.value[0,6] : 'opaque'
+          memory = @obj.memory ? @obj.memory.value.to_s[0,10] : 'opaque'
+          storage = @obj.storage ? @obj.storage.value.to_s[0,10] : 'opaque'
           sprintf("%-15s | %-6s | %10s | %10s ", @obj.id[0, 15],
-            @obj.architecture.value[0,6], @obj.memory.value.to_s[0,10], @obj.storage.value.to_s[0,10])
+           architecture , memory, storage)
         end
       end
 
@@ -94,7 +97,7 @@ module DeltaCloud
     end
 
     def format(obj)
-      object_name = obj.class.name.classify.gsub(/^DeltaCloud::API::/, '')
+      object_name = obj.class.name.classify.gsub(/^DeltaCloud::API::(\w+)::/, '')
       format_class = DeltaCloud::PlainFormatter::FormatObject.const_get(object_name)
       format_class.new(obj).format
     end
diff --git a/client/tests/cmd.rb b/client/tests/cmd.rb
new file mode 100644
index 0000000..4d65f3e
--- /dev/null
+++ b/client/tests/cmd.rb
@@ -0,0 +1,196 @@
+require 'rubygems'
+require 'shoulda'
+require 'tests/common'
+
+include DeltaCloud::TestHelper
+
+class CommandLineTest < Test::Unit::TestCase
+  context "a command line client" do
+
+    should "respond to --help argument" do
+      assert_nothing_raised do
+        base_client('--help')
+      end
+    end
+
+    should "return API version with --version argument" do
+      assert_match /Deltacloud API\(mock\) (\d+)\.(\d+)/, client('--version')
+    end
+
+    should "return list all collections with --list argument" do
+      output = nil
+      assert_nothing_raised do
+        output = client('--list')
+      end
+      assert_not_nil output
+      assert_match /images/, output
+      assert_match /instances/, output
+      assert_match /realms/, output
+      assert_match /hardware_profiles/, output
+    end
+
+    should 'respond with proper error when accessing unknow collection' do
+      output = client('unknown_collection')
+      assert_match /^ERROR: Unknown collection/, output
+    end
+
+  end
+end
+
+class CmdRealmTest < Test::Unit::TestCase
+  context "a realms" do
+
+    should "be listed using realms argument" do
+      output = nil
+      assert_nothing_raised do
+        output = client('realms')
+      end
+      assert_match /^us/m, output
+      assert_match /^eu/m, output
+    end
+
+    should "be filtered using show --id argument" do
+      output = nil
+      assert_nothing_raised do
+        output = client('realms show --id us')
+      end
+      assert_match /^us/, output
+      assert_no_match /^eu/, output
+    end
+
+  end
+end
+
+class CmdHardwareProfilesTest < Test::Unit::TestCase
+  context "a hardware profiles" do
+
+    should "be listed using hardware_profiles argument" do
+      output = nil
+      assert_nothing_raised do
+        output = client('hardware_profiles')
+      end
+      assert_no_warning output
+      assert_match /^m1-small/m, output
+      assert_match /^m1-large/m, output
+      assert_match /^m1-xlarge/m, output
+      assert_match /^opaque/m, output
+    end
+
+    should "be filtered using show --id argument" do
+      output = nil
+      assert_nothing_raised do
+        output = client('hardware_profiles show --id m1-large')
+      end
+      assert_no_warning output
+      assert_match /^m1-large/, output
+      assert_no_match /^m1-small/, output
+    end
+  end
+end
+
+class CmdImagesTest < Test::Unit::TestCase
+
+  context "a images" do
+
+    should "be listed using images argument" do
+      output = nil
+      assert_nothing_raised do
+        output = client('images')
+      end
+      assert_no_warning output
+      assert_match /^img2/m, output
+      assert_match /^img1/m, output
+      assert_match /^img3/m, output
+    end
+
+    should "be filtered using show --id argument" do
+      output = nil
+      assert_nothing_raised do
+        output = client('images show --id img2')
+      end
+      assert_no_warning output
+      assert_match /^img2/m, output
+      assert_no_match /^img1/m, output
+    end
+
+    should "be filtered using --arch argument" do
+      output = nil
+      assert_nothing_raised do
+        output = client('images --arch x86_64')
+      end
+      assert_no_warning output
+      assert_match /x86_64/, output
+      assert_no_match /i386/, output
+    end
+
+  end
+
+end
+
+class CmdInstancesTest < Test::Unit::TestCase
+
+  context 'an instances' do
+
+    should 'be listed using instances argument' do
+      output = nil
+      assert_nothing_raised do
+        output = client('instances')
+      end
+      assert_no_warning output
+      assert_match /^inst1/, output
+    end
+
+    should 'be filtered using --id argument' do
+      output = nil
+      assert_nothing_raised do
+        output = client('instances show --id inst0')
+      end
+      assert_no_warning output
+      assert_match /^inst0/m, output
+      assert_no_match /^inst1/m, output
+    end
+
+  end
+
+  context 'an instance' do
+
+    should 'be created supplying --image-id argument and -p argument' do
+      output = nil
+      assert_nothing_raised do
+        output = client('instances create --image-id img1 -p m1-small')
+      end
+      assert_no_warning output
+      assert_match /^inst(\d+)/, output
+      @@created_instance_id = output.match(/^inst(\d+)/).to_a.first
+    end
+
+    should 'be rebooted using reboot operation' do
+      output = nil
+      assert_nothing_raised do
+        output = client("instances reboot --id #{@@created_instance_id}")
+      end
+      assert_no_warning output
+      assert_match /#{@@created_instance_id}/, output
+      assert_match /RUNNING/, output
+    end
+
+    should 'be stopped using stop operation' do
+      output = nil
+      assert_nothing_raised do
+        output = client("instances stop --id #{@@created_instance_id}")
+      end
+      assert_no_warning output
+      assert_match /#{@@created_instance_id}/, output
+      assert_match /STOPPED/, output
+    end
+
+    should 'be destroyed using destroy operation' do
+      output = nil
+      assert_nothing_raised do
+        output = client("instances destroy --id #{@@created_instance_id}")
+      end
+      assert_no_warning output
+    end
+
+  end
+end
diff --git a/client/tests/common.rb b/client/tests/common.rb
new file mode 100644
index 0000000..6ca04b5
--- /dev/null
+++ b/client/tests/common.rb
@@ -0,0 +1,24 @@
+module DeltaCloud
+  module TestHelper
+
+    include Test::Unit::Assertions
+
+    API_URL   = "http://localhost:3001/api"
+    API_USER  = "mockuser"
+    API_PASWD = "mockpassword"
+
+    def base_client(args)
+      `bin/deltacloudc #{args}`
+    end
+
+    def client(args)
+      args = "-u http://mockuser:mockpassword@localhost:3001/api " + args
+      base_client(args)
+    end
+
+    def assert_no_warning(output)
+      assert_no_match /\[WARNING\] Method unsupported by API: '(\w+)'/, output
+    end
+
+  end
+end
-- 
1.7.3.4


[PATCH 4/5] Various fixes for ruby client to display buckets and blobs. Adds blob 'show' operation, buckets 'show' and 'index' with formatting.

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

---
 client/bin/deltacloudc        |   21 +++++++++++++++++++++
 client/lib/deltacloud.rb      |   18 ++++++++++++++----
 client/lib/plain_formatter.rb |   23 ++++++++++++++++++-----
 3 files changed, 53 insertions(+), 9 deletions(-)

diff --git a/client/bin/deltacloudc b/client/bin/deltacloudc
index 1fe7874..04b383a 100755
--- a/client/bin/deltacloudc
+++ b/client/bin/deltacloudc
@@ -60,6 +60,7 @@ Options:
 BANNER
   opts.on( '-i', '--id ID', 'ID for operation') { |id| options[:id] = id }
   opts.on( '-d', '--image-id ID', 'Image ID') { |id| options[:image_id] = id }
+  opts.on( '-b', '--bucket-id ID', 'Bucket ID') {|id| options[:bucket_id] = id }
   opts.on( '-a', '--arch ARCH', 'Architecture (x86, x86_64)') { |id| options[:architecture] = id }
   opts.on( '-p', '--hardware-profile HARDWARE_PROFILE', 'Hardware Profile') { |id| options[:hwp_id] = id }
   opts.on( '-n', '--name NAME', 'Name (for instance eg.)') { |name| options[:name] = name }
@@ -102,6 +103,8 @@ collections = client.entry_points.keys
 
 # Exclude collection which don't have methods in client library yet
 collections.delete(:instance_states)
+#add blob collection if buckets is present
+collections << :blob if collections.include?(:buckets)
 
 # If list parameter passed print out available collection
 # with API documentation
@@ -115,6 +118,11 @@ end
 # If collection parameter is present and user requested list
 # print all operation defined for collection with API documentation
 if options[:list] and options[:collection]
+  #deal with blobs again - bypass 'normal' docs procedure
+  if options[:collection] =~ /blob/i
+    puts "show : Get details of a specified blob in a specified bucket (GET /api/buckets/:bucket/:blob)"
+    exit(0)
+  end
   doc = client.documentation(options[:collection])
   doc.operations.each do |c|
     puts sprintf("%-20s: %s", c.operation, c.description)
@@ -131,6 +139,8 @@ end
 # Do same if 'index' operation is set
 if options[:collection] and ( options[:operation].nil? or options[:operation].eql?('index') )
   invalid_usage("Unknown collection: #{options[:collection]}") unless collections.include?(options[:collection].to_sym)
+#cannot list blobs - can only show a specific blob
+  invalid_usage("You must specify a particular blob with -i and a particular bucket with -b") if options[:collection] =~ (/blob/i)
   params = {}
   params.merge!(:architecture => options[:architecture]) if options[:architecture]
   params.merge!(:id => options[:id]) if options[:id]
@@ -150,6 +160,17 @@ if options[:collection] and options[:operation]
 
   # If collection is set and requested operation is 'show' just 'singularize'
   # collection name and print item with specified id (-i parameter)
+
+  #Blobs are a special case so deal with first -
+  if options[:operation].eql?('show') && options[:collection] =~ (/blob/i)
+    invalid_usage("Please specify the bucket for this blob using the -b option") unless options[:bucket_id]
+    invalid_usage("Missing blob ID, please specify with -i option") unless options[:id]
+    params = {}
+    params.merge!(:id => options[:id], 'bucket' => options[:bucket_id])
+    puts format(client.send( options[:collection], params))
+    exit(0)
+  end
+
   if options[:operation].eql?('show')
     invalid_usage("Missing ID, must be provided with --id") unless options[:id]
     puts format(client.send(options[:collection].gsub(/s$/, ''), options[:id]))
diff --git a/client/lib/deltacloud.rb b/client/lib/deltacloud.rb
index 292305d..7f9ec9c 100644
--- a/client/lib/deltacloud.rb
+++ b/client/lib/deltacloud.rb
@@ -146,7 +146,6 @@ module DeltaCloud
     # Define methods based on 'rel' attribute in entry point
     # Two methods are declared: 'images' and 'image'
     def declare_entry_points_methods(entry_points)
-
       API.instance_eval do
         entry_points.keys.select {|k| [:instance_states].include?(k)==false }.each do |model|
 
@@ -167,8 +166,20 @@ module DeltaCloud
             self.send(model.to_s.singularize.to_sym, $1)
           end
 
+      end
+
+      #define methods for blobs:
+      if(entry_points.include?(:buckets))
+        define_method :"blob" do |*args|
+            bucket = args[0]["bucket"]
+            blob = args[0][:id]
+            request(:get, "#{entry_points[:buckets]}/#{bucket}/#{blob}") do |response|
+              base_object("blob", response)
+            end
         end
       end
+
+      end
     end
 
     def base_object_collection(model, response)
@@ -179,7 +190,6 @@ module DeltaCloud
 
     # Add default attributes [id and href] to class
     def base_object(model, response)
-
       c = DeltaCloud.add_class("#{model}", DeltaCloud::guess_model_type(response))
       xml_to_class(c, Nokogiri::XML(response).xpath("#{model.to_s.singularize}").first)
     end
@@ -201,9 +211,10 @@ module DeltaCloud
 
       # Traverse across XML document and deal with elements
       item.xpath('./*').each do |attribute|
+
         # Do a link for elements which are links to other REST models
         if self.entry_points.keys.include?(:"#{attribute.name}s")
-          obj.add_link!(attribute.name, attribute['id']) && next
+          obj.add_link!(attribute.name, attribute['id']) && next unless (attribute.name == 'bucket' && item.name == 'blob')
         end
 
         # Do a HWP property for hardware profile properties
@@ -242,7 +253,6 @@ module DeltaCloud
         # Anything else is treaten as text object
         obj.add_text!(attribute.name, attribute.text.convert)
       end
-
       return obj
     end
 
diff --git a/client/lib/plain_formatter.rb b/client/lib/plain_formatter.rb
index ca29f3d..45e7310 100644
--- a/client/lib/plain_formatter.rb
+++ b/client/lib/plain_formatter.rb
@@ -96,11 +96,24 @@ module DeltaCloud
 
       class Bucket < Base
         def format
-          sprintf("%-50s | %-50s | %-10s | %-1000s",
-          @obj.id[0,50],
-          @obj.name[0,50],
-          @obj.size ? @obj.size.to_s[0,10] : "",
-          @obj.blob_list ? @obj.blob_list[0,1000] : ""
+          sprintf("%-s | %-s | %-s | %-s",
+          @obj.id,
+          @obj.name,
+          @obj.size ? @obj.size : "0",
+          @obj.blob_list ? @obj.blob_list : ""
+          )
+        end
+      end
+
+      class Blob < Base
+        def format
+          sprintf("%-s | %-s | %-d | %-s | %-s | %-s " ,
+          @obj.id,
+          @obj.bucket,
+          @obj.content_length,
+          @obj.content_type,
+          @obj.last_modified,
+          @obj.user_metadata
           )
         end
       end
-- 
1.7.3.4


Re: Etc fixes for ruby client + buckets/blobs from deltacloudc

Posted by Michal Fojtik <mf...@redhat.com>.
On 02/03/11 22:04 +0200, marios@redhat.com wrote:
>
>Updates the ruby client with basic functionality for buckets and blobs. Right now implements index and show for buckets and show for blobs (index doesn't apply - blobs aren't a 'proper' collection - you get an 'index' of blobs when you get a specific bucket). Will work on adding create/delete for these later (Streaming PUT takes priority right now). Patch 2/5 is a fix for Jira issue #20. Patch 5/5 is from Michal (patch from a while back that was forgotten on the list).

ACK to whole patchset. Applied cleanely, without whitespace warnings.
Client unit tests are happy with this changes.
Btw. Marios, can you do additionaly some unit testing for buckets/blobs
in client?

Otherwise, code looks safe and clean to me.

   -- Michal

> deltacloudc buckets -l  -u 'http://user:pass@localhost:3001/api'
> deltacloudc buckets index -u 'http://user:pass@localhost:3001/api'
> deltacloudc buckets show -i BUCKETNAME -u 'http://user:pass@localhost:3001/api'
> deltacloudc blob -l -u 'http://user:pass@localhost:3001/api'
> deltacloudc blob show -b BUCKETNAME -i BLOBNAME -u 'http://user:pass@localhost:3001/api'

All commands above tested and worked just fine.

   -- Michal

-- 
--------------------------------------------------------
Michal Fojtik, mfojtik@redhat.com
Deltacloud API: http://deltacloud.org
--------------------------------------------------------

[PATCH 3/5] Fix display of blobs in a bucket (blob_list) when bucket requested by id

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

---
 client/lib/base_object.rb     |   18 ++++++++++++++++++
 client/lib/deltacloud.rb      |    7 ++++++-
 client/lib/plain_formatter.rb |   11 +++++++++++
 3 files changed, 35 insertions(+), 1 deletions(-)

diff --git a/client/lib/base_object.rb b/client/lib/base_object.rb
index 06db703..43d7ee5 100644
--- a/client/lib/base_object.rb
+++ b/client/lib/base_object.rb
@@ -100,6 +100,7 @@ module DeltaCloud
           when :text then return m[:value]
           when :property then return m[:property]
           when :collection then return m[:values]
+          when :list then return m[:value].join(", ")
           else raise NoHandlerForMethod
         end
       end
@@ -115,6 +116,23 @@ module DeltaCloud
         end
       end
 
+      # This method adds blobs to the blob_list property
+      # of a bucket
+      def add_blob!(blob_name)
+        if @blob_list.nil?
+          @blob_list = [blob_name]
+          @objects << {
+            :type => :list,
+            :method_name => "blob_list",
+            :value => @blob_list
+          }
+        else
+          @blob_list << blob_name
+          current = search_for_method('blob_list')
+          current[:value] = @blob_list
+        end
+      end
+
       private
 
       def search_for_method(name)
diff --git a/client/lib/deltacloud.rb b/client/lib/deltacloud.rb
index b1b3e6b..292305d 100644
--- a/client/lib/deltacloud.rb
+++ b/client/lib/deltacloud.rb
@@ -179,6 +179,7 @@ module DeltaCloud
 
     # Add default attributes [id and href] to class
     def base_object(model, response)
+
       c = DeltaCloud.add_class("#{model}", DeltaCloud::guess_model_type(response))
       xml_to_class(c, Nokogiri::XML(response).xpath("#{model.to_s.singularize}").first)
     end
@@ -200,7 +201,6 @@ module DeltaCloud
 
       # Traverse across XML document and deal with elements
       item.xpath('./*').each do |attribute|
-
         # Do a link for elements which are links to other REST models
         if self.entry_points.keys.include?(:"#{attribute.name}s")
           obj.add_link!(attribute.name, attribute['id']) && next
@@ -234,6 +234,11 @@ module DeltaCloud
           obj.add_collection!(attribute.name, (attribute/'*').collect { |value| value.text }) && next
         end
 
+        #deal with blobs for buckets
+        if(attribute.name == 'blob')
+          obj.add_blob!(attribute.attributes['id'].value) && next
+        end
+
         # Anything else is treaten as text object
         obj.add_text!(attribute.name, attribute.text.convert)
       end
diff --git a/client/lib/plain_formatter.rb b/client/lib/plain_formatter.rb
index 89e92f9..ca29f3d 100644
--- a/client/lib/plain_formatter.rb
+++ b/client/lib/plain_formatter.rb
@@ -94,6 +94,17 @@ module DeltaCloud
         end
       end
 
+      class Bucket < Base
+        def format
+          sprintf("%-50s | %-50s | %-10s | %-1000s",
+          @obj.id[0,50],
+          @obj.name[0,50],
+          @obj.size ? @obj.size.to_s[0,10] : "",
+          @obj.blob_list ? @obj.blob_list[0,1000] : ""
+          )
+        end
+      end
+
     end
 
     def format(obj)
-- 
1.7.3.4


[PATCH 5/5] Fix typo in link_to_documentation (/api/docs for html interface)

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

---
 .../lib/deltacloud/helpers/application_helper.rb   |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/server/lib/deltacloud/helpers/application_helper.rb b/server/lib/deltacloud/helpers/application_helper.rb
index 191a0c9..124c56c 100644
--- a/server/lib/deltacloud/helpers/application_helper.rb
+++ b/server/lib/deltacloud/helpers/application_helper.rb
@@ -170,7 +170,7 @@ module ApplicationHelper
   def link_to_documentation
     return '' unless request.env['REQUEST_URI']
     uri = request.env['REQUEST_URI'].dup
-    uri.gsub!('/api/', '/api/docs/')
+    uri.gsub!('/api', '/api/docs/') unless uri.include?("docs") #i.e. if already serving under /api/docs, leave it be
     '<a href="%s">[ Documentation ]</a>' % uri
   end
 
-- 
1.7.3.4