backsum 0.0.1

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.
@@ -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