rest-ftp-daemon 0.6 → 0.9.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.
data/Gemfile CHANGED
@@ -1,3 +1,10 @@
1
1
  source "http://rubygems.org"
2
2
 
3
- gemspec
3
+ gem "sinatra"
4
+ gem "json"
5
+
6
+ group :development do
7
+ gem "shoulda", ">= 0"
8
+ gem "bundler", "~> 1.0"
9
+ gem "jeweler", "~> 2.0.1"
10
+ end
data/Gemfile.lock CHANGED
@@ -1,78 +1,81 @@
1
- PATH
2
- remote: .
3
- specs:
4
- rest-ftp-daemon (0.55)
5
- facter
6
- grape
7
- json
8
- settingslogic
9
- thin (~> 1.6)
10
-
11
1
  GEM
12
2
  remote: http://rubygems.org/
13
3
  specs:
14
- CFPropertyList (2.2.8)
15
- activesupport (4.1.6)
4
+ activesupport (4.1.4)
16
5
  i18n (~> 0.6, >= 0.6.9)
17
6
  json (~> 1.7, >= 1.7.7)
18
7
  minitest (~> 5.1)
19
8
  thread_safe (~> 0.1)
20
9
  tzinfo (~> 1.1)
21
- axiom-types (0.1.1)
22
- descendants_tracker (~> 0.0.4)
23
- ice_nine (~> 0.11.0)
24
- thread_safe (~> 0.3, >= 0.3.1)
10
+ addressable (2.3.6)
25
11
  builder (3.2.2)
26
- coercible (1.0.0)
27
- descendants_tracker (~> 0.0.1)
28
- daemons (1.1.9)
29
12
  descendants_tracker (0.0.4)
30
13
  thread_safe (~> 0.3, >= 0.3.1)
31
- equalizer (0.0.9)
32
- eventmachine (1.0.3)
33
- facter (2.2.0)
34
- CFPropertyList (~> 2.2.6)
35
- grape (0.9.0)
36
- activesupport
37
- builder
38
- hashie (>= 2.1.0)
39
- multi_json (>= 1.3.2)
40
- multi_xml (>= 0.5.2)
41
- rack (>= 1.3.0)
42
- rack-accept
43
- rack-mount
44
- virtus (>= 1.0.0)
45
- hashie (3.3.1)
14
+ faraday (0.9.0)
15
+ multipart-post (>= 1.2, < 3)
16
+ git (1.2.8)
17
+ github_api (0.12.0)
18
+ addressable (~> 2.3)
19
+ descendants_tracker (~> 0.0.4)
20
+ faraday (~> 0.8, < 0.10)
21
+ hashie (>= 3.2)
22
+ multi_json (>= 1.7.5, < 2.0)
23
+ nokogiri (~> 1.6.3)
24
+ oauth2
25
+ hashie (3.2.0)
26
+ highline (1.6.21)
46
27
  i18n (0.6.11)
47
- ice_nine (0.11.0)
28
+ jeweler (2.0.1)
29
+ builder
30
+ bundler (>= 1.0)
31
+ git (>= 1.2.5)
32
+ github_api
33
+ highline (>= 1.6.15)
34
+ nokogiri (>= 1.5.10)
35
+ rake
36
+ rdoc
48
37
  json (1.8.1)
49
- minitest (5.4.1)
38
+ jwt (1.0.0)
39
+ mini_portile (0.6.0)
40
+ minitest (5.4.0)
50
41
  multi_json (1.10.1)
51
42
  multi_xml (0.5.5)
43
+ multipart-post (2.0.0)
44
+ nokogiri (1.6.3.1)
45
+ mini_portile (= 0.6.0)
46
+ oauth2 (1.0.0)
47
+ faraday (>= 0.8, < 0.10)
48
+ jwt (~> 1.0)
49
+ multi_json (~> 1.3)
50
+ multi_xml (~> 0.5)
51
+ rack (~> 1.2)
52
52
  rack (1.5.2)
53
- rack-accept (0.4.5)
54
- rack (>= 0.4)
55
- rack-mount (0.8.3)
56
- rack (>= 1.0.0)
53
+ rack-protection (1.5.3)
54
+ rack
57
55
  rake (10.3.2)
