synchronised_migration 2.1.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c1431ba1cc8bb0ad840ba1020723cbefe53e2b88f3e40e80e24a203b7ab8163
4
- data.tar.gz: 0faa2082f8cbceefcaa72b566d9ec16b02be2e19b27016ddf47e7a8f5d882659
3
+ metadata.gz: fdaaef7abd9ca7478b25d2e8ddc330db3aa699eb5a9027d3aac05b9626fc0a1b
4
+ data.tar.gz: ec0d069a1bedf336381d3241b9f869b2c41b8e25f4e6138abda5dc0b34bbdddd
5
5
  SHA512:
6
- metadata.gz: 842a04dca6b33503bf50d9563d688ea10c2ac624523b893a6f6314367dbff97c98cfecaf0dd9c82c32d0084323e1231aa81409a5616a6806b2b025b8fee9ffc2
7
- data.tar.gz: d0be7428022db07f04c00d6017f29c2988035365c5ce76430cbae5177cd672d77aed79ff850e1a5a93987f25042a141cee384df6fe9ebdfabe7f117c0691fa1c
6
+ metadata.gz: 66a3e6dca6186f13049eae942c13cca91afa9652b6c3dfc69e3e693e5dd8433023f57f98fa3f94502cfb04edafeb323df2f90230b7b8e38f9cd48490b324744a
7
+ data.tar.gz: ae260978d7cca055fe2b361514482175c921361a4a00b944ba0bf3fecce24885f104b6bd4d3f19e205814481e7621f502f8398ad3bd85a55931b648288c87247
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Synchronised Migration
2
2
 
3
+ ## 3.0.0
4
+
5
+ - [OPS-201] Migrate library to CLI tool
6
+
7
+ ## 2.2.0
8
+
9
+ - [TT-8617] Update to build with github actions / ruby 3.0
10
+
11
+ ## 2.1.2
12
+
13
+ * [TT-7511] Use the correct `ex` option for key value expiry times instead of `ttl`
14
+
15
+ ## 2.1.1
16
+
17
+ * [TT-5896] Early exit from migration inside lock if already completed
18
+
3
19
  ## 2.1.0
4
20
 
5
21
  * [TT-5827] Add "success key" when REDLOCK_VERSION_SUFFIX is set, preventing repeat runs
data/README.md CHANGED
@@ -1,50 +1,70 @@
1
1
  # Synchronised Migration
2
2
 
