themigrator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE +29 -0
- data/README.md +64 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/themigrator +10 -0
- data/lib/themigrator.rb +9 -0
- data/lib/themigrator/cli.rb +21 -0
- data/lib/themigrator/helper.rb +0 -0
- data/lib/themigrator/logger.rb +21 -0
- data/lib/themigrator/migration.rb +47 -0
- data/lib/themigrator/migrator.rb +71 -0
- data/lib/themigrator/runner.rb +53 -0
- data/lib/themigrator/version.rb +3 -0
- data/test.sh +24 -0
- data/themigrator.gemspec +26 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b1f67f74c4b64f07f670d8878a8854e64b6de64e
|
4
|
+
data.tar.gz: 45b335ce112247d11d2adda886f635b512cb3f80
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a1764195422d095d79055d1e84cd3257672e493fbf1658e8874843ffd4ca7b4f94b0141cc486bbc909a75499bf4e3cc10db62589a715fc27f44c92a6e672ba32
|
7
|
+
data.tar.gz: 50a6430052769b67d0e3bb28a400514f427c7ed282c6b7a0ff3cceb541ba6ed659da1a39e33a3c476062e07a685fedb78c3bb5551108869000a82dc11cede8b6
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Copyright (c) 2016, Wooga GmbH
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
3. Neither the name of the copyright holder nor the names of its contributors
|
15
|
+
may be used to endorse or promote products derived from this software without
|
16
|
+
specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
28
|
+
|
29
|
+
|
data/README.md
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
# Themigrator
|
2
|
+
|
3
|
+
The migrator is a program that eases and automates data migrations.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
|
8
|
+
Install it as a normal gem
|
9
|
+
|
10
|
+
$ gem install themigrator
|
11
|
+
|
12
|
+
## Usage
|
13
|
+
|
14
|
+
|
15
|
+
`themigrator` helps a data migration by automating a pre-defined process.
|
16
|
+
|
17
|
+
It uses the concepts of _roles_(folders), and _actions_(scripts).
|
18
|
+
|
19
|
+
Each _role_ can contain any file, but `themigrator` will treat some of them in
|
20
|
+
a special way.
|
21
|
+
|
22
|
+
This is the list of actions and its intended behaviour:
|
23
|
+
|
24
|
+
* `setup` Provision machines with tooling or opening tunnels.
|
25
|
+
* `pre-migrate` Put the site in maintenance mode.
|
26
|
+
* `migrate` Copy data and check integrity.
|
27
|
+
* `cleanup` In case `migrate` works, it will cleanup, or enable a site.
|
28
|
+
* `rollback` In case any previous command fails, `rollback` is called.
|
29
|
+
|
30
|
+
There are some constrains:
|
31
|
+
|
32
|
+
* All the _actions_ are called at the same time for all the _roles_.
|
33
|
+
* Unless all the _actions_ return a 0, the next _action_ is not executed.
|
34
|
+
* In case an _action_ return a non zero code, a SIGINT is sent to the rest of
|
35
|
+
the actions still alive.
|
36
|
+
* If case an _action_ return a non zero code, the `rollback` script will be
|
37
|
+
called only for the roles that run any previous action.
|
38
|
+
|
39
|
+
|
40
|
+
## Example
|
41
|
+
|
42
|
+
For example. We have three roles: _mariadb_, _redis_, and _app_.
|
43
|
+
|
44
|
+
/mariadb/setup # Check maria is still app and running.
|
45
|
+
/mariadb/pre-migrate # Disable inserts.
|
46
|
+
/mariadb/migrate # Run `ssh oldmaria mysqldump | mysql newmaria`.
|
47
|
+
/mariadb/cleanup # Stop mariadb.
|
48
|
+
/mariadb/rollback # Enable inserts.
|
49
|
+
/redis/migrate # Run `redis-cli oldredis dump | redis-cli newredis bullread`.
|
50
|
+
/app/pre-migrate # Set maintenance mode and stop the app servers.
|
51
|
+
/app/rollback # Enable the old site
|
52
|
+
|
53
|
+
Will do the following:
|
54
|
+
|
55
|
+
1. Run the `mariadb/setup`. If fails `mariadb/rollback`
|
56
|
+
2. Run the `mariadb/pre-migrate` and `app/pre-migrate`. If fails `mariadb/rollback` and `app/rollback`.
|
57
|
+
3. Run the `mariadb/migrate` and `redis/migrate`. If fails `mariadb/rollback` and `app/rollback`.
|
58
|
+
4. Run the `mariadb/cleanup`. Will output the error.
|
59
|
+
|
60
|
+
|
61
|
+
## Contributing
|
62
|
+
|
63
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/wooga/themigrator.
|
64
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "themigrator"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/setup
ADDED
data/exe/themigrator
ADDED
data/lib/themigrator.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'themigrator/migrator'
|
3
|
+
|
4
|
+
|
5
|
+
module Themigrator
|
6
|
+
class CLI < Thor
|
7
|
+
|
8
|
+
desc "migrate", "Migrate the current project"
|
9
|
+
def migrate
|
10
|
+
migrator = Themigrator::Migrator.new(Dir.pwd)
|
11
|
+
migrator.migrate!
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "roles", "show the list of roles"
|
15
|
+
def roles
|
16
|
+
migration = Themigrator::Migration.new(Dir.pwd)
|
17
|
+
migration.analyze_project!
|
18
|
+
puts migration.roles.join("\n")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
File without changes
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Themigrator
|
4
|
+
module Logger
|
5
|
+
include FileUtils
|
6
|
+
|
7
|
+
|
8
|
+
def log_path(base_dir, run_id, role, action)
|
9
|
+
filename = "#{role}-#{action}.log"
|
10
|
+
directory = log_dir(base_dir, run_id)
|
11
|
+
File.join(directory,filename)
|
12
|
+
end
|
13
|
+
|
14
|
+
def log_dir(base_dir, run_id)
|
15
|
+
dir = File.join(base_dir,"logs",run_id)
|
16
|
+
mkdir_p(dir, mode: 0700)
|
17
|
+
dir
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Themigrator
|
4
|
+
|
5
|
+
# Holds the plan of the migration
|
6
|
+
class Migration
|
7
|
+
|
8
|
+
ACTIONS = %w(setup pre-migrate migrate cleanup rollback)
|
9
|
+
SCRIPTS = ACTIONS | %w(rollback)
|
10
|
+
|
11
|
+
attr_reader :roles, :actions
|
12
|
+
|
13
|
+
def initialize(dir)
|
14
|
+
@dir = dir
|
15
|
+
@action_and_roles = Hash.new{|hash,key| hash[key] = [] }
|
16
|
+
@roles = []
|
17
|
+
@actions = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def analyze_project!
|
21
|
+
roles = Set.new
|
22
|
+
used_actions = [] # Should be in order
|
23
|
+
|
24
|
+
SCRIPTS.each do |action|
|
25
|
+
path_match = File.join(@dir,"*/#{action}")
|
26
|
+
Dir[path_match].select { |f|
|
27
|
+
File.executable?(f)
|
28
|
+
}.each {|f|
|
29
|
+
role = File.basename(File.dirname(f))
|
30
|
+
roles.add(role)
|
31
|
+
used_actions << action
|
32
|
+
@action_and_roles[action] << role
|
33
|
+
|
34
|
+
}
|
35
|
+
|
36
|
+
end
|
37
|
+
@roles = roles.to_a.sort
|
38
|
+
@actions = used_actions.uniq
|
39
|
+
@actions.delete("rollback")
|
40
|
+
end
|
41
|
+
|
42
|
+
def roles_for_action(action)
|
43
|
+
@action_and_roles[action]
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'themigrator/runner'
|
2
|
+
require 'themigrator/logger'
|
3
|
+
require 'themigrator/migration'
|
4
|
+
|
5
|
+
|
6
|
+
module Themigrator
|
7
|
+
class Migrator
|
8
|
+
include Runner
|
9
|
+
include Logger
|
10
|
+
|
11
|
+
|
12
|
+
def initialize(dir)
|
13
|
+
@dir = dir
|
14
|
+
@runned_roles = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def migrate!
|
18
|
+
@migration = Migration.new(@dir)
|
19
|
+
@migration.analyze_project!
|
20
|
+
|
21
|
+
Migration::ACTIONS.each do |action|
|
22
|
+
run_action_and_wait(action)
|
23
|
+
end
|
24
|
+
|
25
|
+
rescue ActionFailedException
|
26
|
+
run_rollback_and_wait
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def run_rollback_and_wait
|
32
|
+
@runned_roles.each do |role|
|
33
|
+
run_role_action(role, "rollback")
|
34
|
+
end
|
35
|
+
|
36
|
+
wait_all_processes
|
37
|
+
end
|
38
|
+
|
39
|
+
def run_id
|
40
|
+
@run_id ||= Time.now.strftime("%Y-%m-%d-%H:%M:%S")
|
41
|
+
end
|
42
|
+
|
43
|
+
def run_action_and_wait(action)
|
44
|
+
run_action(action)
|
45
|
+
wait
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_action(action)
|
49
|
+
@migration.roles_for_action(action).each do |role|
|
50
|
+
@runned_roles << role # To prevent calling rollback on roles that
|
51
|
+
# have not being executed
|
52
|
+
|
53
|
+
start_role_action(role, action)
|
54
|
+
|
55
|
+
end
|
56
|
+
@runned_roles.uniq!
|
57
|
+
end
|
58
|
+
|
59
|
+
def start_role_action(role, action)
|
60
|
+
script_path = find_script(role, action)
|
61
|
+
log_path = log_path(@dir,run_id,role, action)
|
62
|
+
|
63
|
+
run(script_path,log_path)
|
64
|
+
end
|
65
|
+
|
66
|
+
def find_script(role, action)
|
67
|
+
File.join(@dir, role, action)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'themigrator/logger'
|
2
|
+
|
3
|
+
|
4
|
+
module Themigrator
|
5
|
+
module Runner
|
6
|
+
include Logger
|
7
|
+
|
8
|
+
class ActionFailedException < Exception; end
|
9
|
+
|
10
|
+
|
11
|
+
class Script
|
12
|
+
def initialize(script, log_file)
|
13
|
+
@script = script
|
14
|
+
@log_file = File.open(log_file, "w", 0600)
|
15
|
+
end
|
16
|
+
|
17
|
+
def close
|
18
|
+
@log_file.close
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def run(script, log_file)
|
24
|
+
wd_dir = File.dirname(script)
|
25
|
+
log_fd = File.open(log_file, "w", 0600)
|
26
|
+
pid = Process.spawn(script, err: log_fd, out: log_fd, chdir: wd_dir)
|
27
|
+
add_process(pid)
|
28
|
+
end
|
29
|
+
|
30
|
+
def wait
|
31
|
+
@processes.each do |pid|
|
32
|
+
pid, status = Process.wait2(pid)
|
33
|
+
return_code = status.exitstatus
|
34
|
+
@processes.delete(pid)
|
35
|
+
|
36
|
+
if return_code != 0
|
37
|
+
raise ActionFailedException.new(pid)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def processes
|
45
|
+
@processes || []
|
46
|
+
end
|
47
|
+
def add_process(pid)
|
48
|
+
@processes ||= []
|
49
|
+
@processes << pid
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/test.sh
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
|
4
|
+
set -eu
|
5
|
+
|
6
|
+
|
7
|
+
cat <<-EOF > remote_file
|
8
|
+
#!/bin/bash
|
9
|
+
|
10
|
+
redis-cli redis03 dump everything NOW > my file
|
11
|
+
process my file
|
12
|
+
|
13
|
+
|
14
|
+
cat thecooloutput
|
15
|
+
|
16
|
+
EOF
|
17
|
+
|
18
|
+
chmod +x remote_file
|
19
|
+
|
20
|
+
scp remote_file redis:
|
21
|
+
|
22
|
+
ssh redis remote_file | redis-cli pipe asdf
|
23
|
+
|
24
|
+
redis-cli PIPE << asdf
|
data/themigrator.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'themigrator/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "themigrator"
|
8
|
+
spec.version = Themigrator::VERSION
|
9
|
+
spec.authors = ["Guillermo Álvarez"]
|
10
|
+
spec.email = ["guillermo@cientifico.net"]
|
11
|
+
|
12
|
+
spec.summary = %q{tool to ease migration a data migration process.}
|
13
|
+
spec.description = %q{The migrator is a program that eases and automates data migrations.}
|
14
|
+
spec.homepage = "https://github.com/wooga/themigrator"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.bindir = "exe"
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
24
|
+
|
25
|
+
spec.add_dependency "thor", "~> 0.19"
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: themigrator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Guillermo Álvarez
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-08-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.12'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: thor
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.19'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.19'
|
69
|
+
description: The migrator is a program that eases and automates data migrations.
|
70
|
+
email:
|
71
|
+
- guillermo@cientifico.net
|
72
|
+
executables:
|
73
|
+
- themigrator
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- ".travis.yml"
|
79
|
+
- Gemfile
|
80
|
+
- LICENSE
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- bin/console
|
84
|
+
- bin/setup
|
85
|
+
- exe/themigrator
|
86
|
+
- lib/themigrator.rb
|
87
|
+
- lib/themigrator/cli.rb
|
88
|
+
- lib/themigrator/helper.rb
|
89
|
+
- lib/themigrator/logger.rb
|
90
|
+
- lib/themigrator/migration.rb
|
91
|
+
- lib/themigrator/migrator.rb
|
92
|
+
- lib/themigrator/runner.rb
|
93
|
+
- lib/themigrator/version.rb
|
94
|
+
- test.sh
|
95
|
+
- themigrator.gemspec
|
96
|
+
homepage: https://github.com/wooga/themigrator
|
97
|
+
licenses: []
|
98
|
+
metadata: {}
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 2.5.1
|
116
|
+
signing_key:
|
117
|
+
specification_version: 4
|
118
|
+
summary: tool to ease migration a data migration process.
|
119
|
+
test_files: []
|