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.
Files changed (47) hide show
  1. data/.gitignore +6 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +16 -0
  4. data/README.md +31 -0
  5. data/README.rdoc +23 -0
  6. data/Rakefile +66 -0
  7. data/bin/segment_upload +66 -0
  8. data/bin/uricp +103 -0
  9. data/features/auth_download.feature +106 -0
  10. data/features/cacheable_from_uri.feature +71 -0
  11. data/features/documented_options.feature +73 -0
  12. data/features/remote_upload.feature +49 -0
  13. data/features/segmented_upload.feature +27 -0
  14. data/features/segmented_upload_options.feature +65 -0
  15. data/features/step_definitions/orbit_steps.rb +194 -0
  16. data/features/step_definitions/uricp_steps.rb +34 -0
  17. data/features/support/env.rb +20 -0
  18. data/features/userid_download.feature +21 -0
  19. data/lib/segment_upload.rb +38 -0
  20. data/lib/uricp/curl_primitives.rb +45 -0
  21. data/lib/uricp/orbit_auth.rb +59 -0
  22. data/lib/uricp/segmenter.rb +88 -0
  23. data/lib/uricp/strategy/cache_common.rb +48 -0
  24. data/lib/uricp/strategy/cached_get.rb +48 -0
  25. data/lib/uricp/strategy/cleaner.rb +28 -0
  26. data/lib/uricp/strategy/common.rb +108 -0
  27. data/lib/uricp/strategy/local_convert.rb +36 -0
  28. data/lib/uricp/strategy/local_convert_common.rb +29 -0
  29. data/lib/uricp/strategy/local_link.rb +31 -0
  30. data/lib/uricp/strategy/piped_cache.rb +40 -0
  31. data/lib/uricp/strategy/piped_cache_convert.rb +45 -0
  32. data/lib/uricp/strategy/piped_compress.rb +30 -0
  33. data/lib/uricp/strategy/piped_decompress.rb +35 -0
  34. data/lib/uricp/strategy/piped_local_compress.rb +30 -0
  35. data/lib/uricp/strategy/piped_local_get.rb +29 -0
  36. data/lib/uricp/strategy/piped_local_put.rb +29 -0
  37. data/lib/uricp/strategy/piped_remote_get.rb +38 -0
  38. data/lib/uricp/strategy/pruner.rb +22 -0
  39. data/lib/uricp/strategy/remote_put.rb +47 -0
  40. data/lib/uricp/strategy/segmented_remote_put.rb +52 -0
  41. data/lib/uricp/strategy/sweeper.rb +28 -0
  42. data/lib/uricp/uri_strategy.rb +47 -0
  43. data/lib/uricp/version.rb +4 -0
  44. data/lib/uricp.rb +56 -0
  45. data/spec/something_spec.rb +5 -0
  46. data/uricp.gemspec +32 -0
  47. metadata +281 -0