3
- [![Build Status](https://travis-ci.org/sealink/synchronised-migration-rb.svg?branch=master)](https://travis-ci.org/sealink/synchronised-migration-rb)
3
+ [![Gem Version](https://badge.fury.io/rb/synchronised_migration.svg)](http://badge.fury.io/rb/synchronised_migration)
4
+ [![Build Status](https://github.com/sealink/synchronised-migration-rb/workflows/Build%20and%20Test/badge.svg?branch=master)](https://github.com/sealink/synchronised-migration-rb/actions)
4
5
 
5
6
  This gem makes it possible to deploy multiple instances with data migration
6
- simultaneously. It uses Redis to ensure that there will be only one migration
7
+ simultaneously. It uses Redis to ensure that there will be only one migration
7
8
  running.
8
9
 
9
- This gem works out of the box with a Rails project. It should work with other
10
- Ruby projects so long as you load the rake task in Rakefile instead of relying
11
- on Railtie.
10
+ ## Usage
12
11
 
13
- This is a Ruby port of the same logic written in PHP in our [Craft
14
- Docker](https://github.com/sealink/craft-docker) project.
12
+ ### Migrating an application
15
13
 
16
- ## Usage
14
+ ```
15
+ bundle exec synchronised-migration migrate --config=migration.yml --version=1.2.3
16
+ ```
17
17
 
18
- Module `SynchronisedMigration` needs to be configured as below.
18
+ ### Checking lock stats
19
19
 
20
20
  ```
21
- SynchronisedMigration.configure do |config|
22
- config.host = 'example.com'
23
- config.port = 6379
24
- config.db = 0
25
- end
21
+ bundle exec synchronised-migration status --config=migration.yml --version=1.2.3
26
22
  ```
27
23
 
28
- Configuration can be called by using
29
- ```SynchronisedMigration.redis_config.host``` or similar.
24
+ ### Clearing locks
25
+
26
+ If a previous migration has failed a new one will not be allowed to start
27
+ unless it is first cleared.
28
+
29
+ ```
30
+ bundle exec synchronised-migration clear --config=migration.yml
31
+ ```
30
32
 
31
- You may override these settings through environment variables.
33
+ If you wish to clear all locks, \*including the running lock then you must also provide version
32
34
 
33
35
  ```
34
- SYNCHRONISED_COMMAND=bin/launch/migrate
35
- WITH_CLEAN_BUNDLER_ENV=1 # Non-empty for true
36
- REDLOCK_TIMEOUT_MS=3600000
37
- REDLOCK_RETRY_DELAY_MS=200
38
- REDLOCK_LOCK_KEY=migration-in-progress
39
- REDLOCK_FAIL_KEY=migration-failed
36
+ bundle exec synchronised-migration clear --config=migration.yml --version=1.2.3 --all
40
37
  ```
41
38
 
42
- Run this before you launch the application during deployment.
39
+ ### Configuration
40
+
41
+ | Key | Description |
42
+ | ------------------------ | --------------------------------------- |
43
+ | redis_uri | Redis server to use for locking |
44
+ | application | Application being migrated |
45
+ | version | Version being migrated too |
46
+ | debug | Enable additional debug information |
47
+ | command | Command to execute for the migration |
48
+ | timeout_milliseconds | Timeout to wait for the lock |
49
+ | retry_delay_milliseconds | Retry for the lock every x milliseconds |
50
+
51
+ ### Overriding config file options
52
+
53
+ You may override certain config options such as the `redis-uri` and the ```debug``` flag
54
+ by providing them via cli options.
43
55
 
44
56
  ```
45
- $ rake synchronised_migrate:execute
57
+ bundle exec synchronised-migration migrate --config=migration.yml --version=1.2.3 --redis_uri=redis://127.0.0.1:6379/0
46
58
  ```
47
59
 
48
- ## Testing
60
+ ## Release
61
+
62
+ To publish a new version of this gem the following steps must be taken.
49
63
 
50
- Please refer to `.travis.yml` for testing.
64
+ - Update the version in the following files
65
+ ```
66
+ CHANGELOG.md
67
+ lib/synchronised_migration/version.rb
68
+ ```
69
+ - Create a tag using the format v0.1.0
70
+ - Follow build progress in GitHub actions
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "synchronised_migration"
6
+
7
+ Dry::CLI.new(SynchronisedMigration::Commands).call
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SynchronisedMigration
4
+ module Commands
5
+ extend Dry::CLI::Registry
6
+
7
+ class Migrate < Dry::CLI::Command
8
+ desc "Run the provided command in a migration lock"
9
+
10
+ option :config, type: :string, required: true, desc: "Configuration file location"
11
+ option :version, type: :string, required: true, desc: "Version we are migrating to"
12
+ option :application, type: :string, desc: "Application we are migrating"
13
+ option :redis_uri, type: :string, desc: "Redis Server URI"
14
+ option :debug, type: :boolean, desc: "Enable additional debugging output"
15
+
16
+ def call(**options)
17
+ abort "Config location must be provided" if options[:config].nil?
18
+ abort "Version must be provided" if options[:version].nil?
19
+
20
+ config = SynchronisedMigration::Configuration.from_cli(options)
21
+ result = SynchronisedMigration::Main.new(config).call
22
+
23
+ abort(result.error_msg) if result.failure?
24
+ $stdout.puts "Complete!"
25
+ end
26
+ end
27
+
28
+ class Clear < Dry::CLI::Command
29
+ desc "Clear the migration keys"
30
+
31
+ option :config, type: :string, required: true, desc: "Configuration file"
32
+ option :version, type: :string, required: true, desc: "Version we are migrating to"
33
+ option :application, type: :string, desc: "Application we are migrating"
34
+ option :redis_uri, type: :string, desc: "Redis Server URI"
35
+ option :debug, type: :boolean, desc: "Enable additional debugging output"
36
+ option :all, type: :boolean, default: false, desc: "Clear all keys including success and lock"
37
+
38
+ def call(**options)
39
+ abort "Config location must be provided" if options[:config].nil?
40
+
41
+ config = SynchronisedMigration::Configuration.from_cli(options)
42
+
43
+ redis_opts = {url: config.redis_uri}
44
+ redis_opts[:logger] = Logger.new($stdout) if config.debug?
45
+
46
+ redis = Redis.new(redis_opts)
47
+ redis.del(config.fail_key)
48
+
49
+ if options[:all]
50
+ abort "Version must be provided" if options[:version].nil?
51
+ redis.del(config.success_key)
52
+ redis.del(config.lock_key)
53
+ end
54
+
55
+ $stdout.puts "Complete!"
56
+ end
57
+ end
58
+
59
+ class Status < Dry::CLI::Command
60
+ desc "Shows the current status the redis failure if any"
61
+
62
+ option :config, type: :string, required: true, desc: "Configuration file"
63
+ option :version, type: :string, required: true, desc: "Version we are migrating to"
64
+ option :application, type: :string, desc: "Application we are migrating"
65
+ option :redis_uri, type: :string, desc: "Redis Server URI"
66
+ option :debug, type: :boolean, desc: "Enable additional debugging output"
67
+
68
+ def call(**options)
69
+ abort "Config location must be provided" if options[:config].nil?
70
+ abort "Version must be provided" if options[:version].nil?
71
+
72
+ config = SynchronisedMigration::Configuration.from_cli(options)
73
+
74
+ redis_opts = {url: config.redis_uri}
75
+ redis_opts[:logger] = Logger.new($stdout) if config.debug?
76
+ redis = Redis.new(redis_opts)
77
+
78
+ rows = []
79
+ fail_key = config.fail_key
80
+ rows << if redis.exists?(fail_key)
81
+ [fail_key, "true", "Failed Migration"]
82
+ else
83
+ [fail_key, "false", nil]
84
+ end
85
+
86
+ success_key = config.success_key
87
+ rows << if redis.exists?(success_key)
88
+ [success_key, "true", "successfully Migrated"]
89
+ else
90
+ [success_key, "false", nil]
91
+ end
92
+
93
+ lock_key = config.lock_key
94
+ rows << if redis.exists?(lock_key)
95
+ [lock_key, "true", "Running Migration"]
96
+ else
97
+ [lock_key, "false", nil]
98
+ end
99
+
100
+ table = TTY::Table.new(["key", "status", "message"], rows)
101
+ $stdout.puts table.render(:ascii)
102
+ end
103
+ end
104
+
105
+ register "migrate", Migrate
106
+ register "clear", Clear
107
+ register "status", Status
108
+ end
109
+ end
@@ -0,0 +1,50 @@
1
+ module SynchronisedMigration
2
+ class Configuration
3
+ attr_accessor :redis_uri
4
+ attr_accessor :command
5
+ attr_accessor :application
6
+ attr_accessor :debug
7
+ attr_accessor :version
8
+
9
+ def self.from_cli(options)
10
+ config_yml = YAML.load_file(options[:config]).transform_keys(&:to_sym).to_h
11
+ new(config_yml.merge(options.compact))
12
+ end
13
+
14
+ def initialize(options = {})
15
+ @application = options.fetch(:application, nil)
16
+ @version = options.fetch(:version, nil)
17
+ @redis_uri = options.fetch(:redis_uri, "redis://127.0.0.1:6379/0")
18
+ @debug = options.fetch(:debug, false)
19
+ @command = options.fetch(:command, "bin/launch/migrate")
20
+ @timeout_milliseconds = options.fetch(:timeout_milliseconds, 3_600_000).to_i
21
+ @retry_delay_milliseconds = options.fetch(:retry_delay_milliseconds, 3000).to_i
22
+
23
+ raise OptionParser::InvalidArgument, "Application must be configured" if @application.nil?
24
+ end
25
+
26
+ def timeout_milliseconds
27
+ @timeout_milliseconds.to_i
28
+ end
29
+
30
+ def retry_delay_milliseconds
31
+ @retry_delay_milliseconds.to_i
32
+ end
33
+
34
+ def debug?
35
+ debug
36
+ end
37
+
38
+ def success_key
39
+ "migration-success-#{application}-#{version}"
40
+ end
41
+
42
+ def fail_key
43
+ "migration-failed-#{application}"
44
+ end
45
+
46
+ def lock_key
47
+ "migration-lock-#{application}"
48
+ end
49
+ end
50
+ end
@@ -1,143 +1,84 @@
1
- require 'synchronised_migration'
2
- require 'synchronised_migration/result'
3
- require 'redis'
4
- require 'redlock'
5
- require 'singleton'
1
+ module SynchronisedMigration
2
+ class Main
3
+ FAIL_TIMEOUT = 3600 # 1 Hour
4
+ SUCCESS_TIMEOUT = 3600 * 24 * 30 # 30 dyas
6
5
 
7
- class SynchronisedMigration::Main
8
- include Singleton
9
-
10
- Result = SynchronisedMigration::Result
11
-
12
- class << self
13
- extend Forwardable
14
- def_delegators :instance, :call
15
- end
16
-
17
- def call
18
- done_or_execute
19
- end
20
-
21
- private
22
-
23
- def done_or_execute
24
- return Result.ok if migration_already_completed?
25
- lock_and_execute
26
- end
27
-
28
- def lock_and_execute
29
- redlock.lock! lock_key, timeout do
30
- execute
6
+ def initialize(config)
7
+ @config = config
31
8
  end
32
- end
33
-
34
- def execute
35
- return Result.fail 'Halting the script because the previous migration failed.' if previous_failed?
36
- mark_failed
37
- migrate
38
- return Result.fail 'Migration failed.' if migration_failed?
39
- mark_successful
40
- remove_fail_marker
41
- return Result.ok
42
- end
43
9
 
44
- def migration_already_completed?
45
- return false if !success_key
46
- value = redis.get(success_key)
47
- not value.nil? and not value.empty?
48
- end
10
+ def call
11
+ return Result.migration_already_completed if migration_already_completed?
49
12
 
50
- def mark_successful
51
- if success_key
52
- redis.set success_key, timestamp, ttl: 3600*24*30
13
+ lock_and_execute
53
14
  end
54
- end
55
-
56
- def previous_failed?
57
- value = redis.get(fail_key)
58
- not value.nil? and not value.empty?
59
- end
60
-
61
- def mark_failed
62
- redis.set fail_key, timestamp, ttl: 3600
63
- end
64
15
 
65
- def remove_fail_marker
66
- redis.del fail_key
67
- end
68
-
69
- def migrate
70
- return Kernel.system target_command unless with_clean_env?
71
- Bundler.with_original_env do
72
- Kernel.system target_command
16
+ private
17
+
18
+ def lock_and_execute
19
+ client = Redlock::Client.new(
20
+ [@config.redis_uri], {
21
+ retry_count: retry_count,
22
+ retry_delay: @config.retry_delay_milliseconds
23
+ }
24
+ )
25
+ client.lock!(@config.lock_key, @config.timeout_milliseconds) do
26
+ execute_within_lock
27
+ end
73
28
  end
74
- end
75
29
 
76
- def with_clean_env?
77
- not ENV.fetch('WITH_CLEAN_BUNDLER_ENV', '').empty?
78
- end
79
-
80
- def migration_failed?
81
- not $?.success?
82
- end
83
-
84
- def target_command
85
- ENV.fetch 'SYNCHRONISED_COMMAND', 'bin/launch/migrate'
86
- end
87
-
88
- def redis
89
- @redis ||= Redis.new(url: redis_url)
90
- end
30
+ def execute_within_lock
31
+ return Result.ok if migration_already_completed?
32
+ return Result.previous_migration_failed if previous_failed?
33
+ mark_failed
34
+ migration_success = migrate
35
+ return Result.migration_failed unless migration_success
36
+ mark_successful
37
+ remove_fail_marker
38
+ Result.ok
39
+ end
91
40
 
92
- def redlock
93
- @redlock ||= Redlock::Client.new(
94
- [ redis_url ], {
95
- retry_count: retry_count,
96
- retry_delay: retry_delay
97
- }
98
- )
99
- end
41
+ def migration_already_completed?
42
+ redis.exists?(@config.success_key)
43
+ end
100
44
 
101
- def redis_url
102
- sprintf(
103
- 'redis://%s:%s/%s',
104
- SynchronisedMigration.redis_config.host,
105
- SynchronisedMigration.redis_config.port,
106
- SynchronisedMigration.redis_config.db
107
- )
108
- end
45
+ def mark_successful
46
+ success_obj = {application: @config.application, version: @config.version, command: @config.command, timestamp: timestamp}.to_json
47
+ redis.set(@config.success_key, success_obj, ex: SUCCESS_TIMEOUT)
48
+ end
109
49
 
110
- def timestamp
111
- Time.now.to_i
112
- end
50
+ def previous_failed?
51
+ redis.exists?(@config.fail_key)
52
+ end
113
53
 
114
- def timeout
115
- ENV.fetch('REDLOCK_TIMEOUT_MS', 3_600_000).to_i
116
- end
54
+ def mark_failed
55
+ redis.set(@config.fail_key, timestamp, ex: FAIL_TIMEOUT)
56
+ end
117
57
 
118
- def retry_delay
119
- ENV.fetch('REDLOCK_RETRY_DELAY_MS', 3000).to_i
120
- end
58
+ def remove_fail_marker
59
+ redis.del(@config.fail_key)
60
+ end
121
61
 
122
- def retry_count
123
- timeout / retry_delay
124
- end
62
+ def migrate
63
+ Kernel.system @config.command
64
+ $?.success?
65
+ end
125
66
 
126
- def lock_key
127
- ENV.fetch 'REDLOCK_LOCK_KEY', 'migration-in-progress'
128
- end
67
+ def redis
68
+ @redis ||= begin
69
+ redis_opts = {url: @config.redis_uri}
70
+ redis_opts[:logger] = Logger.new($stdout) if @config.debug?
129
71
 
130
- def fail_key
131
- ENV.fetch 'REDLOCK_FAIL_KEY', 'migration-failed' + version_suffix
132
- end
72
+ Redis.new(redis_opts)
73
+ end
74
+ end
133
75
 
134
- def success_key
135
- return false if version_suffix.empty?
136
- 'migration-success' + version_suffix
137
- end
76
+ def timestamp
77
+ Time.now.to_i
78
+ end
138
79
 
139
- def version_suffix
140
- suffix = ENV.fetch 'REDLOCK_VERSION_SUFFIX', false
141
- suffix ? '-' + suffix : ''
80
+ def retry_count
81
+ @config.timeout_milliseconds / @config.retry_delay_milliseconds
82
+ end
142
83
  end
143
84
  end
@@ -1,21 +1,52 @@
1
- require 'synchronised_migration'
1
+ require "synchronised_migration"
2
2
 
3
- class SynchronisedMigration::Result
4
- attr_accessor :error
3
+ module SynchronisedMigration
4
+ MIGRATION_SUCCESS = 0
5
+ PREVIOUS_SUCCESS = 1
6
+ PREVIOUS_FAILED = 2
7
+ MIGRATION_FAILED = 3
5
8
 
6
- def initialize(error = nil)
7
- @error = error
8
- end
9
+ PREVIOUS_FAILED_MSG = "Halting the script because the previous migration failed."
10
+ MIGRATION_FAILED_MSG = "Migration command failed."
9
11
 
10
- def success?
11
- error.nil?
12
- end
12
+ class Result
13
+ attr_reader :code
13
14
 
14
- def self.ok
15
- self.new
16
- end
15
+ def initialize(code)
16
+ @code = code
17
+ end
18
+
19
+ def successful?
20
+ [MIGRATION_SUCCESS, PREVIOUS_SUCCESS].include?(code)
21
+ end
22
+
23
+ def failure?
24
+ !successful?
25
+ end
26
+
27
+ def error_msg
28
+ case code
29
+ when MIGRATION_FAILED
30
+ MIGRATION_FAILED_MSG
31
+ when PREVIOUS_FAILED
32
+ PREVIOUS_FAILED_MSG
33
+ end
34
+ end
35
+
36
+ def self.ok
37
+ new(MIGRATION_SUCCESS)
38
+ end
39
+
40
+ def self.migration_already_completed
41
+ new(PREVIOUS_SUCCESS)
42
+ end
43
+
44
+ def self.previous_migration_failed
45
+ new(PREVIOUS_FAILED)
46
+ end
17
47
 
18
- def self.fail(error)
19
- self.new error
48
+ def self.migration_failed
49
+ new(MIGRATION_FAILED)
50
+ end
20
51
  end
21
52
  end
@@ -1,3 +1,3 @@
1
1
  module SynchronisedMigration
2
- VERSION = '2.1.0'
2
+ VERSION = "3.0.0"
3
3
  end
@@ -1,22 +1,14 @@
1
1
  module SynchronisedMigration
2
- class << self
3
- attr_accessor :redis_config
4
- end
5
-
6
- def self.configure
7
- self.redis_config ||= Configuration.new
8
- yield(redis_config)
9
- end
10
-
11
- class Configuration
12
- attr_accessor :host, :port, :db
13
-
14
- def initialize
15
- @host = ''
16
- @port = 0
17
- @db = 0
18
- end
19
- end
20
2
  end
21
3
 
22
- require 'synchronised_migration/railtie' if defined?(Rails)
4
+ require "redis"
5
+ require "logger"
6
+ require "json"
7
+ require "redlock"
8
+ require "yaml"
9
+ require "dry/cli"
10
+ require "tty/table"
11
+ require "synchronised_migration/commands"
12
+ require "synchronised_migration/configuration"
13
+ require "synchronised_migration/main"
14
+ require "synchronised_migration/result"
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: synchronised_migration
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alvin Yim
8
8
  - Stefan Cooper
9
9
  autorequire:
10
- bindir: bin
10
+ bindir: exe
11
11
  cert_chain: []
12
- date: 2019-08-06 00:00:00.000000000 Z
12
+ date: 2021-09-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redlock
@@ -25,6 +25,48 @@ dependencies:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: redis
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: 4.2.1
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: 4.2.1
42
+ - !ruby/object:Gem::Dependency
43
+ name: dry-cli
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: tty-table
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
28
70
  - !ruby/object:Gem::Dependency
29
71
  name: rake
30
72
  requirement: !ruby/object:Gem::Requirement
@@ -81,29 +123,50 @@ dependencies:
81
123
  - - ">="
82
124
  - !ruby/object:Gem::Version
83
125
  version: '3.5'
126
+ - !ruby/object:Gem::Dependency
127
+ name: standardrb
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ - !ruby/object:Gem::Dependency
141
+ name: timecop
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
84
154
  description: Use Redis to record the data migration status
85
155
  email: support@travellink.com.au
86
- executables: []
156
+ executables:
157
+ - synchronised-migration
87
158
  extensions: []
88
159
  extra_rdoc_files: []
89
160
  files:
90
- - ".gitignore"
91
- - ".ruby-version"
92
- - ".travis.yml"
93
161
  - CHANGELOG.md
94
- - Gemfile
95
- - LICENSE
96
162
  - README.md
97
- - Rakefile
163
+ - exe/synchronised-migration
98
164
  - lib/synchronised_migration.rb
165
+ - lib/synchronised_migration/commands.rb
166
+ - lib/synchronised_migration/configuration.rb
99
167
  - lib/synchronised_migration/main.rb
100
- - lib/synchronised_migration/railtie.rb
101
168
  - lib/synchronised_migration/result.rb
102
169
  - lib/synchronised_migration/version.rb
103
- - lib/tasks/synchronised_migration.rake
104
- - spec/spec_helper.rb
105
- - spec/synchronised_migration/main_spec.rb
106
- - synchronised_migration.gemspec
107
170
  homepage: https://github.com/sealink/synchronised-migration-rb
108
171
  licenses: []
109
172
  metadata: {}
@@ -115,17 +178,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
115
178
  requirements:
116
179
  - - ">="
117
180
  - !ruby/object:Gem::Version
118
- version: '0'
181
+ version: 2.6.0
119
182
  required_rubygems_version: !ruby/object:Gem::Requirement
120
183
  requirements:
121
184
  - - ">="
122
185
  - !ruby/object:Gem::Version
123
186
  version: '0'
124
187
  requirements: []
125
- rubygems_version: 3.0.3
188
+ rubygems_version: 3.2.22
126
189
  signing_key:
127
190
  specification_version: 4
128
191
  summary: For deploying to multiple instances simultaneously
129
- test_files:
130
- - spec/spec_helper.rb
131
- - spec/synchronised_migration/main_spec.rb
192
+ test_files: []
data/.gitignore DELETED
@@ -1,5 +0,0 @@
1
- /.bundle
2
- /vendor
3
- /coverage
4
- /*.gem
5
- Gemfile.lock
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.6.3
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.4
4
- - 2.5
5
- - 2.6
6
- script: bundle exec rspec
7
- sudo: false
8
- cache: bundler
9
- before_install:
10
- - gem install bundler
data/Gemfile DELETED
@@ -1,2 +0,0 @@
1
- source 'https://rubygems.org'
2
- gemspec
data/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2017 SeaLink Travel Group Limited
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
data/Rakefile DELETED
@@ -1,2 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require 'rspec/core/rake_task'
@@ -1,5 +0,0 @@
1
- class SynchronisedMigration::Railtie < Rails::Railtie
2
- rake_tasks do
3
- load 'tasks/synchronised_migration.rake'
4
- end
5
- end
@@ -1,9 +0,0 @@
1
- require 'synchronised_migration/main'
2
-
3
- namespace :synchronised_migration do
4
- task :execute do
5
- result = SynchronisedMigration::Main.call
6
- next if result.success?
7
- fail result.error
8
- end
9
- end
data/spec/spec_helper.rb DELETED
@@ -1,9 +0,0 @@
1
- require 'pry'
2
- require 'simplecov'
3
- require 'simplecov-rcov'
4
-
5
- SimpleCov.formatter = SimpleCov::Formatter::RcovFormatter
6
- SimpleCov.minimum_coverage 100
7
- SimpleCov.start do
8
- add_filter %r{^/vendor}
9
- end
@@ -1,129 +0,0 @@
1
- require 'spec_helper'
2
- require 'synchronised_migration/main'
3
-
4
- describe SynchronisedMigration::Main do
5
- subject { described_class }
6
- let(:result) { subject.call }
7
-
8
- context 'when the prerequisites are meet' do
9
- let(:redis) { double }
10
- let(:redlock) { double }
11
- let(:fail_marker_value) { nil }
12
- let(:success_marker_value) { nil }
13
- let(:set_version_suffix) { ENV['REDLOCK_VERSION_SUFFIX'] = 'bork' }
14
- let(:time_value) { double(to_i: 123456789) }
15
-
16
- before do
17
- set_version_suffix
18
-
19
- subject.instance.instance_variable_set :@redis, nil
20
- subject.instance.instance_variable_set :@redlock, nil
21
-
22
- allow(Redis).to receive(:new).and_return(redis)
23
- allow(redis).to receive(:get).and_return(success_marker_value, fail_marker_value)
24
- allow(redis).to receive(:set)
25
- allow(redis).to receive(:del)
26
-
27
- allow(Redlock::Client).to receive(:new).and_return(redlock)
28
- allow(redlock).to receive(:lock!) { |lock_key, timeout, &block| block.call }
29
-
30
- allow(Time).to receive(:now).and_return(time_value)
31
-
32
- allow(Kernel).to receive(:system).and_wrap_original { |method, *args|
33
- next if args == [ 'bin/launch/migrate' ]
34
- method.call *args
35
- }
36
-
37
- allow(Bundler).to receive(:with_original_env).and_call_original
38
-
39
- SynchronisedMigration.configure do |config|
40
- config.host = 'example.com'
41
- config.port = 6379
42
- config.db = 0
43
- end
44
- end
45
-
46
- context 'in the happy path' do
47
- it 'executes the migration successfully' do
48
- expect(result).to be_success
49
- expect(redlock).to have_received(:lock!)
50
- expect(redis).to have_received(:get).with('migration-failed-bork')
51
- expect(redis).to have_received(:set).with('migration-failed-bork', 123456789, ttl: 3600)
52
- expect(redis).to have_received(:set).with('migration-success-bork', 123456789, ttl: 3600*24*30)
53
- expect(Kernel).to have_received(:system)
54
- expect(Bundler).not_to have_received(:with_original_env)
55
- expect(redis).to have_received(:del).with('migration-failed-bork')
56
- end
57
-
58
- context 'and migration completed previously' do
59
- let(:success_marker_value) { '1' }
60
- it 'contines without executing' do
61
- expect(result).to be_success
62
- expect(redlock).not_to have_received(:lock!)
63
- end
64
- end
65
- end
66
-
67
- context 'when require a clean Bundler environment' do
68
- before do
69
- allow(ENV).to receive(:fetch).and_call_original
70
- allow(ENV).to receive(:fetch).with('WITH_CLEAN_BUNDLER_ENV', '').and_return('1')
71
- end
72
-
73
- it 'executes it with a clean Bundler environment' do
74
- expect(result).to be_success
75
- expect(Kernel).to have_received(:system)
76
- expect(Bundler).to have_received(:with_original_env)
77
- end
78
- end
79
-
80
- context 'after a deployment failed previously' do
81
- let(:fail_marker_value) { '1' }
82
-
83
- it "doesn't execute the migration" do
84
- expect(result).not_to be_success
85
- expect(Kernel).not_to have_received(:system)
86
- end
87
- end
88
-
89
- context 'when the task crashed' do
90
- before do
91
- allow_any_instance_of(Process::Status).to receive(:success?).and_return(false)
92
- end
93
-
94
- it 'marks the failure in Redis' do
95
- expect(result).not_to be_success
96
- expect(redis).to have_received(:set).with('migration-failed-bork', 123456789, ttl: 3600)
97
- expect(redis).not_to have_received(:del)
98
- end
99
- end
100
-
101
- context 'without version suffix' do
102
- let(:set_version_suffix) { ENV.delete 'REDLOCK_VERSION_SUFFIX' }
103
-
104
- context 'in the happy path' do
105
- it 'executes the migration successfully' do
106
- expect(result).to be_success
107
- expect(redlock).to have_received(:lock!)
108
- expect(redis).to have_received(:get).with('migration-failed')
109
- expect(redis).to have_received(:set).with('migration-failed', 123456789, ttl: 3600)
110
- expect(Kernel).to have_received(:system)
111
- expect(Bundler).not_to have_received(:with_original_env)
112
- expect(redis).to have_received(:del).with('migration-failed')
113
- end
114
- end
115
-
116
- context 'when the task crashed' do
117
- before do
118
- allow_any_instance_of(Process::Status).to receive(:success?).and_return(false)
119
- end
120
-
121
- it 'marks the failure in Redis' do
122
- expect(result).not_to be_success
123
- expect(redis).to have_received(:set).with('migration-failed', 123456789, ttl: 3600)
124
- expect(redis).not_to have_received(:del)
125
- end
126
- end
127
- end
128
- end
129
- end
@@ -1,24 +0,0 @@
1
- lib = File.expand_path('../lib', __FILE__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'synchronised_migration/version'
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = 'synchronised_migration'
7
- spec.version = SynchronisedMigration::VERSION
8
- spec.authors = ['Alvin Yim', 'Stefan Cooper']
9
- spec.email = 'support@travellink.com.au'
10
- spec.description = 'Use Redis to record the data migration status'
11
- spec.summary = 'For deploying to multiple instances simultaneously'
12
- spec.homepage = 'https://github.com/sealink/synchronised-migration-rb'
13
-
14
- spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
15
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
- spec.require_paths = ['lib']
18
-
19
- spec.add_dependency 'redlock', '>= 0.2'
20
- spec.add_development_dependency 'rake'
21
- spec.add_development_dependency 'simplecov-rcov', '>= 0.2'
22
- spec.add_development_dependency 'rspec', '>= 3.6'
23
- spec.add_development_dependency 'pry-byebug', '>= 3.5'
24
- end