Skip to content

Commit 81fcfc1

Browse files
authored
Merge pull request #2 from redgeoff/cl
feat(command-line)
2 parents b83e16b + ebb8784 commit 81fcfc1

File tree

8 files changed

+246
-12
lines changed

8 files changed

+246
-12
lines changed

README.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,76 @@
55
A fault-tolerant way to replicate an entire CouchDB cluster
66

77

8+
## Installation
9+
10+
$ npm install -g replicate-couchdb-cluster
11+
12+
13+
## Usage
14+
15+
Usage: replicate-couchdb-cluster -s source -t target options
16+
17+
-s source The URL for the CouchDB cluster from which we will be
18+
replicating
19+
20+
-s target The URL for the CouchDB cluster to which we will be
21+
replicating
22+
23+
Options:
24+
25+
-c max-concurrency The maximum number of concurrent replications. If this
26+
value is omitted then the max-concurrency is defaulted
27+
to 20.
28+
29+
-i dbs-to-skip A comma separated list of DBS to skip
30+
31+
-a Use the target's _replicate API when replicating. This is
32+
particularly useful when you are trying to replicate from
33+
a remote source to localhost. By default, the source's
34+
_replicate API is used.
35+
36+
-v Verbose
37+
38+
Examples:
39+
40+
Replicate all DBs on example1.com to example2.com:
41+
42+
$ replicate-couchdb-cluster -s http://example1.com:5984 -t http://example2.com:5984
43+
44+
Replicate all DBs, except the _users and _replicator DBs:
45+
46+
$ replicate-couchdb-cluster -s http://example1.com:5984 -t http://example2.com:5984 \
47+
-i _users,replicator
48+
49+
Replicate all DBs using SSL and authentication:
50+
51+
$ replicate-couchdb-cluster -s https://admin1:secrect1@example1.com:6984 \
52+
-t https://admin2:secrect2@example2.com:6984
53+
54+
Replicate all DBs from a remote source to a local source:
55+
56+
$ replicate-couchdb-cluster -s https://admin1:secrect1@example1.com \
57+
-t http://localhost:5984
58+
-a
59+
60+
## API
61+
62+
You can also use the API.
63+
64+
Example:
65+
66+
```js
67+
var replicate = require('replicate-couchdb-cluster');
68+
replicate({
69+
source: https://admin1:secrect1@example1.com:6984,
70+
target: https://admin2:secrect2@example2.com:6984,
71+
concurrency: 10,
72+
skip: ['_users', '_replicator'],
73+
verbose: true,
74+
useTargetAPI: true
75+
}).then(function () {
76+
// Replication done
77+
});
78+
```
79+
880
## [Testing](TESTING.md)

bin/cmd.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#!/usr/bin/env node
2+
3+
'use strict';
4+
5+
var replicate = require('../scripts'),
6+
argv = require('minimist')(process.argv.slice(2)),
7+
fs = require('fs');
8+
9+
if (!argv.s || !argv.t) {
10+
return fs.createReadStream(__dirname + '/usage.txt')
11+
.pipe(process.stdout)
12+
.on('close', function () {
13+
process.exit(1);
14+
});
15+
} else {
16+
var skip = null;
17+
if (argv.i) {
18+
skip = argv.i.split(',');
19+
}
20+
21+
replicate({
22+
source: argv.s,
23+
target: argv.t,
24+
concurrency: argv.c,
25+
skip: skip,
26+
verbose: argv.v ? true : false,
27+
useTargetAPI: argv.a ? true : false
28+
}).catch(function (err) {
29+
console.error('Fatal Error:', err.message);
30+
process.exit(1);
31+
});
32+
}

