uricp 0.0.1
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.
- data/.gitignore +6 -0
 - data/Gemfile +4 -0
 - data/LICENSE.txt +16 -0
 - data/README.md +31 -0
 - data/README.rdoc +23 -0
 - data/Rakefile +66 -0
 - data/bin/segment_upload +66 -0
 - data/bin/uricp +103 -0
 - data/features/auth_download.feature +106 -0
 - data/features/cacheable_from_uri.feature +71 -0
 - data/features/documented_options.feature +73 -0
 - data/features/remote_upload.feature +49 -0
 - data/features/segmented_upload.feature +27 -0
 - data/features/segmented_upload_options.feature +65 -0
 - data/features/step_definitions/orbit_steps.rb +194 -0
 - data/features/step_definitions/uricp_steps.rb +34 -0
 - data/features/support/env.rb +20 -0
 - data/features/userid_download.feature +21 -0
 - data/lib/segment_upload.rb +38 -0
 - data/lib/uricp/curl_primitives.rb +45 -0
 - data/lib/uricp/orbit_auth.rb +59 -0
 - data/lib/uricp/segmenter.rb +88 -0
 - data/lib/uricp/strategy/cache_common.rb +48 -0
 - data/lib/uricp/strategy/cached_get.rb +48 -0
 - data/lib/uricp/strategy/cleaner.rb +28 -0
 - data/lib/uricp/strategy/common.rb +108 -0
 - data/lib/uricp/strategy/local_convert.rb +36 -0
 - data/lib/uricp/strategy/local_convert_common.rb +29 -0
 - data/lib/uricp/strategy/local_link.rb +31 -0
 - data/lib/uricp/strategy/piped_cache.rb +40 -0
 - data/lib/uricp/strategy/piped_cache_convert.rb +45 -0
 - data/lib/uricp/strategy/piped_compress.rb +30 -0
 - data/lib/uricp/strategy/piped_decompress.rb +35 -0
 - data/lib/uricp/strategy/piped_local_compress.rb +30 -0
 - data/lib/uricp/strategy/piped_local_get.rb +29 -0
 - data/lib/uricp/strategy/piped_local_put.rb +29 -0
 - data/lib/uricp/strategy/piped_remote_get.rb +38 -0
 - data/lib/uricp/strategy/pruner.rb +22 -0
 - data/lib/uricp/strategy/remote_put.rb +47 -0
 - data/lib/uricp/strategy/segmented_remote_put.rb +52 -0
 - data/lib/uricp/strategy/sweeper.rb +28 -0
 - data/lib/uricp/uri_strategy.rb +47 -0
 - data/lib/uricp/version.rb +4 -0
 - data/lib/uricp.rb +56 -0
 - data/spec/something_spec.rb +5 -0
 - data/uricp.gemspec +32 -0
 - metadata +281 -0
 
| 
         @@ -0,0 +1,65 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Feature: Documented Help for Segmented Upload
         
     | 
| 
      
 2 
     | 
    
         
            +
              In order to work out what segment_upload does
         
     | 
| 
      
 3 
     | 
    
         
            +
              As a command line user
         
     | 
| 
      
 4 
     | 
    
         
            +
              I want to get detailed usage information
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              Scenario: Basic UI
         
     | 
| 
      
 7 
     | 
    
         
            +
                When I get help for "segment_upload"
         
     | 
| 
      
 8 
     | 
    
         
            +
                Then the exit status should be 0
         
     | 
| 
      
 9 
     | 
    
         
            +
                And the banner should include the version
         
     | 
| 
      
 10 
     | 
    
         
            +
                And the banner should document that this app takes options
         
     | 
| 
      
 11 
     | 
    
         
            +
                And the following options should be documented:
         
     | 
| 
      
 12 
     | 
    
         
            +
                  |--version|
         
     | 
| 
      
 13 
     | 
    
         
            +
                  |--auth-token|
         
     | 
| 
      
 14 
     | 
    
         
            +
                  |--auth-user|
         
     | 
| 
      
 15 
     | 
    
         
            +
                  |--auth-key|
         
     | 
| 
      
 16 
     | 
    
         
            +
                  |--from|
         
     | 
| 
      
 17 
     | 
    
         
            +
                  |--segment-size|
         
     | 
| 
      
 18 
     | 
    
         
            +
                And the option "dry-run" should be documented which is negatable
         
     | 
| 
      
 19 
     | 
    
         
            +
                And the banner should document that this app's arguments are:
         
     | 
| 
      
 20 
     | 
    
         
            +
                  |to_uri|which is required|
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              Scenario: auth token and auth-user are mutually exclusive
         
     | 
| 
      
 23 
     | 
    
         
            +
                When I run `segment_upload --auth-token abcdef --auth-user xyz --auth-key abc http://source file:///target`
         
     | 
| 
      
 24 
     | 
    
         
            +
                Then the exit status should not be 0
         
     | 
| 
      
 25 
     | 
    
         
            +
                And the stderr should contain "needless argument"
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
              Scenario: auth user needs auth key
         
     | 
| 
      
 28 
     | 
    
         
            +
                When I run `segment_upload --auth-user xyz http://source`
         
     | 
| 
      
 29 
     | 
    
         
            +
                Then the exit status should not be 0
         
     | 
| 
      
 30 
     | 
    
         
            +
                And the stderr should contain "missing argument"
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
              Scenario: auth key needs auth user
         
     | 
| 
      
 33 
     | 
    
         
            +
                When I run `segment_upload --auth-user xyz http://source`
         
     | 
| 
      
 34 
     | 
    
         
            +
                Then the exit status should not be 0
         
     | 
