backsum 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d878b3d6463c6744afb48d6e8ff579db32e87b1e
4
+ data.tar.gz: b71480829efc739d4bf7342b1a8c2d77ccb2e6d7
5
+ SHA512:
6
+ metadata.gz: c96af7d9f9b43fd4e59724907b740d9bcaf51d1095095b7572f12c12c6d2acc975ec5f509ee65bed927674b07b5a2fcba7345dc9b15e0b8794a11169ff2d20e0
7
+ data.tar.gz: 28ab1177d4f4daf44ae350752c8d204b68a86c0ee24bfe624e6fc8cc79c0caffc2f5bf84f0d657b7d92a606025f133b3f984919a24978b14c3c7e6bf04450823
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ config
19
+ backups
20
+ projects
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in backsum.gemspec
4
+ gemspec
@@ -0,0 +1,9 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard :rspec do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+ end
9
+
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 sunteya
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,51 @@
1
+ # Backsum
2
+
3
+ backsum is unix base the file system backup tools, it will incremental backup remote file to local storage.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'backsum'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install backsum
18
+
19
+ ## Usage
20
+
21
+ First, you have to configure your backup tasks.
22
+
23
+ $ mkdir ./projects
24
+ $ vi ./projects/one_task.rb
25
+
26
+ Create a ruby file to configure your first task.
27
+
28
+ # one_task.rb
29
+
30
+ name "one_task_name"
31
+
32
+ server "remotehost", username: "www-data" do
33
+ folder "/var/www/demo/apps/one_web/shared"
34
+ folder "/var/www/demo/apps/two_web/shared", excluded: ["logs", "assets"], as: "two_web_backup"
35
+ end
36
+
37
+ server "localhost", local: true do
38
+ folder "/foo", excluded: ["bar"], as: "local_backup"
39
+ end
40
+
41
+ Here’s how you run a backup server.
42
+
43
+ $ backsum --all
44
+
45
+ ## Contributing
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
@@ -0,0 +1,33 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'backsum/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "backsum"
8
+ spec.version = Backsum::VERSION
9
+ spec.authors = [ "sunteya", "jyfcrw" ]
10
+ spec.email = [ "sunteya@gmail.com", "jyfcrw@gmail.com" ]
11
+ spec.description = %q{backsum is unix base the file system backup tools, it will incremental backup remote file to local storage.}
12
+ spec.summary = %q{remote incremental backup tools}
13
+ spec.homepage = "https://github.com/sunteya/backsum"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "cocaine", "~> 0.5.2"
22
+ spec.add_runtime_dependency "posix-spawn", "~> 0.3.6"
23
+ spec.add_runtime_dependency "virtus", "~> 1.0.0"
24
+ spec.add_runtime_dependency "activesupport", ">= 3.0.0"
25
+ spec.add_runtime_dependency "logging", "~> 1.6.2"
26
+
27
+ spec.add_development_dependency "bundler", "~> 1.3"
28
+ spec.add_development_dependency "rake"
29
+ spec.add_development_dependency "pry-nav"
30
+ spec.add_development_dependency "rspec", "~> 2.14.1"
31
+ spec.add_development_dependency "simplecov", "~> 0.7.1"
32
+ spec.add_development_dependency "guard-rspec", "~> 4.0.3"
33
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/backsum/cli'
4
+ Backsum::Cli.new.execute(ARGV)
@@ -0,0 +1,14 @@
1
+ require_relative "backsum/version"
2
+ require "active_support/core_ext/module"
3
+ require "logging"
4
+
5
+ module Backsum
6
+ mattr_accessor :verbose
7
+ @@verbose = false
8
+
9
+ mattr_accessor :logger
10
+ @@logger = Logging.logger($stdout).tap do |logger|
11
+ logger.level = :info
12
+ end
13
+
14
+ end
@@ -0,0 +1,36 @@
1
+ require "virtus"
2
+
3
+ module Backsum
4
+ class Backup
5
+ include Virtus.model
6
+ NAME_PATTERN = "%Y%m%dT%H%M%S"
7
+
8
+ attribute :name
9
+ attribute :base_dir
10
+ attribute :outdated, Boolean
11
+
12
+ def <=>(other)
13
+ self.backup_at <=> other.backup_at
14
+ end
15
+
16
+ def backup_at
17
+ DateTime.strptime(self.name, NAME_PATTERN)
18
+ end
19
+
20
+ def backup_at=(datetime)
21
+ self.name = datetime.strftime(NAME_PATTERN)
22
+ end
23
+
24
+ def path
25
+ File.join(self.base_dir, self.name)
26
+ end
27
+
28
+ def cweek
29
+ self.backup_at.cweek
30
+ end
31
+
32
+ def day
33
+ self.backup_at.day
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,97 @@
1
+ require_relative "../backsum"
2
+ require_relative "version"
3
+ require_relative "project_dsl"
4
+
5
+ require 'optparse'
6
+ require "virtus"
7
+
8
+ module Backsum
9
+ class Cli
10
+ attr_accessor :options, :action
11
+
12
+ def initialize(*args)
13
+ self.options = { projects_path: "./projects" }
14
+ self.action = nil
15
+ end
16
+
17
+ def execute(argv = [])
18
+ option_parser!(argv)
19
+
20
+ Backsum.logger.level = :debug if ::Backsum.verbose
21
+
22
+ if self.action
23
+ send self.action
24
+ else
25
+ self.show_usage
26
+ exit 1
27
+ end
28
+ end
29
+
30
+ def option_parser!(argv)
31
+ @option_parser ||= OptionParser.new do |opts|
32
+ opts.banner = "Usage: #{File.basename($0)} [options] action ..."
33
+
34
+ opts.on("-V", "--version", "Display the Backsum version and exit.") do
35
+ self.action = :show_version
36
+ end
37
+
38
+ opts.on("--all[=PATH]", "excute all the files. default: '#{self.options[:projects_path]}'") do |path|
39
+ self.options[:projects_path] = path if path
40
+ self.action = :perform_by_dir
41
+ end
42
+
43
+ opts.on("-v", "--[no-]verbose", "increase verbosity. default: #{Backsum.verbose}") do |value|
44
+ Backsum.verbose = value
45
+ end
46
+ end
47
+
48
+ @option_parser.parse!(argv)
49
+
50
+ if !self.action && argv.any?
51
+ self.action = :perform_by_inputs
52
+ @input_files = argv
53
+ end
54
+ end
55
+
56
+ def show_usage
57
+ stderr "Please specify one action to execute."
58
+ stderr @option_parser.help
59
+ end
60
+
61
+ def show_version
62
+ stdout "Backsum v#{Backsum::VERSION}"
63
+ end
64
+
65
+ def perform_by_dir
66
+ files = Dir[File.join(self.options[:projects_path], '*.rb')]
67
+ perform_files(files)
68
+ end
69
+
70
+ def perform_by_inputs
71
+ perform_files(@input_files)
72
+ end
73
+
74
+ protected
75
+ def perform_files(files)
76
+ files.each do |file|
77
+ if !File.exist?(file)
78
+ stderr "#{file} is not exist!"
79
+ exit 1
80
+ end
81
+ end
82
+
83
+ files.each do |file|
84
+ project = Backsum::Project.dsl(File.read(file), file)
85
+ project.perform
86
+ end
87
+ end
88
+
89
+ def stdout(*args)
90
+ $stdout.puts(*args)
91
+ end
92
+
93
+ def stderr(*args)
94
+ $stderr.puts(*args)
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,109 @@
1
+ require_relative "../backsum"
2
+ require_relative 'server'
3
+ require_relative 'backup'
4
+ require_relative 'shell'
5
+
6
+ require "fileutils"
7
+ require "virtus"
8
+
9
+ module Backsum
10
+ class Project
11
+ include Virtus.model
12
+ LATEST_LINK_NAME = "Latest"
13
+
14
+ attribute :name
15
+ attribute :servers
16
+ attribute :keep_days
17
+ attribute :keep_weeks
18
+ attribute :backup_to
19
+
20
+ def initialize(*args)
21
+ self.keep_days = 3
22
+ self.keep_weeks = 4
23
+ self.name = "default"
24
+ self.backup_to = Proc.new { "./backups/#{name}" }
25
+ self.servers = []
26
+ super
27
+ end
28
+
29
+ def backup_to
30
+ @backup_to.respond_to?(:call) ? instance_eval(&@backup_to) : @backup_to.to_s
31
+ end
32
+
33
+ def perform
34
+ build_target_backup_folder # prepare
35
+ sync_servers_data # execute
36
+ cleanup_outdate_backups # cleanup
37
+ end
38
+
39
+ def build_target_backup_folder
40
+ FileUtils.mkdir_p(self.backup_to)
41
+ FileUtils.mkdir_p(self.current_backup.path) if !self.latest_backup
42
+ end
43
+
44
+ def sync_servers_data
45
+ lastest_backup_path = self.latest_backup.path if self.latest_backup
46
+
47
+ self.servers.each do |server|
48
+ server.sync self.current_backup.path, lastest_backup_path
49
+ end
50
+
51
+ Dir.chdir self.backup_to do
52
+ FileUtils.rm_rf LATEST_LINK_NAME
53
+ FileUtils.ln_sf self.current_backup.name, LATEST_LINK_NAME
54
+ end
55
+ end
56
+
57
+ def current_backup
58
+ @current_backup ||= Backup.new(backup_at: DateTime.now, base_dir: self.backup_to)
59
+ end
60
+
61
+ def latest_backup
62
+ path = File.join(self.backup_to, LATEST_LINK_NAME)
63
+ if File.exists?(path)
64
+ real_path = File.readlink(path)
65
+ Backup.new(name: File.basename(real_path), base_dir: self.backup_to)
66
+ end
67
+ end
68
+
69
+ def backups
70
+ self.backup_names.map { |backup_name| Backup.new(name: backup_name, base_dir: self.backup_to) }
71
+ end
72
+
73
+ def backup_names
74
+ Dir[File.join(self.backup_to, "*")].map do |backup_path|
75
+ next if File.basename(backup_path) == LATEST_LINK_NAME
76
+ File.basename(backup_path)
77
+ end.compact
78
+ end
79
+
80
+ def latest_link_name
81
+ LATEST_LINK_NAME
82
+ end
83
+
84
+ def cleanup_outdate_backups
85
+ backups = self.backups
86
+
87
+ backups.each { |b| b.outdated = true }
88
+
89
+ # keep weekly backups
90
+ cweeks_mapping = backups.group_by(&:cweek)
91
+ cweeks_mapping.keys.sort.reverse.slice(0, self.keep_weeks).each do |cweek|
92
+ cweeks_mapping[cweek].sort.last.outdated = false
93
+ end
94
+
95
+ # keep daily backups
96
+ days_mapping = backups.group_by(&:day)
97
+ days_mapping.keys.sort.reverse.slice(0, self.keep_days).each do |day|
98
+ days_mapping[day].sort.last.outdated = false
99
+ end
100
+
101
+ # keep latest backup
102
+ # skip, already keep by daily
103
+
104
+ backups.each do |backup|
105
+ FileUtils.rm_rf(backup.path) if backup.outdated?
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,45 @@
1
+ require_relative 'project'
2
+ require_relative 'server'
3
+
4
+ module Backsum
5
+ class Project
6
+
7
+ def self.dsl(*args, &block)
8
+ Dsl.new(*args, &block).instance
9
+ end
10
+
11
+ class Dsl
12
+ attr_accessor :instance
13
+
14
+ def initialize(content = nil, filename = nil, lineno = 0, &block)
15
+ self.instance = Project.new
16
+ self.instance.name = File.basename(filename, ".rb") if filename
17
+
18
+ instance_eval(content, filename, lineno) if content
19
+ instance_eval(&block) if block
20
+ end
21
+
22
+ def name(name)
23
+ self.instance.name = name
24
+ end
25
+
26
+ def server(*args, &block)
27
+ server = Server::Dsl.new(*args, &block).instance
28
+ self.instance.servers << server
29
+ end
30
+
31
+ def keep_days(days)
32
+ self.instance.keep_days = days
33
+ end
34
+
35
+ def keep_weeks(weeks)
36
+ self.instance.keep_weeks = weeks
37
+ end
38
+
39
+ def backup_to(path)
40
+ self.instance.backup_to = path
41
+ end
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,89 @@
1
+ require_relative "../backsum"
2
+ require_relative 'shell'
3
+
4
+ require "virtus"
5
+ require "fileutils"
6
+ require "shellwords"
7
+
8
+ module Backsum
9
+ class Server
10
+ include Virtus.model
11
+
12
+ attribute :host
13
+ attribute :username
14
+ attribute :local
15
+ attribute :folders, Hash
16
+
17
+ def connect
18
+ return nil if local
19
+ username ? "#{username}@#{host}" : host if host
20
+ end
21
+
22
+ def sync(backup_path, linkdest_path)
23
+ self.folders.each_pair do |folder, options|
24
+ execute_rsync(backup_path, linkdest_path, connect, folder, options)
25
+ end
26
+ end
27
+
28
+ def execute_rsync(backup_path, linkdest_path, connect, source, options)
29
+ arguments = [ "--archive", "--delete" ]
30
+ target_path = File.join(backup_path, self.host)
31
+ arguments << "--verbose" if Backsum.verbose
32
+
33
+ if linkdest_path
34
+ linkdest_abosulte_path = File.absolute_path(linkdest_path) # rsync link dest must be abosulte path.
35
+ linkdest_dir = File.join([linkdest_abosulte_path, self.host, options[:as], "/"].compact)
36
+ arguments << "--link-dest=#{linkdest_dir}" if File.exists?(linkdest_dir)
37
+ end
38
+
39
+ if options[:as]
40
+ target_path = File.join(target_path, options[:as])
41
+ else
42
+ arguments << "--relative"
43
+ end
44
+
45
+ FileUtils.mkdir_p target_path
46
+
47
+ (options[:excluded] || []).each do |pattern|
48
+ arguments << "--exclude=#{pattern}"
49
+ end
50
+
51
+ source = File.join(source, "/") if File.directory?(source)
52
+ if connect
53
+ arguments << "#{connect}:#{source}"
54
+ else
55
+ arguments << source
56
+ end
57
+
58
+ arguments << target_path
59
+
60
+ copy_command = Shell.new("rsync", arguments.map {|arg| shell_param_escape(arg) }.join(' '))
61
+ copy_command.run
62
+ end
63
+
64
+ def shell_param_escape(str)
65
+ if str.include? " "
66
+ "'" + str.gsub("'", "\\'") + "'"
67
+ else
68
+ str
69
+ end
70
+ end
71
+
72
+ class Dsl
73
+ attr_accessor :instance
74
+
75
+ def initialize(host, options = {}, &block)
76
+ self.instance = Server.new
77
+ self.instance.host = host
78
+ self.instance.local = options[:local]
79
+ self.instance.username = options[:username]
80
+
81
+ instance_eval(&block) if block
82
+ end
83
+
84
+ def folder(path, options = {})
85
+ self.instance.folders[path] = options
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,43 @@
1
+ require_relative "../backsum"
2
+
3
+ require "cocaine"
4
+ require 'posix/spawn'
5
+
6
+ module Backsum
7
+ class Shell
8
+ def initialize(command, params = "", options = {})
9
+ options[:runner] ||= LoggablePosixRunner.new
10
+ @command_line = Cocaine::CommandLine.new(command, params, options)
11
+ end
12
+
13
+ def run(*args)
14
+ @command_line.run(*args)
15
+ end
16
+ end
17
+
18
+ class LoggablePosixRunner
19
+ def call(command, env = {})
20
+ input, output = IO.pipe
21
+ pid = spawn(env, command, :out => output)
22
+ output.close
23
+ result = ""
24
+
25
+ while line = input.readline
26
+ Backsum.logger.debug(line.chomp)
27
+ result << line
28
+ end rescue EOFError
29
+
30
+ waitpid(pid)
31
+ input.close
32
+ result
33
+ end
34
+
35
+ def spawn(*args)
36
+ ::POSIX::Spawn.spawn(*args)
37
+ end
38
+
39
+ def waitpid(pid)
40
+ ::Process.waitpid(pid)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module Backsum
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,118 @@
1
+ require_relative '../../spec_helper'
2
+ require "backsum/cli"
3
+
4
+ describe Backsum::Cli do
5
+ before(:each) do
6
+ @stdout, @stderr = capture_console_output
7
+ @fixture_dir = File.expand_path("../cli_spec", __FILE__)
8
+ @cli = Cli.new
9
+ @project = double("project")
10
+
11
+ Project.should respond_to(:dsl)
12
+ Project.stub(:dsl).with(any_args).and_return(@project)
13
+ end
14
+
15
+ after(:each) do
16
+ release_console_capture
17
+ end
18
+
19
+ it "can show corrent version" do
20
+ exit_code = run_command "-V"
21
+ exit_code.should == 0
22
+ @stdout.string.should include Backsum::VERSION
23
+ end
24
+
25
+ it "can show help with null arguments" do
26
+ exit_code = run_command
27
+ exit_code.should_not == 0
28
+ @stderr.string.should_not be_empty
29
+ end
30
+
31
+ it "have verbose option" do
32
+ exit_code = run_command "--verbose"
33
+ Backsum.verbose.should == true
34
+ Backsum.logger.level.should == Logging.level_num(:debug)
35
+ end
36
+
37
+ it "can't recieve unexists file" do
38
+ exit_code = run_command "./unexists_file.rb"
39
+ exit_code.should_not == 0
40
+ @stderr.string.should_not be_empty
41
+ end
42
+
43
+ it "can execute perform method with existing file" do
44
+ @project.should receive(:perform).once
45
+ Dir.chdir(@fixture_dir) do
46
+ exit_code = run_command "all-projects/uat.rb"
47
+ exit_code.should == 0
48
+ end
49
+ end
50
+
51
+
52
+ it "the argument '--all' have default value" do
53
+ Dir.chdir(@fixture_dir) do
54
+ exit_code = run_command "--all"
55
+ end
56
+ @cli.options[:projects_path].should == "./projects"
57
+ end
58
+
59
+ it "can execute command with option --all" do
60
+ @project.should receive(:perform).twice
61
+ Dir.chdir(@fixture_dir) do
62
+ exit_code = run_command "--all=./all-projects"
63
+ exit_code.should == 0
64
+ end
65
+
66
+ @cli.options[:projects_path].should == "./all-projects"
67
+ end
68
+
69
+ it "can ignore FILE argument if have argument: '--all'" do
70
+ @project.should receive(:perform).twice
71
+ Dir.chdir(@fixture_dir) do
72
+ exit_code = run_command "--all=./all-projects" , "./ignore_file.rb"
73
+ exit_code.should == 0
74
+ end
75
+ end
76
+
77
+ protected
78
+ def run_command(*argv)
79
+ begin
80
+ @cli.execute(argv)
81
+ return 0
82
+ rescue SystemExit => e
83
+ return e.status
84
+ end
85
+ end
86
+
87
+ def capture_console_output(&block)
88
+ @capture_console_output_start = true
89
+ @origin_stdout = $stdout
90
+ @origin_stderr = $stderr
91
+
92
+ stdout = StringIO.new
93
+ stderr = StringIO.new
94
+
95
+ $stdout = stdout
96
+ $stderr = stderr
97
+
98
+ if block_given?
99
+ begin
100
+ result = yield
101
+ [ stdout, stderr, result ]
102
+ ensure
103
+ release_console_capture
104
+ end
105
+ else
106
+ [ stdout, stderr ]
107
+ end
108
+ end
109
+
110
+ def release_console_capture
111
+ if @capture_console_output_start
112
+ $stdout = @origin_stdout
113
+ $stderr = @origin_stderr
114
+ @capture_console_output_start = false
115
+ end
116
+ end
117
+
118
+ end
@@ -0,0 +1,6 @@
1
+ name "yst"
2
+
3
+ EXCLUDED_FILES = ["logs", "assets"]
4
+ server "ocn-pro-yst", username: "www-data" do
5
+ folder "/var/www/starcloud/apps/portal/shared", excluded: EXCLUDED_FILES, as: "yst-portal"
6
+ end
@@ -0,0 +1,7 @@
1
+ name "uat"
2
+
3
+ EXCLUDED_FILES = ["logs", "assets"]
4
+ server "uat.bstar.wido.me", username: "www-data" do
5
+ folder "/var/www/demo/apps/balm/shared", excluded: EXCLUDED_FILES, as: "balm"
6
+ folder "/var/www/demo/apps/balm-game/shared", excluded: EXCLUDED_FILES
7
+ end
@@ -0,0 +1,70 @@
1
+ require_relative '../../spec_helper'
2
+ require "backsum/project_dsl"
3
+
4
+ describe Backsum::Project do
5
+
6
+ it "can create instance by dsl block" do
7
+ project = Backsum::Project::Dsl.new do
8
+ name "github.com"
9
+ end.instance
10
+
11
+ project.name.should == "github.com"
12
+ end
13
+
14
+ it "can create instance by dsl file" do
15
+ dsl = Backsum::Project::Dsl.new <<-end_eval, __FILE__, __LINE__
16
+ name "ooxx"
17
+ end_eval
18
+
19
+ dsl.instance.name.should == "ooxx"
20
+ end
21
+
22
+ it "can append a server" do
23
+ project = Backsum::Project::Dsl.new do
24
+ server "bstar-pro-uat", username: "root" do
25
+ end
26
+ end.instance
27
+
28
+ project.servers.size.should == 1
29
+ project.servers.first.should be_a Backsum::Server
30
+
31
+ end
32
+
33
+ it "can set default project name by config file basename" do
34
+ dsl = Backsum::Project::Dsl.new nil, "./projects/bak_balm.rb"
35
+ dsl.instance.name.should == "bak_balm"
36
+ end
37
+
38
+ it "can set default backup properties" do
39
+ dsl = Backsum::Project::Dsl.new nil, "./projects/bak_balm.rb"
40
+
41
+ dsl.instance.keep_days.should == 3
42
+ dsl.instance.keep_weeks.should == 4
43
+ dsl.instance.backup_to.should == "./backups/bak_balm"
44
+ end
45
+
46
+ it "can override default backup properties" do
47
+ dsl = Backsum::Project::Dsl.new do
48
+ keep_days 5
49
+ keep_weeks 8
50
+ backup_to "./baks/foo"
51
+ end
52
+
53
+ dsl.instance.keep_days.should == 5
54
+ dsl.instance.keep_weeks.should == 8
55
+ dsl.instance.backup_to.should == "./baks/foo"
56
+ end
57
+
58
+ it "can synchronize backup_to with project name" do
59
+ dsl = Backsum::Project::Dsl.new do
60
+ name "oox"
61
+ end
62
+
63
+ dsl.instance.backup_to.should == "./backups/oox"
64
+ end
65
+
66
+ it "can initial a Project::Dsl instance" do
67
+ project = Project.dsl
68
+ project.should be_a Project
69
+ end
70
+ end
@@ -0,0 +1,86 @@
1
+ require_relative '../../spec_helper'
2
+ require "backsum/project"
3
+ require "pathname"
4
+
5
+ describe Project do
6
+
7
+ before(:each) do
8
+ @fixture_dir = Pathname.new("../project_spec").expand_path(__FILE__)
9
+ @autoremove_files = []
10
+ end
11
+
12
+ after(:each) do
13
+ @autoremove_files.each do |file|
14
+ FileUtils.rm_rf(file) if File.exist?(file)
15
+ end
16
+ end
17
+
18
+ it "can build new target backup folders at first time" do
19
+ first_backup_dir = fixture_temp_dir("first_time")
20
+
21
+ project = Project.new(backup_to: first_backup_dir.to_s)
22
+ project.build_target_backup_folder
23
+
24
+ first_backup_dir.should be_exist
25
+ end
26
+
27
+ it "can cleanup outdated daily backups" do
28
+ deploy_dir = fixture_temp_dir("daliy_cleanup")
29
+ backup_names = {
30
+ "20131008T230000" => false,
31
+ "20131009T220000" => false,
32
+ "20131009T230000" => true,
33
+ "20131010T220000" => false,
34
+ "20131010T230000" => true
35
+ }.each_pair do |datetime, result|
36
+ FileUtils.mkdir_p deploy_dir.join(datetime)
37
+ end
38
+
39
+ project = Project.new(backup_to: deploy_dir, keep_days: 2, keep_weeks: 0)
40
+ project.cleanup_outdate_backups
41
+
42
+ backup_names.each_pair do |backup_name, result|
43
+ deploy_dir.join(backup_name).exist?.should eq(result), backup_name
44
+ end
45
+ end
46
+
47
+ it "can cleanup outdated weekly backups" do
48
+ deploy_dir = fixture_temp_dir("daliy_cleanup")
49
+ backup_names = {
50
+ "20131006T000000" => false, # sun
51
+ "20131009T000000" => false,
52
+ "20131013T000000" => true, # sun
53
+ "20131014T000000" => false, # mon
54
+ "20131020T000000" => true # sun
55
+ }.each_pair do |datetime, result|
56
+ FileUtils.mkdir_p deploy_dir.join(datetime)
57
+ end
58
+
59
+ project = Project.new(backup_to: deploy_dir, keep_days: 0, keep_weeks: 2)
60
+ project.cleanup_outdate_backups
61
+
62
+ backup_names.each_pair do |backup_name, result|
63
+ deploy_dir.join(backup_name).exist?.should == result
64
+ end
65
+ end
66
+
67
+ it "can regenerate sysmbol link after sync server" do
68
+ third_backup_dir = fixture_temp_dir("third_time")
69
+ backup_name = "20131020T153839"
70
+ project = Project.new(backup_to: @fixture_dir.join("third_time"))
71
+
72
+ FileUtils.mkdir_p File.join(project.backup_to, backup_name)
73
+ project.current_backup.name = backup_name
74
+ project.sync_servers_data
75
+
76
+ File.readlink(File.join(project.backup_to, project.latest_link_name)).should == backup_name
77
+ end
78
+
79
+ def fixture_temp_dir(*basenames)
80
+ path = @fixture_dir.join(*basenames)
81
+ path.should_not be_exist
82
+ @autoremove_files << path
83
+ path
84
+ end
85
+
86
+ end
@@ -0,0 +1,63 @@
1
+ require_relative '../../spec_helper'
2
+ require "backsum/server"
3
+ require "pathname"
4
+
5
+ describe Server do
6
+
7
+ before(:each) do
8
+ @fixture_dir = Pathname.new("../server_spec").expand_path(__FILE__)
9
+ @autoremove_files = []
10
+ end
11
+
12
+ after(:each) do
13
+ @autoremove_files.each do |file|
14
+ FileUtils.rm_rf(file) if File.exist?(file)
15
+ end
16
+ end
17
+
18
+ it "can create instance by dsl block" do
19
+ server = Server::Dsl.new "localhost", username: "root" do
20
+ folder "/balm/shared", excluded: [ "logs" ]
21
+ folder "/balm-games/shared"
22
+ end.instance
23
+
24
+ server.host.should == "localhost"
25
+ server.username.should == "root"
26
+ server.folders.size.should == 2
27
+ server.folders["/balm/shared"].should == {:excluded => ["logs"]}
28
+ server.folders["/balm-games/shared"].should == {}
29
+ end
30
+
31
+ it "can sync folder from localhost" do
32
+ source_dir = @fixture_dir.join("source_first/")
33
+ target_dir = @fixture_dir.join("target_first/")
34
+ @autoremove_files << target_dir.to_s
35
+
36
+ server = Server::Dsl.new "localhost", local: true do
37
+ folder source_dir.to_s, excluded: [ "folder2" ]
38
+ end.instance
39
+ server.sync target_dir.to_s, nil
40
+ source_relative_dir = source_dir.relative_path_from(Pathname.new("/"))
41
+ target_dir.join(server.host, source_relative_dir, "folder1").should be_exist
42
+ target_dir.join(server.host, source_relative_dir, "folder2").should_not be_exist
43
+ end
44
+
45
+ it "can sync folder from localhost incrementally" do
46
+ source_dir = @fixture_dir.join("source_increment")
47
+ target_dir = @fixture_dir.join("target_increment")
48
+ linkdest_dir = @fixture_dir.join("target_increment_latest")
49
+ linkdest_path = "./spec/lib/backsum/server_spec/target_increment_latest"
50
+ @autoremove_files << target_dir.to_s
51
+
52
+ server = Server::Dsl.new "localhost", local: true do
53
+ folder source_dir.to_s #, as: "folder1"
54
+ end.instance
55
+
56
+ server.sync target_dir.to_s, linkdest_path
57
+
58
+ target_dir.join(server.host, "folder1", "file1").should be_exist
59
+ File.identical?(target_dir.join(server.host, "folder1", "file1"), linkdest_dir.join(server.host, "folder1", "file1")).should be_true
60
+ target_dir.join(server.host, "folder1", "file2").should be_exist
61
+ end
62
+
63
+ end
@@ -0,0 +1,8 @@
1
+ require_relative '../spec_helper'
2
+ require "backsum"
3
+
4
+ describe Backsum do
5
+ it "have a version" do
6
+ Backsum::VERSION.should be_a String
7
+ end
8
+ end
@@ -0,0 +1,24 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ require "pry-nav"
8
+ require 'simplecov'
9
+ SimpleCov.start
10
+
11
+ module Backsum; end
12
+ include Backsum
13
+
14
+ RSpec.configure do |config|
15
+ config.treat_symbols_as_metadata_keys_with_true_values = true
16
+ config.run_all_when_everything_filtered = true
17
+ # config.filter_run :focus
18
+
19
+ # Run specs in random order to surface order dependencies. If you find an
20
+ # order dependency and want to debug it, you can fix the order by providing
21
+ # the seed, which is printed after each run.
22
+ # --seed 1234
23
+ config.order = 'random'
24
+ end
metadata ADDED
@@ -0,0 +1,249 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: backsum
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - sunteya
8
+ - jyfcrw
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cocaine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: 0.5.2
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: 0.5.2
28
+ - !ruby/object:Gem::Dependency
29
+ name: posix-spawn
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: 0.3.6
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: 0.3.6
42
+ - !ruby/object:Gem::Dependency
43
+ name: virtus
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: 1.0.0
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 1.0.0
56
+ - !ruby/object:Gem::Dependency
57
+ name: activesupport
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - '>='
61
+ - !ruby/object:Gem::Version
62
+ version: 3.0.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 3.0.0
70
+ - !ruby/object:Gem::Dependency
71
+ name: logging
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 1.6.2
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ~>
82
+ - !ruby/object:Gem::Version
83
+ version: 1.6.2
84
+ - !ruby/object:Gem::Dependency
85
+ name: bundler
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: '1.3'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ~>
96
+ - !ruby/object:Gem::Version
97
+ version: '1.3'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rake
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: pry-nav
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: rspec
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ~>
131
+ - !ruby/object:Gem::Version
132
+ version: 2.14.1
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ~>
138
+ - !ruby/object:Gem::Version
139
+ version: 2.14.1
140
+ - !ruby/object:Gem::Dependency
141
+ name: simplecov
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ~>
145
+ - !ruby/object:Gem::Version
146
+ version: 0.7.1
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ~>
152
+ - !ruby/object:Gem::Version
153
+ version: 0.7.1
154
+ - !ruby/object:Gem::Dependency
155
+ name: guard-rspec
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ~>
159
+ - !ruby/object:Gem::Version
160
+ version: 4.0.3
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ~>
166
+ - !ruby/object:Gem::Version
167
+ version: 4.0.3
168
+ description: backsum is unix base the file system backup tools, it will incremental
169
+ backup remote file to local storage.
170
+ email:
171
+ - sunteya@gmail.com
172
+ - jyfcrw@gmail.com
173
+ executables:
174
+ - backsum
175
+ extensions: []
176
+ extra_rdoc_files: []
177
+ files:
178
+ - .gitignore
179
+ - .rspec
180
+ - Gemfile
181
+ - Guardfile
182
+ - LICENSE.txt
183
+ - README.md
184
+ - Rakefile
185
+ - backsum.gemspec
186
+ - bin/backsum
187
+ - lib/backsum.rb
188
+ - lib/backsum/backup.rb
189
+ - lib/backsum/cli.rb
190
+ - lib/backsum/project.rb
191
+ - lib/backsum/project_dsl.rb
192
+ - lib/backsum/server.rb
193
+ - lib/backsum/shell.rb
194
+ - lib/backsum/version.rb
195
+ - spec/lib/backsum/cli_spec.rb
196
+ - spec/lib/backsum/cli_spec/all-projects/mh.rb
197
+ - spec/lib/backsum/cli_spec/all-projects/uat.rb
198
+ - spec/lib/backsum/project_dsl_spec.rb
199
+ - spec/lib/backsum/project_spec.rb
200
+ - spec/lib/backsum/project_spec/second_time/20131020T153839/host1/file1
201
+ - spec/lib/backsum/project_spec/second_time/20131020T153839/host2/file2
202
+ - spec/lib/backsum/server_spec.rb
203
+ - spec/lib/backsum/server_spec/source_first/folder1/file1
204
+ - spec/lib/backsum/server_spec/source_first/folder2/file2
205
+ - spec/lib/backsum/server_spec/source_increment/file1
206
+ - spec/lib/backsum/server_spec/source_increment/file2
207
+ - spec/lib/backsum/server_spec/target_increment_latest/localhost/folder1/file1
208
+ - spec/lib/backsum_spec.rb
209
+ - spec/spec_helper.rb
210
+ homepage: https://github.com/sunteya/backsum
211
+ licenses:
212
+ - MIT
213
+ metadata: {}
214
+ post_install_message:
215
+ rdoc_options: []
216
+ require_paths:
217
+ - lib
218
+ required_ruby_version: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - '>='
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
223
+ required_rubygems_version: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - '>='
226
+ - !ruby/object:Gem::Version
227
+ version: '0'
228
+ requirements: []
229
+ rubyforge_project:
230
+ rubygems_version: 2.0.3
231
+ signing_key:
232
+ specification_version: 4
233
+ summary: remote incremental backup tools
234
+ test_files:
235
+ - spec/lib/backsum/cli_spec.rb
236
+ - spec/lib/backsum/cli_spec/all-projects/mh.rb
237
+ - spec/lib/backsum/cli_spec/all-projects/uat.rb
238
+ - spec/lib/backsum/project_dsl_spec.rb
239
+ - spec/lib/backsum/project_spec.rb
240
+ - spec/lib/backsum/project_spec/second_time/20131020T153839/host1/file1
241
+ - spec/lib/backsum/project_spec/second_time/20131020T153839/host2/file2
242
+ - spec/lib/backsum/server_spec.rb
243
+ - spec/lib/backsum/server_spec/source_first/folder1/file1
244
+ - spec/lib/backsum/server_spec/source_first/folder2/file2
245
+ - spec/lib/backsum/server_spec/source_increment/file1
246
+ - spec/lib/backsum/server_spec/source_increment/file2
247
+ - spec/lib/backsum/server_spec/target_increment_latest/localhost/folder1/file1
248
+ - spec/lib/backsum_spec.rb
249
+ - spec/spec_helper.rb