uricp 0.0.15 → 0.0.20

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +3 -0
  3. data/Gemfile.lock +16 -21
  4. data/Jenkinsfile +90 -33
  5. data/Rakefile +24 -36
  6. data/almalinux8/Dockerfile +26 -0
  7. data/bin/uricp +25 -11
  8. data/bionic/Dockerfile +4 -3
  9. data/centos7/Dockerfile +17 -12
  10. data/cucumber.yml +1 -0
  11. data/features/check_uri_path.feature +14 -0
  12. data/features/rbd_access.feature +226 -0
  13. data/features/step_definitions/orbit_steps.rb +11 -4
  14. data/features/step_definitions/rbd_steps.rb +8 -0
  15. data/features/step_definitions/uricp_steps.rb +8 -4
  16. data/focal/Dockerfile +28 -0
  17. data/lib/segment_upload.rb +20 -22
  18. data/lib/uricp/curl_primitives.rb +31 -33
  19. data/lib/uricp/orbit_auth.rb +23 -27
  20. data/lib/uricp/segmenter.rb +12 -16
  21. data/lib/uricp/strategy/cache_common.rb +30 -12
  22. data/lib/uricp/strategy/cached_get.rb +19 -22
  23. data/lib/uricp/strategy/cleaner.rb +2 -8
  24. data/lib/uricp/strategy/common.rb +76 -18
  25. data/lib/uricp/strategy/local_convert.rb +10 -17
  26. data/lib/uricp/strategy/local_link.rb +10 -8
  27. data/lib/uricp/strategy/piped_cache.rb +11 -16
  28. data/lib/uricp/strategy/piped_cache_convert.rb +14 -18
  29. data/lib/uricp/strategy/piped_compress.rb +3 -8
  30. data/lib/uricp/strategy/piped_decompress.rb +7 -11
  31. data/lib/uricp/strategy/piped_local_compress.rb +0 -4
  32. data/lib/uricp/strategy/piped_local_decompress.rb +10 -16
  33. data/lib/uricp/strategy/piped_local_get.rb +0 -5
  34. data/lib/uricp/strategy/piped_local_put.rb +3 -9
  35. data/lib/uricp/strategy/piped_rbd_get.rb +30 -0
  36. data/lib/uricp/strategy/piped_remote_get.rb +20 -20
  37. data/lib/uricp/strategy/rbd_cache_base_snap.rb +27 -0
  38. data/lib/uricp/strategy/rbd_cache_check.rb +42 -0
  39. data/lib/uricp/strategy/rbd_cache_clone.rb +40 -0
  40. data/lib/uricp/strategy/rbd_cache_upload.rb +40 -0
  41. data/lib/uricp/strategy/rbd_cached_get.rb +36 -0
  42. data/lib/uricp/strategy/rbd_cached_put.rb +33 -0
  43. data/lib/uricp/strategy/rbd_flattener.rb +23 -0
  44. data/lib/uricp/strategy/rbd_put.rb +38 -0
  45. data/lib/uricp/strategy/rbd_snap.rb +56 -0
  46. data/lib/uricp/strategy/rbd_sweeper.rb +23 -0
  47. data/lib/uricp/strategy/remote_put.rb +11 -17
  48. data/lib/uricp/strategy/segmented_remote_put.rb +16 -28
  49. data/lib/uricp/strategy/sweeper.rb +2 -8
  50. data/lib/uricp/uri_strategy.rb +22 -13
  51. data/lib/uricp/version.rb +4 -2
  52. data/lib/uricp.rb +27 -19
  53. data/uricp.gemspec +3 -3
  54. data/xenial/Dockerfile +4 -3
  55. metadata +41 -25
  56. data/spec/something_spec.rb +0 -5
  57. data/trusty/Dockerfile +0 -20
@@ -1,45 +1,43 @@
1
1
  module Uricp::CurlPrimitives
2
+ attr_reader :options
2
3
 
3
- attr_reader :options
4
+ def from
5
+ options['from_uri']
6
+ end
4
7
 
5
- def from
6
- options['from_uri']
7
- end
8
+ def from=(target)
9
+ options['from_uri'] = target
10
+ end
8
11
 
9
- def from=(target)
10
- options['from_uri'] = target
11
- end
12
+ def to
13
+ options['to_uri']
14
+ end
12
15
 
13
- def to
14
- options['to_uri']
15
- end
16
+ def to=(target)
17
+ options['to_uri'] = target
18
+ end
16
19
 