58
- settingslogic (2.0.9)
59
- thin (1.6.2)
60
- daemons (>= 1.0.9)
61
- eventmachine (>= 1.0.0)
62
- rack (>= 1.0.0)
56
+ rdoc (4.1.1)
57
+ json (~> 1.4)
58
+ shoulda (3.5.0)
59
+ shoulda-context (~> 1.0, >= 1.0.1)
60
+ shoulda-matchers (>= 1.4.1, < 3.0)
61
+ shoulda-context (1.2.1)
62
+ shoulda-matchers (2.6.2)
63
+ activesupport (>= 3.0.0)
64
+ sinatra (1.4.5)
65
+ rack (~> 1.4)
66
+ rack-protection (~> 1.4)
67
+ tilt (~> 1.3, >= 1.3.4)
63
68
  thread_safe (0.3.4)
64
- tzinfo (1.2.2)
69
+ tilt (1.4.1)
70
+ tzinfo (1.2.1)
65
71
  thread_safe (~> 0.1)
66
- virtus (1.0.3)
67
- axiom-types (~> 0.1)
68
- coercible (~> 1.0)
69
- descendants_tracker (~> 0.0, >= 0.0.3)
70
- equalizer (~> 0.0, >= 0.0.9)
71
72
 
72
73
  PLATFORMS
73
74
  ruby
74
75
 
75
76
  DEPENDENCIES
76
- bundler (~> 1.6)
77
- rake
78
- rest-ftp-daemon!
77
+ bundler (~> 1.0)
78
+ jeweler (~> 2.0.1)
79
+ json
80
+ shoulda
81
+ sinatra
data/README.md CHANGED
@@ -1,46 +1,25 @@
1
1
  rest-ftp-daemon
2
2
  ====================================================================================
3
3
 
4
-
5
-
6
4
  This is a pretty simple FTP client daemon, controlled through a RESTfull API.
7
5
 
8
6
  As of today, its main features are :
9
7
 
10
- * Allow environment-specific configuration in a YAML file
11
- * Delegate a transfer job by ``POST```'ing a simple JSON structure
8
+ * Delegate a transfer job, ```PUT```'ing posting simple JSON structure
12
9
  * Spawn a dedicated thread to handle this job in its own context
13
- * Report transfer status, progress and errors for each job in realtime
10
+ * Report transfer status, progress and errors for each delegated job
14
11
  * Expose JSON status of workers on ```GET /jobs/``` for automated monitoring
15
- * Parralelize jobs as soon as they arrive
16
- * Handle job queues and priority as an attribute of the job
17
- * Allow dynamic evaluation of priorities, and change of any attribute until the job is picked
18
- * Provide RESTful notifications to the requesting client
19
- * Allow authentication in FTP target in a standard URI-format
20
- * Allow configuration-based path templates to abstract local mounts or remote FTPs (endpoint tokens)
21
-
22
-
23
- Expected features in a short-time range :
24
-
25
- * Allow change of priorities or other attributes after a job has been started
26
- * Offer a basic dashboard directly within the daemon HTTP interface
27
- * Periodically send an update-notification with transfer status and progress
28
- * Allow fallback file source when first file path is unavailable (failover)
29
- * Provide swagger-style API documentation
30
- * Authenticate API clients
31
-
32
12
 
33
13
 
34
14
  Installation
35
15
  ------------------------------------------------------------------------------------
36
16
 
37
- This project is available as a rubygem, requires on ruby >= 1.9.3 and rubygems installed.
17
+ This project is available as a rubygem, requires on ruby >= 1.9 and rubygems installed.
38
18
 
39
19
  Get and install the gem from rubygems.org:
40
20
 
41
21
  ```
42
- # apt-get install ruby1.9.3 ruby-dev rubygems gcc g++
43
- gem install rest-ftp-daemon --no-ri --no-rdoc
22
+ gem install rest-ftp-daemon
44
23
  ```
45
24
 
46
25
  Start the daemon:
@@ -49,117 +28,49 @@ Start the daemon:
49
28
  rest-ftp-daemon start
50
29
  ```
51
30
 
