rest-ftp-daemon 0.60 → 0.70
Sign up to get free protection for your applications and to get access to all the features.
- 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
|