backzilla 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.
Files changed (41) hide show
  1. data/.gitignore +1 -0
  2. data/COPYING +340 -0
  3. data/LICENSE +57 -0
  4. data/README.markdown +70 -0
  5. data/Rakefile +28 -0
  6. data/VERSION +2 -0
  7. data/backzilla.gemspec +85 -0
  8. data/bin/backzilla +40 -0
  9. data/examples/projects.yaml +31 -0
  10. data/examples/stores.yaml +12 -0
  11. data/lib/backzilla.rb +116 -0
  12. data/lib/backzilla/configuration.rb +6 -0
  13. data/lib/backzilla/connection.rb +5 -0
  14. data/lib/backzilla/entity.rb +31 -0
  15. data/lib/backzilla/entity/directory.rb +46 -0
  16. data/lib/backzilla/entity/mongo_db.rb +47 -0
  17. data/lib/backzilla/entity/my_sql.rb +68 -0
  18. data/lib/backzilla/executor.rb +17 -0
  19. data/lib/backzilla/logger_helper.rb +31 -0
  20. data/lib/backzilla/project.rb +41 -0
  21. data/lib/backzilla/store.rb +49 -0
  22. data/lib/backzilla/store/directory.rb +21 -0
  23. data/lib/backzilla/store/ftp.rb +37 -0
  24. data/lib/backzilla/store/ssh.rb +37 -0
  25. data/lib/backzilla/version.rb +4 -0
  26. data/spec/configs/directory/projects.yaml +5 -0
  27. data/spec/configs/directory/stores.yaml +6 -0
  28. data/spec/configs/mongodb/projects.yaml +5 -0
  29. data/spec/configs/mongodb/stores.yaml +11 -0
  30. data/spec/configs/mysql/projects.yaml +7 -0
  31. data/spec/configs/mysql/stores.yaml +11 -0
  32. data/spec/entities/directory_spec.rb +46 -0
  33. data/spec/entities/mongodb_spec.rb +104 -0
  34. data/spec/entities/mysql_spec.rb +126 -0
  35. data/spec/fixtures/directory/a.txt +2 -0
  36. data/spec/fixtures/directory/b.txt +1 -0
  37. data/spec/fixtures/directory/some/nested/stuff/c.txt +1 -0
  38. data/spec/fixtures/mysql/backzilla_test.sql +54 -0
  39. data/spec/spec.opts +1 -0
  40. data/spec/spec_helper.rb +47 -0
  41. metadata +98 -0
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require 'rake'
2
+ require 'rake/rdoctask'
3
+ require 'spec/rake/spectask'
4
+ require 'rubygems'
5
+ desc 'Default: run Specs.'
6
+ task :default => :spec
7
+
8
+
9
+ desc 'Run specs'
10
+ Spec::Rake::SpecTask.new(:spec) do |t|
11
+ t.libs << 'lib'
12
+ t.verbose = true
13
+ end
14
+
15
+ begin
16
+ require 'jeweler'
17
+ Jeweler::Tasks.new do |gemspec|
18
+ gemspec.name = "backzilla"
19
+ gemspec.summary = "Multi-purpose backup tool"
20
+ gemspec.description = "Backzilla can backup multiple entities to multiple destinations."
21
+ gemspec.email = "pawel.sobolewski@amberbit.com"
22
+ gemspec.homepage = "http://amberbit.com/"
23
+ gemspec.authors = ["Wojtek Piekutowski, Paweł Sobolewski"]
24
+ end
25
+ rescue LoadError
26
+ puts "Jeweler not available. Install it with: gem install jeweler"
27
+ end
28
+
data/VERSION ADDED
@@ -0,0 +1,2 @@
1
+ 0.0.1
2
+
data/backzilla.gemspec ADDED
@@ -0,0 +1,85 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{backzilla}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Wojtek Piekutowski, Pawe\305\202 Sobolewski"]
12
+ s.date = %q{2010-07-30}
13
+ s.default_executable = %q{backzilla}
14
+ s.description = %q{Backzilla can backup multiple entities to multiple destinations.}
15
+ s.email = %q{pawel.sobolewski@amberbit.com}
16
+ s.executables = ["backzilla"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.markdown"
20
+ ]
21
+ s.files = [
22
+ ".gitignore",
23
+ "COPYING",
24
+ "LICENSE",
25
+ "README.markdown",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "backzilla.gemspec",
29
+ "bin/backzilla",
30
+ "examples/projects.yaml",
31
+ "examples/stores.yaml",
32
+ "lib/backzilla.rb",
33
+ "lib/backzilla/configuration.rb",
34
+ "lib/backzilla/connection.rb",
35
+ "lib/backzilla/entity.rb",
36
+ "lib/backzilla/entity/directory.rb",
37
+ "lib/backzilla/entity/mongo_db.rb",
38
+ "lib/backzilla/entity/my_sql.rb",
39
+ "lib/backzilla/executor.rb",
40
+ "lib/backzilla/logger_helper.rb",
41
+ "lib/backzilla/project.rb",
42
+ "lib/backzilla/store.rb",
43
+ "lib/backzilla/store/directory.rb",
44
+ "lib/backzilla/store/ftp.rb",
45
+ "lib/backzilla/store/ssh.rb",
46
+ "lib/backzilla/version.rb",
47
+ "spec/configs/directory/projects.yaml",
48
+ "spec/configs/directory/stores.yaml",
49
+ "spec/configs/mongodb/projects.yaml",
50
+ "spec/configs/mongodb/stores.yaml",
51
+ "spec/configs/mysql/projects.yaml",
52
+ "spec/configs/mysql/stores.yaml",
53
+ "spec/entities/directory_spec.rb",
54
+ "spec/entities/mongodb_spec.rb",
55
+ "spec/entities/mysql_spec.rb",
56
+ "spec/fixtures/directory/a.txt",
57
+ "spec/fixtures/directory/b.txt",
58
+ "spec/fixtures/directory/some/nested/stuff/c.txt",
59
+ "spec/fixtures/mysql/backzilla_test.sql",
60
+ "spec/spec.opts",
61
+ "spec/spec_helper.rb"
62
+ ]
63
+ s.homepage = %q{http://amberbit.com/}
64
+ s.rdoc_options = ["--charset=UTF-8"]
65
+ s.require_paths = ["lib"]
66
+ s.rubygems_version = %q{1.3.5}
67
+ s.summary = %q{Multi-purpose backup tool}
68
+ s.test_files = [
69
+ "spec/spec_helper.rb",
70
+ "spec/entities/directory_spec.rb",
71
+ "spec/entities/mysql_spec.rb",
72
+ "spec/entities/mongodb_spec.rb"
73
+ ]
74
+
75
+ if s.respond_to? :specification_version then
76
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
77
+ s.specification_version = 3
78
+
79
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
80
+ else
81
+ end
82
+ else
83
+ end
84
+ end
85
+
data/bin/backzilla ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
4
+
5
+ require 'backzilla'
6
+ require 'optparse'
7
+
8
+ options = {}
9
+ options[:quiet] = false
10
+ options[:debug] = false
11
+
12
+ OptionParser.new do |cmd_options|
13
+ cmd_options.banner = "Usage: backzilla.rb [OPTIONS]"
14
+
15
+ cmd_options.on("-h", "--help", "Show this message") do
16
+ puts cmd_options
17
+ exit
18
+ end
19
+
20
+ cmd_options.on("-b", "--backup PROJECT_SPEC", "Backups PROJECT_SPEC") do |spec|
21
+ options[:backup] = true
22
+ options[:spec] = spec
23
+ end
24
+
25
+ cmd_options.on("-r", "--restore PROJECT_SPEC", "Restores PROJECT_SPEC") do |spec|
26
+ options[:restore] = true
27
+ options[:spec] = spec
28
+ end
29
+
30
+ cmd_options.on("-q", "--quite", "Supress log messages with level lower than ERROR") do |spec|
31
+ options[:quiet] = true
32
+ end
33
+
34
+ cmd_options.on("-d", "--debug", "Run in debug mode") do |spec|
35
+ options[:debug] = true
36
+ end
37
+ end.parse!
38
+
39
+ Backzilla.run(options)
40
+
@@ -0,0 +1,31 @@
1
+ pkczb:
2
+ database:
3
+ type: MySQL
4
+ database: pkczb_production
5
+ user: backup
6
+ password: q
7
+ files:
8
+ type: Directory
9
+ path: /home/pkczb-www/app
10
+
11
+ amberbit.com:
12
+ database:
13
+ type: MySQL
14
+ database: amberbit
15
+ uploads:
16
+ type: Directory
17
+ path: /home/amberbit/uploads
18
+
19
+ test:
20
+ database:
21
+ type: MySQL
22
+ database: br_development
23
+ user: backup
24
+ password: q
25
+ host:
26
+ port:
27
+ socket:
28
+ files:
29
+ type: Directory
30
+ path: /tmp/test_project
31
+
@@ -0,0 +1,12 @@
1
+ gnupg_passphrase: '`a\bv/]|xDcsfEq,$X|y4:U=5y"{cqRE@RE<w0Mn3A?8DMNBVmF"ZxH]&w1_D4u'
2
+ stores:
3
+ local:
4
+ type: Directory
5
+ path: /tmp/backups/server1
6
+ # my_ftp:
7
+ # type: FTP
8
+ # host: ubuntu-server
9
+ # user: backuper
10
+ # password: q
11
+ # path: /backup/production.amberbit.com
12
+
data/lib/backzilla.rb ADDED
@@ -0,0 +1,116 @@
1
+ require 'yaml'
2
+ require 'pp'
3
+ require 'ostruct'
4
+ require 'logger'
5
+ require 'fileutils'
6
+ require 'pathname'
7
+
8
+ require 'rubygems'
9
+ require 'open4'
10
+
11
+ class Object
12
+ def blank?
13
+ respond_to?(:empty?) ? empty? : !self
14
+ end
15
+ end
16
+
17
+ module Backzilla
18
+ autoload :LoggerHelper, 'backzilla/logger_helper'
19
+ autoload :Project, 'backzilla/project'
20
+ autoload :Entity, 'backzilla/entity'
21
+ autoload :Store, 'backzilla/store'
22
+ autoload :Executor, 'backzilla/executor'
23
+ autoload :Version, 'backzilla/version'
24
+ autoload :Configuration, 'backzilla/configuration'
25
+
26
+ include Backzilla::Version
27
+ include Backzilla::Executor
28
+ extend Backzilla::LoggerHelper
29
+
30
+ STORES_CONFIG = ENV["BACKZILLA_STORES_CONFIG"] || "~/.backzilla/stores.yaml"
31
+ PROJECTS_CONFIG = ENV["BACKZILLA_PROJECTS_CONFIG"] || "~/.backzilla/projects.yaml"
32
+
33
+ def self.store(path, project_name, entity_name)
34
+ info "Storing #{path}..."
35
+
36
+ stores_file = File.expand_path STORES_CONFIG
37
+ data = YAML.load_file stores_file
38
+ Store.gnugpg_passphrase = data['gnupg_passphrase']
39
+ stores = data['stores'].map do |store_name, store_options|
40
+ klass = Backzilla::Store.const_get(store_options['type'])
41
+ klass.new(store_name, store_options)
42
+ end
43
+
44
+ stores.each { |s| s.put path, project_name, entity_name }
45
+ end
46
+
47
+ def self.restore(path, project_name, entity_name)
48
+ info "Restoring #{path}..."
49
+
50
+ restores_file = File.expand_path STORES_CONFIG
51
+ data = YAML.load_file restores_file
52
+ Store.gnugpg_passphrase = data['gnupg_passphrase']
53
+ stores = data['stores'].map do |store_name, store_options|
54
+ klass = Backzilla::Store.const_get(store_options['type'])
55
+ klass.new(store_name, store_options)
56
+ end
57
+
58
+ stores.each { |s| s.get path, project_name, entity_name }
59
+ end
60
+
61
+ def self.logger
62
+ return @logger if @logger
63
+ @logger = Logger.new(STDOUT)
64
+ @logger.level = Logger::DEBUG
65
+ @logger.progname = 'Backzilla'
66
+ @logger
67
+ end
68
+
69
+ def self.options
70
+ Backzilla::Configuration.instance
71
+ end
72
+
73
+ def self.run(options)
74
+ config = Backzilla::Configuration.instance
75
+ options.each do |key, value|
76
+ config.send "#{key}=", value
77
+ end
78
+
79
+ if config.backup && config.restore
80
+ fatal "Use -r or -b separately"
81
+ exit -1
82
+ elsif !config.backup && !config.restore
83
+ fatal "-r or -b required"
84
+ exit -1
85
+ end
86
+
87
+ projects_file = File.expand_path PROJECTS_CONFIG
88
+ data = YAML.load_file projects_file
89
+ if config.spec == 'all'
90
+ projects = data.inject([]) do |projects, project_data|
91
+ project_name, project_entities_data = *project_data
92
+ project = Project.new(project_name)
93
+ project.setup_entities data[project_name]
94
+ projects << project
95
+ end
96
+ projects.each { |p| config.restore ? p.restore : p.backup }
97
+ else
98
+ spec_parts = config.spec.split(':')
99
+ project_name = spec_parts.shift
100
+ unless data[project_name]
101
+ fatal "No such project '#{project_name}'"
102
+ exit -1
103
+ end
104
+
105
+ project = Project.new(project_name)
106
+ project.setup_entities data[project_name]
107
+
108
+ if config.backup
109
+ project.backup spec_parts
110
+ elsif config.restore
111
+ project.restore spec_parts
112
+ end
113
+ end
114
+ end
115
+ end
116
+
@@ -0,0 +1,6 @@
1
+ require 'singleton'
2
+
3
+ class Backzilla::Configuration < OpenStruct
4
+ include Singleton
5
+ end
6
+
@@ -0,0 +1,5 @@
1
+ class Backzilla::Connection
2
+
3
+
4
+ end
5
+
@@ -0,0 +1,31 @@
1
+ class Backzilla::Entity
2
+ autoload :Directory, 'backzilla/entity/directory'
3
+ autoload :MongoDB, 'backzilla/entity/mongo_db'
4
+ autoload :MySQL, 'backzilla/entity/my_sql'
5
+
6
+ include Backzilla::LoggerHelper
7
+
8
+ BASE_PATH = '/tmp/backzilla'
9
+
10
+ attr_reader :name
11
+ attr_accessor :project
12
+
13
+ def initialize(name)
14
+ @name = name
15
+ end
16
+
17
+ private
18
+
19
+ def self.demodulize
20
+ to_s.split('::').last
21
+ end
22
+
23
+ def backup_msg(options={})
24
+ info "Entity name: [#{name}]. Type of backing up files: [#{self.class.demodulize}]"
25
+ end
26
+
27
+ def restore_msg(options={})
28
+ info "Entity name: [#{name}]. Type of restoring files: [#{self.class.demodulize}]"
29
+ end
30
+ end
31
+
@@ -0,0 +1,46 @@
1
+ class Backzilla::Entity::Directory < Backzilla::Entity
2
+ def initialize(name, options)
3
+ super(name)
4
+ @path = options['path']
5
+ end
6
+
7
+ def prepare_backup
8
+ backup_msg
9
+ validate_path
10
+ @path
11
+ end
12
+
13
+ def backup
14
+ prepare_backup
15
+ Backzilla.store @path, project.name, self.name
16
+ @path
17
+ end
18
+
19
+ def finalize_restore
20
+ restore_msg
21
+ validate_path
22
+ @path
23
+ end
24
+
25
+ def restore
26
+ finalize_restore
27
+ FileUtils.rm_rf @path
28
+ FileUtils.mkdir @path
29
+ Backzilla.restore @path, project.name, self.name
30
+ @path
31
+ end
32
+
33
+ private
34
+
35
+ def validate_path
36
+ if @path.blank?
37
+ fatal 'path option missing'
38
+ exit -1
39
+ end
40
+ unless File.exist?(@path)
41
+ fatal "path doesn't exist"
42
+ exit -1
43
+ end
44
+ end
45
+ end
46
+
@@ -0,0 +1,47 @@
1
+ class Backzilla::Entity::MongoDB < Backzilla::Entity
2
+ include Backzilla::Executor
3
+
4
+ def initialize(name, options)
5
+ super(name)
6
+ @database = options['database']
7
+ end
8
+
9
+ def prepare_backup
10
+ if @database.blank?
11
+ fatal "Database name is blank"
12
+ exit -1
13
+ end
14
+ path = Pathname.new(BASE_PATH) + project.name + name
15
+ FileUtils.mkdir_p path
16
+ cmd = "mongodump -d #{@database} -o #{path}"
17
+ execute cmd
18
+ path
19
+ end
20
+
21
+ def backup
22
+ prepare_backup
23
+ backup_msg
24
+ path = Pathname.new(BASE_PATH) + project.name + name
25
+ Backzilla.store path, project.name, self.name
26
+
27
+ FileUtils.rm_rf path
28
+ end
29
+
30
+ def finalize_restore(options={})
31
+ path = options[:path]
32
+ cmd = "mongorestore --drop -d #{@database} #{path}/#{@database}"
33
+ execute cmd
34
+
35
+ FileUtils.rm_rf path
36
+ end
37
+
38
+ def restore
39
+ restore_msg
40
+ path = Pathname.new(BASE_PATH) + project.name + name
41
+ FileUtils.mkdir_p path
42
+
43
+ Backzilla.restore path, project.name, self.name
44
+ finalize_restore(:path => path)
45
+ end
46
+ end
47
+