function Etcd(hosts, options) { if (hosts == null) { hosts = "127.0.0.1:2379"; } if (options == null) { options = {}; } this.watcher = bind(this.watcher, this); this.hosts = this._cleanHostList(hosts); this.client = new Client(this.hosts, options, null); }
n/a
function Client(hosts, options1, sslopts) { this.hosts = hosts; this.options = options1; this.sslopts = sslopts; this._shouldRetry = bind(this._shouldRetry, this); this._retry = bind(this._retry, this); this._multiserverHelper = bind(this._multiserverHelper, this); this["delete"] = bind(this["delete"], this); this.patch = bind(this.patch, this); this.post = bind(this.post, this); this.get = bind(this.get, this); this.put = bind(this.put, this); this.execute = bind(this.execute, this); this.syncmsg = {}; }
n/a
function Watcher(etcd, key, index, options) { this.etcd = etcd; this.key = key; this.index = index != null ? index : null; this.options = options != null ? options : {}; this._retry = bind(this._retry, this); this._respHandler = bind(this._respHandler, this); this._resync = bind(this._resync, this); this._unexpectedData = bind(this._unexpectedData, this); this._valueChanged = bind(this._valueChanged, this); this._missingValue = bind(this._missingValue, this); this._error = bind(this._error, this); this._watch = bind(this._watch, this); this.stop = bind(this.stop, this); this.stopped = false; this.retryAttempts = 0; this._watch(); }
...
```javascript
etcd.watch("key");
etcd.watch("key", console.log);
```
The watch command is pretty low level, it does not handle reconnects or
timeouts (Etcd will disconnect you after 5 minutes). Use the `.watcher()` below
if you do not wish to handle this yourself.
### .watchIndex(key, index, [options], callback)
This is a convenience method for get with `{wait: true, waitIndex: index}`.
```javascript
...
function Client(hosts, options1, sslopts) { this.hosts = hosts; this.options = options1; this.sslopts = sslopts; this._shouldRetry = bind(this._shouldRetry, this); this._retry = bind(this._retry, this); this._multiserverHelper = bind(this._multiserverHelper, this); this["delete"] = bind(this["delete"], this); this.patch = bind(this.patch, this); this.post = bind(this.post, this); this.get = bind(this.get, this); this.put = bind(this.put, this); this.execute = bind(this.execute, this); this.syncmsg = {}; }
n/a
_doRequest = function (options, reqRespHandler) { return request(options, reqRespHandler); }
...
headers: headers
};
};
})(this);
if (options.synchronous === true) {
callback = syncRespHandler;
}
req = this._doRequest(options, reqRespHandler);
token.setRequest(req);
if (options.synchronous === true && options.syncdone === void 0) {
options.syncdone = false;
while (!options.syncdone) {
deasync.runLoopOnce();
}
delete options.syncdone;
...
_error = function (token, callback) { var error; error = new Error('All servers returned error'); error.errors = token.errors; error.retries = token.retries; if (callback) { return callback(error); } }
...
if (token.isAborted()) {
return;
}
if (host == null) {
if (this._shouldRetry(token)) {
return this._retry(token, options, callback);
}
return this._error(token, callback);
}
reqRespHandler = (function(_this) {
return function(err, resp, body) {
if (token.isAborted()) {
return;
}
if (_this._isHttpError(err, resp)) {
...
_handleResponse = function (err, resp, body, callback) { var error; if (callback == null) { return; } if ((body != null ? body.errorCode : void 0) != null) { error = new Error((body != null ? body.message : void 0) || 'Etcd error'); error.errorCode = body.errorCode; error.error = body; return callback(error, "", (resp != null ? resp.headers : void 0) || {}); } else { return callback(null, body, (resp != null ? resp.headers : void 0) || {}); } }
...
httpstatus: resp != null ? resp.statusCode : void 0,
httpbody: resp != null ? resp.body : void 0,
response: resp,
timestamp: new Date()
});
return _this._multiserverHelper(_.rest(servers), options, token, callback);
}
return _this._handleResponse(err, resp, body, callback);
};
})(this);
syncRespHandler = (function(_this) {
return function(err, body, headers) {
options.syncdone = true;
return _this.syncmsg = {
err: err,
...
_isHttpError = function (err, resp) { return err || (((resp != null ? resp.statusCode : void 0) != null) && resp.statusCode >= 500); }
...
return this._error(token, callback);
}
reqRespHandler = (function(_this) {
return function(err, resp, body) {
if (token.isAborted()) {
return;
}
if (_this._isHttpError(err, resp)) {
token.errors.push({
server: host,
httperror: err,
httpstatus: resp != null ? resp.statusCode : void 0,
httpbody: resp != null ? resp.body : void 0,
response: resp,
timestamp: new Date()
...
_isPossibleLeaderElection = function (errors) { var checkError; checkError = function(e) { var ref, ref1, ref2, ref3; return ((ref = e != null ? (ref1 = e.httperror) != null ? ref1.code : void 0 : void 0) === 'ECONNREFUSED' || ref === 'ECONNRESET ') || ((ref2 = e != null ? (ref3 = e.httpbody) != null ? ref3.errorCode : void 0 : void 0) === 300 || ref2 === 301) || /Not current leader/.test(e != null ? e.httpbody : void 0); }; return (errors != null) && _.every(errors, checkError); }
...
if (process.env.RUNNING_UNIT_TESTS === 'true') {
return 1;
}
return 100 * Math.pow(16, retries);
};
Client.prototype._shouldRetry = function(token) {
return token.retries < token.maxRetries && this._isPossibleLeaderElection
span>(token.errors);
};
Client.prototype._error = function(token, callback) {
var error;
error = new Error('All servers returned error');
error.errors = token.errors;
error.retries = token.retries;
...
_multiserverHelper = function (servers, options, token, callback) { var host, req, reqRespHandler, syncRespHandler; host = _.first(servers); options.url = "" + host + options.path; if (token.isAborted()) { return; } if (host == null) { if (this._shouldRetry(token)) { return this._retry(token, options, callback); } return this._error(token, callback); } reqRespHandler = (function(_this) { return function(err, resp, body) { if (token.isAborted()) { return; } if (_this._isHttpError(err, resp)) { token.errors.push({ server: host, httperror: err, httpstatus: resp != null ? resp.statusCode : void 0, httpbody: resp != null ? resp.body : void 0, response: resp, timestamp: new Date() }); return _this._multiserverHelper(_.rest(servers), options, token, callback); } return _this._handleResponse(err, resp, body, callback); }; })(this); syncRespHandler = (function(_this) { return function(err, body, headers) { options.syncdone = true; return _this.syncmsg = { err: err, body: body, headers: headers }; }; })(this); if (options.synchronous === true) { callback = syncRespHandler; } req = this._doRequest(options, reqRespHandler); token.setRequest(req); if (options.synchronous === true && options.syncdone === void 0) { options.syncdone = false; while (!options.syncdone) { deasync.runLoopOnce(); } delete options.syncdone; return this.syncmsg; } else { return req; } }
...
var opt, servers, syncResp, token;
opt = _.defaults(_.clone(options), this.options, defaultRequestOptions, {
method: method
});
opt.clientOptions = _.defaults(opt.clientOptions, defaultClientOptions);
servers = _.shuffle(this.hosts);
token = new CancellationToken(servers, opt.clientOptions.maxRetries);
syncResp = this._multiserverHelper(servers, opt, token, callback);
if (options.synchronous === true) {
return syncResp;
} else {
return token;
}
};
...
_retry = function (token, options, callback) { var doRetry, waitTime; doRetry = (function(_this) { return function() { return _this._multiserverHelper(token.servers, options, token, callback); }; })(this); waitTime = this._waitTime(token.retries); token.retries += 1; return setTimeout(doRetry, waitTime); }
...
host = _.first(servers);
options.url = "" + host + options.path;
if (token.isAborted()) {
return;
}
if (host == null) {
if (this._shouldRetry(token)) {
return this._retry(token, options, callback);
}
return this._error(token, callback);
}
reqRespHandler = (function(_this) {
return function(err, resp, body) {
if (token.isAborted()) {
return;
...
_shouldRetry = function (token) { return token.retries < token.maxRetries && this._isPossibleLeaderElection(token.errors); }
...
var host, req, reqRespHandler, syncRespHandler;
host = _.first(servers);
options.url = "" + host + options.path;
if (token.isAborted()) {
return;
}
if (host == null) {
if (this._shouldRetry(token)) {
return this._retry(token, options, callback);
}
return this._error(token, callback);
}
reqRespHandler = (function(_this) {
return function(err, resp, body) {
if (token.isAborted()) {
...
_waitTime = function (retries) { if (process.env.RUNNING_UNIT_TESTS === 'true') { return 1; } return 100 * Math.pow(16, retries); }
...
Client.prototype._retry = function(token, options, callback) {
var doRetry, waitTime;
doRetry = (function(_this) {
return function() {
return _this._multiserverHelper(token.servers, options, token, callback);
};
})(this);
waitTime = this._waitTime(token.retries);
token.retries += 1;
return setTimeout(doRetry, waitTime);
};
Client.prototype._waitTime = function(retries) {
if (process.env.RUNNING_UNIT_TESTS === 'true') {
return 1;
...
delete = function (options, callback) { return this.execute("DELETE", options, callback); }
...
etcd.del("key/", { recursive: true }, console.log);
```
Available options include:
- `recursive` (bool, delete recursively)
Alias: `.delete()`
### .compareAndDelete(key, oldvalue, [options], [callback])
Convenience method for test and delete (delete with {prevValue: oldvalue})
```javascript
etcd.compareAndDelete("key", "oldvalue");
...
execute = function (method, options, callback) { var opt, servers, syncResp, token; opt = _.defaults(_.clone(options), this.options, defaultRequestOptions, { method: method }); opt.clientOptions = _.defaults(opt.clientOptions, defaultClientOptions); servers = _.shuffle(this.hosts); token = new CancellationToken(servers, opt.clientOptions.maxRetries); syncResp = this._multiserverHelper(servers, opt, token, callback); if (options.synchronous === true) { return syncResp; } else { return token; } }
...
return syncResp;
} else {
return token;
}
};
Client.prototype.put = function(options, callback) {
return this.execute("PUT", options, callback);
};
Client.prototype.get = function(options, callback) {
return this.execute("GET", options, callback);
};
Client.prototype.post = function(options, callback) {
...
get = function (options, callback) { return this.execute("GET", options, callback); }
...
## Basic usage
```javascript
var Etcd = require('node-etcd');
var etcd = new Etcd();
etcd.set("key", "value");
etcd.get("key", console.log);
```
Callbacks follows the default (error, result) nodejs convention:
```javascript
function callback(err, res) {
console.log("Error: ", err);
...
patch = function (options, callback) { return this.execute("PATCH", options, callback); }
n/a
post = function (options, callback) { return this.execute("POST", options, callback); }
...
- 2.1.4 - Don't wait before reconnecting if Etcd server times out our watcher.
- 2.1.3 - Etcd sends an empty response on timeout in recent versions. Parsing
the empty message caused watcher to emit error. Now it reconnects instead.
- 2.1.2 - Exponential backoff (retry), fix spinning reconnect on error. (@ptte)
- 2.1.1 - Increase request pool.maxSockets to 100
- 2.1.0 - Use proper error objects instead of strings for errors.
- 2.0.10 - Fix error in documentation
- 2.0.9 - Added .post() alias of .create(). Added .compareAndDelete() (for etcd v0.3.
0)
- 2.0.8 - Watchers can be canceled. In-order keys using #create(). Raw requests using #raw().
- 2.0.7 - Avoid calling callback if callback not given.
- 2.0.6 - Refactoring, fix responsehandler error.
- 2.0.5 - Undo use of 'x-etcd-index', this refers to global state.
- 2.0.4 - Use 'x-etcd-index' for index when watching a key.
- 2.0.3 - Watcher supports options. Watcher emits etcd action type.
- 2.0.2 - Mkdir and rmdir. Fix watcher for v2 api.
...
put = function (options, callback) { return this.execute("PUT", options, callback); }
n/a
function Watcher(etcd, key, index, options) { this.etcd = etcd; this.key = key; this.index = index != null ? index : null; this.options = options != null ? options : {}; this._retry = bind(this._retry, this); this._respHandler = bind(this._respHandler, this); this._resync = bind(this._resync, this); this._unexpectedData = bind(this._unexpectedData, this); this._valueChanged = bind(this._valueChanged, this); this._missingValue = bind(this._missingValue, this); this._error = bind(this._error, this); this._watch = bind(this._watch, this); this.stop = bind(this.stop, this); this.stopped = false; this.retryAttempts = 0; this._watch(); }
...
```javascript
etcd.watch("key");
etcd.watch("key", console.log);
```
The watch command is pretty low level, it does not handle reconnects or
timeouts (Etcd will disconnect you after 5 minutes). Use the `.watcher()` below
if you do not wish to handle this yourself.
### .watchIndex(key, index, [options], callback)
This is a convenience method for get with `{wait: true, waitIndex: index}`.
```javascript
...
function EventEmitter() { EventEmitter.init.call(this); }
n/a
init = function () { this.domain = null; if (EventEmitter.usingDomains) { // if there is an active domain, then attach to it. domain = domain || require('domain'); if (domain.active && !(this instanceof domain.Domain)) { this.domain = domain.active; } } if (!this._events || this._events === Object.getPrototypeOf(this)._events) { this._events = new EventHandlers(); this._eventsCount = 0; } this._maxListeners = this._maxListeners || undefined; }
n/a
listenerCount = function (emitter, type) { if (typeof emitter.listenerCount === 'function') { return emitter.listenerCount(type); } else { return listenerCount.call(emitter, type); } }
n/a
_error = function (err) { var error; error = new Error('Connection error, reconnecting.'); error.error = err; error.reconnectCount = this.retryAttempts; this.emit('reconnect', error); return this._retry(); }
...
if (token.isAborted()) {
return;
}
if (host == null) {
if (this._shouldRetry(token)) {
return this._retry(token, options, callback);
}
return this._error(token, callback);
}
reqRespHandler = (function(_this) {
return function(err, resp, body) {
if (token.isAborted()) {
return;
}
if (_this._isHttpError(err, resp)) {
...
_missingValue = function (headers) { var error; error = new Error('Etcd timed out watcher, reconnecting.'); error.headers = headers; this.retryAttempts = 0; this.emit('reconnect', error); return this._watch(); }
...
return;
}
if ((err != null ? err.errorCode : void 0) === 401 && (((ref = err.error) != null ? ref.index : void 0) != null)) {
return this._resync(err);
} else if (err) {
return this._error(err);
} else if (((headers != null ? headers['x-etcd-index'] : void 0) != null) && (val == null)) {
return this._missingValue(headers);
} else if ((val != null ? (ref1 = val.node) != null ? ref1.modifiedIndex : void 0 : void 0) != null) {
return this._valueChanged(val, headers);
} else {
return this._unexpectedData(val, headers);
}
};
...
_respHandler = function (err, val, headers) { var ref, ref1; if (this.stopped) { return; } if ((err != null ? err.errorCode : void 0) === 401 && (((ref = err.error) != null ? ref.index : void 0) != null)) { return this._resync(err); } else if (err) { return this._error(err); } else if (((headers != null ? headers['x-etcd-index'] : void 0) != null) && (val == null)) { return this._missingValue(headers); } else if ((val != null ? (ref1 = val.node) != null ? ref1.modifiedIndex : void 0 : void 0) != null) { return this._valueChanged(val, headers); } else { return this._unexpectedData(val, headers); } }
n/a
_resync = function (err) { this.index = err.error.index; this.retryAttempts = 0; this.emit('resync', err); return this._watch(); }
...
Watcher.prototype._respHandler = function(err, val, headers) {
var ref, ref1;
if (this.stopped) {
return;
}
if ((err != null ? err.errorCode : void 0) === 401 && (((ref = err.error) != null ? ref.index : void 0) != null)) {
return this._resync(err);
} else if (err) {
return this._error(err);
} else if (((headers != null ? headers['x-etcd-index'] : void 0) != null) && (val == null)) {
return this._missingValue(headers);
} else if ((val != null ? (ref1 = val.node) != null ? ref1.modifiedIndex : void 0 : void 0) != null) {
return this._valueChanged(val, headers);
} else {
...
_retry = function () { var timeout; timeout = (Math.pow(2, this.retryAttempts) * 300) + (Math.round(Math.random() * 1000)); setTimeout(this._watch, timeout); return this.retryAttempts++; }
...
host = _.first(servers);
options.url = "" + host + options.path;
if (token.isAborted()) {
return;
}
if (host == null) {
if (this._shouldRetry(token)) {
return this._retry(token, options, callback);
}
return this._error(token, callback);
}
reqRespHandler = (function(_this) {
return function(err, resp, body) {
if (token.isAborted()) {
return;
...
_unexpectedData = function (val, headers) { var error; error = new Error('Received unexpected response'); error.response = val; this.emit('error', error); return this._retry(); }
...
} else if (err) {
return this._error(err);
} else if (((headers != null ? headers['x-etcd-index'] : void 0) != null) && (val == null)) {
return this._missingValue(headers);
} else if ((val != null ? (ref1 = val.node) != null ? ref1.modifiedIndex : void 0 : void 0) != null) {
return this._valueChanged(val, headers);
} else {
return this._unexpectedData(val, headers);
}
};
Watcher.prototype._retry = function() {
var timeout;
timeout = (Math.pow(2, this.retryAttempts) * 300) + (Math.round(Math.random() * 1000));
setTimeout(this._watch, timeout);
...
_valueChanged = function (val, headers) { this.retryAttempts = 0; this.index = val.node.modifiedIndex + 1; this.emit('change', val, headers); if (val.action != null) { this.emit(val.action, val, headers); } return this._watch(); }
...
if ((err != null ? err.errorCode : void 0) === 401 && (((ref = err.error) != null ? ref.index : void 0) != null)) {
return this._resync(err);
} else if (err) {
return this._error(err);
} else if (((headers != null ? headers['x-etcd-index'] : void 0) != null) && (val == null)) {
return this._missingValue(headers);
} else if ((val != null ? (ref1 = val.node) != null ? ref1.modifiedIndex : void 0 : void 0) != null) {
return this._valueChanged(val, headers);
} else {
return this._unexpectedData(val, headers);
}
};
Watcher.prototype._retry = function() {
var timeout;
...
_watch = function () { if (this.index === null) { return this.request = this.etcd.watch(this.key, this.options, this._respHandler); } else { return this.request = this.etcd.watchIndex(this.key, this.index, this.options, this._respHandler); } }
...
this._valueChanged = bind(this._valueChanged, this);
this._missingValue = bind(this._missingValue, this);
this._error = bind(this._error, this);
this._watch = bind(this._watch, this);
this.stop = bind(this.stop, this);
this.stopped = false;
this.retryAttempts = 0;
this._watch();
}
Watcher.prototype.stop = function() {
this.stopped = true;
this.request.abort();
return this.emit('stop', "Watcher for '" + this.key + "' aborted.");
};
...
function Watcher(etcd, key, index, options) { this.etcd = etcd; this.key = key; this.index = index != null ? index : null; this.options = options != null ? options : {}; this._retry = bind(this._retry, this); this._respHandler = bind(this._respHandler, this); this._resync = bind(this._resync, this); this._unexpectedData = bind(this._unexpectedData, this); this._valueChanged = bind(this._valueChanged, this); this._missingValue = bind(this._missingValue, this); this._error = bind(this._error, this); this._watch = bind(this._watch, this); this.stop = bind(this.stop, this); this.stopped = false; this.retryAttempts = 0; this._watch(); }
n/a
stop = function () { this.stopped = true; this.request.abort(); return this.emit('stop', "Watcher for '" + this.key + "' aborted."); }
...
watcher.on("change", console.log); // Triggers on all changes
watcher.on("set", console.log); // Triggers on specific changes (set ops)
watcher.on("delete", console.log); // Triggers on delete.
watcher2 = etcd.watcher("key", null, {recursive: true});
watcher2.on("error", console.log);
```
You can cancel a watcher by calling `.stop()`.
Signals:
- `change` - emitted on value change
- `reconnect` - emitted on reconnect
- `error` - emitted on invalid content
- `<etcd action>` - the etcd action that triggered the watcher (ex: set, delete).
- `stop` - watcher was canceled.
...