| 
      
 35 
     | 
    
         
            +
                And the stderr should contain "missing argument"
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
              Scenario: needs some sort of authentication
         
     | 
| 
      
 38 
     | 
    
         
            +
                When I run `segment_upload http://source`
         
     | 
| 
      
 39 
     | 
    
         
            +
                Then the exit status should not be 0
         
     | 
| 
      
 40 
     | 
    
         
            +
                And the stderr should contain "missing argument"
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
              Scenario: no http should fail
         
     | 
| 
      
 43 
     | 
    
         
            +
                When I run `segment_upload ftp://source`
         
     | 
| 
      
 44 
     | 
    
         
            +
                Then the exit status should not be 0
         
     | 
| 
      
 45 
     | 
    
         
            +
                And the stderr should contain "unsupported url"
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              Scenario: authentication with normal http should fail
         
     | 
| 
      
 48 
     | 
    
         
            +
                When I run `segment_upload --auth-user cli-xxxxx --auth-key fred https://foss-lab-manual.googlecode.com/files`
         
     | 
| 
      
 49 
     | 
    
         
            +
                Then the exit status should not be 0
         
     | 
| 
      
 50 
     | 
    
         
            +
                And the stderr should contain "Cannot authenticate"
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
              Scenario: bad authentication should fail against orbit
         
     | 
| 
      
 53 
     | 
    
         
            +
                When I run `segment_upload --auth-user cli-xxxxx --auth-key fred https://orbit.brightbox.com/v1/acc-xxxxx/test/test.img`
         
     | 
| 
      
 54 
     | 
    
         
            +
                Then the exit status should not be 0
         
     | 
| 
      
 55 
     | 
    
         
            +
                And the stderr should contain "Cannot authenticate"
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              Scenario: should not accept plain number for segment size
         
     | 
| 
      
 58 
     | 
    
         
            +
                When I run `segment_upload --segment-size 100 --auth-token abcdef https:///orbit.brightbox.com/v1/acc-xxxxx/test/test.img`
         
     | 
| 
      
 59 
     | 
    
         
            +
                Then the exit status should not be 0
         
     | 
| 
      
 60 
     | 
    
         
            +
                And the stderr should contain "Unparseable filesize"
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              Scenario: should trap attempts to open a command
         
     | 
| 
      
 63 
     | 
    
         
            +
                When I run `segment_upload --segment-size 100MB --auth-token abcdef --from ' |fred' https:///orbit.brightbox.com/v1/acc-xxxxx/test/test.img`
         
     | 
| 
      
 64 
     | 
    
         
            +
                Then the exit status should not be 0
         
     | 
| 
      
 65 
     | 
    
         
            +
                And the stderr should contain "invalid argument"
         
     | 
| 
         @@ -0,0 +1,194 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'inifile'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            def obtain_credentials
         
     | 
| 
      
 4 
     | 
    
         
            +
              config = IniFile.load(ENV['HOME']+ '/.brightbox/config') ||
         
     | 
| 
      
 5 
     | 
    
         
            +
                raise(RuntimeError, "No brightbox config in home directory. Can't obtain auth token for tests")
         
     | 
| 
      
 6 
     | 
    
         
            +
              section = config.sections.first
         
     | 
| 
      
 7 
     | 
    
         
            +
              [config[section]['client_id'], config[section]['secret']]
         
     | 
| 
      
 8 
     | 
    
         
            +
            end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            def fetch_orbit_token
         
     | 
| 
      
 11 
     | 
    
         
            +
              @clientid, @key = obtain_credentials
         
     | 
| 
      
 12 
     | 
    
         
            +
              cmd = "curl -I https://orbit.brightbox.com/v1 -H 'X-Auth-User: #{@clientid}' -H 'X-Auth-Key: #{@key}'"
         
     | 
| 
      
 13 
     | 
    
         
            +
              run_simple(unescape(cmd))
         
     | 
| 
      
 14 
     | 
    
         
            +
              stdout_from(cmd).each_line do |line|
         
     | 
| 
      
 15 
     | 
    
         
            +
                key, value = line.strip.split(/\s*:\s*/,2)
         
     | 
| 
      
 16 
     | 
    
         
            +
                @current_auth_token = value if key == 'X-Auth-Token'
         
     | 
| 
      
 17 
     | 
    
         
            +
                @current_storage_url = value if key == 'X-Storage-Url'
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            def create_download_file(filetype, name, container)
         
     | 
| 
      
 22 
     | 
    
         
            +
              upload_url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 23 
     | 
    
         
            +
              tempfile = '/tmp/fred55322'
         
     | 
| 
      
 24 
     | 
    
         
            +
              FileUtils.rm_f(Dir.glob(tempfile+'*'))
         
     | 
| 
      
 25 
     | 
    
         
            +
              format = 'raw'
         
     | 
| 
      
 26 
     | 
    
         
            +
              format = 'qcow2' if filetype == 'qcow2'
         
     | 
| 
      
 27 
     | 
    
         
            +
              cmd = "qemu-img create -q -f #{format} #{tempfile} 1G"
         
     | 
| 
      
 28 
     | 
    
         
            +
              system(unescape(cmd))
         
     | 
| 
      
 29 
     | 
    
         
            +
              cmd = "cat #{tempfile}"
         
     | 
| 
      
 30 
     | 
    
         
            +
              if filetype == 'lz4'
         
     | 
| 
      
 31 
     | 
    
         
            +
                cmd += " | lz4c "
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
              cmd += "| curl --silent --fail -T - -H 'X-Auth-Token: #{@current_auth_token}' #{upload_url} "
         
     | 
| 
      
 34 
     | 
    
         
            +
              system(unescape(cmd))
         
     | 
| 
      
 35 
     | 
    
         
            +
            end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            def create_container(name)
         
     | 
