UNPKG

node-etcd

Version:

etcd library for node.js (etcd v2 api)

257 lines (199 loc) 7.49 kB
_ = require 'lodash' 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: (hosts = ["http://127.0.0.1:2379"], options = {}) -> constructor: (hosts = "127.0.0.1:2379", options = {}) -> @hosts = @_cleanHostList hosts @client = new Client(@hosts, options, null) # Set key to value # Usage: # .set("key", "value", callback) # .set("key", "value", {prevValue: "oldvalue"}, callback) set: (key, value, options, callback) -> [options, callback] = @_argParser options, callback opt = @_prepareOpts ("keys/" + @_stripSlashPrefix key), "/v2", value, options @client.put opt, callback # Set key to value synchronously # Usage: # .set("key", "value") # .set("key", "value", {prevValue: "oldvalue"}) setSync: (key, value, options = {}) -> this.set key, value, @_synchronousOpts(options) # Get value of key # Usage: # .get("key", callback) # .get("key", {recursive: true}, callback) get: (key, options, callback) -> [options, callback] = @_argParser options, callback opt = @_prepareOpts ("keys/" + @_stripSlashPrefix key), "/v2", null, options @client.get opt, callback # Synchronously get value of key # Usage: # .get("key") # .get("key", {recursive: true}) getSync: (key, options = {}) -> this.get key, @_synchronousOpts(options) # Create a key (atomic in order) # Usage: # .create("path", "value", callback) # .create("path", "value", options, callback) create: (dir, value, options, callback) -> [options, callback] = @_argParser options, callback opt = @_prepareOpts ("keys/" + @_stripSlashPrefix dir), "/v2", value, options @client.post opt, callback post: @::create # Delete a key # Usage: # .del("key", callback) # .del("key", {recursive: true}, callback) # .delete("key", callback) del: (key, options, callback) -> [options, callback] = @_argParser options, callback opt = @_prepareOpts ("keys/" + @_stripSlashPrefix key), "/v2", null, options @client.delete opt, callback delete: @::del # Synchronous delete a key # Usage: # .del("key") # .del("key", {recursive: true})) delSync: (key, options = {}) -> this.del key, @_synchronousOpts(options) # Make a directory # Usage: # .mkdir("dir", callback) # .mkdir("dir", options, callback) mkdir: (dir, options, callback) -> [options, callback] = @_argParser options, callback options.dir = true @set dir, null, options, callback # Synchronously make a directory # Usage: # .mkdir("dir") # .mkdir("dir", options) mkdirSync: (dir, options = {}) -> this.mkdir dir, @_synchronousOpts(options) # Remove a directory # Usage: # .rmdir("dir", callback) # .rmdir("dir", {recursive: true}, callback) rmdir: (dir, options, callback) -> [options, callback] = @_argParser options, callback options.dir = true @del dir, options, callback # Synchronously remove a directory # Usage: # .rmdir("dir") # .rmdir("dir", {recursive: true}) rmdirSync: (dir, options = {}) -> this.rmdir dir, @_synchronousOpts(options) # Compare and swap value if unchanged # Usage: # .compareAndSwap("key", "newValue", "oldValue", callback) # .compareAndSwap("key", "newValue", "oldValue", options, callback) # .testAndSet("key", "newValue", "oldValue", options, callback) compareAndSwap: (key, value, oldvalue, options, callback) -> [options, callback] = @_argParser options, callback options ?= {} options.prevValue = oldvalue @set key, value, options, callback testAndSet: @::compareAndSwap # Compare and delete if value is unchanged # Usage: # .compareAndDelete("key", "oldValue", options, callback) compareAndDelete: (key, oldvalue, options, callback) -> [options, callback] = @_argParser options, callback options ?= {} options.prevValue = oldvalue @del key, options, callback testAndDelete: @::compareAndDelete # Execute a raw etcd query # Where method is one of: PUT, GET, POST, PATCH, DELETE # # Usage: # .raw("METHOD", "path", "value", options, callback) # .raw("GET", "v2/stats/leader", null, {}, callback) # .raw("PUT", "v2/keys/key", "value", {}, callback) raw: (method, key, value, options, callback) -> [options, callback] = @_argParser options, callback opt = @_prepareOpts key, "", value, options @client.execute method, opt, callback # Watch for value changes on a key watch: (key, options, callback) -> [options, callback] = @_argParser options, callback options ?= {} options.wait = true @get key, options, callback # Watch for value changes on a key since a specific index watchIndex: (key, index, options, callback) -> [options, callback] = @_argParser options, callback options ?= {} options.waitIndex = index @watch key, options, callback # Returns an eventemitter that watches a key, emits 'change' on value change # or 'reconnect' when trying to recover from errors. watcher: (key, index = null, options = {}) => return new Watcher this, key, index, options # Get the etcd cluster machines (server) machines: (callback) -> opt = @_prepareOpts "keys/_etcd/machines" @client.get opt, callback # List servers this etcd client will try to connect to getHosts: () -> _.clone(@hosts) # Get the current cluster leader leader: (callback) -> opt = @_prepareOpts "leader" @client.get opt, callback # Get statistics about the leader leaderStats: (callback) -> opt = @_prepareOpts "stats/leader" @client.get opt, callback # Get statistics about the currently connected entity selfStats: (callback) -> opt = @_prepareOpts "stats/self" @client.get opt, callback # Get version of etcd version: (callback) -> opt = @_prepareOpts "version", "" @client.get opt, callback # Strip the prefix slash if set _stripSlashPrefix: (key) -> key.replace /^\//, '' _synchronousOpts: (options) -> _.extend {}, options, { synchronous: true } # Prepare request options _prepareOpts: (path, apiVersion = "/v2", value = null, allOpts = {}) -> # serverprotocol = if @sslopts? then "https" else "http" queryString = _.omit allOpts, 'maxRetries', 'synchronous' clientOptions = _.pick allOpts, 'maxRetries' opt = { path: "#{apiVersion}/#{path}" # serverprotocol: serverprotocol json: true qs: queryString clientOptions: clientOptions synchronous: allOpts.synchronous form: { value: value } if value? agentOptions: @sslopts if @sslopts? } # Swap callback and options if no options was given. _argParser: (options = {}, callback) -> if typeof options is 'function' [{}, options] 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) -> host = 'http://' + host if host.indexOf('http') is -1 url = new URL(host) url.href.replace /\/$/, "" # Trailing slash exports = module.exports = Etcd