17
- def to=(target)
18
- options['to_uri'] = target
19
- end
20
-
21
- def curl_command
22
- "curl --fail --silent"
23
- end
20
+ def curl_command
21
+ 'curl --fail --silent'
22
+ end
24
23
 
25
- def authentication
26
- "-H X-Auth-Token:#{options['authenticator'].call}" if http_authentication?
27
- end
24
+ def authentication
25
+ "-H X-Auth-Token:#{options['authenticator'].call}" if http_authentication?
26
+ end
28
27
 
29
- def http_authentication?
30
- options['authenticator']
31
- end
28
+ def http_authentication?
29
+ options['authenticator']
30
+ end
32
31
 
33
- def curl_upload_from(source, destination = to)
34
- "#{curl_command} #{authentication} -T #{source} #{destination.to_s};"
35
- end
32
+ def curl_upload_from(source, destination = to)
33
+ "#{curl_command} #{authentication} -T #{source} #{destination};"
34
+ end
36
35
 
37
- def curl_download_to_pipe
38
- "#{curl_command} #{authentication} #{from.to_s} |"
39
- end
36
+ def curl_download_to_pipe
37
+ "#{curl_command} #{authentication} #{from} |"
38
+ end
40
39
 
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
-
40
+ def curl_manifest(object_manifest, destination = to)
41
+ "#{curl_command} #{authentication} -X PUT -H 'X-Object-Manifest: #{object_manifest}' #{destination} --data-binary ''"
42
+ end
45
43
  end
@@ -1,8 +1,6 @@
1
1
  require 'open-uri'
2
2
  module Uricp
3
-
4
3
  class OrbitAuth
5
-
6
4
  AuthenticationFailure = Class.new(ArgumentError)
7
5
 
8
6
  def initialize(auth_uri, auth_id, auth_key)
@@ -14,9 +12,9 @@ module Uricp
14
12
  def storage_url
15
13
  @storage_url ||
16
14
  begin
17
- authenticate
18
- @storage_url
19
- end
15
+ authenticate
16
+ @storage_url
17
+ end
20
18
  end
21
19
 
22
20
  def token
@@ -26,27 +24,27 @@ module Uricp
26
24
 
27
25
  def self.validate_options(options)
28
26
  if options['auth-token'] && (options['auth-key'] || options['auth-user'])
29
- raise ::OptionParser::NeedlessArgument,
30
- "use either key based or token based authentication"
27
+ raise ::OptionParser::NeedlessArgument,
28
+ 'use either key based or token based authentication'
31
29
  end
32
30
  if options['auth-key'].nil? ^ options['auth-user'].nil?
33
- raise ::OptionParser::MissingArgument,
34
- "'auth-user' requires 'auth-key'"
31
+ raise ::OptionParser::MissingArgument,
32
+ "'auth-user' requires 'auth-key'"
35
33
  end
36
34
  if (options['auth-token'] || options['auth-user']) && options['auth_uri'].nil?
37
- raise ::OptionParser::NeedlessArgument,
38
- "authentication is for http uris only"
35
+ raise ::OptionParser::NeedlessArgument,
36
+ 'authentication is for http uris only'
39
37
  end
40
38
  end
41
39
 
42
40
  def self.add_auth_token(options)
43
41
  if options['auth-user']
44
- orbit_credentials = self.new(options['auth_uri'],
45
- options['auth-user'], options['auth-key'])
46
- options['authenticator'] = orbit_credentials.method(:token)
42
+ orbit_credentials = new(options['auth_uri'],
43
+ options['auth-user'], options['auth-key'])
44
+ options['authenticator'] = orbit_credentials.method(:token)
47
45
  elsif options['auth-token']
48
46
  orbit_token = options['auth-token']
49
- options['authenticator'] = lambda { orbit_token }
47
+ options['authenticator'] = -> { orbit_token }
50
48
  end
51
49
  options.delete('auth-key')
52
50
  options.delete('auth-user')
@@ -54,29 +52,27 @@ module Uricp
54
52
  end
55
53
 
56
54
  def self.add_auth_to_optionparser(app)
57
- app.on("--auth-token AUTH_TOKEN",
58
- "Use AUTH_TOKEN for non-local requests")
59
- app.on("--auth-user AUTH_USER",
60
- "Use AUTH_USER for authentication")
61
- app.on("--auth-key AUTH_KEY",
62
- "Use AUTH_KEY for authentication")
55
+ app.on('--auth-token AUTH_TOKEN',
56
+ 'Use AUTH_TOKEN for non-local requests')
57
+ app.on('--auth-user AUTH_USER',
58
+ 'Use AUTH_USER for authentication')
59
+ app.on('--auth-key AUTH_KEY',
60
+ 'Use AUTH_KEY for authentication')
63
61
  end