| 
      
 38 
     | 
    
         
            +
              url = File.join(@current_storage_url, name)
         
     | 
| 
      
 39 
     | 
    
         
            +
              cmd = "curl --silent --fail -I -H 'X-Auth-Token: #{@current_auth_token}' #{url}>/dev/null || curl --silent --fail -I -H 'X-Auth-Token: #{@current_auth_token}' #{url} -X PUT"
         
     | 
| 
      
 40 
     | 
    
         
            +
              system(unescape(cmd))
         
     | 
| 
      
 41 
     | 
    
         
            +
            end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
            Given(/^a container called "([^"]*)"$/) do |container|
         
     | 
| 
      
 44 
     | 
    
         
            +
              create_container(container)
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
            Given(/^a cache of "(.*?)" from container "(.*?)" at "(.*?)"$/) do |name, container, cache|
         
     | 
| 
      
 48 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 49 
     | 
    
         
            +
              target = File.join(cache, 'cache', name)
         
     | 
| 
      
 50 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 51 
     | 
    
         
            +
                When I successfully run `curl --fail --silent -o #{target} -H 'X-Auth-Token: #{@current_auth_token}' #{url}`
         
     | 
| 
      
 52 
     | 
    
         
            +
              }
         
     | 
| 
      
 53 
     | 
    
         
            +
            end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            Given /^a (\d+) byte lz4 file named "([^"]*)"$/ do |file_size, file_name|
         
     | 
| 
      
 56 
     | 
    
         
            +
              write_fixed_size_file(file_name, file_size.to_i)
         
     | 
| 
      
 57 
     | 
    
         
            +
              system("lz4c #{file_name}")
         
     | 
| 
      
 58 
     | 
    
         
            +
              File.rename file_name+'.lz4', file_name
         
     | 
| 
      
 59 
     | 
    
         
            +
            end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            When(/^I store "([^"]*)" into container "([^"]*)" from "([^"]*)"$/) do |name, container, source|
         
     | 
| 
      
 62 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 63 
     | 
    
         
            +
                When I store "#{name}" with options "" into container "#{container}" from "#{source}"
         
     | 
| 
      
 64 
     | 
    
         
            +
              }
         
     | 
| 
      
 65 
     | 
    
         
            +
            end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
            When(/^I retrieve "([^"]*)" with options "([^"]*)" from container "([^"]*)" into "([^"]*)"$/) do |name, runoptions, container, target|
         
     | 
| 
      
 68 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 69 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 70 
     | 
    
         
            +
                When I successfully run `uricp #{runoptions} --auth-token #{@current_auth_token} #{url} #{target}`
         
     | 
| 
      
 71 
     | 
    
         
            +
              }
         
     | 
| 
      
 72 
     | 
    
         
            +
            end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
            When(/^I retrieve "([^"]*)" from container "([^"]*)" into "([^"]*)" with a userid$/) do |name, container, target|
         
     | 
| 
      
 75 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 76 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 77 
     | 
    
         
            +
                When I successfully run `uricp --auth-user '#{@clientid}' --auth-key '#{@key}' #{url} #{target}`
         
     | 
| 
      
 78 
     | 
    
         
            +
              }
         
     | 
| 
      
 79 
     | 
    
         
            +
            end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
            When(/^I store "([^"]*)" into container "([^"]*)" from "([^"]*)" with a userid$/) do |name, container, source|
         
     | 
| 
      
 82 
     | 
    
         
            +
              @current_container = container
         
     | 
| 
      
 83 
     | 
    
         
            +
              @current_name = name
         
     | 
| 
      
 84 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 85 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 86 
     | 
    
         
            +
                When I successfully run `uricp --auth-user '#{@clientid}' --auth-key '#{@key}' #{source} #{url}`
         
     | 
| 
      
 87 
     | 
    
         
            +
              }
         
     | 
| 
      
 88 
     | 
    
         
            +
            end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
            When(/^I retrieve "([^"]*)" from container "([^"]*)" into "([^"]*)"$/) do |name, container, target|
         
     | 
| 
      
 91 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 92 
     | 
    
         
            +
                When I retrieve "#{name}" with options "" from container "#{container}" into "#{target}"
         
     | 
| 
      
 93 
     | 
    
         
            +
              }
         
     | 
| 
      
 94 
     | 
    
         
            +
            end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
            When(/^I store "([^"]*)" with options "([^"]*)" into container "([^"]*)" from "([^"]*)"$/) do |name, runoptions, container, source|
         
     | 
| 
      
 97 
     | 
    
         
            +
              @current_container = container
         
     | 
| 
      
 98 
     | 
    
         
            +
              @current_name = name
         
     | 
| 
      
 99 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 100 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 101 
     | 
    
         
            +
                When I successfully run `uricp #{runoptions} --auth-token #{@current_auth_token} #{source} #{url}`
         
     | 
| 
      
 102 
     | 
    
         
            +
              }
         
     | 
| 
      
 103 
     | 
    
         
            +
            end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
            When(/^I store "([^"]*)" with segment size "([^"]*)" into container "([^"]*)" from "([^"]*)" with a userid$/) do |name, segment_size, container, source|
         
     | 
| 
      
 106 
     | 
    
         
            +
              @current_container = container
         
     | 
| 
      
 107 
     | 
    
         
            +
              @current_name = name
         
     | 
| 
      
 108 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 109 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 110 
     | 
    
         
            +
                When I successfully run `uricp --auth-user '#{@clientid}' --auth-key '#{@key}' --segment-size '#{segment_size}' #{source} #{url}`
         
     | 
| 
      
 111 
     | 
    
         
            +
              }
         
     | 