52
- Start the daemon on a specific port :
53
-
54
- ```
55
- rest-ftp-daemon -p4000 start
56
- ```
57
-
58
31
  Check that the daemon is running and giving status info
59
32
 
60
33
  ```
61
34
  http://localhost:3000/
62
35
  ```
63
36
 
64
- Configuration
65
- ------------------------------------------------------------------------------------
66
- Most of the configuration options live in a YAML configuration file, containing two main sections:
67
-
68
- * the ``defaults`` section should be left as-is and will be used is no other environment-specific value is provided.
69
- * the ``production`` section can receive personnalized settings according to your environment-specific setup and paths.
70
-
71
- Configuration priority is defined as follows (from most important to last resort):
72
-
73
- * command-line parameters
74
- * config file defaults section
75
- * config file environment section
76
- * application internal defaults
77
-
78
-
79
- As a starting point, ``rest-ftp-daemon.yml.sample`` is an exemple config file that can be copied into the expected location ``/etc/rest-ftp-daemon.yml``.
80
-
81
-
82
- Logging
83
- ------------------------------------------------------------------------------------
84
-
85
- The application will not log to any file by default, if not specified in its configuration.
86
- Otherwise separate logging paths can be provided for the Thin webserver, API related messages, and workers related messages. Providing and empty value will simply activate logging to ``STDOUT``.
37
+ For now, daemon logs to ```APP_LOGTO``` defined in ```lib/config.rb```
87
38
 
88
39
 
89
40
  Usage examples
90
41
  ------------------------------------------------------------------------------------
91
42
 
92
- * Start a job to transfer a file named "file.iso" to a local FTP server
43
+ Start a job to transfer a file named "file.ova" to a local FTP server
93
44
 
94
45
  ```
95
46
  curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
96
- '{"source":"~/file.iso","target":"ftp://anonymous@localhost/incoming/dest2.iso"}' "http://localhost:3000/jobs"
47
+ '{"source":"~/file.ova","target":"ftp://anonymous@localhost/incoming/dest2.ova"}' "http://localhost:3000/jobs"
97
48
  ```
98
49
 
99
- Requesting notifications is achieved by passing a "notify" key in the request, with a callback URL.
100
- This URL will be called at some points, ``POST```'ing a generic JSON structure with progress information.
101
-
102
-
103
- * Start a job requesting notifications ``POST```'ed on "http://requestb.in/1321axg1"
50
+ Start a job to transfer a file named "file.dmg" to a local FTP server
104
51
 
105
52
  ```
106
53
  curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
107
- '{"source":"~/file.dmg","target":"ftp://anonymous@localhost/incoming/dest4.dmg","notify":"http://requestb.in/1321axg1"}' "http://localhost:3000/jobs"
54
+ '{"source":"~/file.dmg","target":"ftp://anonymous@localhost/incoming/dest4.dmg"}' "http://localhost:3000/jobs"
108
55
  ```
109
56
 
110
- * Start a job with all the above plus a priority
57
+ Get status of a specific job based on its name
111
58
 
112
59
  ```
113
- curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
114
- '{"source":"~/file.dmg","priority":"3", target":"ftp://anonymous@localhost/incoming/dest4.dmg","notify":"http://requestb.in/1321axg1"}' "http://localhost:3000/jobs"
60
+ curl -H "Content-Type: application/json" -X DELETE -D /dev/stdout "http://localhost:3000/jobs/bob-45320-1"
115
61
  ```
116
62
 
117
- * Start a job using endpoint tokens
118
-
119
- First define ``nas`` ans ``ftp1`` in the configuration file :
63
+ Delete a specific job based on its name
120
64
 
