backup-rails 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +18 -0
- data/.rspec +3 -0
- data/Gemfile +23 -0
- data/Guardfile +31 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/backup-rails.gemspec +28 -0
- data/bin/backup_rails +71 -0
- data/lib/backup-rails/version.rb +5 -0
- data/lib/backup-rails.rb +12 -0
- data/lib/generators/backup/rails/install_generator.rb +32 -0
- data/lib/generators/backup/rails/templates/general.rb +113 -0
- data/lib/generators/backup/rails/templates/schedule.rb +4 -0
- data/lib/tasks/backup.rake +9 -0
- data/spec/bin/prepare_for_testing.rb +90 -0
- data/spec/lib/generators/backup/rails/install_generator_spec.rb +72 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/custom_matchers.rb +168 -0
- metadata +159 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Y2ZiNjBmNjIyY2Q2OTJmNzYyMWIxYzhmYzA0M2UxYTMwYTFkZWYyZA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OGE5ODc1NTk0Yjg5MTBkNTRkM2Q4M2NjYTUyYzIyZjU2NjgwYTY5Ng==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YTQwODczYjIxNGZmYTQ5OWQ3ZGEzMGQ5MDBlZTc1NzY0MDA2ZGM1NWMyYzA2
|
10
|
+
MWJkOTE1OWM2MTBmYzFiMTEwZTE2NWUzODYzZTk4YzIyNWZkYjJiNDVmOWQw
|
11
|
+
OTdjNDhhMmQwODE2OWEyMDI5MWRiMTljYzM0ZTE3YjU1ZWJmNWE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
N2Q0Y2Q4NTkxZmJmODA4NzczZTk1OWMyMTBhMzFiNjkzMWJmYzY4MGRmOGYy
|
14
|
+
NDAwYWZiZWM4ZmI0ODM4MzM5ZWRhZjFkYmI0N2NkYmY1ZmFmZjI2OWIzNTVh
|
15
|
+
YjMyYmQ5MWIxMGY4ODgwOTYwZjNhZjRhNDQ0MDM5Y2I5N2VjNmM=
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
group :development, :test do
|
4
|
+
gem 'guard'
|
5
|
+
gem 'guard-rspec'
|
6
|
+
gem 'guard-bundler'
|
7
|
+
|
8
|
+
gem 'rspec'
|
9
|
+
#gem 'generator_spec', github: "jesson/generator_spec"
|
10
|
+
gem 'rb-inotify'
|
11
|
+
gem 'thor'
|
12
|
+
|
13
|
+
gem 'rails', '~> 3.2.16'
|
14
|
+
gem 'sqlite3'
|
15
|
+
gem 'mysql2'
|
16
|
+
gem 'mongoid'
|
17
|
+
gem 'pg'
|
18
|
+
gem 'bson_ext'
|
19
|
+
end
|
20
|
+
|
21
|
+
# Specify your gem's dependencies in backup-rails.gemspec
|
22
|
+
gemspec
|
23
|
+
|
data/Guardfile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'bundler' do
|
5
|
+
watch('Gemfile')
|
6
|
+
# Uncomment next line if Gemfile contain `gemspec' command
|
7
|
+
# watch(/^.+\.gemspec/)
|
8
|
+
end
|
9
|
+
|
10
|
+
guard 'rspec', cli: "--fail-fast" do
|
11
|
+
watch(%r{^spec/.+_spec\.rb$})
|
12
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
13
|
+
watch('spec/spec_helper.rb') { "spec" }
|
14
|
+
|
15
|
+
# Rails example
|
16
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
17
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
18
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
19
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
20
|
+
watch('config/routes.rb') { "spec/routing" }
|
21
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
22
|
+
|
23
|
+
# Capybara features specs
|
24
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
25
|
+
|
26
|
+
# Turnip features and steps
|
27
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
28
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
29
|
+
end
|
30
|
+
|
31
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Oleg Bavaev
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Backup::Rails
|
2
|
+
|
3
|
+
Backup rails project with backup & whenever gems
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'backup-rails'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install backup-rails
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'backup-rails/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "backup-rails"
|
8
|
+
gem.version = Backup::Rails::VERSION
|
9
|
+
gem.authors = ["Oleg Bavaev"]
|
10
|
+
gem.email = ["jesoba7@gmail.com"]
|
11
|
+
gem.description = %q{Backup rails project with backup & whenever gems}
|
12
|
+
gem.summary = %q{Backup rails project}
|
13
|
+
gem.homepage = "https://github.com/jesson/backup-rails"
|
14
|
+
gem.license = "MIT"
|
15
|
+
|
16
|
+
gem.files = `git ls-files`.split($/)
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ["lib"]
|
20
|
+
|
21
|
+
gem.add_dependency 'backup', '>= 3.4.0'
|
22
|
+
gem.add_dependency 'whenever'
|
23
|
+
gem.add_dependency 'dotenv-rails'
|
24
|
+
gem.add_dependency 'fog', '~> 1.9'
|
25
|
+
gem.add_dependency 'net-ssh', '>= 2.3.0', '<= 2.5.2'
|
26
|
+
gem.add_dependency 'excon', '~> 0.17.0'
|
27
|
+
end
|
28
|
+
|
data/bin/backup_rails
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'thor'
|
3
|
+
require 'dotenv'
|
4
|
+
require 'yaml'
|
5
|
+
|
6
|
+
class BackupRailsCLI < Thor
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
def self.source_root
|
10
|
+
File.dirname(__FILE__)
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "restore ARCHIVE DESTINATION", "Restore files from backup file"
|
14
|
+
method_option :ssl_password,
|
15
|
+
:aliases => ['-sp'],
|
16
|
+
:required => false,
|
17
|
+
:type => :string,
|
18
|
+
:desc => "password for SSL encrypt"
|
19
|
+
def restore archive_path, destination_path
|
20
|
+
puts "Restoring from #{archive_path}"
|
21
|
+
|
22
|
+
Dotenv.load
|
23
|
+
|
24
|
+
dir = Dir.mktmpdir
|
25
|
+
|
26
|
+
begin
|
27
|
+
if options[:ssl_password]
|
28
|
+
# decrypt & untar
|
29
|
+
create_file dir + "/simple.txt", options[:ssl_password]
|
30
|
+
system "cd #{dir} && openssl aes-256-cbc -d -base64 -pass file:simple.txt -in #{archive_path} -out restore.tar && rm simple.txt"
|
31
|
+
system "cd #{dir} && tar -xf restore.tar"
|
32
|
+
else
|
33
|
+
# untar
|
34
|
+
system "cd #{dir} && tar -xf #{archive_path}"
|
35
|
+
end
|
36
|
+
# unpacking code
|
37
|
+
system "mkdir -p #{destination_path} && cd #{destination_path} && tar -xf #{dir}/general/archives/code.tar.gz"
|
38
|
+
|
39
|
+
# restore database
|
40
|
+
environment = 'production'
|
41
|
+
if File.exists? destination_path + "/config/database.yml"
|
42
|
+
dbconfig = YAML::load(ERB.new(IO.read(File.join(destination_path, 'config', 'database.yml'))).result)[environment]
|
43
|
+
|
44
|
+
if dbconfig['adapter'] == 'mysql2'
|
45
|
+
run "echo \"create database #{dbconfig['database']}\" | mysql -u #{dbconfig['username']} --password=#{dbconfig['password']}"
|
46
|
+
run "cd #{dir}/general/databases && gzip -d MySQL.sql.gz"
|
47
|
+
run "cd #{dir}/general/databases && mysql -u #{dbconfig['username']} --password=#{dbconfig['password']} #{dbconfig['database']} < MySQL.sql"
|
48
|
+
elsif dbconfig['adapter'] == 'postgresql'
|
49
|
+
run "export PGPASSWORD=#{dbconfig['password']} && createdb -U#{dbconfig['username']} #{dbconfig['database']}"
|
50
|
+
run "cd #{dir}/general/databases && gzip -d PostgreSQL.sql.gz"
|
51
|
+
run "cd #{dir}/general/databases && export PGPASSWORD=#{dbconfig['password']} && psql -U#{dbconfig['username']} #{dbconfig['database']} < PostgreSQL.sql"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
# mongoid
|
55
|
+
if File.exists? destination_path + "/config/mongoid.yml"
|
56
|
+
dbconfig = YAML::load(ERB.new(IO.read(File.join(destination_path, 'config', 'mongoid.yml'))).result)[environment]
|
57
|
+
dbconfig = dbconfig['sessions']['default']
|
58
|
+
run "cd #{dir}/general/databases && tar -xf MongoDB.tar.gz"
|
59
|
+
options = { username: dbconfig['username'], password: dbconfig['password'], db: dbconfig['database'] }
|
60
|
+
params = options.select {|k,v| !v.nil? && !v.empty? }.map {|k,v| "--#{k} #{v}"}.join(" ")
|
61
|
+
run "cd #{dir}/general/databases && mongorestore #{params} #{dir}/general/databases/MongoDB/#{dbconfig['database']}"
|
62
|
+
end
|
63
|
+
|
64
|
+
ensure
|
65
|
+
# remove the directory.
|
66
|
+
#FileUtils.remove_entry_secure dir
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
BackupRailsCLI.start
|
data/lib/backup-rails.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module Backup
|
4
|
+
module Rails
|
5
|
+
module Generators
|
6
|
+
class InstallGenerator < ::Rails::Generators::Base
|
7
|
+
desc "Generates configurations for backup and whenever"
|
8
|
+
|
9
|
+
def self.source_root
|
10
|
+
@source_root ||= File.join(File.dirname(__FILE__), 'templates')
|
11
|
+
end
|
12
|
+
|
13
|
+
# Generator Code. Remember this is just suped-up Thor so methods are executed in order
|
14
|
+
def install
|
15
|
+
run "bundle exec backup generate:config --config-path=config/backup" unless File.exists?("config/backup/config.rb")
|
16
|
+
template "general.rb", "config/backup/models/general.rb"
|
17
|
+
if File.exists? ".env"
|
18
|
+
append_file ".env" do
|
19
|
+
File.read(File.expand_path(find_in_source_paths('.env')))
|
20
|
+
end
|
21
|
+
else
|
22
|
+
template ".env"
|
23
|
+
end
|
24
|
+
run "bundle exec wheneverize ." unless File.exists?("config/schedule.rb")
|
25
|
+
append_file "config/schedule.rb" do
|
26
|
+
File.read(File.expand_path(find_in_source_paths('schedule.rb')))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'dotenv'
|
3
|
+
require "rails"
|
4
|
+
|
5
|
+
##
|
6
|
+
# Backup Generated: general
|
7
|
+
# Once configured, you can run the backup with the following command:
|
8
|
+
#
|
9
|
+
# $ backup perform -t general [-c <path_to_configuration_file>]
|
10
|
+
#
|
11
|
+
Backup::Model.new(:general, 'Description for general') do
|
12
|
+
Dotenv.load
|
13
|
+
|
14
|
+
root_path = File.dirname(__FILE__)
|
15
|
+
archive :code do |archive|
|
16
|
+
archive.root root_path
|
17
|
+
archive.add "."
|
18
|
+
archive.exclude root_path + '/log'
|
19
|
+
archive.exclude root_path + '/tmp'
|
20
|
+
end
|
21
|
+
|
22
|
+
compress_with Gzip do
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
if ENV['SSL_PASSWORD']
|
27
|
+
encrypt_with OpenSSL do |encryption|
|
28
|
+
encryption.password = ENV['SSL_PASSWORD']
|
29
|
+
encryption.base64 = true
|
30
|
+
encryption.salt = true
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
if File.exists? root_path + "/config/database.yml"
|
35
|
+
environment = 'production'
|
36
|
+
dbconfig = YAML::load(ERB.new(IO.read(File.join(root_path, 'config', 'database.yml'))).result)[environment]
|
37
|
+
if dbconfig['adapter'] == 'mysql2'
|
38
|
+
database MySQL do |db|
|
39
|
+
db.name = dbconfig['database']
|
40
|
+
db.username = dbconfig['username']
|
41
|
+
db.password = dbconfig['password']
|
42
|
+
db.host = dbconfig['host']
|
43
|
+
db.port = dbconfig['port']
|
44
|
+
db.socket = dbconfig['socket']
|
45
|
+
end
|
46
|
+
elsif dbconfig['adapter'] == 'postgresql'
|
47
|
+
database PostgreSQL do |db|
|
48
|
+
db.name = dbconfig['database']
|
49
|
+
db.username = dbconfig['username']
|
50
|
+
db.password = dbconfig['password']
|
51
|
+
db.host = dbconfig['host']
|
52
|
+
db.port = dbconfig['port']
|
53
|
+
db.socket = dbconfig['socket']
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if File.exists? root_path + "/config/mongoid.yml"
|
59
|
+
environment = 'production'
|
60
|
+
dbconfig = YAML::load(ERB.new(IO.read(File.join(root_path, 'config', 'mongoid.yml'))).result)[environment]
|
61
|
+
if dbconfig
|
62
|
+
database MongoDB do |db|
|
63
|
+
db.name = dbconfig['sessions']['default']['database']
|
64
|
+
db.username = dbconfig['sessions']['default']['username']
|
65
|
+
db.password = dbconfig['sessions']['default']['password']
|
66
|
+
#db.host = dbconfig['host']
|
67
|
+
#db.port = dbconfig['port']
|
68
|
+
db.ipv6 = false
|
69
|
+
db.lock = false
|
70
|
+
db.oplog = false
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if ENV['LOCAL_PATH']
|
76
|
+
store_with Local do |local|
|
77
|
+
local.path = ENV['LOCAL_PATH']
|
78
|
+
local.keep = 5
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
if ENV['S3_ACCESS_KEY_ID'] && ENV['S3_SECRET_ACCESS_KEY'] && ENV['S3_BUCKET']
|
83
|
+
store_with S3 do |s3|
|
84
|
+
# AWS Credentials
|
85
|
+
s3.access_key_id = ENV['S3_ACCESS_KEY_ID']
|
86
|
+
s3.secret_access_key = ENV['S3_SECRET_ACCESS_KEY']
|
87
|
+
# Or, to use a IAM Profile:
|
88
|
+
# s3.use_iam_profile = true
|
89
|
+
|
90
|
+
s3.region = ENV['S3_REGION']
|
91
|
+
s3.bucket = ENV['S3_BUCKET']
|
92
|
+
#s3.path = ENV['S3_PATH']
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
if ENV['NOTIFY_MAIL_USER_NAME']
|
97
|
+
notify_by Mail do |mail|
|
98
|
+
mail.on_success = true
|
99
|
+
mail.on_warning = true
|
100
|
+
mail.on_failure = true
|
101
|
+
|
102
|
+
mail.from = ENV['NOTIFY_MAIL_FROM']
|
103
|
+
mail.to = ENV['NOTIFY_MAIL_TO']
|
104
|
+
mail.address = ENV['NOTIFY_MAIL_ADDRESS']
|
105
|
+
mail.port = ENV['NOTIFY_MAIL_PORT']
|
106
|
+
mail.domain = ENV['NOTIFY_MAIL_DOMAIN']
|
107
|
+
mail.user_name = ENV['NOTIFY_MAIL_USER_NAME']
|
108
|
+
mail.password = ENV['NOTIFY_MAIL_PASSWORD']
|
109
|
+
mail.authentication = ENV['NOTIFY_MAIL_AUTHENTICATION']
|
110
|
+
mail.encryption = :starttls
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
namespace "backup" do
|
2
|
+
task "backup" => :environment do
|
3
|
+
system "bundle exec backup perform -t general -c config/backup/config.rb -l tmp/backup/log -d tmp/backup/data --tmp-path=tmp/backup/tmp --cache-path=tmp/backup/cache"
|
4
|
+
end
|
5
|
+
|
6
|
+
task "restore" => :environment do
|
7
|
+
Backup::Config.load Rails.root.join + 'config/backup/config.rb'
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'thor'
|
3
|
+
|
4
|
+
class PrepareForTesting < Thor
|
5
|
+
include Thor::Actions
|
6
|
+
|
7
|
+
def self.source_root
|
8
|
+
File.dirname(__FILE__)
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Generate", "Generate rails apps for testing"
|
12
|
+
def generate
|
13
|
+
destination_root = File.dirname(__FILE__) + "/../../tmp"
|
14
|
+
dbname = "backup_rails"
|
15
|
+
username = "backup_rails"
|
16
|
+
password = "123123123"
|
17
|
+
inside destination_root do
|
18
|
+
rails_versions = %w(3.2.16)
|
19
|
+
rails_versions.each do |rails_version|
|
20
|
+
%w(sqlite3 mysql mongodb postgresql).each do |database_type|
|
21
|
+
path = "test_#{rails_version}_#{database_type}"
|
22
|
+
remove_dir path
|
23
|
+
run "gem install rails -v #{rails_version} --conservative"
|
24
|
+
case database_type
|
25
|
+
when 'sqlite3'
|
26
|
+
run "rails _#{rails_version}_ new #{path} -d sqlite3 -B"
|
27
|
+
when 'mysql'
|
28
|
+
run "rails _#{rails_version}_ new #{path} -d mysql -B"
|
29
|
+
when 'postgresql'
|
30
|
+
run "rails _#{rails_version}_ new #{path} -d postgresql -B"
|
31
|
+
when 'mongodb'
|
32
|
+
run "rails _#{rails_version}_ new #{path} -O -B"
|
33
|
+
end
|
34
|
+
inside path do
|
35
|
+
if %w(mysql postgresql).include? database_type
|
36
|
+
gsub_file "config/database.yml", /database:(.+)$/, "database: #{dbname}"
|
37
|
+
gsub_file "config/database.yml", /username:(.+)$/, "username: #{username}"
|
38
|
+
gsub_file "config/database.yml", /password:(.*)$/, "password: '#{password}'"
|
39
|
+
end
|
40
|
+
gsub_file "config/environments/development.rb", "config.action_mailer", "# config.action_mailer"
|
41
|
+
append_file "Gemfile", "gem 'backup-rails', path: '../../'\n"
|
42
|
+
|
43
|
+
if database_type == "mongodb"
|
44
|
+
if rails_version == "3.2.16"
|
45
|
+
append_file "Gemfile", "gem 'mongoid'\n"
|
46
|
+
else
|
47
|
+
append_file "Gemfile", "gem 'mongoid', github: 'mongoid/mongoid'\n"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
run "bundle install"
|
52
|
+
run "rails generate mongoid:config" if database_type == "mongodb"
|
53
|
+
if database_type == 'mongodb'
|
54
|
+
gsub_file "config/mongoid.yml", /database: (.+)$/, "database: #{dbname}"
|
55
|
+
append_file "config/mongoid.yml",
|
56
|
+
<<-eos
|
57
|
+
production:
|
58
|
+
sessions:
|
59
|
+
default:
|
60
|
+
hosts:
|
61
|
+
- localhost:27017
|
62
|
+
database: #{dbname}
|
63
|
+
eos
|
64
|
+
end
|
65
|
+
run "rails generate scaffold post title:string body:text published_at:datetime"
|
66
|
+
run "rake db:drop"
|
67
|
+
run "rake db:create"
|
68
|
+
run "rake db:migrate"
|
69
|
+
remove_file "db/seeds.rb"
|
70
|
+
create_file "db/seeds.rb", "10.times.each {|i| Post.create!(title: 'Title!', body: 'fjasdfhiwue72h3jh', published_at: Time.now)}"
|
71
|
+
run "rake db:seed"
|
72
|
+
|
73
|
+
# dump
|
74
|
+
case database_type
|
75
|
+
when 'mysql'
|
76
|
+
run "mysqldump -u#{username} --password=#{password} #{dbname} > mysqldump.sql"
|
77
|
+
when 'postgresql'
|
78
|
+
run "export PGPASSWORD=#{password} && pg_dump --username=#{username} #{dbname} > pgsqldump.sql"
|
79
|
+
when 'mongodb'
|
80
|
+
run "mongodump --db #{dbname} --out mongodump"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
PrepareForTesting.start
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "generators/backup/rails/install_generator"
|
3
|
+
|
4
|
+
describe Backup::Rails::Generators::InstallGenerator do
|
5
|
+
let(:tmp_path) { File.expand_path("../../../../../../tmp", __FILE__) }
|
6
|
+
|
7
|
+
let(:dbname) { "backup_rails" }
|
8
|
+
let(:username) { "backup_rails" }
|
9
|
+
let(:password) { "123123123" }
|
10
|
+
|
11
|
+
let(:ssl_password) { "123123123" }
|
12
|
+
let(:backup_path) { tmp_path + "/backups" }
|
13
|
+
|
14
|
+
# Crypt variants
|
15
|
+
[false, true].each do |with_crypt|
|
16
|
+
|
17
|
+
# Database type variants
|
18
|
+
%w(sqlite3 mysql mongodb postgresql).each do |database_type|
|
19
|
+
|
20
|
+
# Storage type variants
|
21
|
+
%w(local S3).each do |storage_type|
|
22
|
+
context "Code + #{database_type.capitalize} => #{storage_type.capitalize} => With#{!with_crypt ? "out":""} crypt" do
|
23
|
+
|
24
|
+
# Rails version variants
|
25
|
+
%w(3.2.16).each do |rails_version|
|
26
|
+
context "(rails #{rails_version})" do
|
27
|
+
let(:test_rails_project_path) { "test_#{rails_version}_#{database_type}" }
|
28
|
+
|
29
|
+
it "backups & restores" do
|
30
|
+
# prepare project
|
31
|
+
prepare_project
|
32
|
+
|
33
|
+
# rails generate backup_rails:install
|
34
|
+
install_config
|
35
|
+
|
36
|
+
# restore database
|
37
|
+
restore_database(database_type)
|
38
|
+
|
39
|
+
assert_file "Gemfile", /gem 'backup-rails'/
|
40
|
+
assert_file "config/backup/config.rb"
|
41
|
+
assert_file "config/backup/models/general.rb"
|
42
|
+
assert_file "config/schedule.rb"
|
43
|
+
assert_file ".env"
|
44
|
+
|
45
|
+
# fill .env
|
46
|
+
write_env(storage_type, with_crypt)
|
47
|
+
|
48
|
+
# backup
|
49
|
+
backup_project
|
50
|
+
|
51
|
+
# drop database
|
52
|
+
drop_database(database_type)
|
53
|
+
|
54
|
+
# check remote storage
|
55
|
+
check_remote_storage(storage_type)
|
56
|
+
|
57
|
+
# restore
|
58
|
+
restore_project(with_crypt)
|
59
|
+
|
60
|
+
# check code
|
61
|
+
expect(compare_dirs(tmp_path + '/test_generator', tmp_path + '/test_generator_restore')).to be_true
|
62
|
+
|
63
|
+
# check database
|
64
|
+
expect(check_database(database_type)).to be_true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#require 'rails'
|
2
|
+
#require 'backup-rails'
|
3
|
+
|
4
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
5
|
+
|
6
|
+
class RSpec::Core::ExampleGroup
|
7
|
+
def self.run_all(reporter=nil)
|
8
|
+
run(reporter || RSpec::Mocks::Mock.new('reporter').as_null_object)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
RSpec.configure do |config|
|
13
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
14
|
+
config.filter_run :focus
|
15
|
+
config.run_all_when_everything_filtered = true
|
16
|
+
config.before(:each) do
|
17
|
+
@real_world = RSpec.world
|
18
|
+
RSpec.instance_variable_set(:@world, RSpec::Core::World.new)
|
19
|
+
end
|
20
|
+
config.after(:each) do
|
21
|
+
RSpec.instance_variable_set(:@world, @real_world)
|
22
|
+
end
|
23
|
+
config.order = :random
|
24
|
+
|
25
|
+
config.include CustomMatchers
|
26
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'mysql2'
|
2
|
+
require 'mongoid'
|
3
|
+
require 'dotenv'
|
4
|
+
require 'fog'
|
5
|
+
require 'pg'
|
6
|
+
|
7
|
+
class Post
|
8
|
+
include Mongoid::Document
|
9
|
+
field :title
|
10
|
+
end
|
11
|
+
|
12
|
+
module CustomMatchers
|
13
|
+
def assert_file(relative, *contents)
|
14
|
+
absolute = File.expand_path(relative, tmp_path + "/test_generator")
|
15
|
+
expect(File).to be_exists(absolute), "Expected file #{relative.inspect} to exist, but does not"
|
16
|
+
|
17
|
+
read = File.read(absolute) if block_given? || !contents.empty?
|
18
|
+
yield read if block_given?
|
19
|
+
|
20
|
+
contents.each do |content|
|
21
|
+
case content
|
22
|
+
when String
|
23
|
+
expect(content).to eq read
|
24
|
+
when Regexp
|
25
|
+
expect(content).to match read
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def sameFile path1, path2
|
31
|
+
return File.stat(path1).size == File.stat(path2).size
|
32
|
+
end
|
33
|
+
|
34
|
+
def compare_dirs dir1, dir2
|
35
|
+
return false unless File.exists?(dir1) && File.exists?(dir2)
|
36
|
+
Dir.foreach(dir1) do |item|
|
37
|
+
next if item == "." or item == ".."
|
38
|
+
path1 = File.join(dir1, item)
|
39
|
+
path2 = File.join(dir1, item)
|
40
|
+
if File.directory?(path1)
|
41
|
+
next if File.symlink? path1
|
42
|
+
return false unless File.directory? path2
|
43
|
+
|
44
|
+
compare_dirs(path1, path2)
|
45
|
+
else
|
46
|
+
return false unless File.file? path2
|
47
|
+
return false unless sameFile(path1, path2)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
def check_database database_type
|
54
|
+
case database_type
|
55
|
+
when 'mysql'
|
56
|
+
client = Mysql2::Client.new(:host => "localhost", :username => username, database: dbname, password: password)
|
57
|
+
return client.query("select * from posts").count == 10
|
58
|
+
when 'mongodb'
|
59
|
+
Mongoid.configure.connect_to("backup_rails")
|
60
|
+
return Post.count == 10
|
61
|
+
when 'postgresql'
|
62
|
+
conn = PG::Connection.new(dbname: dbname, user: username, password: password)
|
63
|
+
return conn.exec("select * from posts").values.size == 10
|
64
|
+
when 'sqlite3'
|
65
|
+
return true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def restore_database database_type
|
70
|
+
output = ""
|
71
|
+
output += drop_database database_type
|
72
|
+
case database_type
|
73
|
+
when 'mysql'
|
74
|
+
output += run "echo \"create database #{dbname}\" | mysql -u #{username} --password=#{password}"
|
75
|
+
output += run "cd #{tmp_path}/test_generator && mysql -u #{username} --password=#{password} #{dbname} < mysqldump.sql"
|
76
|
+
when 'mongodb'
|
77
|
+
output += run "cd #{tmp_path}/test_generator && mongorestore --db backup_rails mongodump/backup_rails"
|
78
|
+
when 'postgresql'
|
79
|
+
output += run "export PGPASSWORD=#{password} && createdb -U#{username} #{dbname}", false
|
80
|
+
output += run "cd #{tmp_path}/test_generator && export PGPASSWORD=#{password} && psql -U#{username} #{dbname} < pgsqldump.sql"
|
81
|
+
end
|
82
|
+
output
|
83
|
+
end
|
84
|
+
|
85
|
+
def drop_database database_type
|
86
|
+
output = ""
|
87
|
+
case database_type
|
88
|
+
when 'mysql'
|
89
|
+
output += run "echo \"drop database #{dbname}\" | mysql -u #{username} --password=#{password}", false
|
90
|
+
when 'mongodb'
|
91
|
+
output += run "cd #{tmp_path}/test_generator && mongo backup_rails --eval \"db.dropDatabase()\"", false
|
92
|
+
when 'postgresql'
|
93
|
+
output += run "export PGPASSWORD=#{password} && dropdb -U#{username} #{dbname}", false
|
94
|
+
end
|
95
|
+
output
|
96
|
+
end
|
97
|
+
|
98
|
+
def write_env storage_type, with_crypt
|
99
|
+
Dotenv.load
|
100
|
+
File.open(tmp_path + "/test_generator/.env", "w") do |f|
|
101
|
+
f.write("SSL_PASSWORD=#{ssl_password}\n") if with_crypt
|
102
|
+
case storage_type
|
103
|
+
when 'local'
|
104
|
+
f.write("LOCAL_PATH=#{backup_path}")
|
105
|
+
when 'S3'
|
106
|
+
f.write("S3_ACCESS_KEY_ID=\"#{ENV['S3_ACCESS_KEY_ID']}\"\n")
|
107
|
+
f.write("S3_SECRET_ACCESS_KEY=\"#{ENV['S3_SECRET_ACCESS_KEY']}\"\n")
|
108
|
+
f.write("S3_REGION=\"#{ENV['S3_REGION']}\"\n")
|
109
|
+
f.write("S3_BUCKET=\"#{ENV['S3_BUCKET']}\"\n")
|
110
|
+
f.write("S3_PATH=\"#{ENV['S3_PATH']}\"\n")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def check_remote_storage storage_type
|
116
|
+
case storage_type
|
117
|
+
when 'S3'
|
118
|
+
connection = Fog::Storage.new({
|
119
|
+
:provider => 'AWS',
|
120
|
+
:aws_access_key_id => ENV['S3_ACCESS_KEY_ID'],
|
121
|
+
:aws_secret_access_key => ENV['S3_SECRET_ACCESS_KEY']
|
122
|
+
})
|
123
|
+
dir = connection.directories.detect { | dir | dir.key == ENV["S3_BUCKET"] }
|
124
|
+
file = dir.files.max_by {|f| f.last_modified }
|
125
|
+
file_path = File.expand_path(file.key, tmp_path)
|
126
|
+
path = File.dirname(file_path)
|
127
|
+
run "mkdir -p #{path}"
|
128
|
+
File.open(file_path, "wb") do |f|
|
129
|
+
f.write file.body
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def restore_project with_crypt
|
135
|
+
run "cd #{tmp_path} && rm -fr test_generator_restore"
|
136
|
+
if with_crypt
|
137
|
+
archive_path = Dir[backup_path + "/general/*/general.tar.enc"].first
|
138
|
+
run "cd #{tmp_path} && ../bin/backup_rails restore #{archive_path} #{tmp_path}/test_generator_restore --ssl_password=#{ssl_password}"
|
139
|
+
else
|
140
|
+
archive_path = Dir[backup_path + "/general/*/general.tar"].first
|
141
|
+
run "cd #{tmp_path} && ../bin/backup_rails restore #{archive_path} #{tmp_path}/test_generator_restore"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def prepare_project
|
146
|
+
run "cd #{tmp_path} && rm -fr test_generator && cp -r #{test_rails_project_path} test_generator"
|
147
|
+
end
|
148
|
+
|
149
|
+
def install_config
|
150
|
+
run "cd #{tmp_path}/test_generator && rails generate backup:rails:install"
|
151
|
+
end
|
152
|
+
|
153
|
+
def run command, check_exitstatus=true
|
154
|
+
output = %x(#{command})
|
155
|
+
p output unless $?.success?
|
156
|
+
$?.success?.should be_true, "Error run command: #{command}" if check_exitstatus
|
157
|
+
output
|
158
|
+
end
|
159
|
+
|
160
|
+
def backup_project
|
161
|
+
# remove backup dir
|
162
|
+
run "rm -fr #{backup_path}"
|
163
|
+
|
164
|
+
output = run "cd #{tmp_path}/test_generator && bundle exec rake backup:backup"
|
165
|
+
expect(output).to_not match /\[warn\]/
|
166
|
+
expect(output).to_not match /\[error\]/
|
167
|
+
end
|
168
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: backup-rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Oleg Bavaev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-01-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: backup
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 3.4.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 3.4.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: whenever
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dotenv-rails
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: fog
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.9'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.9'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: net-ssh
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 2.3.0
|
76
|
+
- - <=
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: 2.5.2
|
79
|
+
type: :runtime
|
80
|
+
prerelease: false
|
81
|
+
version_requirements: !ruby/object:Gem::Requirement
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 2.3.0
|
86
|
+
- - <=
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 2.5.2
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: excon
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ~>
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: 0.17.0
|
96
|
+
type: :runtime
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ~>
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 0.17.0
|
103
|
+
description: Backup rails project with backup & whenever gems
|
104
|
+
email:
|
105
|
+
- jesoba7@gmail.com
|
106
|
+
executables:
|
107
|
+
- backup_rails
|
108
|
+
extensions: []
|
109
|
+
extra_rdoc_files: []
|
110
|
+
files:
|
111
|
+
- .gitignore
|
112
|
+
- .rspec
|
113
|
+
- Gemfile
|
114
|
+
- Guardfile
|
115
|
+
- LICENSE
|
116
|
+
- README.md
|
117
|
+
- Rakefile
|
118
|
+
- backup-rails.gemspec
|
119
|
+
- bin/backup_rails
|
120
|
+
- lib/backup-rails.rb
|
121
|
+
- lib/backup-rails/version.rb
|
122
|
+
- lib/generators/backup/rails/install_generator.rb
|
123
|
+
- lib/generators/backup/rails/templates/general.rb
|
124
|
+
- lib/generators/backup/rails/templates/schedule.rb
|
125
|
+
- lib/tasks/backup.rake
|
126
|
+
- spec/bin/prepare_for_testing.rb
|
127
|
+
- spec/lib/generators/backup/rails/install_generator_spec.rb
|
128
|
+
- spec/spec_helper.rb
|
129
|
+
- spec/support/custom_matchers.rb
|
130
|
+
homepage: https://github.com/jesson/backup-rails
|
131
|
+
licenses:
|
132
|
+
- MIT
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - ! '>='
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.2.1
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: Backup rails project
|
154
|
+
test_files:
|
155
|
+
- spec/bin/prepare_for_testing.rb
|
156
|
+
- spec/lib/generators/backup/rails/install_generator_spec.rb
|
157
|
+
- spec/spec_helper.rb
|
158
|
+
- spec/support/custom_matchers.rb
|
159
|
+
has_rdoc:
|