s3cp 1.1.29 → 1.1.30

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/History.txt CHANGED
@@ -1,3 +1,11 @@
1
+ === 1.1.30 (2013-05-15)
2
+
3
+ * Changed: Improved error handling -- all commands now exit with non-zero exit
4
+ code on error. [Alexis Midon / alexism@github / Pull request #11]
5
+
6
+ * Changed: s3cp properly fails when source key is not specified or does not exist.
7
+ [Alexis Midon / alexism@github / Pull request #11]
8
+
1
9
  === 1.1.29 (2013-05-15)
2
10
 
3
11
  * Added: s3buckets command may now be used to delete buckets and enable/suspend
@@ -58,10 +58,11 @@ op = OptionParser.new do |opts|
58
58
  end
59
59
  op.parse!(ARGV)
60
60
 
61
- S3CP.load_config()
61
+ S3CP.standard_exception_handling(options) do
62
+
63
+ S3CP.load_config()
64
+ s3 = S3CP.connect()
62
65
 
63
- s3 = S3CP.connect()
64
- begin
65
66
  if options[:create]
66
67
  name = options[:create]
67
68
  create_options = {}
@@ -82,10 +83,5 @@ begin
82
83
  puts bucket.name
83
84
  end
84
85
  end
85
- rescue => e
86
- $stderr.print "s3buckets: [#{e.class}] #{e.message}\n"
87
- if options[:debug]
88
- $stderr.print e.backtrace.join("\n") + "\n"
89
- end
90
86
  end
91
87
 
data/lib/s3cp/s3cat.rb CHANGED
@@ -73,7 +73,7 @@ if options[:debug]
73
73
  puts "Options: \n#{options.inspect}"
74
74
  end
75
75
 
76
- begin
76
+ S3CP.standard_exception_handling(options) do
77
77
  @bucket, @prefix = S3CP.bucket_and_key(url)
78
78
  fail "Your URL looks funny, doesn't it?" unless @bucket
79
79
 
@@ -134,17 +134,11 @@ begin
134
134
  else
135
135
  @s3.objects[@prefix].read_as_stream(read_options) do |chunk|
136
136
  begin
137
- STDOUT.print(chunk)
137
+ $stdout.print(chunk)
138
138
  rescue Errno::EPIPE
139
139
  break
140
140
  end
141
141
  end
142
142
  end
143
-
144
- rescue => e
145
- $stderr.print "s3cat: [#{e.class}] #{e.message}\n"
146
- if options[:debug]
147
- $stderr.print e.backtrace.join("\n") + "\n"
148
- end
149
143
  end
150
144
 
data/lib/s3cp/s3cp.rb CHANGED
@@ -148,12 +148,12 @@ if ARGV.size < 2
148
148
  end
149
149
 
150
150
  if options[:include_regex].any? && !options[:recursive]
151
- STDERR.puts "-i (--include regex) option requires -r (recursive) option."
151
+ $stderr.puts "-i (--include regex) option requires -r (recursive) option."
152
152
  exit(1)
153
153
  end
154
154
 
155
155
  if options[:exclude_regex].any? && !options[:recursive]
156
- STDERR.puts "-x (--exclude regex) option requires -r (recursive) option."
156
+ $stderr.puts "-x (--exclude regex) option requires -r (recursive) option."
157
157
  exit(1)
158
158
  end
159
159
 
@@ -161,9 +161,9 @@ destination = ARGV.last
161
161
  sources = ARGV[0..-2]
162
162
 
163
163
  if options[:debug]
164
- STDERR.puts "sources: #{sources.inspect}"
165
- STDERR.puts "destination: #{destination}"
166
- STDERR.puts "Options: \n#{options.inspect}"
164
+ $stderr.puts "sources: #{sources.inspect}"
165
+ $stderr.puts "destination: #{destination}"
166
+ $stderr.puts "Options: \n#{options.inspect}"
167
167
  end
168
168
 
169
169
  class ProxyIO
@@ -282,7 +282,7 @@ def local_to_s3(bucket_to, key, file, options = {})
282
282
  when :not_found
283
283
  nil
284
284
  when :invalid
285
- STDERR.puts "Warning: No MD5 checksum available and ETag not suitable due to multi-part upload; file will be force-copied."
285
+ $stderr.puts "Warning: No MD5 checksum available and ETag not suitable due to multi-part upload; file will be force-copied."
286
286
  nil
287
287
  else
288
288
  md5
@@ -297,7 +297,7 @@ def local_to_s3(bucket_to, key, file, options = {})
297
297
  end
298
298
  if retries > 0
299
299
  delay = options[:retry_delay] * (options[:retry_backoff] ** retries)
300
- STDERR.puts "Sleeping #{"%0.2f" % delay} seconds. Will retry #{options[:retries] - retries} more time(s)."
300
+ $stderr.puts "Sleeping #{"%0.2f" % delay} seconds. Will retry #{options[:retries] - retries} more time(s)."
301
301
  sleep delay
302
302
  end
303
303
 
@@ -338,10 +338,10 @@ def local_to_s3(bucket_to, key, file, options = {})
338
338
  actual_md5 = s3_checksum(bucket_to, key)
339
339
  if actual_md5.is_a? String
340
340
  if actual_md5 != expected_md5
341
- STDERR.puts "Warning: invalid MD5 checksum. Expected: #{expected_md5} Actual: #{actual_md5}"
341
+ $stderr.puts "Warning: invalid MD5 checksum. Expected: #{expected_md5} Actual: #{actual_md5}"
342
342
  end
343
343
  else
344
- STDERR.puts "Warning: invalid MD5 checksum in metadata: #{actual_md5.inspect}; skipped checksum verification."
344
+ $stderr.puts "Warning: invalid MD5 checksum in metadata: #{actual_md5.inspect}; skipped checksum verification."
345
345
  actual_md5 = nil
346
346
  end
347
347
  end
@@ -349,10 +349,10 @@ def local_to_s3(bucket_to, key, file, options = {})
349
349
  actual_md5 = "bad"
350
350
  if progress_bar
351
351
  progress_bar.clear
352
- STDERR.puts "Error copying #{file} to s3://#{bucket_to}/#{key}"
352
+ $stderr.puts "Error copying #{file} to s3://#{bucket_to}/#{key}"
353
353
  end
354
354
  raise e if !options[:checksum] || e.is_a?(AWS::S3::Errors::AccessDenied)
355
- STDERR.puts e
355
+ $stderr.puts e
356
356
  end
357
357
  retries += 1
358
358
  end until options[:checksum] == false || actual_md5.nil? || expected_md5 == actual_md5
@@ -364,6 +364,7 @@ end
364
364
 
365
365
  def s3_to_local(bucket_from, key_from, dest, options = {})
366
366
  log("#{operation(options)} s3://#{bucket_from}/#{key_from} to #{dest}")
367
+ raise ArgumentError, "source key may not be blank" if key_from.to_s.empty?
367
368
 
368
369
  retries = 0
369
370
  begin
@@ -374,17 +375,17 @@ def s3_to_local(bucket_from, key_from, dest, options = {})
374
375
  if retries > 0
375
376
  delay = options[:retry_delay] * (options[:retry_backoff] ** retries)
376
377
  delay = delay.to_i
377
- STDERR.puts "Sleeping #{"%0.2f" % delay} seconds. Will retry #{options[:retries] - retries} more time(s)."
378
+ $stderr.puts "Sleeping #{"%0.2f" % delay} seconds. Will retry #{options[:retries] - retries} more time(s)."
378
379
  sleep delay
379
380
  end
380
381
  begin
381
382
  expected_md5 = if options[:checksum] || options[:sync]
382
383
  md5 = s3_checksum(bucket_from, key_from)
383
384
  if options[:sync] && !md5.is_a?(String)
384
- STDERR.puts "Warning: invalid MD5 checksum in metadata; file will be force-copied."
385
+ $stderr.puts "Warning: invalid MD5 checksum in metadata; file will be force-copied."
385
386
  nil
386
387
  elsif !md5.is_a? String
387
- STDERR.puts "Warning: invalid MD5 checksum in metadata; skipped checksum verification."
388
+ $stderr.puts "Warning: invalid MD5 checksum in metadata; skipped checksum verification."
388
389
  nil
389
390
  else
390
391
  md5
@@ -420,14 +421,15 @@ def s3_to_local(bucket_from, key_from, dest, options = {})
420
421
  return
421
422
  end
422
423
  rescue => e
424
+ raise e if e.is_a?(AWS::S3::Errors::NoSuchKey)
423
425
  raise e unless options[:checksum]
424
- STDERR.puts e
426
+ $stderr.puts e
425
427
  end
426
428
 
427
429
  if options[:checksum] && expected_md5 != nil
428
430
  actual_md5 = S3CP.md5(dest)
429
431
  if actual_md5 != expected_md5
430
- STDERR.puts "Warning: invalid MD5 checksum. Expected: #{expected_md5} Actual: #{actual_md5}"
432
+ $stderr.puts "Warning: invalid MD5 checksum. Expected: #{expected_md5} Actual: #{actual_md5}"
431
433
  end
432
434
  end
433
435
 
@@ -487,7 +489,7 @@ def copy(from, to, options)
487
489
  if match(key)
488
490
  dest = key_path key_to, relative(key_from, key)
489
491
  if !options[:overwrite] && s3_exist?(bucket_to, dest)
490
- STDERR.puts "Skipping s3://#{bucket_to}/#{dest} - already exists."
492
+ $stderr.puts "Skipping s3://#{bucket_to}/#{dest} - already exists."
491
493
  else
492
494
  s3_to_s3(bucket_from, key, bucket_to, dest, options)
493
495
  end
@@ -496,7 +498,7 @@ def copy(from, to, options)
496
498
  else
497
499
  key_to += File.basename(key_from) if key_to[-1..-1] == "/"
498
500
  if !options[:overwrite] && s3_exist?(bucket_to, key_to)
499
- STDERR.puts "Skipping s3://#{bucket_to}/#{key_to} - already exists."
501
+ $stderr.puts "Skipping s3://#{bucket_to}/#{key_to} - already exists."
500
502
  else
501
503
  s3_to_s3(bucket_from, key_from, bucket_to, key_to, options)
502
504
  end
@@ -511,7 +513,7 @@ def copy(from, to, options)
511
513
  #puts "relative(from, f) #{relative(from, f)}"
512
514
  key = key_path key_to, relative(from, f)
513
515
  if !options[:overwrite] && s3_exist?(bucket_to, key)
514
- STDERR.puts "Skipping s3://#{bucket_to}/#{key} - already exists."
516
+ $stderr.puts "Skipping s3://#{bucket_to}/#{key} - already exists."
515
517
  else
516
518
  local_to_s3(bucket_to, key, File.expand_path(f), options)
517
519
  end
@@ -520,7 +522,7 @@ def copy(from, to, options)
520
522
  else
521
523
  key_to += File.basename(from) if key_to[-1..-1] == "/"
522
524
  if !options[:overwrite] && s3_exist?(bucket_to, key_to)
523
- STDERR.puts "Skipping s3://#{bucket_to}/#{key_to} - already exists."
525
+ $stderr.puts "Skipping s3://#{bucket_to}/#{key_to} - already exists."
524
526
  else
525
527
  local_to_s3(bucket_to, key_to, File.expand_path(from), options)
526
528
  end
@@ -539,7 +541,7 @@ def copy(from, to, options)
539
541
  FileUtils.mkdir_p dir unless File.exist? dir
540
542
  fail "Destination path is not a directory: #{dir}" unless File.directory?(dir)
541
543
  if !options[:overwrite] && File.exist?(dest)
542
- STDERR.puts "Skipping #{dest} - already exists."
544
+ $stderr.puts "Skipping #{dest} - already exists."
543
545
  else
544
546
  s3_to_local(bucket_from, key, dest, options)
545
547
  end
@@ -549,7 +551,7 @@ def copy(from, to, options)
549
551
  dest = File.expand_path(to)
550
552
  dest = File.join(dest, File.basename(key_from)) if File.directory?(dest)
551
553
  if !options[:overwrite] && File.exist?(dest)
552
- STDERR.puts "Skipping #{dest} - already exists."
554
+ $stderr.puts "Skipping #{dest} - already exists."
553
555
  else
554
556
  s3_to_local(bucket_from, key_from, dest, options)
555
557
  end
@@ -568,13 +570,8 @@ def copy(from, to, options)
568
570
  end
569
571
  end
570
572
 
571
- begin
573
+ S3CP.standard_exception_handling(options) do
572
574
  sources.each do |source|
573
575
  copy(source, destination, options)
574
576
  end
575
- rescue => e
576
- $stderr.print "s3cp: [#{e.class}] #{e.message}\n"
577
- if options[:debug]
578
- $stderr.print e.backtrace.join("\n") + "\n"
579
- end
580
577
  end
data/lib/s3cp/s3du.rb CHANGED
@@ -67,9 +67,6 @@ url = ARGV[0]
67
67
  @bucket, @prefix = S3CP.bucket_and_key(url)
68
68
  fail "Your URL looks funny, doesn't it?" unless @bucket
69
69
 
70
- S3CP.load_config()
71
-
72
- @s3 = S3CP.connect()
73
70
 
74
71
  def depth(path)
75
72
  path.count("/")
@@ -98,56 +95,57 @@ def print(key, size)
98
95
  puts ("%#{7 + @options[:precision]}s " % size) + key
99
96
  end
100
97
 
101
- begin
102
- s3_options = Hash.new
103
- s3_options[:bucket_name] = @bucket
104
- s3_options[:prefix] = @prefix
105
-
106
- begin
107
- response = @s3.client.list_objects(s3_options)
108
- response[:contents].each do |object|
109
-
110
- key = object[:key]
111
- size = object[:size].to_i
98
+ S3CP.standard_exception_handling(options) do
112
99
 
113
- if options[:regex].nil? || options[:regex].match(key)
114
- current_key = if actual_depth
115
- pos = nth_occurrence(key, "/", actual_depth)
116
- (pos != -1) ? key[0..pos-1] : key
117
- end
100
+ S3CP.load_config()
101
+ @s3 = S3CP.connect()
118
102
 
119
- if (last_key && last_key != current_key)
120
- print(last_key, subtotal_size)
121
- subtotal_size = size
122
- else
123
- subtotal_size += size
103
+ begin
104
+ s3_options = Hash.new
105
+ s3_options[:bucket_name] = @bucket
106
+ s3_options[:prefix] = @prefix
107
+
108
+ begin
109
+ response = @s3.client.list_objects(s3_options)
110
+ response[:contents].each do |object|
111
+
112
+ key = object[:key]
113
+ size = object[:size].to_i
114
+
115
+ if options[:regex].nil? || options[:regex].match(key)
116
+ current_key = if actual_depth
117
+ pos = nth_occurrence(key, "/", actual_depth)
118
+ (pos != -1) ? key[0..pos-1] : key
119
+ end
120
+
121
+ if (last_key && last_key != current_key)
122
+ print(last_key, subtotal_size)
123
+ subtotal_size = size
124
+ else
125
+ subtotal_size += size
126
+ end
127
+
128
+ last_key = current_key
129
+ total_size += size
124
130
  end
125
-
126
- last_key = current_key
127
- total_size += size
128
131
  end
129
- end
130
132
 
131
- break if response[:contents].empty?
133
+ break if response[:contents].empty?
132
134
 
133
- s3_options.merge!(:marker => response[:contents].last[:key])
134
- end while response[:truncated]
135
+ s3_options.merge!(:marker => response[:contents].last[:key])
136
+ end while response[:truncated]
135
137
 
136
- if last_key != nil
137
- print(last_key, subtotal_size)
138
- end
138
+ if last_key != nil
139
+ print(last_key, subtotal_size)
140
+ end
139
141
 
140
- if options[:depth] > 0
141
- print("", total_size)
142
- else
143
- puts S3CP.format_filesize(total_size, :unit => options[:unit], :precision => options[:precision])
144
- end
145
- rescue Errno::EPIPE
146
- # ignore
147
- rescue => e
148
- $stderr.print "s3du: [#{e.class}] #{e.message}\n"
149
- if options[:debug]
150
- $stderr.print e.backtrace.join("\n") + "\n"
142
+ if options[:depth] > 0
143
+ print("", total_size)
144
+ else
145
+ puts S3CP.format_filesize(total_size, :unit => options[:unit], :precision => options[:precision])
146
+ end
147
+ rescue Errno::EPIPE
148
+ # ignore
151
149
  end
152
150
  end
153
151
 
@@ -71,9 +71,6 @@ if ARGV.size == 0
71
71
  exit
72
72
  end
73
73
 
74
- S3CP.load_config()
75
- @s3 = S3CP.connect()
76
-
77
74
  def time_or_date_str(msg, t)
78
75
  if t.is_a?(Fixnum) || t.to_s =~ /^\d+$/
79
76
  "%s after %d days" % [msg, t.to_i]
@@ -91,83 +88,88 @@ def rule_to_str(r)
91
88
  [ r.prefix || "[root]", r.id, r.status, "???"]
92
89
  end
93
90
  end
91
+ S3CP.standard_exception_handling(options) do
92
+
93
+ S3CP.load_config()
94
+ @s3 = S3CP.connect()
95
+
96
+ paths.each do |path|
97
+ bucket,key = S3CP.bucket_and_key(path)
98
+ fail "Invalid bucket/key: #{path}" unless key
94
99
 
95
- paths.each do |path|
96
- bucket,key = S3CP.bucket_and_key(path)
97
- fail "Invalid bucket/key: #{path}" unless key
98
-
99
- case
100
-
101
- when options[:expire]
102
- @s3.buckets[bucket].lifecycle_configuration.update do
103
- rule_options = {}
104
- rule_options[:id] = options[:name] if options[:name]
105
- rule_options[:expiration_time] = S3CP.parse_days_or_date(options[:expire])
106
- add_rule(key, rule_options)
107
- end
108
-
109
- when options[:glacier]
110
- @s3.buckets[bucket].lifecycle_configuration.update do
111
- rule_options = {}
112
- rule_options[:id] = options[:name] if options[:name]
113
- rule_options[:glacier_transition_time] = S3CP.parse_days_or_date(options[:glacier])
114
- add_rule(key, rule_options)
115
- end
116
-
117
- when options[:enable]
118
- success = false
119
- @s3.buckets[bucket].lifecycle_configuration.update do
120
- self.rules.each do |r|
121
- if (r.prefix == key) || (r.id == options[:name])
122
- r.enable!
123
- puts "Enabled rule: "
124
- puts S3CP.tableify([rule_to_str(r)])
125
- success = true
100
+ case
101
+
102
+ when options[:expire]
103
+ @s3.buckets[bucket].lifecycle_configuration.update do
104
+ rule_options = {}
105
+ rule_options[:id] = options[:name] if options[:name]
106
+ rule_options[:expiration_time] = S3CP.parse_days_or_date(options[:expire])
107
+ add_rule(key, rule_options)
108
+ end
109
+
110
+ when options[:glacier]
111
+ @s3.buckets[bucket].lifecycle_configuration.update do
112
+ rule_options = {}
113
+ rule_options[:id] = options[:name] if options[:name]
114
+ rule_options[:glacier_transition_time] = S3CP.parse_days_or_date(options[:glacier])
115
+ add_rule(key, rule_options)
116
+ end
117
+
118
+ when options[:enable]
119
+ success = false
120
+ @s3.buckets[bucket].lifecycle_configuration.update do
121
+ self.rules.each do |r|
122
+ if (r.prefix == key) || (r.id == options[:name])
123
+ r.enable!
124
+ puts "Enabled rule: "
125
+ puts S3CP.tableify([rule_to_str(r)])
126
+ success = true
127
+ end
126
128
  end
127
129
  end
128
- end
129
- fail "Rule or prefix not found" unless success
130
-
131
- when options[:disable]
132
- success = false
133
- @s3.buckets[bucket].lifecycle_configuration.update do
134
- self.rules.each do |r|
135
- if (r.prefix == key) || (r.id == options[:name])
136
- r.disabled!
137
- puts "Disabled rule: "
138
- puts S3CP.tableify([rule_to_str(r)])
139
- success = true
130
+ fail "Rule or prefix not found" unless success
131
+
132
+ when options[:disable]
133
+ success = false
134
+ @s3.buckets[bucket].lifecycle_configuration.update do
135
+ self.rules.each do |r|
136
+ if (r.prefix == key) || (r.id == options[:name])
137
+ r.disabled!
138
+ puts "Disabled rule: "
139
+ puts S3CP.tableify([rule_to_str(r)])
140
+ success = true
141
+ end
140
142
  end
141
143
  end
142
- end
143
- fail "Rule or prefix not found" unless success
144
-
145
- when options[:delete]
146
- success = false
147
- @s3.buckets[bucket].lifecycle_configuration.update do
148
- self.rules.each do |r|
149
- if (r.prefix == key) || (r.id == options[:name])
150
- remove_rule(r)
151
- puts "Deleted rule: "
152
- puts S3CP.tableify([rule_to_str(r)])
153
- success = true
144
+ fail "Rule or prefix not found" unless success
145
+
146
+ when options[:delete]
147
+ success = false
148
+ @s3.buckets[bucket].lifecycle_configuration.update do
149
+ self.rules.each do |r|
150
+ if (r.prefix == key) || (r.id == options[:name])
151
+ remove_rule(r)
152
+ puts "Deleted rule: "
153
+ puts S3CP.tableify([rule_to_str(r)])
154
+ success = true
155
+ end
154
156
  end
155
157
  end
156
- end
157
- fail "Rule or prefix not found" unless success
158
+ fail "Rule or prefix not found" unless success
158
159
 
159
- else
160
- rules = @s3.buckets[bucket].lifecycle_configuration.rules.to_a
161
- if rules.empty?
162
- puts "#{bucket} - no lifecycle rules"
163
160
  else
164
- puts "#{bucket} - lifecycle rules:"
165
- begin
166
- puts S3CP.tableify(rules.map { |r| rule_to_str(r) })
167
- rescue => e
168
- puts rules.inspect
169
- raise e
161
+ rules = @s3.buckets[bucket].lifecycle_configuration.rules.to_a
162
+ if rules.empty?
163
+ puts "#{bucket} - no lifecycle rules"
164
+ else
165
+ puts "#{bucket} - lifecycle rules:"
166
+ begin
167
+ puts S3CP.tableify(rules.map { |r| rule_to_str(r) })
168
+ rescue => e
169
+ puts rules.inspect
170
+ raise e
171
+ end
170
172
  end
171
- end
173
+ end
172
174
  end
173
175
  end
data/lib/s3cp/s3ls.rb CHANGED
@@ -90,75 +90,74 @@ if options[:debug]
90
90
  puts "key #{@key}"
91
91
  end
92
92
 
93
- S3CP.load_config()
94
-
95
- @s3 = S3CP.connect()
96
93
 
97
94
  keys = 0
98
95
  rows = 0
99
96
  directories = true
100
97
 
101
- begin
102
- display = lambda do |entry|
103
- # add '---' separator line between directories and files
104
- if options[:delimiter] && directories && entry.is_a?(AWS::S3::Tree::LeafNode)
105
- directories = false
106
- puts "---"
98
+ S3CP.standard_exception_handling(options) do
99
+
100
+ S3CP.load_config()
101
+
102
+ @s3 = S3CP.connect()
103
+
104
+ begin
105
+ display = lambda do |entry|
106
+ # add '---' separator line between directories and files
107
+ if options[:delimiter] && directories && entry.is_a?(AWS::S3::Tree::LeafNode)
108
+ directories = false
109
+ puts "---"
110
+ end
111
+
112
+ key = "s3://#{@bucket}/#{entry.respond_to?(:key) ? entry.key : entry.prefix}"
113
+ if options[:long_format] && entry.last_modified && entry.content_length
114
+ size = entry.content_length
115
+ size = S3CP.format_filesize(size, :unit => options[:unit], :precision => options[:precision])
116
+ size = ("%#{7 + options[:precision]}s " % size)
117
+ puts "#{entry.last_modified.strftime(options[:date_format])} #{size} #{key}"
118
+ else
119
+ puts key
120
+ end
121
+ rows += 1
122
+ keys += 1
123
+ response = ''
124
+
125
+ if options[:rows_per_page] && (rows % options[:rows_per_page] == 0)
126
+ begin
127
+ print "Continue? (Y/n) "
128
+ response = STDIN.gets.chomp.downcase
129
+ end until response == 'n' || response == 'y' || response == ''
130
+ end
131
+ (response == 'n')
107
132
  end
108
133
 
109
- key = "s3://#{@bucket}/#{entry.respond_to?(:key) ? entry.key : entry.prefix}"
110
- if options[:long_format] && entry.last_modified && entry.content_length
111
- size = entry.content_length
112
- size = S3CP.format_filesize(size, :unit => options[:unit], :precision => options[:precision])
113
- size = ("%#{7 + options[:precision]}s " % size)
114
- puts "#{entry.last_modified.strftime(options[:date_format])} #{size} #{key}"
134
+ if options[:delimiter]
135
+ @s3.buckets[@bucket].objects.with_prefix(@key).as_tree(:delimier => options[:delimiter], :append => false).children.each do |entry|
136
+ break if display.call(entry)
137
+ end
115
138
  else
116
- puts key
117
- end
118
- rows += 1
119
- keys += 1
120
- response = ''
139
+ Struct.new("S3Entry", :key, :last_modified, :content_length)
121
140
 
122
- if options[:rows_per_page] && (rows % options[:rows_per_page] == 0)
123
- begin
124
- print "Continue? (Y/n) "
125
- response = STDIN.gets.chomp.downcase
126
- end until response == 'n' || response == 'y' || response == ''
127
- end
128
- (response == 'n')
129
- end
141
+ s3_options = Hash.new
142
+ s3_options[:bucket_name] = @bucket
143
+ s3_options[:prefix] = @key
144
+ s3_options[:limit] = options[:max_keys] if options[:max_keys]
130
145
 
131
- if options[:delimiter]
132
- @s3.buckets[@bucket].objects.with_prefix(@key).as_tree(:delimier => options[:delimiter], :append => false).children.each do |entry|
133
- break if display.call(entry)
146
+ stop = false
147
+
148
+ begin
149
+ response = @s3.client.list_objects(s3_options)
150
+ response[:contents].each do |object|
151
+ entry = Struct::S3Entry.new(object[:key], object[:last_modified], object[:size].to_i)
152
+ stop = display.call(entry)
153
+ break if stop
154
+ end
155
+ break if stop || response[:contents].empty?
156
+ s3_options.merge!(:marker => response[:contents].last[:key])
157
+ end while response[:truncated]
134
158
  end
135
- else
136
- Struct.new("S3Entry", :key, :last_modified, :content_length)
137
-
138
- s3_options = Hash.new
139
- s3_options[:bucket_name] = @bucket
140
- s3_options[:prefix] = @key
141
- s3_options[:limit] = options[:max_keys] if options[:max_keys]
142
-
143
- stop = false
144
-
145
- begin
146
- response = @s3.client.list_objects(s3_options)
147
- response[:contents].each do |object|
148
- entry = Struct::S3Entry.new(object[:key], object[:last_modified], object[:size].to_i)
149
- stop = display.call(entry)
150
- break if stop
151
- end
152
- break if stop || response[:contents].empty?
153
- s3_options.merge!(:marker => response[:contents].last[:key])
154
- end while response[:truncated]
155
- end
156
- rescue Errno::EPIPE
157
- # ignore
158
- rescue => e
159
- $stderr.print "s3ls: [#{e.class}] #{e.message}\n"
160
- if options[:debug]
161
- $stderr.print e.backtrace.join("\n") + "\n"
159
+ rescue Errno::EPIPE
160
+ # ignore
162
161
  end
163
162
  end
164
163
 
data/lib/s3cp/s3mod.rb CHANGED
@@ -91,7 +91,7 @@ if !options[:acl] && paths.size > 1 && S3CP::LEGAL_MODS.include?(paths.last)
91
91
  options[:acl] = S3CP.validate_acl(paths.pop);
92
92
  end
93
93
 
94
- begin
94
+ S3CP.standard_exception_handling(options) do
95
95
  S3CP.load_config()
96
96
  @s3 = S3CP.connect()
97
97
 
@@ -112,9 +112,4 @@ begin
112
112
  end
113
113
  }
114
114
  end
115
- rescue => e
116
- $stderr.print "s3mod: [#{e.class}] #{e.message}\n"
117
- if options[:debug]
118
- $stderr.print e.backtrace.join("\n") + "\n"
119
- end
120
115
  end
data/lib/s3cp/s3rm.rb CHANGED
@@ -102,7 +102,7 @@ end
102
102
  include_regex = options[:include_regex] ? Regexp.new(options[:include_regex]) : nil
103
103
  exclude_regex = options[:exclude_regex] ? Regexp.new(options[:exclude_regex]) : nil
104
104
 
105
- begin
105
+ S3CP.standard_exception_handling(options) do
106
106
  S3CP.load_config()
107
107
 
108
108
  @s3 = S3CP.connect()
@@ -147,10 +147,5 @@ begin
147
147
  raise e unless e.is_a? AWS::S3::Errors::NoSuchKey
148
148
  end
149
149
  end
150
- rescue => e
151
- $stderr.print "s3rm: [#{e.class}] #{e.message}\n"
152
- if options[:debug]
153
- $stderr.print e.backtrace.join("\n") + "\n"
154
- end
155
150
  end
156
151
 
data/lib/s3cp/s3stat.rb CHANGED
@@ -45,19 +45,21 @@ permission = ARGV.last
45
45
  @bucket, @key = S3CP.bucket_and_key(source)
46
46
  fail "Your URL looks funny, doesn't it?" unless @bucket
47
47
 
48
- S3CP.load_config()
48
+ S3CP.standard_exception_handling(options) do
49
+ S3CP.load_config()
49
50
 
50
- @s3 = S3CP.connect().buckets[@bucket]
51
+ @s3 = S3CP.connect().buckets[@bucket]
51
52
 
52
- obj = @s3.objects[@key]
53
+ obj = @s3.objects[@key]
53
54
 
54
- metadata = obj.head
55
- metadata.to_h.keys.sort { |k1, k2| k1.to_s <=> k2.to_s}.each do |k|
56
- puts "#{"%30s" % k} #{metadata[k].is_a?(Hash) ? metadata[k].inspect : metadata[k].to_s}"
57
- end
55
+ metadata = obj.head
56
+ metadata.to_h.keys.sort { |k1, k2| k1.to_s <=> k2.to_s}.each do |k|
57
+ puts "#{"%30s" % k} #{metadata[k].is_a?(Hash) ? metadata[k].inspect : metadata[k].to_s}"
58
+ end
58
59
 
59
- if @options[:acl]
60
- puts
61
- xml = Nokogiri::XML(obj.acl.to_s)
62
- puts xml.to_s
60
+ if @options[:acl]
61
+ puts
62
+ xml = Nokogiri::XML(obj.acl.to_s)
63
+ puts xml.to_s
64
+ end
63
65
  end
data/lib/s3cp/s3tree.rb CHANGED
@@ -68,104 +68,101 @@ url = ARGV[0]
68
68
  @bucket, @key = S3CP.bucket_and_key(url)
69
69
  fail "Your URL looks funny, doesn't it?" unless @bucket
70
70
 
71
- S3CP.load_config()
72
-
73
- @s3 = S3CP.connect().buckets[@bucket]
74
71
 
75
72
  keys = 0
76
73
  rows = 0
77
74
 
78
- begin
79
- # find last index of character `ch` in `str`.
80
- last_index_of = lambda do |str, ch|
81
- case
82
- when str[ch] then str.length-(str.reverse.index(ch)+1)
83
- else -1
84
- end
85
- end
75
+ S3CP.standard_exception_handling(options) do
76
+ S3CP.load_config()
77
+ @s3 = S3CP.connect().buckets[@bucket]
86
78
 
87
- # displays the next line, returns true if user interrupts or max keys shown
88
- display_line = lambda do |line|
89
- puts line
90
- keys += 1
91
- if options[:max_keys] && keys > options[:max_keys]
92
- return true
93
- end
94
- rows += 1
95
- response = ''
96
- if options[:rows_per_page] && (rows % options[:rows_per_page] == 0)
97
- begin
98
- print "Continue? (Y/n) "
99
- response = STDIN.gets.chomp.downcase
100
- end until response == 'n' || response == 'y' || response == ''
79
+ begin
80
+ # find last index of character `ch` in `str`.
81
+ last_index_of = lambda do |str, ch|
82
+ case
83
+ when str[ch] then str.length-(str.reverse.index(ch)+1)
84
+ else -1
85
+ end
101
86
  end
102
- (response == 'n')
103
- end
104
87
 
105
- # returns relative path against @key
106
- #
107
- # e.g. relative.call("foo/bar") => "bar" (assuming @key = "foo/")
108
- #
109
- relative = lambda do |key|
110
- last_delimiter = last_index_of.call(@key, options[:delimiter])
111
- (last_delimiter != -1) ? key[last_delimiter..-1] : key
112
- end
88
+ # displays the next line, returns true if user interrupts or max keys shown
89
+ display_line = lambda do |line|
90
+ puts line
91
+ keys += 1
92
+ if options[:max_keys] && keys > options[:max_keys]
93
+ return true
94
+ end
95
+ rows += 1
96
+ response = ''
97
+ if options[:rows_per_page] && (rows % options[:rows_per_page] == 0)
98
+ begin
99
+ print "Continue? (Y/n) "
100
+ response = STDIN.gets.chomp.downcase
101
+ end until response == 'n' || response == 'y' || response == ''
102
+ end
103
+ (response == 'n')
104
+ end
113
105
 
114
- # trim up to the last delimiter
115
- #
116
- # e.g. trim.call("foo/bar") => "foo/"
117
- #
118
- trim = lambda do |key|
119
- last_delimiter = last_index_of.call(key, options[:delimiter])
120
- (last_delimiter != -1) ? key[0..last_delimiter] : ""
121
- end
106
+ # returns relative path against @key
107
+ #
108
+ # e.g. relative.call("foo/bar") => "bar" (assuming @key = "foo/")
109
+ #
110
+ relative = lambda do |key|
111
+ last_delimiter = last_index_of.call(@key, options[:delimiter])
112
+ (last_delimiter != -1) ? key[last_delimiter..-1] : key
113
+ end
122
114
 
123
- # recursively display tree elements
124
- #
125
- # +prefix+: line prefix
126
- # +children+: children of the current directory
127
- # +depth+: current directory depth
128
- display_tree = lambda do |prefix, children, depth|
129
- stop = false
130
- children = children.to_a # aws-sdk returns a sucky ChildCollection object
131
- children.each_with_index do |node, index|
132
- node = node
133
- if options[:directories_only] && node.leaf?
134
- next
135
- end
115
+ # trim up to the last delimiter
116
+ #
117
+ # e.g. trim.call("foo/bar") => "foo/"
118
+ #
119
+ trim = lambda do |key|
120
+ last_delimiter = last_index_of.call(key, options[:delimiter])
121
+ (last_delimiter != -1) ? key[0..last_delimiter] : ""
122
+ end
136
123
 
137
- last = (index == children.size - 1)
138
- has_siblings = (children.size > 1)
139
- key = node.branch? ? node.prefix : node.key
140
- parts = relative.call(key).split(options[:delimiter])
141
- postfix = last ? '└── ' : '├── '
124
+ # recursively display tree elements
125
+ #
126
+ # +prefix+: line prefix
127
+ # +children+: children of the current directory
128
+ # +depth+: current directory depth
129
+ display_tree = lambda do |prefix, children, depth|
130
+ stop = false
131
+ children = children.to_a # aws-sdk returns a sucky ChildCollection object
132
+ children.each_with_index do |node, index|
133
+ node = node
134
+ if options[:directories_only] && node.leaf?
135
+ next
136
+ end
137
+
138
+ last = (index == children.size - 1)
139
+ has_siblings = (children.size > 1)
140
+ key = node.branch? ? node.prefix : node.key
141
+ parts = relative.call(key).split(options[:delimiter])
142
+ postfix = last ? '└── ' : '├── '
143
+
144
+ stop = display_line.call(prefix + postfix + parts.last)
145
+ break if stop
142
146
 
143
- stop = display_line.call(prefix + postfix + parts.last)
144
- break if stop
147
+ if node.branch? && depth < options[:max_depth]
148
+ new_prefix = prefix + (has_siblings ? "│ " : " ")
149
+ stop = display_tree.call(new_prefix, node.children, depth + 1)
150
+ break if stop
151
+ end
145
152
 
146
- if node.branch? && depth < options[:max_depth]
147
- new_prefix = prefix + (has_siblings ? "│ " : " ")
148
- stop = display_tree.call(new_prefix, node.children, depth + 1)
149
153
  break if stop
150
154
  end
151
-
152
- break if stop
155
+ stop
153
156
  end
154
- stop
155
- end
156
157
 
157
- display_line.call("s3://#{@bucket}/#{trim.call(@key)}")
158
-
159
- prefix = ""
160
- root = @s3.objects.with_prefix(@key).as_tree( :delimier => options[:delimiter], :append => false)
161
- depth = 1
162
- display_tree.call(prefix, root.children, depth)
163
- rescue Errno::EPIPE
164
- # ignore
165
- rescue => e
166
- $stderr.print "s3tree: [#{e.class}] #{e.message}\n"
167
- if options[:debug]
168
- $stderr.print e.backtrace.join("\n") + "\n"
158
+ display_line.call("s3://#{@bucket}/#{trim.call(@key)}")
159
+
160
+ prefix = ""
161
+ root = @s3.objects.with_prefix(@key).as_tree( :delimier => options[:delimiter], :append => false)
162
+ depth = 1
163
+ display_tree.call(prefix, root.children, depth)
164
+ rescue Errno::EPIPE
165
+ # ignore
169
166
  end
170
167
  end
171
168
 
data/lib/s3cp/s3up.rb CHANGED
@@ -69,9 +69,8 @@ url = ARGV[0]
69
69
  bucket, key = S3CP.bucket_and_key(url)
70
70
  fail "Your URL looks funny, doesn't it?" unless bucket
71
71
 
72
- begin
72
+ S3CP.standard_exception_handling(options) do
73
73
  S3CP.load_config()
74
-
75
74
  @s3 = S3CP.connect()
76
75
 
77
76
  # copy all of STDIN to a temp file
@@ -93,17 +92,12 @@ begin
93
92
  S3CP.set_header_options(s3_options, @headers)
94
93
  s3_options[:acl] = options[:acl]
95
94
  @s3.buckets[bucket].objects[key].write(temp, s3_options)
96
- STDERR.puts "s3://#{bucket}/#{key} => #{S3CP.format_filesize(temp.size)} "
95
+ $stderr.puts "s3://#{bucket}/#{key} => #{S3CP.format_filesize(temp.size)} "
97
96
  ensure
98
97
  # cleanup
99
98
  temp.close
100
99
  temp.delete
101
100
  end
102
- rescue => e
103
- $stderr.print "s3up: [#{e.class}] #{e.message}\n"
104
- if options[:debug]
105
- $stderr.print e.backtrace.join("\n") + "\n"
106
- end
107
101
  end
108
102
 
109
103
 
data/lib/s3cp/utils.rb CHANGED
@@ -250,6 +250,20 @@ module S3CP
250
250
  yield bucket.objects[key]
251
251
  end
252
252
  end
253
+
254
+ def standard_exception_handling(options)
255
+ begin
256
+ yield
257
+ rescue Exception => ex
258
+ cmd_name ||= File.basename(caller[1].split(/:/)[0], '.*')
259
+ $stderr.print "#{cmd_name} [#{ex.class}] #{ex.message}\n"
260
+ if options[:debug]
261
+ $stderr.print ex.backtrace.join("\n") + "\n"
262
+ end
263
+ exit 1
264
+ end
265
+ end
266
+
253
267
  end
254
268
 
255
269
  # Monkey-patch S3 object for download streaming
data/lib/s3cp/version.rb CHANGED
@@ -16,5 +16,5 @@
16
16
  # the License.
17
17
 
18
18
  module S3CP
19
- VERSION = "1.1.29"
19
+ VERSION = "1.1.30"
20
20
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: s3cp
3
3
  version: !ruby/object:Gem::Version
4
- hash: 41
4
+ hash: 47
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
8
  - 1
9
- - 29
10
- version: 1.1.29
9
+ - 30
10
+ version: 1.1.30
11
11
  platform: ruby
12
12
  authors:
13
13
  - Alex Boisvert