| 
      
 112 
     | 
    
         
            +
            end
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
            When(/^I store "([^"]*)" with segment size "([^"]*)" and options "([^"]*)" into container "([^"]*)" from "([^"]*)" with a userid$/) do |name, segment_size, runoptions, container, source|
         
     | 
| 
      
 115 
     | 
    
         
            +
              @current_container = container
         
     | 
| 
      
 116 
     | 
    
         
            +
              @current_name = name
         
     | 
| 
      
 117 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 118 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 119 
     | 
    
         
            +
                When I successfully run `uricp #{runoptions} --auth-user '#{@clientid}' --auth-key '#{@key}' --segment-size '#{segment_size}' #{source} #{url}`
         
     | 
| 
      
 120 
     | 
    
         
            +
              }
         
     | 
| 
      
 121 
     | 
    
         
            +
            end
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
            When(/^I upload "([^"]*)" with segment size "([^"]*)" into container "([^"]*)" from "([^"]*)"$/) do |name, segment_size, container, source|
         
     | 
| 
      
 124 
     | 
    
         
            +
              @current_container = container
         
     | 
| 
      
 125 
     | 
    
         
            +
              @current_name = name
         
     | 
| 
      
 126 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 127 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 128 
     | 
    
         
            +
                When I successfully run `segment_upload --from #{source} --auth-token #{@current_auth_token} --segment-size #{segment_size} #{url}`
         
     | 
| 
      
 129 
     | 
    
         
            +
              }
         
     | 
| 
      
 130 
     | 
    
         
            +
            end
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
            When(/^I upload "([^"]*)" with segment size "([^"]*)" into container "([^"]*)" from "([^"]*)" as a stream$/) do |name, segment_size, container, source|
         
     | 
| 
      
 133 
     | 
    
         
            +
              @current_container = container
         
     | 
| 
      
 134 
     | 
    
         
            +
              @current_name = name
         
     | 
| 
      
 135 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 136 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 137 
     | 
    
         
            +
                When I successfully run `sh -c 'cat #{source} | segment_upload --auth-token #{@current_auth_token} --segment-size #{segment_size} #{url}'`
         
     | 
| 
      
 138 
     | 
    
         
            +
              }
         
     | 
| 
      
 139 
     | 
    
         
            +
            end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
            Then(/^a (\d+) byte entry should exist in container "(.*?)" called "(.*?)"$/) do |size, container, name|
         
     | 
| 
      
 142 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 143 
     | 
    
         
            +
              command = "curl -I --fail --silent -H 'X-Auth-Token:#{@current_auth_token}' #{url}"
         
     | 
| 
      
 144 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 145 
     | 
    
         
            +
                When I successfully run `#{command}`
         
     | 
| 
      
 146 
     | 
    
         
            +
                Then the output should match /\\sContent-Length: #{size}\\s/
         
     | 
| 
      
 147 
     | 
    
         
            +
              }
         
     | 
| 
      
 148 
     | 
    
         
            +
            end
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
            Then(/^the container "(.*?)" should contain (\d+) entries$/) do |container, quantity|
         
     | 
| 
      
 151 
     | 
    
         
            +
              url = File.join(@current_storage_url, container)
         
     | 
| 
      
 152 
     | 
    
         
            +
              command = "curl -I --fail --silent -H 'X-Auth-Token:#{@current_auth_token}' #{url}"
         
     | 
| 
      
 153 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 154 
     | 
    
         
            +
                When I successfully run `#{command}`
         
     | 
| 
      
 155 
     | 
    
         
            +
                Then the output should match /\\sX-Container-Object-Count: #{quantity}\\s/
         
     | 
| 
      
 156 
     | 
    
         
            +
              }
         
     | 
| 
      
 157 
     | 
    
         
            +
            end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
            Then(/^an lz4 compressed entry should exist in container "(.*?)" called "(.*?)"$/) do |container, name|
         
     | 
| 
      
 160 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 161 
     | 
    
         
            +
              cmd="curl -r 0-3 --fail --silent -H X-Auth-Token:#{@current_auth_token} #{url}"
         
     | 
| 
      
 162 
     | 
    
         
            +
              run_simple(unescape(cmd))
         
     | 
| 
      
 163 
     | 
    
         
            +
              assert_exact_output([0x184D2204].pack('V'), stdout_from(cmd))
         
     | 
| 
      
 164 
     | 
    
         
            +
            end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
            Then(/^a qcow2 entry should exist in container "(.*?)" called "(.*?)"$/) do |container, name|
         
     | 
| 
      
 167 
     | 
    
         
            +
              url = File.join(@current_storage_url, container, name)
         
     | 
| 
      
 168 
     | 
    
         
            +
              cmd="curl -r 0-3 --fail --silent -H X-Auth-Token:#{@current_auth_token} #{url}"
         
     | 
| 
      
 169 
     | 
    
         
            +
              run_simple(unescape(cmd))
         
     | 
| 
      
 170 
     | 
    
         
            +
              assert_exact_output(['QFI',0xfb].pack('a3C'), stdout_from(cmd))
         
     | 
| 
      
 171 
     | 
    
         
            +
            end
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
            Before('@orbit') do
         
     | 
| 
      
 174 
     | 
    
         
            +
              fetch_orbit_token
         
     | 
| 
      
 175 
     | 
    
         
            +
            end
         
     | 
| 
      
 176 
     | 
    
         
            +
             
     | 
| 
      
 177 
     | 
    
         
            +
            Before('@orbitdownloads') do
         
     | 
| 
      
 178 
     | 
    
         
            +
              $orbit_setup ||= false
         
     | 
| 
      
 179 
     | 
    
         
            +
              unless $orbit_setup
         
     | 