121
65
  ```
122
- defaults: &defaults
123
-
124
- development:
125
- <<: *defaults
126
-
127
- endpoints:
128
- nas: "~/"
129
- ftp1: "ftp://anonymous@localhost/incoming/"
130
- ```
131
-
132
- Thos tokens will be expanded when the job is ran :
133
-
134
- ```
135
- curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
136
- '{"source":"~/file.dmg","priority":"3", target":"ftp://anonymous@localhost/incoming/dest4.dmg","notify":"http://requestb.in/1321axg1"}' "http://localhost:3000/jobs"
137
- ```
138
-
139
- NB: a special token [RANDOM] helps to generate a random filename when needed
140
-
141
- * Get status of a specific job based on its ID
142
-
143
- ```
144
- curl -H "Content-Type: application/json" -X GET -D /dev/stdout "http://localhost:3000/jobs/3"
145
- ```
146
-
147
-
148
- * Delete a specific job based on its ID
149
-
150
- ```
151
- curl -H "Content-Type: application/json" -X DELETE -D /dev/stdout "http://localhost:3000/jobs/3"
66
+ curl -H "Content-Type: application/json" -X DELETE -D /dev/stdout "http://localhost:3000/jobs/bob-45320-1"
152
67
  ```
153
68
 
154
69
 
155
70
  Getting status
156
71
  ------------------------------------------------------------------------------------
157
72
 
158
- * A global JSON status is provided on ``` GET /index.json ```
159
-
160
- * A nice dashboard gives a global view of the daemon, jobs in queue, and system status, exposed on ``` GET /```
161
-
162
- * The server exposes its jobs list on ``` GET /jobs ```
73
+ The server exposes jobs list on ``` GET /jobs ```
163
74
 
164
75
  ```
165
76
  http://localhost:3000/jobs
@@ -184,19 +95,18 @@ This query will return a job list :
184
95
  "transferred": 37100000
185
96
  },
186
97
  {
187
- source: "[nas]/file.dmg",
188
- target: "[ftp2]/dest4.dmg",
189
- notify: "http://requestb.in/1321axg1",
190
- updated_at: "2014-09-17 22:56:14 +0200",
191
- id: 2,
192
- started_at: "2014-09-17 22:56:01 +0200",
193
- status: "uploading",
194
- error: 0,
195
- debug_source: "/Users/bruno/file.dmg",
196
- debug_target: "#<URI::FTP:0x007ffa9289e650 URL:ftp://uuuuuuuu:yyyyyyyyy@ftp.xxxxxx.fr/subdir/dest4.dmg>",
197
- file_size: 32093208,
198
- file_progress: 5.6,
199
- file_sent: 1800000
98
+ "source": "~/file.ova",
99
+ "target": "ftp://anonymous@localhost/incoming/dest2.ova",
100
+ "worker_name": "bob-92439-2",
101
+ "created": "2014-08-01 16:53:12 +0200",
102
+ "id": 17,
103
+ "runtime": 13.8,
104
+ "status": "uploading",
105
+ "source_size": 1849036800,
106
+ "error": -1,
107
+ "errmsg": "uploading",
108
+ "progress": 36.1,
109
+ "transferred": 668300000
200
110
  }
201
111
  ]
202
112
  ```
data/Rakefile CHANGED
@@ -1,3 +1,51 @@
1
1
  # encoding: utf-8
2
- require "bundler/gem_tasks"
2
+
3
3
  require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