bin/usage.txt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
2+
Usage: replicate-couchdb-cluster -s source -t target options
3+
4+
-s source The URL for the CouchDB cluster from which we will be
5+
replicating
6+
7+
-s target The URL for the CouchDB cluster to which we will be
8+
replicating
9+
10+
Options:
11+
12+
-c max-concurrency The maximum number of concurrent replications. If this
13+
value is omitted then the max-concurrency is defaulted
14+
to 20.
15+
16+
-i dbs-to-skip A comma separated list of DBS to skip
17+
18+
-a Use the target's _replicate API when replicating. This is
19+
particularly useful when you are trying to replicate from
20+
a remote source to localhost. By default, the source's
21+
_replicate API is used.
22+
23+
-v Verbose
24+
25+
Examples:
26+
27+
Replicate all DBs on example1.com to example2.com:
28+
29+
$ replicate-couchdb-cluster -s http://example1.com:5984 -t http://example2.com:5984
30+
31+
Replicate all DBs, except the _users and _replicator DBs:
32+
33+
$ replicate-couchdb-cluster -s http://example1.com:5984 -t http://example2.com:5984 \
34+
-i _users,replicator
35+
36+
Replicate all DBs using SSL and authentication:
37+
38+
$ replicate-couchdb-cluster -s https://admin1:secrect1@example1.com:6984 \
39+
-t https://admin2:secrect2@example2.com:6984
40+
41+
Replicate all DBs from a remote source to a local source:
42+
43+
$ replicate-couchdb-cluster -s https://admin1:secrect1@example1.com \
44+
-t http://localhost:5984
45+
-a

package.json

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
"version": "0.0.1",
44
"description": "A fault-tolerant way to replicate an entire CouchDB cluster",
55
"main": "index.js",
6+
"bin": {
7+
"replicate-couchdb-cluster": "bin/cmd.js"
8+
},
69
"repository": {
710
"type": "git",
811
"url": "git://github.com/redgeoff/replicate-couchdb-cluster"
@@ -19,9 +22,9 @@
1922
"url": "https://github.com/redgeoff/replicate-couchdb-cluster/issues"
2023
},
2124
"scripts": {
22-
"assert-beautified": "beautify-proj -i test -c beautify.json && beautify-proj -i scripts -c beautify.json",
23-
"beautify": "beautify-proj -i test -o . -c beautify.json && beautify-proj -i scripts -o . -c beautify.json",
24-
"jshint": "jshint -c .jshintrc test scripts",
25+
"assert-beautified": "beautify-proj -i test -c beautify.json && beautify-proj -i scripts -c beautify.json && beautify-proj -i bin -c beautify.json",
26+
"beautify": "beautify-proj -i test -o . -c beautify.json && beautify-proj -i scripts -o . -c beautify.json && beautify-proj -i bin -o . -c beautify.json",
27+
"jshint": "jshint -c .jshintrc test scripts bin",
2528
"node-test": "istanbul test --dir cache/coverage/node node_modules/mocha/bin/_mocha test/node.js",
2629
"node-full-test": "npm run node-test --coverage && istanbul check-coverage --lines 100 --function 100 --statements 100 --branches 100",
2730
"browser-server": "node_modules/gofur/scripts/browser/serve.js -c cache -t test/browser.js",
@@ -55,8 +58,9 @@
5558
]
5659
},
5760
"dependencies": {
58-
"couch-slouch": "0.0.3",
59-
"sporks": "0.0.1",
60-
"squadron": "0.0.3"
61+
"couch-slouch": "^0.0.4",
62+
"minimist": "^1.2.0",
63+
"sporks": "^0.0.1",
64+
"squadron": "^0.0.3"
6165
}
6266
}

scripts/cluster.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ var Slouch = require('couch-slouch'),
88
// target
99
// concurrency
1010
// skip
11+
// verbose
12+
// useTargetAPI
1113
var Cluster = function (params) {
1214
this._params = params;
1315

@@ -24,6 +26,12 @@ var Cluster = function (params) {
2426
}
2527
};
2628