| 
      
 180 
     | 
    
         
            +
                create_container('test')
         
     | 
| 
      
 181 
     | 
    
         
            +
                create_download_file('qcow2', 'img-qcow2', 'test')
         
     | 
| 
      
 182 
     | 
    
         
            +
                create_download_file('lz4', 'img-lz4cy', 'test')
         
     | 
| 
      
 183 
     | 
    
         
            +
                $orbit_setup = true
         
     | 
| 
      
 184 
     | 
    
         
            +
              end
         
     | 
| 
      
 185 
     | 
    
         
            +
            end
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
      
 187 
     | 
    
         
            +
            After('@orbit') do
         
     | 
| 
      
 188 
     | 
    
         
            +
              if @current_auth_token && @current_container
         
     | 
| 
      
 189 
     | 
    
         
            +
                steps %{
         
     | 
| 
      
 190 
     | 
    
         
            +
                  When I successfully run `swift --os-storage-url=#{@current_storage_url} --os-auth-token=#{@current_auth_token} delete #{@current_container} #{@current_name}` 
         
     | 
| 
      
 191 
     | 
    
         
            +
                }
         
     | 
| 
      
 192 
     | 
    
         
            +
                @current_container = @current_name = nil
         
     | 
| 
      
 193 
     | 
    
         
            +
              end
         
     | 
| 
      
 194 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,34 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Then(/^the file named "(.*?)" should have a file format of "(.*?)"$/) do |filename, format|
         
     | 
| 
      
 2 
     | 
    
         
            +
              case format
         
     | 
| 
      
 3 
     | 
    
         
            +
              when 'lz4'
         
     | 
| 
      
 4 
     | 
    
         
            +
                assert_exact_output([0x184D2204].pack('V'),
         
     | 
| 
      
 5 
     | 
    
         
            +
                  File.open(filename, 'rb') {|f| f.read(4) })
         
     | 
| 
      
 6 
     | 
    
         
            +
              when 'qcow2v3', 'qcow3'
         
     | 
| 
      
 7 
     | 
    
         
            +
                steps %{
         
     | 
| 
      
 8 
     | 
    
         
            +
                  When I successfully run `qemu-img info #{filename}`
         
     | 
| 
      
 9 
     | 
    
         
            +
                  Then the output from "qemu-img info #{filename}" should contain "file format: qcow2"
         
     | 
| 
      
 10 
     | 
    
         
            +
                  And the output from "qemu-img info #{filename}" should contain "compat: 1.1"
         
     | 
| 
      
 11 
     | 
    
         
            +
                }
         
     | 
| 
      
 12 
     | 
    
         
            +
              else
         
     | 
| 
      
 13 
     | 
    
         
            +
                assert_no_partial_output([0x184D2204].pack('V'),
         
     | 
| 
      
 14 
     | 
    
         
            +
                  File.open(filename, 'rb') {|f| f.read(4) })
         
     | 
| 
      
 15 
     | 
    
         
            +
                steps(%{
         
     | 
| 
      
 16 
     | 
    
         
            +
                  When I successfully run `qemu-img info #{filename}`
         
     | 
| 
      
 17 
     | 
    
         
            +
                  Then the output from "qemu-img info #{filename}" should contain "file format: #{format}"
         
     | 
| 
      
 18 
     | 
    
         
            +
                  And the output from "qemu-img info #{filename}" should not contain "compat: 1.1"
         
     | 
| 
      
 19 
     | 
    
         
            +
                })
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            Given(/^a correctly initialised cache at "(.*?)"$/) do |basedir|
         
     | 
| 
      
 24 
     | 
    
         
            +
              steps %{
         
     | 
| 
      
 25 
     | 
    
         
            +
                Given a directory named "#{basedir}/cache"
         
     | 
| 
      
 26 
     | 
    
         
            +
                And a directory named "#{basedir}/temp"
         
     | 
| 
      
 27 
     | 
    
         
            +
              }
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            Given(/^an empty directory named "([^"]*)"$/) do |dir_name|
         
     | 
| 
      
 31 
     | 
    
         
            +
              create_dir(dir_name)
         
     | 
| 
      
 32 
     | 
    
         
            +
              FileUtils.rm_rf(File.join(dir_name, '.'), :secure => true)
         
     | 
| 
      
 33 
     | 
    
         
            +
            end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'aruba/cucumber'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'methadone/cucumber'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
         
     | 
| 
      
 6 
     | 
    
         
            +
            LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            Before do
         
     | 
| 
      
 9 
     | 
    
         
            +
              # Using "announce" causes massive warnings on 1.9.2
         
     | 
| 
      
 10 
     | 
    
         
            +
              @puts = true
         
     | 
| 
      
 11 
     | 
    
         
            +
              @original_rubylib = ENV['RUBYLIB']
         
     | 
| 
      
 12 
     | 
    
         
            +
              ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            After do
         
     | 
| 
      
 16 
     | 
    
         
            +
              ENV['RUBYLIB'] = @original_rubylib
         
     | 
| 
      
 17 
     | 
    
         
            +
              
         
     | 
| 
      
 18 
     | 
    
         
            +
              FileUtils.rm_rf(Dir.glob('/tmp/uricp/*'))
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            @orbit
         
     | 
| 
      
 2 
     | 
    
         
            +
            @orbitdownloads
         
     | 
| 
      
 3 
     | 
    
         
            +
            Feature: User authenticated download of images from orbit
         
     | 
| 
      
 4 
     | 
    
         
            +
              In order to download images from orbit
         
     | 
| 
      
 5 
     | 
    
         
            +
              As a command line user
         
     | 
| 
      
 6 
     | 
    
         
            +
              I want to retrieve the URI via an optional cache and copy correctly to target in the right format using a user id directly
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              Background: 
         
     | 
