cyclone_lariat 0.4.0 → 1.0.0.rc1
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 +5 -5
- data/.github/workflows/gem-push.yml +4 -4
- data/.rubocop.yml +9 -5
- data/Gemfile.lock +123 -21
- data/Guardfile +42 -0
- data/README.md +417 -220
- data/bin/cyclone_lariat +75 -43
- data/cyclone_lariat.gemspec +10 -3
- data/lib/cyclone_lariat/clients/abstract.rb +40 -0
- data/lib/cyclone_lariat/clients/sns.rb +163 -0
- data/lib/cyclone_lariat/clients/sqs.rb +114 -0
- data/lib/cyclone_lariat/core.rb +21 -0
- data/lib/cyclone_lariat/errors.rb +16 -0
- data/lib/cyclone_lariat/fake.rb +19 -0
- data/lib/cyclone_lariat/generators/command.rb +53 -0
- data/lib/cyclone_lariat/generators/event.rb +52 -0
- data/lib/cyclone_lariat/generators/queue.rb +30 -0
- data/lib/cyclone_lariat/generators/topic.rb +29 -0
- data/lib/cyclone_lariat/messages/v1/abstract.rb +139 -0
- data/lib/cyclone_lariat/messages/v1/command.rb +20 -0
- data/lib/cyclone_lariat/messages/v1/event.rb +20 -0
- data/lib/cyclone_lariat/messages/v1/validator.rb +31 -0
- data/lib/cyclone_lariat/messages/v2/abstract.rb +149 -0
- data/lib/cyclone_lariat/messages/v2/command.rb +20 -0
- data/lib/cyclone_lariat/messages/v2/event.rb +20 -0
- data/lib/cyclone_lariat/messages/v2/validator.rb +39 -0
- data/lib/cyclone_lariat/middleware.rb +9 -6
- data/lib/cyclone_lariat/migration.rb +54 -117
- data/lib/cyclone_lariat/options.rb +52 -0
- data/lib/cyclone_lariat/presenters/graph.rb +54 -0
- data/lib/cyclone_lariat/presenters/queues.rb +41 -0
- data/lib/cyclone_lariat/presenters/subscriptions.rb +34 -0
- data/lib/cyclone_lariat/presenters/topics.rb +40 -0
- data/lib/cyclone_lariat/publisher.rb +25 -0
- data/lib/cyclone_lariat/repo/active_record/messages.rb +92 -0
- data/lib/cyclone_lariat/repo/active_record/versions.rb +28 -0
- data/lib/cyclone_lariat/repo/messages.rb +43 -0
- data/lib/cyclone_lariat/repo/messages_mapper.rb +49 -0
- data/lib/cyclone_lariat/repo/sequel/messages.rb +73 -0
- data/lib/cyclone_lariat/repo/sequel/versions.rb +28 -0
- data/lib/cyclone_lariat/repo/versions.rb +42 -0
- data/lib/cyclone_lariat/resources/queue.rb +167 -0
- data/lib/cyclone_lariat/resources/topic.rb +132 -0
- data/lib/cyclone_lariat/services/migrate.rb +51 -0
- data/lib/cyclone_lariat/services/rollback.rb +51 -0
- data/lib/cyclone_lariat/version.rb +1 -1
- data/lib/cyclone_lariat.rb +4 -11
- data/lib/tasks/console.rake +1 -1
- data/lib/tasks/cyclone_lariat.rake +10 -12
- data/lib/tasks/db.rake +0 -15
- metadata +127 -27
- data/config/db.example.rb +0 -9
- data/config/initializers/sequel.rb +0 -7
- data/db/migrate/01_add_uuid_extensions.rb +0 -15
- data/db/migrate/02_add_events.rb +0 -19
- data/db/migrate/03_add_versions.rb +0 -9
- data/docs/_imgs/graphviz_01.png +0 -0
- data/docs/_imgs/graphviz_02.png +0 -0
- data/docs/_imgs/graphviz_03.png +0 -0
- data/docs/_imgs/lariat.jpg +0 -0
- data/docs/_imgs/logic.png +0 -0
- data/docs/_imgs/sqs_sns_diagram.png +0 -0
- data/lib/cyclone_lariat/abstract/client.rb +0 -112
- data/lib/cyclone_lariat/abstract/message.rb +0 -98
- data/lib/cyclone_lariat/command.rb +0 -13
- data/lib/cyclone_lariat/configure.rb +0 -15
- data/lib/cyclone_lariat/event.rb +0 -13
- data/lib/cyclone_lariat/messages_mapper.rb +0 -46
- data/lib/cyclone_lariat/messages_repo.rb +0 -60
- data/lib/cyclone_lariat/queue.rb +0 -147
- data/lib/cyclone_lariat/sns_client.rb +0 -149
- data/lib/cyclone_lariat/sqs_client.rb +0 -93
- data/lib/cyclone_lariat/topic.rb +0 -113
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cyclone_lariat/messages/v1/event'
|
4
|
+
require 'cyclone_lariat/messages/v1/command'
|
5
|
+
require 'cyclone_lariat/repo/messages_mapper'
|
6
|
+
|
7
|
+
module CycloneLariat
|
8
|
+
module Repo
|
9
|
+
module Sequel
|
10
|
+
class Messages
|
11
|
+
attr_reader :dataset
|
12
|
+
|
13
|
+
def initialize(dataset)
|
14
|
+
@dataset = dataset
|
15
|
+
end
|
16
|
+
|
17
|
+
def enabled?
|
18
|
+
!dataset.nil?
|
19
|
+
end
|
20
|
+
|
21
|
+
def disabled?
|
22
|
+
dataset.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def create(msg)
|
26
|
+
dataset.insert MessagesMapper.to_row(msg)
|
27
|
+
end
|
28
|
+
|
29
|
+
def exists?(uuid:)
|
30
|
+
dataset.where(uuid: uuid).limit(1).any?
|
31
|
+
end
|
32
|
+
|
33
|
+
def processed!(uuid:, error: nil)
|
34
|
+
data = { processed_at: ::Sequel.function(:NOW) }
|
35
|
+
data.merge!(client_error_message: error.message, client_error_details: JSON.generate(error.details)) if error
|
36
|
+
|
37
|
+
!dataset.where(uuid: uuid).update(data).zero?
|
38
|
+
end
|
39
|
+
|
40
|
+
def find(uuid:)
|
41
|
+
row = dataset.where(uuid: uuid).first
|
42
|
+
return if row.nil?
|
43
|
+
|
44
|
+
build MessagesMapper.from_row(row)
|
45
|
+
end
|
46
|
+
|
47
|
+
def each_unprocessed
|
48
|
+
dataset.where(processed_at: nil).each do |row|
|
49
|
+
msg = build MessagesMapper.from_row(row)
|
50
|
+
yield(msg)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def each_with_client_errors
|
55
|
+
dataset.where { (processed_at !~ nil) & (client_error_message !~ nil) }.each do |row|
|
56
|
+
msg = build MessagesMapper.from_row(row)
|
57
|
+
yield(msg)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def build(raw)
|
64
|
+
case kind = raw.delete(:kind)
|
65
|
+
when 'event' then CycloneLariat::Messages::V1::Event.wrap raw
|
66
|
+
when 'command' then CycloneLariat::Messages::V1::Command.wrap raw
|
67
|
+
else raise ArgumentError, "Unknown kind `#{kind}` of message"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CycloneLariat
|
4
|
+
module Repo
|
5
|
+
module Sequel
|
6
|
+
class Versions
|
7
|
+
attr_reader :dataset
|
8
|
+
|
9
|
+
def initialize(dataset)
|
10
|
+
@dataset = dataset
|
11
|
+
end
|
12
|
+
|
13
|
+
def add(version)
|
14
|
+
dataset.insert(version: version)
|
15
|
+
true
|
16
|
+
end
|
17
|
+
|
18
|
+
def remove(version)
|
19
|
+
dataset.filter(version: version).delete.positive?
|
20
|
+
end
|
21
|
+
|
22
|
+
def all
|
23
|
+
dataset.all
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'luna_park/extensions/injector'
|
5
|
+
require 'cyclone_lariat/core'
|
6
|
+
require 'cyclone_lariat/repo/sequel/versions'
|
7
|
+
require 'cyclone_lariat/repo/active_record/versions'
|
8
|
+
|
9
|
+
module CycloneLariat
|
10
|
+
module Repo
|
11
|
+
class Versions
|
12
|
+
include LunaPark::Extensions::Injector
|
13
|
+
|
14
|
+
attr_reader :config
|
15
|
+
|
16
|
+
dependency(:sequel_versions_class) { Repo::Sequel::Versions }
|
17
|
+
dependency(:active_record_versions_class) { Repo::ActiveRecord::Versions }
|
18
|
+
|
19
|
+
extend Forwardable
|
20
|
+
|
21
|
+
def_delegators :driver, :add, :remove, :all
|
22
|
+
|
23
|
+
def initialize(**options)
|
24
|
+
@config = CycloneLariat::Options.wrap(options).merge!(CycloneLariat.config)
|
25
|
+
end
|
26
|
+
|
27
|
+
def driver
|
28
|
+
@driver ||= select(driver: config.driver)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def select(driver:)
|
34
|
+
case driver
|
35
|
+
when :sequel then sequel_versions_class.new(config.versions_dataset)
|
36
|
+
when :active_record then active_record_versions_class.new(config.versions_dataset)
|
37
|
+
else raise ArgumentError, "Undefined driver `#{driver}`"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
module CycloneLariat
|
6
|
+
module Resources
|
7
|
+
class Queue
|
8
|
+
SNS_SUFFIX = :queue
|
9
|
+
|
10
|
+
attr_reader :instance, :kind, :region, :dest, :account_id, :publisher, :type, :fifo, :content_based_deduplication
|
11
|
+
|
12
|
+
def initialize(instance:, kind:, region:, dest:, account_id:, publisher:, type:, fifo:, content_based_deduplication: nil, tags: nil, name: nil)
|
13
|
+
@instance = instance
|
14
|
+
@kind = kind
|
15
|
+
@region = region
|
16
|
+
@dest = dest
|
17
|
+
@account_id = account_id
|
18
|
+
@publisher = publisher
|
19
|
+
@type = type
|
20
|
+
@fifo = fifo
|
21
|
+
@tags = tags
|
22
|
+
@name = name
|
23
|
+
@content_based_deduplication = content_based_deduplication
|
24
|
+
end
|
25
|
+
|
26
|
+
def arn
|
27
|
+
['arn', 'aws', 'sqs', region, account_id, name].join ':'
|
28
|
+
end
|
29
|
+
|
30
|
+
##
|
31
|
+
# Url example:
|
32
|
+
# https://sqs.eu-west-1.amazonaws.com/247606935658/stage-event-queue
|
33
|
+
def url
|
34
|
+
"https://sqs.#{region}.amazonaws.com/#{account_id}/#{name}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def custom?
|
38
|
+
!standard?
|
39
|
+
end
|
40
|
+
|
41
|
+
def standard?
|
42
|
+
instance && kind && publisher && type && true
|
43
|
+
end
|
44
|
+
|
45
|
+
def name
|
46
|
+
@name ||= begin
|
47
|
+
name = [instance, kind, SNS_SUFFIX, publisher, type, dest].compact.join '-'
|
48
|
+
name += '.fifo' if fifo
|
49
|
+
name
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
alias to_s name
|
54
|
+
|
55
|
+
def topic?
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
def queue?
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def protocol
|
64
|
+
'sqs'
|
65
|
+
end
|
66
|
+
|
67
|
+
def attributes
|
68
|
+
attrs = {}
|
69
|
+
attrs['FifoQueue'] = 'true' if fifo
|
70
|
+
attrs['ContentBasedDeduplication'] = 'true' if content_based_deduplication
|
71
|
+
attrs
|
72
|
+
end
|
73
|
+
|
74
|
+
class << self
|
75
|
+
##
|
76
|
+
# Name example: test-event-queue-cyclone_lariat-note_added.fifo
|
77
|
+
# instance: teste
|
78
|
+
# kind: event
|
79
|
+
# publisher: cyclone_lariat
|
80
|
+
# type: note_added
|
81
|
+
# dest: nil
|
82
|
+
# fifo: true
|
83
|
+
def from_name(name, region:, account_id:)
|
84
|
+
is_fifo_array = name.split('.')
|
85
|
+
full_name = is_fifo_array[0]
|
86
|
+
fifo_suffix = is_fifo_array[-1]
|
87
|
+
suffix_exists = fifo_suffix != full_name
|
88
|
+
|
89
|
+
if suffix_exists && fifo_suffix != 'fifo'
|
90
|
+
raise ArgumentError, "Queue name #{name} consists unexpected suffix #{fifo_suffix}"
|
91
|
+
end
|
92
|
+
|
93
|
+
fifo = suffix_exists
|
94
|
+
queue_array = full_name.split('-')
|
95
|
+
|
96
|
+
raise ArgumentError, "Topic name should consists `#{SNS_SUFFIX}`" unless queue_array[2] != SNS_SUFFIX
|
97
|
+
|
98
|
+
queue_array.clear if queue_array.size < 5
|
99
|
+
|
100
|
+
new(
|
101
|
+
instance: queue_array[0], kind: queue_array[1], region: region, dest: queue_array[5],
|
102
|
+
account_id: account_id, publisher: queue_array[3], type: queue_array[4], fifo: fifo, name: name
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
##
|
107
|
+
# URL example: https://sqs.eu-west-1.amazonaws.com/247606935658/test-event-queue-cyclone_lariat-note_added.fifo
|
108
|
+
# url_array[0] => https
|
109
|
+
# host_array[0] => sqs
|
110
|
+
# host_array[1] => eu-west-1
|
111
|
+
# url_array[3] => 247606935658 # account_id
|
112
|
+
# url_array[4] => test-event-queue-cyclone_lariat-note_added.fifo # name
|
113
|
+
def from_url(url)
|
114
|
+
raise ArgumentError, 'Url is not http format' unless url =~ URI::DEFAULT_PARSER.make_regexp
|
115
|
+
|
116
|
+
url_array = url.split('/')
|
117
|
+
raise ArgumentError, 'Url should start from https' unless url_array[0] == 'https:'
|
118
|
+
|
119
|
+
host_array = url_array[2].split('.')
|
120
|
+
raise ArgumentError, 'It is not queue url' unless host_array[0] == 'sqs'
|
121
|
+
|
122
|
+
from_name(url_array[4], region: host_array[1], account_id: url_array[3])
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Arn example: "arn:aws:sqs:eu-west-1:247606935658:custom_queue"
|
127
|
+
# arn_array[0] => 'arn'
|
128
|
+
# arn_array[1] => 'aws'
|
129
|
+
# arn_array[2] => 'sqs'
|
130
|
+
# arn_array[3] => 'eu-west-1' # region
|
131
|
+
# arn_array[4] => '247606935658' # account_id
|
132
|
+
# arn_array[5] => 'alexey_test2' # name
|
133
|
+
def from_arn(arn)
|
134
|
+
arn_array = arn.split(':')
|
135
|
+
|
136
|
+
raise ArgumentError, "Arn `#{arn}` should consists `arn`" unless arn_array[0] == 'arn'
|
137
|
+
raise ArgumentError, "Arn `#{arn}` should consists `aws`" unless arn_array[1] == 'aws'
|
138
|
+
raise ArgumentError, "Arn `#{arn}` should consists `sqs`" unless arn_array[2] == 'sqs'
|
139
|
+
|
140
|
+
from_name(arn_array[5], region: arn_array[3], account_id: arn_array[4])
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def tags
|
145
|
+
@tags ||= begin
|
146
|
+
if standard?
|
147
|
+
{
|
148
|
+
'standard' => 'true',
|
149
|
+
'instance' => String(instance),
|
150
|
+
'kind' => String(kind),
|
151
|
+
'publisher' => String(publisher),
|
152
|
+
'type' => String(type),
|
153
|
+
'dest' => dest ? String(dest) : 'undefined',
|
154
|
+
'fifo' => fifo ? 'true' : 'false'
|
155
|
+
}
|
156
|
+
else
|
157
|
+
{
|
158
|
+
'standard' => 'false',
|
159
|
+
'name' => String(name),
|
160
|
+
'fifo' => fifo ? 'true' : 'false'
|
161
|
+
}
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CycloneLariat
|
4
|
+
module Resources
|
5
|
+
class Topic
|
6
|
+
SNS_SUFFIX = :fanout
|
7
|
+
|
8
|
+
attr_reader :instance, :kind, :region, :account_id, :publisher, :type, :fifo, :content_based_deduplication
|
9
|
+
|
10
|
+
def initialize(instance:, kind:, region:, account_id:, publisher:, type:, fifo:, content_based_deduplication: nil, tags: nil, name: nil)
|
11
|
+
@instance = instance
|
12
|
+
@kind = kind
|
13
|
+
@region = region
|
14
|
+
@account_id = account_id
|
15
|
+
@publisher = publisher
|
16
|
+
@type = type
|
17
|
+
@fifo = fifo
|
18
|
+
@tags = tags
|
19
|
+
@name = name
|
20
|
+
@content_based_deduplication = content_based_deduplication
|
21
|
+
end
|
22
|
+
|
23
|
+
def arn
|
24
|
+
['arn', 'aws', 'sns', region, account_id, to_s].join ':'
|
25
|
+
end
|
26
|
+
|
27
|
+
def custom?
|
28
|
+
!standard?
|
29
|
+
end
|
30
|
+
|
31
|
+
def standard?
|
32
|
+
return false unless instance && kind && publisher && type
|
33
|
+
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def name
|
38
|
+
@name ||= begin
|
39
|
+
name = [instance, kind, SNS_SUFFIX, publisher, type].compact.join '-'
|
40
|
+
name += '.fifo' if fifo
|
41
|
+
name
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def attributes
|
46
|
+
attrs = {}
|
47
|
+
attrs['FifoTopic'] = 'true' if fifo
|
48
|
+
attrs['ContentBasedDeduplication'] = 'true' if content_based_deduplication
|
49
|
+
attrs
|
50
|
+
end
|
51
|
+
|
52
|
+
def topic?
|
53
|
+
true
|
54
|
+
end
|
55
|
+
|
56
|
+
def queue?
|
57
|
+
false
|
58
|
+
end
|
59
|
+
|
60
|
+
def protocol
|
61
|
+
'sns'
|
62
|
+
end
|
63
|
+
|
64
|
+
alias to_s name
|
65
|
+
|
66
|
+
def ==(other)
|
67
|
+
arn == other.arn
|
68
|
+
end
|
69
|
+
|
70
|
+
class << self
|
71
|
+
def from_name(name, region:, account_id:)
|
72
|
+
is_fifo_array = name.split('.')
|
73
|
+
full_name = is_fifo_array[0]
|
74
|
+
fifo_suffix = is_fifo_array[-1]
|
75
|
+
suffix_exists = fifo_suffix != full_name
|
76
|
+
|
77
|
+
if suffix_exists && fifo_suffix != 'fifo'
|
78
|
+
raise ArgumentError, "Topic name #{name} consists unexpected suffix #{fifo_suffix}"
|
79
|
+
end
|
80
|
+
|
81
|
+
fifo = suffix_exists
|
82
|
+
topic_array = full_name.split('-')
|
83
|
+
|
84
|
+
raise ArgumentError, "Topic name should consists `#{SNS_SUFFIX}`" unless topic_array[2] != SNS_SUFFIX
|
85
|
+
|
86
|
+
topic_array.clear if topic_array.size < 5
|
87
|
+
|
88
|
+
new(
|
89
|
+
instance: topic_array[0],
|
90
|
+
kind: topic_array[1],
|
91
|
+
publisher: topic_array[3],
|
92
|
+
type: topic_array[4],
|
93
|
+
region: region,
|
94
|
+
account_id: account_id,
|
95
|
+
fifo: fifo,
|
96
|
+
name: name
|
97
|
+
)
|
98
|
+
end
|
99
|
+
|
100
|
+
def from_arn(arn)
|
101
|
+
arn_array = arn.split(':')
|
102
|
+
raise ArgumentError, 'Arn should consists `arn`' unless arn_array[0] == 'arn'
|
103
|
+
raise ArgumentError, 'Arn should consists `aws`' unless arn_array[1] == 'aws'
|
104
|
+
raise ArgumentError, 'Arn should consists `sns`' unless arn_array[2] == 'sns'
|
105
|
+
|
106
|
+
from_name(arn_array[5], region: arn_array[3], account_id: arn_array[4])
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def tags
|
111
|
+
@tags ||= begin
|
112
|
+
if standard?
|
113
|
+
[
|
114
|
+
{ key: 'standard', value: 'true' },
|
115
|
+
{ key: 'instance', value: String(instance) },
|
116
|
+
{ key: 'kind', value: String(kind) },
|
117
|
+
{ key: 'publisher', value: String(publisher) },
|
118
|
+
{ key: 'type', value: String(type) },
|
119
|
+
{ key: 'fifo', value: fifo ? 'true' : 'false' }
|
120
|
+
]
|
121
|
+
else
|
122
|
+
[
|
123
|
+
{ key: 'standard', value: 'false' },
|
124
|
+
{ key: 'name', value: String(name) },
|
125
|
+
{ key: 'fifo', value: fifo ? 'true' : 'false' }
|
126
|
+
]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CycloneLariat
|
4
|
+
module Services
|
5
|
+
class Migrate
|
6
|
+
attr_reader :repo, :dir
|
7
|
+
|
8
|
+
def initialize(repo:, dir:)
|
9
|
+
@repo = repo
|
10
|
+
@dir = dir
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
alert('No one migration exists') if !Dir.exist?(dir) || Dir.empty?(dir)
|
15
|
+
output = []
|
16
|
+
|
17
|
+
migration_paths.each do |path|
|
18
|
+
filename = File.basename(path, '.rb')
|
19
|
+
version, title = filename.split('_', 2)
|
20
|
+
|
21
|
+
next if existed_migrations.include? version.to_i
|
22
|
+
|
23
|
+
class_name = title.split('_').collect(&:capitalize).join
|
24
|
+
output << "Up - #{version} #{class_name} #{path}"
|
25
|
+
require_relative Pathname.new(Dir.pwd) + Pathname.new(path)
|
26
|
+
Object.const_get(class_name).new.up
|
27
|
+
repo.add(version)
|
28
|
+
end
|
29
|
+
|
30
|
+
output
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Sorted by timestamp
|
36
|
+
def migration_paths
|
37
|
+
# lariat/migrate/1668161620_many_to_one.rb
|
38
|
+
Dir.glob("#{dir}/*.rb").sort_by do |file_path|
|
39
|
+
# 1668161620_many_to_one.rb
|
40
|
+
file_name = file_path.split('/')[-1]
|
41
|
+
# 1668161620
|
42
|
+
file_name.split('_').first.to_i
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def existed_migrations
|
47
|
+
@existed_migrations ||= repo.all.map { |row| row[:version] }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CycloneLariat
|
4
|
+
module Services
|
5
|
+
class Rollback
|
6
|
+
attr_reader :repo, :dir
|
7
|
+
|
8
|
+
def initialize(repo:, dir:)
|
9
|
+
@repo = repo
|
10
|
+
@dir = dir
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(version = nil)
|
14
|
+
version ||= existed_migrations[-1]
|
15
|
+
output = []
|
16
|
+
|
17
|
+
paths_of_downgrades(version).each do |path|
|
18
|
+
filename = File.basename(path, '.rb')
|
19
|
+
version, title = filename.split('_', 2)
|
20
|
+
class_name = title.split('_').collect(&:capitalize).join
|
21
|
+
output << "Down - #{version} #{class_name} #{path}"
|
22
|
+
require_relative Pathname.new(Dir.pwd) + Pathname.new(path)
|
23
|
+
Object.const_get(class_name).new.down
|
24
|
+
repo.remove(version)
|
25
|
+
end
|
26
|
+
|
27
|
+
output
|
28
|
+
end
|
29
|
+
|
30
|
+
def existed_migrations
|
31
|
+
@existed_migrations ||= repo.all.map { |row| row[:version] }.sort
|
32
|
+
end
|
33
|
+
|
34
|
+
def paths_of_downgrades(version)
|
35
|
+
migrations_to_downgrade = existed_migrations.select { |migration| migration >= version }
|
36
|
+
|
37
|
+
paths = []
|
38
|
+
migrations_to_downgrade.each do |migration|
|
39
|
+
path = Pathname.new(Dir.pwd) + Pathname.new(dir)
|
40
|
+
founded = Dir.glob("#{path}/#{migration}_*.rb")
|
41
|
+
raise "Could not found migration: `#{migration}` in #{path}" if founded.empty?
|
42
|
+
raise "Found lot of migration: `#{migration}` in #{path}" if founded.size > 1
|
43
|
+
|
44
|
+
paths += founded
|
45
|
+
end
|
46
|
+
|
47
|
+
paths
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/cyclone_lariat.rb
CHANGED
@@ -1,13 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
require_relative 'cyclone_lariat/messages_mapper'
|
8
|
-
require_relative 'cyclone_lariat/messages_repo'
|
9
|
-
require_relative 'cyclone_lariat/migration'
|
10
|
-
require_relative 'cyclone_lariat/middleware'
|
11
|
-
require_relative 'cyclone_lariat/version'
|
12
|
-
|
13
|
-
module CycloneLariat; end
|
3
|
+
require 'cyclone_lariat/core'
|
4
|
+
require 'cyclone_lariat/middleware'
|
5
|
+
require 'cyclone_lariat/migration'
|
6
|
+
require 'cyclone_lariat/publisher'
|
data/lib/tasks/console.rake
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
desc 'IRB console with required CycloneLariat'
|
4
4
|
task :console do
|
5
5
|
# require 'cyclone_lariat'
|
6
|
-
|
6
|
+
require 'cyclone_lariat'
|
7
7
|
require 'irb'
|
8
8
|
require_relative '../../config/initializers/cyclone_lariat'
|
9
9
|
# require_relative(init_file) if File.exists?(init_file)
|
@@ -3,42 +3,40 @@
|
|
3
3
|
require 'cyclone_lariat'
|
4
4
|
|
5
5
|
namespace :cyclone_lariat do
|
6
|
-
desc 'Migrate topics for
|
6
|
+
desc 'Migrate topics for SQS/SNS'
|
7
7
|
task migrate: :config do
|
8
|
-
require_relative '../../config/initializers/cyclone_lariat'
|
9
8
|
CycloneLariat::Migration.migrate
|
10
9
|
end
|
11
10
|
|
12
|
-
desc 'Rollback topics for
|
11
|
+
desc 'Rollback topics for SQS/SNS'
|
13
12
|
task :rollback, [:version] => :config do |_, args|
|
14
|
-
require_relative '../../config/initializers/cyclone_lariat'
|
15
13
|
target_version = args[:version] ? args[:version].to_i : nil
|
16
14
|
CycloneLariat::Migration.rollback(target_version)
|
17
15
|
end
|
18
16
|
|
19
17
|
namespace :list do
|
20
18
|
desc 'List all topics'
|
21
|
-
task :
|
22
|
-
require_relative '../../config/initializers/cyclone_lariat'
|
19
|
+
task topics: :config do
|
23
20
|
CycloneLariat::Migration.list_topics
|
24
21
|
end
|
25
22
|
|
26
23
|
desc 'List all queues'
|
27
|
-
task :
|
28
|
-
require_relative '../../config/initializers/cyclone_lariat'
|
24
|
+
task queues: :config do
|
29
25
|
CycloneLariat::Migration.list_queues
|
30
26
|
end
|
31
27
|
|
32
28
|
desc 'List all subscriptions'
|
33
|
-
task :
|
34
|
-
require_relative '../../config/initializers/cyclone_lariat'
|
29
|
+
task subscriptions: :config do
|
35
30
|
CycloneLariat::Migration.list_subscriptions
|
36
31
|
end
|
37
32
|
end
|
38
33
|
|
39
34
|
desc 'Build graphviz graph for whole system'
|
40
|
-
task :
|
41
|
-
require_relative '../../config/initializers/cyclone_lariat'
|
35
|
+
task graph: :config do
|
42
36
|
CycloneLariat::Migration.build_graph
|
43
37
|
end
|
38
|
+
|
39
|
+
task :config do
|
40
|
+
require_relative '../../config/initializers/cyclone_lariat'
|
41
|
+
end
|
44
42
|
end
|
data/lib/tasks/db.rake
CHANGED
@@ -22,20 +22,6 @@ namespace :db do
|
|
22
22
|
puts "Database `#{DB_CONF[:database]}` successfully dropped" if system(cmd)
|
23
23
|
end
|
24
24
|
|
25
|
-
desc 'Apply migrate'
|
26
|
-
task :migrate, [:version] => :config do |_, args|
|
27
|
-
require 'logger'
|
28
|
-
require 'sequel/core'
|
29
|
-
|
30
|
-
Sequel.extension :migration
|
31
|
-
version = args[:version] ? args[:version].to_i : nil
|
32
|
-
migrations_path = "#{__dir__}/../../db/migrate/"
|
33
|
-
|
34
|
-
Sequel.connect(**DB_CONF, logger: Logger.new($stdout)) do |db|
|
35
|
-
Sequel::Migrator.run(db, migrations_path, target: version)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
25
|
desc 'Database console'
|
40
26
|
task console: :config do
|
41
27
|
cmd = "PGPASSWORD=#{DB_CONF[:password]} psql" \
|
@@ -50,6 +36,5 @@ namespace :db do
|
|
50
36
|
task :reset do
|
51
37
|
Rake::Task['db:drop'].invoke
|
52
38
|
Rake::Task['db:create'].invoke
|
53
|
-
Rake::Task['db:migrate'].invoke
|
54
39
|
end
|
55
40
|
end
|