capistrano-mongo-sync 0.1.4 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +13 -8
- data/README.md +6 -0
- data/RELEASE.md +6 -0
- data/capistrano-mongo-sync.gemspec +4 -4
- data/lib/capistrano/mongo_sync/mongo_sync.rb +29 -3
- data/lib/capistrano/tasks/mongo-sync.cap +2 -0
- data/test/test_mongo_sync.rb +15 -4
- metadata +6 -5
- data/lib/capistrano/mongo-sync/mongo-sync.cap +0 -75
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0f8dd6e66c59528341b95db1601e6d5ed1e9003628ab675d0ed8a19f39b00b20
|
4
|
+
data.tar.gz: bced25f1f251ab2aa23897cc0cb1571e61b1897f105e67565bbdd95b68ca1304
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d6d7c9bca76a43f706947f585d407210080f68dff2d4c3dc7122832b7bd51c382cd6cda73768f97f3d2cca30b8481dbc3301805a7eedc3968428bbc23f7970a2
|
7
|
+
data.tar.gz: a45416c9fd270069861d19c339b0ec5cd8815d0822da5aca48a32d60e8c114f3ff1925a95e1b83c37cdf5126d5cda4b4dc359110f66f64bb5256cce32a0e2264
|
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,27 +1,32 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
capistrano-mongo-sync (0.1.
|
4
|
+
capistrano-mongo-sync (0.1.6)
|
5
5
|
capistrano (~> 3.1)
|
6
6
|
sshkit (~> 1.2)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
|
11
|
+
airbrussh (1.3.0)
|
12
|
+
sshkit (>= 1.6.1, != 1.7.0)
|
13
|
+
capistrano (3.10.1)
|
14
|
+
airbrussh (>= 1.0.0)
|
12
15
|
i18n
|
13
16
|
rake (>= 10.0.0)
|
14
|
-
sshkit (
|
15
|
-
|
17
|
+
sshkit (>= 1.9.0)
|
18
|
+
concurrent-ruby (1.0.5)
|
19
|
+
i18n (0.9.1)
|
20
|
+
concurrent-ruby (~> 1.0)
|
16
21
|
metaclass (0.0.4)
|
17
22
|
minitest (5.8.4)
|
18
23
|
mocha (1.1.0)
|
19
24
|
metaclass (~> 0.0.1)
|
20
25
|
net-scp (1.2.1)
|
21
26
|
net-ssh (>= 2.6.5)
|
22
|
-
net-ssh (
|
23
|
-
rake (
|
24
|
-
sshkit (1.
|
27
|
+
net-ssh (4.2.0)
|
28
|
+
rake (12.3.0)
|
29
|
+
sshkit (1.15.1)
|
25
30
|
net-scp (>= 1.1.2)
|
26
31
|
net-ssh (>= 2.8.0)
|
27
32
|
|
@@ -34,4 +39,4 @@ DEPENDENCIES
|
|
34
39
|
mocha (~> 1.1)
|
35
40
|
|
36
41
|
BUNDLED WITH
|
37
|
-
1.
|
42
|
+
1.16.1
|
data/README.md
CHANGED
@@ -13,6 +13,12 @@ Or sync just one collection from the database:
|
|
13
13
|
COLLECTION=users cap production mongo:pull
|
14
14
|
```
|
15
15
|
|
16
|
+
Or sync some records (comma separated list of mongo id's) from one collection from the database:
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
COLLECTION=users IDS=1,2,3,4 cap production mongo:pull
|
20
|
+
```
|
21
|
+
|
16
22
|
Or sync your staging database from your production database.
|
17
23
|
|
18
24
|
```ruby
|
data/RELEASE.md
ADDED
@@ -1,11 +1,11 @@
|
|
1
1
|
Gem::Specification.new do |gem|
|
2
2
|
gem.name = 'capistrano-mongo-sync'
|
3
|
-
gem.version = '0.1.
|
4
|
-
gem.date = '
|
3
|
+
gem.version = '0.1.6'
|
4
|
+
gem.date = '2018-01-18'
|
5
5
|
gem.summary = "A tool for keeping local mongo in sync with remote"
|
6
6
|
gem.description = "A tool for keeping local mongo in sync with remote"
|
7
|
-
gem.authors = ["Open Listings Engineering"]
|
8
|
-
gem.email = 'engineering@
|
7
|
+
gem.authors = ["Open Listings Engineering", "We're hiring!"]
|
8
|
+
gem.email = 'engineering@openlistings.com'
|
9
9
|
gem.homepage = 'https://github.com/openlistings/capistrano-mongo-sync'
|
10
10
|
gem.license = 'MIT'
|
11
11
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
1
3
|
class MongoSync
|
2
4
|
def initialize(connection)
|
3
5
|
@connection = connection
|
@@ -8,6 +10,7 @@ class MongoSync
|
|
8
10
|
@staging_db = fetch(:staging_db)
|
9
11
|
@from_db = fetch(:from_db)
|
10
12
|
@collection = fetch(:collection) || 'full'
|
13
|
+
@collection_ids = fetch(:collection_ids)
|
11
14
|
@hipchat_client = fetch(:hipchat_client)
|
12
15
|
|
13
16
|
fail "Incomplete configuration: missing remote_dump_base" unless @remote_dump_base
|
@@ -15,11 +18,18 @@ class MongoSync
|
|
15
18
|
fail "Incomplete configuration: missing production_db" unless @production_db
|
16
19
|
fail "Incomplete configuration: missing development_db" unless @development_db
|
17
20
|
fail "Incomplete configuration: missing from_db" unless @from_db
|
21
|
+
fail "Incomplete configuration: missing collection" if @collection_ids && @collection.nil?
|
18
22
|
end
|
19
23
|
|
20
24
|
# the first part of the dump dir, without the timestamp... for example "mydatabase-full"
|
21
25
|
def dump_dir_part
|
22
|
-
[@from_db, @collection].join('-')
|
26
|
+
str = [@from_db, @collection].join('-')
|
27
|
+
|
28
|
+
if @collection_ids
|
29
|
+
str = [str, Digest::MD5.hexdigest(@collection_ids)].join('-')
|
30
|
+
end
|
31
|
+
|
32
|
+
str
|
23
33
|
end
|
24
34
|
|
25
35
|
## Remote
|
@@ -40,12 +50,17 @@ class MongoSync
|
|
40
50
|
|
41
51
|
args = ['-d', @from_db, '-o', File.join(@remote_dump_base, dump_dir)]
|
42
52
|
args += ['-c', @collection] unless 'full' == @collection
|
53
|
+
args += ['-q', collection_ids_arg] if @collection_ids
|
43
54
|
|
44
55
|
@connection.execute :mongodump, *args
|
45
56
|
|
46
57
|
dump_dir
|
47
58
|
end
|
48
59
|
|
60
|
+
def collection_ids_arg
|
61
|
+
'{_id: {$in: [%s]}}' % @collection_ids.split(',').map{|id| 'ObjectId("%s")' % id}.join(',')
|
62
|
+
end
|
63
|
+
|
49
64
|
def staging_mongorestore!( remote_dump_dir )
|
50
65
|
full_path_to_remote_dump_dir = if remote_dump_dir == File.basename(remote_dump_dir)
|
51
66
|
File.join(@remote_dump_base, remote_dump_dir, @production_db)
|
@@ -53,7 +68,10 @@ class MongoSync
|
|
53
68
|
remote_dump_dir
|
54
69
|
end
|
55
70
|
|
56
|
-
|
71
|
+
args = []
|
72
|
+
args << '--drop' if drop_collection?
|
73
|
+
args += ['-d', @staging_db, full_path_to_remote_dump_dir]
|
74
|
+
@connection.execute :mongorestore, *args
|
57
75
|
end
|
58
76
|
|
59
77
|
def last_remote_dump
|
@@ -144,10 +162,18 @@ class MongoSync
|
|
144
162
|
def local_mongorestore!(local_dump_dir)
|
145
163
|
db_dump_path = File.join local_dump_dir, @from_db
|
146
164
|
@connection.within( @local_dump_base ) do
|
147
|
-
|
165
|
+
args = []
|
166
|
+
args << '--drop' if drop_collection?
|
167
|
+
args += ['-d', @development_db, db_dump_path]
|
168
|
+
@connection.execute :mongorestore, *args
|
148
169
|
end
|
149
170
|
end
|
150
171
|
|
172
|
+
# don't drop the collection if it's importing partially
|
173
|
+
def drop_collection?
|
174
|
+
@collection_ids.nil?
|
175
|
+
end
|
176
|
+
|
151
177
|
## Hipchat
|
152
178
|
def hipchat_notify!( room, user, msg, opts = {} )
|
153
179
|
return unless @hipchat_client
|
@@ -2,12 +2,14 @@
|
|
2
2
|
# Usage:
|
3
3
|
# ./bin/cap production mongo:pull
|
4
4
|
# COLLECTION=agents ./bin/cap production mongo:pull
|
5
|
+
# COLLECTION=agents IDS=1,2,3 ./bin/cap production mongo:pull
|
5
6
|
# ./bin/cap production mongo:sync_prod_to_staging
|
6
7
|
|
7
8
|
namespace :mongo do
|
8
9
|
set :remote_dump_base, '/tmp/dumps'
|
9
10
|
set :local_dump_base, '/tmp/dumps'
|
10
11
|
set :collection, ENV['COLLECTION'] || 'full'
|
12
|
+
set :collection_ids, ENV['IDS']
|
11
13
|
|
12
14
|
task :sync_prod_to_staging do
|
13
15
|
set :from_db, fetch(:production_db)
|
data/test/test_mongo_sync.rb
CHANGED
@@ -13,6 +13,7 @@ class MongoSync
|
|
13
13
|
@from_db = 'mydb'.freeze
|
14
14
|
@hipchat_client = nil
|
15
15
|
@collection = 'full'
|
16
|
+
@collection_ids = nil
|
16
17
|
|
17
18
|
instance_variable_get("@#{x}")
|
18
19
|
end
|
@@ -62,6 +63,16 @@ class MongoSyncTest < Minitest::Test
|
|
62
63
|
assert_equal 'mydb-agents-2015-01-01-01-01', output_dir
|
63
64
|
end
|
64
65
|
|
66
|
+
def test_remote_mongodump_agents_collection_with_collection_ids
|
67
|
+
@mongo_sync.instance_variable_set("@collection", 'agents')
|
68
|
+
@mongo_sync.instance_variable_set("@collection_ids", '1,2,3')
|
69
|
+
@connection.expects(:execute).once().with(:mongodump, '-d', 'mydb', '-o', '/mnt/tmp/dumps/mydb-agents-55b84a9d317184fe61224bfb4a060fb0-2015-01-01-01-01', '-c', 'agents', '-q', '{_id: {$in: [ObjectId("1"),ObjectId("2"),ObjectId("3")]}}')
|
70
|
+
output_dir = @mongo_sync.remote_mongodump!
|
71
|
+
|
72
|
+
# path includes hexdigest of IDS string
|
73
|
+
assert_equal 'mydb-agents-55b84a9d317184fe61224bfb4a060fb0-2015-01-01-01-01', output_dir
|
74
|
+
end
|
75
|
+
|
65
76
|
def test_dump_prompt_message
|
66
77
|
path_to_tgz = '/tmp/dumps/mydb-full-2015-01-01-01-00.tgz'
|
67
78
|
expected_msg = "Use local dump from today at 01:00 AM? \"%s\"? (y/n)" % path_to_tgz
|
@@ -104,7 +115,7 @@ class MongoSyncTest < Minitest::Test
|
|
104
115
|
def test_last_remote_dump_no_dumps
|
105
116
|
@connection.expects(:test, 'ls -td /mnt/tmp/dumps/*/mydb').returns(false)
|
106
117
|
lrd = @mongo_sync.last_remote_dump
|
107
|
-
|
118
|
+
assert_nil lrd
|
108
119
|
end
|
109
120
|
|
110
121
|
def test_last_remote_dump_dumps_y
|
@@ -126,19 +137,19 @@ class MongoSyncTest < Minitest::Test
|
|
126
137
|
@mongo_sync.instance_variable_set '@use_remote_dump_dir', 'n'
|
127
138
|
|
128
139
|
lrd = @mongo_sync.last_remote_dump
|
129
|
-
|
140
|
+
assert_nil lrd
|
130
141
|
end
|
131
142
|
|
132
143
|
def test_last_local_dump_no_dumps
|
133
144
|
@connection.expects(:test, 'ls -td /mnt/tmp/dumps/mydb-full*/mydb').returns(false)
|
134
145
|
lrd = @mongo_sync.last_remote_dump
|
135
|
-
|
146
|
+
assert_nil lrd
|
136
147
|
end
|
137
148
|
|
138
149
|
def test_last_remote_dump_tgz
|
139
150
|
@connection.expects(:test, 'ls -td /mnt/tmp/dumps/mydb-full*/mydb').returns(false)
|
140
151
|
lrd = @mongo_sync.last_remote_dump
|
141
|
-
|
152
|
+
assert_nil lrd
|
142
153
|
end
|
143
154
|
|
144
155
|
def test_local_mongorestore
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: capistrano-mongo-sync
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Open Listings Engineering
|
8
|
+
- We're hiring!
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date:
|
12
|
+
date: 2018-01-18 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: capistrano
|
@@ -67,7 +68,7 @@ dependencies:
|
|
67
68
|
- !ruby/object:Gem::Version
|
68
69
|
version: '1.1'
|
69
70
|
description: A tool for keeping local mongo in sync with remote
|
70
|
-
email: engineering@
|
71
|
+
email: engineering@openlistings.com
|
71
72
|
executables: []
|
72
73
|
extensions: []
|
73
74
|
extra_rdoc_files: []
|
@@ -78,10 +79,10 @@ files:
|
|
78
79
|
- Gemfile.lock
|
79
80
|
- LICENSE
|
80
81
|
- README.md
|
82
|
+
- RELEASE.md
|
81
83
|
- Rakefile
|
82
84
|
- capistrano-mongo-sync.gemspec
|
83
85
|
- lib/capistrano/mongo-sync.rb
|
84
|
-
- lib/capistrano/mongo-sync/mongo-sync.cap
|
85
86
|
- lib/capistrano/mongo_sync/mongo_sync.rb
|
86
87
|
- lib/capistrano/tasks/mongo-sync.cap
|
87
88
|
- test/test_mongo_sync.rb
|
@@ -105,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
106
|
version: '0'
|
106
107
|
requirements: []
|
107
108
|
rubyforge_project:
|
108
|
-
rubygems_version: 2.
|
109
|
+
rubygems_version: 2.7.3
|
109
110
|
signing_key:
|
110
111
|
specification_version: 4
|
111
112
|
summary: A tool for keeping local mongo in sync with remote
|
@@ -1,75 +0,0 @@
|
|
1
|
-
##
|
2
|
-
# Usage:
|
3
|
-
# ./bin/cap production mongo:pull
|
4
|
-
# COLLECTION=agents ./bin/cap production mongo:pull
|
5
|
-
# ./bin/cap production mongo:sync_prod_to_staging
|
6
|
-
|
7
|
-
namespace :mongo do
|
8
|
-
set :remote_dump_base, '/tmp/dumps'
|
9
|
-
set :local_dump_base, '/tmp/dumps'
|
10
|
-
set :collection, ENV['COLLECTION'] || 'full'
|
11
|
-
|
12
|
-
task :sync_prod_to_staging do
|
13
|
-
set :from_db, fetch(:production_db)
|
14
|
-
|
15
|
-
on roles(:db) do
|
16
|
-
ms_remote = MongoSync.new(self)
|
17
|
-
ms_remote.hipchat_notify! 'Engineering', 'capistrano', 'Started syncing prod to staging...', color: 'yellow'
|
18
|
-
ms_remote.remote_setup!
|
19
|
-
ms_remote.remote_cleanup!
|
20
|
-
|
21
|
-
remote_dump_dir = ms_remote.last_remote_dump
|
22
|
-
remote_dump_dir ||= ms_remote.remote_mongodump!
|
23
|
-
|
24
|
-
ms_remote.staging_mongorestore! remote_dump_dir
|
25
|
-
ms_remote.hipchat_notify! 'Engineering', 'capistrano', 'Finished syncing prod to staging...', color: 'green'
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
task :pull do
|
30
|
-
set :from_db, :production == fetch(:stage) ? fetch(:production_db) : fetch(:staging_db)
|
31
|
-
|
32
|
-
run_locally do
|
33
|
-
ms_local = MongoSync.new(self)
|
34
|
-
ms_local.local_setup!
|
35
|
-
ms_local.local_cleanup!
|
36
|
-
|
37
|
-
# variable scope
|
38
|
-
remote_tgz = nil
|
39
|
-
local_tgz = nil
|
40
|
-
|
41
|
-
if local_tgz = ms_local.last_local_dump
|
42
|
-
# use local tgz
|
43
|
-
ms_local.local_unarchive! local_tgz
|
44
|
-
|
45
|
-
local_dump_dir = File.join fetch(:local_dump_base), File.basename(local_tgz, '.tgz')
|
46
|
-
ms_local.local_mongorestore!(local_dump_dir)
|
47
|
-
else
|
48
|
-
# get dump from remote
|
49
|
-
on roles(:db) do
|
50
|
-
ms_remote = MongoSync.new(self)
|
51
|
-
ms_remote.remote_setup!
|
52
|
-
ms_remote.remote_cleanup!
|
53
|
-
|
54
|
-
# find & choose tarfile or dump & archive
|
55
|
-
remote_tgz = ms_remote.last_remote_dump_tgz
|
56
|
-
unless remote_tgz
|
57
|
-
dump_dir = ms_remote.remote_mongodump!
|
58
|
-
remote_tgz = ms_remote.remote_archive! dump_dir
|
59
|
-
end
|
60
|
-
|
61
|
-
# download!
|
62
|
-
local_tgz = File.join fetch(:local_dump_base), File.basename(remote_tgz)
|
63
|
-
download! remote_tgz, local_tgz, method: :scp
|
64
|
-
end
|
65
|
-
|
66
|
-
# unarchive!
|
67
|
-
ms_local.local_unarchive!(local_tgz)
|
68
|
-
|
69
|
-
# restore!
|
70
|
-
local_dump_dir = File.join fetch(:local_dump_base), File.basename(remote_tgz, '.tgz')
|
71
|
-
ms_local.local_mongorestore!(local_dump_dir)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|