| 
      
 9 
     | 
    
         
            +
                Given an empty directory named "/tmp/uricp"
         
     | 
| 
      
 10 
     | 
    
         
            +
                And the default aruba timeout is 15 seconds
         
     | 
| 
      
 11 
     | 
    
         
            +
                And a container called "test"
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              Scenario: qcow download no conversion, no cache with userid
         
     | 
| 
      
 14 
     | 
    
         
            +
                When I retrieve "img-qcow2" from container "test" into "file:///tmp/uricp/srv-test1" with a userid
         
     | 
| 
      
 15 
     | 
    
         
            +
                Then a file named "/tmp/uricp/srv-test1" should exist
         
     | 
| 
      
 16 
     | 
    
         
            +
                And the file named "/tmp/uricp/srv-test1" should have a file format of "qcow2v3"
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              Scenario: direct upload with userid
         
     | 
| 
      
 19 
     | 
    
         
            +
                Given a 102400 byte file named "/tmp/uricp/srv-testy"
         
     | 
| 
      
 20 
     | 
    
         
            +
                When I store "img-usrid" into container "test" from "file:///tmp/uricp/srv-testy" with a userid
         
     | 
| 
      
 21 
     | 
    
         
            +
                Then a 102400 byte entry should exist in container "test" called "img-usrid"
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "uricp/version"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "uricp/curl_primitives"
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "uricp/orbit_auth"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "uricp/segmenter"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Uricp
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              UnsupportedURLtype = Class.new(ArgumentError)
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            #Monkey patch a copy_stream facility in using 'sendfile'
         
     | 
| 
      
 13 
     | 
    
         
            +
            unless IO.respond_to? :copy_stream
         
     | 
| 
      
 14 
     | 
    
         
            +
              require 'sendfile'
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              def IO.copy_stream(src, dst, copy_length=nil, offset=nil)
         
     | 
| 
      
 17 
     | 
    
         
            +
                unless src.stat.pipe?
         
     | 
| 
      
 18 
     | 
    
         
            +
                  current_pos = src.pos
         
     | 
| 
      
 19 
     | 
    
         
            +
                  count = dst.sendfile(src, offset || current_pos, copy_length)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  src.seek(count, IO::SEEK_CUR)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  return count
         
     | 
| 
      
 22 
     | 
    
         
            +
                else
         
     | 
| 
      
 23 
     | 
    
         
            +
                  amount = copy_length.to_i
         
     | 
| 
      
 24 
     | 
    
         
            +
                  buf_size = 2**16
         
     | 
| 
      
 25 
     | 
    
         
            +
                  buffer=""
         
     | 
| 
      
 26 
     | 
    
         
            +
                  while amount > 0 do
         
     | 
| 
      
 27 
     | 
    
         
            +
            	src.read(buf_size, buffer)
         
     | 
| 
      
 28 
     | 
    
         
            +
            	amount_read = buffer.length
         
     | 
| 
      
 29 
     | 
    
         
            +
            	dst.write(buffer)
         
     | 
| 
      
 30 
     | 
    
         
            +
            	amount -= amount_read
         
     | 
| 
      
 31 
     | 
    
         
            +
            	break if src.eof?
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  return copy_length.to_i - amount
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              rescue EOFError
         
     | 
| 
      
 36 
     | 
    
         
            +
                0
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,45 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Uricp::CurlPrimitives
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
                attr_reader :options
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
                def from
         
     | 
| 
      
 6 
     | 
    
         
            +
                  options['from_uri']
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def from=(target)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  options['from_uri'] = target
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                def to
         
     | 
| 
      
 14 
     | 
    
         
            +
                  options['to_uri']
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def to=(target)
         
     | 
| 
      
 18 
     | 
    
         
            +
                  options['to_uri'] = target
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              
         
     | 
| 
      
 21 
     | 
    
         
            +
                def curl_command
         
     | 
| 
      
 22 
     | 
    
         
            +
                  "curl --fail --silent"
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                def authentication
         
     | 
| 
      
 26 
     | 
    
         
            +
                  "-H X-Auth-Token:#{options['auth-token']}" if http_authentication?
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                def http_authentication?
         
     | 
| 
      
 30 
     | 
    
         
            +
                  options['auth-token']
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def curl_upload_from(source, destination = to)
         
     | 
| 
      
 34 
     | 
    
         
            +
                  "#{curl_command} #{authentication} -T #{source} #{destination.to_s};"
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                def curl_download_to_pipe
         
     | 
| 
      
 38 
     | 
    
         
            +
                  "#{curl_command} #{authentication} #{from.to_s} |"
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def curl_manifest(object_manifest, destination = to)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  "#{curl_command} #{authentication} -X PUT -H 'X-Object-Manifest: #{object_manifest}' #{destination.to_s} --data-binary ''"
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
              
         
     | 
| 
      
 45 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,59 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'open-uri'
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Uricp
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
              class OrbitAuth
         
     | 
| 
      
 5 
     | 
    
         
            +
                
         
     | 
| 
      
 6 
     | 
    
         
            +
                AuthenticationFailure = Class.new(ArgumentError)
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(auth_uri, auth_id, auth_key)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  auth_uri.open(
         
     | 
| 
      
 10 
     | 
    
         
            +
                    'X-Auth-User' => auth_id,
         
     | 
| 
      
 11 
     | 
    
         
            +
            	'X-Auth-Key' => auth_key,
         
     | 
| 
      
 12 
     | 
    
         
            +
            	'Range' => 'bytes=0-0'
         
     | 
| 
      
 13 
     | 
    
         
            +
                  ) do |uri|
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @storage_url = uri.meta['x-storage-url']
         
     | 