29+
Cluster.prototype._log = function (msg) {
30+
if (this._params.verbose) {
31+
console.log(new Date() + ': ' + msg);
32+
}
33+
};
34+
2735
Cluster.prototype.replicate = function () {
2836
var self = this;
2937
return self._sourceSlouch.db.all().each(function (db) {
@@ -50,19 +58,23 @@ Cluster.prototype._replicateSecurity = function (sourceDB, targetDB) {
5058
};
5159

5260
Cluster.prototype._replicateRawDB = function (sourceDB, targetDB) {
53-
return this._sourceSlouch.db.replicate({
61+
var slouch = this._params.useTargetAPI ? this._targetSlouch : this._sourceSlouch;
62+
return slouch.db.replicate({
5463
source: this._params.source + '/' + sourceDB,
5564
target: this._params.target + '/' + targetDB
5665
});
5766
};
5867

5968
Cluster.prototype._replicateDB = function (sourceDB, targetDB) {
6069
var self = this;
70+
self._log('beginning replication of ' + sourceDB + '...');
6171
return self._createDBIfMissing(targetDB).then(function () {
6272
// Replicate security first so that security is put in place before data is copied over
6373
return self._replicateSecurity(sourceDB, targetDB);
6474
}).then(function () {
6575
return self._replicateRawDB(sourceDB, targetDB);
76+
}).then(function () {
77+
self._log('finished replicating ' + sourceDB);
6678
});
6779
};
6880

scripts/index.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
11
'use strict';
22

3-
module.exports = require('./cluster');
3+
var Cluster = require('./cluster');
4+
5+
module.exports = function (params) {
6+
var cluster = new Cluster(params);
7+
return cluster.replicate();
8+
};

test/node-and-browser.js

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use strict';
22

33
var Promise = require('sporks/scripts/promise'),
4-
Cluster = require('../scripts'),
4+
Cluster = require('../scripts/cluster'),
55
sporks = require('sporks'),
66
Slouch = require('couch-slouch');
77

@@ -11,7 +11,8 @@ describe('node and browser', function () {
1111
id = 0,
1212
cluster = null,
1313
replicatedDBs = null,
14-
differentCluster = false;
14+
differentCluster = false,
15+
consoleLog = null;
1516

1617
var data = {
1718
db1: {
@@ -169,11 +170,15 @@ describe('node and browser', function () {
169170

170171
differentCluster = false;
171172

173+
consoleLog = console.log;
174+
172175
return createData();
173176
});
174177

175178
afterEach(function () {
176-
return destroyData();
179+
return destroyData().then(function () {
180+
console.log = consoleLog;
181+
});
177182
});
178183

179184
it('should replicate', function () {
@@ -223,4 +228,28 @@ describe('node and browser', function () {
223228
});
224229
});
225230

231+
it('should replicate with targets api', function () {
232+
return replicate({
233+
source: 'http://admin:admin@localhost:5984',
234+
target: 'http://admin:admin@localhost:5984',
235+
useTargetAPI: true
236+
});
237+
});
238+
239+
it('should log when verbose on', function () {
240+
// Mock
241+
var msg = null;
242+
console.log = function (_msg) {
243+
msg = _msg;
244+
};
245+
246+
return replicate({
247+
source: 'http://admin:admin@localhost:5984',
248+
target: 'http://admin:admin@localhost:5984',
249+
verbose: true
250+
}).then(function () {
251+
(msg === null).should.eql(false);
252+
});
253+
});
254+
226255
});

test/node.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,41 @@ var chai = require('chai');
44
chai.use(require('chai-as-promised'));
55
chai.should();
66

7+
var path = require('path'),
8+
spawn = require('child_process').spawn;
9+
710
require('./node-and-browser');
811

9-
// TODO: use spawn to test CL interface
12+
describe('node', function () {
13+
14+
// The test sometimes takes longer than the default of 2 secs
15+
this.timeout(4000);
16+
17+
it('should work via command line', function (done) {
18+
19+
var options = [
20+
'-s', 'http://admin:admin@localhost:5984',
21+
'-t', 'http://admin:admin@localhost:5984'
22+
];
23+
24+
var child = spawn(path.join(__dirname, '/../bin/cmd.js'), options);
25+
26+
child.stderr.on('data', function ( /* data */ ) {
27+
throw new Error('should not get data on stderr');
28+
});
29+
30+
child.on('error', function (err) {
31+
throw err;
32+
});
33+
34+
child.on('close', function (code) {
35+
if (code > 0) {
36+
throw new Error('non-zero code');
37+
} else {
38+
done();
39+
}
40+
});
41+
42+
});
43+
44+
});

0 commit comments

Comments
 (0)