17
+ gem.name = "rest-ftp-daemon"
18
+ gem.homepage = "http://github.com/bmedici/rest-ftp-daemon"
19
+ gem.license = "MIT"
20
+ gem.summary = "RESTful FTP client daemon"
21
+ gem.description = "This is a pretty simple FTP client daemon, controlled through a RESTfull API"
22
+ gem.email = "rest-ftp-daemon@bmconseil.com"
23
+ gem.authors = ["Bruno"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ desc "Code coverage detail"
36
+ task :simplecov do
37
+ ENV['COVERAGE'] = "true"
38
+ Rake::Task['test'].execute
39
+ end
40
+
41
+ task :default => :test
42
+
43
+ require 'rdoc/task'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "rest-ftp-daemon #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.9.0
data/bin/rest-ftp-daemon CHANGED
@@ -1,42 +1,22 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  # Libs and init
4
- require "thin"
5
- DEVELOPMENT=ARGV.include?("development")
6
- HERE=File.expand_path(File.dirname(__FILE__) + '/../')
7
- #APP_NAME2="rest-ftp-daemon"
8
-
9
- # Include config file and build rack commandline
10
- require File.expand_path("#{HERE}/lib/rest-ftp-daemon/config.rb")
11
-
4
+ require 'thin'
5
+
6
+ # Initialize some local constants
7
+ APP_ROOT = File.dirname(__FILE__) + '/../'
8
+ APP_NAME = 'rest-ftp-daemon'
9
+ APP_VER = File.read "#{APP_ROOT}/VERSION"
10
+ APP_STARTED = Time.now
11
+ APP_DEFAULT_PORT = 3000
12
+ APP_LOGTO = "/tmp/#{APP_NAME}.log"
13
+
14
+ # Prepare thin
15
+ rackup_file = File.expand_path "#{APP_ROOT}/lib/config.ru"
12
16
  argv = ARGV
13
- argv << ["-p", Settings.port.to_s] unless ARGV.include?("-p") || Settings.port.nil?
14
- argv << ["-e", Settings.namespace] unless ARGV.include?("-e") || Settings.namespace.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==1 || Settings.daemonize.nil?) unless ARGV.include?("-d")
17
-
18
- # Rackup file
19
- argv << ["-R", File.expand_path("#{HERE}/config.ru")] unless ARGV.include?("-R")
20
-
21
- # Display compiled configuration
22
- puts
23
- puts "Daemon name \t #{Settings.name}"
24
- puts "Version \t #{Settings.version}"
25
- puts "Environment \t #{Settings.namespace}"
26
- puts "Config file \t #{Settings.source}"
27
- puts "Parameters \t #{argv.flatten}"
28
- puts Settings.to_hash.to_yaml( :Indent => 4, :UseHeader => true, :UseVersion => false )
29
-
30
- # Start Thin with this rackup configuration
31
- puts
32
- begin
33
- Thin::Runner.new(argv.flatten).run!
34
- rescue Thin::PidFileExist
35
- puts "EXITING: daemon was already running (Thin::PidFileExist)"
36
- rescue Thin::PidFileNotFound
37
- puts "EXITING: daemon was not running (Thin::PidFileNotFound)"
38
- else
39
- puts "PROCESS ENDING"
40
- end
41
-
17
+ argv << ["-R", rackup_file] unless ARGV.include?("-R")
18
+ argv << ["-p", APP_DEFAULT_PORT.to_s] unless ARGV.include?("-p")
19
+ argv << ["-e", "production"] unless ARGV.include?("-e")
20
+ argv << ["--stats", "/stats"] unless ARGV.include?("--stats")
42
21
 
22
+ Thin::Runner.new(argv.flatten).run!
data/lib/config.rb ADDED
@@ -0,0 +1,3 @@
1
+ # Transfer configuration
2
+ TRANSFER_CHUNK_SIZE = 100000
3
+ THREAD_SLEEP_BEFORE_DIE = 120
data/lib/config.ru ADDED
@@ -0,0 +1,11 @@
1
+ # Main libs
2
+ require 'sinatra'
3
+ require 'sinatra/base'
4
+ require 'net/ftp'
5
+ require 'json'
6
+
7
+ # My local libs
8
+ Dir[APP_ROOT+"/lib/*.rb"].each {|file| require File.expand_path file }
9
+
10
+ # Start application
11
+ run RestFtpDaemon
data/lib/errors.rb ADDED
@@ -0,0 +1,12 @@
1
+ ERR_OK = 0
2
+ ERR_BUSY = -1
3
+
4
+ ERR_REQ_SOURCE_MISSING = 11
5
+ ERR_REQ_TARGET_MISSING = 12
6
+ ERR_REQ_TARGET_SCHEME = 13
7
+
8
+ ERR_JOB_SOURCE_NOTFOUND = 21
9
+ ERR_JOB_TARGET_UNPARSEABLE = 22
10
+ ERR_JOB_PERMISSION = 24
11
+ ERR_JOB_TARGET_PRESENT = 25
12
+
@@ -0,0 +1,14 @@
1
+ class Thread
2
+
3
+ def job
4
+ # Basic fields
5
+ job = self[:job]
6
+
7
+ # Calculated fields
8
+ job[:id] = self[:id]
9
+ job[:runtime] = (Time.now - job[:created]).round(1)
10
+
11
+ return job
12
+ end
13
+
14
+ end