64
62
 
65
- private
63
+ private
66
64
 
67
65
  def authenticate
68
66
  @auth_uri.open(
69
67
  'X-Auth-User' => @auth_id,
70
- 'X-Auth-Key' => @auth_key,
71
- 'Range' => 'bytes=0-0'
68
+ 'X-Auth-Key' => @auth_key,
69
+ 'Range' => 'bytes=0-0'
72
70
  ) do |uri|
73
71
  @storage_url = uri.meta['x-storage-url']
74
- @token = uri.meta['x-auth-token']
72
+ @token = uri.meta['x-auth-token']
75
73
  end
76
74
  rescue OpenURI::HTTPError => e
77
75
  raise AuthenticationFailure, "Cannot authenticate against #{@auth_uri}"
78
76
  end
79
-
80
77
  end
81
-
82
78
  end
@@ -1,9 +1,7 @@
1
1
  require 'pathname'
2
2
 
3
3
  module Uricp
4
-
5
4
  class Segmenter
6
-
7
5
  include Methadone::CLILogging
8
6
  include Methadone::SH
9
7
  include Uricp::CurlPrimitives
@@ -15,7 +13,7 @@ module Uricp
15
13
  @stream = File.pipe?(source) || File.chardev?(source)
16
14
  else
17
15
  @source = STDIN
18
- @stream = true
16
+ @stream = true
19
17
  end
20
18
  split_path
21
19
  end
@@ -23,9 +21,9 @@ module Uricp
23
21
  def split_path
24
22
  elements = Pathname.new(to.path).enum_for(:each_filename).to_a
25
23
  elements.shift
26
- @account=elements.shift
27
- @container=elements.shift
28
- @object_path=File.join(elements)
24
+ @account = elements.shift
25
+ @container = elements.shift
26
+ @object_path = File.join(elements)
29
27
  end
30
28
 
31
29
  def upload
@@ -33,7 +31,7 @@ module Uricp
33
31
  segmented_upload
34
32
  else
35
33
  sh! curl_upload_from(options['from']),
36
- :on_fail => "Upload to #{to} failed"
34
+ on_fail: "Upload to #{to} failed"
37
35
  end
38
36
  end
39
37
 
@@ -54,16 +52,16 @@ module Uricp
54
52
  end
55
53
 
56
54
  def object_manifest
57
- @object_manifest ||= File.join(@container, @object_path, manifest_suffix)+'/'
55
+ @object_manifest ||= File.join(@container, @object_path, manifest_suffix) + '/'
58
56
  end
59
57
 
60
58
  def segmented_upload
61
59
  debug "#{self.class.name}: Large upload detected - segmenting into #{segment_size} byte chunks."
62
60
  suffix = 0
63
61
  until @source.eof?
64
- debug "#{self.class.name}: Uploading segment #{suffix}"
65
- upload_segment(suffix)
66
- suffix = suffix.next
62
+ debug "#{self.class.name}: Uploading segment #{suffix}"
63
+ upload_segment(suffix)
64
+ suffix = suffix.next
67
65
  end
68
66
  add_manifest
69
67
  end
@@ -71,18 +69,16 @@ module Uricp
71
69
  def upload_segment(segment_number)
72
70
  segment_name = File.join(to.to_s, manifest_suffix, '%08d' % segment_number)
73
71
  debug "Uploading with #{curl_upload_from('-', segment_name)}"
74
- open('|'+curl_upload_from('-', segment_name), 'w') do |destination|
72
+ open('|' + curl_upload_from('-', segment_name), 'w') do |destination|
75
73
  copy_length = IO.copy_stream(@source, destination, segment_size)
76
- debug "#{self.class.name}: Uploaded #{copy_length} bytes to #{segment_name}"
74
+ debug "#{self.class.name}: Uploaded #{copy_length} bytes to #{segment_name}"
77
75
  end
78
76
  end
79
77
 
80
78
  def add_manifest
81
79
  debug "Adding DLO object_manifest #{object_manifest}"
82
80
  sh! curl_manifest(object_manifest),
83
- :on_fail => "Upload to #{to} failed"
81
+ on_fail: "Upload to #{to} failed"
84
82
  end
85
-
86
83
  end
87
-
88
84
  end