@@ -0,0 +1,28 @@
1
+ module Uricp::Strategy
2
+
3
+ class Cleaner
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+ def appropriate?
8
+ if options['clean'] && sequence_complete?
9
+ return proposal
10
+ end
11
+ debug "#{self.class.name}: not appropriate"
12
+ false
13
+ end
14
+
15
+ def command
16
+ "rm -f #{options['clean'].join(' ')};"
17
+ end
18
+
19
+ def proposal
20
+ @proposed_options = options.dup
21
+ @proposed_options.delete('clean')
22
+ self
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+
@@ -0,0 +1,108 @@
1
+ module Uricp::Strategy
2
+
3
+ module Common
4
+
5
+ include Methadone::CLILogging
6
+ include Uricp::CurlPrimitives
7
+
8
+ def initialize(options)
9
+ @options = options
10
+ debug "#{self.class.name}: options are #{options.inspect}"
11
+ end
12
+
13
+ attr_reader :proposed_options
14
+
15
+ def unsupported_transfer
16
+ raise Uricp::UnsupportedURLtype,
17
+ "Unsupported transfer from #{from.to_s} to #{to.to_s}"
18
+ end
19
+
20
+ alias :command :unsupported_transfer
21
+
22
+ def all_local_files?
23
+ !sequence_complete? && file_source? && to.scheme == 'file'
24
+ end
25
+
26
+ def compression_required?
27
+ options['compress']
28
+ end
29
+
30
+ def segmented?
31
+ options['segment-size']
32
+ end
33
+
34
+ def conversion_required?
35
+ options['target-format']
36
+ end
37
+
38
+ def lz4?(magic)
39
+ magic.unpack('V') == [0x184D2204]
40
+ end
41
+
42
+ def qcow2?(magic)
43
+ magic.unpack('a3C') == ['QFI',0xfb]
44
+ end
45
+
46
+ def encoding(io)
47
+ magic = io.read(4)
48
+ case
49
+ when lz4?(magic)
50
+ :lz4
51
+ when qcow2?(magic)
52
+ version = io.read(4)
53
+ case version.unpack('N')
54
+ when [2]
55
+ :qcow2
56
+ when [3]
57
+ :qcow3
58
+ else
59
+ :qcow2un
60
+ end
61
+ else
62
+ :raw
63
+ end
64
+ end
65
+
66
+ def format_change?
67
+ conversion_required? && (
68
+ options['source-format'] != options['target-format']
69
+ )
70
+ end
71
+
72
+ def lz4_source?
73
+ options['source-format'] == :lz4
74
+ end
75
+
76
+ def raw_target?
77
+ options['target-format'] == :raw
78
+ end
79
+
80
+ def file_source?
81
+ from.scheme == 'file'
82
+ end
83
+
84
+ def sequence_complete?
85
+ from == to
86
+ end
87
+
88
+ PIPE_URI = URI('pipe:/')
89
+
90
+ def get_temp_filename(filename)
91
+ Dir::Tmpname.make_tmpname(filename, nil)
92
+ end
93
+
94
+ def proposed_path
95
+ proposed_options['from_uri'].path
96
+ end
97
+
98
+ def temp_uri
99
+ URI.join('file:///', get_temp_filename(options['temp']))
100
+ end
101
+
102
+ def supported_source?
103
+ options['source-format'] && !lz4_source?
104
+ end
105
+
106
+ end
107
+
108
+ end
@@ -0,0 +1,36 @@
1
+ module Uricp::Strategy
2
+
3
+ class LocalConvert
4
+
5
+ include Uricp::Strategy::LocalConvertCommon
6
+
7
+ def appropriate?
8
+ if conversion_required? && file_source? && supported_source?
9
+ return proposal
10
+ end
11
+ debug "#{self.class.name}: not appropriate"
12
+ false
13
+ end
14
+
15
+ def command
16
+ qemu_img_command(proposed_path)
17
+ end
18
+
19
+ def proposal
20
+ @proposed_options = options.dup
21
+ if to.scheme == 'file'
22
+ @proposed_options['from_uri'] = @proposed_options['to_uri']
23
+ else
24
+ @proposed_options['from_uri'] = temp_uri
25
+ @proposed_options['clean'] ||= []
26
+ @proposed_options['clean'] << proposed_path
27
+ end
28
+ @proposed_options.delete('source-format')
29
+ @proposed_options.delete('target-format')
30
+ self
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+
@@ -0,0 +1,29 @@
1
+ module Uricp::Strategy
2
+
3
+ module LocalConvertCommon
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+ def qemu_img_command(target)
8
+ "qemu-img convert #{conversion_options} #{from.path} #{target};"
9
+ end
10
+
11
+ def conversion_options
12
+ case options['target-format']
13
+ when :raw
14
+ '-O raw'
15
+ when :qcow2
16
+ "#{compress_option} -O qcow2 -o compat=0.10"
17
+ when :qcow3, :qcow2v3
18
+ "#{compress_option} -O qcow2 -o compat=1.1"
19
+ end
20
+ end
21
+
22
+ def compress_option
23
+ options['compress'] && '-c'
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
@@ -0,0 +1,31 @@
1
+ module Uricp::Strategy
2
+
3
+ class LocalLink
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+ def appropriate?
8
+ return proposal if all_local_files? && !format_change? && linkable?
9
+ debug "#{self.class.name}: not appropriate"
10
+ false
11
+ end
12
+
13
+ def command
14
+ "ln -fT #{from.path} #{to.path};"
15
+ end
16
+
17
+ def proposal
18
+ @proposed_options = options.dup
19
+ @proposed_options['from_uri'] = @proposed_options['to_uri']
20
+ self
21
+ end
22
+
23
+ def linkable?
24
+ File.stat(File.dirname(from.path)).dev == File.stat(File.dirname(to.path)).dev &&
25
+ (!File.exist?(to.path) || File.file?(to.path))
26
+ end
27
+
28
+ end
29
+
30
+ end
31
+
@@ -0,0 +1,40 @@
1
+ require 'fileutils'
2
+
3
+ module Uricp::Strategy
4
+
5
+ class PipedCache
6
+
7
+ include Uricp::Strategy::Common
8
+ include Uricp::Strategy::CacheCommon
9
+
10
+ def appropriate?
11
+ if cache_root
12
+ case from.scheme
13
+ when 'pipe'
14
+ validate_cache!
15
+ return proposal
16
+ end
17
+ debug "#{self.class.name}: not appropriate"
18
+ else
19
+ debug "#{self.class.name}: no cacheing requested"
20
+ end
21
+ false
22
+ end
23
+
24
+ def command
25
+ "tee #{temp_cache_file} |"
26
+ end
27
+
28
+ def proposal
29
+ @proposed_options = options.dup
30
+ @proposed_options['sweep'] = [temp_cache_file, cache_file]
31
+ @proposed_options.delete('cache')
32
+ @proposed_options.delete('cache_name')
33
+ self
34
+ end
35
+
36
+ end
37
+
38
+ end
39
+
40
+
@@ -0,0 +1,45 @@
1
+ require 'fileutils'
2
+
3
+ module Uricp::Strategy
4
+
5
+ class PipedCacheConvert
6
+
7
+ include Uricp::Strategy::Common
8
+ include Uricp::Strategy::CacheCommon
9
+
10
+ def appropriate?
11
+ if conversion_required? && supported_source?
12
+ case from.scheme
13
+ when 'pipe'
14
+ validate_cache! if cache_root
15
+ return proposal
16
+ end
17
+ debug "#{self.class.name}: not appropriate"
18
+ else
19
+ debug "#{self.class.name}: no non-stream conversion detected"
20
+ end
21
+ false
22
+ end
23
+
24
+ def command
25
+ "cp --sparse=always /dev/stdin #{proposed_path};"
26
+ end
27
+
28
+ def proposal
29
+ @proposed_options = options.dup
30
+ if cache_root
31
+ @proposed_options['from_uri'] = temp_cache_uri
32
+ @proposed_options['sweep'] = [temp_cache_file, cache_file]
33
+ @proposed_options.delete('cache')
34
+ @proposed_options.delete('cache_name')
35
+ else
36
+ @proposed_options['from_uri'] = temp_uri
37
+ @proposed_options['clean'] ||= []
38
+ @proposed_options['clean'] << proposed_path
39
+ end
40
+ self
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,30 @@
1
+ module Uricp::Strategy
2
+
3
+ class PipedCompress
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+
8
+ def appropriate?
9
+ case from.scheme
10
+ when 'pipe'
11
+ return proposal if compression_required?
12
+ end
13
+ debug "#{self.class.name}: not appropriate"
14
+ false
15
+ end
16
+
17
+ def command
18
+ "lz4c |"
19
+ end
20
+
21
+ def proposal
22
+ @proposed_options = options.dup
23
+ @proposed_options.delete('compress')
24
+ @proposed_options['encoding']='lz4'
25
+ self
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,35 @@
1
+ module Uricp::Strategy
2
+
3
+ class PipedDecompress
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+ def appropriate?
8
+ case from.scheme
9
+ when 'pipe'
10
+ return proposal if lz4_source?
11
+ end
12
+ debug "#{self.class.name}: not appropriate"
13
+ false
14
+ end
15
+
16
+ def command
17
+ "lz4c -d |"
18
+ end
19
+
20
+ def proposal
21
+ @proposed_options = options.dup
22
+ @proposed_options.delete('source-format')
23
+ if @proposed_options['target-format']
24
+ @proposed_options['source-format'] = :raw
25
+ if @proposed_options['source-format'] == @proposed_options['target-format']
26
+ @proposed_options.delete('source-format')
27
+ @proposed_options.delete('target-format')
28
+ end
29
+ end
30
+ self
31
+ end
32
+
33
+ end
34
+
35
+ end
@@ -0,0 +1,30 @@
1
+ module Uricp::Strategy
2
+
3
+ class PipedLocalCompress
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+ def appropriate?
8
+ case from.scheme
9
+ when 'file'
10
+ return proposal if !sequence_complete? && compression_required?
11
+ end
12
+ debug "#{self.class.name}: not appropriate"
13
+ false
14
+ end
15
+
16
+ def command
17
+ "lz4c <#{from.path} |"
18
+ end
19
+
20
+ def proposal
21
+ @proposed_options = options.dup
22
+ @proposed_options.delete('compress')
23
+ @proposed_options['encoding'] = 'lz4'
24
+ @proposed_options['from_uri'] = PIPE_URI
25
+ self
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,29 @@
1
+ module Uricp::Strategy
2
+
3
+ class PipedLocalGet
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+ def appropriate?
8
+ case from.scheme
9
+ when 'file'
10
+ return proposal unless sequence_complete?
11
+ end
12
+ debug "#{self.class.name}: not appropriate"
13
+ false
14
+ end
15
+
16
+ def command
17
+ "cat #{from.path} |"
18
+ end
19
+
20
+ def proposal
21
+ @proposed_options = options.dup
22
+ @proposed_options['from_uri'] = PIPE_URI
23
+ self
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
@@ -0,0 +1,29 @@
1
+ module Uricp::Strategy
2
+
3
+ class PipedLocalPut
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+ def appropriate?
8
+ case from.scheme
9
+ when 'pipe'
10
+ return proposal if to.scheme == 'file'
11
+ end
12
+ debug "#{self.class.name}: not appropriate"
13
+ false
14
+ end
15
+
16
+ def command
17
+ "cp --sparse=always /dev/stdin #{to.path};"
18
+ end
19
+
20
+ def proposal
21
+ @proposed_options = options.dup
22
+ @proposed_options['from_uri'] = @proposed_options['to_uri']
23
+ self
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
@@ -0,0 +1,38 @@
1
+ require 'open-uri'
2
+ module Uricp::Strategy
3
+
4
+ class PipedRemoteGet
5
+
6
+ include Uricp::Strategy::Common
7
+
8
+ def appropriate?
9
+ case from.scheme
10
+ when 'http', 'https'
11
+ return proposal unless sequence_complete?
12
+ else
13
+ debug "#{self.class.name}: not appropriate"
14
+ false
15
+ end
16
+ end
17
+
18
+ alias :command :curl_download_to_pipe
19
+
20
+ def proposal
21
+ @proposed_options = options.dup
22
+ @proposed_options['from_uri'] = PIPE_URI
23
+ if options['target-format']
24
+ @proposed_options['source-format'] =
25
+ options['from_uri'].open(headers) { |u| encoding(u) }
26
+ end
27
+ self
28
+ end
29
+
30
+ def headers
31
+ headers={'Range' => 'bytes=0-7'}
32
+ headers['X-Auth-Token'] = options['auth-token'] if http_authentication?
33
+ headers
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,22 @@
1
+ module Uricp::Strategy
2
+
3
+ class Pruner
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+ def appropriate?
8
+ if conversion_required? && options['source-format']
9
+ unsupported_conversion
10
+ end
11
+ debug "#{self.class.name}: no dead ends to prune"
12
+ false
13
+ end
14
+
15
+ def unsupported_conversion
16
+ raise Uricp::UnsupportedConversion,
17
+ "Unsupported source conversion to #{options['target-format'].to_s}"
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,47 @@
1
+ module Uricp::Strategy
2
+
3
+ class RemotePut
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+ def appropriate?
8
+ if compression_required? || conversion_required?
9
+ debug "#{self.class.name}: not ready to upload"
10
+ else
11
+ case to.scheme
12
+ when 'http', 'https'
13
+ case from.scheme
14
+ when 'file', 'pipe'
15
+ if http_authentication?
16
+ return proposal
17
+ else
18
+ unsupported_transfer
19
+ end
20
+ end
21
+ end
22
+ debug "#{self.class.name}: not appropriate"
23
+ end
24
+ false
25
+ end
26
+
27
+ def command
28
+ curl_upload_from(curl_source)
29
+ end
30
+
31
+ def proposal
32
+ @proposed_options = options.dup
33
+ @proposed_options['from_uri'] = to
34
+ self
35
+ end
36
+
37
+ def curl_source
38
+ if from.scheme == 'pipe'
39
+ '-'
40
+ else
41
+ from.path
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,52 @@
1
+ module Uricp::Strategy
2
+
3
+ class SegmentedRemotePut
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+ def appropriate?
8
+ if segmented?
9
+ if compression_required? || conversion_required?
10
+ debug "#{self.class.name}: not ready to upload"
11
+ else
12
+ case to.scheme
13
+ when 'http', 'https'
14
+ case from.scheme
15
+ when 'file', 'pipe'
16
+ if http_authentication?
17
+ return proposal
18
+ else
19
+ unsupported_transfer
20
+ end
21
+ end
22
+ end
23
+ debug "#{self.class.name}: not appropriate"
24
+ end
25
+ else
26
+ debug "#{self.class.name}: no segmentation requested"
27
+ end
28
+ false
29
+ end
30
+
31
+ def command
32
+ "segment_upload --segment-size '#{options['segment-size']}' #{source_details} #{to.to_s};"
33
+ end
34
+
35
+ def proposal
36
+ @proposed_options = options.dup
37
+ @proposed_options['from_uri'] = to
38
+ @proposed_options['segment-size'] = nil
39
+ self
40
+ end
41
+
42
+ def source_details
43
+ "--from #{from.path}" unless from.scheme == 'pipe'
44
+ end
45
+
46
+ def segmented?
47
+ options['segment-size']
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,28 @@
1
+ module Uricp::Strategy
2
+
3
+ class Sweeper
4
+
5
+ include Uricp::Strategy::Common
6
+
7
+ def appropriate?
8
+ if options['sweep'] && sequence_complete?
9
+ return proposal
10
+ end
11
+ debug "#{self.class.name}: not appropriate"
12
+ false
13
+ end
14
+
15
+ def command
16
+ "mv #{options['sweep'].first} #{options['sweep'].last};"
17
+ end
18
+
19
+ def proposal
20
+ @proposed_options = options.dup
21
+ @proposed_options.delete('sweep')
22
+ self
23
+ end
24
+
25
+ end
26
+
27
+ end
28
+