| 
      
 15 
     | 
    
         
            +
            	@token = uri.meta['x-auth-token']
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                rescue OpenURI::HTTPError => e
         
     | 
| 
      
 18 
     | 
    
         
            +
                  raise AuthenticationFailure, "Cannot authenticate against #{auth_uri}"
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                attr_reader :storage_url, :token
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def self.validate_options(options)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  if options['auth-token'] && (options['auth-key'] || options['auth-user'])
         
     | 
| 
      
 25 
     | 
    
         
            +
            	raise ::OptionParser::NeedlessArgument,
         
     | 
| 
      
 26 
     | 
    
         
            +
            	  "use either key based or token based authentication"
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
                  if options['auth-key'].nil? ^ options['auth-user'].nil?
         
     | 
| 
      
 29 
     | 
    
         
            +
            	raise ::OptionParser::MissingArgument,
         
     | 
| 
      
 30 
     | 
    
         
            +
            	  "'auth-user' requires 'auth-key'"
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
                  if (options['auth-token'] || options['auth-user']) && options['auth_uri'].nil?
         
     | 
| 
      
 33 
     | 
    
         
            +
            	raise ::OptionParser::NeedlessArgument,
         
     | 
| 
      
 34 
     | 
    
         
            +
            	  "authentication is for http uris only"
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def self.add_auth_token(options)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  if options['auth-user']
         
     | 
| 
      
 40 
     | 
    
         
            +
            	orbit_credentials = self.new(options['auth_uri'],
         
     | 
| 
      
 41 
     | 
    
         
            +
            			      options['auth-user'], options['auth-key'])
         
     | 
| 
      
 42 
     | 
    
         
            +
            	options['auth-token'] = orbit_credentials.token
         
     | 
| 
      
 43 
     | 
    
         
            +
            	options.delete('auth-key')
         
     | 
| 
      
 44 
     | 
    
         
            +
            	options.delete('auth-user')
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def self.add_auth_to_optionparser(app)
         
     | 
| 
      
 49 
     | 
    
         
            +
                  app.on("--auth-token AUTH_TOKEN",
         
     | 
| 
      
 50 
     | 
    
         
            +
            	"Use AUTH_TOKEN for non-local requests")
         
     | 
| 
      
 51 
     | 
    
         
            +
                  app.on("--auth-user AUTH_USER",
         
     | 
| 
      
 52 
     | 
    
         
            +
            	"Use AUTH_USER for authentication")
         
     | 
| 
      
 53 
     | 
    
         
            +
                  app.on("--auth-key AUTH_KEY",
         
     | 
| 
      
 54 
     | 
    
         
            +
            	"Use AUTH_KEY for authentication")
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,88 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'pathname'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Uricp
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              class Segmenter
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                include Methadone::CLILogging
         
     | 
| 
      
 8 
     | 
    
         
            +
                include Methadone::SH
         
     | 
| 
      
 9 
     | 
    
         
            +
                include Uricp::CurlPrimitives
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(options)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @options = options
         
     | 
| 
      
 13 
     | 
    
         
            +
                  source = options['from']
         
     | 
| 
      
 14 
     | 
    
         
            +
                  if @source = source && open(source)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @stream = File.pipe?(source) || File.chardev?(source)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  else
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @source = STDIN
         
     | 
| 
      
 18 
     | 
    
         
            +
            	@stream = true
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                  split_path
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def split_path
         
     | 
| 
      
 24 
     | 
    
         
            +
                  elements = Pathname.new(to.path).enum_for(:each_filename).to_a
         
     | 
| 
      
 25 
     | 
    
         
            +
                  elements.shift
         
     | 
| 
      
 26 
     | 
    
         
            +
                  @account=elements.shift
         
     | 
| 
      
 27 
     | 
    
         
            +
                  @container=elements.shift
         
     | 
| 
      
 28 
     | 
    
         
            +
                  @object_path=File.join(elements)
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def upload
         
     | 
| 
      
 32 
     | 
    
         
            +
                  if large_upload?
         
     | 
| 
      
 33 
     | 
    
         
            +
                    segmented_upload
         
     | 
| 
      
 34 
     | 
    
         
            +
                  else
         
     | 
| 
      
 35 
     | 
    
         
            +
                    sh! curl_upload_from(options['from']),
         
     | 
| 
      
 36 
     | 
    
         
            +
            	  :on_fail => "Upload to #{to} failed"
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def segment_size
         
     | 
| 
      
 41 
     | 
    
         
            +
                  options['segment-size'].to_i
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                def stream?
         
     | 
| 
      
 45 
     | 
    
         
            +
                  @stream
         
     | 
| 
      
 46 
     | 
    
         
            +
                end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                def large_upload?
         
     | 
| 
      
 49 
     | 
    
         
            +
                  stream? || @source.stat.size > segment_size
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def manifest_suffix
         
     | 
| 
      
 53 
     | 
    
         
            +
                  @manifest_suffix ||= [Time.now.to_f, @source.stat.size, segment_size].join('/')
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                def object_manifest
         
     | 
| 
      
 57 
     | 
    
         
            +
                  @object_manifest ||= File.join(@container, @object_path, manifest_suffix)+'/'
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                def segmented_upload
         
     | 
| 
      
 61 
     | 
    
         
            +
                  debug "#{self.class.name}: Large upload detected - segmenting into #{segment_size} byte chunks."
         
     | 
| 
      
 62 
     | 
    
         
            +
                  suffix = 0
         
     | 
| 
      
 63 
     | 
    
         
            +
                  until @source.eof?
         
     | 
