Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: stianeikeland/node-etcd
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 4177b4253d0b2f67404eea0d428312455063dab2
Choose a base ref
...
head repository: stianeikeland/node-etcd
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 480cc604bc240ef0692695d095497776fc3867f3
Choose a head ref
  • 11 commits
  • 6 files changed
  • 1 contributor

Commits on Sep 8, 2015

  1. Merge pull request #44 from stianeikeland/fix-deasync

    Fix for node 4.0.0 and iojs 3.3.0, closes #43
    stianeikeland committed Sep 8, 2015
    Copy the full SHA
    ff1e858 View commit details
  2. Release 4.2.1

    stianeikeland committed Sep 8, 2015
    Copy the full SHA
    1aab8ce View commit details

Commits on Nov 1, 2015

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    8c9a64f View commit details
  2. Copy the full SHA
    7631e9e View commit details
  3. Support for basic auth

    stianeikeland committed Nov 1, 2015
    Copy the full SHA
    9d16d66 View commit details

Commits on Dec 27, 2015

  1. Copy the full SHA
    d78ed09 View commit details
  2. Copy the full SHA
    92744e8 View commit details
  3. Fix broken "abort" test

    stianeikeland committed Dec 27, 2015
    Copy the full SHA
    da8be4d View commit details
  4. Copy the full SHA
    e9ca4af View commit details
  5. Copy the full SHA
    886505d View commit details
  6. Copy the full SHA
    480cc60 View commit details
Showing with 231 additions and 73 deletions.
  1. +2 −2 .travis.yml
  2. +94 −12 README.md
  3. +14 −9 package.json
  4. +9 −5 src/client.coffee
  5. +16 −16 src/index.coffee
  6. +96 −29 test/index.coffee
4 changes: 2 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
language: node_js
sudo: false
node_js:
- "iojs-v3.2.0"
- "iojs"
- "4.0.0"
- "4.1"
- "4.0"
- "0.12"
- "0.11"
- "0.10"
106 changes: 94 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -22,6 +22,12 @@ $ npm install node-etcd@3.0.2

## Changes

