rest-ftp-daemon 0.423.3 → 0.424.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 +4 -4
- data/.codeclimate.yml +30 -0
- data/Gemfile.lock +9 -8
- data/README.md +1 -5
- data/bin/rest-ftp-daemon +11 -5
- data/defaults.yml +16 -10
- data/lib/rest-ftp-daemon.rb +14 -10
- data/lib/rest-ftp-daemon/api/jobs.rb +42 -18
- data/lib/rest-ftp-daemon/api/root.rb +5 -1
- data/lib/rest-ftp-daemon/constants.rb +0 -1
- data/lib/rest-ftp-daemon/{api/entities → entities}/job.rb +0 -0
- data/lib/rest-ftp-daemon/{api/entities → entities}/location.rb +0 -0
- data/lib/rest-ftp-daemon/{api/entities → entities}/options.rb +0 -0
- data/lib/rest-ftp-daemon/exceptions.rb +1 -0
- data/lib/rest-ftp-daemon/helpers/views.rb +7 -2
- data/lib/rest-ftp-daemon/initialize.rb +3 -0
- data/lib/rest-ftp-daemon/job.rb +3 -0
- data/lib/rest-ftp-daemon/jobs/transfer.rb +4 -4
- data/lib/rest-ftp-daemon/location.rb +5 -0
- data/lib/rest-ftp-daemon/remote/base.rb +76 -0
- data/lib/rest-ftp-daemon/remote/ftp.rb +144 -0
- data/lib/rest-ftp-daemon/remote/s3.rb +78 -0
- data/lib/rest-ftp-daemon/remote/sftp.rb +147 -0
- data/lib/rest-ftp-daemon/static/images/feature_reload.png +0 -0
- data/lib/rest-ftp-daemon/static/images/feature_rollbar.png +0 -0
- data/lib/rest-ftp-daemon/static/swagger/css/print.css +2 -2
- data/lib/rest-ftp-daemon/static/swagger/css/screen.css +2 -2
- data/lib/rest-ftp-daemon/views/dashboard_footer.haml +3 -2
- data/lib/rest-ftp-daemon/views/dashboard_jobs.haml +3 -3
- data/lib/rest-ftp-daemon/views/dashboard_table.haml +5 -4
- data/lib/rest-ftp-daemon/workers/reporter.rb +2 -2
- data/rest-ftp-daemon.gemspec +4 -7
- metadata +28 -25
- data/lib/rest-ftp-daemon/remote.rb +0 -74
- data/lib/rest-ftp-daemon/remote_ftp.rb +0 -142
- data/lib/rest-ftp-daemon/remote_s3.rb +0 -76
- data/lib/rest-ftp-daemon/remote_sftp.rb +0 -145
@@ -0,0 +1,76 @@
|
|
1
|
+
# Handles transfers for Job class
|
2
|
+
module RestFtpDaemon
|
3
|
+
module Remote
|
4
|
+
class RemoteBase
|
5
|
+
include BmcDaemonLib::LoggerHelper
|
6
|
+
|
7
|
+
# Class options
|
8
|
+
attr_reader :logger
|
9
|
+
attr_reader :log_prefix
|
10
|
+
attr_accessor :job
|
11
|
+
|
12
|
+
# Delegate set_info info to Job
|
13
|
+
delegate :set_info, to: :job
|
14
|
+
|
15
|
+
def initialize target, log_prefix, debug = false, ftpes = false
|
16
|
+
# Init
|
17
|
+
@target = target
|
18
|
+
@ftpes = ftpes
|
19
|
+
@debug = !!debug
|
20
|
+
|
21
|
+
# Build and empty job to protect set_info delegation
|
22
|
+
@job = Job.new(nil, {})
|
23
|
+
|
24
|
+
# Logger
|
25
|
+
@log_prefix = log_prefix || {}
|
26
|
+
@logger = BmcDaemonLib::LoggerPool.instance.get :transfer
|
27
|
+
|
28
|
+
# Annnounce object
|
29
|
+
log_info "Remote.initialize debug[#{debug}] target[#{target.path}] "
|
30
|
+
|
31
|
+
# Prepare real object
|
32
|
+
prepare
|
33
|
+
end
|
34
|
+
|
35
|
+
def prepare
|
36
|
+
end
|
37
|
+
|
38
|
+
def connect
|
39
|
+
# Debug mode ?
|
40
|
+
return unless @debug
|
41
|
+
puts
|
42
|
+
puts "-------------------- SESSION STARTING -------------------------"
|
43
|
+
puts "class\t #{myname}"
|
44
|
+
puts "host\t #{@target.host}"
|
45
|
+
puts "user\t #{@target.user}"
|
46
|
+
puts "port\t #{@target.port}"
|
47
|
+
puts "---------------------------------------------------------------"
|
48
|
+
end
|
49
|
+
|
50
|
+
def chdir_or_create directory, mkdir = false
|
51
|
+
end
|
52
|
+
|
53
|
+
def remove! target
|
54
|
+
end
|
55
|
+
|
56
|
+
def close
|
57
|
+
# Debug mode ?
|
58
|
+
return unless @debug
|
59
|
+
puts "-------------------- SESSION CLOSING --------------------------"
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def extract_parent path
|
65
|
+
return unless path.is_a? String
|
66
|
+
m = path.match(/^(.*)\/([^\/]+)\/?$/)
|
67
|
+
return m[1], m[2] unless m.nil?
|
68
|
+
end
|
69
|
+
|
70
|
+
def myname
|
71
|
+
self.class.to_s
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require "net/ftp"
|
2
|
+
require "double_bag_ftps"
|
3
|
+
|
4
|
+
# Handle FTP and FTPeS transfers for Remote class
|
5
|
+
module RestFtpDaemon
|
6
|
+
module Remote
|
7
|
+
class RemoteFTP < RemoteBase
|
8
|
+
|
9
|
+
# Class options
|
10
|
+
attr_reader :ftp
|
11
|
+
|
12
|
+
def prepare
|
13
|
+
# Create FTP object
|
14
|
+
if @ftpes
|
15
|
+
prepare_ftpes
|
16
|
+
else
|
17
|
+
prepare_ftp
|
18
|
+
end
|
19
|
+
@ftp.passive = true
|
20
|
+
@ftp.debug_mode = @debug
|
21
|
+
|
22
|
+
# Config
|
23
|
+
@chunk_size = DEFAULT_FTP_CHUNK.to_i * 1024
|
24
|
+
|
25
|
+
# Announce object
|
26
|
+
log_debug "Remote::RemoteFTP.prepare chunk_size:#{@chunk_size}"
|
27
|
+
end
|
28
|
+
|
29
|
+
def connect
|
30
|
+
super
|
31
|
+
|
32
|
+
# Connect remote server
|
33
|
+
@ftp.connect @target.host, @target.port
|
34
|
+
@ftp.login @target.user, @target.password
|
35
|
+
end
|
36
|
+
|
37
|
+
def present? target
|
38
|
+
size = @ftp.size target.path
|
39
|
+
log_debug "Remote::RemoteFTP.present? [#{target.name}]"
|
40
|
+
|
41
|
+
rescue Net::FTPPermError
|
42
|
+
return false
|
43
|
+
else
|
44
|
+
return size
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove! target
|
48
|
+
@ftp.delete target.path
|
49
|
+
rescue Net::FTPPermError
|
50
|
+
log_debug "Remote::RemoteFTP.remove! [#{target.name}] not found"
|
51
|
+
else
|
52
|
+
log_debug "Remote::RemoteFTP.remove! [#{target.name}] removed"
|
53
|
+
end
|
54
|
+
|
55
|
+
def mkdir directory
|
56
|
+
log_debug "Remote::RemoteFTP.mkdir [#{directory}]"
|
57
|
+
@ftp.mkdir directory
|
58
|
+
|
59
|
+
rescue StandardError => ex
|
60
|
+
raise TargetPermissionError, ex.message
|
61
|
+
end
|
62
|
+
|
63
|
+
def chdir_or_create directory, mkdir = false
|
64
|
+
# Init, extract my parent name and my own name
|
65
|
+
log_debug "Remote::RemoteFTP.chdir_or_create mkdir[#{mkdir}] dir[#{directory}]"
|
66
|
+
parent, current = extract_parent(directory)
|
67
|
+
|
68
|
+
#dirname, _current = extract_parent(directory)
|
69
|
+
|
70
|
+
|
71
|
+
# Access this directory
|
72
|
+
begin
|
73
|
+
@ftp.chdir "/#{directory}"
|
74
|
+
|
75
|
+
rescue Net::FTPPermError => _e
|
76
|
+
# If not allowed to create path, that's over, we're stuck
|
77
|
+
return false unless mkdir
|
78
|
+
chdir_or_create parent, mkdir
|
79
|
+
|
80
|
+
# Now I was able to chdir into my parent, create the current directory
|
81
|
+
mkdir current
|
82
|
+
|
83
|
+
# Finally retry the chdir
|
84
|
+
retry
|
85
|
+
else
|
86
|
+
return true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def upload source, target, use_temp_name = false, &callback
|
91
|
+
# Push init
|
92
|
+
raise RestFtpDaemon::AssertionFailed, "upload/ftp" if @ftp.nil?
|
93
|
+
|
94
|
+
# Temp file if needed
|
95
|
+
dest = target.clone
|
96
|
+
if use_temp_name
|
97
|
+
dest.generate_temp_name!
|
98
|
+
end
|
99
|
+
|
100
|
+
# Move to the directory
|
101
|
+
log_debug "Remote::RemoteFTP.upload chdir [#{dest.dir}]"
|
102
|
+
@ftp.chdir "/#{dest.dir}"
|
103
|
+
|
104
|
+
# Do the transfer
|
105
|
+
log_debug "Remote::RemoteFTP.upload putbinaryfile [#{dest.name}]"
|
106
|
+
@ftp.putbinaryfile source.path, dest.name, @chunk_size do |data|
|
107
|
+
# Update job status after this block transfer
|
108
|
+
yield data.bytesize, dest.name
|
109
|
+
end
|
110
|
+
|
111
|
+
# Move the file back to its original name
|
112
|
+
if use_temp_name
|
113
|
+
log_debug "Remote::RemoteFTP.upload rename [#{dest.name}] > [#{target.name}]"
|
114
|
+
@ftp.rename dest.name, target.name
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def close
|
119
|
+
# Close init
|
120
|
+
super
|
121
|
+
|
122
|
+
# Close FTP connexion and free up memory
|
123
|
+
@ftp.close
|
124
|
+
end
|
125
|
+
|
126
|
+
def connected?
|
127
|
+
!@ftp.welcome.nil?
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def prepare_ftp
|
133
|
+
@ftp = Net::FTP.new
|
134
|
+
end
|
135
|
+
|
136
|
+
def prepare_ftpes
|
137
|
+
@ftp = DoubleBagFTPS.new
|
138
|
+
@ftp.ssl_context = DoubleBagFTPS.create_ssl_context(verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
139
|
+
@ftp.ftps_mode = DoubleBagFTPS::EXPLICIT
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'aws-sdk-resources'
|
2
|
+
|
3
|
+
# Handle sFTP transfers for Remote class
|
4
|
+
module RestFtpDaemon
|
5
|
+
module Remote
|
6
|
+
class RemoteS3 < RemoteBase
|
7
|
+
|
8
|
+
MULTIPART_THRESHOLD_MB = 4
|
9
|
+
|
10
|
+
# Class options
|
11
|
+
attr_reader :client
|
12
|
+
attr_reader :target
|
13
|
+
|
14
|
+
def prepare
|
15
|
+
@multipart_threshold = MULTIPART_THRESHOLD_MB.to_i * 1024 * 1024
|
16
|
+
log_debug "Remote::RemoteS3.prepare target[#{@target.inspect}] #{@multipart_threshold}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def connect
|
20
|
+
super
|
21
|
+
|
22
|
+
# Connect init
|
23
|
+
log_debug "Remote::RemoteS3.connect region[#{target.aws_region}] id[#{target.aws_id}]"
|
24
|
+
|
25
|
+
# Connect remote server
|
26
|
+
@client = Aws::S3::Resource.new(
|
27
|
+
region: @target.aws_region,
|
28
|
+
credentials: Aws::Credentials.new(@target.aws_id, @target.aws_secret),
|
29
|
+
http_wire_trace: @debug
|
30
|
+
)
|
31
|
+
#s3 = Aws::S3::Client.new(http_wire_trace: true)
|
32
|
+
end
|
33
|
+
|
34
|
+
def upload source, target, use_temp_name = false, &callback
|
35
|
+
# Push init
|
36
|
+
raise RestFtpDaemon::AssertionFailed, "upload/client" if @client.nil?
|
37
|
+
log_debug "Remote::RemoteS3.upload bucket[#{target.aws_bucket}] name[#{target.name}]"
|
38
|
+
|
39
|
+
# Update progress before
|
40
|
+
#yield 0, target.name
|
41
|
+
# Point to the right bucket and object
|
42
|
+
bucket = @client.bucket(target.aws_bucket)
|
43
|
+
object = bucket.object(target.name)
|
44
|
+
|
45
|
+
# Do the transfer
|
46
|
+
object.upload_file(source.path, {
|
47
|
+
multipart_threshold: @multipart_threshold
|
48
|
+
})
|
49
|
+
|
50
|
+
# Wait for transfer to complete
|
51
|
+
object.wait_until_exists do |waiter|
|
52
|
+
# waiter.delay = 1
|
53
|
+
# # log_debug "- progress[#{progress}] total[#{total}]"
|
54
|
+
# waiter.before_wait do |attempts, response|
|
55
|
+
# puts "#{attempts} made"
|
56
|
+
# puts response.error.inspect
|
57
|
+
# puts response.data.inspect
|
58
|
+
# end
|
59
|
+
# log_debug "- progress[] #{waiter.inspect}"
|
60
|
+
end
|
61
|
+
|
62
|
+
# Update progress after
|
63
|
+
#yield target.size, target.name
|
64
|
+
|
65
|
+
# Dump information about this file
|
66
|
+
log_debug "Remote::RemoteS3.upload url[#{object.public_url}]"
|
67
|
+
log_debug "Remote::RemoteS3.upload etag[#{object.etag}]"
|
68
|
+
set_info :target_aws_public_url, object.public_url
|
69
|
+
set_info :target_aws_etag, object.etag
|
70
|
+
end
|
71
|
+
|
72
|
+
def connected?
|
73
|
+
!@client.nil?
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require "net/sftp"
|
2
|
+
|
3
|
+
# Handle sFTP transfers for Remote class
|
4
|
+
module RestFtpDaemon
|
5
|
+
module Remote
|
6
|
+
class RemoteSFTP < RemoteBase
|
7
|
+
|
8
|
+
# Class options
|
9
|
+
attr_reader :sftp
|
10
|
+
|
11
|
+
def prepare
|
12
|
+
end
|
13
|
+
|
14
|
+
def connect
|
15
|
+
super
|
16
|
+
|
17
|
+
# Connect init
|
18
|
+
log_debug "Remote::RemoteSFTP.connect [#{@target.user}]@[#{@target.host}]:[#{@target.port}]"
|
19
|
+
|
20
|
+
# Debug level
|
21
|
+
verbosity = @debug ? Logger::DEBUG : false
|
22
|
+
|
23
|
+
# Connect remote server
|
24
|
+
@sftp = Net::SFTP.start(@target.host.to_s, @target.user.to_s,
|
25
|
+
password: @target.password.to_s,
|
26
|
+
verbose: verbosity,
|
27
|
+
port: @target.port,
|
28
|
+
non_interactive: true,
|
29
|
+
timeout: DEFAULT_SFTP_TIMEOUT
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def present? target
|
34
|
+
log_debug "Remote::RemoteSFTP.present? [#{target.name}]"
|
35
|
+
stat = @sftp.stat! target.path
|
36
|
+
|
37
|
+
rescue Net::SFTP::StatusException
|
38
|
+
return false
|
39
|
+
else
|
40
|
+
return stat.size
|
41
|
+
end
|
42
|
+
|
43
|
+
def remove! target
|
44
|
+
log_debug "Remote::RemoteSFTP.remove! [#{target.name}]"
|
45
|
+
@sftp.remove target.path
|
46
|
+
|
47
|
+
rescue Net::SFTP::StatusException
|
48
|
+
log_debug "#{LOG_INDENT}[#{target.name}] file not found"
|
49
|
+
else
|
50
|
+
log_debug "#{LOG_INDENT}[#{target.name}] removed"
|
51
|
+
end
|
52
|
+
|
53
|
+
def mkdir directory
|
54
|
+
log_debug "Remote::RemoteSFTP.mkdir [#{directory}]"
|
55
|
+
@sftp.mkdir! directory
|
56
|
+
|
57
|
+
rescue StandardError => ex
|
58
|
+
raise TargetPermissionError, ex.message
|
59
|
+
end
|
60
|
+
|
61
|
+
def chdir_or_create directory, mkdir = false
|
62
|
+
# Init, extract my parent name and my own name
|
63
|
+
log_debug "Remote::RemoteSFTP.chdir_or_create mkdir[#{mkdir}] dir[#{directory}]"
|
64
|
+
parent, _current = extract_parent(directory)
|
65
|
+
|
66
|
+
# Access this directory
|
67
|
+
begin
|
68
|
+
#log_debug "chdir [/#{directory}]"
|
69
|
+
@sftp.opendir! directory
|
70
|
+
|
71
|
+
rescue Net::SFTP::StatusException => _e
|
72
|
+
# If not allowed to create path, that's over, we're stuck
|
73
|
+
return false unless mkdir
|
74
|
+
|
75
|
+
# Recurse upward
|
76
|
+
chdir_or_create parent, mkdir
|
77
|
+
|
78
|
+
# Now I was able to chdir into my parent, create the current directory
|
79
|
+
mkdir directory
|
80
|
+
|
81
|
+
# Finally retry the chdir
|
82
|
+
retry
|
83
|
+
else
|
84
|
+
return true
|
85
|
+
end
|
86
|
+
|
87
|
+
# We should never get here
|
88
|
+
raise JobTargetShouldBeDirectory
|
89
|
+
end
|
90
|
+
|
91
|
+
def upload source, target, use_temp_name = false, &callback
|
92
|
+
# Push init
|
93
|
+
raise RestFtpDaemon::AssertionFailed, "upload/sftp" if @sftp.nil?
|
94
|
+
|
95
|
+
# Temp file if needed
|
96
|
+
dest = target.clone
|
97
|
+
if use_temp_name
|
98
|
+
dest.generate_temp_name!
|
99
|
+
end
|
100
|
+
|
101
|
+
# Do the transfer
|
102
|
+
log_debug "Remote::RemoteSFTP.upload temp[#{use_temp_name}] name[#{dest.name}]"
|
103
|
+
@sftp.upload! source.path, dest.path do |event, _uploader, *args|
|
104
|
+
case event
|
105
|
+
when :open then
|
106
|
+
# args[0] : file metadata
|
107
|
+
when :put then
|
108
|
+
# args[0] : file metadata
|
109
|
+
# args[1] : byte offset in remote file
|
110
|
+
# args[2] : data being written (as string)
|
111
|
+
# puts "writing #{args[2].length} bytes to #{args[0].remote} starting at #{args[1]}"
|
112
|
+
|
113
|
+
# Update job status after this block transfer
|
114
|
+
yield args[2].length, dest.name
|
115
|
+
|
116
|
+
when :close then
|
117
|
+
# args[0] : file metadata
|
118
|
+
when :mkdir
|
119
|
+
# args[0] : remote path name
|
120
|
+
when :finish
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
# Move the file back to its original name
|
126
|
+
if use_temp_name
|
127
|
+
flags = 0x00000001
|
128
|
+
log_debug "Remote::RemoteSFTP.upload rename [#{dest.name}] > [#{target.name}]"
|
129
|
+
@sftp.rename! dest.path, target.path, flags
|
130
|
+
end
|
131
|
+
|
132
|
+
# progress:
|
133
|
+
# Net::SFTP::StatusException
|
134
|
+
end
|
135
|
+
|
136
|
+
def close
|
137
|
+
# Close init
|
138
|
+
super
|
139
|
+
end
|
140
|
+
|
141
|
+
def connected?
|
142
|
+
@sftp && !@sftp.closed?
|
143
|
+
end
|
144
|
+
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|