backdat 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +30 -0
- data/LICENSE.txt +20 -0
- data/README.md +32 -0
- data/Rakefile +56 -0
- data/bin/backdat +7 -0
- data/features/backdat.feature +9 -0
- data/features/step_definitions/backdat_steps.rb +0 -0
- data/features/support/env.rb +13 -0
- data/lib/backdat/application/backdat-server.rb +119 -0
- data/lib/backdat/application.rb +134 -0
- data/lib/backdat/client.rb +10 -0
- data/lib/backdat/config.rb +84 -0
- data/lib/backdat/daemon.rb +155 -0
- data/lib/backdat/exceptions.rb +8 -0
- data/lib/backdat/http.rb +9 -0
- data/lib/backdat/log.rb +19 -0
- data/lib/backdat/server.rb +47 -0
- data/lib/backdat/version.rb +5 -0
- data/lib/backdat.rb +8 -0
- data/spec/backdat_spec.rb +7 -0
- data/spec/spec_helper.rb +32 -0
- metadata +344 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
gem "yajl-ruby", "~> 1.1.0"
|
7
|
+
gem "eventmachine", "~> 0.12.10"
|
8
|
+
gem "em-synchrony", "~> 0.2.0"
|
9
|
+
gem "redis", "~> 3.0.1"
|
10
|
+
|
11
|
+
gem "tilt", "~> 1.3.3"
|
12
|
+
|
13
|
+
gem "mixlib-cli", ">= 1.2.2"
|
14
|
+
gem "mixlib-config", ">= 1.1.0"
|
15
|
+
gem "mixlib-log", ">= 1.3.0"
|
16
|
+
|
17
|
+
gem "puma", "~> 1.5.0"
|
18
|
+
gem "rack", "~> 1.4.1"
|
19
|
+
gem "sinatra", "~> 1.3.2"
|
20
|
+
|
21
|
+
# Add dependencies to develop your gem here.
|
22
|
+
# Include everything needed to run rake, tests, features, etc.
|
23
|
+
group :development do
|
24
|
+
gem "rspec", "~> 2.10.0"
|
25
|
+
gem "yard", "~> 0.8"
|
26
|
+
gem "cucumber", ">= 0"
|
27
|
+
gem "bundler", "~> 1.1.0"
|
28
|
+
gem "jeweler", "~> 1.8.3"
|
29
|
+
gem (RUBY_VERSION =~ /^1\.9/ ? "simplecov" : "rcov"), ">= 0"
|
30
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Josh Toft
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# backdat
|
2
|
+
|
3
|
+
A better backup library and service.
|
4
|
+
|
5
|
+
## Library
|
6
|
+
|
7
|
+
The library consists of a number of backends very similar to the backup gem.
|
8
|
+
|
9
|
+
## Service
|
10
|
+
|
11
|
+
backdat manages backups for the current node and it's transports to the
|
12
|
+
various storage backends.
|
13
|
+
|
14
|
+
## Orchestrator
|
15
|
+
|
16
|
+
Currently not implemented. Intended to acts as a proxy for multiple servers.
|
17
|
+
|
18
|
+
## Contributing to backdat
|
19
|
+
|
20
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
21
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
22
|
+
* Fork the project.
|
23
|
+
* Start a feature/bugfix branch.
|
24
|
+
* Commit and push until you are happy with your contribution.
|
25
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
26
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
27
|
+
|
28
|
+
## Copyright
|
29
|
+
|
30
|
+
Copyright (c) 2012 Josh Toft. See LICENSE.txt for
|
31
|
+
further details.
|
32
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
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
|
+
$:.unshift File.join(File.dirname(__FILE__), 'lib')
|
15
|
+
require 'jeweler'
|
16
|
+
require 'backdat'
|
17
|
+
Jeweler::Tasks.new do |gem|
|
18
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
19
|
+
gem.name = "backdat"
|
20
|
+
gem.homepage = "http://github.com/seryl/backdat"
|
21
|
+
gem.license = "MIT"
|
22
|
+
gem.summary = %Q{A better backup library and service.}
|
23
|
+
gem.description = %Q{A better backup library and service.}
|
24
|
+
gem.email = "joshtoft@gmail.com"
|
25
|
+
gem.authors = ["Josh Toft"]
|
26
|
+
gem.version = Backdat::VERSION
|
27
|
+
# dependencies defined in Gemfile
|
28
|
+
end
|
29
|
+
Jeweler::RubygemsDotOrgTasks.new
|
30
|
+
|
31
|
+
require 'rspec/core'
|
32
|
+
require 'rspec/core/rake_task'
|
33
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
34
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
35
|
+
end
|
36
|
+
|
37
|
+
if RUBY_VERSION =~ /^1\.9/
|
38
|
+
desc "Code coverage detail"
|
39
|
+
task :simplecov do
|
40
|
+
ENV['COVERAGE'] = "true"
|
41
|
+
Rake::Task['spec'].execute
|
42
|
+
end
|
43
|
+
else
|
44
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
45
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
46
|
+
spec.rcov = true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
require 'cucumber/rake/task'
|
51
|
+
Cucumber::Rake::Task.new(:features)
|
52
|
+
|
53
|
+
task :default => :spec
|
54
|
+
|
55
|
+
require 'yard'
|
56
|
+
YARD::Rake::YardocTask.new
|
data/bin/backdat
ADDED
File without changes
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
begin
|
3
|
+
Bundler.setup(:default, :development)
|
4
|
+
rescue Bundler::BundlerError => e
|
5
|
+
$stderr.puts e.message
|
6
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
7
|
+
exit e.status_code
|
8
|
+
end
|
9
|
+
|
10
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
11
|
+
require 'backdat'
|
12
|
+
|
13
|
+
require 'rspec/expectations'
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'backdat/application'
|
2
|
+
require 'backdat/client'
|
3
|
+
require 'backdat/server'
|
4
|
+
|
5
|
+
# The backdat server command line parser.
|
6
|
+
class Backdat::Application::Server < Backdat::Application
|
7
|
+
|
8
|
+
option :config_file,
|
9
|
+
:short => "-c CONFIG",
|
10
|
+
:long => "--config CONFIG",
|
11
|
+
:default => "/etc/backdat.rb",
|
12
|
+
:description => "The configuration file to use"
|
13
|
+
|
14
|
+
option :log_level,
|
15
|
+
:short => "-l LEVEL",
|
16
|
+
:long => "--log_level LEVEL",
|
17
|
+
:description => "Set the log level (debug, info, warn, error, fatal)",
|
18
|
+
:proc => lambda { |l| l.to_sym }
|
19
|
+
|
20
|
+
option :log_location,
|
21
|
+
:short => "-L LOG_LOCATION",
|
22
|
+
:long => "--logfile LOG_LOCATION",
|
23
|
+
:description => "Set the log file location, defaults to STDOUT",
|
24
|
+
:proc => nil
|
25
|
+
|
26
|
+
option :user,
|
27
|
+
:short => "-u USER",
|
28
|
+
:long => "--user USER",
|
29
|
+
:description => "User to set privilege to",
|
30
|
+
:proc => nil
|
31
|
+
|
32
|
+
option :group,
|
33
|
+
:short => "-g GROUP",
|
34
|
+
:long => "--group GROUP",
|
35
|
+
:description => "Group to set privilege to",
|
36
|
+
:proc => nil
|
37
|
+
|
38
|
+
option :daemonize,
|
39
|
+
:short => "-d",
|
40
|
+
:long => "--daemonize",
|
41
|
+
:default => false,
|
42
|
+
:description => "Run the application as a daemon (forces `-s`)",
|
43
|
+
:proc => lambda { |p| true }
|
44
|
+
|
45
|
+
option :environment,
|
46
|
+
:short => "-E",
|
47
|
+
:long => "--environment",
|
48
|
+
:description => "The environment profile to use",
|
49
|
+
:proc => nil
|
50
|
+
|
51
|
+
option :rack_host,
|
52
|
+
:short => "-H HOSTNAME",
|
53
|
+
:long => "--hostname HOSTNAME",
|
54
|
+
:description => "Hostname to listen on (default: 0.0.0.0)",
|
55
|
+
:proc => nil
|
56
|
+
|
57
|
+
option :rack_port,
|
58
|
+
:short => "-P PORT",
|
59
|
+
:long => "--port PORT",
|
60
|
+
:description => "Port to listen on (default: 8080)",
|
61
|
+
:proc => lambda { |p| p.to_i }
|
62
|
+
|
63
|
+
option :pid_file,
|
64
|
+
:short => "-f PID_FILE",
|
65
|
+
:long => "--pid PID_FILE",
|
66
|
+
:description => "Set the PID file location, defaults to /tmp/backdat.pid",
|
67
|
+
:proc => nil
|
68
|
+
|
69
|
+
option :server,
|
70
|
+
:short => "-s",
|
71
|
+
:long => "--server",
|
72
|
+
:default => false,
|
73
|
+
:description => "Start the application as a server",
|
74
|
+
:boolean => true,
|
75
|
+
:proc => nil
|
76
|
+
|
77
|
+
option :help,
|
78
|
+
:short => "-h",
|
79
|
+
:long => "--help",
|
80
|
+
:description => "Show this message",
|
81
|
+
:on => :tail,
|
82
|
+
:boolean => true,
|
83
|
+
:show_options => true,
|
84
|
+
:exit => 0
|
85
|
+
|
86
|
+
option :version,
|
87
|
+
:short => "-v",
|
88
|
+
:long => "--version",
|
89
|
+
:description => "Show backdat version",
|
90
|
+
:boolean => true,
|
91
|
+
:proc => lambda { |v| puts "backdat: #{::Backdat::VERSION}"},
|
92
|
+
:exit => 0
|
93
|
+
|
94
|
+
# Grabs all of the cli parameters and generates the mixlib config object.
|
95
|
+
def initialize
|
96
|
+
super
|
97
|
+
Backdat::Config.merge!(config)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Configures the backdat server based on the cli parameters.
|
101
|
+
def setup_application
|
102
|
+
Backdat::Daemon.change_privilege
|
103
|
+
Backdat::Config[:server] = true if Backdat::Config[:daemonize]
|
104
|
+
if Backdat::Config[:server]
|
105
|
+
@app = Backdat::Server.new
|
106
|
+
else
|
107
|
+
@app = Backdat::Client.new(*ARGV)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Runs the backdat server.
|
112
|
+
def run_application
|
113
|
+
if Backdat::Config[:daemonize]
|
114
|
+
Backdat::Config[:server] = true
|
115
|
+
Backdat::Daemon.daemonize("backdat")
|
116
|
+
end
|
117
|
+
@app.run
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'mixlib/cli'
|
2
|
+
|
3
|
+
# The backdat application class for both server and worker.
|
4
|
+
class Backdat::Application
|
5
|
+
include Mixlib::CLI
|
6
|
+
|
7
|
+
# Added a Wakeup exception.
|
8
|
+
class Wakeup < Exception; end
|
9
|
+
|
10
|
+
# Initialize the application, setting up default handlers.
|
11
|
+
def initialize
|
12
|
+
super
|
13
|
+
|
14
|
+
trap("TERM") do
|
15
|
+
Backdat::Application.fatal!("SIGTERM received, stopping", 1)
|
16
|
+
end
|
17
|
+
|
18
|
+
trap("INT") do
|
19
|
+
Backdat::Application.fatal!("SIGINT received, stopping", 2)
|
20
|
+
end
|
21
|
+
|
22
|
+
trap("QUIT") do
|
23
|
+
Backdat::Log.info("SIGQUIT received, call stack:\n ", caller.join("\n "))
|
24
|
+
end
|
25
|
+
|
26
|
+
trap("HUP") do
|
27
|
+
Backdat::Log.info("SIGHUP received, reconfiguring")
|
28
|
+
reconfigure
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Reconfigure the application and logging.
|
33
|
+
def reconfigure
|
34
|
+
configure_backdat
|
35
|
+
configure_logging
|
36
|
+
end
|
37
|
+
|
38
|
+
# Configure the application throwing a warning when there is no config file.
|
39
|
+
def configure_backdat
|
40
|
+
parse_options
|
41
|
+
|
42
|
+
begin
|
43
|
+
::File.open(config[:config_file]) { |f| apply_config(f.path) }
|
44
|
+
rescue Errno::ENOENT => error
|
45
|
+
msg = "Did not find the config file: #{config[:config_file]}"
|
46
|
+
msg << ", Using command line options."
|
47
|
+
Backdat::Log.warn "*****************************************"
|
48
|
+
Backdat::Log.warn msg
|
49
|
+
Backdat::Log.warn "*****************************************"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Configures the logging in a relatively sane fashion.
|
54
|
+
# Only prints to STDOUT given a valid tty.
|
55
|
+
# Does not write to STDOUT when daemonizing.
|
56
|
+
def configure_logging
|
57
|
+
Backdat::Log.init(Backdat::Config[:log_location])
|
58
|
+
if ( Backdat::Config[:log_location] != STDOUT ) && STDOUT.tty? &&
|
59
|
+
( !Backdat::Config[:daemonize] )
|
60
|
+
stdout_loger = Logger.new(STDOUT)
|
61
|
+
STDOUT.sync = true
|
62
|
+
stdout_logger = Backdat::Log.logger.formatter
|
63
|
+
Backdat::Log.loggers << stdout_logger
|
64
|
+
end
|
65
|
+
Backdat::Log.level = Backdat::Config[:log_level]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Run the application itself. Configure, setup, and then run.
|
69
|
+
def run
|
70
|
+
reconfigure
|
71
|
+
setup_application
|
72
|
+
run_application
|
73
|
+
end
|
74
|
+
|
75
|
+
# Placeholder for setup_application, intended to be overridden.
|
76
|
+
#
|
77
|
+
# @raise Backdat::Exceptions::Application Must be overridden.
|
78
|
+
def setup_application
|
79
|
+
error_msg = "#{self.to_s}: you must override setup_application"
|
80
|
+
raise Backdat::Exceptions::Application, error_msg
|
81
|
+
end
|
82
|
+
|
83
|
+
# Placeholder for run_application, intended to be overridden.
|
84
|
+
#
|
85
|
+
# @raise Backdat::Exceptions::Application Must be overridden.
|
86
|
+
def run_application
|
87
|
+
error_msg = "#{self.to_s}: you must override run_application"
|
88
|
+
raise Backdat::Exceptions::Application, error_msg
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
# Apply the configuration given a file path.
|
94
|
+
#
|
95
|
+
# @param [ String ] config_file_path The path to the configuration file.
|
96
|
+
def apply_config(config_file_path)
|
97
|
+
Backdat::Config.from_file(config_file_path)
|
98
|
+
Backdat::Config.merge!(config)
|
99
|
+
end
|
100
|
+
|
101
|
+
class << self
|
102
|
+
# Present a debug stracktrace upon an error.
|
103
|
+
# Gives a readable backtrace with a timestamp.
|
104
|
+
#
|
105
|
+
# @param [ Exception ] e The raised exception.
|
106
|
+
def debug_stacktrace(e)
|
107
|
+
message = "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
|
108
|
+
stacktrace_out = "Generated at #{Time.now.to_s}\n"
|
109
|
+
stacktrace_out += message
|
110
|
+
|
111
|
+
Backdat::Log.debug(message)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Log a fatal error message to both STDERR and the Logger,
|
115
|
+
# exit the application with a fatal message.
|
116
|
+
#
|
117
|
+
# @param [ String ] msg The message to log.
|
118
|
+
# @param [ Fixnum ] err The exit level.
|
119
|
+
def fatal!(msg, err = -1)
|
120
|
+
Backdat::Log.fatal(msg)
|
121
|
+
Process.exit err
|
122
|
+
end
|
123
|
+
|
124
|
+
# Log a fatal error message to both STDERR and the Logger,
|
125
|
+
# exit the application with a debug message.
|
126
|
+
#
|
127
|
+
# @param [ String ] msg The message to log.
|
128
|
+
# @param [ Fixnum ] err The exit level.
|
129
|
+
def exit!(msg, err = -1)
|
130
|
+
Backdat::Log.debug(msg)
|
131
|
+
Process.exit err
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'backdat/log'
|
2
|
+
require 'mixlib/config'
|
3
|
+
require 'yajl'
|
4
|
+
|
5
|
+
# The configuration object for backdat.
|
6
|
+
class Backdat::Config
|
7
|
+
extend Mixlib::Config
|
8
|
+
|
9
|
+
# Return the configuration itself upon inspection.
|
10
|
+
def self.inspect
|
11
|
+
configuration.inspect
|
12
|
+
end
|
13
|
+
|
14
|
+
# Loads a given file and passes it to the appropriate parser.
|
15
|
+
#
|
16
|
+
# @raise [ IOError ] Any IO Exceptions that occur.
|
17
|
+
#
|
18
|
+
# @param [ String ] filename The filename to read.
|
19
|
+
def self.from_file(filename, parser="ruby")
|
20
|
+
send("from_file_#{parser}".to_sym, filename)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Loads a given ruby file and runs instance_eval against it
|
24
|
+
# in the context of the current object.
|
25
|
+
#
|
26
|
+
# @raise [ IOError ] Any IO Exceptions that occur.
|
27
|
+
#
|
28
|
+
# @param [ String ] filename The file to read.
|
29
|
+
def self.from_file_ruby(filename)
|
30
|
+
self.instance_eval(IO.read(filename), filename, 1)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Loads a given json file and merges the current context
|
34
|
+
# configuration with the updated hash.
|
35
|
+
#
|
36
|
+
# @raise [ IOError ] Any IO Exceptions that occur.
|
37
|
+
# @raise [ Yajl::ParseError ] Raises Yajl Parsing error on improper json.
|
38
|
+
#
|
39
|
+
# @param [ String ] filename The file to read.
|
40
|
+
def self.from_file_json(filename)
|
41
|
+
self.from_stream_json(IO.read(filename))
|
42
|
+
end
|
43
|
+
|
44
|
+
# Loads a given json input and merges the current context
|
45
|
+
# configuration with the updated hash.
|
46
|
+
#
|
47
|
+
# @raise [ IOError ] Any IO Exceptions that occur.
|
48
|
+
# @raise [ Yajl::ParseError ] Raises Yajl Parsing error on improper json.
|
49
|
+
#
|
50
|
+
# @param [ String ] input The json configuration input.
|
51
|
+
def self.from_stream_json(input)
|
52
|
+
parser = Yajl::Parser.new(:symbolize_keys => true)
|
53
|
+
configuration.merge!(parser.parse(input))
|
54
|
+
end
|
55
|
+
|
56
|
+
# When you are using ActiveSupport, they monkey-patch 'daemonize' into
|
57
|
+
# Kernel. So while this is basically identical to what method_missing
|
58
|
+
# would do, we pull it up here and get a real method written so that
|
59
|
+
# things get dispatched properly.
|
60
|
+
config_attr_writer :daemonize do |v|
|
61
|
+
configure do |c|
|
62
|
+
c[:daemonize] = v
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Configuration Settings
|
67
|
+
config_file "/etc/backdat.rb"
|
68
|
+
|
69
|
+
# Logging Settings
|
70
|
+
log_level :info
|
71
|
+
log_location STDOUT
|
72
|
+
|
73
|
+
# Daemonization Settings
|
74
|
+
user nil
|
75
|
+
group nil
|
76
|
+
umask 0022
|
77
|
+
|
78
|
+
pid_file nil
|
79
|
+
|
80
|
+
# Rack Settings
|
81
|
+
rack_host "0.0.0.0"
|
82
|
+
rack_port 8080
|
83
|
+
rack_environment "development"
|
84
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'etc'
|
2
|
+
|
3
|
+
# Daemon helper routines.
|
4
|
+
class Backdat::Daemon
|
5
|
+
class << self
|
6
|
+
attr_accessor :name
|
7
|
+
|
8
|
+
# Daemonize the current process, managing pidfiles and process uid/gid.
|
9
|
+
#
|
10
|
+
# @param [ String ] name The name to be used for the pid file
|
11
|
+
def daemonize(name)
|
12
|
+
@name = name
|
13
|
+
pid = pid_from_file
|
14
|
+
unless running?
|
15
|
+
remove_pid_file()
|
16
|
+
Backdat::Log.info("Daemonizing...")
|
17
|
+
begin
|
18
|
+
exit if fork; Process.setsid; exit if fork
|
19
|
+
msg = "Forked, in #{Process.pid}. "
|
20
|
+
msg << "Privileges: #{Process.euid} #{Process.egid}"
|
21
|
+
Backdat::Log.info(msg)
|
22
|
+
File.umask Backdat::Config[:umask]
|
23
|
+
$stdin.reopen("/dev/null")
|
24
|
+
$stdout.reopen("/dev/null", "a")
|
25
|
+
$stderr.reopen($stdout)
|
26
|
+
save_pid_file;
|
27
|
+
at_exit { remove_pid_file }
|
28
|
+
rescue NotImplementedError => e
|
29
|
+
Backdat::Application.fatal!("There is no fork: #{e.message}")
|
30
|
+
end
|
31
|
+
else
|
32
|
+
Backdat::Application.fatal!("Backdat is already running pid #{pid}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Checks if Backdat is running based on the pid_file.
|
37
|
+
#
|
38
|
+
# @return [ true,false ] Whether or not Backdat is running.
|
39
|
+
def running?
|
40
|
+
if pid_from_file.nil?
|
41
|
+
false
|
42
|
+
else
|
43
|
+
Process.kill(0, pid_from_file)
|
44
|
+
true
|
45
|
+
end
|
46
|
+
rescue Errno::ESRCH, Errno::ENOENT
|
47
|
+
false
|
48
|
+
rescue Errno::EACCES => e
|
49
|
+
msg = "You don't have access to the PID "
|
50
|
+
msg << "file at #{pid_file}: #{e.message}"
|
51
|
+
Backdat::Application.fatal!(msg)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Gets the pid file for @name.
|
55
|
+
#
|
56
|
+
# @return [ String ] Location of the pid file for @name.
|
57
|
+
def pid_file
|
58
|
+
Backdat::Config[:pid_file] or "/tmp/#{@name}.pid"
|
59
|
+
end
|
60
|
+
|
61
|
+
# Sucks the pid out of pid_file.
|
62
|
+
#
|
63
|
+
# @return [ Fixnum,NilClass ] The PID from pid_file or nil if it doesn't exist.
|
64
|
+
def pid_from_file
|
65
|
+
File.read(pid_file).chomp.to_i
|
66
|
+
rescue Errno::ENOENT, Errno::EACCES
|
67
|
+
nil
|
68
|
+
end
|
69
|
+
|
70
|
+
# Store the PID on the filesystem.
|
71
|
+
#
|
72
|
+
# @note
|
73
|
+
# This uses the Backdat::Config[:pid_file] option or "/tmp/name.pid"
|
74
|
+
# by default.
|
75
|
+
def save_pid_file
|
76
|
+
file = pid_file
|
77
|
+
begin
|
78
|
+
FileUtils.mkdir_p(File.dirname(file))
|
79
|
+
rescue Errno::EACCES => e
|
80
|
+
msg = "Failed store pid in #{File.dirname(file)}, "
|
81
|
+
msg << "permission denied: #{e.message}"
|
82
|
+
Backdat::Application.fatal!(msg)
|
83
|
+
end
|
84
|
+
|
85
|
+
begin
|
86
|
+
File.open(file, "w") { |f| f.write(Process.pid.to_s) }
|
87
|
+
rescue Errno::EACCES => e
|
88
|
+
msg = "Couldn't write to pidfile #{file}, "
|
89
|
+
msg << "permission denied: #{e.message}"
|
90
|
+
Backdat::Application.fatal!(msg)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Delete the PID from the filesystem
|
95
|
+
def remove_pid_file
|
96
|
+
FileUtils.rm(pid_file) if File.exists?(pid_file)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Change process user/group to those specified in Backdat::Config
|
100
|
+
def change_privilege
|
101
|
+
Dir.chdir("/")
|
102
|
+
|
103
|
+
msg = "About to change privilege to "
|
104
|
+
if Backdat::Config[:user] and Backdat::Config[:group]
|
105
|
+
msg << "#{Backdat::Config[:user]}:#{Backdat::Config[:group]}"
|
106
|
+
Backdat::Log.info(msg)
|
107
|
+
_change_privilege(Backdat::Config[:user], Backdat::Config[:group])
|
108
|
+
elsif Backdat::Config[:user]
|
109
|
+
msg << "#{Backdat::Config[:user]}"
|
110
|
+
Backdat::Log.info(msg)
|
111
|
+
_change_privilege(Backdat::Config[:user])
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Change privileges of the process to be the specified user and group
|
116
|
+
#
|
117
|
+
# @param [ String ] user The user to change the process to.
|
118
|
+
# @param [ String ] group The group to change the process to.
|
119
|
+
#
|
120
|
+
# @note
|
121
|
+
# The group parameter defaults to user unless specified.
|
122
|
+
def _change_privilege(user, group=user)
|
123
|
+
uid, gid = Process.euid, Process.egid
|
124
|
+
|
125
|
+
begin
|
126
|
+
target_uid = Etc.getpwnam(user).uid
|
127
|
+
rescue ArgumentError => e
|
128
|
+
msg = "Failed to get UID for user #{user}, does it exist? "
|
129
|
+
msg << e.message
|
130
|
+
Backdat::Application.fatal!(msg)
|
131
|
+
return false
|
132
|
+
end
|
133
|
+
|
134
|
+
begin
|
135
|
+
target_gid = Etc.getgrnam(group).gid
|
136
|
+
rescue ArgumentError => e
|
137
|
+
msg = "Failed to get GID for group #{group}, does it exist? "
|
138
|
+
msg << e.message
|
139
|
+
Backdat::Application.fatal!(msg)
|
140
|
+
return false
|
141
|
+
end
|
142
|
+
|
143
|
+
if (uid != target_uid) or (gid != target_gid)
|
144
|
+
Process.initgroups(user, target_gid)
|
145
|
+
Process::GID.change_privilege(target_gid)
|
146
|
+
Process::UID.change_privilege(target_uid)
|
147
|
+
end
|
148
|
+
true
|
149
|
+
rescue Errno::EPERM => e
|
150
|
+
msg = "Permission denied when trying to change #{uid}:#{gid} "
|
151
|
+
msg << "to #{target_uid}:#{target_gid}. #{e.message}"
|
152
|
+
Backdat::Application.fatal!(msg)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/backdat/http.rb
ADDED
data/lib/backdat/log.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'mixlib/log'
|
3
|
+
|
4
|
+
# Backdat's internal logging facility.
|
5
|
+
# Standardized to provide a consistent log format.
|
6
|
+
class Backdat::Log
|
7
|
+
extend Mixlib::Log
|
8
|
+
|
9
|
+
# Force initialization of the primary log device (@logger)
|
10
|
+
init
|
11
|
+
|
12
|
+
# Monkeypatch Formatter to allow local show_time updates.
|
13
|
+
class Formatter
|
14
|
+
# Allow enabling and disabling of time with a singleton.
|
15
|
+
def self.show_time=(*args)
|
16
|
+
Mixlib::Log::Formatter.show_time = *args
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'puma'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'backdat/http'
|
4
|
+
|
5
|
+
# The backdat server and management daemon.
|
6
|
+
class Backdat::Server
|
7
|
+
|
8
|
+
# Creates a new backdat server.
|
9
|
+
def initialize
|
10
|
+
end
|
11
|
+
|
12
|
+
# Runs the server.
|
13
|
+
def run
|
14
|
+
if Backdat::Config[:server]
|
15
|
+
EM.run do
|
16
|
+
@app = Rack::Builder.new do
|
17
|
+
use Rack::Lint
|
18
|
+
use Rack::ShowExceptions
|
19
|
+
run Rack::Cascade.new([Backdat::Http])
|
20
|
+
end.to_app
|
21
|
+
|
22
|
+
Rack::Handler.get(:puma).run(@app, rack_options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Gets the rack options from the configuration.
|
28
|
+
#
|
29
|
+
# @return [ Hash ] The rack options from Backdat::Config.
|
30
|
+
def rack_options
|
31
|
+
opts = Hash.new
|
32
|
+
Backdat::Config.configuration.each do |k,v|
|
33
|
+
if /^rack/ =~ k.to_s
|
34
|
+
param = k.to_s.gsub('rack_', '')
|
35
|
+
|
36
|
+
case param
|
37
|
+
when "environment"
|
38
|
+
opts[param.to_sym] = v
|
39
|
+
else
|
40
|
+
opts[param.capitalize.to_sym] = v
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
opts
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
data/lib/backdat.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
|
4
|
+
if RUBY_VERSION =~ /^1\.9/
|
5
|
+
require 'simplecov'
|
6
|
+
|
7
|
+
module SimpleCov::Configuration
|
8
|
+
def clean_filters
|
9
|
+
@filters = []
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
SimpleCov.configure do
|
14
|
+
clean_filters
|
15
|
+
load_adapter 'test_frameworks'
|
16
|
+
end
|
17
|
+
|
18
|
+
ENV["COVERAGE"] && SimpleCov.start do
|
19
|
+
add_filter "/.rvm/"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'rspec'
|
24
|
+
require 'backdat'
|
25
|
+
|
26
|
+
# Requires supporting files with custom matchers and macros, etc,
|
27
|
+
# in ./support/ and its subdirectories.
|
28
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
29
|
+
|
30
|
+
RSpec.configure do |config|
|
31
|
+
|
32
|
+
end
|
metadata
ADDED
@@ -0,0 +1,344 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: backdat
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Josh Toft
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-08-15 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: yajl-ruby
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.1.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.1.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: eventmachine
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.12.10
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 0.12.10
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: em-synchrony
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.2.0
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.2.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: redis
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 3.0.1
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 3.0.1
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: tilt
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 1.3.3
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 1.3.3
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: mixlib-cli
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 1.2.2
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 1.2.2
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: mixlib-config
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 1.1.0
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 1.1.0
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: mixlib-log
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: 1.3.0
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: 1.3.0
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: puma
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ~>
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: 1.5.0
|
150
|
+
type: :runtime
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ~>
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: 1.5.0
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: rack
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ~>
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: 1.4.1
|
166
|
+
type: :runtime
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ~>
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: 1.4.1
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
name: sinatra
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ~>
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: 1.3.2
|
182
|
+
type: :runtime
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ~>
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: 1.3.2
|
190
|
+
- !ruby/object:Gem::Dependency
|
191
|
+
name: rspec
|
192
|
+
requirement: !ruby/object:Gem::Requirement
|
193
|
+
none: false
|
194
|
+
requirements:
|
195
|
+
- - ~>
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: 2.10.0
|
198
|
+
type: :development
|
199
|
+
prerelease: false
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ~>
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: 2.10.0
|
206
|
+
- !ruby/object:Gem::Dependency
|
207
|
+
name: yard
|
208
|
+
requirement: !ruby/object:Gem::Requirement
|
209
|
+
none: false
|
210
|
+
requirements:
|
211
|
+
- - ~>
|
212
|
+
- !ruby/object:Gem::Version
|
213
|
+
version: '0.8'
|
214
|
+
type: :development
|
215
|
+
prerelease: false
|
216
|
+
version_requirements: !ruby/object:Gem::Requirement
|
217
|
+
none: false
|
218
|
+
requirements:
|
219
|
+
- - ~>
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '0.8'
|
222
|
+
- !ruby/object:Gem::Dependency
|
223
|
+
name: cucumber
|
224
|
+
requirement: !ruby/object:Gem::Requirement
|
225
|
+
none: false
|
226
|
+
requirements:
|
227
|
+
- - ! '>='
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0'
|
230
|
+
type: :development
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
none: false
|
234
|
+
requirements:
|
235
|
+
- - ! '>='
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: '0'
|
238
|
+
- !ruby/object:Gem::Dependency
|
239
|
+
name: bundler
|
240
|
+
requirement: !ruby/object:Gem::Requirement
|
241
|
+
none: false
|
242
|
+
requirements:
|
243
|
+
- - ~>
|
244
|
+
- !ruby/object:Gem::Version
|
245
|
+
version: 1.1.0
|
246
|
+
type: :development
|
247
|
+
prerelease: false
|
248
|
+
version_requirements: !ruby/object:Gem::Requirement
|
249
|
+
none: false
|
250
|
+
requirements:
|
251
|
+
- - ~>
|
252
|
+
- !ruby/object:Gem::Version
|
253
|
+
version: 1.1.0
|
254
|
+
- !ruby/object:Gem::Dependency
|
255
|
+
name: jeweler
|
256
|
+
requirement: !ruby/object:Gem::Requirement
|
257
|
+
none: false
|
258
|
+
requirements:
|
259
|
+
- - ~>
|
260
|
+
- !ruby/object:Gem::Version
|
261
|
+
version: 1.8.3
|
262
|
+
type: :development
|
263
|
+
prerelease: false
|
264
|
+
version_requirements: !ruby/object:Gem::Requirement
|
265
|
+
none: false
|
266
|
+
requirements:
|
267
|
+
- - ~>
|
268
|
+
- !ruby/object:Gem::Version
|
269
|
+
version: 1.8.3
|
270
|
+
- !ruby/object:Gem::Dependency
|
271
|
+
name: simplecov
|
272
|
+
requirement: !ruby/object:Gem::Requirement
|
273
|
+
none: false
|
274
|
+
requirements:
|
275
|
+
- - ! '>='
|
276
|
+
- !ruby/object:Gem::Version
|
277
|
+
version: '0'
|
278
|
+
type: :development
|
279
|
+
prerelease: false
|
280
|
+
version_requirements: !ruby/object:Gem::Requirement
|
281
|
+
none: false
|
282
|
+
requirements:
|
283
|
+
- - ! '>='
|
284
|
+
- !ruby/object:Gem::Version
|
285
|
+
version: '0'
|
286
|
+
description: A better backup library and service.
|
287
|
+
email: joshtoft@gmail.com
|
288
|
+
executables:
|
289
|
+
- backdat
|
290
|
+
extensions: []
|
291
|
+
extra_rdoc_files:
|
292
|
+
- LICENSE.txt
|
293
|
+
- README.md
|
294
|
+
files:
|
295
|
+
- .document
|
296
|
+
- .rspec
|
297
|
+
- Gemfile
|
298
|
+
- LICENSE.txt
|
299
|
+
- README.md
|
300
|
+
- Rakefile
|
301
|
+
- bin/backdat
|
302
|
+
- features/backdat.feature
|
303
|
+
- features/step_definitions/backdat_steps.rb
|
304
|
+
- features/support/env.rb
|
305
|
+
- lib/backdat.rb
|
306
|
+
- lib/backdat/application.rb
|
307
|
+
- lib/backdat/application/backdat-server.rb
|
308
|
+
- lib/backdat/client.rb
|
309
|
+
- lib/backdat/config.rb
|
310
|
+
- lib/backdat/daemon.rb
|
311
|
+
- lib/backdat/exceptions.rb
|
312
|
+
- lib/backdat/http.rb
|
313
|
+
- lib/backdat/log.rb
|
314
|
+
- lib/backdat/server.rb
|
315
|
+
- lib/backdat/version.rb
|
316
|
+
- spec/backdat_spec.rb
|
317
|
+
- spec/spec_helper.rb
|
318
|
+
homepage: http://github.com/seryl/backdat
|
319
|
+
licenses:
|
320
|
+
- MIT
|
321
|
+
post_install_message:
|
322
|
+
rdoc_options: []
|
323
|
+
require_paths:
|
324
|
+
- lib
|
325
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
326
|
+
none: false
|
327
|
+
requirements:
|
328
|
+
- - ! '>='
|
329
|
+
- !ruby/object:Gem::Version
|
330
|
+
version: '0'
|
331
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
332
|
+
none: false
|
333
|
+
requirements:
|
334
|
+
- - ! '>='
|
335
|
+
- !ruby/object:Gem::Version
|
336
|
+
version: '0'
|
337
|
+
requirements: []
|
338
|
+
rubyforge_project:
|
339
|
+
rubygems_version: 1.8.24
|
340
|
+
signing_key:
|
341
|
+
specification_version: 3
|
342
|
+
summary: A better backup library and service.
|
343
|
+
test_files: []
|
344
|
+
has_rdoc:
|