uricp 0.0.15 → 0.0.20

Sign up to get free protection for your applications and to get access to all the features.
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