| 
      
 64 
     | 
    
         
            +
            	debug "#{self.class.name}: Uploading segment #{suffix}"
         
     | 
| 
      
 65 
     | 
    
         
            +
            	upload_segment(suffix)
         
     | 
| 
      
 66 
     | 
    
         
            +
            	suffix = suffix.next
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
                  add_manifest
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                def upload_segment(segment_number)
         
     | 
| 
      
 72 
     | 
    
         
            +
                  segment_name = File.join(to.to_s, manifest_suffix, '%08d' % segment_number)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  debug "Uploading with #{curl_upload_from('-', segment_name)}"
         
     | 
| 
      
 74 
     | 
    
         
            +
                  open('|'+curl_upload_from('-', segment_name), 'w') do |destination|
         
     | 
| 
      
 75 
     | 
    
         
            +
                    copy_length = IO.copy_stream(@source, destination, segment_size)
         
     | 
| 
      
 76 
     | 
    
         
            +
            	debug "#{self.class.name}: Uploaded #{copy_length} bytes to #{segment_name}"
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                def add_manifest
         
     | 
| 
      
 81 
     | 
    
         
            +
                  debug "Adding DLO object_manifest #{object_manifest}"
         
     | 
| 
      
 82 
     | 
    
         
            +
                  sh! curl_manifest(object_manifest),
         
     | 
| 
      
 83 
     | 
    
         
            +
            	:on_fail => "Upload to #{to} failed"
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
              end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,48 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Uricp::Strategy
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
              module CacheCommon
         
     | 
| 
      
 6 
     | 
    
         
            +
                
         
     | 
| 
      
 7 
     | 
    
         
            +
                def validate_cache!
         
     | 
| 
      
 8 
     | 
    
         
            +
                  raise Uricp::MissingCache,
         
     | 
| 
      
 9 
     | 
    
         
            +
            	"No cache found at #{cache_root}. Expected a 'cache' and 'temp' directory" unless cache_exists?
         
     | 
| 
      
 10 
     | 
    
         
            +
                  raise Uricp::MissingCache,
         
     | 
| 
      
 11 
     | 
    
         
            +
            	"No cache name found" unless cache_name
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
                  
         
     | 
| 
      
 14 
     | 
    
         
            +
                def in_cache?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  File.readable? cache_file
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def cache_root
         
     | 
| 
      
 19 
     | 
    
         
            +
                  options['cache']
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                def cache_name
         
     | 
| 
      
 23 
     | 
    
         
            +
                  options['cache_name']
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                def cache_exists?
         
     | 
| 
      
 27 
     | 
    
         
            +
                  cache_root && %w{temp cache}.all? do |d|
         
     | 
| 
      
 28 
     | 
    
         
            +
                    File.directory?(File.join(cache_root, d))
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
                end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                def temp_cache_file
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @temp_cache_file ||= File.join(cache_root, 'temp', cache_name)
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                def temp_cache_uri
         
     | 
| 
      
 37 
     | 
    
         
            +
                  URI.join('file:///', temp_cache_file)
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def cache_file
         
     | 
| 
      
 41 
     | 
    
         
            +
                  @cache_file ||= File.join(cache_root, 'cache', cache_name)
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,48 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'uri'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Uricp::Strategy
         
     | 
| 
      
 4 
     | 
    
         
            +
              
         
     | 
| 
      
 5 
     | 
    
         
            +
              class CachedGet
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                include Uricp::Strategy::Common
         
     | 
| 
      
 8 
     | 
    
         
            +
                include Uricp::Strategy::CacheCommon
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def appropriate?
         
     | 
| 
      
 11 
     | 
    
         
            +
                  if cache_root
         
     | 
| 
      
 12 
     | 
    
         
            +
                    validate_cache!
         
     | 
| 
      
 13 
     | 
    
         
            +
            	if in_cache? || file_source?
         
     | 
| 
      
 14 
     | 
    
         
            +
            	  return proposal
         
     | 
| 
      
 15 
     | 
    
         
            +
            	else
         
     | 
| 
      
 16 
     | 
    
         
            +
            	  debug "#{self.class.name}: no cache entry for #{options['from_uri']}"
         
     | 
| 
      
 17 
     | 
    
         
            +
            	end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  else
         
     | 
| 
      
 19 
     | 
    
         
            +
            	debug "#{self.class.name}: not appropriate"
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  false
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def command
         
     | 
| 
      
 25 
     | 
    
         
            +
                  ":;" 
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def proposal
         
     | 
| 
      
 29 
     | 
    
         
            +
                  @proposed_options = options.dup
         
     | 
| 
      
 30 
     | 
    
         
            +
                  unless file_source?
         
     | 
| 
      
 31 
     | 
    
         
            +
            	@proposed_options['from_uri'] = URI.join('file:///', cache_file)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
                  @proposed_options.delete('cache')
         
     | 
| 
      
 34 
     | 
    
         
            +
                  @proposed_options.delete('cache_name')
         
     | 
| 
      
 35 
     | 
    
         
            +
                  if conversion_required?
         
     | 
| 
      
 36 
     | 
    
         
            +
            	@proposed_options['source-format'] =
         
     | 
| 
      
 37 
     | 
    
         
            +
            	  File.open(@proposed_options['from_uri'].path) {|f| encoding(f)}
         
     | 
| 
      
 38 
     | 
    
         
            +
            	if @proposed_options['source-format'] == @proposed_options['target-format']
         
     | 
| 
      
 39 
     | 
    
         
            +
            	  @proposed_options.delete('source-format')
         
     | 
| 
      
 40 
     | 
    
         
            +
            	  @proposed_options.delete('target-format')
         
     | 
| 
      
 41 
     | 
    
         
            +
            	end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                  self
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
            end
         
     |