- 5.0.0
- Supports basic auth, timeout, etc. See options.
- **Breaking**: Constructor changes (see below)
- **Breaking**: Must provide https url to use https
- 4.2.1
- Newer deasync fixes issues with iojs 3.3.0 and nodejs 4.0.0.
- 4.1.0
- Bumps [request](https://github.com/request/request) library version to
v2.60.0, this solves an issue with HTTP proxies. `HTTP_PROXY` and `NO_PROXY`
@@ -74,24 +80,39 @@ $ npm install node-etcd@3.0.2
## Basic usage

```javascript
Etcd = require('node-etcd');
etcd = new Etcd();
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);
console.log("Return: ", res);
}
etcd.get("key", callback);
// Error: null
// Return: { action: 'get', node: { key: '/key', value: 'value', modifiedIndex: 4, createdIndex: 4 } }
```

## Methods

### Etcd([host = '127.0.0.1'], [port = '4001'], [ssloptions])
### Etcd(hosts = ['127.0.0.1:4001'], [options])

Create a new etcd client for a single host etcd setup

```javascript
etcd = new Etcd();
etcd = new Etcd('127.0.0.1', '4001');
etcd = new Etcd("127.0.0.1:4001");
etcd = new Etcd("http://127.0.0.1:4001");
etcd = new Etcd("https://127.0.0.1:4001");
etcd = new Etcd(["http://127.0.0.1:4001"]);
```

### Etcd(hosts, [ssloptions])
### Etcd(hosts, [options])

Create a new etcd client for a clustered etcd setup. Client will connect to
servers in random order. On failure it will try the next server. When all
@@ -101,6 +122,7 @@ retries can be controlled by adding `{ maxRetries: x }` as an option to requests

```javascript
etcd = new Etcd(['127.0.0.1:4001','192.168.1.1:4001']);
etcd = new Etcd(['http://127.0.0.1:4001','http://192.168.1.1:4001']);
```

### .set(key, value = null, [options], [callback])
@@ -394,18 +416,78 @@ you won't get any callbacks from the request after calling `.abort()`.

## SSL support

Pass etcdclient a dictionary containing ssl options, check out http://nodejs.org/api/https.html#https_https_request_options_callback
Provide `ca`, `cert`, `key` as options. Remember to use `https`-url.

```javascript
fs = require('fs');
var fs = require('fs');

var options = {
ca: fs.readFileSync('ca.pem'),
cert: fs.readFileSync('cert.pem'),
key: fs.readFileSync('key.pem')
};

var etcdssl = new Etcd("https://localhost:4001", options);
```

## Basic auth

Pass a hash containing username and password as auth option to use basic auth.

sslopts = {
ca: [ fs.readFileSync('ca.pem') ],
cert: fs.readFileSync('cert.pem'),
key: fs.readFileSync('key.pem')
```javascript
var auth = {
user: "username",
pass: "password"
};

etcdssl = new Etcd('localhost', '4001', sslopts);
var etcd = new Etcd("localhost:4001", { auth: auth });
```

## Constructor options

Pass in a hash after server in the constructor to set options. Some useful constructor options include:

- `timeout` - Integer request timeout in milliseconds to wait for server response.
- `ca` - Ca certificate
- `cert` - Client certificate
- `key` - Client key
- `passphrase` - Client key passphrase
- `auth` - A hash containing `{user: "username", pass: "password"}` for basic auth.

```javascript
var etcd = new Etcd("127.0.0.1:4001", { timeout: 1000, ... });'
```
## Debugging
Nodejs `console.log`/`console.dir` truncates output to a couple of levels -
often hiding nested errors. Use `util.inspect` to show inner errors.
```javascript
etcd.get('key', function(err, val) {
console.log(require('util').inspect(err, true, 10));
});
//{ [Error: All servers returned error]
// [stack]: [Getter/Setter],
// [message]: 'All servers returned error',
// errors:
// [ { server: 'https://localhost:2379',
// httperror:
// { [Error: Hostname/IP doesn't match certificate's altnames: "Host: localhost. is not cert's CN: undefined"]
// [stack]: [Getter/Setter],
// [message]: 'Hostname/IP doesn\'t match certificate\'s altnames: "Host: localhost. is not cert\'s CN: undefined"',
// reason: 'Host: localhost. is not cert\'s CN: undefined',
// host: 'localhost.',
// cert:
// { subject: { C: 'USA', O: 'etcd-ca', OU: 'CA' },
// issuer: { C: 'USA', O: 'etcd-ca', OU: 'CA' } } },
// httpstatus: undefined,
// httpbody: undefined,
// response: undefined,
// timestamp: Sun Dec 27 2015 23:05:15 GMT+0100 (CET) },
// [length]: 1 ],
// retries: 0 }
```

## FAQ:
23 changes: 14 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "node-etcd",
"version": "4.1.0",
"version": "4.2.1",
"description": "etcd library for node.js (etcd v2 api)",
"licenses": [
{
@@ -31,15 +31,20 @@
"watch": "node_modules/.bin/mocha --compilers coffee:coffee-script/register --watch"
},
"dependencies": {
"underscore": "~1.8.2",
"request": "~2.60.0",
"deasync": "~0.1.1"
"deasync": "^0.1.1",
"request": "^2.60.0",
"underscore": "^1.8.2",
"url-parse": "^1.0.5"
},
"devDependencies": {
"coffee-script": "~1.9.1",
"mocha": "~2.1.0",
"nock": "~0.59.0",
"should": "~5.0.1"
"coffee-script": "^1.9.1",
"mocha": "^2.1.0",
"nock": "^2.17.0",
"should": "^5.0.1",
"simple-mock": "^0.5.0"
},
"keywords": ["etcd", "raft"]
"keywords": [
"etcd",
"raft"
]
}
14 changes: 9 additions & 5 deletions src/client.coffee
Original file line number Diff line number Diff line change
@@ -29,16 +29,16 @@ class CancellationToken
@req.abort() if @req?

cancel: @::abort

wasAborted: @::isAborted

# HTTP Client for connecting to etcd servers
class Client

constructor: (@hosts, @sslopts) ->
constructor: (@hosts, @options, @sslopts) ->
@syncmsg = {}

execute: (method, options, callback) =>
opt = _.defaults (_.clone options), defaultRequestOptions, { method: method }
opt = _.defaults (_.clone options), @options, defaultRequestOptions, { method: method }
opt.clientOptions = _.defaults opt.clientOptions, defaultClientOptions

servers = _.shuffle @hosts
@@ -59,7 +59,7 @@ class Client
# Multiserver (cluster) executer
_multiserverHelper: (servers, options, token, callback) =>
host = _.first(servers)
options.url = "#{options.serverprotocol}://#{host}#{options.path}"
options.url = "#{host}#{options.path}"

return if token.isAborted() # Aborted by user?

@@ -93,7 +93,7 @@ class Client
headers: headers
callback = syncRespHandler if options.synchronous is true

req = request options, reqRespHandler
req = @_doRequest options, reqRespHandler
token.setRequest req

if options.synchronous is true and options.syncdone is undefined
@@ -105,6 +105,10 @@ class Client
return req


_doRequest: (options, reqRespHandler) ->
request options, reqRespHandler


_retry: (token, options, callback) =>
doRetry = () =>
@_multiserverHelper token.servers, options, token, callback
32 changes: 16 additions & 16 deletions src/index.coffee
Original file line number Diff line number Diff line change
@@ -2,26 +2,17 @@ _ = require 'underscore'
Watcher = require './watcher'
Client = require './client'
HttpsAgent = (require 'https').Agent
URL = require 'url-parse'

# Etcd client for etcd protocol version 2
class Etcd

# Constructor, set etcd host and port.
# For https: provide {ca, crt, key} as sslopts.
constructor: (host = '127.0.0.1', port = null, sslopts = null, client = null) ->

if _.isArray(host)
@hosts = host
@sslopts = port
@client = sslopts
else
port ?= 4001
@hosts = ["#{host}:#{port}"]
@sslopts = sslopts
@client = client

@client ?= new Client(@hosts, @sslopts)

# constructor: (hosts = ["http://127.0.0.1:4001"], options = {}) ->
constructor: (hosts = "127.0.0.1:4001", options = {}) ->
@hosts = @_cleanHostList hosts
@client = new Client(@hosts, options, null)

# Set key to value
# Usage:
@@ -230,15 +221,15 @@ class Etcd

# Prepare request options
_prepareOpts: (path, apiVersion = "/v2", value = null, allOpts = {}) ->
serverprotocol = if @sslopts? then "https" else "http"
# serverprotocol = if @sslopts? then "https" else "http"

queryString = _.omit allOpts, 'maxRetries', 'synchronous'

clientOptions = _.pick allOpts, 'maxRetries'

opt = {
path: "#{apiVersion}/#{path}"
serverprotocol: serverprotocol
# serverprotocol: serverprotocol
json: true
qs: queryString
clientOptions: clientOptions
@@ -255,5 +246,14 @@ class Etcd
else
[options, callback]

# Make sure hosts is a list, make sure all have protocol added
# defaults to http and remove trailing slash
_cleanHostList: (hosts) ->
hostlist = if _.isArray(hosts) then hosts else [hosts]
hostlist.map (host) ->
url = new URL(host)
url.set 'protocol', 'http:' if url.protocol is ''
url.href.replace /\/$/, "" # Trailing slash


exports = module.exports = Etcd
Loading