@@ -1,18 +1,40 @@
1
1
  require 'fileutils'
2
2
 
3
3
  module Uricp::Strategy
4
-
5
4
  module CacheCommon
6
-
7
5
  def validate_cache!
6
+ return if dry_run?
7
+
8
+ unless cache_exists?
9
+ raise Uricp::MissingCache,
10
+ "no cache found at #{cache_root}. Expected a 'cache' and 'temp' directory"
11
+ end
12
+ return if cache_name
13
+
8
14
  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
15
+ 'no cache name found'
16
+ end
17
+
18
+ def with_active_cache
19
+ if cache_root
20
+ yield
21
+ else
22
+ debug "#{self.class.name}: no cacheing requested"
23
+ false
24
+ end
12
25
  end
13
-
26
+
27
+ def without_active_cache
28
+ unless cache_root
29
+ yield
30
+ else
31
+ debug "#{self.class.name}: cache active - not appropriate"
32
+ false
33
+ end
34
+ end
35
+
14
36
  def in_cache?
15
- File.readable? cache_file
37
+ File.readable?(cache_file) || options['dry-cache']
16
38
  end
17
39
 
18
40
  def cache_root
@@ -24,7 +46,7 @@ module Uricp::Strategy
24
46
  end
25
47
 
26
48
  def cache_exists?
27
- cache_root && %w{temp cache}.all? do |d|
49
+ cache_root && %w[temp cache].all? do |d|
28
50
  File.directory?(File.join(cache_root, d))
29
51
  end
30
52
  end
@@ -40,9 +62,5 @@ module Uricp::Strategy
40
62
  def cache_file
41
63
  @cache_file ||= File.join(cache_root, 'cache', cache_name)
42
64
  end
43
-
44
65
  end
45
-
46
66
  end
47
-
48
-
@@ -1,48 +1,45 @@
1
1
  require 'uri'
2
2
 
3
3
  module Uricp::Strategy
4
-
5
4
  class CachedGet
6
-
7
5
  include Uricp::Strategy::Common
8
6
  include Uricp::Strategy::CacheCommon
9
7
 
10
8
  def appropriate?
11
- if cache_root
9
+ with_active_cache do
12
10
  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"
11
+ return proposal if in_cache? || file_source?
12
+
13
+ debug "#{self.class.name}: no cache entry for #{options['from_uri']}"
14
+ false
20
15
  end
21
- false
22
16
  end
23
17
 
24
18
  def command
25
- ":;"
19
+ ':;'
26
20
  end
27
21
 
28
22
  def proposal
29
23
  @proposed_options = options.dup
30
- unless file_source?
31
- @proposed_options['from_uri'] = URI.join('file:///', cache_file)
24
+ @proposed_options['from_uri'] = URI.join('file:///', cache_file) unless file_source?
25
+ if to.scheme == 'rbd'
26
+ @proposed_options['rbd_cache_name'] = rbd_cache_image_spec(to)
32
27
  end
33
28
  @proposed_options.delete('cache')
34
29
  @proposed_options.delete('cache_name')
35
30
  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
31
+ if dry_run?
32
+ @proposed_options['source-format'] = @proposed_options['target-format']
33
+ else
34
+ @proposed_options['source-format'] =
35
+ File.open(@proposed_options['from_uri'].path) { |f| encoding(f) }
36
+ end
37
+ if @proposed_options['source-format'] == @proposed_options['target-format']
38
+ @proposed_options.delete('source-format')
39
+ @proposed_options.delete('target-format')
40
+ end
42
41
  end
43
42
  self
44
43
  end
45
-
46
44
  end
47
-
48
45
  end
@@ -1,13 +1,10 @@
1
1
  module Uricp::Strategy
2
-
3
2
  class Cleaner
4
-
5
3
  include Uricp::Strategy::Common
6
4
 
7
5
  def appropriate?
8
- if options['clean'] && sequence_complete?
9
- return proposal
10
- end
6
+ return proposal if options['clean'] && sequence_complete?
7
+
11
8
  debug "#{self.class.name}: not appropriate"
12
9
  false
13
10
  end
@@ -21,8 +18,5 @@ module Uricp::Strategy
21
18
  @proposed_options.delete('clean')
22
19
  self
23
20
  end
24
-
25
21
  end
26
-
27
22
  end
28
-
@@ -1,7 +1,5 @@
1
1
  module Uricp::Strategy
2
-
3
2
  module Common
4
-
5
3
  include Methadone::CLILogging
6
4
  include Uricp::CurlPrimitives
