tn_s3_file_uploader 0.1.0
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.
- checksums.yaml +15 -0
- data/LICENSE.txt +13 -0
- data/README.md +23 -0
- data/bin/tn_s3_file_uploader +11 -0
- data/lib/tn_s3_file_uploader/cli_parser.rb +89 -0
- data/lib/tn_s3_file_uploader/error_reporting/error_report_manager.rb +35 -0
- data/lib/tn_s3_file_uploader/error_reporting/honeybadger_error_reporter.rb +20 -0
- data/lib/tn_s3_file_uploader/error_reporting/log_error_reporter.rb +17 -0
- data/lib/tn_s3_file_uploader/file_path_generator.rb +96 -0
- data/lib/tn_s3_file_uploader/log_uploader.rb +64 -0
- data/lib/tn_s3_file_uploader/runner.rb +64 -0
- data/lib/tn_s3_file_uploader/s3.rb +55 -0
- data/lib/tn_s3_file_uploader/version.rb +3 -0
- data/lib/tn_s3_file_uploader.rb +9 -0
- metadata +86 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MGRmOTk0YjUyZjhhMTE5MjU5MjNhZjBiNmFmYTAwYWI2YThmMTVjYw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NmM5MWNhYzIyZDlhNGIzZjRjOWM0ZmM1MjViZGE4Zjg1N2Q5ODc5Zg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MDhlMTJmNDFkZDU2YWNjZGI4M2JhZmYwZWRmOWMwZjc2ZmM4MjQ5M2U2M2I0
|
10
|
+
ZDczMDFiMGFhYzAzYTdmN2M2MTMyODRmNGJmZTRkYmNlZjNiNGFiZWRlMTQ1
|
11
|
+
NTdmNTZlYzE1NTc3MjJhMmE5MjFiZTQ3MDBmMjNjM2FmODQyMDg=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MzhmN2EzMWE3ZGZjOTM3Mzc2YTliNWZlYjZjMmVlN2MxZTNmMDJhYTEzZTdl
|
14
|
+
MTkwYjZhYTBmOWZjMTMwYzQ5NjlmZDc4Y2ExM2FiNmM2ODI5N2JkYzliZWYw
|
15
|
+
NmU4Yjg3YzI3ZTllNzRkZTFlMmM0YWJmNTZmYTBmNTUzYWQ4OGE=
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2014 Telenav, Inc.
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
|
6
|
+
You may obtain a copy of the License at
|
7
|
+
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
|
10
|
+
Unless required by applicable law or agreed to in writing, software distributed
|
11
|
+
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
12
|
+
either express or implied. See the License for the specific language governing permissions and limitations
|
13
|
+
under the License.
|
data/README.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
tn_s3_file_uploader
|
2
|
+
===================
|
3
|
+
|
4
|
+
Introduction
|
5
|
+
------------
|
6
|
+
tn_s3_file_uploader is an Amazon S3 file uploader that can build destination folder structures based on the timestamp and
|
7
|
+
a number of other accepted substitutions.
|
8
|
+
|
9
|
+
Getting started
|
10
|
+
---------------
|
11
|
+
Visit the [Getting started wiki page](https://github.com/ThinkNear/tn_s3_file_uploader/wiki/Getting-started)
|
12
|
+
|
13
|
+
License
|
14
|
+
-------
|
15
|
+
See the `LICENSE.txt` file for license information.
|
16
|
+
|
17
|
+
Contribute
|
18
|
+
----------
|
19
|
+
Visit the [Contribute wiki page](https://github.com/ThinkNear/tn_s3_file_uploader/wiki/Contribute)
|
20
|
+
|
21
|
+
Questions? Problems? New Requests?
|
22
|
+
----------------------------------
|
23
|
+
Visit the [Troubleshooting wiki page](https://github.com/ThinkNear/tn_s3_file_uploader/wiki/Troubleshooting)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'tn_s3_file_uploader'
|
3
|
+
|
4
|
+
# Executable - usage tn_s3_file_uploader --input-file-pattern='<<pattern matching file to be uploaded>>'
|
5
|
+
# --s3-output-pattern=<<S3 destination folder>>
|
6
|
+
#
|
7
|
+
# e.g. tn_s3_file_uploader --input-file-pattern='/etc/sonarqube/logs/*.log'
|
8
|
+
# --s3-output-pattern=bucket/sonarqube/%{file-extension}/y=%Y/m=%m/d=%d/h=%H/%{file-name}.%{file-extension}
|
9
|
+
options = TnS3FileUploader::CliParser.new.parse_cmd_line(ARGV)
|
10
|
+
TnS3FileUploader::Runner.new(options).run
|
11
|
+
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module TnS3FileUploader
|
4
|
+
|
5
|
+
class CliParser
|
6
|
+
|
7
|
+
# Parses the CLI parameters and returns them in a hash
|
8
|
+
# Parameters 'host-unique-id', 'log-file-pattern', 's3-dest-folder' and 'partition-pattern' are mandatory and
|
9
|
+
# a missing argument exception will be raised if any of them is not provided
|
10
|
+
def parse_cmd_line(args)
|
11
|
+
options = {}
|
12
|
+
opts = OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: tn_s3_file_uploader.rb [options]"
|
14
|
+
|
15
|
+
opts.separator ""
|
16
|
+
opts.separator "Specific options:"
|
17
|
+
|
18
|
+
opts.on("-s", "--input-file-pattern INPUT_FILE_PATTERN", "The file pattern to match the source log file") do |file_pattern|
|
19
|
+
options[:input_file_pattern] = file_pattern
|
20
|
+
end
|
21
|
+
|
22
|
+
opts.on("-o", "--s3-output-pattern S3_OUTPUT_PATTERN", "The S3 destination pattern name."\
|
23
|
+
"It accepts macros for building the resulting filename and folder structure. See documentation for details") do |s3_output_pattern|
|
24
|
+
options[:s3_output_pattern] = s3_output_pattern
|
25
|
+
end
|
26
|
+
|
27
|
+
options[:file_timestamp_resolution] = 300
|
28
|
+
opts.on("--file-timestamp-resolution RES", Integer, "The resolution of the destination filename in seconds (positive non-zero integer)") do |file_timestamp_resolution|
|
29
|
+
if valid_seconds?(file_timestamp_resolution)
|
30
|
+
options[:file_timestamp_resolution] = file_timestamp_resolution
|
31
|
+
else
|
32
|
+
puts "Warning: negative seconds value given: #{file_timestamp_resolution}, defaulting to 300 (5 minutes)"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on("--honeybadger-api-key API-KEY", "API key for optional honeybadger error reporting support") do |honeybadger_api_key|
|
37
|
+
options[:honeybadger_api_key] = honeybadger_api_key
|
38
|
+
end
|
39
|
+
|
40
|
+
opts.on("--aws-access-key-id AWS-ACCESS-KEY-ID", "Provide the AWS access key id.") do |aws_access_key_id|
|
41
|
+
options[:aws_access_key_id] = aws_access_key_id
|
42
|
+
end
|
43
|
+
|
44
|
+
opts.on("--aws-secret-access-key AWS-SECRET-ACCESS-KEY", "Provide the AWS secret access key") do |aws_secret_access_key|
|
45
|
+
options[:aws_secret_access_key] = aws_secret_access_key
|
46
|
+
end
|
47
|
+
|
48
|
+
opts.on("-v", "--verbose", "Display verbose output") do |v|
|
49
|
+
options[:verbose] = !v.nil?
|
50
|
+
end
|
51
|
+
|
52
|
+
opts.separator ""
|
53
|
+
|
54
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
55
|
+
puts opts
|
56
|
+
exit!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
opts.parse! args
|
61
|
+
|
62
|
+
check_mandatory options
|
63
|
+
|
64
|
+
options
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def check_mandatory(options)
|
70
|
+
mandatory_arguments = [:input_file_pattern, :s3_output_pattern]
|
71
|
+
|
72
|
+
missing_arguments = []
|
73
|
+
mandatory_arguments.each do |arg|
|
74
|
+
missing_arguments << arg.to_s.gsub!('_', '-') if options[arg].nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
unless missing_arguments.empty?
|
78
|
+
raise OptionParser::MissingArgument,
|
79
|
+
"The following mandatory options are missing: #{ missing_arguments.join(', ') }"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def valid_seconds?(seconds)
|
84
|
+
seconds > 0
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'tn_s3_file_uploader/error_reporting/log_error_reporter'
|
3
|
+
|
4
|
+
module TnS3FileUploader
|
5
|
+
|
6
|
+
# Singleton that instruments all error reporters.
|
7
|
+
# Register your error reporter by calling `ErrorReportManager.instance.register_error_reporter`
|
8
|
+
# error reporters provided have to specify a method with name `report_error`
|
9
|
+
class ErrorReportManager
|
10
|
+
include Singleton
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@error_reporters = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def register_error_reporter(error_reporter)
|
17
|
+
unless error_reporter.respond_to?(:report_error)
|
18
|
+
raise ArgumentError, 'Provided error_reporter instance does not support the report_error method'
|
19
|
+
end
|
20
|
+
|
21
|
+
@error_reporters << error_reporter
|
22
|
+
end
|
23
|
+
|
24
|
+
def count_error_reporters
|
25
|
+
@error_reporters.count
|
26
|
+
end
|
27
|
+
|
28
|
+
def report_error(exception, options = {})
|
29
|
+
@error_reporters.each do |error_reporter|
|
30
|
+
error_reporter.report_error(exception, options)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'honeybadger'
|
2
|
+
|
3
|
+
module TnS3FileUploader
|
4
|
+
|
5
|
+
# Error reporter that uses Honeybadger service to report errors
|
6
|
+
class HoneybadgerErrorReporter
|
7
|
+
|
8
|
+
# Configure honeybadger with provided api-key. Assumes api-key is not null
|
9
|
+
def initialize(api_key)
|
10
|
+
Honeybadger.configure do |config|
|
11
|
+
config.api_key = api_key
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def report_error(exception, options = {})
|
16
|
+
Honeybadger.notify(exception, options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module TnS3FileUploader
|
2
|
+
|
3
|
+
# Simple error reporter that logs exception message and backtrace to stdout
|
4
|
+
class LogErrorReporter
|
5
|
+
|
6
|
+
def initialize(output)
|
7
|
+
@output = output
|
8
|
+
end
|
9
|
+
|
10
|
+
def report_error(exception, options ={} )
|
11
|
+
@output.puts options
|
12
|
+
@output.puts exception.message
|
13
|
+
@output.puts exception.backtrace.join("\n")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
3
|
+
module TnS3FileUploader
|
4
|
+
|
5
|
+
# Examples
|
6
|
+
# partition = y=2014/m=06/d=18/h=18
|
7
|
+
# minute_partition = 45
|
8
|
+
# file_timestamp = 20140618184502
|
9
|
+
# date = Wed Jun 18 18:50:02 UTC 2014
|
10
|
+
|
11
|
+
# [ec2-user@ip-10-185-180-243 tmp]$ ./time.sh
|
12
|
+
# partition = y=2014/m=06/d=18/h=18
|
13
|
+
# minute_partition = 55
|
14
|
+
# file_timestamp = 20140618185540
|
15
|
+
# date = Wed Jun 18 19:03:40 UTC 2014
|
16
|
+
class FilePathGenerator
|
17
|
+
|
18
|
+
def initialize(time, options)
|
19
|
+
#Find the last rotation window
|
20
|
+
@options = options
|
21
|
+
@time = previous_rotation_window(time)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
# Makes datetime and macro substitutions for input file 'file', based on the s3_output_pattern option
|
26
|
+
# Assumes input file 'file' and s3_output_pattern option are both valid.
|
27
|
+
# This method removes the bucket (everything until the first '/', including the '/') from the s3_output_pattern
|
28
|
+
# while applying the datetime/macro/substitutions
|
29
|
+
def dest_full_path_for(file)
|
30
|
+
|
31
|
+
output_file_pattern = remove_bucket(@options[:s3_output_pattern])
|
32
|
+
|
33
|
+
# Time#strftime is removing '%' characters on our macros. Our macro substitution must run first
|
34
|
+
subs = build_substitutions(file)
|
35
|
+
replace_macros!(output_file_pattern, subs)
|
36
|
+
|
37
|
+
substitute_datetime_macros(output_file_pattern)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def remove_bucket(output_file_pattern)
|
43
|
+
output_file_pattern.split('/')[1..-1].join('/')
|
44
|
+
end
|
45
|
+
|
46
|
+
# Makes the datetime substitutions on the give s3_output_pattern option
|
47
|
+
# For example:
|
48
|
+
# Given s3_output_pattern y=%Y/m=%m/d=%d/h=%H
|
49
|
+
# and time: Thu Jun 12 23:57:49 UTC 2014
|
50
|
+
# it will produce the following folder structure: y=2014/m=06/d=12/h=23
|
51
|
+
def substitute_datetime_macros(output_pattern)
|
52
|
+
@time.strftime(output_pattern)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Generates rounded off timestamp based on rotation_seconds
|
56
|
+
def generate_file_timestamp
|
57
|
+
@time.strftime('%Y%m%d%H%M%S')
|
58
|
+
end
|
59
|
+
|
60
|
+
def replace_macros!(output_file_pattern, subs)
|
61
|
+
subs.each do |macro, sub|
|
62
|
+
output_file_pattern.gsub!(macro, sub)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def build_substitutions(file)
|
67
|
+
file_components = file.split('/').last.split('.')
|
68
|
+
|
69
|
+
if file_components.size == 1
|
70
|
+
file_name = file_components[0]
|
71
|
+
file_extension = ''
|
72
|
+
else
|
73
|
+
file_name = file_components[0..-2].join('.')
|
74
|
+
file_extension = file_components.last
|
75
|
+
end
|
76
|
+
|
77
|
+
ip_address = IPSocket.getaddress(Socket.gethostname).gsub('.', '-')
|
78
|
+
file_timestamp = generate_file_timestamp
|
79
|
+
|
80
|
+
{
|
81
|
+
'%{file-name}' => file_name,
|
82
|
+
'%{file-timestamp}' => file_timestamp,
|
83
|
+
'%{file-extension}' => file_extension,
|
84
|
+
'%{ip-address}' => ip_address
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def previous_rotation_window(time)
|
89
|
+
rotation_seconds = @options[:file_timestamp_resolution]
|
90
|
+
t = time - rotation_seconds
|
91
|
+
floored_seconds = (t.to_f / rotation_seconds).floor * rotation_seconds
|
92
|
+
Time.at(floored_seconds).utc
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'tn_s3_file_uploader/s3'
|
3
|
+
require 'tn_s3_file_uploader/file_path_generator'
|
4
|
+
|
5
|
+
module TnS3FileUploader
|
6
|
+
|
7
|
+
class LogUploader
|
8
|
+
|
9
|
+
# Initialisation block
|
10
|
+
# Params:
|
11
|
+
# ::s3:: - S3 wrapper
|
12
|
+
def initialize(s3)
|
13
|
+
raise ArgumentError, "s3 client cannot be nil" if s3 == nil
|
14
|
+
@s3 = s3
|
15
|
+
end
|
16
|
+
|
17
|
+
# Uploads all (log) files that options[:input_file_pattern] matches to an S3 location
|
18
|
+
# based on the value of options[:s3_output_pattern]
|
19
|
+
# options[:input_file_pattern] should match at least one local file
|
20
|
+
# options[:s3_output_pattern] should contain the bucket, folder and destination filename
|
21
|
+
def upload_log_files(options)
|
22
|
+
raise ArgumentError, 's3_output_pattern cannot be empty' if blank?(options[:s3_output_pattern])
|
23
|
+
bucket = check_bucket_dest_path(options[:s3_output_pattern])
|
24
|
+
log_files = check_log_file(options[:input_file_pattern])
|
25
|
+
|
26
|
+
now = Time.now.utc
|
27
|
+
file_path_generator = FilePathGenerator.new(now, options)
|
28
|
+
|
29
|
+
log_files.each do |log_file|
|
30
|
+
destination_full_path = file_path_generator.dest_full_path_for(log_file)
|
31
|
+
|
32
|
+
puts "Found log file #{ log_file }, formatting file name for upload to S3 bucket #{ bucket } into folder #{ destination_full_path }"
|
33
|
+
|
34
|
+
# Note no leading or trailing slashes - this will break the upload to S3 (see our s3.rb)
|
35
|
+
@s3.upload_file(log_file, bucket, destination_full_path)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
def check_log_file(log_file_pattern)
|
42
|
+
raise ArgumentError, 'log file pattern cannot be nil' if log_file_pattern == nil
|
43
|
+
|
44
|
+
last_folder_separator = log_file_pattern.rindex('.')
|
45
|
+
raise ArgumentError, "#{ log_file_pattern } is not a valid path. It lacks a file extension." if last_folder_separator == nil
|
46
|
+
|
47
|
+
files = Dir[log_file_pattern].entries
|
48
|
+
raise ArgumentError, "#{ log_file_pattern } did not match any files." if files.empty?
|
49
|
+
|
50
|
+
files
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_bucket_dest_path(bucket_dest_path)
|
54
|
+
path_components = bucket_dest_path.split('/')
|
55
|
+
raise ArgumentError, "Bucket destination folder #{ bucket_dest_path } must have at least two path components, e.g. my/path." unless path_components.size > 1
|
56
|
+
path_components.first
|
57
|
+
end
|
58
|
+
|
59
|
+
def blank?(str)
|
60
|
+
str.nil? || str == ""
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'aws-sdk'
|
3
|
+
require 'honeybadger'
|
4
|
+
require 'tn_s3_file_uploader/log_uploader'
|
5
|
+
|
6
|
+
module TnS3FileUploader
|
7
|
+
class Runner
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@options = options
|
11
|
+
@error_report_manager = ErrorReportManager.instance
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
add_log_error_reporter
|
16
|
+
add_honeybadger
|
17
|
+
puts "Running TnS3FileUploader..." if @options[:verbose]
|
18
|
+
|
19
|
+
upload
|
20
|
+
rescue Exception => e
|
21
|
+
@error_report_manager.report_error(e, { :options => @options } )
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def upload
|
27
|
+
if @options[:verbose]
|
28
|
+
puts "Using:"
|
29
|
+
puts "log file pattern = #{ @options[:input_file_pattern] }"
|
30
|
+
puts "s3 dest folder = #{ @options[:s3_output_pattern] }"
|
31
|
+
puts "file timestamp resolution = #{ options[:file_timestamp_resolution] }"
|
32
|
+
end
|
33
|
+
|
34
|
+
s3_client = create_s3_client
|
35
|
+
s3 = S3.new(s3_client)
|
36
|
+
|
37
|
+
log_uploader = LogUploader.new(s3)
|
38
|
+
log_uploader.upload_log_files(@options)
|
39
|
+
end
|
40
|
+
|
41
|
+
def create_s3_client
|
42
|
+
if @options[:aws_access_key_id].nil? && @options[:aws_secret_access_key].nil?
|
43
|
+
AWS::S3.new
|
44
|
+
else
|
45
|
+
AWS::S3.new(
|
46
|
+
:access_key_id => @options[:aws_access_key_id],
|
47
|
+
:secret_access_key => @options[:aws_secret_access_key]
|
48
|
+
)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_log_error_reporter
|
53
|
+
@error_report_manager.register_error_reporter(LogErrorReporter.new(STDOUT))
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_honeybadger
|
57
|
+
unless @options[:honeybadger_api_key].nil?
|
58
|
+
honeybadger_error_reporter = HoneybadgerErrorReporter.new(@options[:honeybadger_api_key])
|
59
|
+
@error_report_manager.register_error_reporter(honeybadger_error_reporter)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'aws-sdk'
|
3
|
+
require 'aws-sdk'
|
4
|
+
require 'honeybadger'
|
5
|
+
require 'tn_s3_file_uploader/file_path_generator'
|
6
|
+
module TnS3FileUploader
|
7
|
+
|
8
|
+
class S3
|
9
|
+
|
10
|
+
MAX_RETRIES = 2
|
11
|
+
|
12
|
+
def initialize(s3_client)
|
13
|
+
@s3_client = s3_client
|
14
|
+
end
|
15
|
+
|
16
|
+
# File must be fully qualified
|
17
|
+
# bucket is just string name, no slashes
|
18
|
+
# dest_path is fully qualified path to file on S3 including folders - NO leading or trailing slashes or
|
19
|
+
# it won't work!
|
20
|
+
def upload_file(file, bucket, dest_path)
|
21
|
+
raise ArgumentError, "file cannot be nil" if file == nil
|
22
|
+
raise ArgumentError, "bucket cannot be nil" if bucket == nil
|
23
|
+
raise ArgumentError, "dest_path cannot be nil" if dest_path == nil
|
24
|
+
|
25
|
+
file_path = Pathname.new(file)
|
26
|
+
raise ArgumentError, "#{file} is not a valid file" unless file_path.exist? && file_path.file?
|
27
|
+
|
28
|
+
upload(bucket, dest_path, file)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def upload(bucket, dest_full_path, file, retry_count = 0)
|
33
|
+
begin
|
34
|
+
s3_bucket = @s3_client.buckets[bucket]
|
35
|
+
s3_file_path = s3_bucket.objects[dest_full_path]
|
36
|
+
|
37
|
+
puts "Uploading file #{file} to S3 bucket #{bucket} and path #{dest_full_path}"
|
38
|
+
|
39
|
+
s3_file_path.write(File.open(file, 'rb'))
|
40
|
+
rescue StandardError, Timeout::Error => e
|
41
|
+
if retry_count < MAX_RETRIES
|
42
|
+
#This fixes a bug where the credentials may have rotated on the EC2 instance but the old values
|
43
|
+
#are still cached
|
44
|
+
sleep 1
|
45
|
+
@s3_client.config.credential_provider.refresh
|
46
|
+
upload(bucket, dest_full_path, file, retry_count + 1)
|
47
|
+
else
|
48
|
+
raise e
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'tn_s3_file_uploader/runner'
|
2
|
+
require 'tn_s3_file_uploader/cli_parser'
|
3
|
+
require 'tn_s3_file_uploader/file_path_generator'
|
4
|
+
require 'tn_s3_file_uploader/log_uploader'
|
5
|
+
require 'tn_s3_file_uploader/s3'
|
6
|
+
|
7
|
+
require 'tn_s3_file_uploader/error_reporting/error_report_manager'
|
8
|
+
require 'tn_s3_file_uploader/error_reporting/honeybadger_error_reporter'
|
9
|
+
require 'tn_s3_file_uploader/error_reporting/log_error_reporter'
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tn_s3_file_uploader
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Thinknear.com
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: honeybadger
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.15'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.15'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.35'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.35'
|
41
|
+
description: S3 file uploader that can build folder structures based on timestamp.
|
42
|
+
Typically used in conjunction with Unix's logrotate.
|
43
|
+
email: opensource@thinknear.com
|
44
|
+
executables:
|
45
|
+
- tn_s3_file_uploader
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- LICENSE.txt
|
50
|
+
- README.md
|
51
|
+
- bin/tn_s3_file_uploader
|
52
|
+
- lib/tn_s3_file_uploader.rb
|
53
|
+
- lib/tn_s3_file_uploader/cli_parser.rb
|
54
|
+
- lib/tn_s3_file_uploader/error_reporting/error_report_manager.rb
|
55
|
+
- lib/tn_s3_file_uploader/error_reporting/honeybadger_error_reporter.rb
|
56
|
+
- lib/tn_s3_file_uploader/error_reporting/log_error_reporter.rb
|
57
|
+
- lib/tn_s3_file_uploader/file_path_generator.rb
|
58
|
+
- lib/tn_s3_file_uploader/log_uploader.rb
|
59
|
+
- lib/tn_s3_file_uploader/runner.rb
|
60
|
+
- lib/tn_s3_file_uploader/s3.rb
|
61
|
+
- lib/tn_s3_file_uploader/version.rb
|
62
|
+
homepage: http://www.thinknear.com
|
63
|
+
licenses:
|
64
|
+
- Copyright (c) ThinkNear 2014, Licensed under APLv2.0
|
65
|
+
metadata: {}
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ! '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ! '>='
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
requirements: []
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 2.2.2
|
83
|
+
signing_key:
|
84
|
+
specification_version: 4
|
85
|
+
summary: S3 file uploader
|
86
|
+
test_files: []
|