rest-ftp-daemon 0.6 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +8 -1
- data/Gemfile.lock +57 -54
- data/README.md +26 -116
- data/Rakefile +49 -1
- data/VERSION +1 -0
- data/bin/rest-ftp-daemon +17 -37
- data/lib/config.rb +3 -0
- data/lib/config.ru +11 -0
- data/lib/errors.rb +12 -0
- data/lib/extend_threads.rb +14 -0
- data/lib/rest-ftp-daemon.rb +304 -18
- data/rest-ftp-daemon.gemspec +62 -31
- metadata +46 -100
- checksums.yaml +0 -15
- data/.gitignore +0 -6
- data/config.ru +0 -14
- data/lib/rest-ftp-daemon/api/defaults.rb +0 -76
- data/lib/rest-ftp-daemon/api/jobs.rb +0 -151
- data/lib/rest-ftp-daemon/api/root.rb +0 -129
- data/lib/rest-ftp-daemon/api/workers.rb +0 -49
- data/lib/rest-ftp-daemon/common.rb +0 -49
- data/lib/rest-ftp-daemon/config.rb +0 -24
- data/lib/rest-ftp-daemon/exceptions.rb +0 -26
- data/lib/rest-ftp-daemon/job.rb +0 -246
- data/lib/rest-ftp-daemon/job_queue.rb +0 -105
- data/lib/rest-ftp-daemon/logger.rb +0 -7
- data/lib/rest-ftp-daemon/notification.rb +0 -82
- data/lib/rest-ftp-daemon/static/css/bootstrap.min.css +0 -5
- data/lib/rest-ftp-daemon/views/dashboard.haml +0 -86
- data/lib/rest-ftp-daemon/views/dashboard_jobs.haml +0 -75
- data/lib/rest-ftp-daemon/views/index.haml +0 -116
- data/lib/rest-ftp-daemon/worker_pool.rb +0 -63
- data/rest-ftp-daemon.yml.sample +0 -15
@@ -1,76 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
require 'grape'
|
3
|
-
SIZE_UNITS = ["B", "KB", "MB", "GB", "TB", "PB" ]
|
4
|
-
|
5
|
-
|
6
|
-
module RestFtpDaemon
|
7
|
-
module API
|
8
|
-
module Defaults
|
9
|
-
extend ActiveSupport::Concern
|
10
|
-
|
11
|
-
included do
|
12
|
-
#version 'v1', using: :header, vendor: 'ftven'
|
13
|
-
format :json
|
14
|
-
do_not_route_head!
|
15
|
-
do_not_route_options!
|
16
|
-
|
17
|
-
# before do
|
18
|
-
# header['Access-Control-Allow-Origin'] = '*'
|
19
|
-
# header['Access-Control-Request-Method'] = '*'
|
20
|
-
# end
|
21
|
-
|
22
|
-
# Handle authentication
|
23
|
-
# http_basic do |username, password|
|
24
|
-
# User.authenticate!(username, password)
|
25
|
-
# end
|
26
|
-
|
27
|
-
# # global handler for simple not found case
|
28
|
-
# rescue_from ActiveRecord::RecordNotFound do |e|
|
29
|
-
# error_response(message: e.message, status: 404)
|
30
|
-
# end
|
31
|
-
|
32
|
-
# # global exception handler, used for error notifications
|
33
|
-
# rescue_from :all do |e|
|
34
|
-
# if Rails.env.development?
|
35
|
-
# raise e
|
36
|
-
# else
|
37
|
-
# Raven.capture_exception(e)
|
38
|
-
# error_response(message: "Internal server error", status: 500)
|
39
|
-
# end
|
40
|
-
# end
|
41
|
-
|
42
|
-
# # HTTP header based authentication
|
43
|
-
# before do
|
44
|
-
# error!('Unauthorized', 401) unless headers['Authorization'] == "some token"
|
45
|
-
# end
|
46
|
-
|
47
|
-
helpers do
|
48
|
-
|
49
|
-
def format_nice_bytes( number )
|
50
|
-
return "Ø" if number.nil? || number.zero?
|
51
|
-
index = ( Math.log( number ) / Math.log( 2 ) ).to_i / 10
|
52
|
-
converted = number.to_i / ( 1024 ** index )
|
53
|
-
"#{converted} #{SIZE_UNITS[index]}"
|
54
|
-
end
|
55
|
-
|
56
|
-
def api_error exception
|
57
|
-
{
|
58
|
-
:error => exception.class,
|
59
|
-
:errmsg => exception.message,
|
60
|
-
:backtrace => exception.backtrace.first,
|
61
|
-
#:backtrace => exception.backtrace,
|
62
|
-
}
|
63
|
-
end
|
64
|
-
|
65
|
-
def render name, values={}
|
66
|
-
template = File.read("#{APP_ROOT}/lib/#{APP_NAME}/views/#{name.to_s}.haml")
|
67
|
-
haml_engine = Haml::Engine.new(template)
|
68
|
-
haml_engine.render(binding, values)
|
69
|
-
end
|
70
|
-
|
71
|
-
end
|
72
|
-
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
@@ -1,151 +0,0 @@
|
|
1
|
-
module RestFtpDaemon
|
2
|
-
module API
|
3
|
-
|
4
|
-
class Jobs < Grape::API
|
5
|
-
include RestFtpDaemon::API::Defaults
|
6
|
-
logger ActiveSupport::Logger.new Settings.logs.api, 'daily' unless Settings.logs.api.nil?
|
7
|
-
|
8
|
-
params do
|
9
|
-
optional :overwrite, type: Integer, default: false
|
10
|
-
end
|
11
|
-
|
12
|
-
helpers do
|
13
|
-
|
14
|
-
def threads_with_id job_id
|
15
|
-
$threads.list.select do |thread|
|
16
|
-
next unless thread[:job].is_a? Job
|
17
|
-
thread[:job].id == job_id
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def job_describe job_id
|
22
|
-
raise RestFtpDaemon::JobNotFound if ($queue.queued_size==0 && $queue.popped_size==0)
|
23
|
-
|
24
|
-
# Find job with this id
|
25
|
-
found = $queue.all.select { |job| job.id == job_id }.first
|
26
|
-
raise RestFtpDaemon::JobNotFound if found.nil?
|
27
|
-
raise RestFtpDaemon::JobNotFound unless found.is_a? Job
|
28
|
-
|
29
|
-
# Return job description
|
30
|
-
found.describe
|
31
|
-
end
|
32
|
-
|
33
|
-
# def job_delete job_id
|
34
|
-
# end
|
35
|
-
|
36
|
-
def job_list
|
37
|
-
$queue.all.map do |item|
|
38
|
-
next unless item.is_a? Job
|
39
|
-
item.describe
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
desc "Get information about a specific job"
|
46
|
-
params do
|
47
|
-
requires :id, type: Integer, desc: "job id"
|
48
|
-
end
|
49
|
-
get ':id' do
|
50
|
-
info "GET /jobs/#{params[:id]}"
|
51
|
-
begin
|
52
|
-
response = job_describe params[:id].to_i
|
53
|
-
rescue RestFtpDaemon::JobNotFound => exception
|
54
|
-
status 404
|
55
|
-
api_error exception
|
56
|
-
rescue RestFtpDaemonException => exception
|
57
|
-
status 500
|
58
|
-
api_error exception
|
59
|
-
rescue Exception => exception
|
60
|
-
status 501
|
61
|
-
api_error exception
|
62
|
-
else
|
63
|
-
status 200
|
64
|
-
response
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# Delete jobs
|
69
|
-
desc "Kill and remove a specific job"
|
70
|
-
delete ':id' do
|
71
|
-
info "DELETE /jobs/#{params[:name]}"
|
72
|
-
status 501
|
73
|
-
# begin
|
74
|
-
# response = job_delete params[:id].to_i
|
75
|
-
# rescue RestFtpDaemon::JobNotFound => exception
|
76
|
-
# status 404
|
77
|
-
# api_error exception
|
78
|
-
# rescue RestFtpDaemonException => exception
|
79
|
-
# status 500
|
80
|
-
# api_error exception
|
81
|
-
# rescue Exception => exception
|
82
|
-
# status 501
|
83
|
-
# api_error exception
|
84
|
-
# else
|
85
|
-
# status 200
|
86
|
-
# response
|
87
|
-
# end
|
88
|
-
end
|
89
|
-
|
90
|
-
# List jobs
|
91
|
-
desc "Get a list of jobs"
|
92
|
-
get do
|
93
|
-
info "GET /jobs"
|
94
|
-
begin
|
95
|
-
response = job_list
|
96
|
-
rescue RestFtpDaemonException => exception
|
97
|
-
status 501
|
98
|
-
api_error exception
|
99
|
-
rescue Exception => exception
|
100
|
-
status 501
|
101
|
-
api_error exception
|
102
|
-
else
|
103
|
-
status 200
|
104
|
-
response
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
|
109
|
-
# Spawn a new thread for this new job
|
110
|
-
desc "Create a new job"
|
111
|
-
post do
|
112
|
-
info "POST /jobs: #{request.body.read}"
|
113
|
-
begin
|
114
|
-
# Extract params
|
115
|
-
request.body.rewind
|
116
|
-
params = JSON.parse request.body.read
|
117
|
-
|
118
|
-
# Create a new job
|
119
|
-
job_id = $last_worker_id += 1
|
120
|
-
job = Job.new(job_id, params)
|
121
|
-
|
122
|
-
# And psuh it to the queue
|
123
|
-
$queue.push job
|
124
|
-
|
125
|
-
# Later: start it asynchronously
|
126
|
-
#job.future.process
|
127
|
-
|
128
|
-
rescue JSON::ParserError => exception
|
129
|
-
status 406
|
130
|
-
api_error exception
|
131
|
-
rescue RestFtpDaemonException => exception
|
132
|
-
status 412
|
133
|
-
api_error exception
|
134
|
-
rescue Exception => exception
|
135
|
-
status 501
|
136
|
-
api_error exception
|
137
|
-
else
|
138
|
-
status 201
|
139
|
-
job.describe
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
protected
|
144
|
-
|
145
|
-
def progname
|
146
|
-
"API::Jobs"
|
147
|
-
end
|
148
|
-
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
@@ -1,129 +0,0 @@
|
|
1
|
-
require 'haml'
|
2
|
-
require "facter"
|
3
|
-
require "sys/cpu"
|
4
|
-
|
5
|
-
|
6
|
-
module RestFtpDaemon
|
7
|
-
module API
|
8
|
-
|
9
|
-
class Root < Grape::API
|
10
|
-
include RestFtpDaemon::API::Defaults
|
11
|
-
logger ActiveSupport::Logger.new Settings.logs.api, 'daily' unless Settings.logs.api.nil?
|
12
|
-
#add_swagger_documentation
|
13
|
-
|
14
|
-
mount RestFtpDaemon::API::Jobs => '/jobs'
|
15
|
-
# mount RestFtpDaemon::API::Workers => '/workers'
|
16
|
-
|
17
|
-
helpers do
|
18
|
-
def info message, level = 0
|
19
|
-
Root.logger.add(Logger::INFO, "#{' '*level} #{message}", "API::Root")
|
20
|
-
end
|
21
|
-
|
22
|
-
def job_list_by_status
|
23
|
-
statuses = {}
|
24
|
-
alljobs = $queue.all.map do |item|
|
25
|
-
next unless item.is_a? Job
|
26
|
-
statuses[item.get_status] ||= 0
|
27
|
-
statuses[item.get_status] +=1
|
28
|
-
end
|
29
|
-
|
30
|
-
statuses
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
######################################################################
|
36
|
-
####### INIT
|
37
|
-
######################################################################
|
38
|
-
def initialize
|
39
|
-
$last_worker_id = 0
|
40
|
-
super
|
41
|
-
end
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
######################################################################
|
46
|
-
####### API DEFINITION
|
47
|
-
######################################################################
|
48
|
-
|
49
|
-
# Server global status
|
50
|
-
get '/' do
|
51
|
-
# Prepare data
|
52
|
-
@jobs_all = $queue.all
|
53
|
-
#@jobs_all_size = $queue.all_size
|
54
|
-
#@jobs_all = $queue.all_size
|
55
|
-
|
56
|
-
# Initialize UsageWatch
|
57
|
-
Facter.loadfacts
|
58
|
-
@info_load = Sys::CPU.load_avg.first.to_f
|
59
|
-
@info_procs = (Facter.value :processorcount).to_i
|
60
|
-
@info_ipaddr = Facter.value(:ipaddress)
|
61
|
-
@info_memfree = Facter.value(:memoryfree)
|
62
|
-
|
63
|
-
|
64
|
-
# Compute normalized load
|
65
|
-
# puts "info_procs: #{info_procs}"
|
66
|
-
if @info_procs.zero?
|
67
|
-
@info_norm = "N/A"
|
68
|
-
else
|
69
|
-
@info_norm = (100 * @info_load / @info_procs).round(1)
|
70
|
-
end
|
71
|
-
|
72
|
-
# Compute total transferred
|
73
|
-
@total_transferred = 0
|
74
|
-
@jobs_all.each do |job|
|
75
|
-
sent = job.get(:file_sent)
|
76
|
-
@total_transferred += sent unless sent.nil?
|
77
|
-
end
|
78
|
-
|
79
|
-
# Compile haml template
|
80
|
-
@name = "Test"
|
81
|
-
output = render :dashboard
|
82
|
-
|
83
|
-
# Send response
|
84
|
-
env['api.format'] = :html
|
85
|
-
format "html"
|
86
|
-
status 200
|
87
|
-
content_type "text/html"
|
88
|
-
body output
|
89
|
-
|
90
|
-
end
|
91
|
-
|
92
|
-
# Server global status
|
93
|
-
get '/index.json' do
|
94
|
-
info "GET /"
|
95
|
-
status 200
|
96
|
-
return {
|
97
|
-
hostname: `hostname`.chomp,
|
98
|
-
version: Settings.version,
|
99
|
-
config: Settings.to_hash,
|
100
|
-
started: APP_STARTED,
|
101
|
-
uptime: (Time.now - APP_STARTED).round(1),
|
102
|
-
status: job_list_by_status,
|
103
|
-
queue_size: $queue.all_size,
|
104
|
-
jobs_queued: $queue.queued.collect(&:id),
|
105
|
-
jobs_popped: $queue.popped.collect(&:id),
|
106
|
-
routes: RestFtpDaemon::API::Root::routes,
|
107
|
-
}
|
108
|
-
end
|
109
|
-
|
110
|
-
# Server test
|
111
|
-
get '/debug' do
|
112
|
-
info "GET /debug/"
|
113
|
-
begin
|
114
|
-
raise RestFtpDaemon::DummyException
|
115
|
-
rescue RestFtpDaemon::RestFtpDaemonException => exception
|
116
|
-
status 501
|
117
|
-
api_error exception
|
118
|
-
rescue Exception => exception
|
119
|
-
status 501
|
120
|
-
api_error exception
|
121
|
-
else
|
122
|
-
status 200
|
123
|
-
{}
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# module RestFtpDaemon
|
2
|
-
# module API
|
3
|
-
|
4
|
-
# class Workers < Grape::API
|
5
|
-
# include RestFtpDaemon::API::Defaults
|
6
|
-
# logger ActiveSupport::Logger.new Settings.logs.api, 'daily' unless Settings.logs.api.nil?
|
7
|
-
|
8
|
-
# helpers do
|
9
|
-
# def info message, level = 0
|
10
|
-
# Jobs.logger.add(Logger::INFO, "#{' '*level} #{message}", "API::Workers")
|
11
|
-
# end
|
12
|
-
|
13
|
-
# def worker_list
|
14
|
-
# return {
|
15
|
-
# busy: $pool.busy_size,
|
16
|
-
# idle: $pool.idle_size,
|
17
|
-
# to_s: $pool.to_s,
|
18
|
-
# }
|
19
|
-
# #return $workers.list.size
|
20
|
-
# $workers.list.map do |thread|
|
21
|
-
# #next unless thread[:job].is_a? Worker
|
22
|
-
# "worker"
|
23
|
-
# #thread[:worker].inspect
|
24
|
-
# end
|
25
|
-
# end
|
26
|
-
|
27
|
-
# end
|
28
|
-
|
29
|
-
# # List jobs
|
30
|
-
# desc "Get a list of workers"
|
31
|
-
# get do
|
32
|
-
# info "GET /workers"
|
33
|
-
# begin
|
34
|
-
# response = worker_list
|
35
|
-
# rescue RestFtpDaemonException => exception
|
36
|
-
# status 501
|
37
|
-
# api_error exception
|
38
|
-
# rescue Exception => exception
|
39
|
-
# status 501
|
40
|
-
# api_error exception
|
41
|
-
# else
|
42
|
-
# status 200
|
43
|
-
# response
|
44
|
-
# end
|
45
|
-
# end
|
46
|
-
|
47
|
-
# end
|
48
|
-
# end
|
49
|
-
# end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
module RestFtpDaemon
|
2
|
-
|
3
|
-
class Common
|
4
|
-
|
5
|
-
protected
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
# Logger
|
9
|
-
@logger = ActiveSupport::Logger.new Settings.logs.workers, 'daily' unless Settings.logs.workers.nil?
|
10
|
-
end
|
11
|
-
|
12
|
-
def id
|
13
|
-
end
|
14
|
-
|
15
|
-
def progname
|
16
|
-
end
|
17
|
-
|
18
|
-
def info message, level = 0
|
19
|
-
# progname = "Job [#{id}]" unless id.nil?
|
20
|
-
# progname = "Worker [#{id}]" unless worker_id.nil?
|
21
|
-
@logger.add(Logger::INFO, "#{' '*(level+1)} #{message}", progname)
|
22
|
-
end
|
23
|
-
|
24
|
-
def notify signal, error = 0, status = {}
|
25
|
-
# Skip is not callback URL defined
|
26
|
-
url = get :notify
|
27
|
-
if url.nil?
|
28
|
-
info "Skipping notification (no valid url provided) sig[#{signal}] e[#{error}] s#{status.inspect}"
|
29
|
-
return
|
30
|
-
end
|
31
|
-
|
32
|
-
# Build notification
|
33
|
-
n = RestFtpDaemon::Notification.new
|
34
|
-
n.job_id = id
|
35
|
-
n.url = url
|
36
|
-
n.signal = signal
|
37
|
-
n.error = error.inspect
|
38
|
-
n.status = status
|
39
|
-
|
40
|
-
# Now, send the notification
|
41
|
-
info "Queuing notification key[#{n.key}] sig[#{signal}] url[#{url}]"
|
42
|
-
Thread.new(n) do |thread|
|
43
|
-
n.notify
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'settingslogic'
|
2
|
-
DEVELOPMENT = false unless defined? DEVELOPMENT
|
3
|
-
APP_NAME="rest-ftp-daemon"
|
4
|
-
|
5
|
-
class Settings < Settingslogic
|
6
|
-
namespace DEVELOPMENT ? "development" : "production"
|
7
|
-
suppress_errors namespace!="development"
|
8
|
-
end
|
9
|
-
|
10
|
-
# Fix application defaults and load config file if found
|
11
|
-
app_config_file = "/etc/#{APP_NAME}.yml"
|
12
|
-
if File.exists? app_config_file
|
13
|
-
Settings.source app_config_file
|
14
|
-
else
|
15
|
-
Settings.source Hash.new
|
16
|
-
end
|
17
|
-
|
18
|
-
# Forced shared settings
|
19
|
-
Settings[:name] = APP_NAME
|
20
|
-
Settings[:version] = 0.60
|
21
|
-
|
22
|
-
# Forced fixed settings
|
23
|
-
Settings[:default_trim_progname] = "18"
|
24
|
-
Settings[:default_chunk_size] = "1000000"
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module RestFtpDaemon
|
2
|
-
|
3
|
-
class RestFtpDaemonException < StandardError; end
|
4
|
-
|
5
|
-
class DummyException < RestFtpDaemonException; end
|
6
|
-
|
7
|
-
class RequestSourceMissing < RestFtpDaemonException; end
|
8
|
-
class RequestSourceNotFound < RestFtpDaemonException; end
|
9
|
-
class RequestTargetMissing < RestFtpDaemonException; end
|
10
|
-
class RequestTargetScheme < RestFtpDaemonException; end
|
11
|
-
|
12
|
-
class JobPrerequisitesNotMet < RestFtpDaemonException; end
|
13
|
-
|
14
|
-
class JobNotFound < RestFtpDaemonException; end
|
15
|
-
class JobSourceMissing < RestFtpDaemonException; end
|
16
|
-
class JobSourceNotFound < RestFtpDaemonException; end
|
17
|
-
class JobTargetMissing < RestFtpDaemonException; end
|
18
|
-
class JobTargetUnparseable < RestFtpDaemonException; end
|
19
|
-
#class JobTargetPermission < RestFtpDaemonException; end
|
20
|
-
class JobTargetFileExists < RestFtpDaemonException; end
|
21
|
-
|
22
|
-
class NotificationMissingUrl < RestFtpDaemonException; end
|
23
|
-
class NotificationMissingSignal < RestFtpDaemonException; end
|
24
|
-
|
25
|
-
|
26
|
-
end
|