rest-ftp-daemon 0.60 → 0.70
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 +8 -8
- data/Gemfile.lock +16 -6
- data/README.md +7 -6
- data/bin/rest-ftp-daemon +21 -22
- data/config.ru +16 -7
- data/lib/rest-ftp-daemon.rb +1 -0
- data/lib/rest-ftp-daemon/api/defaults.rb +1 -2
- data/lib/rest-ftp-daemon/api/jobs.rb +25 -2
- data/lib/rest-ftp-daemon/api/root.rb +20 -12
- data/lib/rest-ftp-daemon/common.rb +1 -1
- data/lib/rest-ftp-daemon/config.rb +19 -19
- data/lib/rest-ftp-daemon/exceptions.rb +7 -0
- data/lib/rest-ftp-daemon/job.rb +99 -40
- data/lib/rest-ftp-daemon/logger.rb +1 -1
- data/lib/rest-ftp-daemon/uri.rb +6 -0
- data/lib/rest-ftp-daemon/views/dashboard.haml +3 -13
- data/lib/rest-ftp-daemon/views/dashboard_jobs.haml +8 -2
- data/lib/rest-ftp-daemon/views/dashboard_tokens.haml +9 -0
- data/lib/rest-ftp-daemon/worker_pool.rb +3 -1
- data/rest-ftp-daemon.gemspec +6 -4
- data/rest-ftp-daemon.yml.sample +1 -1
- metadata +18 -3
- data/lib/rest-ftp-daemon/views/index.haml +0 -116
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MDZkM2FiNjVjM2U0ZWYzOWFiOThhMGVjM2YzMjc2NzFmYWVkN2QyYQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YmVlMjAzMjEzYjg3NTkzNDQzYmM3NTM5MmE3OGQxODM3MTcwOTEzNA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MjYxNGE4N2YwNTI4YzhkMmJiODczMTYyZWYyOTI5YmVhNWJhZDkzOWUwOTg2
|
10
|
+
YWI4MTBkZjM1ZWVhODgwZWViZGFkNzMyMjg3YWVjNWI1NjhiMGMwNWJhMmMy
|
11
|
+
ZGE5NTNmMzM1YmY3OGI4NzdlMjU3MjYyNzlhMThlOWFlZWE0NDg=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
NjUwOWNlZDRiOWY3Mjk0OTI5N2MwODA0NmY5YTYxZmRiMTViZWE3ZDNjZGZi
|
14
|
+
YmViNGQ0M2FlNTI4M2EzZTE1OTIzZmZiMmJkNTFiMzUyYzg0MjYxNDQ0N2Y2
|
15
|
+
NWI0M2YxOTUyNjM3N2MwODRkNDI0ZjBmZWRiZGU2ODM3OWQ3NzI=
|
data/Gemfile.lock
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rest-ftp-daemon (0.
|
4
|
+
rest-ftp-daemon (0.63)
|
5
|
+
double-bag-ftps
|
5
6
|
facter
|
6
7
|
grape
|
8
|
+
haml
|
7
9
|
json
|
8
10
|
settingslogic
|
11
|
+
sys-cpu
|
9
12
|
thin (~> 1.6)
|
10
13
|
|
11
14
|
GEM
|
@@ -28,10 +31,12 @@ GEM
|
|
28
31
|
daemons (1.1.9)
|
29
32
|
descendants_tracker (0.0.4)
|
30
33
|
thread_safe (~> 0.3, >= 0.3.1)
|
34
|
+
double-bag-ftps (0.1.2)
|
31
35
|
equalizer (0.0.9)
|
32
36
|
eventmachine (1.0.3)
|
33
37
|
facter (2.2.0)
|
34
38
|
CFPropertyList (~> 2.2.6)
|
39
|
+
ffi (1.9.5)
|
35
40
|
grape (0.9.0)
|
36
41
|
activesupport
|
37
42
|
builder
|
@@ -42,11 +47,13 @@ GEM
|
|
42
47
|
rack-accept
|
43
48
|
rack-mount
|
44
49
|
virtus (>= 1.0.0)
|
50
|
+
haml (4.0.5)
|
51
|
+
tilt
|
45
52
|
hashie (3.3.1)
|
46
53
|
i18n (0.6.11)
|
47
54
|
ice_nine (0.11.0)
|
48
55
|
json (1.8.1)
|
49
|
-
minitest (5.4.
|
56
|
+
minitest (5.4.2)
|
50
57
|
multi_json (1.10.1)
|
51
58
|
multi_xml (0.5.5)
|
52
59
|
rack (1.5.2)
|
@@ -56,11 +63,14 @@ GEM
|
|
56
63
|
rack (>= 1.0.0)
|
57
64
|
rake (10.3.2)
|
58
65
|
settingslogic (2.0.9)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
66
|
+
sys-cpu (0.7.1)
|
67
|
+
ffi (>= 1.0.0)
|
68
|
+
thin (1.6.3)
|
69
|
+
daemons (~> 1.0, >= 1.0.9)
|
70
|
+
eventmachine (~> 1.0)
|
71
|
+
rack (~> 1.0)
|
63
72
|
thread_safe (0.3.4)
|
73
|
+
tilt (2.0.1)
|
64
74
|
tzinfo (1.2.2)
|
65
75
|
thread_safe (~> 0.1)
|
66
76
|
virtus (1.0.3)
|
data/README.md
CHANGED
@@ -18,7 +18,7 @@ As of today, its main features are :
|
|
18
18
|
* Provide RESTful notifications to the requesting client
|
19
19
|
* Allow authentication in FTP target in a standard URI-format
|
20
20
|
* Allow configuration-based path templates to abstract local mounts or remote FTPs (endpoint tokens)
|
21
|
-
|
21
|
+
* Remote supported protocols: FTP and FTPs
|
22
22
|
|
23
23
|
Expected features in a short-time range :
|
24
24
|
|
@@ -28,7 +28,8 @@ Expected features in a short-time range :
|
|
28
28
|
* Allow fallback file source when first file path is unavailable (failover)
|
29
29
|
* Provide swagger-style API documentation
|
30
30
|
* Authenticate API clients
|
31
|
-
|
31
|
+
* Allow to specify random remote/local source/target
|
32
|
+
* Allow more remote protocols: sFTP, FTPs, HTTP POST, etc
|
32
33
|
|
33
34
|
|
34
35
|
Installation
|
@@ -39,7 +40,7 @@ This project is available as a rubygem, requires on ruby >= 1.9.3 and rubygems i
|
|
39
40
|
Get and install the gem from rubygems.org:
|
40
41
|
|
41
42
|
```
|
42
|
-
# apt-get install ruby1.9.3 ruby-dev rubygems gcc g++
|
43
|
+
# apt-get install ruby1.9.3 ruby1.9.1-dev ruby-dev rubygems gcc g++
|
43
44
|
gem install rest-ftp-daemon --no-ri --no-rdoc
|
44
45
|
```
|
45
46
|
|
@@ -55,10 +56,10 @@ Start the daemon on a specific port :
|
|
55
56
|
rest-ftp-daemon -p4000 start
|
56
57
|
```
|
57
58
|
|
58
|
-
Check that the daemon is running and
|
59
|
+
Check that the daemon is running and providing its status info
|
59
60
|
|
60
61
|
```
|
61
|
-
http://localhost:
|
62
|
+
http://localhost:3200/
|
62
63
|
```
|
63
64
|
|
64
65
|
Configuration
|
@@ -155,7 +156,7 @@ curl -H "Content-Type: application/json" -X DELETE -D /dev/stdout "http://localh
|
|
155
156
|
Getting status
|
156
157
|
------------------------------------------------------------------------------------
|
157
158
|
|
158
|
-
* A global JSON status is provided on ``` GET /
|
159
|
+
* A global JSON status is provided on ``` GET /status ```
|
159
160
|
|
160
161
|
* A nice dashboard gives a global view of the daemon, jobs in queue, and system status, exposed on ``` GET /```
|
161
162
|
|
data/bin/rest-ftp-daemon
CHANGED
@@ -2,41 +2,40 @@
|
|
2
2
|
|
3
3
|
# Libs and init
|
4
4
|
require "thin"
|
5
|
-
DEVELOPMENT=ARGV.include?("development")
|
6
|
-
HERE=File.expand_path(File.dirname(__FILE__) + '/../')
|
7
|
-
#APP_NAME2="rest-ftp-daemon"
|
8
5
|
|
9
6
|
# Include config file and build rack commandline
|
10
|
-
|
7
|
+
app_root = File.expand_path(File.dirname(__FILE__) + '/../')
|
8
|
+
require File.expand_path("lib/rest-ftp-daemon/config.rb")
|
11
9
|
|
10
|
+
# Build rackup context
|
11
|
+
settings_daemonize = [nil, true, 1, "1"].include?(Settings.daemonize)
|
12
12
|
argv = ARGV
|
13
|
-
argv << ["-
|
14
|
-
argv << ["-
|
15
|
-
argv << ["-l", Settings
|
16
|
-
argv << ["--daemonize"] if
|
17
|
-
|
18
|
-
# Rackup file
|
19
|
-
argv << ["-R", File.expand_path("#{HERE}/config.ru")] unless ARGV.include?("-R")
|
13
|
+
argv << ["-e", "development"] unless ARGV.include?("-e")
|
14
|
+
argv << ["-p", Settings[:port].to_s] unless ARGV.include?("-p") || Settings[:port].nil?
|
15
|
+
argv << ["-l", Settings[:logs][:thin]] unless ARGV.include?("-l") unless Settings[:logs][:thin].nil? unless Settings[:logs].nil?
|
16
|
+
argv << ["--daemonize"] if settings_daemonize unless (ARGV.include?("-d") || ARGV.include?("--daemonize"))
|
17
|
+
argv << ["-R", File.expand_path("config.ru")] unless ARGV.include?("-R")
|
20
18
|
|
21
19
|
# Display compiled configuration
|
22
|
-
puts
|
23
|
-
puts "Daemon name \t #{Settings.
|
24
|
-
puts "Version \t #{Settings.
|
20
|
+
puts "---"
|
21
|
+
puts "Daemon name \t #{Settings.app_name}"
|
22
|
+
puts "Version \t #{Settings.app_ver}"
|
25
23
|
puts "Environment \t #{Settings.namespace}"
|
26
24
|
puts "Config file \t #{Settings.source}"
|
27
25
|
puts "Parameters \t #{argv.flatten}"
|
28
26
|
puts Settings.to_hash.to_yaml( :Indent => 4, :UseHeader => true, :UseVersion => false )
|
29
27
|
|
30
28
|
# Start Thin with this rackup configuration
|
29
|
+
puts "---"
|
31
30
|
puts
|
32
31
|
begin
|
33
32
|
Thin::Runner.new(argv.flatten).run!
|
34
|
-
rescue Thin::PidFileExist
|
35
|
-
|
36
|
-
rescue Thin::PidFileNotFound
|
37
|
-
|
38
|
-
|
39
|
-
|
33
|
+
rescue Thin::PidFileExist
|
34
|
+
puts "ERROR: daemon was already running (Thin::PidFileExist)"
|
35
|
+
rescue Thin::PidFileNotFound
|
36
|
+
puts "ERROR: daemon was not running (Thin::PidFileNotFound)"
|
37
|
+
rescue SystemExit
|
38
|
+
puts "EXITING: daemon in the background (SystemExit)"
|
39
|
+
else
|
40
|
+
puts "EXITING: process ending"
|
40
41
|
end
|
41
|
-
|
42
|
-
|
data/config.ru
CHANGED
@@ -1,14 +1,23 @@
|
|
1
1
|
# Load gem files
|
2
|
-
|
2
|
+
APP_LIBS = File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
|
3
|
+
$LOAD_PATH.unshift(APP_LIBS) unless $LOAD_PATH.include?(APP_LIBS)
|
3
4
|
require 'rest-ftp-daemon'
|
4
5
|
|
5
|
-
#
|
6
|
-
APP_STARTED = Time.now
|
7
|
-
|
8
|
-
# Create worker pool
|
6
|
+
# Create queue and worker pool
|
9
7
|
$queue = RestFtpDaemon::JobQueue.new
|
10
8
|
$pool = RestFtpDaemon::WorkerPool.new(Settings.workers.to_i)
|
11
9
|
|
12
|
-
#
|
13
|
-
use Rack::
|
10
|
+
# Rack middleware
|
11
|
+
# use Rack::Etag # Add an ETag
|
12
|
+
# use Rack::Reloader, 0
|
13
|
+
# Rack::Auth::Basic
|
14
|
+
# use Rack::Auth::Basic, "Restricted Area" do |username, password|
|
15
|
+
# [username, password] == ['admin', 'admin']
|
16
|
+
# end
|
17
|
+
#use Rack::Deflator # Compress
|
18
|
+
|
19
|
+
# Serve static assets
|
20
|
+
use Rack::Static, :urls => ["/css", "/images"], :root => "lib/#{Settings.app_name}/static/"
|
21
|
+
|
22
|
+
# Launch the main daemon
|
14
23
|
run Rack::Cascade.new [RestFtpDaemon::API::Root]
|
data/lib/rest-ftp-daemon.rb
CHANGED
@@ -8,6 +8,7 @@ require 'securerandom'
|
|
8
8
|
require 'rest-ftp-daemon/config'
|
9
9
|
require 'rest-ftp-daemon/exceptions'
|
10
10
|
require 'rest-ftp-daemon/common'
|
11
|
+
require 'rest-ftp-daemon/uri'
|
11
12
|
require 'rest-ftp-daemon/job_queue'
|
12
13
|
require 'rest-ftp-daemon/worker_pool'
|
13
14
|
require 'rest-ftp-daemon/logger'
|
@@ -45,7 +45,6 @@ module RestFtpDaemon
|
|
45
45
|
# end
|
46
46
|
|
47
47
|
helpers do
|
48
|
-
|
49
48
|
def format_nice_bytes( number )
|
50
49
|
return "Ø" if number.nil? || number.zero?
|
51
50
|
index = ( Math.log( number ) / Math.log( 2 ) ).to_i / 10
|
@@ -63,7 +62,7 @@ module RestFtpDaemon
|
|
63
62
|
end
|
64
63
|
|
65
64
|
def render name, values={}
|
66
|
-
template = File.read("
|
65
|
+
template = File.read("lib/#{Settings.app_name}/views/#{name.to_s}.haml")
|
67
66
|
haml_engine = Haml::Engine.new(template)
|
68
67
|
haml_engine.render(binding, values)
|
69
68
|
end
|
@@ -2,13 +2,33 @@ module RestFtpDaemon
|
|
2
2
|
module API
|
3
3
|
|
4
4
|
class Jobs < Grape::API
|
5
|
-
|
6
|
-
|
5
|
+
|
6
|
+
|
7
|
+
####### CLASS CONFIG
|
8
|
+
|
9
|
+
#include RestFtpDaemon::API::Defaults
|
10
|
+
#logger ActiveSupport::Logger.new Settings.logs.api, 'daily' unless Settings.logs.api.nil?
|
7
11
|
|
8
12
|
params do
|
9
13
|
optional :overwrite, type: Integer, default: false
|
10
14
|
end
|
11
15
|
|
16
|
+
|
17
|
+
####### INITIALIZATION
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
$last_worker_id = 0
|
21
|
+
|
22
|
+
# Check that Queue and Pool are available
|
23
|
+
raise RestFtpDaemon::MissingQueue unless defined? $queue
|
24
|
+
raise RestFtpDaemon::MissingQueue unless defined? $pool
|
25
|
+
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
####### HELPERS
|
31
|
+
|
12
32
|
helpers do
|
13
33
|
|
14
34
|
def threads_with_id job_id
|
@@ -42,6 +62,9 @@ module RestFtpDaemon
|
|
42
62
|
|
43
63
|
end
|
44
64
|
|
65
|
+
|
66
|
+
####### API DEFINITION
|
67
|
+
|
45
68
|
desc "Get information about a specific job"
|
46
69
|
params do
|
47
70
|
requires :id, type: Integer, desc: "job id"
|
@@ -7,13 +7,19 @@ module RestFtpDaemon
|
|
7
7
|
module API
|
8
8
|
|
9
9
|
class Root < Grape::API
|
10
|
+
|
11
|
+
|
12
|
+
####### CLASS CONFIG
|
10
13
|
include RestFtpDaemon::API::Defaults
|
11
|
-
logger ActiveSupport::Logger.new
|
14
|
+
logger ActiveSupport::Logger.new(Settings.logs.api, 'daily') unless Settings.logs.api.nil?
|
12
15
|
#add_swagger_documentation
|
13
16
|
|
14
17
|
mount RestFtpDaemon::API::Jobs => '/jobs'
|
15
18
|
# mount RestFtpDaemon::API::Workers => '/workers'
|
16
19
|
|
20
|
+
|
21
|
+
####### HELPERS
|
22
|
+
|
17
23
|
helpers do
|
18
24
|
def info message, level = 0
|
19
25
|
Root.logger.add(Logger::INFO, "#{' '*level} #{message}", "API::Root")
|
@@ -32,19 +38,21 @@ module RestFtpDaemon
|
|
32
38
|
|
33
39
|
end
|
34
40
|
|
35
|
-
|
36
|
-
|
37
|
-
|
41
|
+
|
42
|
+
####### INITIALIZATION
|
43
|
+
|
38
44
|
def initialize
|
39
45
|
$last_worker_id = 0
|
46
|
+
|
47
|
+
# Check that Queue and Pool are available
|
48
|
+
raise RestFtpDaemon::MissingQueue unless defined? $queue
|
49
|
+
raise RestFtpDaemon::MissingQueue unless defined? $pool
|
50
|
+
|
40
51
|
super
|
41
52
|
end
|
42
53
|
|
43
54
|
|
44
|
-
|
45
|
-
######################################################################
|
46
|
-
####### API DEFINITION
|
47
|
-
######################################################################
|
55
|
+
####### API DEFINITION
|
48
56
|
|
49
57
|
# Server global status
|
50
58
|
get '/' do
|
@@ -90,15 +98,15 @@ module RestFtpDaemon
|
|
90
98
|
end
|
91
99
|
|
92
100
|
# Server global status
|
93
|
-
get '/
|
101
|
+
get '/status' do
|
94
102
|
info "GET /"
|
95
103
|
status 200
|
96
104
|
return {
|
97
105
|
hostname: `hostname`.chomp,
|
98
|
-
version: Settings.
|
106
|
+
version: Settings.app_ver,
|
99
107
|
config: Settings.to_hash,
|
100
|
-
started:
|
101
|
-
uptime: (Time.now -
|
108
|
+
started: Settings.app_started,
|
109
|
+
uptime: (Time.now - Settings.app_started).round(1),
|
102
110
|
status: job_list_by_status,
|
103
111
|
queue_size: $queue.all_size,
|
104
112
|
jobs_queued: $queue.queued.collect(&:id),
|
@@ -18,7 +18,7 @@ module RestFtpDaemon
|
|
18
18
|
def info message, level = 0
|
19
19
|
# progname = "Job [#{id}]" unless id.nil?
|
20
20
|
# progname = "Worker [#{id}]" unless worker_id.nil?
|
21
|
-
@logger.add(Logger::INFO, "#{' '*(level+1)} #{message}", progname)
|
21
|
+
@logger.add(Logger::INFO, "#{' '*(level+1)} #{message}", progname) unless @logger.nil?
|
22
22
|
end
|
23
23
|
|
24
24
|
def notify signal, error = 0, status = {}
|
@@ -1,24 +1,24 @@
|
|
1
1
|
require 'settingslogic'
|
2
|
-
DEVELOPMENT = false unless defined? DEVELOPMENT
|
3
|
-
APP_NAME="rest-ftp-daemon"
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
3
|
+
# Terrific assertions
|
4
|
+
#raise "config.rb: APP_ROOT is not defined" unless defined? APP_ROOT
|
5
|
+
APP_NAME = "rest-ftp-daemon"
|
6
|
+
APP_CONF = "/etc/#{APP_NAME}.yml"
|
7
|
+
APP_DEV = ARGV.include?("development") ? true : false
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Settings.source Hash.new
|
16
|
-
end
|
9
|
+
class Settings < Settingslogic
|
10
|
+
# Read configuration
|
11
|
+
source (File.exists? APP_CONF) ? APP_CONF : Hash.new
|
12
|
+
namespace (APP_DEV ? "development" : "production")
|
13
|
+
suppress_errors true
|
17
14
|
|
18
|
-
#
|
19
|
-
|
20
|
-
|
15
|
+
# Some constants
|
16
|
+
self[:dev] = APP_DEV
|
17
|
+
self[:app_name] = APP_NAME
|
18
|
+
self[:app_ver] = "0.70"
|
19
|
+
self[:app_started] = Time.now
|
21
20
|
|
22
|
-
#
|
23
|
-
|
24
|
-
|
21
|
+
# Some defaults
|
22
|
+
self[:app_trim_progname] = "18"
|
23
|
+
self[:app_chunk_size] = "1000000"
|
24
|
+
end
|
@@ -4,6 +4,10 @@ module RestFtpDaemon
|
|
4
4
|
|
5
5
|
class DummyException < RestFtpDaemonException; end
|
6
6
|
|
7
|
+
class MissingQueue < RestFtpDaemonException; end
|
8
|
+
class MissingPool < RestFtpDaemonException; end
|
9
|
+
|
10
|
+
|
7
11
|
class RequestSourceMissing < RestFtpDaemonException; end
|
8
12
|
class RequestSourceNotFound < RestFtpDaemonException; end
|
9
13
|
class RequestTargetMissing < RestFtpDaemonException; end
|
@@ -15,9 +19,12 @@ module RestFtpDaemon
|
|
15
19
|
class JobSourceMissing < RestFtpDaemonException; end
|
16
20
|
class JobSourceNotFound < RestFtpDaemonException; end
|
17
21
|
class JobTargetMissing < RestFtpDaemonException; end
|
22
|
+
class JobTargetUnsupported < RestFtpDaemonException; end
|
18
23
|
class JobTargetUnparseable < RestFtpDaemonException; end
|
19
24
|
#class JobTargetPermission < RestFtpDaemonException; end
|
20
25
|
class JobTargetFileExists < RestFtpDaemonException; end
|
26
|
+
class JobPrerequisitesNotMet < RestFtpDaemonException; end
|
27
|
+
|
21
28
|
|
22
29
|
class NotificationMissingUrl < RestFtpDaemonException; end
|
23
30
|
class NotificationMissingSignal < RestFtpDaemonException; end
|
data/lib/rest-ftp-daemon/job.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
-
require 'net/
|
1
|
+
#require 'net/ftptls'
|
2
2
|
|
3
|
+
require 'uri'
|
4
|
+
require 'net/ftp'
|
5
|
+
require 'double_bag_ftps'
|
3
6
|
|
4
7
|
module RestFtpDaemon
|
5
8
|
class Job < RestFtpDaemon::Common
|
@@ -10,8 +13,6 @@ module RestFtpDaemon
|
|
10
13
|
|
11
14
|
# Grab params
|
12
15
|
@params = params
|
13
|
-
@target = nil
|
14
|
-
@source = nil
|
15
16
|
|
16
17
|
# Init context
|
17
18
|
set :id, id
|
@@ -57,22 +58,17 @@ module RestFtpDaemon
|
|
57
58
|
set :error, exception.class
|
58
59
|
|
59
60
|
rescue RestFtpDaemonException => exception
|
60
|
-
info "Job.process failed [
|
61
|
+
info "Job.process failed [::#{exception.class}]"
|
61
62
|
set :status, :failed
|
62
63
|
set :error, exception.class
|
63
64
|
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
rescue Exception => exception
|
66
|
+
info "Job.process exception [#{exception.class}] #{exception.backtrace.inspect}"
|
67
|
+
set :status, :crashed
|
68
|
+
set :error, exception.class
|
67
69
|
|
68
70
|
else
|
69
71
|
info "Job.process finished"
|
70
|
-
# set :error, 0
|
71
|
-
#set :status, :wandering
|
72
|
-
|
73
|
-
# Wait for a few seconds before marking the job as finished
|
74
|
-
# info "#{prefix} wander for #{RestFtpDaemon::THREAD_SLEEP_BEFORE_DIE} sec"
|
75
|
-
# wander RestFtpDaemon::THREAD_SLEEP_BEFORE_DIE
|
76
72
|
set :status, :finished
|
77
73
|
end
|
78
74
|
|
@@ -80,8 +76,8 @@ module RestFtpDaemon
|
|
80
76
|
|
81
77
|
def describe
|
82
78
|
# Update realtime info
|
83
|
-
w = wandering_time
|
84
|
-
set :wandering, w.round(2) unless w.nil?
|
79
|
+
#w = wandering_time
|
80
|
+
#set :wandering, w.round(2) unless w.nil?
|
85
81
|
|
86
82
|
# Update realtime info
|
87
83
|
u = up_time
|
@@ -131,15 +127,15 @@ module RestFtpDaemon
|
|
131
127
|
@params[attribute.to_s] = value
|
132
128
|
end
|
133
129
|
|
134
|
-
def
|
135
|
-
File.expand_path
|
130
|
+
def expand_path path
|
131
|
+
File.expand_path replace_token(path)
|
136
132
|
end
|
137
133
|
|
138
|
-
def
|
139
|
-
URI
|
134
|
+
def expand_url path
|
135
|
+
URI::parse replace_token(path) rescue nil
|
140
136
|
end
|
141
137
|
|
142
|
-
def
|
138
|
+
def replace_token path
|
143
139
|
# Ensure endpoints are not a nil value
|
144
140
|
return path unless Settings.endpoints.is_a? Enumerable
|
145
141
|
newpath = path.clone
|
@@ -157,21 +153,36 @@ module RestFtpDaemon
|
|
157
153
|
|
158
154
|
def prepare
|
159
155
|
# Init
|
156
|
+
info "Job.prepare"
|
160
157
|
set :status, :preparing
|
158
|
+
@source_method = :file
|
159
|
+
@target_method = nil
|
160
|
+
@source_path = nil
|
161
|
+
@target_url = nil
|
161
162
|
|
162
163
|
# Check source
|
163
164
|
raise JobSourceMissing unless @params["source"]
|
164
|
-
|
165
|
-
|
165
|
+
#@source = expand_path @params["source"]
|
166
|
+
@source_path = expand_path @params["source"]
|
167
|
+
set :source_path, @source_path
|
168
|
+
set :source_method, :file
|
166
169
|
|
167
170
|
# Check target
|
168
171
|
raise JobTargetMissing unless @params["target"]
|
169
|
-
@
|
170
|
-
set :
|
172
|
+
@target_url = expand_url @params["target"]
|
173
|
+
set :target_url, @target_url.inspect
|
174
|
+
|
175
|
+
if @target_url.kind_of? URI::FTP
|
176
|
+
@target_method = :ftp
|
177
|
+
elsif @target_url.kind_of? URI::FTPS
|
178
|
+
@target_method = :ftps
|
179
|
+
end
|
180
|
+
set :target_method, @target_method
|
171
181
|
|
172
182
|
# Check compliance
|
173
|
-
raise JobTargetUnparseable if @
|
174
|
-
raise
|
183
|
+
raise JobTargetUnparseable if @target_url.nil?
|
184
|
+
raise JobTargetUnsupported if @target_method.nil?
|
185
|
+
raise JobSourceNotFound unless File.exists? @source_path
|
175
186
|
|
176
187
|
end
|
177
188
|
|
@@ -187,44 +198,90 @@ module RestFtpDaemon
|
|
187
198
|
end
|
188
199
|
|
189
200
|
def transfer
|
201
|
+
# Init
|
202
|
+
info "Job.transfer"
|
203
|
+
|
190
204
|
# Send first notification
|
191
205
|
transferred = 0
|
192
206
|
notify "rftpd.started"
|
193
207
|
|
194
208
|
# Ensure @source and @target are there
|
209
|
+
info "Job.transfer checking_source"
|
195
210
|
set :status, :checking_source
|
196
|
-
raise JobPrerequisitesNotMet unless @
|
197
|
-
raise JobPrerequisitesNotMet unless @
|
198
|
-
target_path = File.dirname @
|
199
|
-
target_name = File.basename @
|
211
|
+
raise RestFtpDaemon::JobPrerequisitesNotMet unless @source_path
|
212
|
+
raise RestFtpDaemon::JobPrerequisitesNotMet unless @target_url
|
213
|
+
target_path = File.dirname @target_url.path
|
214
|
+
target_name = File.basename @target_url.path
|
200
215
|
|
201
216
|
# Read source file size
|
202
|
-
source_size = File.size @
|
217
|
+
source_size = File.size @source_path
|
203
218
|
set :file_size, source_size
|
204
219
|
|
205
220
|
# Prepare FTP transfer
|
206
|
-
|
207
|
-
|
221
|
+
info "Job.transfer preparing"
|
222
|
+
|
223
|
+
# Scheme-aware config
|
224
|
+
case @target_method
|
225
|
+
when :ftp
|
226
|
+
info "Job.transfer scheme FTP"
|
227
|
+
ftp = Net::FTP.new
|
228
|
+
when :ftps
|
229
|
+
info "Job.transfer scheme FTPS"
|
230
|
+
ftp = DoubleBagFTPS.new
|
231
|
+
ftp.ssl_context = DoubleBagFTPS.create_ssl_context(:verify_mode => OpenSSL::SSL::VERIFY_NONE)
|
232
|
+
ftp.ftps_mode = DoubleBagFTPS::EXPLICIT
|
233
|
+
else
|
234
|
+
info "Job.transfer scheme other: [#{@target_url.scheme}]"
|
235
|
+
end
|
236
|
+
|
237
|
+
# Connect remote server
|
238
|
+
info "Job.transfer connecting"
|
239
|
+
set :status, :connecting
|
240
|
+
ftp.connect(@target_url.host)
|
208
241
|
ftp.passive = true
|
209
|
-
|
242
|
+
|
243
|
+
# Logging in
|
244
|
+
info "Job.transfer login"
|
245
|
+
begin
|
246
|
+
u = ftp.login @target_url.user, @target_url.password
|
247
|
+
rescue Exception => exception
|
248
|
+
info "Job.process login failed [#{exception.class}] #{u.inspect}"
|
249
|
+
set :status, :login_failed
|
250
|
+
set :error, exception.class
|
251
|
+
end
|
252
|
+
|
253
|
+
# Changing to directory
|
254
|
+
info "Job.transfer chdir"
|
255
|
+
set :status, :chdir
|
210
256
|
ftp.chdir(target_path)
|
211
257
|
|
212
258
|
# Check for target file presence
|
213
259
|
if get(:overwrite).nil?
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
260
|
+
info "Job.transfer remote_check (#{target_name})"
|
261
|
+
set :status, :remote_check
|
262
|
+
|
263
|
+
# Get file list, sometimes the response can be an empty value
|
264
|
+
results = ftp.list(target_name) rescue nil
|
265
|
+
|
266
|
+
# Result can be nil or a list of files
|
267
|
+
if results.nil? || results.count.zero?
|
268
|
+
info "Job.transfer remote_absent"
|
269
|
+
set :status, :remote_absent
|
270
|
+
else
|
271
|
+
info "Job.transfer remote_present"
|
272
|
+
set :status, :remote_present
|
218
273
|
ftp.close
|
219
274
|
notify "rftpd.ended", RestFtpDaemon::JobTargetFileExists
|
220
275
|
raise RestFtpDaemon::JobTargetFileExists
|
221
276
|
end
|
277
|
+
|
222
278
|
end
|
223
279
|
|
224
280
|
# Do transfer
|
281
|
+
info "Job.transfer uploading"
|
225
282
|
set :status, :uploading
|
226
|
-
chunk_size = Settings.transfer.chunk_size || Settings[:
|
227
|
-
ftp.putbinaryfile(@
|
283
|
+
chunk_size = Settings.transfer.chunk_size || Settings[:app_chunk_size]
|
284
|
+
ftp.putbinaryfile(@source_path, target_name, chunk_size) do |block|
|
228
285
|
# Update counters
|
229
286
|
transferred += block.bytesize
|
230
287
|
|
@@ -235,6 +292,8 @@ module RestFtpDaemon
|
|
235
292
|
end
|
236
293
|
|
237
294
|
# Close FTP connexion
|
295
|
+
info "Job.transfer closing"
|
296
|
+
set :status, :disconnecting
|
238
297
|
notify "rftpd.ended"
|
239
298
|
set :progress, nil
|
240
299
|
ftp.close
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class Logger
|
2
2
|
def format_message(severity, timestamp, progname, msg)
|
3
3
|
stamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
4
|
-
progname = "%-#{Settings[:
|
4
|
+
progname = "%-#{Settings[:app_trim_progname]}s" % progname
|
5
5
|
"#{stamp} #{severity} #{progname} #{msg}\n"
|
6
6
|
end
|
7
7
|
end
|
@@ -30,9 +30,9 @@
|
|
30
30
|
|
31
31
|
.navbar-header
|
32
32
|
%h1
|
33
|
-
= Settings.
|
33
|
+
= Settings.app_name
|
34
34
|
%small
|
35
|
-
= "v#{Settings.
|
35
|
+
= "v#{Settings.app_ver}"
|
36
36
|
= "[#{Settings.namespace}]"
|
37
37
|
|
38
38
|
|
@@ -70,17 +70,7 @@
|
|
70
70
|
|
71
71
|
= render :dashboard_jobs, {jobs: @jobs_all}
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
%h2 Endpoint tokens
|
77
|
-
- endpoints = Settings.endpoints || {}
|
78
|
-
- endpoints.each do |token, value|
|
79
|
-
.btn-group.btn-group-sm
|
80
|
-
.btn.btn-default.btn-danger= token
|
81
|
-
.btn.btn-default= value
|
82
|
-
|
83
|
-
|
73
|
+
= render :dashboard_tokens, {tokens: Settings.endpoints || {}}
|
84
74
|
|
85
75
|
%br
|
86
76
|
%br
|
@@ -35,6 +35,7 @@
|
|
35
35
|
%th= "priority"
|
36
36
|
%th= "source"
|
37
37
|
%th= "target"
|
38
|
+
%th= "mode"
|
38
39
|
%th= "size"
|
39
40
|
%th= "status"
|
40
41
|
%th= "error"
|
@@ -60,8 +61,13 @@
|
|
60
61
|
|
61
62
|
%td= job.id
|
62
63
|
%td= job.get :priority
|
63
|
-
%td
|
64
|
-
|
64
|
+
%td{title: job.get(:source_path)}
|
65
|
+
= job.get :source
|
66
|
+
%td{title: job.get(:target_url)}
|
67
|
+
= job.get :target
|
68
|
+
%td
|
69
|
+
.label.label-info= job.get :source_method
|
70
|
+
.label.label-primary= job.get :target_method
|
65
71
|
%td= format_nice_bytes(size)
|
66
72
|
%td= status
|
67
73
|
%td= error
|
@@ -12,9 +12,11 @@ module RestFtpDaemon
|
|
12
12
|
super()
|
13
13
|
|
14
14
|
# Create worker threads
|
15
|
+
info "WorkerPool initializing with #{number_threads} workers"
|
15
16
|
number_threads.times do
|
16
17
|
Thread.new { run }
|
17
18
|
end
|
19
|
+
|
18
20
|
end
|
19
21
|
|
20
22
|
def wait
|
@@ -35,7 +37,7 @@ module RestFtpDaemon
|
|
35
37
|
|
36
38
|
begin
|
37
39
|
loop do
|
38
|
-
info "worker [#{@wid}] waiting for a job
|
40
|
+
info "worker [#{@wid}] ready, waiting for a job"
|
39
41
|
|
40
42
|
# Wait for a job to come into the queue
|
41
43
|
job = $queue.pop
|
data/rest-ftp-daemon.gemspec
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
3
|
# Libs
|
4
|
-
|
5
|
-
|
4
|
+
# APP_ROOT = File.expand_path(File.dirname(__FILE__) + '/../')
|
5
|
+
APP_LIBS = File.expand_path('../lib', __FILE__)
|
6
|
+
$LOAD_PATH.unshift(APP_LIBS) unless $LOAD_PATH.include?(APP_LIBS)
|
6
7
|
require 'rest-ftp-daemon/config'
|
7
8
|
|
8
9
|
Gem::Specification.new do |spec|
|
9
|
-
spec.name = Settings[:
|
10
|
+
spec.name = Settings[:app_name]
|
10
11
|
spec.date = Time.now.strftime("%Y-%m-%d")
|
11
12
|
spec.authors = ["Bruno MEDICI"]
|
12
13
|
spec.email = "rest-ftp-daemon@bmconseil.com"
|
@@ -18,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
18
19
|
spec.files = `git ls-files -z`.split("\x0")
|
19
20
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
21
|
spec.require_paths = ["lib"]
|
21
|
-
spec.version = Settings[:
|
22
|
+
spec.version = Settings[:app_ver]
|
22
23
|
|
23
24
|
spec.required_ruby_version = '>= 1.9.3'
|
24
25
|
|
@@ -30,6 +31,7 @@ Gem::Specification.new do |spec|
|
|
30
31
|
spec.add_runtime_dependency "settingslogic"
|
31
32
|
spec.add_runtime_dependency "haml"
|
32
33
|
spec.add_runtime_dependency "json"
|
34
|
+
spec.add_runtime_dependency "double-bag-ftps"
|
33
35
|
spec.add_runtime_dependency "facter"
|
34
36
|
spec.add_runtime_dependency "sys-cpu"
|
35
37
|
|
data/rest-ftp-daemon.yml.sample
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rest-ftp-daemon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.70'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bruno MEDICI
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -108,6 +108,20 @@ dependencies:
|
|
108
108
|
- - ! '>='
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: double-bag-ftps
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: facter
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -165,9 +179,10 @@ files:
|
|
165
179
|
- lib/rest-ftp-daemon/logger.rb
|
166
180
|
- lib/rest-ftp-daemon/notification.rb
|
167
181
|
- lib/rest-ftp-daemon/static/css/bootstrap.min.css
|
182
|
+
- lib/rest-ftp-daemon/uri.rb
|
168
183
|
- lib/rest-ftp-daemon/views/dashboard.haml
|
169
184
|
- lib/rest-ftp-daemon/views/dashboard_jobs.haml
|
170
|
-
- lib/rest-ftp-daemon/views/
|
185
|
+
- lib/rest-ftp-daemon/views/dashboard_tokens.haml
|
171
186
|
- lib/rest-ftp-daemon/worker_pool.rb
|
172
187
|
- rest-ftp-daemon.gemspec
|
173
188
|
- rest-ftp-daemon.yml.sample
|
@@ -1,116 +0,0 @@
|
|
1
|
-
!!! 5
|
2
|
-
%html{:lang => "en"}
|
3
|
-
%head
|
4
|
-
%meta{:charset => "utf-8"}/
|
5
|
-
%link{ href:"//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" , rel: "stylesheet"}
|
6
|
-
|
7
|
-
%title="#{Settings.name} dashboard"
|
8
|
-
|
9
|
-
:css
|
10
|
-
|
11
|
-
#footer {
|
12
|
-
padding: 15px 0;
|
13
|
-
_border-top: 1px solid silver;
|
14
|
-
}
|
15
|
-
|
16
|
-
.progress {
|
17
|
-
margin-bottom: 0;
|
18
|
-
}
|
19
|
-
|
20
|
-
%body
|
21
|
-
|
22
|
-
.container
|
23
|
-
%h1
|
24
|
-
= Settings.name
|
25
|
-
%small
|
26
|
-
= "v#{Settings.version}"
|
27
|
-
= "[#{Settings.namespace}]"
|
28
|
-
|
29
|
-
|
30
|
-
%h2 System status
|
31
|
-
.btn-group.btn-group-sm
|
32
|
-
.btn.btn-default.btn-warning Processors
|
33
|
-
.btn.btn-default= @info_procs
|
34
|
-
|
35
|
-
.btn-group.btn-group-sm
|
36
|
-
.btn.btn-default.btn-warning Load
|
37
|
-
.btn.btn-default= @info_load.round(1)
|
38
|
-
|
39
|
-
.btn-group.btn-group-sm
|
40
|
-
.btn.btn-default.btn-warning CPU
|
41
|
-
.btn.btn-default= "#{@info_norm} %"
|
42
|
-
|
43
|
-
.btn-group.btn-group-sm
|
44
|
-
.btn.btn-default.btn-warning Free
|
45
|
-
.btn.btn-default= @info_memfree
|
46
|
-
|
47
|
-
.btn-group.btn-group-sm
|
48
|
-
.btn.btn-default.btn-success IP
|
49
|
-
.btn.btn-default= @info_ipaddr
|
50
|
-
|
51
|
-
.btn-group.btn-group-sm
|
52
|
-
.btn.btn-default.btn-success Workers
|
53
|
-
.btn.btn-default= Settings.workers
|
54
|
-
|
55
|
-
.btn-group.btn-group-sm
|
56
|
-
.btn.btn-default.btn-info Transferred
|
57
|
-
.btn.btn-default= format_nice_bytes(@info_total_transferred)
|
58
|
-
|
59
|
-
|
60
|
-
%h2
|
61
|
-
Jobs on this system (#{@jobs_all})
|
62
|
-
%small
|
63
|
-
queued (#{@jobs_queued}) / popped (#{@jobs_popped})
|
64
|
-
|
65
|
-
%table.table.table-striped.table-hover.table-condensed
|
66
|
-
%tr
|
67
|
-
%th= "ID"
|
68
|
-
%th= "priority"
|
69
|
-
%th= "source"
|
70
|
-
%th= "target"
|
71
|
-
%th= "size"
|
72
|
-
%th= "status"
|
73
|
-
%th= "error"
|
74
|
-
%th= "progress"
|
75
|
-
%th
|
76
|
-
|
77
|
-
- @jobs.each do |job|
|
78
|
-
- error = job.get :error
|
79
|
-
- status = job.get :status
|
80
|
-
- size = job.get :file_size
|
81
|
-
- progress = job.get :progress
|
82
|
-
|
83
|
-
- if error!=0 && !error.nil?
|
84
|
-
- trclass = "danger"
|
85
|
-
- elsif status == :uploading
|
86
|
-
- trclass = "info"
|
87
|
-
- elsif status == :finished
|
88
|
-
- trclass = "success"
|
89
|
-
- else
|
90
|
-
- trclass = "warning"
|
91
|
-
|
92
|
-
%tr{class: trclass}
|
93
|
-
|
94
|
-
%td= job.id
|
95
|
-
%td= job.get :priority
|
96
|
-
%td= job.get :source
|
97
|
-
%td= job.get :target
|
98
|
-
%td= format_nice_bytes(size)
|
99
|
-
%td= status
|
100
|
-
%td= error
|
101
|
-
%td
|
102
|
-
- unless progress.nil?
|
103
|
-
.progress
|
104
|
-
.progress-bar{style:"width: #{progress}%;"}
|
105
|
-
= format_nice_bytes job.get(:file_sent)
|
106
|
-
%td
|
107
|
-
- unless progress.nil?
|
108
|
-
= "#{progress} %"
|
109
|
-
|
110
|
-
|
111
|
-
%h2 Endpoint tokens
|
112
|
-
- endpoints = Settings.endpoints || {}
|
113
|
-
- endpoints.each do |token, value|
|
114
|
-
.btn-group.btn-group-sm
|
115
|
-
.btn.btn-default.btn-danger= token
|
116
|
-
.btn.btn-default= value
|