themigrator 0.1.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 +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: []
|