themigrator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ test/fixtures/sample_project/logs/
11
+ .byebug_history
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ before_install: gem install bundler -v 1.12.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in themigrator.gemspec
4
+ gemspec
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
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/themigrator ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "themigrator/cli"
4
+
5
+ Themigrator::CLI.start(ARGV)
6
+
7
+
8
+
9
+
10
+
@@ -0,0 +1,9 @@
1
+ require "themigrator/version"
2
+ require "themigrator/logger"
3
+ require "themigrator/runner"
4
+ require "themigrator/migration"
5
+
6
+ module Themigrator
7
+
8
+
9
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Themigrator
2
+ VERSION = "0.1.0"
3
+ 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
@@ -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: []