7
5
 
@@ -14,10 +12,10 @@ module Uricp::Strategy
14
12
 
15
13
  def unsupported_transfer
16
14
  raise Uricp::UnsupportedURLtype,
17
- "Unsupported transfer from #{from.to_s} to #{to.to_s}"
15
+ "Unsupported transfer from #{from} to #{to}"
18
16
  end
19
17
 
20
- alias :command :unsupported_transfer
18
+ alias command unsupported_transfer
21
19
 
22
20
  def all_local_files?
23
21
  !sequence_complete? && file_source? && to.scheme == 'file'
@@ -44,24 +42,23 @@ module Uricp::Strategy
44
42
  end
45
43
 
46
44
  def qcow2?(magic)
47
- magic.unpack('a3C') == ['QFI',0xfb]
45
+ magic.unpack('a3C') == ['QFI', 0xfb]
48
46
  end
49
47
 
50
48
  def encoding(io)
51
49
  magic = io.read(4).to_s
52
- case
53
- when lz4?(magic)
50
+ if lz4?(magic)
54
51
  :lz4
55
- when qcow2?(magic)
52
+ elsif qcow2?(magic)
56
53
  version = io.read(4)
57
- case version.unpack('N')
58
- when [2]
59
- :qcow2
60
- when [3]
61
- :qcow3
62
- else
63
- :qcow2un
64
- end
54
+ case version.unpack('N')
55
+ when [2]
56
+ :qcow2
57
+ when [3]
58
+ :qcow3
59
+ else
60
+ :qcow2un
61
+ end
65
62
  else
66
63
  :raw
67
64
  end
@@ -73,6 +70,10 @@ module Uricp::Strategy
73
70
  )
74
71
  end
75
72
 
73
+ def dry_run?
74
+ options['dry-run']
75
+ end
76
+
76
77
  def lz4_source?
77
78
  options['source-format'] == :lz4
78
79
  end
@@ -90,9 +91,16 @@ module Uricp::Strategy
90
91
  end
91
92
 
92
93
  PIPE_URI = URI('pipe:/')
94
+ def rbd_base_name
95
+ 'base'.freeze
96
+ end
97
+
98
+ def rbd_snapshot_name
99
+ 'uricp_snap'.freeze
100
+ end
93
101
 
94
102
  def get_temp_filename(base_dir)
95
- t = Time.now.strftime("%Y%m%d")
103
+ t = Time.now.strftime('%Y%m%d')
96
104
  File.join(base_dir, "uricp-#{t}-#{$$}-#{rand(0x100000000).to_s(36)}")
97
105
  end
98
106
 
@@ -108,6 +116,56 @@ module Uricp::Strategy
108
116
  options['source-format'] && !lz4_source?
109
117
  end
110
118
 
111
- end
119
+ def rbd_image_spec(uri)
120
+ uri.path[1..-1]
121
+ end
122
+
123
+ def rbd_cache_image_spec(uri)
124
+ File.join(File.dirname(uri.path)[1..-1], options['cache_name'])
125
+ end
126
+
127
+ def rbd_cache_name
128
+ options['rbd_cache_name']
129
+ end
130
+
131
+ def rbd_clone_snapshot(cache=rbd_cache_name)
132
+ "#{cache}@#{rbd_base_name}"
133
+ end
134
+
135
+ def rbd_uri(image_spec)
136
+ URI.join('rbd:///', image_spec)
137
+ end
112
138
 
139
+ def rbd_sequence_complete?
140
+ from.path.include?(to.path)
141
+ end
142
+
143
+ def rbd_id
144
+ 'libvirt'
145
+ end
146
+
147
+ def in_rbd_cache?
148
+ options['in_rbd_cache']
149
+ end
150
+
151
+ def not_in_rbd_cache?
152
+ options['in_rbd_cache'] == false
153
+ end
154
+
155
+ def rbd_snapshot_spec?(uri)
156
+ uri.scheme == 'rbd' && uri.path.include?('@')
157
+ end
158
+
159
+ def in_rbd_cache(target)
160
+ result = false
161
+ if dry_run?
162
+ result = options['dry-cache'] && options['cache_name'] !~ /srv-...../
163
+ else
164
+ sh "rbd snap ls --id #{rbd_id} --format json #{target}" do |stdout|
165
+ result = JSON.parse(stdout).any? { |x| x['name'] == rbd_base_name }
166
+ end
167
+ end
168
+ result && rbd_clone_snapshot(target)
169
+ end
170
+ end
113
171
  end