Compare commits

...

591 Commits

Author SHA1 Message Date
b606078e93 version: bump to 2.0.0 2015-01-27 23:08:58 -08:00
127fe322a4 Merge pull request #2172 from philips/README-updates
README: remove the etcd release candidate disclaimer
2015-01-27 23:04:09 -08:00
b377110c11 README: remove the etcd release candidate disclaimer
Remove the information about the v1 API since it has been removed.
Remove the disclaimer since we are no longer in release candidate mode.
2015-01-27 22:54:10 -08:00
7167cd6ccd Merge pull request #2149 from vdemedes/patch-1
Update README.md
2015-01-27 16:09:20 -08:00
bff2ccaa22 Merge pull request #2170 from xiang90/remove_log
raft: remove default verbose logging
2015-01-27 15:58:53 -08:00
553379e82b raft: remove default verbose logging 2015-01-27 15:57:44 -08:00
67d141a0af Merge pull request #2167 from bdarnell/send-after-response
raft: Send any waiting appends after receiving MsgAppResp.
2015-01-27 15:31:50 -08:00
0b2fde38d0 Merge pull request #2169 from philips/remove-CHANGELOG
CHANGELOG: remove unupdated changelog file
2015-01-27 15:03:47 -08:00
0c1329ace2 CHANGELOG: remove unupdated changelog file
We have been putting the changelog in the tags. Nuke this file.
2015-01-27 15:01:23 -08:00
43f1ccc88c version: bump to v2.0.0-rc.2 2015-01-27 14:48:30 -08:00
33d2400063 raft: Send any waiting appends after receiving MsgAppResp.
This addresses a problem that comes up in the cockroach tests,
in which the order of messages may lead to deadlocks (due to
the fact that we don't have regular heartbeat timers in most
of our tests).
2015-01-27 17:43:29 -05:00
4c33d12bf8 Merge pull request #2132 from xiang90/ctl
etcdctl: support upgrade
2015-01-27 14:18:16 -08:00
0f2582e0be Merge pull request #2042 from yichengq/279
docs: improve doc for server timeout
2015-01-27 14:12:42 -08:00
a03c906e9d docs: improve doc for server timeout 2015-01-27 14:12:27 -08:00
c530e6fc55 Merge pull request #2165 from yichengq/312
docs: fix details about 2.0
2015-01-27 13:53:20 -08:00
c5adff4988 docs: fix details about 2.0 2015-01-27 13:52:37 -08:00
91bd02dce1 Merge pull request #2161 from yichengq/311
migrate: set HardState.Term in migration
2015-01-27 13:59:26 -05:00
62b0fe50eb Merge pull request #2164 from barakmich/2160redux
migrate: convert 0.4 terms to start at 1
2015-01-27 13:59:20 -05:00
9eaa79a12a Merge pull request #2137 from jurmous/patch-2
Update libraries-and-tools.md
2015-01-27 10:54:49 -08:00
55c1635cee comment 2015-01-27 13:54:23 -05:00
7f91a35313 migrate: convert 0.4 terms to start at 1 2015-01-27 13:10:13 -05:00
517eb340dd migrate: set HardState.Term in migration 2015-01-26 23:41:21 -08:00
915c22292f Merge pull request #2140 from xiang90/raft_stats
etcdserver: support raft.status
2015-01-26 16:44:58 -08:00
276c9540b4 etcdserver: support raft.status 2015-01-26 16:39:33 -08:00
825107629a Merge pull request #2157 from yichengq/309
pkg/metrics: protect global vars in reset func
2015-01-26 16:24:00 -08:00
8c932ff719 pkg/metrics: protect global vars in reset func 2015-01-26 16:23:35 -08:00
f0c9a54edb Merge pull request #2156 from yichengq/309
pkg/metrics: self-manage global expvar map
2015-01-26 16:20:31 -08:00
08b34a3f5b pkg/metrics: self-manage global expvar map
This helps the embedded tests.
2015-01-26 16:20:09 -08:00
78f70137ea Merge pull request #2155 from yichengq/310
README: add doc about easy etcd cluster bootstrap
2015-01-26 15:47:21 -08:00
4c55e8a7c0 README: add doc about easy etcd cluster bootstrap 2015-01-26 15:41:56 -08:00
4427b889df Merge pull request #2151 from efrecon/tcl-binding-reference
Update libraries-and-tools.md
2015-01-26 14:33:02 -08:00
37f8e2d5e0 Merge pull request #2150 from sorah/master
Treat URLs have same IP address as same
2015-01-26 12:08:26 -08:00
f8ce5996b0 Treat URLs have same IP address as same
- To solve validation error problem using URLs in hostname #2123
2015-01-27 04:36:41 +09:00
9c7f66c5d9 Merge pull request #2119 from sorah/peer-ca-on-fetching-members
etcdserver: User peerTLSInfo to get cluster member
2015-01-26 10:50:44 -08:00
033e7d1db9 etcdserver: User peerTLSInfo to get cluster member 2015-01-27 03:43:21 +09:00
a6661201c5 Merge pull request #2148 from jonboulle/copyright
*: switch to line comments for copyright
2015-01-26 09:58:57 -08:00
f1ed69e883 *: switch to line comments for copyright
Build tags are not compatible with block comments.
Also adds copyright header to a few places it was missing.
2015-01-26 09:53:30 -08:00
200d4d6f41 Update libraries-and-tools.md
This documents a reference to the Tcl bindings for the etcd API.

Fixes #2127
2015-01-26 14:18:08 +01:00
f1dcefa834 docs: update etcd4j in libraries-and-tools 2015-01-26 11:34:02 +01:00
b2acb12c8e Update README.md
Example output of `$ etcdctl exec-watch` command was wrong and showing non-existing ETCD_VALUE, ETCD_KEY, etc keys. Submitting a PR, which updates those examples with correct keys (ETCD_WATCH_VALUE, ETCD_WATCH_KEY, etc).
2015-01-25 14:10:08 +01:00
93e4880ae6 Merge pull request #2135 from bcwaldon/test-int
test: do not run integration tests by default
2015-01-23 11:44:46 -08:00
d5f6b97b20 test: do not run integration tests by default
The ./test script will no longer run the integration tests. To run the
integration test, set the INTEGRATION env var to a nonzero value. For
example, `INTEGRATION=y ./test`.
2015-01-22 17:31:27 -08:00
6b304ce605 Merge pull request #2134 from bcwaldon/pkg-rename
pkg: ioutils -> ioutil
2015-01-22 17:30:36 -08:00
2120af8cfc pkg: ioutils -> ioutil 2015-01-22 17:14:01 -08:00
f16ff64949 Merge pull request #2131 from xiang90/version
etcdhttp: add internalVersion
2015-01-22 15:52:15 -08:00
c658e9a3e9 etcdctl: support upgrade 2015-01-22 15:51:45 -08:00
a77bf97c14 etcdhttp: add internalVersion 2015-01-22 15:42:16 -08:00
7d33a2686c Merge pull request #1990 from lemenkov/docs_proper_links
Proper links to the docs
2015-01-22 10:56:03 -08:00
e7d539e4ce Merge pull request #2128 from bdarnell/applied-restart
raft: Add applied index as an argument to newRaft and RestartNode.
2015-01-22 10:11:12 -08:00
8c3a6508e9 raft: Add applied to the newRaft log message. 2015-01-22 12:04:40 -05:00
59214978a2 raft: Add applied index as an argument to newRaft and RestartNode. 2015-01-22 11:38:05 -05:00
c2fa486920 Proper links to the docs
Signed-off-by: Peter Lemenkov <lemenkov@gmail.com>
2015-01-22 14:02:11 +03:00
4409e88358 Merge pull request #2120 from bdarnell/formatter
raft: Add support for custom formatters in DescribeMessage/DescribeEntry
2015-01-21 16:32:59 -08:00
cd9d5573d4 raft: make EntryFormatter less clever. 2015-01-21 19:27:26 -05:00
891cb62b81 Merge pull request #2118 from yichengq/308
metrics: add /rafthttp/stream metrics
2015-01-21 15:30:30 -08:00
99821579bf metrics: add /rafthttp/stream metrics 2015-01-21 13:24:21 -08:00
e73d442e32 raft: Add support for custom formatters in DescribeMessage/DescribeEntry 2015-01-21 14:12:58 -05:00
88704c70e7 Merge pull request #2116 from yichengq/307
store: optimize ttlKeyHeap GC
2015-01-20 12:09:30 -08:00
c104ca89c2 store: optimize ttlKeyHeap GC
It helps to recycle nodes in heap array, whose value can be unlimited
long.
2015-01-20 12:01:57 -08:00
7c7d78a11f Merge pull request #2004 from xiang90/status
raft: add Status interface
2015-01-20 10:51:33 -08:00
003b97a60f raft: public progress struct in raft 2015-01-20 10:26:22 -08:00
b34936b097 raft: add progress into status 2015-01-18 15:23:50 -08:00
0eaaad0e48 raft: add Status interface
Status returns the current status of raft state machine.
2015-01-16 14:02:04 -08:00
3ec91ead88 Merge pull request #2112 from xiang90/health
etcdhttp: add health endpoint
2015-01-16 10:57:36 -08:00
a97f331a0e etcdhttp: add health endpoint 2015-01-16 10:52:02 -08:00
4735324403 Merge pull request #2113 from yichengq/306
*: remove consistent-get related stuffs
2015-01-16 10:46:56 -08:00
37dde76cd5 *: remove consistent-get related stuffs 2015-01-15 22:21:53 -08:00
c36aa3be6e Merge pull request #2110 from xiang90/raft
etcdserver: separate out raft related stuff
2015-01-15 15:17:48 -08:00
973f79e1c9 etcdserver: separate out raft related stuff 2015-01-15 15:15:13 -08:00
4b6fa2d24f Merge pull request #2108 from yichengq/305
rafthttp: write StatusOK before start streaming
2015-01-15 14:58:52 -08:00
84ceefbffc rafthttp: write StatusOK before start streaming 2015-01-15 14:44:24 -08:00
1a6161d08a Merge pull request #2104 from xiang90/timeout
etcdserver: make heartbeat/election configurable
2015-01-15 13:52:20 -08:00
6bd8c435f9 Merge pull request #2107 from yichengq/304
integration: fix TestForceNewCluster to wait leader
2015-01-15 13:35:11 -08:00
b28bad3b4a Merge pull request #2106 from yichengq/303
docs: update errorcode.md
2015-01-15 13:28:39 -08:00
d380be8fa1 integration: fix TestForceNewCluster to wait leader 2015-01-15 13:27:24 -08:00
e8698b0e42 docs: update errorcode.md 2015-01-15 13:23:24 -08:00
295fa1ca99 error: deprecate unused error code 2015-01-15 11:49:41 -08:00
276a4abac0 etcdserver: make heartbeat/election configurable 2015-01-15 11:11:33 -08:00
e9235002f7 Merge pull request #2068 from yichengq/285
add tests in pkg/types package
2015-01-15 10:55:55 -08:00
ae7153bf38 Merge pull request #2103 from yichengq/302
integration: fix TestForceNewCluster
2015-01-15 10:48:08 -08:00
68fdd70580 integration: fix TestForceNewCluster 2015-01-15 10:42:57 -08:00
190fd446f9 pkg/types: add URLs tests 2015-01-15 10:24:23 -08:00
886a6a6194 pkg/types: add unsafeSet.ContainsAll test 2015-01-15 10:21:53 -08:00
9b4e72dd3a pkg/types: add Uint64Slice test 2015-01-15 10:21:53 -08:00
c4e4a9711f Merge pull request #2075 from endocode/alban/build-aci
scripts: update build-aci
2015-01-15 09:57:06 -08:00
cb1903ddcb Merge pull request #2101 from jonboulle/relver
scripts: remove old release version scripts
2015-01-15 08:42:06 -08:00
5568d590ef Merge pull request #2100 from jonboulle/docs
README: update to reflect doc changes
2015-01-15 08:41:50 -08:00
b0a4637ebd build: etcd statically linked
So that it can easily be used in a container.

Symptoms:
$ sudo bin/rkt run ../etcd/etcd-${VERSION}-linux-amd64.aci
Error: Unable to open "/lib64/ld-linux-x86-64.so.2": No such file or directory
2015-01-15 12:30:12 +01:00
78a7e0e551 scripts/build-aci: improve the script
- fix path for bash
- check for common errors
2015-01-15 12:30:12 +01:00
e41d1e2064 scripts: remove old release version scripts 2015-01-14 22:34:59 -08:00
28feb073b5 README: update to reflect doc changes 2015-01-14 22:17:29 -08:00
e109836fef Merge pull request #2094 from yichengq/298
docs: remove discovery-protocol
2015-01-14 17:29:25 -08:00
5e4cc73991 Merge pull request #2099 from yichengq/301
docs: use 2.0 docs
2015-01-14 17:29:15 -08:00
245e23ca47 docs: use 2.0 docs 2015-01-14 17:27:35 -08:00
6bda827b67 Merge pull request #2096 from yichengq/299
docs: optimal-cluster-size -> 2.0/admin_guide
2015-01-14 17:24:45 -08:00
f1c6771726 docs: optimal-cluster-size -> 2.0/admin_guide 2015-01-14 17:23:37 -08:00
1146cb9461 Merge pull request #2097 from xiang90/fix_force_test
integration: fix force cluster test
2015-01-14 16:46:20 -08:00
467ce1e730 Merge pull request #2098 from yichengq/300
docs: refine security.md
2015-01-14 16:45:40 -08:00
507021d884 docs: refine security.md 2015-01-14 16:42:05 -08:00
8e8552b2ad integration: fix force cluster test 2015-01-14 16:40:09 -08:00
2b94119fb1 docs: remove discovery-protocol
It is out-of-data, and will be reintroduced when the protocol is settled
down.
2015-01-14 15:20:48 -08:00
41a30ff21b Merge pull request #2092 from yichengq/296
docs: remove cluster-discovery
2015-01-14 15:19:12 -08:00
dde0e6d05a Merge pull request #2093 from yichengq/297
docs: remove unused img/
2015-01-14 15:18:52 -08:00
4df434334f docs: remove unused img/ 2015-01-14 15:17:57 -08:00
1d02493a53 docs: remove cluster-discovery
It is covered by 2.0/clustering.md
2015-01-14 15:14:58 -08:00
d117a12d02 Merge pull request #2091 from yichengq/295
docs: remove clients-matrix
2015-01-14 15:09:04 -08:00
23406dc2ee docs: remove clients-matrix
The doc is out of maintainence, and may confuse users. We will bring it
back when we have efforts and better method to manage client bindings.
2015-01-14 15:05:35 -08:00
e5daa2c6ca Merge pull request #2086 from xiang90/cluster_doc
doc: remove old clustering doc
2015-01-14 15:03:43 -08:00
5de4a464f7 Merge pull request #2089 from bdarnell/heartbeat-response
raft: introduce MsgHeartbeatResp.
2015-01-14 14:56:24 -08:00
2e1c36cdd9 raft: introduce MsgHeartbeatResp.
Now that heartbeats are distinct from MsgApp{,Resp}, the retries
currently performed in stepLeader's MsgAppResp section are only
performed on an actual MsgAppResp (or a new MsgProp). This means
that it may take a long time to recover from a dropped MsgAppResp
in a quiet cluster.

This commit adds a dedicated heartbeat response message. This message
does not convey the follower's current log position because the
MsgHeartbeat does not include the leaders term and index. Upon receipt
of a heartbeat response, the leader may retry the latest MsgApp if it
believes the follower to be behind.
2015-01-14 17:34:10 -05:00
238b17fee0 Merge pull request #2090 from bdarnell/raft-ticks
raft: Use <= instead of < for heartbeat ticks.
2015-01-14 13:01:33 -08:00
9972e62d94 raft: Use <= instead of < for heartbeat ticks.
In code outside the raft package, we cannot call raft.bcastHeartbeat
directly. Instead, to control heartbeats we set heartbeatInterval to 1
and call Tick().
2015-01-14 15:27:32 -05:00
232927d9dc Merge pull request #2088 from bdarnell/listen-localhost
pkg/transport: tests always listen on 127.0.0.1
2015-01-14 10:54:46 -08:00
4510993b67 pkg/transport: tests always listen on 127.0.0.1
This avoids firewall prompts when running tests on OSX.
2015-01-14 13:14:57 -05:00
8e6297780b Merge pull request #2087 from xiang90/doc_rm
doc: remove out-of-dated docs
2015-01-13 20:58:15 -08:00
3e268467c8 doc: remove out-of-dated docs 2015-01-13 18:35:11 -08:00
733b655bfa doc: remove old clustering doc 2015-01-13 18:26:48 -08:00
1b9ccfc66f Merge pull request #2084 from yichengq/294
integration: add TestForceNewCluster
2015-01-13 15:47:21 -08:00
a318112c7a integration: add TestForceNewCluster 2015-01-13 13:55:38 -08:00
89d95539cf Merge pull request #2083 from yichengq/293
*: move etcdserver/idutil -> pkg/idutil
2015-01-13 13:04:50 -08:00
07a69430c1 *: move etcdserver/idutil -> pkg/idutil 2015-01-13 11:54:51 -08:00
a83aba12f0 Merge pull request #2082 from yichengq/292
error: remove unused Message func
2015-01-13 11:11:04 -08:00
c212a511fe Merge pull request #2078 from yichengq/290
pkg/crc: add test
2015-01-13 11:10:51 -08:00
c68f5c2059 pkg/crc: add test 2015-01-13 11:07:18 -08:00
51005d32c7 Merge pull request #2079 from yichengq/291
pkg/cors: add tests
2015-01-13 11:05:02 -08:00
b9544d32b6 error: remove unused Message func 2015-01-13 10:40:06 -08:00
586a5e463e Merge pull request #2059 from trainchou/master
docs: fix send rate usage in api.md
2015-01-13 08:33:36 -08:00
42ae6e5f5b docs: fix send rate usage in api.md 2015-01-13 23:18:12 +08:00
c8994cff37 pkg/cors: add tests 2015-01-12 18:42:40 -08:00
0015372939 pkg/cors: remove http flush
The code is introduced in 7dce4c8fbb, and
the comments cannot explain the usefulness of the code at all.
2015-01-12 18:03:30 -08:00
2e776117f8 Merge pull request #2071 from yichengq/287
etcdhttp: add NewPeerHandler test
2015-01-12 15:59:37 -08:00
dc6aef0d02 etcdhttp: add NewPeerHandler test 2015-01-12 15:56:29 -08:00
9010e8a2c4 Merge pull request #2069 from yichengq/286
pkg/pbutil: add marshal-related tests
2015-01-12 15:32:18 -08:00
7e67fd13f6 pkg/pbutil: add marshal-related tests 2015-01-12 15:26:55 -08:00
e01ae2c083 Merge pull request #2073 from yichengq/288
etcdmain: add config tests
2015-01-12 13:39:34 -08:00
50395a53fb etcdmain: add license 2015-01-12 13:34:21 -08:00
60d6c34c28 etcdmain: add config tests 2015-01-12 13:34:16 -08:00
2d8f5e1250 Merge pull request #2074 from yichengq/289
make `go test ./...` work
2015-01-12 12:25:20 -08:00
e3b2f08bd0 migrate/cmd/etcd-dump-logs: fix building 2015-01-12 12:17:41 -08:00
aec2eef498 Merge pull request #2067 from yichengq/284
add tests in pkg/transport package
2015-01-10 13:13:23 -08:00
dfb66ab8ce pkg/transport: add NewKeepAliveListener test 2015-01-10 13:09:57 -08:00
f1368a00fb pkg/transport: add NewListener test 2015-01-10 13:09:13 -08:00
3577ed69a2 pkg/transport: add NewTimeoutTransport test 2015-01-10 13:03:15 -08:00
e688471c28 pkg/transport: add NewTimeoutListener test 2015-01-09 15:57:04 -08:00
5d99024fea Merge pull request #2066 from yichengq/283
add tests and do clean in wal package
2015-01-09 15:54:35 -08:00
05e591f805 wal: remove unused encoder.buffered func 2015-01-09 14:59:46 -08:00
9bdc343b7c wal: add ReleaseLockTo test 2015-01-09 14:59:41 -08:00
270e67db84 wal: not export unnecessary public functions 2015-01-09 14:55:10 -08:00
50c179ec1c wal: add DetectVersion test 2015-01-09 14:55:05 -08:00
f08d1090d0 wal: refine parseWalName function
According to http://godoc.org/fmt#Scan, if scan number is less than the
number of arguments, err will report why. So we don't need to handle
this error case.
2015-01-08 14:56:21 -08:00
9532810f76 wal: remove unused max function 2015-01-08 14:49:14 -08:00
92f013393c test: remove no-test directory etcdserverpb 2015-01-08 14:46:13 -08:00
096cbbcbf6 Merge pull request #2061 from endocode/alban/fix-build-aci
build-aci: change 'val' abbreviation to 'value'
2015-01-08 09:48:35 -08:00
fcbe7fdc83 Merge pull request #2062 from splattael/patch-1
Fix link to Documentation/2.0 in README
2015-01-08 08:16:10 -08:00
d225690b08 doc: fix link to documentation/2.0 in readme
[ci skip]
2015-01-08 17:14:44 +01:00
80c174255a build-aci: change 'val' abbreviation to 'value'
The spec changed, so etcd must be updated to follow the new spec:
12a9617c2f
2015-01-08 09:52:21 +01:00
bca1e5aea6 Merge pull request #2057 from yichengq/282
fix context time-out failure on travis
2015-01-07 13:41:26 -08:00
9132098960 integration: wait longer for member to be removed 2015-01-07 13:36:59 -08:00
930156c18a integration: adjust election ticks using env var 2015-01-07 11:18:29 -08:00
f98d0ef817 Merge pull request #2051 from xiang90/fix_keepalive
pkg/transport: enable keep alive
2015-01-07 08:37:07 -08:00
6b237416e1 Merge pull request #2044 from yichengq/278
wal: record mark when snapshotting
2015-01-07 08:26:33 -08:00
1d1a4754a7 pkg/transport: enable keep alive 2015-01-06 22:02:30 -08:00
6460e49a33 wal: save empty snapshot when create
So caller can open at empty snapshot to read all entries.
2015-01-06 19:48:21 -08:00
78bb207bac wal: update doc about snapshot 2015-01-06 19:33:57 -08:00
84f62f21ee wal: record and check snapshot 2015-01-06 16:27:40 -08:00
945c5dd558 Merge pull request #2049 from xiang90/fix_timeout
etcdmain: do not set timeout for client api
2015-01-06 16:26:06 -08:00
a15f39e6a2 etcdmain: do not set timeout for client api 2015-01-06 16:17:56 -08:00
02085153c9 Merge pull request #2046 from xiang90/keepalive
*: use keepalive listener to detect dead clients
2015-01-06 13:38:46 -08:00
7f1c630a0b *: use keepalive listener to detect dead clients 2015-01-06 12:09:34 -08:00
47113d776e Merge pull request #2045 from xiang90/read_timeout
etcdmain: add readtimeout for http server
2015-01-06 11:31:24 -08:00
0afbca4090 etcdmain: add readtimeout for http server 2015-01-06 11:04:38 -08:00
cbdb0266e9 Merge pull request #2043 from xiang90/leader_member
etcdhttp: support member/leader endpoint
2015-01-06 09:01:58 -08:00
1ebad5e42c etcdhttp: support member/leader endpoint 2015-01-06 08:52:33 -08:00
7a2fa39e52 Merge pull request #2012 from andybons/master
raft: add link to the paper raft_paper_test.go refers to
2015-01-06 00:27:47 -08:00
6d288fa9e9 Merge pull request #2041 from yichengq/277
docs: add doc for server timeout
2015-01-05 15:27:02 -08:00
88a9eedf06 docs: add doc for server timeout 2015-01-05 15:23:22 -08:00
0c55cfb21e Merge pull request #2036 from xiang90/trtr
refactor rafthttp
2015-01-05 14:08:51 -08:00
1aa8f1eee6 rafthttp: clean up reader when failed 2015-01-05 12:04:25 -08:00
6b8667152b Merge pull request #2035 from xiang90/errorc
etcdserver: collect error from errorc
2015-01-05 11:29:01 -08:00
8ac184ad52 Merge pull request #2037 from xiang90/doc_watch
Doc watch
2015-01-05 11:26:12 -08:00
d4a145ab0d doc: add note for watch api 2015-01-05 11:04:01 -08:00
66d9f28926 Merge pull request #2027 from yichengq/273
integration: extend timeout to wait
2015-01-05 08:33:35 -08:00
cb5bff5b05 Merge pull request #2034 from yichengq/276
etcdhttp: reset serve and watch timeout
2015-01-05 08:33:25 -08:00
4938e6bff5 rafthttp: a stopped peer does not accept any methods 2015-01-03 20:02:43 -08:00
3319f716d9 rafthttp: a stopped stream does not accept any methods 2015-01-03 19:39:33 -08:00
15be030aaa etcdserver: collect error from errorc 2015-01-02 20:13:46 -08:00
4dd00be365 etcdhttp: reset serve and watch timeout 2015-01-02 16:39:13 -08:00
b44d7f84c4 integration: extend timeout to wait 2015-01-02 16:28:27 -08:00
d719bc0e29 Merge pull request #2032 from xiang90/note
doc: add note for reconfiguration
2015-01-02 15:03:19 -08:00
bc6f062008 doc: add note for reconfiguration 2015-01-02 15:02:35 -08:00
2a83e350b1 Merge pull request #1992 from xiang90/rm_leader
*: support removing the leader from a 2 members cluster
2015-01-02 14:15:12 -08:00
6e727625b9 etcdserver: continue to apply after self-removed 2015-01-02 14:10:07 -08:00
0632dc2023 Merge pull request #2031 from xiang90/rm_test
etcdsever: remove mult_server_test
2015-01-02 13:53:13 -08:00
51ffc88096 etcdsever: remove mult_server_test 2015-01-02 13:49:58 -08:00
5bb43b5276 Merge pull request #2030 from xiang90/server_test
Server test
2015-01-02 12:27:39 -08:00
41f6137261 etcdserver: use the actual store implementation when we need the actual implementation 2015-01-02 12:22:01 -08:00
27d47977d9 etcdserver: move recorder to testutil 2015-01-02 11:21:23 -08:00
ac6cd03365 etcdserver: refactor server_test.go 2015-01-02 10:56:09 -08:00
921ce4c25b Merge pull request #2021 from xiang90/raft_reject_hint
raft: add lastIndex as rejectHint
2015-01-02 09:16:33 -08:00
35b907ac58 raft: add lastIndex as rejectHint
Add the lastindex of the raft log as reject hint, so the leader can
bypass the greater index probing and decrease the next index directly
to last + 1.
2015-01-01 19:04:07 -08:00
95a661251d rafthttp: rename streamClient -> streamReader 2014-12-31 21:20:58 -08:00
fe53ffd74d rafthttp: streamserver -> streamwriter 2014-12-31 21:11:24 -08:00
5a867611ca Merge pull request #2025 from yichengq/270
tools: move etcd-migrate to tools
2014-12-31 11:47:21 -08:00
1f8eef3b3b Merge pull request #2026 from yichengq/272
rafthttp: fix stream.open call
2014-12-31 11:30:58 -08:00
2292da15d6 rafthttp: fix stream.open call 2014-12-31 10:02:45 -08:00
04003a01ba Merge pull request #2013 from xiang90/tr
rafthttp cleanup
2014-12-31 08:35:20 -08:00
4974bb0349 Merge pull request #2022 from yichengq/268
godeps: bump go-etcd to 6aa2da5
2014-12-31 07:39:00 -08:00
9de4e36b6a tools: move etcd-migrate to tools 2014-12-30 22:48:47 -08:00
dc863459f8 godeps: bump go-etcd to 6aa2da5 2014-12-30 20:12:48 -08:00
17401994c0 Merge pull request #2019 from yichengq/266
tools: add etcd-dump-logs
2014-12-30 16:16:11 -08:00
8b0c7bf652 tools: add etcd-dump-logs
The tool can dump the log from data directory.
It helps develop and debug.
2014-12-30 16:14:27 -08:00
7273a861a6 Merge pull request #2018 from xiang90/server_error
etcdserver: move error to errors.go
2014-12-30 15:05:05 -08:00
803c38f448 etcdserver: move error to errors.go
Both server.go and cluster.go are using defined ErrX. Move error
to errors.go
2014-12-30 15:02:07 -08:00
2c21ac656b Merge pull request #2017 from xiang90/pbutil
pbutil: move getbool to pbutil
2014-12-30 14:51:53 -08:00
c3d2f5eea0 pbutil: add getbool to pbutil 2014-12-30 14:51:26 -08:00
8088440e1d Merge pull request #2015 from yichengq/265
etcdserver: cleanup server tests
2014-12-30 13:52:58 -08:00
241a474935 etcdserver: refactor server tests
1. remove redundant fake struct
2. use fake node for better testing
3. code clean
2014-12-30 13:49:55 -08:00
d2c7a7e5cb rafthttp: remove raftPrefix 2014-12-30 13:48:07 -08:00
2193b70fb3 rafthttp: add stream 2014-12-30 13:45:11 -08:00
bbfed7e6ef Merge pull request #2016 from Mic92/patch-1
README.md: New etcdctl link
2014-12-30 11:49:23 -08:00
3f8a85ed7e README.md: New etcdctl link 2014-12-30 18:20:57 +01:00
a92bd1d165 etcdserver: add multi_server_test.go 2014-12-30 00:10:18 -08:00
f79b9042ab etcdserver: fix streaming handler 2014-12-29 22:12:58 -08:00
3748088b96 etcdserver: print out log of normal tests 2014-12-29 14:38:00 -08:00
6ccaadc95d Merge pull request #1952 from yichengq/262
etcdserver: add id generator
2014-12-29 13:59:06 -08:00
05c921229e etcdserver: add id generator 2014-12-29 13:03:04 -08:00
c712dd682a rafthttp: make Transport private 2014-12-29 12:20:52 -08:00
a14d13f724 rafthttp: make fields in Transport private 2014-12-29 12:08:13 -08:00
7c8b9c0203 Merge pull request #2011 from xiang90/timeutil
etcdserver: move getExpr to timeutil
2014-12-29 12:03:25 -08:00
152676f43a *: support removing the leader from a 2 members cluster 2014-12-29 11:34:33 -08:00
dc6ba914c8 Merge pull request #2010 from yichengq/264
rafthttp: cleanup transport
2014-12-29 11:22:59 -08:00
5bb8eeb5cf rafthttp: transport cleanup 2014-12-29 11:21:40 -08:00
4463f5c4b3 raft: add link to the paper raft_paper_tests.go refers to 2014-12-29 14:17:48 -05:00
cea29fe158 etcdserver: move getExpr to timeutil 2014-12-29 11:15:02 -08:00
5f11d5a0d0 Merge pull request #1999 from xiang90/discovery_srv
*: move srv into pkg discovery
2014-12-29 10:27:45 -08:00
e1ee335c3a Merge pull request #1802 from yichengq/235
rafthttp: set the API boundary of the package
2014-12-28 16:00:24 -08:00
08f839e32c rafthttp: set the API boundary of the package 2014-12-28 15:50:27 -08:00
0630f42e7a Merge pull request #2009 from xiang90/refactor
etcdserver: remove unused containsUint64()
2014-12-26 11:09:23 -08:00
1535596252 etcdserver: remove unnecessary indirection 2014-12-26 11:03:13 -08:00
3dcd66459d etcdserver: remove unused containsUint64() 2014-12-26 10:56:56 -08:00
e056e96ad5 Merge pull request #2008 from xiang90/server_clean
etcdserver: cleanup server.go
2014-12-25 21:40:35 -08:00
69444b6bba etcdserver: cleanup server.go 2014-12-25 21:37:20 -08:00
60d25635c4 Merge pull request #2007 from xiang90/cluster_clean
etcdserver: cleanup cluster.go
2014-12-25 21:03:40 -08:00
78b51d3f2f etcdserver: cleanup cluster.go 2014-12-25 20:56:30 -08:00
9c84443f42 Merge pull request #2006 from xiang90/cluster_clean
cluster clean up
2014-12-25 20:45:01 -08:00
6dc3af5da4 etcdserver: cluster clean up 2014-12-25 20:36:48 -08:00
7a5bf53222 etcdserver: move member sort interface to member.go 2014-12-25 20:18:55 -08:00
7ce0fc782e Merge pull request #2005 from xiang90/kill_todo
etcdserver: kill a todo in test
2014-12-25 18:14:27 -08:00
ef0a66bb0a etcdserver: kill a todo in test 2014-12-25 18:14:05 -08:00
52fc768c28 Merge pull request #2001 from xiang90/cleanup
etcdserver: cleanup
2014-12-25 17:57:08 -08:00
00b4e919d0 Merge pull request #2003 from xiang90/raft_clean
raft: remove unnecessary funcs in raft.go
2014-12-25 17:06:02 -08:00
fc96a9e4a7 raft: remove unnecessary funcs in raft.go 2014-12-25 17:04:33 -08:00
f43bc809b9 etcdserver: cleanup wal upgrade 2014-12-24 22:02:46 -08:00
7d866dbc44 Merge pull request #2000 from xiang90/rm
etcdserver: remove example.go
2014-12-24 21:41:43 -08:00
b9d228b0fa etcdserver: remove example.go 2014-12-24 21:41:31 -08:00
08e9c25ea5 *: move srv into pkg discovery 2014-12-24 21:37:20 -08:00
7ec2e382bd Merge pull request #1996 from kelseyhightower/fix-dns-discovery-hostnames
etcdmain: resolve DNS hostnames for client and peer URLs
2014-12-24 13:24:07 -05:00
705ec45083 etcdmain: resolve DNS hostnames for client and peer URLs
etcd resolves DNS hostnames to IP addresses for client and peer URLs
before creating any listening sockets.

The following messages are logged during startup:

    etcd: Resolving infra0.coreos.com:2380 to 10.0.1.10:2380

Fixes #1991
2014-12-24 13:12:32 -05:00
289b070aa5 Merge pull request #1985 from mgwilliams/config-docs
fix an error in the 2.0 config docs
2014-12-22 18:48:41 -08:00
08f74cf68f fix an error in the 2.0 config docs 2014-12-23 02:41:12 +00:00
2b9f388a91 Merge pull request #1983 from xiang90/raft_storage_doc
raft: add doc for storage
2014-12-22 12:34:36 -08:00
2dbdf87f86 raft: add doc for storage 2014-12-22 12:33:14 -08:00
d87ee9819b Merge pull request #1982 from xiang90/srv_doc
doc: doc addition/fix for discovery-srv flag
2014-12-22 12:22:14 -08:00
ee7f23d0d5 doc: doc addition/fix for discovery-srv flag 2014-12-22 12:10:04 -08:00
841368c8e3 Merge pull request #1981 from xiang90/flag_test
etcdmian: add tests for configuration parsing
2014-12-22 12:00:25 -08:00
3abe71dff5 etcdmian: add tests for configuration parsing 2014-12-22 11:56:56 -08:00
f143948fdd Merge pull request #1976 from xiang90/flag
etcdmain: add config.go
2014-12-19 18:39:19 -08:00
0fa754d90e etcdmain: add config.go 2014-12-19 18:33:19 -08:00
39786c4bea Merge pull request #1977 from kelseyhightower/update-clustering-docs
docs: document DNS bootstrapping
2014-12-19 17:53:40 -08:00
fb4781920c docs: document DNS bootstrapping 2014-12-19 17:51:03 -08:00
1a5afaec7a Merge pull request #1973 from kelseyhightower/dns-bootstrap-docs
docs: add dns bootstrap guide
2014-12-19 15:07:57 -08:00
4f2d35679e Merge pull request #1947 from barakmich/dns_bootstrap
add capability to bootstrap from DNS SRV
2014-12-19 13:45:03 -08:00
8fc17147ef change logging 2014-12-19 16:40:29 -05:00
1f98d15535 docs: add dns bootstrap guide 2014-12-19 13:24:28 -08:00
f78bf987c8 Merge pull request #1968 from xiang90/fix_raft_test
raft: flush the commit to fix a race in test
2014-12-18 17:11:13 -08:00
896bac1f76 raft: flush the commit to fix a race in test 2014-12-18 17:10:37 -08:00
f8752f9879 Merge pull request #1966 from jonboulle/5to2
*: change remaining 0.5 references -> 2.0
2014-12-18 16:51:15 -08:00
021fc140b8 Merge pull request #1965 from jonboulle/fileutil
pkg/fileutil: sort filenames during ReadDir
2014-12-18 16:44:07 -08:00
1ec98cb795 pkg/fileutil: sort filenames during ReadDir 2014-12-18 16:36:11 -08:00
2311463935 Merge pull request #1967 from jonboulle/master
README: update version disclaimer
2014-12-18 16:27:32 -08:00
f1890ea48b README: update version disclaimer 2014-12-18 16:22:14 -08:00
6295dfba5a resolve all hostnames in DNS discovery 2014-12-18 19:19:21 -05:00
4e6cbc937e *: change remaining 0.5 references -> 2.0 2014-12-18 16:14:42 -08:00
1d859790e5 Merge pull request #1961 from barakmich/flock
Fix building the lock on windows
2014-12-18 16:09:48 -08:00
a5923e5b00 apologize profusely about locking on windows 2014-12-18 19:04:58 -05:00
9f84be81a2 Add notification in docs re backup command 2014-12-18 18:58:01 -05:00
977c74069c move constants out for windows 2014-12-18 18:57:11 -05:00
a0d72fb00c Fix building the lock on windows 2014-12-18 18:57:11 -05:00
2dfcf053d4 rename flag to discovery-srv 2014-12-18 18:13:40 -05:00
5a99e969b7 Merge pull request #1964 from robszumski/master
readme: add logo
2014-12-18 15:10:40 -08:00
7f733ad68b Fully resolve DNS entries to IPs and ignore single errors (such as no etcd-server-ssl) 2014-12-18 18:08:56 -05:00
0a40e18f68 Merge pull request #1962 from xiang90/raft
raft: leader waits for the reply of previous message
2014-12-18 15:05:41 -08:00
8b8ebb96c4 readme: add logo 2014-12-18 15:05:13 -08:00
88767d913d raft: leader waits for the reply of previous message when follower is not in good path.
It is reasonable for the leader to wait for the reply before sending out the next
msgApp or msgSnap for the follower in bad path. Or the leader will send out useless
messages if the previous message is rejected or the previous message is a snapshot.
Especially for the snapshot case, the leader will be 100% to send out duplicate message
including the snapshot, which is a huge waste.

This commit implement a timeout based wait mechanism. The timeout for normal msgApp is a
heartbeatTimeout and the timeout for snapshot is electionTimeout(snapshot is larger). We
can implement a piggyback mechanism(application notifies the msg lost) in the future
if necessary.
2014-12-18 15:01:50 -08:00
7588e4ddfb Merge pull request #1963 from robszumski/add-logo
logos: add SVG and PNG logos
2014-12-18 15:01:00 -08:00
d375b67a50 logos: add SVG and PNG logos 2014-12-18 14:59:06 -08:00
221abdcb3b version: bump to v2.0.0-rc.1 2014-12-18 10:27:29 -08:00
fa35363f74 Documentation: update to 2.0
In anticipation for a 2.0.0-rc.0 release update and move the
documentation.
2014-12-18 10:18:26 -08:00
fc70aa27d2 add apurl checking and logging 2014-12-17 20:53:12 -05:00
04d9f848a7 fix from comments 2014-12-17 20:28:48 -05:00
fdad6630ea Add a simple test and mock for genDNS 2014-12-17 20:18:41 -05:00
35a772753c Merge pull request #1958 from xiang90/compatibility
doc: add backword_compatibility.md
2014-12-17 15:58:28 -08:00
aea87bc88d Merge pull request #1951 from jlsalvador/patch-1
etcdctl: add environment support to certs args
2014-12-17 15:53:59 -08:00
ce6f606766 doc: add backword_compatibility.md 2014-12-17 15:47:49 -08:00
a000e97eea Merge pull request #1956 from coreos/member-migration-example
doc: update node migration guide
2014-12-17 12:10:46 -08:00
2d76e5e273 doc: update node migration guide 2014-12-17 12:10:10 -08:00
f0b9ad3863 Merge pull request #1955 from coreos/doc-cleanup
docs: fix port in peer URLs
2014-12-17 11:01:27 -08:00
0a14927823 docs: fix port in peer URLs 2014-12-17 10:51:52 -08:00
910198d117 etcdctl: add environment support to certs args 2014-12-16 16:18:36 +01:00
722247a752 Merge pull request #1948 from xiang90/stats
etcdserver: fix leader stats
2014-12-15 17:10:48 -08:00
c27c288bef etcdserver: update stats when become leader 2014-12-15 17:02:48 -08:00
04522baeee etcdserver: fix leader stats 2014-12-15 16:50:03 -08:00
af4272848d add capability to bootstrap from DNS 2014-12-15 19:26:42 -05:00
43bb6cf038 Merge pull request #1946 from barakmich/less_logging
Remove verbose logging and extraneous debug. Fixes #1904
2014-12-15 11:49:48 -08:00
8ece28d4f7 Remove verbose logging and extraneous debug. Fixes #1904 2014-12-15 14:47:02 -05:00
5369fb1c4f Merge pull request #1945 from xiang90/raft_test
raft: use newRaft
2014-12-15 11:31:11 -08:00
044e35b814 raft: use newRaft 2014-12-15 11:25:35 -08:00
e9b06416de Merge pull request #1942 from xiang90/doc
doc: specify listening addrs in the clustering example
2014-12-15 10:41:34 -08:00
9dc5b5a7e8 Merge pull request #1943 from xiang90/fix_streamSrv
sender: set strmSrv to nil after stoping it
2014-12-14 20:13:23 -08:00
e3dbfefbe0 sender: set strmSrv to nil after stoping it 2014-12-14 20:00:32 -08:00
0ea8c0929e Merge pull request #1927 from xiang90/flock
*: lock the in using files; do not purge locked the wal files
2014-12-14 19:39:58 -08:00
502396edd5 wal: fix wal doc 2014-12-14 19:36:37 -08:00
6b73a72d42 test: add fileutil to test 2014-12-14 19:34:54 -08:00
53bf7e4b5e wal: rename openAtIndex -> open; OpenAtIndexUntilUsing -> openNotInUse 2014-12-14 19:33:06 -08:00
f538cba272 *: do not backup files still in use 2014-12-14 19:27:22 -08:00
ea94d19147 *: lock the in using files; do not purge locked the wal files 2014-12-14 19:27:22 -08:00
b90693ccae doc: specify listening addrs in the clustering example 2014-12-14 19:21:54 -08:00
dcf34c0ab4 Merge pull request #1938 from yichengq/262
etcdserver: protect the sender map in SendHub
2014-12-15 10:41:52 +08:00
ceb077424d etcdserver: protect the sender map in SendHub 2014-12-15 10:37:41 +08:00
d07434f99e Merge pull request #1939 from xiang90/sender_logging
rafthttp: better logging
2014-12-14 18:23:36 -08:00
cb6983cbb1 Merge pull request #1940 from xiang90/raft_log
rafT: log term as %d
2014-12-14 10:06:59 -08:00
c586d5012c raft: log term as %d 2014-12-14 10:06:45 -08:00
d86603840d rafthttp: better logging 2014-12-14 09:50:59 -08:00
e40a53b534 Merge pull request #1926 from jainvipin/master
minor fix for #1786
2014-12-13 16:26:59 -08:00
3f64c677e1 modify directory deletion sequence 2014-12-13 16:22:36 -08:00
b8ab2b0b5c Merge pull request #1936 from xiang90/fix_test
pkg/transport: change write size from 1MB -> 5MB
2014-12-13 11:38:51 -08:00
3cc4cdd363 pkg/transport: change write size from 1MB -> 5MB
As we move to container-based infrastructure testing env
on travis, the tcp write buffer is more than 1MB. Change
the test according to the change on the testing env.
2014-12-13 11:32:29 -08:00
c620238257 Merge pull request #1934 from xiang90/fix_test
discovery: fix watch index
2014-12-13 11:18:32 -08:00
f265afa8ac discovery: fix watch index 2014-12-13 11:15:24 -08:00
bee3103931 Merge pull request #1931 from barakmich/travis
Disable unused sudo-ability from Travis in hopes of faster startup
2014-12-12 19:47:42 -05:00
fa195dae39 Merge pull request #1929 from lisael/master
added aioetcd (python 3.4+) to client libs list
2014-12-12 16:43:29 -08:00
fe4abc40ce Disable unused sudo-ability from Travis in hopes of faster startup
times.
2014-12-12 19:39:09 -05:00
3794f6ab88 version: bump to alpha.5 2014-12-12 16:26:16 -08:00
22e56ae9c6 Merge pull request #1930 from xiang90/flag
doc: add max wal/snap flags
2014-12-12 16:14:33 -08:00
b7cd72b593 doc: add max wal/snap flags 2014-12-12 16:09:53 -08:00
1f0d43250f documentation: fix min python version for aioetcd 2014-12-12 23:46:34 +01:00
97025bf5f1 documentation: added aioetcd to client libs 2014-12-12 23:40:56 +01:00
ae1f3d5640 Merge pull request #1925 from xiang90/stats
Stats
2014-12-12 10:17:12 -08:00
4724cbbe2c etcdserver: one line 2014-12-11 22:17:36 -08:00
935f7128a9 etcdserver: move stats inferface to stats pkg 2014-12-11 22:14:05 -08:00
b555843e0f Merge pull request #1922 from crawford/docs
docs: Update the flags and environment variables
2014-12-11 17:15:50 -08:00
d5d034ecd2 Merge pull request #1921 from xiang90/fix_time
store: return utc time to user
2014-12-11 16:40:20 -08:00
f054dd9d6f docs: Update the flags and environment variables
The configuration docs were missing some flags environment variables.
Arranged both lists in alphabetical order as well.
2014-12-11 16:26:05 -08:00
773f112a5d store: return utc time to user 2014-12-11 16:24:33 -08:00
ec777ebd28 Merge pull request #1918 from xiang90/http_no_logging
etcdmain: discard the http server logging
2014-12-11 16:06:58 -08:00
3a83ab1b71 etcdmain: discard the http server logging 2014-12-11 16:06:28 -08:00
d381889a84 Merge pull request #1920 from xiang90/better_logging
etcdmain: better logging for discovery error
2014-12-11 16:04:39 -08:00
d9b21c79d4 etcdmain: better logging for discovery error 2014-12-11 16:03:27 -08:00
2e9f6f70d6 Merge pull request #1917 from philips/add-build-aci-script
scripts: build-aci initial commit
2014-12-11 14:00:12 -08:00
2c2e032155 Merge pull request #1908 from bdarnell/error-fixes
raft: remove panic when we see a proposal with no leader.
2014-12-11 13:58:51 -08:00
b1d7597a9e Merge pull request #1828 from cap10morgan/statically-compile-etcdctl
build: statically compile etcdctl binary
2014-12-11 13:27:38 -08:00
f21cc09d83 scripts: build-aci initial commit
This will build an ACI from an etcd release tarball. This can be slimmed
down once the `actool` gets better
2014-12-11 15:58:37 -05:00
b26856b603 raft: add detail to "no leader" log message 2014-12-11 15:07:32 -05:00
5f16fab541 Merge pull request #1915 from barakmich/1834
Return Unknown instead of NotExist
2014-12-11 13:49:26 -05:00
cf7690cb51 detect more cases of empty directories and actual errors 2014-12-11 13:37:32 -05:00
421fe128c3 Return Unknown instead of NotExist
Unless the data dir truly does not exist.
2014-12-11 13:09:50 -05:00
0416503124 Merge pull request #1803 from junxu/master
etcdmain: Fix misuse "-addr" flag
2014-12-11 09:45:17 -08:00
c26542b7f2 Merge pull request #1913 from xiang90/lazy_snap_dir
etcdserver: create snap dir until start the node
2014-12-11 09:39:51 -08:00
2d4ca7e448 Merge pull request #1905 from robszumski/minor-log
migrate: improve logging for name and ID
2014-12-11 09:39:37 -08:00
836ccabad2 etcdserver: create snap dir until start the node 2014-12-11 09:25:18 -08:00
5b2ec31a15 Merge pull request #1910 from xiang90/gateway
discovery: fix gateway timeout
2014-12-10 23:12:50 -08:00
7171410422 discovery: discovery will try forever when there is a timeout.
Perviously, etcd retries three times for timeout error. After this
commit, etcd retries forever. Also this commit make etcd client
aware of gateway timetout.
2014-12-10 23:08:24 -08:00
f2863e5279 Merge pull request #1911 from diffoperator/issue_1903
rafthttp: fixes issue 1903
2014-12-10 22:55:22 -08:00
123b3dd64c rafthttp: fixes issue 1903
Record the URL being fetched in the log when we 404
2014-12-10 21:34:29 -08:00
89cba625d6 Merge pull request #1897 from xiang90/raft
raft: get rid of the using of defer in critical path
2014-12-10 21:24:38 -08:00
e89cc25c50 Merge pull request #1901 from yichengq/260
rafthttp: batch MsgProp
2014-12-10 21:16:07 -08:00
8aba4caa72 rafthttp: batch MsgProp
If amounts of MsgProp goes to the follower, it could batch them and
forward to the leader. This can avoid dropping MsgProp in good path
due to exceed maximal serving in sender.

Moreover, batching MsgProp can increase the throughput of proposals that
come from follower.
2014-12-10 21:08:40 -08:00
3867c72c8a raft: support to do multiple proposals in one message 2014-12-10 20:00:59 -08:00
a729c829a5 Merge pull request #1907 from robszumski/dash-note
docs: make clear that dashboard is a module
2014-12-10 16:50:55 -08:00
fa247d09cc raft: remove panic when we see a proposal with no leader.
This panic can never be reached when using raft.Node, because we only
read from propc when there is a leader. However, it is possible to see
this error when using raft the raft object directly (as in MultiNode),
and in this case it is better to simply drop the proposal (as if we had
sent it to a leader that immediately vanished).

Add an error return to MemoryStorage.Append for consistency.
2014-12-10 17:34:40 -05:00
7e242acf04 docs: make clear that dashboard is a module 2014-12-10 14:21:29 -08:00
96de9776b7 raft: get rid of allocation 2014-12-10 13:41:04 -08:00
d4dcd39b83 Merge pull request #1906 from yichengq/261
rafthttp: log the type of message that is dropped when sending
2014-12-10 13:23:24 -08:00
07e876592b rafthttp: log the type of message that is dropped when sending 2014-12-10 12:50:31 -08:00
e7f5b14f1b migrate: improve logging for name and ID 2014-12-10 11:53:04 -08:00
4777cba995 Merge pull request #1898 from robszumski/improve-logging
Improve logging for etcdserver and rafthttp
2014-12-10 10:46:47 -08:00
2593914973 rafthttp: feedback 2014-12-10 10:18:01 -08:00
ce0b0ef418 Merge pull request #1900 from diffoperator/etcd_tests
etcdserver: removed an unhelpful test failure message
2014-12-09 23:10:07 -08:00
a852936a59 etcdserver: removed an unhelpful test failure message
this commit changes instances of "blah" in a test to more
descriptive messages
2014-12-09 21:45:50 -08:00
4094812b39 rafthttp: improve start/stop logging 2014-12-09 16:57:40 -08:00
13f3158728 etcdserver: improve discovery ignore warning 2014-12-09 15:57:25 -08:00
e4c0f5c1a8 Merge pull request #1895 from xiang90/snap_nodes
etcd: update conf when apply the confChange entry
2014-12-09 11:45:01 -08:00
a5efbf826d raft: drop nodes in softState 2014-12-09 11:43:52 -08:00
0472ddf05f Merge pull request #1890 from yichengq/259
raft: set raft.Commit too when setting raftLog.committed
2014-12-09 11:28:05 -08:00
29d7a2a558 etcd: update conf when apply the confChange entry 2014-12-08 23:37:07 -08:00
4804c45e14 raft: set raft.Commit too when setting raftLog.committed 2014-12-08 22:35:55 -08:00
22dd3b039c Merge pull request #1888 from yichengq/258
raft: increase term to 1 before append initial entries
2014-12-08 22:27:23 -08:00
7317834417 raft: increase term to 1 before append initial entries
Because the term of new raft is 0, it is weird to have term-1 committed
entries in the log.
2014-12-08 22:21:39 -08:00
20e2c8f431 Merge pull request #1894 from xiang90/fix_snap
snap: error on empty snapshot
2014-12-08 22:02:19 -08:00
e981dda287 snap: error on empty snapshot 2014-12-08 21:45:28 -08:00
9c8f5c9535 Merge pull request #1891 from yichengq/257
etcdserver: init state before run loop correctly
2014-12-08 16:38:33 -08:00
325e768c7b Merge pull request #1889 from xiang90/chord_raft
Chord raft
2014-12-08 16:35:42 -08:00
13814c9d7d etcdserver: init state before run loop correctly 2014-12-08 16:13:16 -08:00
7e06d85651 etcdserver: apply entries when it is not empty
Or it updates appliedi wrongly.
2014-12-08 15:56:38 -08:00
ba45637ba3 raft: group step funcs 2014-12-08 15:29:54 -08:00
099f4f10ea raft: one line 2014-12-08 15:28:48 -08:00
dfbe16445d Merge pull request #1882 from yichengq/256
etcdserver: correct the log about recovering from snapshot
2014-12-08 15:25:19 -08:00
8ead428e76 raft: group getter funcs 2014-12-08 15:24:34 -08:00
f73d059d80 raft: group configuration related funcs 2014-12-08 15:23:21 -08:00
25313b1210 raft: move poll close to campaign 2014-12-08 15:21:57 -08:00
d52c66ad42 raft: removed unused func 2014-12-08 15:20:43 -08:00
a5ec7040e0 Merge pull request #1887 from xiang90/raft_logging
raft: refactoring logging
2014-12-08 15:17:20 -08:00
62ed1de10d raft: refactoring logging 2014-12-08 15:16:02 -08:00
71f3b80fbe etcdserver: check recovery error when new server 2014-12-08 14:55:23 -08:00
8c338ffcc7 etcdserver: correct the log about recovering from snapshot 2014-12-08 14:51:42 -08:00
b4773a15b2 Merge pull request #1886 from xiang90/raft_log
raft: print out log when creating a newraft
2014-12-08 14:47:04 -08:00
6cb7f2d9e9 raft: print out log when creating a newraft 2014-12-08 14:37:39 -08:00
576aba700e Merge pull request #1885 from xiang90/doc
Doc
2014-12-08 14:16:16 -08:00
8065206839 doc: fix typo 2014-12-08 14:15:37 -08:00
cd878ea9a9 doc: add a link to the peer url section in other_apis.go 2014-12-08 14:14:33 -08:00
091cc237e3 Merge pull request #1883 from xiang90/member_migration
doc: add doc for member migration
2014-12-08 14:09:09 -08:00
069578c29c doc: add doc for member migration 2014-12-08 14:08:01 -08:00
31b9f712ba Merge pull request #1879 from xiang90/peer_url_doc
doc: add doc for changing peer urls
2014-12-08 13:40:47 -08:00
706b6f96b3 doc: http status for other_apis.md 2014-12-08 13:10:21 -08:00
e83e2bff92 doc: refactor other_apis.md 2014-12-08 11:46:24 -08:00
2b519c90b9 doc: add doc for changing peer urls 2014-12-08 11:43:14 -08:00
f10f7802be Merge pull request #1874 from bdarnell/bootstrap-apply
Pre-apply the bootstrapping ConfChange entries.
2014-12-08 10:52:33 -08:00
f63d51e40f Merge pull request #1878 from yichengq/253
rafthttp: increase sender buffer size
2014-12-07 22:38:50 -08:00
b24d546bd0 rafthttp: increase sender buffer size
The buffer size is set big enough to buffer all messages generated in
one second as a follower in good path.
2014-12-07 22:35:50 -08:00
ea4d645a83 raft: Ignore redundant addNode calls.
This avoids clobbering any state when bootstrapping entries are
applied twice.
2014-12-05 17:15:50 -05:00
3d91faf85a Pre-apply the bootstrapping ConfChange entries.
This eliminates the need to fake an ApplyConfChange call before Campaign
in tests.

Fixes #1856.
2014-12-05 15:35:39 -05:00
6bfa5d409e Merge pull request #1872 from xiang90/fix_watcher_race
store: fix race in watcher_hub
2014-12-05 12:12:07 -08:00
793cb095b0 store: fix race in watcher_hub
Get the lock before modifing the global objects in the hub.
2014-12-05 12:09:48 -08:00
c03da80330 Merge pull request #1871 from xiang90/fix_node
raft: filter out messages from unknown sender.
2014-12-05 11:50:34 -08:00
6409a8bf0d raft: filter out messages from unknow sender.
If we cannot find the `m.from` from current peers in the raft and it is a response
message, we should filter it out or raft panics. We are not targetting to avoid
malicious peers.

It has to be done in the raft node layer syncchronously. Although we can check
it at the application layer asynchronously, but after the checking and before
the message going into raft, the raft state machine might make progress and
unfortunately remove the `m.from` peer.
2014-12-05 11:34:56 -08:00
15aed05071 Merge pull request #1869 from yichengq/251
etcdserver: not add self into sendhub when new server
2014-12-05 10:09:01 -08:00
771ff4589d etcdserver: not add self into sendhub when new server 2014-12-05 00:18:40 -08:00
a16dd7ea67 Merge pull request #1861 from yichengq/250
wal: sync after writing data to disk in Cut function
2014-12-04 23:49:07 -08:00
b9bf957c6d wal: sync after writing data to disk in Cut function 2014-12-04 22:56:34 -08:00
abb72f60bc Merge pull request #1866 from xiang90/raftnode_log
raft: refactor logging at node level
2014-12-04 21:03:32 -08:00
182c30a41a raft: refactor logging at node level 2014-12-04 21:03:06 -08:00
1b43f60e0e Merge pull request #1857 from yichengq/248
integration: add TestProgressWithMajority
2014-12-04 18:39:39 -08:00
6d046d94d6 integration: improve member tests 2014-12-04 17:37:44 -08:00
933a9f3e3f Merge pull request #1863 from xiang90/usage
etcdmain: format usage
2014-12-04 17:37:20 -08:00
a1f648e5db etcdmain: format usage 2014-12-04 17:21:23 -08:00
1d1c2ff834 Merge pull request #1841 from yichengq/246
etcdserver: close storage when stop
2014-12-04 15:36:24 -08:00
29982dc935 Merge pull request #1839 from yichengq/245
wal: save latest state into new WAL
2014-12-04 15:21:27 -08:00
a7bc03b42b etcdserver: close storage when stop 2014-12-04 15:16:22 -08:00
88e2fab572 Merge pull request #1859 from xiang90/pause_test
*: add pauseMember test
2014-12-04 15:11:59 -08:00
197e6b1b20 Merge pull request #1858 from vlajos/typofixes-vlajos-20141204
typofixes - https://github.com/vlajos/misspell_fixer
2014-12-04 14:52:27 -08:00
3de2ab2c04 *: typofixes
https://github.com/vlajos/misspell_fixer
2014-12-04 22:51:19 +00:00
ca32a5fe9b Merge pull request #1860 from yichengq/249
integration: fix possible early fire in TestWatch
2014-12-04 14:48:39 -08:00
356146b5a0 integration: fix possible early fire in TestWatch 2014-12-04 14:34:20 -08:00
151f043414 *: add pauseMember test 2014-12-04 14:22:43 -08:00
af0f34c595 wal: save latest state into new WAL
So we could always read out state when open at valid index.
2014-12-04 12:19:21 -08:00
a47690dd30 Merge pull request #1845 from xiang90/testunstable
raft: add TestUnstableTruncateAndAppend
2014-12-04 11:03:37 -08:00
4ebd3a0b10 Merge pull request #1852 from xiang90/heartbeat
raft: add msgHeartbeat type
2014-12-04 10:25:46 -08:00
72d2597f3d Merge pull request #1854 from yichengq/247
integration: not check whether readloop goroutines exist
2014-12-04 10:16:04 -08:00
149389cbfa raft: add msgHeartbeat type 2014-12-04 08:29:31 -08:00
719a634fdc integration: not check whether readloop goroutines exist
Readloop goroutines may be left from time to time. Skip the check for now,
and will dig into it when we have time.
2014-12-04 00:51:31 -08:00
e344774c10 Merge pull request #1850 from yichengq/247
raft: return 0 for term of compacted index
2014-12-03 17:23:32 -08:00
34a468de36 raft: return 0 for term of compacted index
It is necessary to make this check because of the following case:

1. memory storage contains ents from index 0 to 50, and unstable has
ents from index 50 to 60.
2. raft receives an incoming snapshot with index 100.
3. raft restores its unstable to 100, but has not applied snapshot on memory storage.
4. raft receives an out-dated MsgApp from index 60.
5. raft finds the term of index 60 to check the match.
6. raft asks memory storage about the term of index 60 after it failed to get
it from unstable.
7. memory storage panics because it knows nothing about index 60.
2014-12-03 17:22:36 -08:00
ddd9cb7345 raft: add TestUnstableTruncateAndAppend 2014-12-03 16:37:19 -08:00
2c0d323318 Merge pull request #1848 from xiang90/raft_log
raft: fix log format in sendAppend
2014-12-03 16:15:12 -08:00
2caf4f5f22 raft: fix log format in sendAppend 2014-12-03 16:11:44 -08:00
a426b310fc Merge pull request #1846 from xiang90/raft
raft: more logging
2014-12-03 14:59:06 -08:00
06a5892a18 raft: more logging 2014-12-03 14:46:24 -08:00
a36d07047a Merge pull request #1844 from xiang90/testunstable
Testunstable
2014-12-03 13:47:28 -08:00
8074a5b5a4 raft: fix error message format in test 2014-12-03 13:36:47 -08:00
37ab463e86 raft: add TestUnstableStableTo 2014-12-03 13:26:35 -08:00
7703d4942c raft: add TestUnstableRestore 2014-12-03 13:03:56 -08:00
be60c88603 Merge pull request #1842 from xiang90/unstable_test
raft: add TestUnstableFirstIndex
2014-12-03 11:50:39 -08:00
256e51874e Merge pull request #1843 from yichengq/248
raft: print out term in decimal format
2014-12-03 11:34:55 -08:00
63ed202db6 raft: print out term in decimal format 2014-12-03 11:33:51 -08:00
48f75ca645 raft: add TestUnstableMaybeTerm 2014-12-03 11:30:59 -08:00
058356d9bd raft: add TestUnstableLastIndex 2014-12-03 11:11:31 -08:00
98ebfa3468 raft: add TestUnstableFirstIndex 2014-12-03 11:11:11 -08:00
70bd26a652 Merge pull request #1815 from ravigadde/patch-1
docs: add etcd-lock into libraries-and-tools.md
2014-12-02 22:45:52 -08:00
16f9fd63ab doc: add etcd-lock into libraries-and-tools.md
Lock implementation for etcd. It has three go routines:
    a) acquire - loop that watches for the lock to be free and tries to acquire it.
    b) watch - to watch for lock changes
    c) refresh - to refresh the ttl when the lock is acquired

All the changes in lock ownership are notified on the events channel. Any feedback welcome!
2014-12-02 22:39:06 -08:00
23b32a6cbe Merge pull request #1716 from yichengq/225
raft: panic if loaded commit is out of range
2014-12-02 22:14:12 -08:00
7305451d43 Merge pull request #1825 from yichengq/242
wal: not return ErrIndexNotFound in ReadAll
2014-12-02 22:11:49 -08:00
38768e5396 raft: panic if loaded commit is out of range 2014-12-02 22:09:34 -08:00
7e01c02abb Merge pull request #1837 from xiang90/fix_restore
raft: do not restore snapshot if local raft has longer matching history
2014-12-02 21:48:43 -08:00
b3841afcc3 raft: do not restore snapshot if local raft has longer matching history
Raft should not restore the snapshot if it has longer matching history.
Or restoring snapshot might remove the matched entries.
2014-12-02 21:34:14 -08:00
e07e2ac124 Merge pull request #1836 from xiang90/panic_slice
raft: panic on bad slice
2014-12-02 17:48:34 -08:00
3209fd544b raft: panic on bad slice 2014-12-02 17:48:03 -08:00
79014556e9 Merge pull request #1831 from xiang90/fix_unstable
raft: fix unstable
2014-12-02 14:43:11 -08:00
2f5b748a90 raft: clearify that the firstIndex might not be available. 2014-12-02 14:27:52 -08:00
1c7b9317a9 Merge pull request #1833 from yichengq/244
raft: not call stableTo for restored snapshot
2014-12-02 13:20:39 -08:00
551a56fb98 raft: not call stableTo for restored snapshot
Stable has been set when restoring the snapshot in raftlog, so we don't need
to set it after advance.
2014-12-02 13:10:35 -08:00
b7ca56e3c8 raft: move good case of truncateAndAppend to the first place 2014-12-02 13:05:55 -08:00
3cadaca1a3 Merge pull request #1830 from xiang90/raft_snap_log
raft: log snapshot events
2014-12-02 12:06:15 -08:00
411063e14f raft: log snapshot events 2014-12-02 11:57:10 -08:00
99c2e905e2 Merge pull request #1829 from xiang90/raft_index
raft: use index in entry
2014-12-02 10:42:55 -08:00
788d1e59a2 raft: use index in entry 2014-12-02 10:25:27 -08:00
70b501d17c Merge pull request #1824 from yichengq/241
etcdserver: close idle connections when stop sendhub
2014-12-02 10:12:45 -08:00
6692a8060e Merge pull request #1823 from xiang90/raft_log
raft: logging state change events and events on bad path
2014-12-02 10:10:08 -08:00
51de095d2c raft: logging state change events and events on bad path 2014-12-02 10:08:19 -08:00
f02eae934b Merge pull request #1827 from lamielle/doc-whitespaces
doc: uses spaces consistently in sample JSON
2014-12-02 07:44:03 -08:00
57b076f710 build: statically compile etcdctl binary 2014-12-02 10:24:05 -05:00
2b7af3d101 doc: uses spaces consistently in sample JSON
Replaces spurious uses of tabs with four spaces.  Removes strange
unicode space character with standard space character.
2014-12-02 06:43:07 -08:00
aa61009560 wal: not return ErrIndexNotFound in ReadAll
This IndexNotFound case is reasonable now because we don't write dummy
entries into wals any more.
2014-12-02 00:28:54 -08:00
fa292391d8 etcdserver: close idle connections when stop sendhub 2014-12-02 00:08:47 -08:00
f34fe6e4ae Merge pull request #1819 from yichengq/239
integration: use timeout transport when launching cluster
2014-12-01 23:00:28 -08:00
cb74b6812b Merge pull request #1820 from xiang90/fix_storage
raft: fix memory storage
2014-12-01 21:25:17 -08:00
312db7f0f3 raft: fix memory storage
Memory storage should append all entries that have greater index
than the snap.Matedata.Index. We first truncate the old parts of
incoming entries. Then truncate the existing entries in the storage.
At last, we append the incoming entries to the existing entries.
2014-12-01 16:37:16 -08:00
7a1d147795 integration: use timeout transport when launching cluster
This makes it do the same behavior as etcdmain does.
2014-12-01 16:26:27 -08:00
19ccdbee18 Merge pull request #1806 from xiang90/no_copy
No copy
2014-12-01 13:15:13 -08:00
7beac083ff Merge pull request #1810 from xiang90/purge
*: support purging old wal/snap files
2014-12-01 12:05:05 -08:00
d3db010190 *: support purging old wal/snap files 2014-12-01 11:50:17 -08:00
92d4112feb Merge pull request #1809 from xiang90/unstable
raft: stableTo checks term matching
2014-12-01 11:09:40 -08:00
649176934a raft: add tests for stableTo 2014-12-01 10:54:34 -08:00
7c47decd19 Merge pull request #1813 from xiang90/snap_event_log
etcdserver: log snapshot event
2014-11-30 12:11:23 -08:00
bc5acd3c42 etcdserver: log snapshot event 2014-11-30 12:10:20 -08:00
3c0fbe285c raft: stableTo checks term matching
stableTo should only mark the index stable if the term is matched. After raft sends out unstable
entries to application, raft makes progress without waiting for reply. When the appliaction
calls the stableTo to notify the entries up to "index" are stable, raft might have truncated
some entries before "index" due to leader lost. raft must verify the (index,term) of stableTo,
before marking the entries as stable.
2014-11-28 14:13:07 -08:00
d214e87aee raft: make unstable.entries immutable; copy the entries at bad path 2014-11-27 19:35:03 -08:00
d244e3bf6e raft: fix node bench 2014-11-26 23:07:35 -08:00
fe0bc4ff36 Merge pull request #1805 from xiang90/fix_raft_b
raft: fix start term
2014-11-26 21:41:38 -08:00
746c66b466 raft: fix start term 2014-11-26 21:21:13 -08:00
43d6f9f964 Update etcd.go
etcdmain: Fix misuse "-addr" flag

In code, it uses "-advertise-client-urls" or "-addr" flags to get the list of this member's peer URLs, 
It should be using "-peer-addr" flag instead of "-addr" flag.
2014-11-27 10:38:47 +08:00
35cf7b5a31 Merge pull request #1800 from xiang90/unstable
raft: move unstable related function to log_unstable.go
2014-11-26 16:12:43 -08:00
7929e46dd8 raft: clean up 2014-11-26 15:31:07 -08:00
8a626257c7 raft: move unstable related function to log_unstable.go 2014-11-26 15:25:24 -08:00
416b799ecf Merge pull request #1788 from yichengq/233
rafthttp: increase the size of streaming buffer
2014-11-26 15:22:28 -08:00
00ce0702b9 rafthttp: increase the size of streaming buffer
Streaming buffer is used for:
1. hand over data to io goroutine in non-blocking way
2. hold pressure for temprorary network delay
3. be able to wait on I/O instead of data coming under high throughput

The old 1024 value is too small and is very likely to be full and
break the streaming when suffering temprorary network delay.
2014-11-26 14:46:52 -08:00
7358ef21a2 Merge pull request #1799 from yichengq/237
integration: attempt more times to listen on specified port
2014-11-26 14:26:41 -08:00
e03cf6d488 Merge pull request #1797 from yichengq/236
raft: no need to save dummy entry into stable storage
2014-11-26 14:23:32 -08:00
670d98ec72 integration: attempt more times to listen on specified port
Travis is rather slow, and it may fail to listen on that port sometimes.
2014-11-26 14:21:15 -08:00
0f070f3d2d raft: no need to save dummy entry into stable storage 2014-11-26 14:04:56 -08:00
b2d686495c Merge pull request #1796 from xiang90/unstable
raft: move all unstable stuff into one struct for future cleanup
2014-11-26 13:59:07 -08:00
66252c7d62 raft: move all unstable stuff into one struct for future cleanup 2014-11-26 13:36:17 -08:00
488f508505 Merge pull request #1777 from xiang90/log_interface
Log interface
2014-11-26 12:51:37 -08:00
ab2a40ea37 Merge branch 'log_interface'
Conflicts:
	raft/log.go
2014-11-26 12:16:02 -08:00
732cfa1ad6 raft: remove the applysnap from Storage interface 2014-11-26 11:28:51 -08:00
e23f9e76d1 raft: do not applysnapshot in raft 2014-11-26 10:59:13 -08:00
8de98d4903 raft: clean up 2014-11-25 16:21:50 -08:00
9bd1786fe4 raft: memory storage does not append out of date entries 2014-11-25 15:18:40 -08:00
9df0e7715d raft: do not panic on out of date compaction 2014-11-25 15:14:39 -08:00
01cbcce8ba etcdserver: do not applySnapshot twice 2014-11-25 14:53:49 -08:00
74d8c7f457 etcdserver: cleanup main loop 2014-11-25 14:38:18 -08:00
7e6e305c4f Merge branch 'log_interface'
Conflicts:
	raft/raft.go
2014-11-25 14:22:11 -08:00
a13d5a70ff etcdserver: save snapshot before entries 2014-11-25 12:39:15 -08:00
8aa89dba3d raft: make if checking match the error in storage.Term 2014-11-25 00:52:13 -08:00
8ee1bf31d6 raft: use IsEmptySnap to check the empty snapshot 2014-11-25 00:37:21 -08:00
e466126510 raft: set snapshot to nil when it is saved 2014-11-25 00:30:22 -08:00
e17bcd8932 raft: remove wont-fix TODO in ApplyConfChange 2014-11-25 00:10:44 -08:00
85d0e2f130 raft: remove unused raftLog.isOutOfAppliedBounds 2014-11-25 00:07:55 -08:00
1e0f87df8c raft: stricter checking in raftLog.slice 2014-11-25 00:05:00 -08:00
1d01c8aa2d raft: remove unused raftLog.at function 2014-11-24 23:52:28 -08:00
2c06a1d815 raft: not set applied when restore log from snapshot
applied is only updated by application level through Advance.
2014-11-24 23:37:47 -08:00
0d200baf72 raft: refine raftLog.term 2014-11-24 23:27:57 -08:00
7fcaca6d18 raft: simplify raftLog.lastIndex 2014-11-24 23:08:51 -08:00
8670f4012b raft: remove useless line in raftLog.append 2014-11-24 22:42:55 -08:00
239c8dd479 raft: add comment to newLog 2014-11-24 21:47:12 -08:00
54e1237271 etcdserver: panic when snapshot on raft storage
Snapshot on raft storage should always succeed. If there is an error, it must
be internal fault and needs stack info to debug.
2014-11-24 21:22:49 -08:00
1b038da18a etcdserver: init snapi when init appliedi 2014-11-24 21:19:30 -08:00
bd9e93eeea etcdserver: remove finished TODO for raftStorage.Compact 2014-11-24 21:10:53 -08:00
185d37c333 etcdserver: not load dummy entry from the wal 2014-11-24 20:51:04 -08:00
65ad1f6ffd raft: attach Index to Entry in all tests 2014-11-24 17:13:47 -08:00
10ebf1a335 raft: fix memoryStorage append 2014-11-24 16:36:59 -08:00
2876c652ab raft: fix for go vet 2014-11-24 15:00:38 -08:00
d69e4dbe6d etcdserver: initial index to 1 2014-11-24 14:57:08 -08:00
453133977d etcdserver: save snapshot only if the index is greater than previous snap index 2014-11-24 14:47:59 -08:00
4b7af29c37 etcdserver: fix TriggerSnap test.
Sleep for millisecond to allow the server to apply the first nop and
first put separately.
2014-11-24 14:47:49 -08:00
62a8df304a raft: fix error message in TestLogRestore 2014-11-24 11:10:02 -08:00
e8afdcfe0a raft: refactor testUnstableEnts 2014-11-24 10:40:38 -08:00
08f156a1de etcdserver: remove extra empty line in snapshot func 2014-11-24 10:27:18 -08:00
3dd4c458ca raft: refactor term in log.go 2014-11-24 10:13:56 -08:00
94190286ff raft: add comment for append in unstableEntries in log.go 2014-11-24 09:05:40 -08:00
0a46c70f5d raft: use empty slice in unstableEntries in log.go 2014-11-24 09:04:45 -08:00
bc0e72acb9 raft: clean up panic in log.go 2014-11-24 09:01:25 -08:00
f3cef87c69 raft: remove extra empty line in log.go 2014-11-24 08:43:34 -08:00
6c8e294d20 test: longer test timeout 2014-11-24 08:37:26 -08:00
bdbafe2cf3 raft: use max in log.slice 2014-11-24 08:36:15 -08:00
9ddd8ee539 Rename Storage.HardState back to InitialState and include ConfState.
This fixes integration/migration_test.go (and highlights the fact that
we need some more raft-level testing of restoring from snapshots).
2014-11-21 17:22:20 -05:00
03c8881e35 Fix TestSlowNodeRestore 2014-11-21 16:40:41 -05:00
0d680d0e6b Merge remote-tracking branch 'coreos/master' into merge
* coreos/master:
  rafthttp: fix import
  raft: should not decrease match and next when handling out of order msgAppResp
  Fix migration to allow snapshots to have the right IDs
  add snapshotted integration test
  fix test import loop
  fix import loop, add set to types, and fix comments
  etcdserver: autodetect v0.4 WALs and upgrade them to v0.5 automatically
  wal: add a bench for write entry
  rafthttp: add streaming server and client
  dep: use vendored imports in codegangsta/cli
  dep: bump golang.org/x/net/context

Conflicts:
	etcdserver/server.go
	etcdserver/server_test.go
	migrate/snapshot.go
2014-11-21 15:40:11 -05:00
30690d15d9 Re-enable a few tests I had missed.
Fix integration test for the change to log entry zero.

Increase test timeouts since integration tests often take
longer than 10s for me.
2014-11-21 15:27:17 -05:00
b29240baf0 Merge remote-tracking branch 'coreos/master' into merge
* coreos/master:
  scripts: build-docker tag and use ENTRYPOINT
  scripts: build-release add etcd-migrate
  create .godir
  raft: optimistically increase the next if the follower is already matched
  raft: add handleHeartbeat handleHeartbeat commits to the commit index in the message. It never decreases the commit index of the raft state machine.
  rafthttp: send takes raft message instead of bytes
  *: add rafthttp pkg into test list
  raft: include commitIndex in heartbeat
  rafthttp: move server stats in raftHandler to etcdserver
  *: etcdhttp.raftHandler -> rafthttp.RaftHandler
  etcdserver: rename sender.go -> sendhub.go
  *: etcdserver.sender -> rafthttp.Sender

Conflicts:
	raft/log.go
	raft/raft_paper_test.go
2014-11-19 17:05:16 -05:00
355ee4f393 raft: Integrate snapshots into the raft.Storage interface.
Compaction is now treated as an implementation detail of Storage
implementations; Node.Compact() and related functionality have been
removed. Ready.Snapshot is now used only for incoming snapshots.

A return value has been added to ApplyConfChange to allow applications
to track the node information that must be stored in the snapshot.

raftpb.Snapshot has been split into Snapshot and SnapshotMetadata, to
allow the full snapshot data to be read from disk only when needed.

raft.Storage has new methods Snapshot, ApplySnapshot, HardState, and
SetHardState. The Snapshot and HardState parameters have been removed
from RestartNode() and will now be loaded from Storage instead.
The only remaining difference between StartNode and RestartNode is that
the former bootstraps an initial list of Peers.
2014-11-19 16:40:26 -05:00
46ee58c6f0 raft: Rename ErrSnapshotRequired To ErrCompacted. 2014-11-18 13:15:10 -05:00
300c5a2001 Merge remote-tracking branch 'coreos/master' into log-storage-interface
* coreos/master: (21 commits)
  etcdserver: refactor ValidateClusterAndAssignIDs
  integration: add integration test for remove member
  integration: add test for member restart
  version: bump to alpha.3
  etcdserver: add buffer to the sender queue
  *: gracefully stop etcdserver
  Fix up migration tool, add snapshot migration
  etcd4: migration from v0.4 -> v0.5
  etcdserver: export Member.StoreKey
  etcdserver: recover cluster when receiving newer snapshot
  etcdserver: check and select committed entries to apply
  etcdserver: recover from snapshot before applying requests
  raft: not set applied when restored from snapshot
  sender: support elegant stop
  etcdserver: add StopNotify
  etcdserver: fix TestDoProposalStopped test
  etcdserver: minor cleanup
  etcdserver: validate new node is not registered before in best effort
  etcdserver: fix server.Stop()
  *: print out configuration when necessary
  ...

Conflicts:
	etcdserver/server.go
	etcdserver/server_test.go
	raft/log.go
2014-11-17 18:28:24 -05:00
64d9bcabf1 Add Storage.Term() method and hide the first entry from other methods.
The first entry in the log is a dummy which is used for matchTerm
but may not have an actual payload. This change permits Storage
implementations to treat this term value specially instead of
storing it as a dummy Entry.

Storage.FirstIndex() no longer includes the term-only entry.

This reverses a recent decision to create entry zero as initially
unstable; Storage implementations are now required to make
Term(0) == 0 and the first unstable entry is now index 1.
stableTo(0) is no longer allowed.
2014-11-17 16:54:12 -05:00
45e96be605 raft: PR feedback.
Removed Get prefix in method names, added assertions and fixed comments.
2014-11-14 13:53:42 -05:00
0e8ffe9128 raft: remove a guard that is no longer necessary 2014-11-13 15:51:36 -05:00
39eddd8565 Merge remote-tracking branch 'coreos/master' into log-storage-interface
* coreos/master:
  etcdserver: add sender tests
  raft: Only call stableTo when we have ready entries or a snapshot.
  etcdserver: add ID() function to the Server interface.
  sender: use RoundTripper instead of Client in sender
2014-11-13 15:50:08 -05:00
b29c512f50 Merge remote-tracking branch 'coreos/master' into log-storage-interface
* coreos/master: (27 commits)
  pkg/wait: move wait to pkg/wait
  etcdserver: do not add/remove/update local member to/from sender hub
  etcdserver: not record attributes when add member
  raft: add a test for proposeConfChange
  raft: block Stop() on n.done, support idempotency
  raft: add a test for node proposal
  integration: add increase cluster size test
  integration: remove unnecessary t.Testing argument
  raft: stop the node synchronously
  integration: fix test to propagate NewServer errors
  etcdserver: move peer URLs check to config
  etcdserver: ensure initial-advertise-peer-urls match initial-cluster
  raft: add a test for node.Tick
  raft: add comment string for TestNodeStart
  etcdserver: use member instead of node at etcd level
  raft: nodes return sorted ids
  raft: update unstable when calling stableTo with 0
  *: support updating advertise-peer-url Users might want to update the peerurl of the etcd member in several cases. For example, if the IP address of the physical machine etcd running on is changed, user need to update the adversite-pee-rurl accordingly. This commit makes etcd support updating the advertise-peer-url of its members.
  transport: create a tls listener only if the tlsInfo is not empty and the scheme is HTTPS
  etcdserver: use member pointer for all tests
  ...

Conflicts:
	etcdserver/server.go
	raft/log.go
	raft/log_test.go
	raft/node.go
2014-11-13 14:21:09 -05:00
54b07d7974 Remove raft.loadEnts and the ents parameter to raft.RestartNode.
The initial entries are now provided via the Storage interface.
2014-11-12 18:31:19 -05:00
147fd614ce The initial term=0 log entry is now initially unstable.
This entry is now persisted through the normal flow instead of appearing
in the stored log at creation time.  This is how things worked before
the Storage interface was introduced. (see coreos/etcd#1689)
2014-11-12 18:24:16 -05:00
76a3de9a33 Require a non-nil Storage parameter in newLog.
Callers must in general have a reference to their Storage objects to
transfer entries from Ready to Storage, so it doesn't make sense to
create a hidden Storage for them.

By explicitly creating Storage objects in tests we can remove a
few casts of raftLog's storage field.
2014-11-12 16:38:50 -05:00
25b6590547 raft: introduce log storage interface.
This change splits the raftLog.entries array into an in-memory
"unstable" list and a pluggable interface for retrieving entries that
have been persisted to disk. An in-memory implementation of this
interface is provided which behaves the same as the old version;
in a future commit etcdserver could replace the MemoryStorage with
one backed by the WAL.
2014-11-10 17:40:39 -05:00
276 changed files with 12984 additions and 9238 deletions

33
.header
View File

@ -1,20 +1,13 @@
/*
Copyright 2013 CoreOS Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package x
import (
)
// Copyright 2014 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

View File

@ -1,4 +1,5 @@
language: go
sudo: false
go:
- 1.3
@ -7,4 +8,4 @@ install:
- go get code.google.com/p/go.tools/cmd/vet
script:
- ./test
- INTEGRATION=y ./test

View File

@ -1,87 +0,0 @@
v0.4.6
* Fix long-term timer leak (#900, #875, #868, #904)
* Fix `Running` field in standby_info file (#881)
* Add `quorum=true` query parameter for GET requests (#866, #883)
* Add `Access-Control-Allow-Headers` header for CORS requests (#886)
* Various documentation improvements (#907, #882)
v0.4.5
* Flush headers immediatly on `wait=true` requests (#877)
* Add `ETCD_HTTP_READ_TIMEOUT` and `ETCD_HTTP_WRITE_TIMEOUT` (#880)
* Add `ETCDCTL_PEERS` configuration to etcdctl (#95)
* etcdctl takes stdin for mk (#91)
v0.4.4
* Fix `--no-sync` flag in etcdctl (#83)
* Improved logging for machine removal (#844)
* Various documentation improvements (#858, #851, #847)
v0.4.3
* Avoid panic() on truncated or unexpected log data (#834, #833)
* Fix missing stats field (#807)
* Lengthen default peer removal delay to 30mins (#835)
* Reduce logging on heartbeat timeouts (#836)
v0.4.2
* Improvements to the clustering documents
* Set content-type properly on errors (#469)
* Standbys re-join if they should be part of the cluster (#810, #815, #818)
v0.4.1
* Re-introduce DELETE on the machines endpoint
* Document the machines endpoint
v0.4.0
* Introduced standby mode
* Added HEAD requests
* Set logs NOCOW flag when BTRFS is detected to avoid fsync overhead
* Fix all known data races, and pass Go race detector (TODO: re-run race detector)
* Fixed timeouts when using HTTPS
* Improved snapshot stability
* Migration of machine names to new IPs
* Updated peer discovery ordering
v0.3.0
* Add Compare-and-Delete support.
* Added prevNode to response objects.
* Added Discovery API.
* Add tracing and debug endpoints (Documentation/debugging.md).
* Improved logging of cluster events.
* go get github.com/coreos/etcd works.
* info file is no longer used.
* Snapshots are on by default.
* Statistics APIs documented.
v0.2.0
* Support directory creation and removal.
* Add Compare-and-Swap (CAS) support.
* Support recursive GETs.
* Support fully consistent GETs.
* Allow clients to watch specific paths.
* Allow clients to watch for key expiration.
* Unique key generation.
* Support hidden paths.
* Refactor low-level data store.
* Modularize store, server and API code.
* Integrate Gorilla Web Toolkit.
* Add tiered configuration (command line args, env variables, config file).
* Add peer protocol versioning.
* Add rolling upgrade support for future versions.
* Sync key expiration across cluster.
* Significantly improve test coverage.
* Improve migration testing.
* Configurable snapshot count.
* Reduce TCP connection count.
* Fix TCP connection leak.
* Bug Fixes: https://github.com/coreos/etcd/issues?milestone=1&state=closed
Contributors:
* Xiang Li (@xiangli-cmu)
* Ben Johnson (@benbjohnson)
* Brandon Philips (@philips)
* Yifan (@yifan-gu)
* Rob Szumski
* Hongchao Deng (@fengjingchao)
* Kelsey Hightower (@kelseyhightower)
* Adrián (@adrianlzt)
* Antonio Terreno (@aterreno)

View File

@ -1,79 +0,0 @@
## Administration
### Data Directory
#### Lifecycle
When first started, etcd stores its configuration into a data directory specified by the data-dir configuration parameter.
Configuration is stored in the write ahead log and includes: the local member ID, cluster ID, and initial cluster configuration.
The write ahead log and snapshot files are used during member operation and to recover after a restart.
If a members data directory is ever lost or corrupted then the user should remove the etcd member from the cluster via the [members API][members-api].
A user should avoid restarting an etcd member with a data directory from an out-of-date backup.
Using an out-of-date data directory can lead to inconsistency as the member had agreed to store information via raft then re-joins saying it needs that information again.
For maximum safety, if an etcd member suffers any sort of data corruption or loss, it must be removed from the cluster.
Once removed the member can be re-added with an empty data directory.
[members-api]: https://github.com/coreos/etcd/blob/master/Documentation/0.5/other_apis.md#members-api
#### Contents
The data directory has two sub-directories in it:
1. wal: write ahead log files are stored here. For details see the [wal package documentation][wal-pkg]
2. snap: log snapshots are stored here. For details see the [snap package documentation][snap-pkg]
[wal-pkg]: http://godoc.org/github.com/coreos/etcd/wal
[snap-pkg]: http://godoc.org/github.com/coreos/etcd/snap
### Cluster Lifecycle
If you are spinning up multiple clusters for testing it is recommended that you specify a unique initial-cluster-token for the different clusters.
This can protect you from cluster corruption in case of mis-configuration because two members started with different cluster tokens will refuse members from each other.
### Disaster Recovery
etcd is designed to be resilient to machine failures. An etcd cluster can automatically recover from any number of temporary failures (for example, machine reboots), and a cluster of N members can tolerate up to _(N/2)-1_ permanent failures (where a member can no longer access the cluster, due to hardware failure or disk corruption). However, in extreme circumstances, a cluster might permanently lose enough members such that quorum is irrevocably lost. For example, if a three-node cluster suffered two simultaneous and unrecoverable machine failures, it would be normally impossible for the cluster to restore quorum and continue functioning.
To recover from such scenarios, etcd provides functionality to backup and restore the datastore and recreate the cluster without data loss.
#### Backing up the datastore
The first step of the recovery is to backup the data directory on a functioning etcd node. To do this, use the `etcdctl backup` command, passing in the original data directory used by etcd. For example:
```sh
etcdctl backup \
--data-dir /var/lib/etcd \
--backup-dir /tmp/etcd_backup
```
This command will rewrite some of the metadata contained in the backup (specifically, the node ID and cluster ID), which means that the node will lose its former identity. In order to recreate a cluster from the backup, you will need to start a new, single-node cluster. The metadata is rewritten to prevent the new node from inadvertently being joined onto an existing cluster.
#### Restoring a backup
To restore a backup using the procedure created above, start etcd with the `-force-new-cluster` option and pointing to the backup directory. This will initialize a new, single-member cluster with the default advertised peer URLs, but preserve the entire contents of the etcd data store. Continuing from the previous example:
```sh
etcd \
-data-dir=/tmp/etcd_backup \
-force-new-cluster \
...
```
Now etcd should be available on this node and serving the original datastore.
Once you have verified that etcd has started successfully, shut it down and move the data back to the previous location (you may wish to make another copy as well to be safe):
```sh
pkill etcd
rm -fr /var/lib/etcd
mv /tmp/etcd_backup /var/lib/etcd
etcd \
-data-dir=/var/lib/etcd \
...
```
#### Restoring the cluster
Now that the node is running successfully, you can add more nodes to the cluster and restore resiliency. See the [runtime configuration](runtime-configuration.md) guide for more details.

File diff suppressed because it is too large Load Diff

View File

@ -1,218 +0,0 @@
# Clustering Guide
This guide will walk you through configuring a three machine etcd cluster with the following details:
|Name |Address |
|-------|-----------|
|infra0 |10.0.1.10 |
|infra1 |10.0.1.11 |
|infra2 |10.0.1.12 |
## Static
As we know the cluster members, their addresses and the size of the cluster before starting, we can use an offline bootstrap configuration by setting the `initial-cluster` flag. Each machine will get either the following command line or environment variables:
```
ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380"
ETCD_INITIAL_CLUSTER_STATE=new
```
```
-initial-cluster infra0=http://10.0.1.10:2380,http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
-initial-cluster-state new
```
Note that the URLs specified in `initial-cluster` are the _advertised peer URLs_, i.e. they should match the value of `initial-advertise-peer-urls` on the respective nodes.
If you are spinning up multiple clusters (or creating and destroying a single cluster) with same configuration for testing purpose, it is highly recommended that you specify a unique `initial-cluster-token` for the different clusters. By doing this, etcd can generate unique cluster IDs and member IDs for the clusters even if they otherwise have the exact same configuration. This can protect you from cross-cluster-interaction, which might corrupt your clusters.
On each machine you would start etcd with these flags:
```
$ etcd -name infra0 -initial-advertise-peer-urls https://10.0.1.10:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
-initial-cluster-state new
```
```
$ etcd -name infra1 -initial-advertise-peer-urls https://10.0.1.11:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
-initial-cluster-state new
```
```
$ etcd -name infra2 -initial-advertise-peer-urls https://10.0.1.12:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
-initial-cluster-state new
```
The command line parameters starting with `-initial-cluster` will be ignored on subsequent runs of etcd. You are free to remove the environment variables or command line flags after the initial bootstrap process. If you need to make changes to the configuration later (for example, adding or removing members to/from the cluster), see the [runtime configuration](runtime-configuration.md) guide.
### Error Cases
In the following example, we have not included our new host in the list of enumerated nodes. If this is a new cluster, the node _must_ be added to the list of initial cluster members.
```
$ etcd -name infra1 -initial-advertise-peer-urls http://10.0.1.11:2380 \
-initial-cluster infra0=http://10.0.1.10:2380 \
-initial-cluster-state new
etcd: infra1 not listed in the initial cluster config
exit 1
```
In this example, we are attempting to map a node (infra0) on a different address (127.0.0.1:2380) than its enumerated address in the cluster list (10.0.1.10:2380). If this node is to listen on multiple addresses, all addresses _must_ be reflected in the "initial-cluster" configuration directive.
```
$ etcd -name infra0 -initial-advertise-peer-urls http://127.0.0.1:2380 \
-initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
-initial-cluster-state=new
etcd: error setting up initial cluster: infra0 has different advertised URLs in the cluster and advertised peer URLs list
exit 1
```
If you configure a peer with a different set of configuration and attempt to join this cluster you will get a cluster ID mismatch and etcd will exit.
```
$ etcd -name infra3 -initial-advertise-peer-urls http://10.0.1.13:2380 \
-initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra3=http://10.0.1.13:2380 \
-initial-cluster-state=new
etcd: conflicting cluster ID to the target cluster (c6ab534d07e8fcc4 != bc25ea2a74fb18b0). Exiting.
exit 1
```
## Discovery
In a number of cases, you might not know the IPs of your cluster peers ahead of time. This is common when utilizing cloud providers or when your network uses DHCP. In these cases, rather than specifying a static configuration, you can use an existing etcd cluster to bootstrap a new one. We call this process "discovery".
### Lifetime of a Discovery URL
A discovery URL identifies a unique etcd cluster. Instead of reusing a discovery URL, you should always create discovery URLs for new clusters.
Moreover, discovery URLs should ONLY be used for the initial bootstrapping of a cluster. To change cluster membership after the cluster is already running, see the [runtime reconfiguration][runtime] guide.
[runtime]: https://github.com/coreos/etcd/blob/master/Documentation/0.5/runtime-configuration.md
### Custom etcd discovery service
Discovery uses an existing cluster to bootstrap itself. If you are using your own etcd cluster you can create a URL like so:
```
$ curl -X PUT https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=3
```
By setting the size key to the URL, you create a discovery URL with an expected cluster size of 3.
If you bootstrap an etcd cluster using discovery service with more than the expected number of etcd members, the extra etcd processes will [fall back][fall-back] to being [proxies][proxy] by default.
The URL you will use in this case will be `https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83` and the etcd members will use the `https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83` directory for registration as they start.
Now we start etcd with those relevant flags for each member:
```
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \
-discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
```
```
$ etcd -name infra1 -initial-advertise-peer-urls http://10.0.1.11:2380 \
-discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
```
```
$ etcd -name infra2 -initial-advertise-peer-urls http://10.0.1.12:2380 \
-discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
```
This will cause each member to register itself with the custom etcd discovery service and begin the cluster once all machines have been registered.
### Public discovery service
If you do not have access to an existing cluster, you can use the public discovery service hosted at `discovery.etcd.io`. You can create a private discovery URL using the "new" endpoint like so:
```
$ curl https://discovery.etcd.io/new?size=3
https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
This will create the cluster with an initial expected size of 3 members. If you do not specify a size, a default of 3 will be used.
If you bootstrap an etcd cluster using discovery service with more than the expected number of etcd members, the extra etcd processes will [fall back][fall-back] to being [proxies][proxy] by default.
[fall-back]: proxy.md#fallback-to-proxy-mode-with-discovery-service
[proxy]: proxy.md
```
ETCD_DISCOVERY=https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
```
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
Now we start etcd with those relevant flags for each member:
```
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
```
$ etcd -name infra1 -initial-advertise-peer-urls http://10.0.1.11:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
```
$ etcd -name infra2 -initial-advertise-peer-urls http://10.0.1.12:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
This will cause each member to register itself with the discovery service and begin the cluster once all members have been registered.
You can use the environment variable `ETCD_DISCOVERY_PROXY` to cause etcd to use an HTTP proxy to connect to the discovery service.
### Error and Warning Cases
#### Discovery Server Errors
```
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
etcd: error: the cluster doesnt have a size configuration value in https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de/_config
exit 1
```
#### User Errors
This error will occur if the discovery cluster already has the configured number of members, and `discovery-fallback` is explicitly disabled
```
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de \
-discovery-fallback exit
etcd: discovery: cluster is full
exit 1
```
#### Warnings
This is a harmless warning notifying you that the discovery URL will be
ignored on this machine.
```
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
etcdserver: warn: ignoring discovery: etcd has already been initialized and has a valid log in /var/lib/etcd
```
# 0.4 to 0.5+ Migration Guide
In etcd 0.5 we introduced the ability to listen on more than one address and to advertise multiple addresses. This makes using etcd easier when you have complex networking, such as private and public networks on various cloud providers.
To make understanding this feature easier, we changed the naming of some flags, but we support the old flags to make the migration from the old to new version easier.
|Old Flag |New Flag |Migration Behavior |
|-----------------------|-----------------------|---------------------------------------------------------------------------------------|
|-peer-addr |-initial-advertise-peer-urls |If specified, peer-addr will be used as the only peer URL. Error if both flags specified.|
|-addr |-advertise-client-urls |If specified, addr will be used as the only client URL. Error if both flags specified.|
|-peer-bind-addr |-listen-peer-urls |If specified, peer-bind-addr will be used as the only peer bind URL. Error if both flags specified.|
|-bind-addr |-listen-client-urls |If specified, bind-addr will be used as the only client bind URL. Error if both flags specified.|
|-peers |none |Deprecated. The -initial-cluster flag provides a similar concept with different semantics. Please read this guide on cluster startup.|
|-peers-file |none |Deprecated. The -initial-cluster flag provides a similar concept with different semantics. Please read this guide on cluster startup.|

View File

@ -1,131 +0,0 @@
## Configuration Flags
etcd is configurable through command-line flags and environment variables. Options set on the command line take precedence over those from the environment.
The format of environment variable for flag `-my-flag` is `ENV_MY_FLAG`. It applies to all flags.
To start etcd automatically using custom settings at startup in Linux, using a [systemd][systemd-intro] unit is highly recommended.
[systemd-intro]: http://freedesktop.org/wiki/Software/systemd/
### Member Flags
##### -name
+ Human-readable name for this member.
+ default: "default"
##### -data-dir
+ Path to the data directory.
+ default: "${name}.etcd"
##### -snapshot-count
+ Number of committed transactions to trigger a snapshot to disk.
+ default: "10000"
##### -listen-peer-urls
+ List of URLs to listen on for peer traffic.
+ default: "http://localhost:2380,http://localhost:7001"
##### -listen-client-urls
+ List of URLs to listen on for client traffic.
+ default: "http://localhost:2379,http://localhost:4001"
##### -cors
+ Comma-separated white list of origins for CORS (cross-origin resource sharing).
+ default: none
### Clustering Flags
`-initial` prefix flags are used in bootstrapping ([static bootstrap][build-cluster], [discovery-service bootstrap][discovery] or [runtime reconfiguration][reconfig]) a new member, and ignored when restarting an existing member.
`-discovery` prefix flags need to be set when using [discovery service][discovery].
##### -initial-advertise-peer-urls
+ List of this member's peer URLs to advertise to the rest of the cluster. These addresses are used for communicating etcd data around the cluster. At least one must be routable to all cluster members.
+ default: "http://localhost:2380,http://localhost:7001"
##### -initial-cluster
+ Initial cluster configuration for bootstrapping.
+ default: "default=http://localhost:2380,default=http://localhost:7001"
##### initial-cluster-state
+ Initial cluster state ("new" or "existing").
+ default: "new"
##### initial-cluster-token
+ Initial cluster token for the etcd cluster during bootstrap.
+ default: "etcd-cluster"
##### advertise-client-urls
+ List of this member's client URLs to advertise to the rest of the cluster.
+ default: "http://localhost:2379,http://localhost:4001"
##### -discovery
+ Discovery URL used to bootstrap the cluster.
+ default: none
##### -discovery-fallback
+ Expected behavior ("exit" or "proxy") when discovery services fails.
+ default: "proxy"
##### -discovery-proxy
+ HTTP proxy to use for traffic to discovery service.
+ default: none
### Proxy Flags
`-proxy` prefix flags configures etcd to run in [proxy mode][proxy].
##### -proxy
+ Proxy mode setting ("off", "readonly" or "on").
+ default: "off"
### Security Flags
The security flags help to [build a secure etcd cluster][security].
##### -ca-file
+ Path to the client server TLS CA file.
+ default: none
##### -cert-file
+ Path to the client server TLS cert file.
+ default: none
##### -key-file
+ Path to the client server TLS key file.
+ default: none
##### -peer-ca-file
+ Path to the peer server TLS CA file.
+ default: none
##### -peer-cert-file
+ Path to the peer server TLS cert file.
+ default: none
##### -peer-key-file
+ Path to the peer server TLS key file.
+ default: none
### Unsafe Flags
Be CAUTIOUS to use unsafe flags because it will break the guarantee given by consensus protocol. For example, it may panic if other members in the cluster are still alive. Follow the instructions when using these falgs.
##### -force-new-cluster
+ Force to create a new one-member cluster. It commits configuration changes in force to remove all existing members in the cluster and add itself. It needs to be set to [restore a backup][restore].
+ default: false
### Miscellaneous Flags
##### -version
+ Print the version and exit.
+ default: false
[build-cluster]: https://github.com/coreos/etcd/blob/master/Documentation/0.5/clustering.md#static
[reconfig]: https://github.com/coreos/etcd/blob/master/Documentation/0.5/runtime-configuration.md
[discovery]: https://github.com/coreos/etcd/blob/master/Documentation/0.5/clustering.md#discovery
[proxy]: https://github.com/coreos/etcd/blob/master/Documentation/0.5/proxy.md
[security]: https://github.com/coreos/etcd/blob/master/Documentation/security.md
[restore]: https://github.com/coreos/etcd/blob/master/Documentation/0.5/admin_guide.md#restoring-a-backup

View File

@ -1,14 +1,14 @@
## etcd 0.4.x -> 0.5.0 Data Migration Tool
## etcd 0.4.x -> 2.0.0 Data Migration Tool
### Upgrading from 0.4.x
Between 0.4.x and 0.5, the on-disk data formats have changed. In order to allow users to convert to 0.5, a migration tool is provided.
Between 0.4.x and 2.0, the on-disk data formats have changed. In order to allow users to convert to 2.0, a migration tool is provided.
In the early 0.5.0-alpha series, we're providing this tool early to encourage adoption. However, before 0.5.0-release, etcd will autodetect the 0.4.x data dir upon upgrade and automatically update the data too (while leaving a backup, in case of emergency).
etcd will detect 0.4.x data dir and update the data automatically (while leaving a backup, in case of emergency).
### Data Migration Tips
* Keep the environment variables and etcd instance flags the same (much as [the upgrade document](../upgrade.md) suggests), particularly `--name`/`ETCD_NAME`.
* Keep the environment variables and etcd instance flags the same, particularly `--name`/`ETCD_NAME`.
* Don't change the cluster configuration. If there's a plan to add or remove machines, it's probably best to arrange for that after the migration, rather than before or at the same time.
### Running the tool
@ -18,7 +18,7 @@ The tool can be run via:
./bin/etcd-migrate --data-dir=<PATH TO YOUR DATA>
```
It should autodetect everything and convert the data-dir to be 0.5 compatible. It does not remove the 0.4.x data, and is safe to convert multiple times; the 0.5 data will be overwritten. Recovering the disk space once everything is settled is covered later in the document.
It should autodetect everything and convert the data-dir to be 2.0 compatible. It does not remove the 0.4.x data, and is safe to convert multiple times; the 2.0 data will be overwritten. Recovering the disk space once everything is settled is covered later in the document.
If, however, it complains about autodetecting the name (which can happen, depending on how the cluster was configured), you need to supply the name of this particular node. This is equivalent to the `--name` flag (or `ETCD_NAME` variable) that etcd was run with, which can also be found by accessing the self api, eg:
@ -38,10 +38,10 @@ And the tool should migrate successfully. If it still has an error at this time,
### Recovering Disk Space
If the conversion has completed, the entire cluster is running on something 0.5-based, and the disk space is important, the following command will clear 0.4.x data from the data-dir:
If the conversion has completed, the entire cluster is running on something 2.0-based, and the disk space is important, the following command will clear 0.4.x data from the data-dir:
```sh
rm -ri snapshot conf log
```
It will ask before every deletion, but these are the 0.4.x files and will not affect the working 0.5 data.
It will ask before every deletion, but these are the 0.4.x files and will not affect the working 2.0 data.

View File

@ -0,0 +1,205 @@
## Administration
### Data Directory
#### Lifecycle
When first started, etcd stores its configuration into a data directory specified by the data-dir configuration parameter.
Configuration is stored in the write ahead log and includes: the local member ID, cluster ID, and initial cluster configuration.
The write ahead log and snapshot files are used during member operation and to recover after a restart.
If a members data directory is ever lost or corrupted then the user should remove the etcd member from the cluster via the [members API][members-api].
A user should avoid restarting an etcd member with a data directory from an out-of-date backup.
Using an out-of-date data directory can lead to inconsistency as the member had agreed to store information via raft then re-joins saying it needs that information again.
For maximum safety, if an etcd member suffers any sort of data corruption or loss, it must be removed from the cluster.
Once removed the member can be re-added with an empty data directory.
[members-api]: https://github.com/coreos/etcd/blob/master/Documentation/other_apis.md#members-api
#### Contents
The data directory has two sub-directories in it:
1. wal: write ahead log files are stored here. For details see the [wal package documentation][wal-pkg]
2. snap: log snapshots are stored here. For details see the [snap package documentation][snap-pkg]
[wal-pkg]: http://godoc.org/github.com/coreos/etcd/wal
[snap-pkg]: http://godoc.org/github.com/coreos/etcd/snap
### Cluster Management
#### Lifecycle
If you are spinning up multiple clusters for testing it is recommended that you specify a unique initial-cluster-token for the different clusters.
This can protect you from cluster corruption in case of mis-configuration because two members started with different cluster tokens will refuse members from each other.
#### Optimal Cluster Size
The recommended etcd cluster size is 3, 5 or 7, which is decided by the fault tolerance requirement. A 7-member cluster can provide enough fault tolerance in most cases. While larger cluster provides better fault tolerance, its write performance becomes lower since data needs to be replicated to more machines.
#### Fault Tolerance Table
It is recommended to have an odd number of members in a cluster. Having an odd cluster size doesn't change the number needed for majority, but you gain a higher tolerance for failure by adding the extra member. You can see this in practice when comparing even and odd sized clusters:
| Cluster Size | Majority | Failure Tolerance |
|--------------|------------|-------------------|
| 1 | 1 | 0 |
| 3 | 2 | 1 |
| 4 | 3 | 1 |
| 5 | 3 | **2** |
| 6 | 4 | 2 |
| 7 | 4 | **3** |
| 8 | 5 | 3 |
| 9 | 5 | **4** |
As you can see, adding another member to bring the size of cluster up to an odd size is always worth it. During a network partition, an odd number of members also guarantees that there will almost always be a majority of the cluster that can continue to operate and be the source of truth when the partition ends.
### Member Migration
When there is a scheduled machine maintenance or retirement, you might want to migrate an etcd member to another machine without losing the data and changing the member ID.
The data directory contains all the data to recover a member to its point-in-time state. To migrate a member:
* Stop the member process
* Copy the data directory of the now-idle member to the new machine
* Update the peer URLs for that member to reflect the new machine according to the [member api] [change peer url]
* Start etcd on the new machine, using the same configuration and the copy of the data directory
This example will walk you through the process of migrating the infra1 member to a new machine:
|Name|Peer URL|
|------|--------------|
|infra0|10.0.1.10:2380|
|infra1|10.0.1.11:2380|
|infra2|10.0.1.12:2380|
```
$ export ETCDCTL_PEERS=http://10.0.1.10:2379,http://10.0.1.11:2379,http://10.0.1.12:2379
```
```
$ etcdctl member list
84194f7c5edd8b37: name=infra0 peerURLs=http://10.0.1.10:2380 clientURLs=http://127.0.0.1:2379,http://10.0.1.10:2379
b4db3bf5e495e255: name=infra1 peerURLs=http://10.0.1.11:2380 clientURLs=http://127.0.0.1:2379,http://10.0.1.11:2379
bc1083c870280d44: name=infra2 peerURLs=http://10.0.1.12:2380 clientURLs=http://127.0.0.1:2379,http://10.0.1.12:2379
```
#### Stop the member etcd process
```
$ ssh core@10.0.1.11
```
```
$ sudo systemctl stop etcd
```
#### Copy the data directory of the now-idle member to the new machine
```
$ tar -cvzf node1.etcd.tar.gz /var/lib/etcd/node1.etcd
```
```
$ scp node1.etcd.tar.gz core@10.0.1.13:~/
```
#### Update the peer URLs for that member to reflect the new machine
```
$ curl http://10.0.1.10:2379/v2/members/b4db3bf5e495e255 -XPUT \
-H "Content-Type: application/json" -d '{"peerURLs":["http://10.0.1.13:2380"]}'
```
#### Start etcd on the new machine, using the same configuration and the copy of the data directory
```
$ ssh core@10.0.1.13
```
```
$ tar -xzvf node1.etcd.tar.gz -C /var/lib/etcd
```
```
etcd -name node1 \
-listen-peer-urls http://10.0.1.13:2380 \
-listen-client-urls http://10.0.1.13:2379,http://127.0.0.1:2379 \
-advertise-client-urls http://10.0.1.13:2379,http://127.0.0.1:2379
```
[change peer url]: https://github.com/coreos/etcd/blob/master/Documentation/other_apis.md#change-the-peer-urls-of-a-member
### Disaster Recovery
etcd is designed to be resilient to machine failures. An etcd cluster can automatically recover from any number of temporary failures (for example, machine reboots), and a cluster of N members can tolerate up to _(N/2)-1_ permanent failures (where a member can no longer access the cluster, due to hardware failure or disk corruption). However, in extreme circumstances, a cluster might permanently lose enough members such that quorum is irrevocably lost. For example, if a three-node cluster suffered two simultaneous and unrecoverable machine failures, it would be normally impossible for the cluster to restore quorum and continue functioning.
To recover from such scenarios, etcd provides functionality to backup and restore the datastore and recreate the cluster without data loss.
#### Backing up the datastore
**NB:** Windows users must stop etcd before running the backup command.
The first step of the recovery is to backup the data directory on a functioning etcd node. To do this, use the `etcdctl backup` command, passing in the original data directory used by etcd. For example:
```sh
etcdctl backup \
--data-dir /var/lib/etcd \
--backup-dir /tmp/etcd_backup
```
This command will rewrite some of the metadata contained in the backup (specifically, the node ID and cluster ID), which means that the node will lose its former identity. In order to recreate a cluster from the backup, you will need to start a new, single-node cluster. The metadata is rewritten to prevent the new node from inadvertently being joined onto an existing cluster.
#### Restoring a backup
To restore a backup using the procedure created above, start etcd with the `-force-new-cluster` option and pointing to the backup directory. This will initialize a new, single-member cluster with the default advertised peer URLs, but preserve the entire contents of the etcd data store. Continuing from the previous example:
```sh
etcd \
-data-dir=/tmp/etcd_backup \
-force-new-cluster \
...
```
Now etcd should be available on this node and serving the original datastore.
Once you have verified that etcd has started successfully, shut it down and move the data back to the previous location (you may wish to make another copy as well to be safe):
```sh
pkill etcd
rm -fr /var/lib/etcd
mv /tmp/etcd_backup /var/lib/etcd
etcd \
-data-dir=/var/lib/etcd \
...
```
#### Restoring the cluster
Now that the node is running successfully, you can add more nodes to the cluster and restore resiliency. See the [runtime configuration](runtime-configuration.md) guide for more details.
### Client Request Timeout
etcd sets different timeouts for various types of client requests. The timeout value is not tunable now, which will be improved soon(https://github.com/coreos/etcd/issues/2038).
#### Get requests
Timeout is not set for get requests, because etcd serves the result locally in a non-blocking way.
**Note**: QuorumGet request is a different type, which is mentioned in the following sections.
#### Watch requests
Timeout is not set for watch requests. etcd will not stop a watch request until client cancels it, or the connection is broken.
#### Delete, Put, Post, QuorumGet requests
The default timeout is 5 seconds. It should be large enough to allow all key modifications if the majority of cluster is functioning.
If the request times out, it indicates two possibilities:
1. the server the request sent to was not functioning at that time.
2. the majority of the cluster is not functioning.
If timeout happens several times continuously, administrators should check status of cluster and resolve it as soon as possible.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,55 @@
### Backward Compatibility
The main goal of etcd 2.0 release is to improve cluster safety around bootstrapping and dynamic reconfiguration. To do this, we deprecated the old error-prone APIs and provide a new set of APIs.
The other main focus of this release was a more reliable Raft implementation, but as this change is internal it should not have any notable effects to users.
#### Command Line Flags Changes
The major flag changes are to mostly related to bootstrapping. The `initial-*` flags provide an improved way to specify the required criteria to start the cluster. The advertised URLs now support a list of values instead of a single value, which allows etcd users to gracefully migrate to the new set of IANA-assigned ports (2379/client and 2380/peers) while maintaining backward compatibility with the old ports.
- `-addr` is replaced by `-advertise-client-urls`.
- `-bind-addr` is replaced by `-listen-client-urls`.
- `-peer-addr` is replaced by `-initial-advertise-peer-urls`.
- `-peer-bind-addr` is replaced by `-listen-peer-urls`.
- `-peers` is replaced by `-initial-cluster`.
- `-peers-file` is replaced by `-initial-cluster`.
- `-peer-heartbeat-interval` is replaced by `-heartbeat-interval`.
- `-peer-election-timeout` is replaced by `-election-timeout`.
The documentation of new command line flags can be found at
https://github.com/coreos/etcd/blob/master/Documentation/configuration.md.
#### Data Dir
- Default data dir location has changed from {$hostname}.etcd to {name}.etcd.
- The disk format within the data dir has changed. etcd 2.0 should be able to auto upgrade the old data format. Instructions on doing so manually are in the [migration tool doc][migrationtooldoc].
[migrationtooldoc]: https://github.com/coreos/etcd/blob/master/Documentation/0_4_migration_tool.md
#### Standby
etcd 0.4s standby mode has been deprecated. [Proxy mode][proxymode] is introduced to solve a subset of problems standby was solving.
Standby mode was intended for large clusters that had a subset of the members acting in the consensus process. Overall this process was too magical and allowed for operators to back themselves into a corner.
Proxy mode in 2.0 will provide similar functionality, and with improved control over which machines act as proxies due to the operator specifically configuring them. Proxies also support read only or read/write modes for increased security and durability.
[proxymode]: https://github.com/coreos/etcd/blob/master/Documentation/proxy.md
#### Discovery Service
A size key needs to be provided inside a [discovery token][discoverytoken].
[discoverytoken]: https://github.com/coreos/etcd/blob/master/Documentation/clustering.md#custom-etcd-discovery-service
#### HTTP Admin API
`v2/admin` on peer url and `v2/keys/_etcd` are unified under the new [v2/member API][memberapi] to better explain which machines are part of an etcd cluster, and to simplify the keyspace for all your use cases.
[memberapi]: https://github.com/coreos/etcd/blob/master/Documentation/other_apis.md
#### HTTP Key Value API
- The follower can now transparently proxy write equests to the leader. Clients will no longer see 307 redirections to the leader from etcd.
- Expiration time is in UTC instead of local time.

View File

@ -1,43 +0,0 @@
# Client libraries support matrix for etcd
As etcd features support is really uneven between client libraries, a compatibility matrix can be important.
## v2 clients
The v2 API has a lot of features, we will categorize them in a few categories:
- **Language**: The language in which the client library was written.
- **HTTPS Auth**: Support for SSL-certificate based authentication
- **Reconnect**: If the client is able to reconnect automatically to another server if one fails.
- **Mod/Lock**: Support for the locking module
- **Mod/Leader**: Support for the leader election module
- **GET,PUT,POST,DEL Features**: Support for all the modifiers when calling the etcd server with said HTTP method.
### Supported features matrix
**Legend**
**F**: Full support **G**: Good support **B**: Basic support
**Y**: Feature supported **-**: Feature not supported
Sorted alphabetically on language/name
|Client |**Language**|**HTTPS Auth**|**Re-connect**|**GET**|**PUT**|**POST**|**DEL**|**Mod Lock**|**Mod Leader**|
| --- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|[etcd-api](https://github.com/jdarcy/etcd-api) |C |-|Y|B|G|-|B|-|-|
|[etcdcpp](https://github.com/edwardcapriolo/etcdcpp) |C++ |-|-|F|F|G|-|-|-|
|[cetcd](https://github.com/dwwoelfel/cetcd) |Clojure|-|-|F|F|-|G|-|-|
|[clj-etcd](https://github.com/rthomas/clj-etcd) |Clojure|-|-|G|G|-|B|-|-|
|[etcd-clojure](https://github.com/aterreno/etcd-clojure) |Clojure|-|-|F|F|F|F|-|-|
|[go-etcd](https://github.com/coreos/go-etcd) |go |Y|Y|F|F|F|F|-|-|
|[boon etcd client](https://github.com/boonproject/boon/blob/master/etcd/README.md) |java |Y|Y|F|F|F|F|-|F|
|[etcd4j](https://github.com/jurmous/etcd4j) |java |Y|Y|F|F|F|F|-|-|
|[jetcd](https://github.com/diwakergupta/jetcd) |java |Y|-|B|B|-|B|-|-|
|[jetcd](https://github.com/justinsb/jetcd) |java |-|-|B|B|-|B|-|-|
|[Etcd.jl](https://github.com/forio/Etcd.jl) |Julia |-|-|F|F|F|F|Y|Y|
|[etcetera](https://github.com/drusellers/etcetera) |.net |-|-|F|F|F|F|-|-|
|[node-etcd](https://github.com/stianeikeland/node-etcd) |nodejs |Y|-|F|F|-|F|-|-|
|[nodejs-etcd](https://github.com/lavagetto/nodejs-etcd) |nodejs |Y|-|F|F|F|F|-|-|
|[p5-etcd](https://metacpan.org/release/Etcd) |perl |-|-|F|F|F|F|-|-|
|[python-etcd](https://github.com/jplana/python-etcd) |python |Y|Y|F|F|F|F|Y|-|
|[python-etcd-client](https://github.com/dsoprea/PythonEtcdClient)|python |Y|Y|F|F|F|F|Y|Y|
|[txetcd](https://github.com/russellhaering/txetcd) |python |-|-|G|G|F|G|-|-|
|[etcd-ruby](https://github.com/ranjib/etcd-ruby) |ruby |-|-|F|F|F|F|-|-|

View File

@ -1,60 +0,0 @@
# Cluster Discovery
## Overview
Starting an etcd cluster requires that each node knows another in the cluster. If you are trying to bring up a cluster all at once, say using a cloud formation, you also need to coordinate who will be the initial cluster leader. The discovery protocol helps you by providing an automated way to discover other existing peers in a cluster.
For more information on how etcd can locate the cluster, see the [finding the cluster][cluster-finding] documentation.
Please note - at least 3 nodes are required for [cluster availability][optimal-cluster-size].
[cluster-finding]: https://github.com/coreos/etcd/blob/master/Documentation/design/cluster-finding.md
[optimal-cluster-size]: https://github.com/coreos/etcd/blob/master/Documentation/optimal-cluster-size.md
## Using discovery.etcd.io
### Create a Discovery URL
To use the discovery API, you must first create a unique discovery URL for your etcd cluster. Visit [https://discovery.etcd.io/new](https://discovery.etcd.io/new) to create a new discovery URL.
You can inspect the list of peers by viewing `https://discovery.etcd.io/<cluster id>`.
### Start etcd With the Discovery Flag
Specify the `-discovery` flag when you start each etcd instance. The list of existing peers in the cluster will be downloaded and configured. If the instance is the first peer, it will start as the leader of the cluster.
Here's a full example:
```
URL=$(curl https://discovery.etcd.io/new)
./etcd -name instance1 -peer-addr 10.1.2.3:7001 -addr 10.1.2.3:4001 -discovery $URL
./etcd -name instance2 -peer-addr 10.1.2.4:7001 -addr 10.1.2.4:4001 -discovery $URL
./etcd -name instance3 -peer-addr 10.1.2.5:7001 -addr 10.1.2.5:4001 -discovery $URL
```
## Running Your Own Discovery Endpoint
The discovery API communicates with a separate etcd cluster to store and retrieve the list of peers. CoreOS provides [https://discovery.etcd.io](https://discovery.etcd.io) as a free service, but you can easily run your own etcd cluster for this purpose. Here's an example using an etcd cluster located at `10.10.10.10:4001`:
```
URL="http://10.10.10.10:4001/v2/keys/testcluster"
./etcd -name instance1 -peer-addr 10.1.2.3:7001 -addr 10.1.2.3:4001 -discovery $URL
./etcd -name instance2 -peer-addr 10.1.2.4:7001 -addr 10.1.2.4:4001 -discovery $URL
./etcd -name instance3 -peer-addr 10.1.2.5:7001 -addr 10.1.2.5:4001 -discovery $URL
```
If you're interested in how to discovery API works behind the scenes, read about the [Discovery Protocol](https://github.com/coreos/etcd/blob/master/Documentation/discovery-protocol.md).
## Setting Peer Addresses Correctly
The Discovery API submits the `-peer-addr` of each etcd instance to the configured Discovery endpoint. It's important to select an address that *all* peers in the cluster can communicate with. For example, if you're located in two regions of a cloud provider, configuring a private `10.x` address will not work between the two regions, and communication will not be possible between all peers.
## Stale Peers
The discovery API will automatically clean up the address of a stale peer that is no longer part of the cluster. The TTL for this process is a week, which should be long enough to handle any extremely long outage you may encounter. There is no harm in having stale peers in the list until they are cleaned up, since an etcd instance only needs to connect to one valid peer in the cluster to join.
## Lifetime of a Discovery URL
A discovery URL identifies a single etcd cluster. Do not re-use discovery URLs for new clusters.
When a machine starts with a new discovery URL the discovery URL will be activated and record the machine's metadata. If you destroy the whole cluster and attempt to bring the cluster back up with the same discovery URL it will fail. This is intentional because all of the registered machines are gone including their logs so there is nothing to recover the killed cluster.

View File

@ -1,175 +1,356 @@
## Clustering
# Clustering Guide
### Example cluster of three machines
## Overview
Let's explore the use of etcd clustering.
We use Raft as the underlying distributed protocol which provides consistency and persistence of the data across all of the etcd instances.
Starting an etcd cluster statically requires that each member knows another in the cluster. In a number of cases, you might not know the IPs of your cluster members ahead of time. In these cases, you can bootstrap an etcd cluster with the help of a discovery service.
Let start by creating 3 new etcd instances.
This guide willcover the following mechanisms for bootstrapping an etcd cluster:
We use `-peer-addr` to specify server port and `-addr` to specify client port and `-data-dir` to specify the directory to store the log and info of the machine in the cluster:
* [Static](#static)
* [etcd Discovery](#etcd-discovery)
* [DNS Discovery](#dns-discovery)
```sh
./etcd -peer-addr 127.0.0.1:7001 -addr 127.0.0.1:4001 -data-dir machines/machine1 -name machine1
```
Each of the bootstrapping mechanisms will be used to create a three machine etcd cluster with the following details:
**Note:** If you want to run etcd on an external IP address and still have access locally, you'll need to add `-bind-addr 0.0.0.0` so that it will listen on both external and localhost addresses.
A similar argument `-peer-bind-addr` is used to setup the listening address for the server port.
|Name|Address|Hostname|
|------|---------|------------------|
|infra0|10.0.1.10|infra0.example.com|
|infra1|10.0.1.11|infra1.example.com|
|infra2|10.0.1.12|infra2.example.com|
Let's join two more machines to this cluster using the `-peers` argument. A single connection to any peer will allow a new machine to join, but multiple can be specified for greater resiliency.
## Static
```sh
./etcd -peer-addr 127.0.0.1:7002 -addr 127.0.0.1:4002 -peers 127.0.0.1:7001,127.0.0.1:7003 -data-dir machines/machine2 -name machine2
./etcd -peer-addr 127.0.0.1:7003 -addr 127.0.0.1:4003 -peers 127.0.0.1:7001,127.0.0.1:7002 -data-dir machines/machine3 -name machine3
```
We can retrieve a list of machines in the cluster using the HTTP API:
```sh
curl -L http://127.0.0.1:4001/v2/machines
```
We should see there are three machines in the cluster
As we know the cluster members, their addresses and the size of the cluster before starting, we can use an offline bootstrap configuration by setting the `initial-cluster` flag. Each machine will get either the following command line or environment variables:
```
http://127.0.0.1:4001, http://127.0.0.1:4002, http://127.0.0.1:4003
```
The machine list is also available via the main key API:
```sh
curl -L http://127.0.0.1:4001/v2/keys/_etcd/machines
```
```json
{
"action": "get",
"node": {
"createdIndex": 1,
"dir": true,
"key": "/_etcd/machines",
"modifiedIndex": 1,
"nodes": [
{
"createdIndex": 1,
"key": "/_etcd/machines/machine1",
"modifiedIndex": 1,
"value": "raft=http://127.0.0.1:7001&etcd=http://127.0.0.1:4001"
},
{
"createdIndex": 2,
"key": "/_etcd/machines/machine2",
"modifiedIndex": 2,
"value": "raft=http://127.0.0.1:7002&etcd=http://127.0.0.1:4002"
},
{
"createdIndex": 3,
"key": "/_etcd/machines/machine3",
"modifiedIndex": 3,
"value": "raft=http://127.0.0.1:7003&etcd=http://127.0.0.1:4003"
}
]
}
}
```
We can also get the current leader in the cluster:
```
curl -L http://127.0.0.1:4001/v2/leader
```
The first server we set up should still be the leader unless it has died during these commands.
```
http://127.0.0.1:7001
```
Now we can do normal SET and GET operations on keys as we explored earlier.
```sh
curl -L http://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar
```
```json
{
"action": "set",
"node": {
"createdIndex": 4,
"key": "/foo",
"modifiedIndex": 4,
"value": "bar"
}
}
```
### Rejoining to the Cluster
If one machine disconnects from the cluster, it could rejoin the cluster automatically when the communication is recovered.
If one machine is killed, it could rejoin the cluster when started with old name. If the peer address is changed, etcd will treat the new peer address as the refreshed one, which benefits instance migration, or virtual machine boot with different IP. The peer-address-changing functionality is only supported when the majority of the cluster is alive, because this behavior needs the consensus of the etcd cluster.
**Note:** For now, it is user responsibility to ensure that the machine doesn't join the cluster that has the member with the same name. Or unexpected error will happen. It would be improved sooner or later.
### Killing Nodes in the Cluster
Now if we kill the leader of the cluster, we can get the value from one of the other two machines:
```sh
curl -L http://127.0.0.1:4002/v2/keys/foo
```
We can also see that a new leader has been elected:
```
curl -L http://127.0.0.1:4002/v2/leader
ETCD_INITIAL_CLUSTER="infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380"
ETCD_INITIAL_CLUSTER_STATE=new
```
```
http://127.0.0.1:7002
-initial-cluster infra0=http://10.0.1.10:2380,http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
-initial-cluster-state new
```
or
Note that the URLs specified in `initial-cluster` are the _advertised peer URLs_, i.e. they should match the value of `initial-advertise-peer-urls` on the respective nodes.
If you are spinning up multiple clusters (or creating and destroying a single cluster) with same configuration for testing purpose, it is highly recommended that you specify a unique `initial-cluster-token` for the different clusters. By doing this, etcd can generate unique cluster IDs and member IDs for the clusters even if they otherwise have the exact same configuration. This can protect you from cross-cluster-interaction, which might corrupt your clusters.
On each machine you would start etcd with these flags:
```
http://127.0.0.1:7003
$ etcd -name infra0 -initial-advertise-peer-urls https://10.0.1.10:2380 \
-listen-peer-urls https://10.0.1.10:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
-initial-cluster-state new
```
```
$ etcd -name infra1 -initial-advertise-peer-urls https://10.0.1.11:2380 \
-listen-peer-urls https://10.0.1.11:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
-initial-cluster-state new
```
```
$ etcd -name infra2 -initial-advertise-peer-urls https://10.0.1.12:2380 \
-listen-peer-urls https://10.0.1.12:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
-initial-cluster-state new
```
The command line parameters starting with `-initial-cluster` will be ignored on subsequent runs of etcd. You are free to remove the environment variables or command line flags after the initial bootstrap process. If you need to make changes to the configuration later (for example, adding or removing members to/from the cluster), see the [runtime configuration](runtime-configuration.md) guide.
### Testing Persistence
### Error Cases
Next we'll kill all the machines to test persistence.
Type `CTRL-C` on each terminal and then rerun the same command you used to start each machine.
In the following example, we have not included our new host in the list of enumerated nodes. If this is a new cluster, the node _must_ be added to the list of initial cluster members.
Your request for the `foo` key will return the correct value:
```sh
curl -L http://127.0.0.1:4002/v2/keys/foo
```
$ etcd -name infra1 -initial-advertise-peer-urls http://10.0.1.11:2380 \
-listen-peer-urls https://10.0.1.11:2380 \
-initial-cluster infra0=http://10.0.1.10:2380 \
-initial-cluster-state new
etcd: infra1 not listed in the initial cluster config
exit 1
```
```json
{
"action": "get",
"node": {
"createdIndex": 4,
"key": "/foo",
"modifiedIndex": 4,
"value": "bar"
}
}
In this example, we are attempting to map a node (infra0) on a different address (127.0.0.1:2380) than its enumerated address in the cluster list (10.0.1.10:2380). If this node is to listen on multiple addresses, all addresses _must_ be reflected in the "initial-cluster" configuration directive.
```
$ etcd -name infra0 -initial-advertise-peer-urls http://127.0.0.1:2380 \
-listen-peer-urls http://10.0.1.10:2380 \
-initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra2=http://10.0.1.12:2380 \
-initial-cluster-state=new
etcd: error setting up initial cluster: infra0 has different advertised URLs in the cluster and advertised peer URLs list
exit 1
```
If you configure a peer with a different set of configuration and attempt to join this cluster you will get a cluster ID mismatch and etcd will exit.
### Using HTTPS between servers
```
$ etcd -name infra3 -initial-advertise-peer-urls http://10.0.1.13:2380 \
-listen-peer-urls http://10.0.1.13:2380 \
-initial-cluster infra0=http://10.0.1.10:2380,infra1=http://10.0.1.11:2380,infra3=http://10.0.1.13:2380 \
-initial-cluster-state=new
etcd: conflicting cluster ID to the target cluster (c6ab534d07e8fcc4 != bc25ea2a74fb18b0). Exiting.
exit 1
```
In the previous example we showed how to use SSL client certs for client-to-server communication.
Etcd can also do internal server-to-server communication using SSL client certs.
To do this just change the `-*-file` flags to `-peer-*-file`.
## Discovery
If you are using SSL for server-to-server communication, you must use it on all instances of etcd.
In a number of cases, you might not know the IPs of your cluster peers ahead of time. This is common when utilizing cloud providers or when your network uses DHCP. In these cases, rather than specifying a static configuration, you can use an existing etcd cluster to bootstrap a new one. We call this process "discovery".
### Bootstrapping a new cluster by name
There two methods that can be used for discovery:
An etcd server is uniquely defined by the peer addresses it listens to. Suppose, however, that you wish to start over, while maintaining the data from the previous cluster -- that is, to pretend that this machine has never joined a cluster before.
* etcd discovery service
* DNS SRV records
You can use `--initial-cluster-name` to generate a new unique ID for each node, as a shared token that every node understands. Nodes also take this into account for bootstrapping the new cluster ID, so it also provides a way for a machine to listen on the same interfaces, disconnect from one cluster, and join a different cluster.
### etcd Discovery
#### Lifetime of a Discovery URL
A discovery URL identifies a unique etcd cluster. Instead of reusing a discovery URL, you should always create discovery URLs for new clusters.
Moreover, discovery URLs should ONLY be used for the initial bootstrapping of a cluster. To change cluster membership after the cluster is already running, see the [runtime reconfiguration][runtime] guide.
[runtime]: https://github.com/coreos/etcd/blob/master/Documentation/runtime-configuration.md
#### Custom etcd Discovery Service
Discovery uses an existing cluster to bootstrap itself. If you are using your own etcd cluster you can create a URL like so:
```
$ curl -X PUT https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83/_config/size -d value=3
```
By setting the size key to the URL, you create a discovery URL with an expected cluster size of 3.
If you bootstrap an etcd cluster using discovery service with more than the expected number of etcd members, the extra etcd processes will [fall back][fall-back] to being [proxies][proxy] by default.
The URL you will use in this case will be `https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83` and the etcd members will use the `https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83` directory for registration as they start.
Now we start etcd with those relevant flags for each member:
```
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \
-listen-peer-urls http://10.0.1.10:2380 \
-discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
```
```
$ etcd -name infra1 -initial-advertise-peer-urls http://10.0.1.11:2380 \
-listen-peer-urls http://10.0.1.11:2380 \
-discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
```
```
$ etcd -name infra2 -initial-advertise-peer-urls http://10.0.1.12:2380 \
-listen-peer-urls http://10.0.1.12:2380 \
-discovery https://myetcd.local/v2/keys/discovery/6c007a14875d53d9bf0ef5a6fc0257c817f0fb83
```
This will cause each member to register itself with the custom etcd discovery service and begin the cluster once all machines have been registered.
#### Public etcd Discovery Service
If you do not have access to an existing cluster, you can use the public discovery service hosted at `discovery.etcd.io`. You can create a private discovery URL using the "new" endpoint like so:
```
$ curl https://discovery.etcd.io/new?size=3
https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
This will create the cluster with an initial expected size of 3 members. If you do not specify a size, a default of 3 will be used.
If you bootstrap an etcd cluster using discovery service with more than the expected number of etcd members, the extra etcd processes will [fall back][fall-back] to being [proxies][proxy] by default.
[fall-back]: proxy.md#fallback-to-proxy-mode-with-discovery-service
[proxy]: proxy.md
```
ETCD_DISCOVERY=https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
```
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
Now we start etcd with those relevant flags for each member:
```
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \
-listen-peer-urls http://10.0.1.10:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
```
$ etcd -name infra1 -initial-advertise-peer-urls http://10.0.1.11:2380 \
-listen-peer-urls http://10.0.1.11:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
```
$ etcd -name infra2 -initial-advertise-peer-urls http://10.0.1.12:2380 \
-listen-peer-urls http://10.0.1.12:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
```
This will cause each member to register itself with the discovery service and begin the cluster once all members have been registered.
You can use the environment variable `ETCD_DISCOVERY_PROXY` to cause etcd to use an HTTP proxy to connect to the discovery service.
#### Error and Warning Cases
##### Discovery Server Errors
```
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \
-listen-peer-urls http://10.0.1.10:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
etcd: error: the cluster doesnt have a size configuration value in https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de/_config
exit 1
```
##### User Errors
This error will occur if the discovery cluster already has the configured number of members, and `discovery-fallback` is explicitly disabled
```
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \
-listen-peer-urls http://10.0.1.10:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de \
-discovery-fallback exit
etcd: discovery: cluster is full
exit 1
```
##### Warnings
This is a harmless warning notifying you that the discovery URL will be
ignored on this machine.
```
$ etcd -name infra0 -initial-advertise-peer-urls http://10.0.1.10:2380 \
-listen-peer-urls http://10.0.1.10:2380 \
-discovery https://discovery.etcd.io/3e86b59982e49066c5d813af1c2e2579cbf573de
etcdserver: discovery token ignored since a cluster has already been initialized. Valid log found at /var/lib/etcd
```
### DNS Discovery
DNS [SRV records](http://www.ietf.org/rfc/rfc2052.txt) can be used as a discovery mechanism.
The `-discovery-srv` flag can be used to set the DNS domain name where the discovery SRV records can be found.
The following DNS SRV records are looked up in the listed order:
* _etcd-server-ssl._tcp.example.com
* _etcd-server._tcp.example.com
If `_etcd-server-ssl._tcp.example.com` is found then etcd will attempt the bootstrapping process over SSL.
#### Create DNS SRV records
```
$ dig +noall +answer SRV _etcd-server._tcp.example.com
_etcd-server._tcp.example.com. 300 IN SRV 0 0 2380 infra0.example.com.
_etcd-server._tcp.example.com. 300 IN SRV 0 0 2380 infra1.example.com.
_etcd-server._tcp.example.com. 300 IN SRV 0 0 2380 infra2.example.com.
```
```
$ dig +noall +answer infra0.example.com infra1.example.com infra2.example.com
infra0.example.com. 300 IN A 10.0.1.10
infra1.example.com. 300 IN A 10.0.1.11
infra2.example.com. 300 IN A 10.0.1.12
```
#### Bootstrap the etcd cluster using DNS
etcd cluster memebers can listen on domain names or IP address, the bootstrap process will resolve DNS A records.
```
$ etcd -name infra0 \
-discovery-srv example.com \
-initial-advertise-peer-urls http://infra0.example.com:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster-state new \
-advertise-client-urls http://infra0.example.com:2379 \
-listen-client-urls http://infra0.example.com:2379 \
-listen-peer-urls http://infra0.example.com:2380
```
```
$ etcd -name infra1 \
-discovery-srv example.com \
-initial-advertise-peer-urls http://infra1.example.com:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster-state new \
-advertise-client-urls http://infra1.example.com:2379 \
-listen-client-urls http://infra1.example.com:2379 \
-listen-peer-urls http://infra1.example.com:2380
```
```
$ etcd -name infra2 \
-discovery-srv example.com \
-initial-advertise-peer-urls http://infra2.example.com:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster-state new \
-advertise-client-urls http://infra2.example.com:2379 \
-listen-client-urls http://infra2.example.com:2379 \
-listen-peer-urls http://infra2.example.com:2380
```
You can also bootstrap the cluster using IP addresses instead of domain names:
```
$ etcd -name infra0 \
-discovery-srv example.com \
-initial-advertise-peer-urls http://10.0.1.10:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster-state new \
-advertise-client-urls http://10.0.1.10:2379 \
-listen-client-urls http://10.0.1.10:2379 \
-listen-peer-urls http://10.0.1.10:2380
```
```
$ etcd -name infra1 \
-discovery-srv example.com \
-initial-advertise-peer-urls http://10.0.1.11:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster-state new \
-advertise-client-urls http://10.0.1.11:2379 \
-listen-client-urls http://10.0.1.11:2379 \
-listen-peer-urls http://10.0.1.11:2380
```
```
$ etcd -name infra2 \
-discovery-srv example.com \
-initial-advertise-peer-urls http://10.0.1.12:2380 \
-initial-cluster-token etcd-cluster-1 \
-initial-cluster-state new \
-advertise-client-urls http://10.0.1.12:2379 \
-listen-client-urls http://10.0.1.12:2379 \
-listen-peer-urls http://10.0.1.12:2380
```
#### etcd proxy configuration
DNS SRV records can also be used to configure the list of peers for an etcd server running in proxy mode:
```
$ etcd --proxy on -discovery-srv example.com
```
# 0.4 to 2.0+ Migration Guide
In etcd 2.0 we introduced the ability to listen on more than one address and to advertise multiple addresses. This makes using etcd easier when you have complex networking, such as private and public networks on various cloud providers.
To make understanding this feature easier, we changed the naming of some flags, but we support the old flags to make the migration from the old to new version easier.
|Old Flag |New Flag |Migration Behavior |
|-----------------------|-----------------------|---------------------------------------------------------------------------------------|
|-peer-addr |-initial-advertise-peer-urls |If specified, peer-addr will be used as the only peer URL. Error if both flags specified.|
|-addr |-advertise-client-urls |If specified, addr will be used as the only client URL. Error if both flags specified.|
|-peer-bind-addr |-listen-peer-urls |If specified, peer-bind-addr will be used as the only peer bind URL. Error if both flags specified.|
|-bind-addr |-listen-client-urls |If specified, bind-addr will be used as the only client bind URL. Error if both flags specified.|
|-peers |none |Deprecated. The -initial-cluster flag provides a similar concept with different semantics. Please read this guide on cluster startup.|
|-peers-file |none |Deprecated. The -initial-cluster flag provides a similar concept with different semantics. Please read this guide on cluster startup.|

View File

@ -1,135 +1,153 @@
# Etcd Configuration
## Configuration Flags
## Node Configuration
etcd is configurable through command-line flags and environment variables. Options set on the command line take precedence over those from the environment.
Individual node configuration options can be set in three places:
The format of environment variable for flag `-my-flag` is `ETCD_MY_FLAG`. It applies to all flags.
1. Command line flags
2. Environment variables
3. Configuration file
To start etcd automatically using custom settings at startup in Linux, using a [systemd][systemd-intro] unit is highly recommended.
Options set on the command line take precedence over all other sources.
Options set in environment variables take precedence over options set in
configuration files.
[systemd-intro]: http://freedesktop.org/wiki/Software/systemd/
## Cluster Configuration
### Member Flags
Cluster-wide settings are configured via the `/config` admin endpoint and additionally in the configuration file. Values contained in the configuration file will seed the cluster setting with the provided value. After the cluster is running, only the admin endpoint is used.
##### -name
+ Human-readable name for this member.
+ default: "default"
The full documentation is contained in the [API docs](https://github.com/coreos/etcd/blob/master/Documentation/api.md#cluster-config).
##### -data-dir
+ Path to the data directory.
+ default: "${name}.etcd"
* `activeSize` - the maximum number of peers that can participate in the consensus protocol. Other peers will join as standbys.
* `removeDelay` - the minimum time in seconds that a machine has been observed to be unresponsive before it is removed from the cluster.
* `syncInterval` - the amount of time in seconds between cluster sync when it runs in standby mode.
##### -snapshot-count
+ Number of committed transactions to trigger a snapshot to disk.
+ default: "10000"
## Command Line Flags
##### -heartbeat-interval
+ Time (in milliseconds) of a heartbeat interval.
+ default: "100"
### Required
##### -election-timeout
+ Time (in milliseconds) for an election to timeout.
+ default: "1000"
* `-name` - The node name. Defaults to a UUID.
##### -listen-peer-urls
+ List of URLs to listen on for peer traffic.
+ default: "http://localhost:2380,http://localhost:7001"
### Optional
##### -listen-client-urls
+ List of URLs to listen on for client traffic.
+ default: "http://localhost:2379,http://localhost:4001"
* `-addr` - The advertised public hostname:port for client communication. Defaults to `127.0.0.1:4001`.
* `-discovery` - A URL to use for discovering the peer list. (i.e `"https://discovery.etcd.io/your-unique-key"`).
* `-http-read-timeout` - The number of seconds before an HTTP read operation is timed out.
* `-http-write-timeout` - The number of seconds before an HTTP write operation is timed out.
* `-bind-addr` - The listening hostname for client communication. Defaults to advertised IP.
* `-peers` - A comma separated list of peers in the cluster (i.e `"203.0.113.101:7001,203.0.113.102:7001"`).
* `-peers-file` - The file path containing a comma separated list of peers in the cluster.
* `-ca-file` - The path of the client CAFile. Enables client cert authentication when present.
* `-cert-file` - The cert file of the client.
* `-key-file` - The key file of the client.
* `-config` - The path of the etcd configuration file. Defaults to `/etc/etcd/etcd.conf`.
* `-cors` - A comma separated white list of origins for cross-origin resource sharing.
* `-cpuprofile` - The path to a file to output CPU profile data. Enables CPU profiling when present.
* `-data-dir` - The directory to store log and snapshot. Defaults to the current working directory.
* `-max-result-buffer` - The max size of result buffer. Defaults to `1024`.
* `-max-retry-attempts` - The max retry attempts when trying to join a cluster. Defaults to `3`.
* `-peer-addr` - The advertised public hostname:port for server communication. Defaults to `127.0.0.1:7001`.
* `-peer-bind-addr` - The listening hostname for server communication. Defaults to advertised IP.
* `-peer-ca-file` - The path of the CAFile. Enables client/peer cert authentication when present.
* `-peer-cert-file` - The cert file of the server.
* `-peer-key-file` - The key file of the server.
* `-peer-election-timeout` - The number of milliseconds to wait before the leader is declared unhealthy.
* `-peer-heartbeat-interval` - The number of milliseconds in between heartbeat requests
* `-snapshot=false` - Disable log snapshots. Defaults to `true`.
* `-cluster-active-size` - The expected number of instances participating in the consensus protocol. Only applied if the etcd instance is the first peer in the cluster.
* `-cluster-remove-delay` - The number of seconds before one node is removed from the cluster since it cannot be connected at all. Only applied if the etcd instance is the first peer in the cluster.
* `-cluster-sync-interval` - The number of seconds between synchronization for standby-mode instance with the cluster. Only applied if the etcd instance is the first peer in the cluster.
* `-v` - Enable verbose logging. Defaults to `false`.
* `-vv` - Enable very verbose logging. Defaults to `false`.
* `-version` - Print the version and exit.
##### -max-snapshots
+ Maximum number of snapshot files to retain (0 is unlimited)
+ default: 5
+ The default for users on Windows is unlimited, and manual purging down to 5 (or your preference for safety) is recommended.
## Configuration File
##### -max-wals
+ Maximum number of wal files to retain (0 is unlimited)
+ default: 5
+ The default for users on Windows is unlimited, and manual purging down to 5 (or your preference for safety) is recommended.
The etcd configuration file is written in [TOML](https://github.com/mojombo/toml)
and read from `/etc/etcd/etcd.conf` by default.
##### -cors
+ Comma-separated white list of origins for CORS (cross-origin resource sharing).
+ default: none
```TOML
addr = "127.0.0.1:4001"
bind_addr = "127.0.0.1:4001"
ca_file = ""
cert_file = ""
cors = []
cpu_profile_file = ""
data_dir = "."
discovery = "http://etcd.local:4001/v2/keys/_etcd/registry/examplecluster"
http_read_timeout = 10.0
http_write_timeout = 10.0
key_file = ""
peers = []
peers_file = ""
max_cluster_size = 9
max_result_buffer = 1024
max_retry_attempts = 3
name = "default-name"
snapshot = true
verbose = false
very_verbose = false
### Clustering Flags
[peer]
addr = "127.0.0.1:7001"
bind_addr = "127.0.0.1:7001"
ca_file = ""
cert_file = ""
key_file = ""
`-initial` prefix flags are used in bootstrapping ([static bootstrap][build-cluster], [discovery-service bootstrap][discovery] or [runtime reconfiguration][reconfig]) a new member, and ignored when restarting an existing member.
[cluster]
active_size = 9
remove_delay = 1800.0
sync_interval = 5.0
```
`-discovery` prefix flags need to be set when using [discovery service][discovery].
## Environment Variables
##### -initial-advertise-peer-urls
* `ETCD_ADDR`
* `ETCD_BIND_ADDR`
* `ETCD_CA_FILE`
* `ETCD_CERT_FILE`
* `ETCD_CORS_ORIGINS`
* `ETCD_CONFIG`
* `ETCD_CPU_PROFILE_FILE`
* `ETCD_DATA_DIR`
* `ETCD_DISCOVERY`
* `ETCD_HTTP_READ_TIMEOUT`
* `ETCD_HTTP_WRITE_TIMEOUT`
* `ETCD_KEY_FILE`
* `ETCD_PEERS`
* `ETCD_PEERS_FILE`
* `ETCD_MAX_CLUSTER_SIZE`
* `ETCD_MAX_RESULT_BUFFER`
* `ETCD_MAX_RETRY_ATTEMPTS`
* `ETCD_NAME`
* `ETCD_SNAPSHOT`
* `ETCD_VERBOSE`
* `ETCD_VERY_VERBOSE`
* `ETCD_PEER_ADDR`
* `ETCD_PEER_BIND_ADDR`
* `ETCD_PEER_CA_FILE`
* `ETCD_PEER_CERT_FILE`
* `ETCD_PEER_KEY_FILE`
* `ETCD_PEER_ELECTION_TIMEOUT`
* `ETCD_CLUSTER_ACTIVE_SIZE`
* `ETCD_CLUSTER_REMOVE_DELAY`
* `ETCD_CLUSTER_SYNC_INTERVAL`
+ List of this member's peer URLs to advertise to the rest of the cluster. These addresses are used for communicating etcd data around the cluster. At least one must be routable to all cluster members.
+ default: "http://localhost:2380,http://localhost:7001"
##### -initial-cluster
+ Initial cluster configuration for bootstrapping.
+ default: "default=http://localhost:2380,default=http://localhost:7001"
##### -initial-cluster-state
+ Initial cluster state ("new" or "existing").
+ default: "new"
##### -initial-cluster-token
+ Initial cluster token for the etcd cluster during bootstrap.
+ default: "etcd-cluster"
##### -advertise-client-urls
+ List of this member's client URLs to advertise to the rest of the cluster.
+ default: "http://localhost:2379,http://localhost:4001"
##### -discovery
+ Discovery URL used to bootstrap the cluster.
+ default: none
##### -discovery-srv
+ DNS srv domain used to bootstrap the cluster.
+ default: none
##### -discovery-fallback
+ Expected behavior ("exit" or "proxy") when discovery services fails.
+ default: "proxy"
##### -discovery-proxy
+ HTTP proxy to use for traffic to discovery service.
+ default: none
### Proxy Flags
`-proxy` prefix flags configures etcd to run in [proxy mode][proxy].
##### -proxy
+ Proxy mode setting ("off", "readonly" or "on").
+ default: "off"
### Security Flags
The security flags help to [build a secure etcd cluster][security].
##### -ca-file
+ Path to the client server TLS CA file.
+ default: none
##### -cert-file
+ Path to the client server TLS cert file.
+ default: none
##### -key-file
+ Path to the client server TLS key file.
+ default: none
##### -peer-ca-file
+ Path to the peer server TLS CA file.
+ default: none
##### -peer-cert-file
+ Path to the peer server TLS cert file.
+ default: none
##### -peer-key-file
+ Path to the peer server TLS key file.
+ default: none
### Unsafe Flags
Be CAUTIOUS to use unsafe flags because it will break the guarantee given by consensus protocol. For example, it may panic if other members in the cluster are still alive. Follow the instructions when using these falgs.
##### -force-new-cluster
+ Force to create a new one-member cluster. It commits configuration changes in force to remove all existing members in the cluster and add itself. It needs to be set to [restore a backup][restore].
+ default: false
### Miscellaneous Flags
##### -version
+ Print the version and exit.
+ default: false
[build-cluster]: https://github.com/coreos/etcd/blob/master/Documentation/clustering.md#static
[reconfig]: https://github.com/coreos/etcd/blob/master/Documentation/runtime-configuration.md
[discovery]: https://github.com/coreos/etcd/blob/master/Documentation/clustering.md#discovery
[proxy]: https://github.com/coreos/etcd/blob/master/Documentation/proxy.md
[security]: https://github.com/coreos/etcd/blob/master/Documentation/security.md
[restore]: https://github.com/coreos/etcd/blob/master/Documentation/admin_guide.md#restoring-a-backup

View File

@ -1,69 +0,0 @@
# Debugging etcd
Diagnosing issues in a distributed application is hard.
etcd will help as much as it can - just enable these debug features using the CLI flag `-trace=*` or the config option `trace=*`.
## Logging
Log verbosity can be increased to the max using either the `-vvv` CLI flag or the `very_very_verbose=true` config option.
The only supported logging mode is to stdout.
## Metrics
etcd itself can generate a set of metrics.
These metrics represent many different internal data points that can be helpful when debugging etcd servers.
#### Metrics reference
Each individual metric name is prefixed with `etcd.<NAME>`, where \<NAME\> is the configured name of the etcd server.
* `timer.appendentries.handle`: amount of time a peer takes to process an AppendEntriesRequest from the POV of the peer itself
* `timer.peer.<PEER>.heartbeat`: amount of time a peer heartbeat operation takes from the POV of the leader that initiated that operation for peer \<PEER\>
* `timer.command.<COMMAND>`: amount of time a given command took to be processed through the local server's raft state machine. This does not include time waiting on locks.
#### Fetching metrics over HTTP
Once tracing has been enabled on a given etcd server, all metric data is available at the server's `/debug/metrics` HTTP endpoint (i.e. `http://127.0.0.1:4001/debug/metrics`).
Executing a GET HTTP command against the metrics endpoint will yield the current state of all metrics in the etcd server.
#### Sending metrics to Graphite
etcd supports [Graphite's Carbon plaintext protocol](https://graphite.readthedocs.org/en/latest/feeding-carbon.html#the-plaintext-protocol) - a TCP wire protocol designed for shipping metric data to an aggregator.
To send metrics to a Graphite endpoint using this protocol, use of the `-graphite-host` CLI flag or the `graphite_host` config option (i.e. `graphite_host=172.17.0.19:2003`).
See an [example graphite deploy script](https://github.com/coreos/etcd/contrib/graphite).
#### Generating additional metrics with Collectd
[Collectd](http://collectd.org/documentation.shtml) gathers metrics from the host running etcd.
While these aren't metrics generated by etcd itself, it can be invaluable to compare etcd's view of the world to that of a separate process running next to etcd.
See an [example collectd deploy script](https://github.com/coreos/etcd/contrib/collectd).
## Profiling
etcd exposes profiling information from the Go pprof package over HTTP.
The basic browsable interface is served by etcd at the `/debug/pprof` HTTP endpoint (i.e. `http://127.0.0.1:4001/debug/pprof`).
For more information on using profiling tools, see http://blog.golang.org/profiling-go-programs.
**NOTE**: In the following examples you need to ensure that the `./bin/etcd` is identical to the `./bin/etcd` that you are targeting (same git hash, arch, platform, etc).
#### Heap memory profile
```
go tool pprof ./bin/etcd http://127.0.0.1:4001/debug/pprof/heap
```
#### CPU profile
```
go tool pprof ./bin/etcd http://127.0.0.1:4001/debug/pprof/profile
```
#### Blocked goroutine profile
```
go tool pprof ./bin/etcd http://127.0.0.1:4001/debug/pprof/block
```

View File

@ -1,34 +0,0 @@
## Cluster Finding Process
Peer discovery uses the following sources in this order: log data in `-data-dir`, `-discovery` and `-peers`.
If log data is provided, etcd will concatenate possible peers from three sources: the log data, the `-discovery` option, and `-peers`. Then it tries to join cluster through them one by one. If all connection attempts fail (which indicates that the majority of the cluster is currently down), it will restart itself based on the log data, which helps the cluster to recover from a full outage.
Without log data, the instance is assumed to be a brand new one. If possible targets are provided by `-discovery` and `-peers`, etcd will make a best effort attempt to join them, and if none is reachable it will exit. Otherwise, if no `-discovery` or `-peers` option is provided, a new cluster will always be started.
This ensures that users can always restart the node safely with the same command (without --force), and etcd will either reconnect to the old cluster if it is still running or recover its cluster from a outage.
## Logical Workflow
Start an etcd machine:
```
If log data is given:
Try to join via peers in previous cluster
Try to join via peers found in discover URL
Try to join via peers in peer list
Restart the previous cluster which is down
return
If discover URL is given:
Fetch peers through discover URL
If Success:
Join via peers found
return
If peer list is given:
Join as follower via peers in peer list
return
Start as the leader of a new cluster
```

View File

@ -1,232 +0,0 @@
## Standbys
Adding peers in an etcd cluster adds network, CPU, and disk overhead to the leader since each one requires replication.
Peers primarily provide resiliency in the event of a leader failure but the benefit of more failover nodes decreases as the cluster size increases.
A lightweight alternative is the standby.
Standbys are a way for an etcd node to forward requests along to the cluster but the standbys are not part of the Raft cluster themselves.
This provides an easier API for local applications while reducing the overhead required by a regular peer node.
Standbys also act as standby nodes in the event that a peer node in the cluster has not recovered after a long duration.
## Configuration Parameters
There are three configuration parameters used by standbys: active size, remove delay and standby sync interval.
The active size specifies a target size for the number of peers in the cluster.
If there are not enough peers to meet the active size, standbys will send join requests until the peer count is equal to the active size.
If there are more peers than the target active size then peers are removed by the leader and will become standbys.
The remove delay specifies how long the cluster should wait before removing a dead peer.
By default this is 30 minutes.
If a peer is inactive for 30 minutes then the peer is removed.
The standby sync interval specifies the synchronization interval of standbys with the cluster.
By default this is 5 seconds.
After each interval, standbys synchronize information with cluster.
## Logical Workflow
### Start a etcd machine
#### Main logic
```
If find existing standby cluster info:
Goto standby loop
Find cluster as required
If determine to start peer server:
Goto peer loop
Else:
Goto standby loop
Peer loop:
Start peer mode
If running:
Wait for stop
Goto standby loop
Standby loop:
Start standby mode
If running:
Wait for stop
Goto peer loop
```
#### [Cluster finding logic][cluster-finding.md]
#### Join request logic:
```
Fetch machine info
If cannot match version:
return false
If active size <= peer count:
return false
If it has existed in the cluster:
return true
If join request fails:
return false
return true
```
**Note**
1. [TODO] The running mode cannot be determined by log, because the log may be outdated. But the log could be used to estimate its state.
2. Even if sync cluster fails, it will restart still for recovery from full outage.
#### Peer mode start logic
```
Start raft server
Start other helper routines
```
#### Peer mode auto stop logic
```
When removed from the cluster:
Stop raft server
Stop other helper routines
```
#### Standby mode run logic
```
Loop:
Sleep for some time
Sync cluster, and write cluster info into disk
Check active size and send join request if needed
If succeed:
Clear cluster info from disk
Return
```
#### Serve Requests as Standby
Return '404 Page Not Found' always on peer address. This is because peer address is used for raft communication and cluster management, which should not be used in standby mode.
Serve requests from client:
```
Redirect all requests to client URL of leader
```
**Note**
1. The leader here implies the one in raft cluster when doing the latest successful synchronization.
2. [IDEA] We could extend HTTP Redirect to multiple possible targets.
### Join Request Handling
```
If machine has existed in the cluster:
Return
If peer count < active size:
Add peer
Increase peer count
```
### Remove Request Handling
```
If machine exists in the cluster:
Remove peer
Decrease peer count
```
## Cluster Monitor Logic
### Active Size Monitor:
This is only run by current cluster leader.
```
Loop:
Sleep for some time
If peer count > active size:
Remove randomly selected peer
```
### Peer Activity Monitor
This is only run by current cluster leader.
```
Loop:
Sleep for some time
For each peer:
If peer last activity time > remove delay:
Remove the peer
Goto Loop
```
## Cluster Cases
### Create Cluster with Thousands of Instances
First few machines run in peer mode.
All the others check the status of the cluster and run in standby mode.
### Recover from full outage
Machines with log data restart with join failure.
Machines in peer mode recover heartbeat between each other.
Machines in standby mode always sync the cluster. If sync fails, it uses the first address from data log as redirect target.
### Kill one peer machine
Leader of the cluster lose the connection with the peer.
When the time exceeds remove delay, it removes the peer from the cluster.
Machine in standby mode finds one available place of the cluster. It sends join request and joins the cluster.
**Note**
1. [TODO] Machine which was divided from majority and was removed from the cluster will distribute running of the cluster if the new node uses the same name.
### Kill one standby machine
No change for the cluster.
## Cons
1. New instance cannot join immediately after one peer is kicked out of the cluster, because the leader doesn't know the info about the standby instances.
2. It may introduce join collision
3. Cluster needs a good interval setting to balance the join delay and join collision.
## Future Attack Plans
1. Based on heartbeat miss and remove delay, standby could adjust its next check time.
2. Preregister the promotion target when heartbeat miss happens.
3. Get the estimated cluster size from the check happened in the sync interval, and adjust sync interval dynamically.
4. Accept join requests based on active size and alive peers.

View File

@ -1,87 +0,0 @@
# Discovery Protocol
Starting a new etcd cluster can be painful since each machine needs to know of at least one live machine in the cluster. If you are trying to bring up a new cluster all at once, say using an AWS cloud formation, you also need to coordinate who will be the initial cluster leader. The discovery protocol uses an existing running etcd cluster to start a second etcd cluster.
To use this feature you add the command line flag `-discovery` to your etcd args. In this example we will use `http://example.com/v2/keys/_etcd/registry` as the URL prefix.
## The Protocol
By convention the etcd discovery protocol uses the key prefix `_etcd/registry`. A full URL to the keyspace will be `http://example.com/v2/keys/_etcd/registry`.
### Creating a New Cluster
Generate a unique token that will identify the new cluster. This will be used as a key prefix in the following steps. An easy way to do this is to use uuidgen:
```
UUID=$(uuidgen)
```
### Bringing up Machines
Now that you have your cluster ID you can start bringing up machines. Every machine will follow this protocol internally in etcd if given a `-discovery`.
### Registering your Machine
The first thing etcd must do is register your machine. This is done by using the machine name (from the `-name` arg) and posting it with a long TTL to the given key.
```
curl -X PUT "http://example.com/v2/keys/_etcd/registry/${UUID}/${etcd_machine_name}?ttl=604800" -d value=${peer_addr}
```
### Discovering Peers
Now that this etcd machine is registered it must discover its peers.
But, the tricky bit of starting a new cluster is that one machine needs to assume the initial role of leader and will have no peers. To figure out if another machine has already started the cluster etcd needs to create the `_state` key and set its value to "started":
```
curl -X PUT "http://example.com/v2/keys/_etcd/registry/${UUID}/_state?prevExist=false" -d value=started
```
If this returns a `200 OK` response then this machine is the initial leader and should start with no peers configured. If, however, this returns a `412 Precondition Failed` then you need to find all of the registered peers:
```
curl -X GET "http://example.com/v2/keys/_etcd/registry/${UUID}?recursive=true"
```
```
{
"action": "get",
"node": {
"createdIndex": 11,
"dir": true,
"key": "/_etcd/registry/9D4258A5-A1D3-4074-8837-31C1E091131D",
"modifiedIndex": 11,
"nodes": [
{
"createdIndex": 16,
"expiration": "2014-02-03T13:19:57.631253589-08:00",
"key": "/_etcd/registry/9D4258A5-A1D3-4074-8837-31C1E091131D/peer1",
"modifiedIndex": 16,
"ttl": 604765,
"value": "127.0.0.1:7001"
},
{
"createdIndex": 17,
"expiration": "2014-02-03T13:19:57.631253589-08:00",
"key": "/_etcd/registry/9D4258A5-A1D3-4074-8837-31C1E091131D/peer2",
"modifiedIndex": 17,
"ttl": 604765,
"value": "127.0.0.1:7002"
}
]
}
}
```
Using this information you can connect to the rest of the peers in the cluster.
### Heartbeating
At this point etcd will start heart beating to your registration URL. The
protocol uses a heartbeat so permanently deleted nodes get slowly removed from
the discovery information cluster.
The heartbeat interval is about once per day and the TTL is one week. This
should give a sufficiently wide window to protect against a discovery service
taking a temporary outage yet provide adequate cleanup.

View File

@ -1,60 +1,42 @@
Error Code
======
This document describes the error code in **Etcd** project.
This document describes the error code used in key space '/v2/keys'. Feel free to import 'github.com/coreos/etcd/error' to use.
It's categorized into four groups:
- Command Related Error
| name | code | strerror |
|----------------------|------|-----------------------|
| EcodeKeyNotFound | 100 | "Key not found" |
| EcodeTestFailed | 101 | "Compare failed" |
| EcodeNotFile | 102 | "Not a file" |
| EcodeNotDir | 104 | "Not a directory" |
| EcodeNodeExist | 105 | "Key already exists" |
| EcodeRootROnly | 107 | "Root is read only" |
| EcodeDirNotEmpty | 108 | "Directory not empty" |
- Post Form Related Error
| name | code | strerror |
|--------------------------|------|------------------------------------------------|
| EcodePrevValueRequired | 201 | "PrevValue is Required in POST form" |
| EcodeTTLNaN | 202 | "The given TTL in POST form is not a number" |
| EcodeIndexNaN | 203 | "The given index in POST form is not a number" |
| EcodeInvalidField | 209 | "Invalid field" |
| EcodeInvalidForm | 210 | "Invalid POST form" |
- Raft Related Error
| name | code | strerror |
|-------------------|------|--------------------------|
| EcodeRaftInternal | 300 | "Raft Internal Error" |
| EcodeLeaderElect | 301 | "During Leader Election" |
- Etcd Related Error
Error code corresponding strerror
------
const (
EcodeKeyNotFound = 100
EcodeTestFailed = 101
EcodeNotFile = 102
EcodeNoMorePeer = 103
EcodeNotDir = 104
EcodeNodeExist = 105
EcodeKeyIsPreserved = 106
EcodeRootROnly = 107
EcodeValueRequired = 200
EcodePrevValueRequired = 201
EcodeTTLNaN = 202
EcodeIndexNaN = 203
EcodeRaftInternal = 300
EcodeLeaderElect = 301
EcodeWatcherCleared = 400
EcodeEventIndexCleared = 401
)
// command related errors
errors[100] = "Key Not Found"
errors[101] = "Test Failed" //test and set
errors[102] = "Not A File"
errors[103] = "Reached the max number of peers in the cluster"
errors[104] = "Not A Directory"
errors[105] = "Already exists" // create
errors[106] = "The prefix of given key is a keyword in etcd"
errors[107] = "Root is read only"
// Post form related errors
errors[200] = "Value is Required in POST form"
errors[201] = "PrevValue is Required in POST form"
errors[202] = "The given TTL in POST form is not a number"
errors[203] = "The given index in POST form is not a number"
// raft related errors
errors[300] = "Raft Internal Error"
errors[301] = "During Leader Election"
// etcd related errors
errors[400] = "watcher is cleared due to etcd recovery"
errors[401] = "The event in requested index is outdated and cleared"
| name | code | strerror |
|-------------------------|------|--------------------------------------------------------|
| EcodeWatcherCleared | 400 | "watcher is cleared due to etcd recovery" |
| EcodeEventIndexCleared | 401 | "The event in requested index is outdated and cleared" |

View File

@ -1,101 +0,0 @@
#Etcd File System
## Structure
[TODO]
![alt text](./img/etcd_fs_structure.jpg "etcd file system structure")
## Node
In **etcd**, the **node** is the base from which the filesystem is constructed.
**etcd**'s file system is Unix-like with two kinds of nodes: file and directories.
- A **file node** has data associated with it.
- A **directory node** has child nodes associated with it.
All nodes, regardless of type, have the following attributes and operations:
### Attributes:
- **Expiration Time** [optional]
The node will be deleted when it expires.
- **ACL**
The path to the node's access control list.
### Operation:
- **Get** (path, recursive, sorted)
Get the content of the node
- If the node is a file, the data of the file will be returned.
- If the node is a directory, the child nodes of the directory will be returned.
- If recursive is true, it will recursively get the nodes of the directory.
- If sorted is true, the result will be sorted based on the path.
- **Create** (path, value[optional], ttl [optional])
Create a file. Create operation will help to create intermediate directories with no expiration time.
- If the file already exists, create will fail.
- If the value is given, set will create a file.
- If the value is not given, set will crate a directory.
- If ttl is given, the node will be deleted when it expires.
- **Update** (path, value[optional], ttl [optional])
Update the content of the node.
- If the value is given, the value of the key will be updated.
- If ttl is given, the expiration time of the node will be updated.
- **Delete** (path, recursive)
Delete the node of given path.
- If the node is a directory:
- If recursive is true, the operation will delete all nodes under the directory.
- If recursive is false, error will be returned.
- **TestAndSet** (path, prevValue [prevIndex], value, ttl)
Atomic *test and set* value to a file. If test succeeds, this operation will change the previous value of the file to the given value.
- If the prevValue is given, it will test against previous value of
the node.
- If the prevValue is empty, it will test if the node is not existing.
- If the prevValue is not empty, it will test if the prevValue is equal to the current value of the file.
- If the prevIndex is given, it will test if the create/last modified index of the node is equal to prevIndex.
- **Renew** (path, ttl)
Set the node's expiration time to (current time + ttl)
## ACL
### Theory
Etcd exports a Unix-like file system interface consisting of files and directories, collectively called nodes.
Each node has various meta-data, including three names of the access control lists used to control reading, writing and changing (change ACL names for the node).
We are storing the ACL names for nodes under a special *ACL* directory.
Each node has ACL name corresponding to one file within *ACL* dir.
Unless overridden, a node naturally inherits the ACL names of its parent directory on creation.
For each ACL name, it has three children: *R (Reading)*, *W (Writing)*, *C (Changing)*
Each permission is also a node. Under the node it contains the users who have this permission for the file referring to this ACL name.
### Example
[TODO]
### Diagram
[TODO]
### Interface
Testing permissions:
- (node *Node) get_perm()
- (node *Node) has_perm(perm string, user string)
Setting/Changing permissions:
- (node *Node) set_perm(perm string)
- (node *Node) change_ACLname(aclname string)
## User Group
[TODO]

View File

@ -28,4 +28,4 @@ Client is a caller of the cluster's HTTP API.
### Machine (deprecated)
The alternative of Member in etcd before 0.5
The alternative of Member in etcd before 2.0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@ -7,6 +7,7 @@
- [etcd-dump](https://npmjs.org/package/etcd-dump) - Command line utility for dumping/restoring etcd.
- [etcd-fs](https://github.com/xetorthio/etcd-fs) - FUSE filesystem for etcd
- [etcd-browser](https://github.com/henszey/etcd-browser) - A web-based key/value editor for etcd using AngularJS
- [etcd-lock](https://github.com/datawisesystems/etcd-lock) - A lock implementation for etcd
**Go libraries**
@ -14,11 +15,10 @@
**Java libraries**
- [boonproject/etcd](https://github.com/boonproject/boon/blob/master/etcd/README.md) - Supports v2, Async/Sync and waits
- [justinsb/jetcd](https://github.com/justinsb/jetcd)
- [diwakergupta/jetcd](https://github.com/diwakergupta/jetcd) - Supports v2
- [jurmous/etcd4j](https://github.com/jurmous/etcd4j) - Supports v2
- [jurmous/etcd4j](https://github.com/jurmous/etcd4j) - Supports v2, Async/Sync, waits and SSL
- [AdoHe/etcd4j](http://github.com/AdoHe/etcd4j) - Supports v2 (enhance for real production cluster)
**Python libraries**
@ -26,6 +26,7 @@
- [jplana/python-etcd](https://github.com/jplana/python-etcd) - Supports v2
- [russellhaering/txetcd](https://github.com/russellhaering/txetcd) - a Twisted Python library
- [cholcombe973/autodock](https://github.com/cholcombe973/autodock) - A docker deployment automation tool
- [lisael/aioetcd](https://github.com/lisael/aioetcd) - (Python 3.4+) Asyncio coroutines client (Supports v2)
**Node libraries**
@ -66,6 +67,10 @@
**Haskell libraries**
- [wereHamster/etcd-hs](https://github.com/wereHamster/etcd-hs)
**Tcl libraries**
- [efrecon/etcd-tcl](https://github.com/efrecon/etcd-tcl) - Supports v2, except wait.
A detailed recap of client functionalities can be found in the [clients compatibility matrix][clients-matrix.md].

View File

@ -1,118 +0,0 @@
## Modules
etcd has a number of modules that are built on top of the core etcd API.
These modules provide things like dashboards, locks and leader election (removed).
**Warning**: Modules are deprecated from v0.4 until we have a solid base we can apply them back onto.
For now, we are choosing to focus on raft algorithm and core etcd to make sure that it works correctly and fast.
And it is time consuming to maintain these modules in this period, given that etcd's API changes from time to time.
Moreover, the lock module has some unfixed bugs, which may mislead users.
But we also notice that these modules are popular and useful, and plan to add them back with full functionality as soon as possible.
### Dashboard
An HTML dashboard can be found at `http://127.0.0.1:4001/mod/dashboard/`.
This dashboard is compiled into the etcd binary and uses the same API as regular etcd clients.
Use the `-cors='*'` flag to allow your browser to request information from the current master as it changes.
### Lock
The Lock module implements a fair lock that can be used when lots of clients want access to a single resource.
A lock can be associated with a value.
The value is unique so if a lock tries to request a value that is already queued for a lock then it will find it and watch until that value obtains the lock.
You may supply a `timeout` which will cancel the lock request if it is not obtained within `timeout` seconds. If `timeout` is not supplied, it is presumed to be infinite. If `timeout` is `0`, the lock request will fail if it is not immediately acquired.
If you lock the same value on a key from two separate curl sessions they'll both return at the same time.
Here's the API:
**Acquire a lock (with no value) for "customer1"**
```sh
curl -X POST http://127.0.0.1:4001/mod/v2/lock/customer1?ttl=60
```
**Acquire a lock for "customer1" that is associated with the value "bar"**
```sh
curl -X POST http://127.0.0.1:4001/mod/v2/lock/customer1?ttl=60 -d value=bar
```
**Acquire a lock for "customer1" that is associated with the value "bar" only if it is done within 2 seconds**
```sh
curl -X POST http://127.0.0.1:4001/mod/v2/lock/customer1?ttl=60 -d value=bar -d timeout=2
```
**Renew the TTL on the "customer1" lock for index 2**
```sh
curl -X PUT http://127.0.0.1:4001/mod/v2/lock/customer1?ttl=60 -d index=2
```
**Renew the TTL on the "customer1" lock for value "bar"**
```sh
curl -X PUT http://127.0.0.1:4001/mod/v2/lock/customer1?ttl=60 -d value=bar
```
**Retrieve the current value for the "customer1" lock.**
```sh
curl http://127.0.0.1:4001/mod/v2/lock/customer1
```
**Retrieve the current index for the "customer1" lock**
```sh
curl http://127.0.0.1:4001/mod/v2/lock/customer1?field=index
```
**Delete the "customer1" lock with the index 2**
```sh
curl -X DELETE http://127.0.0.1:4001/mod/v2/lock/customer1?index=2
```
**Delete the "customer1" lock with the value "bar"**
```sh
curl -X DELETE http://127.0.0.1:4001/mod/v2/lock/customer1?value=bar
```
### Leader Election (Deprecated and Removed in 0.4)
The Leader Election module wraps the Lock module to allow clients to come to consensus on a single value.
This is useful when you want one server to process at a time but allow other servers to fail over.
The API is similar to the Lock module but is limited to simple strings values.
Here's the API:
**Attempt to set a value for the "order_processing" leader key:**
```sh
curl -X PUT http://127.0.0.1:4001/mod/v2/leader/order_processing?ttl=60 -d name=myserver1.foo.com
```
**Retrieve the current value for the "order_processing" leader key:**
```sh
curl http://127.0.0.1:4001/mod/v2/leader/order_processing
myserver1.foo.com
```
**Remove a value from the "order_processing" leader key:**
```sh
curl -X DELETE http://127.0.0.1:4001/mod/v2/leader/order_processing?name=myserver1.foo.com
```
If multiple clients attempt to set the value for a key then only one will succeed.
The other clients will hang until the current value is removed because of TTL or because of a `DELETE` operation.
Multiple clients can submit the same value and will all be notified when that value succeeds.
To update the TTL of a value simply reissue the same `PUT` command that you used to set the value.

View File

@ -1,38 +0,0 @@
# Optimal etcd Cluster Size
etcd's Raft consensus algorithm is most efficient in small clusters between 3 and 9 peers. For clusters larger than 9, etcd will select a subset of instances to participate in the algorithm in order to keep it efficient. The end of this document briefly explores how etcd works internally and why these choices have been made.
## Cluster Management
You can manage the active cluster size through the [cluster config API](https://github.com/coreos/etcd/blob/master/Documentation/api.md#cluster-config). `activeSize` represents the etcd peers allowed to actively participate in the consensus algorithm.
If the total number of etcd instances exceeds this number, additional peers are started as [standbys](https://github.com/coreos/etcd/blob/master/Documentation/design/standbys.md), which can be promoted to active participation if one of the existing active instances has failed or been removed.
## Internals of etcd
### Writing to etcd
Writes to an etcd peer are always redirected to the leader of the cluster and distributed to all of the peers immediately. A write is only considered successful when a majority of the peers acknowledge the write.
For example, in a cluster with 5 peers, a write operation is only as fast as the 3rd fastest machine. This is the main reason for keeping the number of active peers below 9. In practice, you only need to worry about write performance in high latency environments such as a cluster spanning multiple data centers.
### Leader Election
The leader election process is similar to writing a key &mdash; a majority of the active peers must acknowledge the new leader before cluster operations can continue. The longer each peer takes to elect a new leader means you have to wait longer before you can write to the cluster again. In low latency environments this process takes milliseconds.
### Odd Active Cluster Size
The other important cluster optimization is to always have an odd active cluster size (i.e. `activeSize`). Adding an odd node to the number of peers doesn't change the size of the majority and therefore doesn't increase the total latency of the majority as described above. But, you gain a higher tolerance for peer failure by adding the extra machine. You can see this in practice when comparing two even and odd sized clusters:
| Active Peers | Majority | Failure Tolerance |
|--------------|------------|-------------------|
| 1 peers | 1 peers | None |
| 3 peers | 2 peers | 1 peer |
| 4 peers | 3 peers | 1 peer |
| 5 peers | 3 peers | **2 peers** |
| 6 peers | 4 peers | 2 peers |
| 7 peers | 4 peers | **3 peers** |
| 8 peers | 5 peers | 3 peers |
| 9 peers | 5 peers | **4 peers** |
As you can see, adding another peer to bring the number of active peers up to an odd size is always worth it. During a network partition, an odd number of active peers also guarantees that there will almost always be a majority of the cluster that can continue to operate and be the source of truth when the partition ends.

View File

@ -3,6 +3,7 @@
* [List members](#list-members)
* [Add a member](#add-a-member)
* [Delete a member](#delete-a-member)
* [Change the peer urls of a member](#change-the-peer-urls-of-a-member)
## List members
@ -16,7 +17,7 @@ GET /v2/members HTTP/1.1
### Example
```
```sh
curl http://10.0.0.10:2379/v2/members
```
@ -58,20 +59,21 @@ If the POST body is malformed an HTTP 400 will be returned. If the member exists
```
POST /v2/members HTTP/1.1
{"peerURLs": ["http://10.0.0.10:2379"]}
{"peerURLs": ["http://10.0.0.10:2380"]}
```
### Example
```
curl http://10.0.0.10:2379/v2/members -XPOST -H "Content-Type: application/json" -d '{"peerURLs":["http://10.0.0.10:2379"]}'
```sh
curl http://10.0.0.10:2379/v2/members -XPOST \
-H "Content-Type: application/json" -d '{"peerURLs":["http://10.0.0.10:2380"]}'
```
```json
{
"id": "3777296169",
"peerURLs": [
"http://10.0.0.10:2379"
"http://10.0.0.10:2380"
]
}
```
@ -79,7 +81,7 @@ curl http://10.0.0.10:2379/v2/members -XPOST -H "Content-Type: application/json"
## Delete a member
Remove a member from the cluster. The member ID must be a hex-encoded uint64.
Returns empty when successful. Returns a string describing the failure condition when unsuccessful.
Returns 204 with empty content when successful. Returns a string describing the failure condition when unsuccessful.
If the member does not exist in the cluster an HTTP 500(TODO: fix this) will be returned. If the cluster fails to process the request within timeout an HTTP 500 will be returned, though the request may be processed later.
@ -91,6 +93,27 @@ DELETE /v2/members/<id> HTTP/1.1
### Example
```
```sh
curl http://10.0.0.10:2379/v2/members/272e204152 -XDELETE
```
## Change the peer urls of a member
Change the peer urls of a given mamber. The member ID must be a hex-encoded uint64. Returns 204 with empty content when successful. Returns a string describing the failure condition when unsuccessful.
If the POST body is malformed an HTTP 400 will be returned. If the member does not exist in the cluster an HTTP 404 will be returned. If any of the given peerURLs exists in the cluster an HTTP 409 will be returned. If the cluster fails to process the request within timeout an HTTP 500 will be returned, though the request may be processed later.
#### Request
```
PUT /v2/members/<id> HTTP/1.1
{"peerURLs": ["http://10.0.0.10:2380"]}
```
#### Example
```sh
curl http://10.0.0.10:2379/v2/members/272e204152 -XPUT \
-H "Content-Type: application/json" -d '{"peerURLs":["http://10.0.0.10:2380"]}'
```

View File

@ -2,6 +2,3 @@ etcd is being used successfully by many companies in production. It is,
however, under active development and systems like etcd are difficult to get
correct. If you are comfortable with bleeding-edge software please use etcd and
provide us with the feedback and testing young software needs.
When the etcd team feels confident removing this warning we will release etcd
1.0.

View File

@ -29,4 +29,4 @@ etcd -proxy on -client-listen-urls 127.0.0.1:8080 -discovery https://discovery.
#### Fallback to proxy mode with discovery service
If you bootstrap a etcd cluster using [discovery service][discovery-service] with more than the expected number of etcd members, the extra etcd processes will fall back to being `readwrite` proxies by default. They will forward the requests to the cluster as described above. For example, if you create a discovery url with `size=5`, and start ten etcd processes using that same discovery URL, the result will be a cluster with five etcd members and five proxies. Note that this behaviour can be disabled with the `proxy-fallback` flag.
[discovery-service]: https://github.com/coreos/etcd/blob/master/Documentation/0.5/clustering.md#discovery
[discovery-service]: https://github.com/coreos/etcd/blob/master/Documentation/clustering.md#discovery

View File

@ -2,16 +2,24 @@
etcd comes with support for incremental runtime reconfiguration, which allows users to update the membership of the cluster at run time.
Reconfiguration requests can only be processed when the the majority of the cluster members are functioning. It is **highly recommended** to always have a cluster size greater than two in production. It is unsafe to remove a member from a two member cluster. The majority of a two member cluster is also two. If there is a failure during the removal process, the cluster might not able to make progress and need to [restart from majority failure][majority failure].
[majority failure]: #restart-cluster-from-majority-failure
## Reconfiguration Use Cases
Let us walk through the four use cases for re-configuring a cluster: replacing a member, increasing or decreasing cluster size, and restarting a cluster from a majority failure.
### Replace a Member
### Replace a Non-recoverable Member
The most common use case of cluster reconfiguration is to replace a member because of a permanent failure of the existing member: for example, hardware failure, loss of network address, or data directory corruption.
The most common use case of cluster reconfiguration is to replace a member because of a permanent failure of the existing member: for example, hardware failure or data directory corruption.
It is important to replace failed members as soon as the failure is detected.
If etcd falls below a simple majority of members it can no longer accept writes: e.g. in a 3 member cluster the loss of two members will cause writes to fail and the cluster to stop operating.
If you want to migrate a running member to another machine, please refer [member migration section][member migration].
[member migration]: https://github.com/coreos/etcd/blob/master/Documentation/admin_guide.md#member-migration
### Increase Cluster Size
To make your cluster more resilient to machine failure you can increase the size of the cluster.
@ -49,7 +57,7 @@ To increase from 3 to 5 members you will make two add operations
To decrease from 5 to 3 you will make two remove operations
All of these examples will use the `etcdctl` command line tool that ships with etcd.
If you want to use the member API directly you can find the documentation [here](https://github.com/coreos/etcd/blob/master/Documentation/0.5/other_apis.md).
If you want to use the member API directly you can find the documentation [here](https://github.com/coreos/etcd/blob/master/Documentation/other_apis.md).
### Remove a Member
@ -82,7 +90,7 @@ Removal of the leader is safe, but the cluster will be out of progress for a per
Adding a member is a two step process:
* Add the new member to the cluster via the [members API](https://github.com/coreos/etcd/blob/master/Documentation/0.5/other_apis.md#post-v2members) or the `etcdctl member add` command.
* Add the new member to the cluster via the [members API](https://github.com/coreos/etcd/blob/master/Documentation/other_apis.md#post-v2members) or the `etcdctl member add` command.
* Start the member with the correct configuration.
Using `etcdctl` let's add the new member to the cluster:

View File

@ -1,8 +1,8 @@
# Etcd security model
# security model
Etcd supports SSL/TLS as well as authentication through client certificates, both for clients to server as well as peer (server to server / cluster) communication.
etcd supports SSL/TLS as well as authentication through client certificates, both for clients to server as well as peer (server to server / cluster) communication.
To get up and running you first need to have a CA certificate and a signed key pair for your node. It is recommended to create and sign a new key pair for every node in a cluster.
To get up and running you first need to have a CA certificate and a signed key pair for one member. It is recommended to create and sign a new key pair for every member in a cluster.
For convenience the [etcd-ca](https://github.com/coreos/etcd-ca) tool provides an easy interface to certificate generation, alternatively this site provides a good reference on how to generate self-signed key pairs:
@ -10,38 +10,44 @@ http://www.g-loaded.eu/2005/11/10/be-your-own-ca/
## Basic setup
Etcd takes several certificate related configuration options, either through command-line flags or environment variables:
etcd takes several certificate related configuration options, either through command-line flags or environment variables:
**Client-to-server communication:**
`--cert-file=<path>`: Certificate used for SSL/TLS connections **to** etcd. When this option is set, you can reach etcd through HTTPS - for example at `https://127.0.0.1:4001`
`--key-file=<path>`: Key for the certificate. Must be unencrypted.
`--cert-file=<path>`: Certificate used for SSL/TLS connections **to** etcd. When this option is set, you can set advertise-client-urls using HTTPS schema.
`--key-file=<path>`: Key for the certificate. Must be unencrypted.
`--ca-file=<path>`: When this is set etcd will check all incoming HTTPS requests for a client certificate signed by the supplied CA, requests that don't supply a valid client certificate will fail.
**Peer (server-to-server / cluster) communication:**
The peer options work the same way as the client-to-server options:
`--peer-cert-file=<path>`: Certificate used for SSL/TLS connections between peers. This will be used both for listening on the peer address as well as sending requests to other peers.
`--peer-key-file=<path>`: Key for the certificate. Must be unencrypted.
`--peer-cert-file=<path>`: Certificate used for SSL/TLS connections between peers. This will be used both for listening on the peer address as well as sending requests to other peers.
`--peer-key-file=<path>`: Key for the certificate. Must be unencrypted.
`--peer-ca-file=<path>`: When set, etcd will check all incoming peer requests from the cluster for valid client certificates signed by the supplied CA.
If either a client-to-server or peer certificate is supplied the key must also be set. All of these configuration options are also available through the environment variables, `ETCD_CA_FILE`, `ETCD_PEER_CA_FILE` and so on.
## Example 1: Client-to-server transport security with HTTPS
For this you need your CA certificate (`ca.crt`) and signed key pair (`server.crt`, `server.key`) ready. If you just want to test the functionality, there are example certificates provided in the [etcd git repository](https://github.com/coreos/etcd/tree/master/fixtures/ca) (namely `server.crt` and `server.key.insecure`).
For this you need your CA certificate (`ca.crt`) and signed key pair (`server.crt`, `server.key`) ready.
Assuming you have these files ready, let's configure etcd to use them to provide simple HTTPS transport security.
Let us configure etcd to provide simple HTTPS transport security step by step:
```sh
etcd -name machine0 -data-dir machine0 -cert-file=/path/to/server.crt -key-file=/path/to/server.key
$ etcd -name infra0 -data-dir infra0 \
-cert-file=/path/to/server.crt -key-file=/path/to/server.key \
-advertise-client-urls=https://127.0.0.1:2379 -listen-client-urls=https://127.0.0.1:2379
```
This should start up fine and you can now test the configuration by speaking HTTPS to etcd:
```sh
curl --cacert /path/to/ca.crt https://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar -v
$ curl --cacert /path/to/ca.crt https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v
```
You should be able to see the handshake succeed. Because we use self-signed certificates with our own certificate authorities you need to provide the CA to curl using the `--cacert` option. Another possibility would be to add your CA certificate to the trusted certificates on your system (usually in `/etc/ssl/certs`).
@ -61,16 +67,17 @@ The clients will provide their certificates to the server and the server will ch
You need the same files mentioned in the first example for this, as well as a key pair for the client (`client.crt`, `client.key`) signed by the same certificate authority.
```sh
etcd -name machine0 -data-dir machine0 -ca-file=/path/to/ca.crt -cert-file=/path/to/server.crt -key-file=/path/to/server.key
$ etcd -name infra0 -data-dir infra0 \
-ca-file=/path/to/ca.crt -cert-file=/path/to/server.crt -key-file=/path/to/server.key \
-advertise-client-urls https://127.0.0.1:2379 -listen-client-urls https://127.0.0.1:2379
```
Notice that the addition of the `-ca-file` option automatically enables client certificate checking.
Now try the same request as above to this server:
```sh
curl --cacert /path/to/ca.crt https://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar -v
$ curl --cacert /path/to/ca.crt https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v
```
The request should be rejected by the server:
@ -84,7 +91,8 @@ routines:SSL3_READ_BYTES:sslv3 alert bad certificate
To make it succeed, we need to give the CA signed client certificate to the server:
```sh
curl --cacert /path/to/ca.crt --cert /path/to/client.crt --key /path/to/client.key -L https://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar -v
$ curl --cacert /path/to/ca.crt --cert /path/to/client.crt --key /path/to/client.key \
-L https://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -v
```
You should able to see:
@ -112,22 +120,28 @@ And also the response from the server:
## Example 3: Transport security & client certificates in a cluster
Etcd supports the same model as above for **peer communication**, that means the communication between etcd nodes in a cluster.
etcd supports the same model as above for **peer communication**, that means the communication between etcd members in a cluster.
Assuming we have our `ca.crt` and two nodes with their own keypairs (`node1.crt` & `node1.key`, `node2.crt` & `node2.key`) signed by this CA, we launch etcd as follows:
Assuming we have our `ca.crt` and two members with their own keypairs (`member1.crt` & `member1.key`, `member2.crt` & `member2.key`) signed by this CA, we launch etcd as follows:
```sh
DISCOVERY_URL=... # from https://discovery.etcd.io/new
# Node1
etcd -name node1 -data-dir node1 -ca-file=/path/to/ca.crt -cert-file=/path/to/node1.crt -key-file=/path/to/node1.key -peer-addr ${node1_public_ip}:7001 -discovery ${DISCOVERY_URL}
# member1
$ etcd -name infra1 -data-dir infra1 \
-ca-file=/path/to/ca.crt -cert-file=/path/to/member1.crt -key-file=/path/to/member1.key \
-initial-advertise-peer-urls=https://10.0.1.10:2380 -listen-peer-urls=https://10.0.1.10:2380 \
-discovery ${DISCOVERY_URL}
# Node2
etcd -name node2 -data-dir node2 -ca-file=/path/to/ca.crt -cert-file=/path/to/node2.crt -key-file=/path/to/node2.key -peer-addr ${node2_public_ip}:7001 -discovery ${DISCOVERY_URL}
# member2
$ etcd -name infra2 -data-dir infra2 \
-ca-file=/path/to/ca.crt -cert-file=/path/to/member2.crt -key-file=/path/to/member2.key \
-initial-advertise-peer-urls=https://10.0.1.11:2380 -listen-peer-urls=https://10.0.1.11:2380 \
-discovery ${DISCOVERY_URL}
```
The etcd nodes will form a cluster and all communication between nodes in the cluster will be encrypted and authenticated using the client certificates. You will see in the output of etcd that the addresses it connects to use HTTPS.
The etcd members will form a cluster and all communication between members in the cluster will be encrypted and authenticated using the client certificates. You will see in the output of etcd that the addresses it connects to use HTTPS.
## Frequently Asked Questions
@ -150,10 +164,10 @@ Add the following section to your openssl.cnf:
When creating the cert be sure to reference it in the `-extensions` flag:
```
openssl ca -config openssl.cnf -policy policy_anything -extensions ssl_client -out certs/machine.crt -infiles machine.csr
$ openssl ca -config openssl.cnf -policy policy_anything -extensions ssl_client -out certs/machine.crt -infiles machine.csr
```
### With peer certificate authentication I receive "certificate is valid for 127.0.0.1, not $MY_IP"
Make sure that you sign your certificates with a Subject Name your node's public IP address. The `etcd-ca` tool for example provides an `--ip=` option for its `new-cert` command.
Make sure that you sign your certificates with a Subject Name your member's public IP address. The `etcd-ca` tool for example provides an `--ip=` option for its `new-cert` command.
If you need your certificate to be signed for your node's FQDN in its Subject Name then you could use Subject Alternative Names (short IP SNAs) to add your IP address. This is not [currently supported](https://github.com/coreos/etcd-ca/issues/29) by `etcd-ca` but can be done [with openssl](http://wiki.cacert.org/FAQ/subjectAltName).
If you need your certificate to be signed for your member's FQDN in its Subject Name then you could use Subject Alternative Names (short IP SANs) to add your IP address. The `etcd-ca` tool provides `--domain=` option for its `new-cert` command, and openssl can make [it](http://wiki.cacert.org/FAQ/subjectAltName) too.

View File

@ -32,23 +32,14 @@ You can override the default values on the command line:
```sh
# Command line arguments:
$ etcd -peer-heartbeat-interval=100 -peer-election-timeout=500
$ etcd -heartbeat-interval=100 -election-timeout=500
# Environment variables:
$ ETCD_PEER_HEARTBEAT_INTERVAL=100 ETCD_PEER_ELECTION_TIMEOUT=500 etcd
```
Or you can set the values within the configuration file:
```toml
[peer]
heartbeat_interval = 100
election_timeout = 500
$ ETCD_HEARTBEAT_INTERVAL=100 ETCD_ELECTION_TIMEOUT=500 etcd
```
The values are specified in milliseconds.
### Snapshots
etcd appends all key changes to a log file.
@ -72,12 +63,6 @@ $ etcd -snapshot-count=5000
$ ETCD_SNAPSHOT_COUNT=5000 etcd
```
Or you can change the setting in the configuration file:
```toml
snapshot_count = 5000
```
You can also disable snapshotting by adding the following to your command line:
```sh
@ -87,9 +72,3 @@ $ etcd -snapshot false
# Environment variables:
$ ETCD_SNAPSHOT=false etcd
```
You can also disable snapshotting within the configuration file:
```toml
snapshot = false
```

View File

@ -1,17 +0,0 @@
# Upgrading an Existing Cluster
etcd clusters can be upgraded by doing a rolling upgrade or all at once. We make every effort to test this process, but please be sure to backup your data [by etcd-dump](https://github.com/AaronO/etcd-dump), or make a copy of data directory beforehand.
## Upgrade Process
- Stop the old etcd processes
- Upgrade the etcd binary
- Restart the etcd instance using the original --name, --address, --peer-address and --data-dir.
## Rolling Upgrade
During an upgrade, etcd clusters are designed to continue working in a mix of old and new versions. It's recommended to converge on the new version quickly. Using new API features before the entire cluster has been upgraded is only supported as a best effort. Each instance's version can be found with `curl http://127.0.0.1:4001/version`.
## All at Once
If downtime is not an issue, the easiest way to upgrade your cluster is to shutdown all of the etcd instances and restart them with the new binary. The current state of the cluster is saved to disk and will be loaded into the cluster when it restarts.

4
Godeps/Godeps.json generated
View File

@ -16,8 +16,8 @@
},
{
"ImportPath": "github.com/coreos/go-etcd/etcd",
"Comment": "v0.2.0-rc1-127-g6fe04d5",
"Rev": "6fe04d580dfb71c9e34cbce2f4df9eefd1e1241e"
"Comment": "v0.2.0-rc1-130-g6aa2da5",
"Rev": "6aa2da5a7a905609c93036b9307185a04a5a84a5"
},
{
"ImportPath": "github.com/jonboulle/clockwork",

View File

@ -379,11 +379,13 @@ func buildValues(value string, ttl uint64) url.Values {
return v
}
// convert key string to http path exclude version
// convert key string to http path exclude version, including URL escaping
// for example: key[foo] -> path[keys/foo]
// key[/%z] -> path[keys/%25z]
// key[/] -> path[keys/]
func keyToPath(key string) string {
p := path.Join("keys", key)
// URL-escape our key, except for slashes
p := strings.Replace(url.QueryEscape(path.Join("keys", key)), "%2F", "/", -1)
// corner case: if key is "/" or "//" ect
// path join will clear the tailing "/"

View File

@ -0,0 +1,22 @@
package etcd
import "testing"
func TestKeyToPath(t *testing.T) {
tests := []struct {
key string
wpath string
}{
{"", "keys/"},
{"foo", "keys/foo"},
{"foo/bar", "keys/foo/bar"},
{"%z", "keys/%25z"},
{"/", "keys/"},
}
for i, tt := range tests {
path := keyToPath(tt.key)
if path != tt.wpath {
t.Errorf("#%d: path = %s, want %s", i, path, tt.wpath)
}
}
}

View File

@ -3,15 +3,7 @@
[![Build Status](https://travis-ci.org/coreos/etcd.png?branch=master)](https://travis-ci.org/coreos/etcd)
[![Docker Repository on Quay.io](https://quay.io/repository/coreos/etcd-git/status "Docker Repository on Quay.io")](https://quay.io/repository/coreos/etcd-git)
### WARNING ###
The current `master` branch of etcd is under heavy development in anticipation of the forthcoming 0.5.0 release.
It is strongly recommended that users work with the latest 0.4.x release (0.4.6), which can be found on the [releases](https://github.com/coreos/etcd/releases) page.
Unless otherwise noted, the etcd documentation refers to configuring and running 0.4.x releases.
## README version 0.4.6
![etcd Logo](logos/etcd-horizontal-color.png)
A highly-available key value store for shared configuration and service discovery.
etcd is inspired by [Apache ZooKeeper][zookeeper] and [doozer][doozer], with a focus on being:
@ -29,7 +21,7 @@ Or feel free to just use curl, as in the examples below.
[zookeeper]: http://zookeeper.apache.org/
[doozer]: https://github.com/ha/doozerd
[raft]: http://raftconsensus.github.io/
[etcdctl]: http://github.com/coreos/etcdctl/
[etcdctl]: https://github.com/coreos/etcd/tree/master/etcdctl
If you're considering etcd for production use, please see: [production-ready.md](./Documentation/production-ready.md)
@ -43,7 +35,7 @@ The latest release and setup instructions are available at [GitHub][github-relea
### Running etcd
First start a single-machine cluster of etcd:
First start a single-member cluster of etcd:
```sh
./bin/etcd
@ -58,27 +50,41 @@ curl -L http://127.0.0.1:4001/v2/keys/mykey -XPUT -d value="this is awesome"
curl -L http://127.0.0.1:4001/v2/keys/mykey
```
You have successfully started an etcd on a single machine and written a key to the store. Now it's time to dig into the full etcd API and other guides.
You have successfully started an etcd and written a key to the store.
### Running local etcd cluster
First install [goreman](https://github.com/mattn/goreman), which manages Procfile-based applications.
Our [Profile script](./Procfile) will set up a local example cluster. You can start it with:
```sh
goreman start
```
This will bring up 3 etcd members `infra1`, `infra2` and `infra3` and etcd proxy `proxy`, which runs locally and composes a cluster.
You can write a key to the cluster and retrieve the value back from any member or proxy.
### Next Steps
Now it's time to dig into the full etcd API and other guides.
- Explore the full [API][api].
- Set up a [multi-machine cluster][clustering].
- Learn the [config format, env variables and flags][configuration].
- Find [language bindings and tools][libraries-and-tools].
- Learn about the dashboard, lock and leader election [modules][modules].
- Use TLS to [secure an etcd cluster][security].
- [Tune etcd][tuning].
- [Upgrade from old version][upgrade].
- [Upgrade from 0.4.6 to 2.0.0][upgrade].
[api]: https://github.com/coreos/etcd/blob/master/Documentation/api.md
[clustering]: https://github.com/coreos/etcd/blob/master/Documentation/clustering.md
[configuration]: https://github.com/coreos/etcd/blob/master/Documentation/configuration.md
[libraries-and-tools]: https://github.com/coreos/etcd/blob/master/Documentation/libraries-and-tools.md
[modules]: https://github.com/coreos/etcd/blob/master/Documentation/modules.md
[security]: https://github.com/coreos/etcd/blob/master/Documentation/security.md
[tuning]: https://github.com/coreos/etcd/blob/master/Documentation/tuning.md
[upgrade]: https://github.com/coreos/etcd/blob/master/Documentation/upgrade.md
[api]: ./Documentation/api.md
[clustering]: ./Documentation/clustering.md
[configuration]: ./Documentation/configuration.md
[libraries-and-tools]: ./Documentation/libraries-and-tools.md
[security]: ./Documentation/security.md
[tuning]: ./Documentation/tuning.md
[upgrade]: ./Documentation/0_4_migration_tool.md
## Contact
@ -108,11 +114,7 @@ curl -L http://127.0.0.1:4001/version
#### API Versioning
The `v2` API responses should not change after the 0.2.0 release but new features will be added over time.
The `v1` API has been deprecated and will not be supported.
During the pre-v1.0.0 series of releases we may break the API as we fix bugs and get feedback.
The `v2` API responses should not change after the 2.0.0 release but new features will be added over time.
#### 32-bit systems

8
build
View File

@ -11,6 +11,8 @@ ln -s ${PWD} $GOPATH/src/${REPO_PATH}
eval $(go env)
go build -o bin/etcd ${REPO_PATH}
go build -o bin/etcdctl ${REPO_PATH}/etcdctl
go build -o bin/etcd-migrate ${REPO_PATH}/migrate/cmd/etcd-migrate
# Static compilation is useful when etcd is run in a container
CGO_ENABLED=0 go build -a -ldflags '-s' -o bin/etcd ${REPO_PATH}
CGO_ENABLED=0 go build -a -ldflags '-s' -o bin/etcdctl ${REPO_PATH}/etcdctl
go build -o bin/etcd-migrate ${REPO_PATH}/tools/etcd-migrate
go build -o bin/etcd-dump-logs ${REPO_PATH}/tools/etcd-dump-logs

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package client

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package client

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package client
@ -71,6 +69,7 @@ type Response struct {
Action string `json:"action"`
Node *Node `json:"node"`
PrevNode *Node `json:"prevNode"`
Index uint64
}
type Nodes []*Node
@ -107,7 +106,7 @@ func (k *httpKeysAPI) Create(ctx context.Context, key, val string, ttl time.Dura
return nil, err
}
return unmarshalHTTPResponse(resp.StatusCode, body)
return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
}
func (k *httpKeysAPI) Get(ctx context.Context, key string) (*Response, error) {
@ -122,7 +121,7 @@ func (k *httpKeysAPI) Get(ctx context.Context, key string) (*Response, error) {
return nil, err
}
return unmarshalHTTPResponse(resp.StatusCode, body)
return unmarshalHTTPResponse(resp.StatusCode, resp.Header, body)
}
func (k *httpKeysAPI) Watch(key string, idx uint64) Watcher {
@ -160,7 +159,7 @@ func (hw *httpWatcher) Next(ctx context.Context) (*Response, error) {
return nil, err
}
resp, err := unmarshalHTTPResponse(httpresp.StatusCode, body)
resp, err := unmarshalHTTPResponse(httpresp.StatusCode, httpresp.Header, body)
if err != nil {
return nil, err
}
@ -243,10 +242,10 @@ func (c *createAction) HTTPRequest(ep url.URL) *http.Request {
return req
}
func unmarshalHTTPResponse(code int, body []byte) (res *Response, err error) {
func unmarshalHTTPResponse(code int, header http.Header, body []byte) (res *Response, err error) {
switch code {
case http.StatusOK, http.StatusCreated:
res, err = unmarshalSuccessfulResponse(body)
res, err = unmarshalSuccessfulResponse(header, body)
default:
err = unmarshalErrorResponse(code)
}
@ -254,13 +253,18 @@ func unmarshalHTTPResponse(code int, body []byte) (res *Response, err error) {
return
}
func unmarshalSuccessfulResponse(body []byte) (*Response, error) {
func unmarshalSuccessfulResponse(header http.Header, body []byte) (*Response, error) {
var res Response
err := json.Unmarshal(body, &res)
if err != nil {
return nil, err
}
if header.Get("X-Etcd-Index") != "" {
res.Index, err = strconv.ParseUint(header.Get("X-Etcd-Index"), 10, 64)
}
if err != nil {
return nil, err
}
return &res, nil
}
@ -273,6 +277,8 @@ func unmarshalErrorResponse(code int) error {
case http.StatusInternalServerError:
// this isn't necessarily true
return ErrNoLeader
case http.StatusGatewayTimeout:
return ErrTimeout
default:
}

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package client
@ -255,40 +253,46 @@ func assertResponse(got http.Request, wantURL *url.URL, wantHeader http.Header,
func TestUnmarshalSuccessfulResponse(t *testing.T) {
tests := []struct {
indexHeader string
body string
res *Response
expectError bool
}{
// Neither PrevNode or Node
{
"1",
`{"action":"delete"}`,
&Response{Action: "delete"},
&Response{Action: "delete", Index: 1},
false,
},
// PrevNode
{
"15",
`{"action":"delete", "prevNode": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10}}`,
&Response{Action: "delete", PrevNode: &Node{Key: "/foo", Value: "bar", ModifiedIndex: 12, CreatedIndex: 10}},
&Response{Action: "delete", Index: 15, PrevNode: &Node{Key: "/foo", Value: "bar", ModifiedIndex: 12, CreatedIndex: 10}},
false,
},
// Node
{
"15",
`{"action":"get", "node": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10}}`,
&Response{Action: "get", Node: &Node{Key: "/foo", Value: "bar", ModifiedIndex: 12, CreatedIndex: 10}},
&Response{Action: "get", Index: 15, Node: &Node{Key: "/foo", Value: "bar", ModifiedIndex: 12, CreatedIndex: 10}},
false,
},
// PrevNode and Node
{
"15",
`{"action":"update", "prevNode": {"key": "/foo", "value": "baz", "modifiedIndex": 10, "createdIndex": 10}, "node": {"key": "/foo", "value": "bar", "modifiedIndex": 12, "createdIndex": 10}}`,
&Response{Action: "update", PrevNode: &Node{Key: "/foo", Value: "baz", ModifiedIndex: 10, CreatedIndex: 10}, Node: &Node{Key: "/foo", Value: "bar", ModifiedIndex: 12, CreatedIndex: 10}},
&Response{Action: "update", Index: 15, PrevNode: &Node{Key: "/foo", Value: "baz", ModifiedIndex: 10, CreatedIndex: 10}, Node: &Node{Key: "/foo", Value: "bar", ModifiedIndex: 12, CreatedIndex: 10}},
false,
},
// Garbage in body
{
"",
`garbage`,
nil,
true,
@ -296,7 +300,9 @@ func TestUnmarshalSuccessfulResponse(t *testing.T) {
}
for i, tt := range tests {
res, err := unmarshalSuccessfulResponse([]byte(tt.body))
h := make(http.Header)
h.Add("X-Etcd-Index", tt.indexHeader)
res, err := unmarshalSuccessfulResponse(h, []byte(tt.body))
if tt.expectError != (err != nil) {
t.Errorf("#%d: expectError=%t, err=%v", i, tt.expectError, err)
}
@ -305,14 +311,16 @@ func TestUnmarshalSuccessfulResponse(t *testing.T) {
t.Errorf("#%d: received res==%v, but expected res==%v", i, res, tt.res)
continue
} else if tt.res == nil {
// expected and succesfully got nil response
// expected and successfully got nil response
continue
}
if res.Action != tt.res.Action {
t.Errorf("#%d: Action=%s, expected %s", i, res.Action, tt.res.Action)
}
if res.Index != tt.res.Index {
t.Errorf("#%d: Index=%d, expected %d", i, res.Index, tt.res.Index)
}
if !reflect.DeepEqual(res.Node, tt.res.Node) {
t.Errorf("#%d: Node=%v, expected %v", i, res.Node, tt.res.Node)
}
@ -350,7 +358,7 @@ func TestUnmarshalErrorResponse(t *testing.T) {
{http.StatusNotImplemented, unrecognized},
{http.StatusBadGateway, unrecognized},
{http.StatusServiceUnavailable, unrecognized},
{http.StatusGatewayTimeout, unrecognized},
{http.StatusGatewayTimeout, ErrTimeout},
{http.StatusHTTPVersionNotSupported, unrecognized},
}

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package client

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package client

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package discovery
@ -20,6 +18,7 @@ import (
"errors"
"fmt"
"log"
"math"
"net/http"
"net/url"
"path"
@ -44,9 +43,9 @@ var (
ErrTooManyRetries = errors.New("discovery: too many retries")
)
const (
var (
// Number of retries discovery will attempt before giving up and erroring out.
nRetries = uint(3)
nRetries = uint(math.MaxUint32)
)
// JoinCluster will connect to the discovery service at the given url, and
@ -135,7 +134,7 @@ func newDiscovery(durl, dproxyurl string, id types.ID) (*discovery, error) {
func (d *discovery) joinCluster(config string) (string, error) {
// fast path: if the cluster is full, return the error
// do not need to register to the cluster in this case.
if _, _, err := d.checkCluster(); err != nil {
if _, _, _, err := d.checkCluster(); err != nil {
return "", err
}
@ -146,12 +145,12 @@ func (d *discovery) joinCluster(config string) (string, error) {
return "", err
}
nodes, size, err := d.checkCluster()
nodes, size, index, err := d.checkCluster()
if err != nil {
return "", err
}
all, err := d.waitNodes(nodes, size)
all, err := d.waitNodes(nodes, size, index)
if err != nil {
return "", err
}
@ -160,7 +159,7 @@ func (d *discovery) joinCluster(config string) (string, error) {
}
func (d *discovery) getCluster() (string, error) {
nodes, size, err := d.checkCluster()
nodes, size, index, err := d.checkCluster()
if err != nil {
if err == ErrFullCluster {
return nodesToCluster(nodes), nil
@ -168,7 +167,7 @@ func (d *discovery) getCluster() (string, error) {
return "", err
}
all, err := d.waitNodes(nodes, size)
all, err := d.waitNodes(nodes, size, index)
if err != nil {
return "", err
}
@ -180,6 +179,9 @@ func (d *discovery) createSelf(contents string) error {
resp, err := d.c.Create(ctx, d.selfKey(), contents, -1)
cancel()
if err != nil {
if err == client.ErrKeyExists {
return ErrDuplicateID
}
return err
}
@ -189,7 +191,7 @@ func (d *discovery) createSelf(contents string) error {
return err
}
func (d *discovery) checkCluster() (client.Nodes, int, error) {
func (d *discovery) checkCluster() (client.Nodes, int, uint64, error) {
configKey := path.Join("/", d.cluster, "_config")
ctx, cancel := context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
// find cluster size
@ -197,16 +199,16 @@ func (d *discovery) checkCluster() (client.Nodes, int, error) {
cancel()
if err != nil {
if err == client.ErrKeyNoExist {
return nil, 0, ErrSizeNotFound
return nil, 0, 0, ErrSizeNotFound
}
if err == client.ErrTimeout {
return d.checkClusterRetry()
}
return nil, 0, err
return nil, 0, 0, err
}
size, err := strconv.Atoi(resp.Node.Value)
if err != nil {
return nil, 0, ErrBadSizeKey
return nil, 0, 0, ErrBadSizeKey
}
ctx, cancel = context.WithTimeout(context.Background(), client.DefaultRequestTimeout)
@ -216,7 +218,7 @@ func (d *discovery) checkCluster() (client.Nodes, int, error) {
if err == client.ErrTimeout {
return d.checkClusterRetry()
}
return nil, 0, err
return nil, 0, 0, err
}
nodes := make(client.Nodes, 0)
// append non-config keys to nodes
@ -235,10 +237,10 @@ func (d *discovery) checkCluster() (client.Nodes, int, error) {
break
}
if i >= size-1 {
return nodes[:size], size, ErrFullCluster
return nodes[:size], size, resp.Index, ErrFullCluster
}
}
return nodes, size, nil
return nodes, size, resp.Index, nil
}
func (d *discovery) logAndBackoffForRetry(step string) {
@ -248,31 +250,32 @@ func (d *discovery) logAndBackoffForRetry(step string) {
d.clock.Sleep(retryTime)
}
func (d *discovery) checkClusterRetry() (client.Nodes, int, error) {
func (d *discovery) checkClusterRetry() (client.Nodes, int, uint64, error) {
if d.retries < nRetries {
d.logAndBackoffForRetry("cluster status check")
return d.checkCluster()
}
return nil, 0, ErrTooManyRetries
return nil, 0, 0, ErrTooManyRetries
}
func (d *discovery) waitNodesRetry() (client.Nodes, error) {
if d.retries < nRetries {
d.logAndBackoffForRetry("waiting for other nodes")
nodes, n, err := d.checkCluster()
nodes, n, index, err := d.checkCluster()
if err != nil {
return nil, err
}
return d.waitNodes(nodes, n)
return d.waitNodes(nodes, n, index)
}
return nil, ErrTooManyRetries
}
func (d *discovery) waitNodes(nodes client.Nodes, size int) (client.Nodes, error) {
func (d *discovery) waitNodes(nodes client.Nodes, size int, index uint64) (client.Nodes, error) {
if len(nodes) > size {
nodes = nodes[:size]
}
w := d.c.RecursiveWatch(d.cluster, nodes[len(nodes)-1].ModifiedIndex+1)
// watch from the next index
w := d.c.RecursiveWatch(d.cluster, index+1)
all := make(client.Nodes, len(nodes))
copy(all, nodes)
for _, n := range all {

View File

@ -1,23 +1,22 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package discovery
import (
"errors"
"math"
"math/rand"
"net/http"
"reflect"
@ -31,6 +30,10 @@ import (
"github.com/coreos/etcd/client"
)
const (
maxRetryInTest = 3
)
func TestNewProxyFuncUnset(t *testing.T) {
pf, err := newProxyFunc("")
if pf != nil {
@ -89,6 +92,7 @@ func TestCheckCluster(t *testing.T) {
tests := []struct {
nodes []*client.Node
index uint64
werr error
wsize int
}{
@ -102,6 +106,7 @@ func TestCheckCluster(t *testing.T) {
{Key: "/1000/3", CreatedIndex: 4},
{Key: "/1000/4", CreatedIndex: 5},
},
5,
nil,
3,
},
@ -115,6 +120,7 @@ func TestCheckCluster(t *testing.T) {
{Key: self, CreatedIndex: 4},
{Key: "/1000/4", CreatedIndex: 5},
},
5,
nil,
3,
},
@ -128,6 +134,7 @@ func TestCheckCluster(t *testing.T) {
{Key: "/1000/4", CreatedIndex: 4},
{Key: self, CreatedIndex: 5},
},
5,
ErrFullCluster,
3,
},
@ -139,6 +146,7 @@ func TestCheckCluster(t *testing.T) {
{Key: "/1000/2", CreatedIndex: 2},
{Key: "/1000/3", CreatedIndex: 3},
},
3,
nil,
3,
},
@ -150,6 +158,7 @@ func TestCheckCluster(t *testing.T) {
{Key: "/1000/3", CreatedIndex: 3},
{Key: "/1000/4", CreatedIndex: 4},
},
3,
ErrFullCluster,
3,
},
@ -158,12 +167,14 @@ func TestCheckCluster(t *testing.T) {
[]*client.Node{
{Key: "/1000/_config/size", Value: "bad", CreatedIndex: 1},
},
0,
ErrBadSizeKey,
0,
},
{
// no size key
[]*client.Node{},
0,
ErrSizeNotFound,
0,
},
@ -172,12 +183,13 @@ func TestCheckCluster(t *testing.T) {
for i, tt := range tests {
rs := make([]*client.Response, 0)
if len(tt.nodes) > 0 {
rs = append(rs, &client.Response{Node: tt.nodes[0]})
rs = append(rs, &client.Response{Node: tt.nodes[0], Index: tt.index})
rs = append(rs, &client.Response{
Node: &client.Node{
Key: cluster,
Nodes: tt.nodes[1:],
},
Index: tt.index,
})
}
c := &clientWithResp{rs: rs}
@ -190,12 +202,12 @@ func TestCheckCluster(t *testing.T) {
for _, d := range []discovery{d, dRetry} {
go func() {
for i := uint(1); i <= nRetries; i++ {
for i := uint(1); i <= maxRetryInTest; i++ {
fc.BlockUntil(1)
fc.Advance(time.Second * (0x1 << i))
}
}()
ns, size, err := d.checkCluster()
ns, size, index, err := d.checkCluster()
if err != tt.werr {
t.Errorf("#%d: err = %v, want %v", i, err, tt.werr)
}
@ -205,6 +217,9 @@ func TestCheckCluster(t *testing.T) {
if size != tt.wsize {
t.Errorf("#%d: size = %v, want %d", i, size, tt.wsize)
}
if index != tt.index {
t.Errorf("#%d: index = %v, want %d", i, index, tt.index)
}
}
}
}
@ -278,12 +293,12 @@ func TestWaitNodes(t *testing.T) {
for _, d := range []*discovery{d, dRetry} {
go func() {
for i := uint(1); i <= nRetries; i++ {
for i := uint(1); i <= maxRetryInTest; i++ {
fc.BlockUntil(1)
fc.Advance(time.Second * (0x1 << i))
}
}()
g, err := d.waitNodes(tt.nodes, 3)
g, err := d.waitNodes(tt.nodes, 3, 0) // we do not care about index in this test
if err != nil {
t.Errorf("#%d: err = %v, want %v", i, err, nil)
}
@ -368,6 +383,9 @@ func TestSortableNodes(t *testing.T) {
}
func TestRetryFailure(t *testing.T) {
nRetries = maxRetryInTest
defer func() { nRetries = math.MaxUint32 }()
cluster := "1000"
c := &clientWithRetry{failTimes: 4}
fc := clockwork.NewFakeClock()
@ -378,12 +396,12 @@ func TestRetryFailure(t *testing.T) {
clock: fc,
}
go func() {
for i := uint(1); i <= nRetries; i++ {
for i := uint(1); i <= maxRetryInTest; i++ {
fc.BlockUntil(1)
fc.Advance(time.Second * (0x1 << i))
}
}()
if _, _, err := d.checkCluster(); err != ErrTooManyRetries {
if _, _, _, err := d.checkCluster(); err != ErrTooManyRetries {
t.Errorf("err = %v, want %v", err, ErrTooManyRetries)
}
}

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package discovery provides an implementation of the cluster discovery that

93
discovery/srv.go Normal file
View File

@ -0,0 +1,93 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package discovery
import (
"fmt"
"log"
"net"
"strings"
"github.com/coreos/etcd/pkg/types"
)
var (
// indirection for testing
lookupSRV = net.LookupSRV
)
// TODO(barakmich): Currently ignores priority and weight (as they don't make as much sense for a bootstrap)
// Also doesn't do any lookups for the token (though it could)
// Also sees each entry as a separate instance.
func SRVGetCluster(name, dns string, defaultToken string, apurls types.URLs) (string, string, error) {
stringParts := make([]string, 0)
tempName := int(0)
tcpAPUrls := make([]string, 0)
// First, resolve the apurls
for _, url := range apurls {
tcpAddr, err := net.ResolveTCPAddr("tcp", url.Host)
if err != nil {
log.Printf("discovery: Couldn't resolve host %s during SRV discovery", url.Host)
return "", "", err
}
tcpAPUrls = append(tcpAPUrls, tcpAddr.String())
}
updateNodeMap := func(service, prefix string) error {
_, addrs, err := lookupSRV(service, "tcp", dns)
if err != nil {
return err
}
for _, srv := range addrs {
host := net.JoinHostPort(srv.Target, fmt.Sprintf("%d", srv.Port))
tcpAddr, err := net.ResolveTCPAddr("tcp", host)
if err != nil {
log.Printf("discovery: Couldn't resolve host %s during SRV discovery", host)
continue
}
n := ""
for _, url := range tcpAPUrls {
if url == tcpAddr.String() {
n = name
}
}
if n == "" {
n = fmt.Sprintf("%d", tempName)
tempName += 1
}
stringParts = append(stringParts, fmt.Sprintf("%s=%s%s", n, prefix, tcpAddr.String()))
log.Printf("discovery: Got bootstrap from DNS for %s at host %s to %s%s", service, host, prefix, tcpAddr.String())
}
return nil
}
failCount := 0
err := updateNodeMap("etcd-server-ssl", "https://")
if err != nil {
log.Printf("discovery: Error querying DNS SRV records for _etcd-server-ssl %s", err)
failCount += 1
}
err = updateNodeMap("etcd-server", "http://")
if err != nil {
log.Printf("discovery: Error querying DNS SRV records for _etcd-server %s", err)
failCount += 1
}
if failCount == 2 {
log.Printf("discovery: SRV discovery failed: too many errors querying DNS SRV records")
return "", "", err
}
return strings.Join(stringParts, ","), defaultToken, nil
}

99
discovery/srv_test.go Normal file
View File

@ -0,0 +1,99 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package discovery
import (
"errors"
"net"
"testing"
"github.com/coreos/etcd/pkg/testutil"
)
func TestSRVGetCluster(t *testing.T) {
defer func() { lookupSRV = net.LookupSRV }()
name := "dnsClusterTest"
tests := []struct {
withSSL []*net.SRV
withoutSSL []*net.SRV
urls []string
expected string
}{
{
[]*net.SRV{},
[]*net.SRV{},
nil,
"",
},
{
[]*net.SRV{
&net.SRV{Target: "10.0.0.1", Port: 2480},
&net.SRV{Target: "10.0.0.2", Port: 2480},
&net.SRV{Target: "10.0.0.3", Port: 2480},
},
[]*net.SRV{},
nil,
"0=https://10.0.0.1:2480,1=https://10.0.0.2:2480,2=https://10.0.0.3:2480",
},
{
[]*net.SRV{
&net.SRV{Target: "10.0.0.1", Port: 2480},
&net.SRV{Target: "10.0.0.2", Port: 2480},
&net.SRV{Target: "10.0.0.3", Port: 2480},
},
[]*net.SRV{
&net.SRV{Target: "10.0.0.1", Port: 7001},
},
nil,
"0=https://10.0.0.1:2480,1=https://10.0.0.2:2480,2=https://10.0.0.3:2480,3=http://10.0.0.1:7001",
},
{
[]*net.SRV{
&net.SRV{Target: "10.0.0.1", Port: 2480},
&net.SRV{Target: "10.0.0.2", Port: 2480},
&net.SRV{Target: "10.0.0.3", Port: 2480},
},
[]*net.SRV{
&net.SRV{Target: "10.0.0.1", Port: 7001},
},
[]string{"https://10.0.0.1:2480"},
"dnsClusterTest=https://10.0.0.1:2480,0=https://10.0.0.2:2480,1=https://10.0.0.3:2480,2=http://10.0.0.1:7001",
},
}
for i, tt := range tests {
lookupSRV = func(service string, proto string, domain string) (string, []*net.SRV, error) {
if service == "etcd-server-ssl" {
return "", tt.withSSL, nil
}
if service == "etcd-server" {
return "", tt.withoutSSL, nil
}
return "", nil, errors.New("Unkown service in mock")
}
urls := testutil.MustNewURLs(t, tt.urls)
str, token, err := SRVGetCluster(name, "example.com", "token", urls)
if err != nil {
t.Fatalf("%d: err: %#v", i, err)
}
if token != "token" {
t.Errorf("%d: token: %s", i, token)
}
if str != tt.expected {
t.Errorf("#%d: cluster = %s, want %s", i, str, tt.expected)
}
}
}

View File

@ -1,19 +1,20 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// error package describes errors in etcd project.
// When any change happens, Documentation/errorcode.md needs to be updated
// correspondingly.
package error
import (
@ -27,24 +28,24 @@ var errors = map[int]string{
EcodeKeyNotFound: "Key not found",
EcodeTestFailed: "Compare failed", //test and set
EcodeNotFile: "Not a file",
EcodeNoMorePeer: "Reached the max number of peers in the cluster",
ecodeNoMorePeer: "Reached the max number of peers in the cluster",
EcodeNotDir: "Not a directory",
EcodeNodeExist: "Key already exists", // create
ecodeKeyIsPreserved: "The prefix of given key is a keyword in etcd",
EcodeRootROnly: "Root is read only",
EcodeKeyIsPreserved: "The prefix of given key is a keyword in etcd",
EcodeDirNotEmpty: "Directory not empty",
EcodeExistingPeerAddr: "Peer address has existed",
ecodeExistingPeerAddr: "Peer address has existed",
// Post form related errors
EcodeValueRequired: "Value is Required in POST form",
ecodeValueRequired: "Value is Required in POST form",
EcodePrevValueRequired: "PrevValue is Required in POST form",
EcodeTTLNaN: "The given TTL in POST form is not a number",
EcodeIndexNaN: "The given index in POST form is not a number",
EcodeValueOrTTLRequired: "Value or TTL is required in POST form",
EcodeTimeoutNaN: "The given timeout in POST form is not a number",
EcodeNameRequired: "Name is required in POST form",
EcodeIndexOrValueRequired: "Index or value is required",
EcodeIndexValueMutex: "Index and value cannot both be specified",
ecodeValueOrTTLRequired: "Value or TTL is required in POST form",
ecodeTimeoutNaN: "The given timeout in POST form is not a number",
ecodeNameRequired: "Name is required in POST form",
ecodeIndexOrValueRequired: "Index or value is required",
ecodeIndexValueMutex: "Index and value cannot both be specified",
EcodeInvalidField: "Invalid field",
EcodeInvalidForm: "Invalid POST form",
@ -55,12 +56,12 @@ var errors = map[int]string{
// etcd related errors
EcodeWatcherCleared: "watcher is cleared due to etcd recovery",
EcodeEventIndexCleared: "The event in requested index is outdated and cleared",
EcodeStandbyInternal: "Standby Internal Error",
EcodeInvalidActiveSize: "Invalid active size",
EcodeInvalidRemoveDelay: "Standby remove delay",
ecodeStandbyInternal: "Standby Internal Error",
ecodeInvalidActiveSize: "Invalid active size",
ecodeInvalidRemoveDelay: "Standby remove delay",
// client related errors
EcodeClientInternal: "Client Internal Error",
ecodeClientInternal: "Client Internal Error",
}
var errorStatus = map[int]int{
@ -77,23 +78,23 @@ const (
EcodeKeyNotFound = 100
EcodeTestFailed = 101
EcodeNotFile = 102
EcodeNoMorePeer = 103
ecodeNoMorePeer = 103
EcodeNotDir = 104
EcodeNodeExist = 105
EcodeKeyIsPreserved = 106
ecodeKeyIsPreserved = 106
EcodeRootROnly = 107
EcodeDirNotEmpty = 108
EcodeExistingPeerAddr = 109
ecodeExistingPeerAddr = 109
EcodeValueRequired = 200
ecodeValueRequired = 200
EcodePrevValueRequired = 201
EcodeTTLNaN = 202
EcodeIndexNaN = 203
EcodeValueOrTTLRequired = 204
EcodeTimeoutNaN = 205
EcodeNameRequired = 206
EcodeIndexOrValueRequired = 207
EcodeIndexValueMutex = 208
ecodeValueOrTTLRequired = 204
ecodeTimeoutNaN = 205
ecodeNameRequired = 206
ecodeIndexOrValueRequired = 207
ecodeIndexValueMutex = 208
EcodeInvalidField = 209
EcodeInvalidForm = 210
@ -102,11 +103,11 @@ const (
EcodeWatcherCleared = 400
EcodeEventIndexCleared = 401
EcodeStandbyInternal = 402
EcodeInvalidActiveSize = 403
EcodeInvalidRemoveDelay = 404
ecodeStandbyInternal = 402
ecodeInvalidActiveSize = 403
ecodeInvalidRemoveDelay = 404
EcodeClientInternal = 500
ecodeClientInternal = 500
)
type Error struct {
@ -129,10 +130,6 @@ func NewError(errorCode int, cause string, index uint64) *Error {
}
}
func Message(code int) string {
return errors[code]
}
// Only for error interface
func (e Error) Error() string {
return e.Message + " (" + e.Cause + ")"

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package error

View File

@ -83,13 +83,6 @@ $ etcdctl get /foo/bar
Hello world
```
Get the current value for a key within the cluster:
```
$ etcdctl get /foo/bar --consistent
Hello world
```
Get the value of a key with additional metadata in a parseable format:
```
@ -203,26 +196,26 @@ Continuously watch a key and exec a program:
```
$ etcdctl exec-watch /foo/bar -- sh -c "env | grep ETCD"
ETCD_WATCH_ACTION=set
ETCD_VALUE=My configuration stuff
ETCD_MODIFIED_INDEX=1999
ETCD_KEY=/foo/bar
ETCD_WATCH_VALUE=My configuration stuff
ETCD_WATCH_MODIFIED_INDEX=1999
ETCD_WATCH_KEY=/foo/bar
ETCD_WATCH_ACTION=set
ETCD_VALUE=My new configuration stuff
ETCD_MODIFIED_INDEX=2000
ETCD_KEY=/foo/bar
ETCD_WATCH_VALUE=My new configuration stuff
ETCD_WATCH_MODIFIED_INDEX=2000
ETCD_WATCH_KEY=/foo/bar
```
Continuously and recursively watch a key and exec a program:
```
$ etcdctl exec-watch --recursive /foo -- sh -c "env | grep ETCD"
ETCD_WATCH_ACTION=set
ETCD_VALUE=My configuration stuff
ETCD_MODIFIED_INDEX=1999
ETCD_KEY=/foo/bar
ETCD_WATCH_VALUE=My configuration stuff
ETCD_WATCH_MODIFIED_INDEX=1999
ETCD_WATCH_KEY=/foo/bar
ETCD_WATCH_ACTION=set
ETCD_VALUE=My new configuration stuff
ETCD_MODIFIED_INDEX=2000
ETCD_KEY=/foo/barbar
ETCD_WATCH_VALUE=My new configuration stuff
ETCD_WATCH_MODIFIED_INDEX=2000
ETCD_WATCH_KEY=/foo/barbar
```
## Return Codes

View File

@ -1,34 +1,32 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command
import (
"log"
"math/rand"
"os"
"path"
"time"
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
"github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/pkg/idutil"
"github.com/coreos/etcd/pkg/pbutil"
"github.com/coreos/etcd/snap"
"github.com/coreos/etcd/wal"
"github.com/coreos/etcd/wal/walpb"
)
func NewBackupCommand() cli.Command {
@ -58,16 +56,16 @@ func handleBackup(c *cli.Context) {
if err != nil && err != snap.ErrNoSnapshot {
log.Fatal(err)
}
var index uint64
var walsnap walpb.Snapshot
if snapshot != nil {
index = snapshot.Index
walsnap.Index, walsnap.Term = snapshot.Metadata.Index, snapshot.Metadata.Term
newss := snap.New(destSnap)
if err := newss.SaveSnap(*snapshot); err != nil {
log.Fatal(err)
}
}
w, err := wal.OpenAtIndex(srcWAL, index)
w, err := wal.OpenNotInUse(srcWAL, walsnap)
if err != nil {
log.Fatal(err)
}
@ -78,9 +76,9 @@ func handleBackup(c *cli.Context) {
}
var metadata etcdserverpb.Metadata
pbutil.MustUnmarshal(&metadata, wmetadata)
rand.Seed(time.Now().UnixNano())
metadata.NodeID = etcdserver.GenID()
metadata.ClusterID = etcdserver.GenID()
idgen := idutil.NewGenerator(0, time.Now())
metadata.NodeID = idgen.Next()
metadata.ClusterID = idgen.Next()
neww, err := wal.Create(destWAL, pbutil.MustMarshal(&metadata))
if err != nil {

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command
@ -32,7 +30,6 @@ func NewGetCommand() cli.Command {
Usage: "retrieve the value of a key",
Flags: []cli.Flag{
cli.BoolFlag{Name: "sort", Usage: "returns result in sorted order"},
cli.BoolFlag{Name: "consistent", Usage: "send request to the leader, thereby guranteeing that any earlier writes will be seen by the read"},
},
Action: func(c *cli.Context) {
handleGet(c, getCommandFunc)
@ -61,16 +58,8 @@ func getCommandFunc(c *cli.Context, client *etcd.Client) (*etcd.Response, error)
return nil, errors.New("Key required")
}
key := c.Args()[0]
consistent := c.Bool("consistent")
sorted := c.Bool("sort")
// Setup consistency on the client.
if consistent {
client.SetConsistency(etcd.STRONG_CONSISTENCY)
} else {
client.SetConsistency(etcd.WEAK_CONSISTENCY)
}
// Retrieve the value from the server.
return client.Get(key, sorted, false)
}

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -0,0 +1,69 @@
/*
Copyright 2015 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package command
import (
"fmt"
"log"
"net/http"
"os"
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/codegangsta/cli"
)
func UpgradeCommand() cli.Command {
return cli.Command{
Name: "upgrade",
Usage: "upgrade an old version etcd cluster to a new version",
Flags: []cli.Flag{
cli.StringFlag{Name: "old-version", Value: "1", Usage: "Old internal version"},
cli.StringFlag{Name: "new-version", Value: "2", Usage: "New internal version"},
cli.StringFlag{Name: "peer-url", Value: "", Usage: "An etcd peer url string"},
},
Action: handleUpgrade,
}
}
func handleUpgrade(c *cli.Context) {
if c.String("old-version") != "1" {
fmt.Printf("Do not support upgrade from version %s\n", c.String("old-version"))
os.Exit(1)
}
if c.String("new-version") != "2" {
fmt.Printf("Do not support upgrade to version %s\n", c.String("new-version"))
os.Exit(1)
}
t, err := getTransport(c)
if err != nil {
log.Fatal(err)
}
client := http.Client{Transport: t}
resp, err := client.Get(c.String("peer-url") + "/v2/admin/next-internal-version")
if err != nil {
fmt.Printf("Failed to send upgrade request to %s: %v\n", c.String("peer-url"), err)
return
}
if resp.StatusCode == http.StatusOK {
fmt.Println("Cluster will start upgrading from internal version 1 to 2 in 10 seconds.")
return
}
if resp.StatusCode == http.StatusNotFound {
fmt.Println("Cluster cannot upgrade to 2: version is not 0.4.7")
return
}
fmt.Printf("Faild to send upgrade request to %s: bad status code %d\n", c.String("cluster-url"), resp.StatusCode)
}

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command
@ -91,10 +89,26 @@ func getEndpoints(c *cli.Context) ([]string, error) {
}
func getTransport(c *cli.Context) (*http.Transport, error) {
cafile := c.GlobalString("ca-file")
certfile := c.GlobalString("cert-file")
keyfile := c.GlobalString("key-file")
// Use an environment variable if nothing was supplied on the
// command line
if cafile == "" {
cafile = os.Getenv("ETCDCTL_CA_FILE")
}
if certfile == "" {
certfile = os.Getenv("ETCDCTL_CERT_FILE")
}
if keyfile == "" {
keyfile = os.Getenv("ETCDCTL_KEY_FILE")
}
tls := transport.TLSInfo{
CAFile: c.GlobalString("ca-file"),
CertFile: c.GlobalString("cert-file"),
KeyFile: c.GlobalString("key-file"),
CAFile: cafile,
CertFile: certfile,
KeyFile: keyfile,
}
return transport.NewTransport(tls)

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package command

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
@ -54,6 +52,7 @@ func main() {
command.NewWatchCommand(),
command.NewExecWatchCommand(),
command.NewMemberCommand(),
command.UpgradeCommand(),
}
app.Run(os.Args)

267
etcdmain/config.go Normal file
View File

@ -0,0 +1,267 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdmain
import (
"errors"
"flag"
"fmt"
"log"
"net/url"
"os"
"strings"
"github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/pkg/cors"
"github.com/coreos/etcd/pkg/flags"
"github.com/coreos/etcd/pkg/netutil"
"github.com/coreos/etcd/pkg/transport"
"github.com/coreos/etcd/version"
)
const (
proxyFlagOff = "off"
proxyFlagReadonly = "readonly"
proxyFlagOn = "on"
fallbackFlagExit = "exit"
fallbackFlagProxy = "proxy"
clusterStateFlagNew = "new"
clusterStateFlagExisting = "existing"
)
var (
ignored = []string{
"cluster-active-size",
"cluster-remove-delay",
"cluster-sync-interval",
"config",
"force",
"max-result-buffer",
"max-retry-attempts",
"peer-heartbeat-interval",
"peer-election-timeout",
"retry-interval",
"snapshot",
"v",
"vv",
}
ErrConflictBootstrapFlags = fmt.Errorf("multiple discovery or bootstrap flags are set" +
"Choose one of \"initial-cluster\", \"discovery\" or \"discovery-srv\"")
)
type config struct {
*flag.FlagSet
// member
corsInfo *cors.CORSInfo
dir string
lpurls, lcurls []url.URL
maxSnapFiles uint
maxWalFiles uint
name string
snapCount uint64
// TODO: decouple tickMs and heartbeat tick (current heartbeat tick = 1).
// make ticks a cluster wide configuration.
TickMs uint
ElectionMs uint
// clustering
apurls, acurls []url.URL
clusterState *flags.StringsFlag
dnsCluster string
dproxy string
durl string
fallback *flags.StringsFlag
initialCluster string
initialClusterToken string
// proxy
proxy *flags.StringsFlag
// security
clientTLSInfo, peerTLSInfo transport.TLSInfo
// unsafe
forceNewCluster bool
printVersion bool
ignored []string
}
func NewConfig() *config {
cfg := &config{
corsInfo: &cors.CORSInfo{},
clusterState: flags.NewStringsFlag(
clusterStateFlagNew,
clusterStateFlagExisting,
),
fallback: flags.NewStringsFlag(
fallbackFlagExit,
fallbackFlagProxy,
),
ignored: ignored,
proxy: flags.NewStringsFlag(
proxyFlagOff,
proxyFlagReadonly,
proxyFlagOn,
),
}
cfg.FlagSet = flag.NewFlagSet("etcd", flag.ContinueOnError)
fs := cfg.FlagSet
fs.Usage = func() {
fmt.Println(usageline)
fmt.Println(flagsline)
}
// member
fs.Var(cfg.corsInfo, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")
fs.StringVar(&cfg.dir, "data-dir", "", "Path to the data directory")
fs.Var(flags.NewURLsValue("http://localhost:2380,http://localhost:7001"), "listen-peer-urls", "List of URLs to listen on for peer traffic")
fs.Var(flags.NewURLsValue("http://localhost:2379,http://localhost:4001"), "listen-client-urls", "List of URLs to listen on for client traffic")
fs.UintVar(&cfg.maxSnapFiles, "max-snapshots", defaultMaxSnapshots, "Maximum number of snapshot files to retain (0 is unlimited)")
fs.UintVar(&cfg.maxWalFiles, "max-wals", defaultMaxWALs, "Maximum number of wal files to retain (0 is unlimited)")
fs.StringVar(&cfg.name, "name", "default", "Unique human-readable name for this node")
fs.Uint64Var(&cfg.snapCount, "snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot")
fs.UintVar(&cfg.TickMs, "heartbeat-interval", 100, "Time (in milliseconds) of a heartbeat interval.")
fs.UintVar(&cfg.ElectionMs, "election-timeout", 1000, "Time (in milliseconds) for an election to timeout.")
// clustering
fs.Var(flags.NewURLsValue("http://localhost:2380,http://localhost:7001"), "initial-advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster")
fs.Var(flags.NewURLsValue("http://localhost:2379,http://localhost:4001"), "advertise-client-urls", "List of this member's client URLs to advertise to the rest of the cluster")
fs.StringVar(&cfg.durl, "discovery", "", "Discovery service used to bootstrap the initial cluster")
fs.Var(cfg.fallback, "discovery-fallback", fmt.Sprintf("Valid values include %s", strings.Join(cfg.fallback.Values, ", ")))
if err := cfg.fallback.Set(fallbackFlagProxy); err != nil {
// Should never happen.
log.Panicf("unexpected error setting up discovery-fallback flag: %v", err)
}
fs.StringVar(&cfg.dproxy, "discovery-proxy", "", "HTTP proxy to use for traffic to discovery service")
fs.StringVar(&cfg.dnsCluster, "discovery-srv", "", "DNS domain used to bootstrap initial cluster")
fs.StringVar(&cfg.initialCluster, "initial-cluster", "default=http://localhost:2380,default=http://localhost:7001", "Initial cluster configuration for bootstrapping")
fs.StringVar(&cfg.initialClusterToken, "initial-cluster-token", "etcd-cluster", "Initial cluster token for the etcd cluster during bootstrap")
fs.Var(cfg.clusterState, "initial-cluster-state", "Initial cluster configuration for bootstrapping")
if err := cfg.clusterState.Set(clusterStateFlagNew); err != nil {
// Should never happen.
log.Panicf("unexpected error setting up clusterStateFlag: %v", err)
}
// proxy
fs.Var(cfg.proxy, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(cfg.proxy.Values, ", ")))
if err := cfg.proxy.Set(proxyFlagOff); err != nil {
// Should never happen.
log.Panicf("unexpected error setting up proxyFlag: %v", err)
}
// security
fs.StringVar(&cfg.clientTLSInfo.CAFile, "ca-file", "", "Path to the client server TLS CA file.")
fs.StringVar(&cfg.clientTLSInfo.CertFile, "cert-file", "", "Path to the client server TLS cert file.")
fs.StringVar(&cfg.clientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.")
fs.StringVar(&cfg.peerTLSInfo.CAFile, "peer-ca-file", "", "Path to the peer server TLS CA file.")
fs.StringVar(&cfg.peerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.")
fs.StringVar(&cfg.peerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.")
// unsafe
fs.BoolVar(&cfg.forceNewCluster, "force-new-cluster", false, "Force to create a new one member cluster")
// version
fs.BoolVar(&cfg.printVersion, "version", false, "Print the version and exit")
// backwards-compatibility with v0.4.6
fs.Var(&flags.IPAddressPort{}, "addr", "DEPRECATED: Use -advertise-client-urls instead.")
fs.Var(&flags.IPAddressPort{}, "bind-addr", "DEPRECATED: Use -listen-client-urls instead.")
fs.Var(&flags.IPAddressPort{}, "peer-addr", "DEPRECATED: Use -initial-advertise-peer-urls instead.")
fs.Var(&flags.IPAddressPort{}, "peer-bind-addr", "DEPRECATED: Use -listen-peer-urls instead.")
fs.Var(&flags.DeprecatedFlag{Name: "peers"}, "peers", "DEPRECATED: Use -initial-cluster instead")
fs.Var(&flags.DeprecatedFlag{Name: "peers-file"}, "peers-file", "DEPRECATED: Use -initial-cluster instead")
// ignored
for _, f := range cfg.ignored {
fs.Var(&flags.IgnoredFlag{Name: f}, f, "")
}
return cfg
}
func (cfg *config) Parse(arguments []string) error {
perr := cfg.FlagSet.Parse(arguments)
switch perr {
case nil:
case flag.ErrHelp:
os.Exit(0)
default:
os.Exit(2)
}
if cfg.printVersion {
fmt.Println("etcd version", version.Version)
os.Exit(0)
}
err := flags.SetFlagsFromEnv(cfg.FlagSet)
if err != nil {
log.Fatalf("etcd: %v", err)
}
set := make(map[string]bool)
cfg.FlagSet.Visit(func(f *flag.Flag) {
set[f.Name] = true
})
nSet := 0
for _, v := range []bool{set["discovery"], set["initial-cluster"], set["discovery-srv"]} {
if v {
nSet += 1
}
}
if nSet > 1 {
return ErrConflictBootstrapFlags
}
cfg.lpurls, err = flags.URLsFromFlags(cfg.FlagSet, "listen-peer-urls", "peer-bind-addr", cfg.peerTLSInfo)
if err != nil {
return err
}
cfg.apurls, err = flags.URLsFromFlags(cfg.FlagSet, "initial-advertise-peer-urls", "peer-addr", cfg.peerTLSInfo)
if err != nil {
return err
}
cfg.lcurls, err = flags.URLsFromFlags(cfg.FlagSet, "listen-client-urls", "bind-addr", cfg.clientTLSInfo)
if err != nil {
return err
}
cfg.acurls, err = flags.URLsFromFlags(cfg.FlagSet, "advertise-client-urls", "addr", cfg.clientTLSInfo)
if err != nil {
return err
}
if err := cfg.resolveUrls(); err != nil {
return errors.New("cannot resolve DNS hostnames.")
}
return nil
}
func (cfg *config) resolveUrls() error {
return netutil.ResolveTCPAddrs(cfg.lpurls, cfg.apurls, cfg.lcurls, cfg.acurls)
}
func (cfg config) isNewCluster() bool { return cfg.clusterState.String() == clusterStateFlagNew }
func (cfg config) isProxy() bool { return cfg.proxy.String() != proxyFlagOff }
func (cfg config) isReadonlyProxy() bool { return cfg.proxy.String() == proxyFlagReadonly }
func (cfg config) shouldFallbackToProxy() bool { return cfg.fallback.String() == fallbackFlagProxy }
func (cfg config) electionTicks() int { return int(cfg.ElectionMs / cfg.TickMs) }

260
etcdmain/config_test.go Normal file
View File

@ -0,0 +1,260 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdmain
import (
"net/url"
"reflect"
"testing"
)
func TestConfigParsingMemberFlags(t *testing.T) {
args := []string{
"-data-dir=testdir",
"-name=testname",
"-max-wals=10",
"-max-snapshots=10",
"-snapshot-count=10",
"-listen-peer-urls=http://localhost:8000,https://localhost:8001",
"-listen-client-urls=http://localhost:7000,https://localhost:7001",
}
wcfg := &config{
dir: "testdir",
lpurls: []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}},
lcurls: []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}},
maxSnapFiles: 10,
maxWalFiles: 10,
name: "testname",
snapCount: 10,
}
cfg := NewConfig()
err := cfg.Parse(args)
if err != nil {
t.Fatal(err)
}
if cfg.dir != wcfg.dir {
t.Errorf("dir = %v, want %v", cfg.dir, wcfg.dir)
}
if cfg.maxSnapFiles != wcfg.maxSnapFiles {
t.Errorf("maxsnap = %v, want %v", cfg.maxSnapFiles, wcfg.maxSnapFiles)
}
if cfg.maxWalFiles != wcfg.maxWalFiles {
t.Errorf("maxwal = %v, want %v", cfg.maxWalFiles, wcfg.maxWalFiles)
}
if cfg.name != wcfg.name {
t.Errorf("name = %v, want %v", cfg.name, wcfg.name)
}
if cfg.snapCount != wcfg.snapCount {
t.Errorf("snapcount = %v, want %v", cfg.snapCount, wcfg.snapCount)
}
if !reflect.DeepEqual(cfg.lpurls, wcfg.lpurls) {
t.Errorf("listen-peer-urls = %v, want %v", cfg.lpurls, wcfg.lpurls)
}
if !reflect.DeepEqual(cfg.lcurls, wcfg.lcurls) {
t.Errorf("listen-client-urls = %v, want %v", cfg.lcurls, wcfg.lcurls)
}
}
func TestConfigParsingClusteringFlags(t *testing.T) {
args := []string{
"-initial-cluster=0=http://localhost:8000",
"-initial-cluster-state=existing",
"-initial-cluster-token=etcdtest",
"-initial-advertise-peer-urls=http://localhost:8000,https://localhost:8001",
"-advertise-client-urls=http://localhost:7000,https://localhost:7001",
"-discovery-fallback=exit",
}
wcfg := NewConfig()
wcfg.apurls = []url.URL{{Scheme: "http", Host: "localhost:8000"}, {Scheme: "https", Host: "localhost:8001"}}
wcfg.acurls = []url.URL{{Scheme: "http", Host: "localhost:7000"}, {Scheme: "https", Host: "localhost:7001"}}
wcfg.clusterState.Set(clusterStateFlagExisting)
wcfg.fallback.Set(fallbackFlagExit)
wcfg.initialCluster = "0=http://localhost:8000"
wcfg.initialClusterToken = "etcdtest"
cfg := NewConfig()
err := cfg.Parse(args)
if err != nil {
t.Fatal(err)
}
if cfg.clusterState.String() != wcfg.clusterState.String() {
t.Errorf("clusterState = %v, want %v", cfg.clusterState, wcfg.clusterState)
}
if cfg.fallback.String() != wcfg.fallback.String() {
t.Errorf("fallback = %v, want %v", cfg.fallback, wcfg.fallback)
}
if cfg.initialCluster != wcfg.initialCluster {
t.Errorf("initialCluster = %v, want %v", cfg.initialCluster, wcfg.initialCluster)
}
if cfg.initialClusterToken != wcfg.initialClusterToken {
t.Errorf("initialClusterToken = %v, want %v", cfg.initialClusterToken, wcfg.initialClusterToken)
}
if !reflect.DeepEqual(cfg.apurls, wcfg.apurls) {
t.Errorf("initial-advertise-peer-urls = %v, want %v", cfg.lpurls, wcfg.lpurls)
}
if !reflect.DeepEqual(cfg.acurls, wcfg.acurls) {
t.Errorf("advertise-client-urls = %v, want %v", cfg.lcurls, wcfg.lcurls)
}
}
func TestConfigParsingOtherFlags(t *testing.T) {
args := []string{
"-proxy=readonly",
"-ca-file=cafile",
"-cert-file=certfile",
"-key-file=keyfile",
"-peer-ca-file=peercafile",
"-peer-cert-file=peercertfile",
"-peer-key-file=peerkeyfile",
"-force-new-cluster=true",
}
wcfg := NewConfig()
wcfg.proxy.Set(proxyFlagReadonly)
wcfg.clientTLSInfo.CAFile = "cafile"
wcfg.clientTLSInfo.CertFile = "certfile"
wcfg.clientTLSInfo.KeyFile = "keyfile"
wcfg.peerTLSInfo.CAFile = "peercafile"
wcfg.peerTLSInfo.CertFile = "peercertfile"
wcfg.peerTLSInfo.KeyFile = "peerkeyfile"
wcfg.forceNewCluster = true
cfg := NewConfig()
err := cfg.Parse(args)
if err != nil {
t.Fatal(err)
}
if cfg.proxy.String() != wcfg.proxy.String() {
t.Errorf("proxy = %v, want %v", cfg.proxy, wcfg.proxy)
}
if cfg.clientTLSInfo.String() != wcfg.clientTLSInfo.String() {
t.Errorf("clientTLS = %v, want %v", cfg.clientTLSInfo, wcfg.clientTLSInfo)
}
if cfg.peerTLSInfo.String() != wcfg.peerTLSInfo.String() {
t.Errorf("peerTLS = %v, want %v", cfg.peerTLSInfo, wcfg.peerTLSInfo)
}
if cfg.forceNewCluster != wcfg.forceNewCluster {
t.Errorf("forceNewCluster = %t, want %t", cfg.forceNewCluster, wcfg.forceNewCluster)
}
}
func TestConfigParsingConflictClusteringFlags(t *testing.T) {
conflictArgs := [][]string{
[]string{
"-initial-cluster=0=localhost:8000",
"-discovery=http://example.com/abc",
},
[]string{
"-discovery-srv=example.com",
"-discovery=http://example.com/abc",
},
[]string{
"-initial-cluster=0=localhost:8000",
"-discovery-srv=example.com",
},
[]string{
"-initial-cluster=0=localhost:8000",
"-discovery=http://example.com/abc",
"-discovery-srv=example.com",
},
}
for i, tt := range conflictArgs {
cfg := NewConfig()
err := cfg.Parse(tt)
if err != ErrConflictBootstrapFlags {
t.Errorf("%d: err = %v, want %v", i, err, ErrConflictBootstrapFlags)
}
}
}
func TestConfigIsNewCluster(t *testing.T) {
tests := []struct {
state string
wIsNew bool
}{
{clusterStateFlagExisting, false},
{clusterStateFlagNew, true},
}
for i, tt := range tests {
cfg := NewConfig()
if err := cfg.clusterState.Set(tt.state); err != nil {
t.Fatalf("#%d: unexpected clusterState.Set error: %v", i, err)
}
if g := cfg.isNewCluster(); g != tt.wIsNew {
t.Errorf("#%d: isNewCluster = %v, want %v", i, g, tt.wIsNew)
}
}
}
func TestConfigIsProxy(t *testing.T) {
tests := []struct {
proxy string
wIsProxy bool
}{
{proxyFlagOff, false},
{proxyFlagReadonly, true},
{proxyFlagOn, true},
}
for i, tt := range tests {
cfg := NewConfig()
if err := cfg.proxy.Set(tt.proxy); err != nil {
t.Fatalf("#%d: unexpected proxy.Set error: %v", i, err)
}
if g := cfg.isProxy(); g != tt.wIsProxy {
t.Errorf("#%d: isProxy = %v, want %v", i, g, tt.wIsProxy)
}
}
}
func TestConfigIsReadonlyProxy(t *testing.T) {
tests := []struct {
proxy string
wIsReadonly bool
}{
{proxyFlagOff, false},
{proxyFlagReadonly, true},
{proxyFlagOn, false},
}
for i, tt := range tests {
cfg := NewConfig()
if err := cfg.proxy.Set(tt.proxy); err != nil {
t.Fatalf("#%d: unexpected proxy.Set error: %v", i, err)
}
if g := cfg.isReadonlyProxy(); g != tt.wIsReadonly {
t.Errorf("#%d: isReadonlyProxy = %v, want %v", i, g, tt.wIsReadonly)
}
}
}
func TestConfigShouldFallbackToProxy(t *testing.T) {
tests := []struct {
fallback string
wFallback bool
}{
{fallbackFlagProxy, true},
{fallbackFlagExit, false},
}
for i, tt := range tests {
cfg := NewConfig()
if err := cfg.fallback.Set(tt.fallback); err != nil {
t.Fatalf("#%d: unexpected fallback.Set error: %v", i, err)
}
if g := cfg.shouldFallbackToProxy(); g != tt.wFallback {
t.Errorf("#%d: shouldFallbackToProxy = %v, want %v", i, g, tt.wFallback)
}
}
}

22
etcdmain/const_unix.go Normal file
View File

@ -0,0 +1,22 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build !windows,!plan9
package etcdmain
const (
defaultMaxSnapshots = 5
defaultMaxWALs = 5
)

26
etcdmain/const_windows.go Normal file
View File

@ -0,0 +1,26 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build windows
package etcdmain
// TODO(barakmich): So because file locking on Windows is untested, the
// temporary fix is to default to unlimited snapshots and WAL files, with manual
// removal. Perhaps not the most elegant solution, but it's at least safe and
// we'd totally love a PR to fix the story around locking.
const (
defaultMaxSnapshots = 0
defaultMaxWALs = 0
)

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/* Package etcd contains the main entry point for the etcd binary. */

View File

@ -1,237 +1,108 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdmain
import (
"flag"
"fmt"
"log"
"net"
"net/http"
"net/url"
"os"
"strings"
"time"
"github.com/coreos/etcd/discovery"
"github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/etcdserver/etcdhttp"
"github.com/coreos/etcd/pkg/cors"
"github.com/coreos/etcd/pkg/fileutil"
"github.com/coreos/etcd/pkg/flags"
"github.com/coreos/etcd/pkg/transport"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/proxy"
"github.com/coreos/etcd/rafthttp"
"github.com/coreos/etcd/version"
)
const (
// the owner can make/remove files inside the directory
privateDirMode = 0700
proxyFlagOff = "off"
proxyFlagReadonly = "readonly"
proxyFlagOn = "on"
fallbackFlagExit = "exit"
fallbackFlagProxy = "proxy"
clusterStateFlagNew = "new"
clusterStateFlagExisting = "existing"
)
var (
fs = flag.NewFlagSet("etcd", flag.ContinueOnError)
name = fs.String("name", "default", "Unique human-readable name for this node")
dir = fs.String("data-dir", "", "Path to the data directory")
durl = fs.String("discovery", "", "Discovery service used to bootstrap the cluster")
dproxy = fs.String("discovery-proxy", "", "HTTP proxy to use for traffic to discovery service")
snapCount = fs.Uint64("snapshot-count", etcdserver.DefaultSnapCount, "Number of committed transactions to trigger a snapshot")
printVersion = fs.Bool("version", false, "Print the version and exit")
forceNewCluster = fs.Bool("force-new-cluster", false, "Force to create a new one member cluster")
initialCluster = fs.String("initial-cluster", "default=http://localhost:2380,default=http://localhost:7001", "Initial cluster configuration for bootstrapping")
initialClusterToken = fs.String("initial-cluster-token", "etcd-cluster", "Initial cluster token for the etcd cluster during bootstrap")
corsInfo = &cors.CORSInfo{}
clientTLSInfo = transport.TLSInfo{}
peerTLSInfo = transport.TLSInfo{}
proxyFlag = flags.NewStringsFlag(
proxyFlagOff,
proxyFlagReadonly,
proxyFlagOn,
)
fallbackFlag = flags.NewStringsFlag(
fallbackFlagExit,
fallbackFlagProxy,
)
clusterStateFlag = flags.NewStringsFlag(
clusterStateFlagNew,
clusterStateFlagExisting,
)
ignored = []string{
"cluster-active-size",
"cluster-remove-delay",
"cluster-sync-interval",
"config",
"force",
"max-result-buffer",
"max-retry-attempts",
"peer-heartbeat-interval",
"peer-election-timeout",
"retry-interval",
"snapshot",
"v",
"vv",
}
)
func init() {
fs.Var(clusterStateFlag, "initial-cluster-state", "Initial cluster configuration for bootstrapping")
if err := clusterStateFlag.Set(clusterStateFlagNew); err != nil {
// Should never happen.
log.Panicf("unexpected error setting up clusterStateFlag: %v", err)
}
fs.Var(flags.NewURLsValue("http://localhost:2380,http://localhost:7001"), "initial-advertise-peer-urls", "List of this member's peer URLs to advertise to the rest of the cluster")
fs.Var(flags.NewURLsValue("http://localhost:2379,http://localhost:4001"), "advertise-client-urls", "List of this member's client URLs to advertise to the rest of the cluster")
fs.Var(flags.NewURLsValue("http://localhost:2380,http://localhost:7001"), "listen-peer-urls", "List of URLs to listen on for peer traffic")
fs.Var(flags.NewURLsValue("http://localhost:2379,http://localhost:4001"), "listen-client-urls", "List of URLs to listen on for client traffic")
fs.Var(corsInfo, "cors", "Comma-separated white list of origins for CORS (cross-origin resource sharing).")
fs.Var(proxyFlag, "proxy", fmt.Sprintf("Valid values include %s", strings.Join(proxyFlag.Values, ", ")))
if err := proxyFlag.Set(proxyFlagOff); err != nil {
// Should never happen.
log.Panicf("unexpected error setting up proxyFlag: %v", err)
}
fs.Var(fallbackFlag, "discovery-fallback", fmt.Sprintf("Valid values include %s", strings.Join(fallbackFlag.Values, ", ")))
if err := fallbackFlag.Set(fallbackFlagProxy); err != nil {
// Should never happen.
log.Panicf("unexpected error setting up discovery-fallback flag: %v", err)
}
fs.StringVar(&clientTLSInfo.CAFile, "ca-file", "", "Path to the client server TLS CA file.")
fs.StringVar(&clientTLSInfo.CertFile, "cert-file", "", "Path to the client server TLS cert file.")
fs.StringVar(&clientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.")
fs.StringVar(&peerTLSInfo.CAFile, "peer-ca-file", "", "Path to the peer server TLS CA file.")
fs.StringVar(&peerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.")
fs.StringVar(&peerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.")
// backwards-compatibility with v0.4.6
fs.Var(&flags.IPAddressPort{}, "addr", "DEPRECATED: Use -advertise-client-urls instead.")
fs.Var(&flags.IPAddressPort{}, "bind-addr", "DEPRECATED: Use -listen-client-urls instead.")
fs.Var(&flags.IPAddressPort{}, "peer-addr", "DEPRECATED: Use -initial-advertise-peer-urls instead.")
fs.Var(&flags.IPAddressPort{}, "peer-bind-addr", "DEPRECATED: Use -listen-peer-urls instead.")
for _, f := range ignored {
fs.Var(&flags.IgnoredFlag{Name: f}, f, "")
}
fs.Var(&flags.DeprecatedFlag{Name: "peers"}, "peers", "DEPRECATED: Use -initial-cluster instead")
fs.Var(&flags.DeprecatedFlag{Name: "peers-file"}, "peers-file", "DEPRECATED: Use -initial-cluster instead")
}
func Main() {
fs.Usage = flags.UsageWithIgnoredFlagsFunc(fs, ignored)
perr := fs.Parse(os.Args[1:])
switch perr {
case nil:
case flag.ErrHelp:
os.Exit(0)
default:
cfg := NewConfig()
err := cfg.Parse(os.Args[1:])
if err != nil {
log.Printf("etcd: error verifying flags, %v", err)
os.Exit(2)
}
if *printVersion {
fmt.Println("etcd version", version.Version)
os.Exit(0)
}
err := flags.SetFlagsFromEnv(fs)
if err != nil {
log.Fatalf("etcd: %v", err)
}
shouldProxy := proxyFlag.String() != proxyFlagOff
var stopped <-chan struct{}
shouldProxy := cfg.isProxy()
if !shouldProxy {
stopped, err = startEtcd()
if err == discovery.ErrFullCluster && fallbackFlag.String() == fallbackFlagProxy {
stopped, err = startEtcd(cfg)
if err == discovery.ErrFullCluster && cfg.shouldFallbackToProxy() {
log.Printf("etcd: discovery cluster full, falling back to %s", fallbackFlagProxy)
shouldProxy = true
}
}
if shouldProxy {
err = startProxy()
err = startProxy(cfg)
}
if err != nil {
log.Fatalf("etcd: %v", err)
switch err {
case discovery.ErrDuplicateID:
log.Fatalf("etcd: member %s has previously registered with discovery service (%s), but the data-dir (%s) on disk cannot be found.",
cfg.name, cfg.durl, cfg.dir)
default:
log.Fatalf("etcd: %v", err)
}
}
<-stopped
}
// startEtcd launches the etcd server and HTTP handlers for client/server communication.
func startEtcd() (<-chan struct{}, error) {
apurls, err := flags.URLsFromFlags(fs, "initial-advertise-peer-urls", "addr", peerTLSInfo)
if err != nil {
return nil, err
}
cls, err := setupCluster(apurls)
func startEtcd(cfg *config) (<-chan struct{}, error) {
cls, err := setupCluster(cfg)
if err != nil {
return nil, fmt.Errorf("error setting up initial cluster: %v", err)
}
if *dir == "" {
*dir = fmt.Sprintf("%v.etcd", *name)
log.Printf("no data-dir provided, using default data-dir ./%s", *dir)
if cfg.dir == "" {
cfg.dir = fmt.Sprintf("%v.etcd", cfg.name)
log.Printf("no data-dir provided, using default data-dir ./%s", cfg.dir)
}
if err := os.MkdirAll(*dir, privateDirMode); err != nil {
if err := os.MkdirAll(cfg.dir, privateDirMode); err != nil {
return nil, fmt.Errorf("cannot create data directory: %v", err)
}
if err := fileutil.IsDirWriteable(*dir); err != nil {
if err := fileutil.IsDirWriteable(cfg.dir); err != nil {
return nil, fmt.Errorf("cannot write to data directory: %v", err)
}
pt, err := transport.NewTimeoutTransport(peerTLSInfo, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
pt, err := transport.NewTimeoutTransport(cfg.peerTLSInfo, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
if err != nil {
return nil, err
}
acurls, err := flags.URLsFromFlags(fs, "advertise-client-urls", "addr", clientTLSInfo)
if err != nil {
return nil, err
}
lpurls, err := flags.URLsFromFlags(fs, "listen-peer-urls", "peer-bind-addr", peerTLSInfo)
if err != nil {
return nil, err
}
if !peerTLSInfo.Empty() {
log.Printf("etcd: peerTLS: %s", peerTLSInfo)
if !cfg.peerTLSInfo.Empty() {
log.Printf("etcd: peerTLS: %s", cfg.peerTLSInfo)
}
plns := make([]net.Listener, 0)
for _, u := range lpurls {
for _, u := range cfg.lpurls {
var l net.Listener
l, err = transport.NewTimeoutListener(u.Host, u.Scheme, peerTLSInfo, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
l, err = transport.NewTimeoutListener(u.Host, u.Scheme, cfg.peerTLSInfo, rafthttp.ConnReadTimeout, rafthttp.ConnWriteTimeout)
if err != nil {
return nil, err
}
@ -247,18 +118,13 @@ func startEtcd() (<-chan struct{}, error) {
plns = append(plns, l)
}
lcurls, err := flags.URLsFromFlags(fs, "listen-client-urls", "bind-addr", clientTLSInfo)
if err != nil {
return nil, err
}
if !clientTLSInfo.Empty() {
log.Printf("etcd: clientTLS: %s", clientTLSInfo)
if !cfg.clientTLSInfo.Empty() {
log.Printf("etcd: clientTLS: %s", cfg.clientTLSInfo)
}
clns := make([]net.Listener, 0)
for _, u := range lcurls {
for _, u := range cfg.lcurls {
var l net.Listener
l, err = transport.NewListener(u.Host, u.Scheme, clientTLSInfo)
l, err = transport.NewKeepAliveListener(u.Host, u.Scheme, cfg.clientTLSInfo)
if err != nil {
return nil, err
}
@ -274,71 +140,78 @@ func startEtcd() (<-chan struct{}, error) {
clns = append(clns, l)
}
cfg := &etcdserver.ServerConfig{
Name: *name,
ClientURLs: acurls,
PeerURLs: apurls,
DataDir: *dir,
SnapCount: *snapCount,
srvcfg := &etcdserver.ServerConfig{
Name: cfg.name,
ClientURLs: cfg.acurls,
PeerURLs: cfg.apurls,
DataDir: cfg.dir,
SnapCount: cfg.snapCount,
MaxSnapFiles: cfg.maxSnapFiles,
MaxWALFiles: cfg.maxWalFiles,
Cluster: cls,
DiscoveryURL: *durl,
DiscoveryProxy: *dproxy,
NewCluster: clusterStateFlag.String() == clusterStateFlagNew,
ForceNewCluster: *forceNewCluster,
DiscoveryURL: cfg.durl,
DiscoveryProxy: cfg.dproxy,
NewCluster: cfg.isNewCluster(),
ForceNewCluster: cfg.forceNewCluster,
Transport: pt,
TickMs: cfg.TickMs,
ElectionTicks: cfg.electionTicks(),
}
var s *etcdserver.EtcdServer
s, err = etcdserver.NewServer(cfg)
s, err = etcdserver.NewServer(srvcfg)
if err != nil {
return nil, err
}
s.Start()
if corsInfo.String() != "" {
log.Printf("etcd: cors = %s", corsInfo)
if cfg.corsInfo.String() != "" {
log.Printf("etcd: cors = %s", cfg.corsInfo)
}
ch := &cors.CORSHandler{
Handler: etcdhttp.NewClientHandler(s),
Info: corsInfo,
Info: cfg.corsInfo,
}
ph := etcdhttp.NewPeerHandler(s)
ph := etcdhttp.NewPeerHandler(s.Cluster, s.RaftHandler())
// Start the peer server in a goroutine
for _, l := range plns {
go func(l net.Listener) {
log.Fatal(http.Serve(l, ph))
log.Fatal(serveHTTP(l, ph, 5*time.Minute))
}(l)
}
// Start a client server goroutine for each listen address
for _, l := range clns {
go func(l net.Listener) {
log.Fatal(http.Serve(l, ch))
// read timeout does not work with http close notify
// TODO: https://github.com/golang/go/issues/9524
log.Fatal(serveHTTP(l, ch, 0))
}(l)
}
return s.StopNotify(), nil
}
// startProxy launches an HTTP proxy for client communication which proxies to other etcd nodes.
func startProxy() error {
apurls, err := flags.URLsFromFlags(fs, "initial-advertise-peer-urls", "addr", peerTLSInfo)
if err != nil {
return err
}
cls, err := setupCluster(apurls)
func startProxy(cfg *config) error {
cls, err := setupCluster(cfg)
if err != nil {
return fmt.Errorf("error setting up initial cluster: %v", err)
}
if *durl != "" {
s, err := discovery.GetCluster(*durl, *dproxy)
if cfg.durl != "" {
s, err := discovery.GetCluster(cfg.durl, cfg.dproxy)
if err != nil {
return err
}
if cls, err = etcdserver.NewClusterFromString(*durl, s); err != nil {
if cls, err = etcdserver.NewClusterFromString(cfg.durl, s); err != nil {
return err
}
}
pt, err := transport.NewTransport(clientTLSInfo)
pt, err := transport.NewTransport(cfg.clientTLSInfo)
if err != nil {
return err
}
tr, err := transport.NewTransport(cfg.peerTLSInfo)
if err != nil {
return err
}
@ -347,7 +220,7 @@ func startProxy() error {
// clientURLs) instead of just using the initial fixed list here
peerURLs := cls.PeerURLs()
uf := func() []string {
cls, err := etcdserver.GetClusterFromPeers(peerURLs)
cls, err := etcdserver.GetClusterFromPeers(peerURLs, tr)
if err != nil {
log.Printf("proxy: %v", err)
return []string{}
@ -357,19 +230,15 @@ func startProxy() error {
ph := proxy.NewHandler(pt, uf)
ph = &cors.CORSHandler{
Handler: ph,
Info: corsInfo,
Info: cfg.corsInfo,
}
if proxyFlag.String() == proxyFlagReadonly {
if cfg.isReadonlyProxy() {
ph = proxy.NewReadonlyHandler(ph)
}
lcurls, err := flags.URLsFromFlags(fs, "listen-client-urls", "bind-addr", clientTLSInfo)
if err != nil {
return err
}
// Start a proxy server goroutine for each listen address
for _, u := range lcurls {
l, err := transport.NewListener(u.Host, u.Scheme, clientTLSInfo)
for _, u := range cfg.lcurls {
l, err := transport.NewListener(u.Host, u.Scheme, cfg.clientTLSInfo)
if err != nil {
return err
}
@ -384,27 +253,24 @@ func startProxy() error {
}
// setupCluster sets up an initial cluster definition for bootstrap or discovery.
func setupCluster(apurls []url.URL) (*etcdserver.Cluster, error) {
set := make(map[string]bool)
fs.Visit(func(f *flag.Flag) {
set[f.Name] = true
})
if set["discovery"] && set["initial-cluster"] {
return nil, fmt.Errorf("both discovery and bootstrap-config are set")
}
func setupCluster(cfg *config) (*etcdserver.Cluster, error) {
var cls *etcdserver.Cluster
var err error
switch {
case set["discovery"]:
case cfg.durl != "":
// If using discovery, generate a temporary cluster based on
// self's advertised peer URLs
clusterStr := genClusterString(*name, apurls)
cls, err = etcdserver.NewClusterFromString(*durl, clusterStr)
case set["initial-cluster"]:
fallthrough
clusterStr := genClusterString(cfg.name, cfg.apurls)
cls, err = etcdserver.NewClusterFromString(cfg.durl, clusterStr)
case cfg.dnsCluster != "":
clusterStr, clusterToken, err := discovery.SRVGetCluster(cfg.name, cfg.dnsCluster, cfg.initialClusterToken, cfg.apurls)
if err != nil {
return nil, err
}
cls, err = etcdserver.NewClusterFromString(clusterToken, clusterStr)
default:
// We're statically configured, and cluster has appropriately been set.
cls, err = etcdserver.NewClusterFromString(*initialClusterToken, *initialCluster)
cls, err = etcdserver.NewClusterFromString(cfg.initialClusterToken, cfg.initialCluster)
}
return cls, err
}

View File

@ -1,36 +1,25 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdmain
import (
"net/url"
"testing"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/pkg/testutil"
)
func mustNewURLs(t *testing.T, urls []string) []url.URL {
u, err := types.NewURLs(urls)
if err != nil {
t.Fatalf("unexpected new urls error: %v", err)
}
return u
}
func TestGenClusterString(t *testing.T) {
tests := []struct {
token string
@ -47,7 +36,7 @@ func TestGenClusterString(t *testing.T) {
},
}
for i, tt := range tests {
urls := mustNewURLs(t, tt.urls)
urls := testutil.MustNewURLs(t, tt.urls)
str := genClusterString(tt.token, urls)
if str != tt.wstr {
t.Errorf("#%d: cluster = %s, want %s", i, str, tt.wstr)

100
etcdmain/help.go Normal file
View File

@ -0,0 +1,100 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdmain
var (
usageline = `usage: etcd [flags]
start an etcd server
etcd --version
show the version of etcd
etcd -h | --help
show the help information about etcd
`
flagsline = `
member flags:
--name 'default'
human-readable name for this member.
--data-dir '${name}.etcd'
path to the data directory.
--snapshot-count '10000'
number of committed transactions to trigger a snapshot to disk.
--heartbeat-interval '100'
time (in milliseconds) of a heartbeat interval.
--election-timeout '1000'
time (in milliseconds) for an election to timeout.
--listen-peer-urls 'http://localhost:2380,http://localhost:7001'
list of URLs to listen on for peer traffic.
--listen-client-urls 'http://localhost:2379,http://localhost:4001'
list of URLs to listen on for client traffic.
-cors ''
comma-separated whitelist of origins for CORS (cross-origin resource sharing).
clustering flags:
--initial-advertise-peer-urls 'http://localhost:2380,http://localhost:7001'
list of this member's peer URLs to advertise to the rest of the cluster.
--initial-cluster 'default=http://localhost:2380,default=http://localhost:7001'
initial cluster configuration for bootstrapping.
--initial-cluster-state 'new'
initial cluster state ('new' or 'existing').
--initial-cluster-token 'etcd-cluster'
initial cluster token for the etcd cluster during bootstrap.
--advertise-client-urls 'http://localhost:2379,http://localhost:4001'
list of this member's client URLs to advertise to the rest of the cluster.
--discovery ''
discovery URL used to bootstrap the cluster.
--discovery-fallback 'proxy'
expected behavior ('exit' or 'proxy') when discovery services fails.
--discovery-proxy ''
HTTP proxy to use for traffic to discovery service.
--discovery-srv ''
dns srv domain used to bootstrap the cluster.
proxy flags:
--proxy 'off'
proxy mode setting ('off', 'readonly' or 'on').
security flags:
--ca-file ''
path to the client server TLS CA file.
--cert-file ''
path to the client server TLS cert file.
--key-file ''
path to the client server TLS key file.
--peer-ca-file ''
path to the peer server TLS CA file.
--peer-cert-file ''
path to the peer server TLS cert file.
--peer-key-file ''
path to the peer server TLS key file.
unsafe flags:
Please be CAUTIOUS to use unsafe flags because it will break the guarantee given
by consensus protocol.
--force-new-cluster 'false'
force to create a new one-member cluster.
`
)

37
etcdmain/http.go Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdmain
import (
"io/ioutil"
"log"
"net"
"net/http"
"time"
)
// serveHTTP accepts incoming HTTP connections on the listener l,
// creating a new service goroutine for each. The service goroutines
// read requests and then call handler to reply to them.
func serveHTTP(l net.Listener, handler http.Handler, readTimeout time.Duration) error {
logger := log.New(ioutil.Discard, "etcdhttp", 0)
// TODO: add debug flag; enable logging when debug flag is set
srv := &http.Server{
Handler: handler,
ReadTimeout: readTimeout,
ErrorLog: logger, // do not log user error
}
return srv.Serve(l)
}

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdserver
@ -24,13 +22,12 @@ import (
"log"
"net/url"
"path"
"reflect"
"sort"
"strings"
"sync"
etcdErr "github.com/coreos/etcd/error"
"github.com/coreos/etcd/pkg/flags"
"github.com/coreos/etcd/pkg/netutil"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/raft/raftpb"
"github.com/coreos/etcd/store"
@ -135,12 +132,6 @@ func (c *Cluster) Members() []*Member {
return []*Member(sms)
}
type SortableMemberSlice []*Member
func (s SortableMemberSlice) Len() int { return len(s) }
func (s SortableMemberSlice) Less(i, j int) bool { return s[i].ID < s[j].ID }
func (s SortableMemberSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (c *Cluster) Member(id types.ID) *Member {
c.Lock()
defer c.Unlock()
@ -181,25 +172,23 @@ func (c *Cluster) IsIDRemoved(id types.ID) bool {
return c.removed[id]
}
// PeerURLs returns a list of all peer addresses. Each address is prefixed
// with the scheme (currently "http://"). The returned list is sorted in
// ascending lexicographical order.
// PeerURLs returns a list of all peer addresses.
// The returned list is sorted in ascending lexicographical order.
func (c *Cluster) PeerURLs() []string {
c.Lock()
defer c.Unlock()
endpoints := make([]string, 0)
urls := make([]string, 0)
for _, p := range c.members {
for _, addr := range p.PeerURLs {
endpoints = append(endpoints, addr)
urls = append(urls, addr)
}
}
sort.Strings(endpoints)
return endpoints
sort.Strings(urls)
return urls
}
// ClientURLs returns a list of all client addresses. Each address is prefixed
// with the scheme (currently "http://"). The returned list is sorted in
// ascending lexicographical order.
// ClientURLs returns a list of all client addresses.
// The returned list is sorted in ascending lexicographical order.
func (c *Cluster) ClientURLs() []string {
c.Lock()
defer c.Unlock()
@ -336,52 +325,25 @@ func (c *Cluster) RemoveMember(id types.ID) {
c.removed[id] = true
}
func (c *Cluster) UpdateMemberAttributes(id types.ID, attr Attributes) {
func (c *Cluster) UpdateAttributes(id types.ID, attr Attributes) {
c.Lock()
defer c.Unlock()
c.members[id].Attributes = attr
// TODO: update store in this function
}
func (c *Cluster) UpdateMember(nm *Member) {
func (c *Cluster) UpdateRaftAttributes(id types.ID, raftAttr RaftAttributes) {
c.Lock()
defer c.Unlock()
b, err := json.Marshal(nm.RaftAttributes)
b, err := json.Marshal(raftAttr)
if err != nil {
log.Panicf("marshal raftAttributes should never fail: %v", err)
}
p := path.Join(memberStoreKey(nm.ID), raftAttributesSuffix)
p := path.Join(memberStoreKey(id), raftAttributesSuffix)
if _, err := c.store.Update(p, string(b), store.Permanent); err != nil {
log.Panicf("update raftAttributes should never fail: %v", err)
}
c.members[nm.ID].RaftAttributes = nm.RaftAttributes
}
// nodeToMember builds member through a store node.
// the child nodes of the given node should be sorted by key.
func nodeToMember(n *store.NodeExtern) (*Member, error) {
m := &Member{ID: mustParseMemberIDFromKey(n.Key)}
attrs := make(map[string][]byte)
raftAttrKey := path.Join(n.Key, raftAttributesSuffix)
attrKey := path.Join(n.Key, attributesSuffix)
for _, nn := range n.Nodes {
if nn.Key != raftAttrKey && nn.Key != attrKey {
return nil, fmt.Errorf("unknown key %q", nn.Key)
}
attrs[nn.Key] = []byte(*nn.Value)
}
if data := attrs[raftAttrKey]; data != nil {
if err := json.Unmarshal(data, &m.RaftAttributes); err != nil {
return nil, fmt.Errorf("unmarshal raftAttributes error: %v", err)
}
} else {
return nil, fmt.Errorf("raftAttributes key doesn't exist")
}
if data := attrs[attrKey]; data != nil {
if err := json.Unmarshal(data, &m.Attributes); err != nil {
return m, fmt.Errorf("unmarshal attributes error: %v", err)
}
}
return m, nil
c.members[id].RaftAttributes = raftAttr
}
func membersFromStore(st store.Store) (map[types.ID]*Member, map[types.ID]bool) {
@ -429,7 +391,8 @@ func ValidateClusterAndAssignIDs(local *Cluster, existing *Cluster) error {
sort.Sort(SortableMemberSliceByPeerURLs(lms))
for i := range ems {
if !reflect.DeepEqual(ems[i].PeerURLs, lms[i].PeerURLs) {
// TODO: Remove URLStringsEqual after improvement of using hostnames #2150 #2123
if !netutil.URLStringsEqual(ems[i].PeerURLs, lms[i].PeerURLs) {
return fmt.Errorf("unmatched member while checking PeerURLs")
}
lms[i].ID = ems[i].ID
@ -440,8 +403,3 @@ func ValidateClusterAndAssignIDs(local *Cluster, existing *Cluster) error {
}
return nil
}
func isKeyNotFound(err error) bool {
e, ok := err.(*etcdErr.Error)
return ok && e.ErrorCode == etcdErr.EcodeKeyNotFound
}

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdserver
@ -23,6 +21,7 @@ import (
"reflect"
"testing"
"github.com/coreos/etcd/pkg/testutil"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/raft/raftpb"
"github.com/coreos/etcd/store"
@ -535,10 +534,10 @@ func TestClusterAddMember(t *testing.T) {
c.SetStore(st)
c.AddMember(newTestMember(1, nil, "node1", nil))
wactions := []action{
wactions := []testutil.Action{
{
name: "Create",
params: []interface{}{
Name: "Create",
Params: []interface{}{
path.Join(storeMembersPrefix, "1", "raftAttributes"),
false,
`{"peerURLs":null}`,
@ -623,9 +622,9 @@ func TestClusterRemoveMember(t *testing.T) {
c.SetStore(st)
c.RemoveMember(1)
wactions := []action{
{name: "Delete", params: []interface{}{memberStoreKey(1), true, true}},
{name: "Create", params: []interface{}{removedMemberStoreKey(1), false, "", false, store.Permanent}},
wactions := []testutil.Action{
{Name: "Delete", Params: []interface{}{memberStoreKey(1), true, true}},
{Name: "Create", Params: []interface{}{removedMemberStoreKey(1), false, "", false, store.Permanent}},
}
if !reflect.DeepEqual(st.Action(), wactions) {
t.Errorf("actions = %v, want %v", st.Action(), wactions)
@ -654,3 +653,5 @@ func newTestCluster(membs []*Member) *Cluster {
}
return c
}
func stringp(s string) *string { return &s }

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdserver
@ -21,9 +19,9 @@ import (
"log"
"net/http"
"path"
"reflect"
"sort"
"github.com/coreos/etcd/pkg/netutil"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/raft"
)
@ -37,10 +35,15 @@ type ServerConfig struct {
PeerURLs types.URLs
DataDir string
SnapCount uint64
MaxSnapFiles uint
MaxWALFiles uint
Cluster *Cluster
NewCluster bool
ForceNewCluster bool
Transport *http.Transport
TickMs uint
ElectionTicks int
}
// VerifyBootstrapConfig sanity-checks the initial config and returns an error
@ -71,9 +74,10 @@ func (c *ServerConfig) VerifyBootstrapConfig() error {
}
// Advertised peer URLs must match those in the cluster peer list
// TODO: Remove URLStringsEqual after improvement of using hostnames #2150 #2123
apurls := c.PeerURLs.StringSlice()
sort.Strings(apurls)
if !reflect.DeepEqual(apurls, m.PeerURLs) {
if !netutil.URLStringsEqual(apurls, m.PeerURLs) {
return fmt.Errorf("%s has different advertised URLs in the cluster and advertised peer URLs list", c.Name)
}
return nil
@ -83,17 +87,11 @@ func (c *ServerConfig) WALDir() string { return path.Join(c.DataDir, "wal") }
func (c *ServerConfig) SnapDir() string { return path.Join(c.DataDir, "snap") }
func (c *ServerConfig) ShouldDiscover() bool {
return c.DiscoveryURL != ""
}
func (c *ServerConfig) ShouldDiscover() bool { return c.DiscoveryURL != "" }
func (c *ServerConfig) PrintWithInitial() {
c.print(true)
}
func (c *ServerConfig) PrintWithInitial() { c.print(true) }
func (c *ServerConfig) Print() {
c.print(false)
}
func (c *ServerConfig) Print() { c.print(false) }
func (c *ServerConfig) print(initial bool) {
log.Printf("etcdserver: name = %s", c.Name)
@ -101,6 +99,8 @@ func (c *ServerConfig) print(initial bool) {
log.Println("etcdserver: force new cluster")
}
log.Printf("etcdserver: data dir = %s", c.DataDir)
log.Printf("etcdserver: heartbeat = %dms", c.TickMs)
log.Printf("etcdserver: election = %dms", c.ElectionTicks*int(c.TickMs))
log.Printf("etcdserver: snapshot count = %d", c.SnapCount)
if len(c.DiscoveryURL) != 0 {
log.Printf("etcdserver: discovery URL= %s", c.DiscoveryURL)

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdserver

50
etcdserver/errors.go Normal file
View File

@ -0,0 +1,50 @@
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdserver
import (
"errors"
etcdErr "github.com/coreos/etcd/error"
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
)
var (
ErrUnknownMethod = errors.New("etcdserver: unknown method")
ErrStopped = errors.New("etcdserver: server stopped")
ErrIDRemoved = errors.New("etcdserver: ID removed")
ErrIDExists = errors.New("etcdserver: ID exists")
ErrIDNotFound = errors.New("etcdserver: ID not found")
ErrPeerURLexists = errors.New("etcdserver: peerURL exists")
ErrCanceled = errors.New("etcdserver: request cancelled")
ErrTimeout = errors.New("etcdserver: request timed out")
)
func parseCtxErr(err error) error {
switch err {
case context.Canceled:
return ErrCanceled
case context.DeadlineExceeded:
return ErrTimeout
default:
return err
}
}
func isKeyNotFound(err error) bool {
e, ok := err.(*etcdErr.Error)
return ok && e.ErrorCode == etcdErr.EcodeKeyNotFound
}

View File

@ -1,24 +1,23 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdhttp
import (
"encoding/json"
"errors"
"expvar"
"fmt"
"io/ioutil"
"log"
@ -35,7 +34,10 @@ import (
"github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
"github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/etcdserver/stats"
"github.com/coreos/etcd/pkg/metrics"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/raft"
"github.com/coreos/etcd/store"
"github.com/coreos/etcd/version"
)
@ -45,7 +47,9 @@ const (
deprecatedMachinesPrefix = "/v2/machines"
membersPrefix = "/v2/members"
statsPrefix = "/v2/stats"
versionPrefix = "/version"
statsPath = "/stats"
healthPath = "/health"
versionPath = "/version"
)
// NewClientHandler generates a muxed http.Handler with the given parameters to serve etcd client requests.
@ -73,12 +77,14 @@ func NewClientHandler(server *etcdserver.EtcdServer) http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/", http.NotFound)
mux.HandleFunc(versionPrefix, serveVersion)
mux.Handle(healthPath, healthHandler(server))
mux.HandleFunc(versionPath, serveVersion)
mux.Handle(keysPrefix, kh)
mux.Handle(keysPrefix+"/", kh)
mux.HandleFunc(statsPrefix+"/store", sh.serveStore)
mux.HandleFunc(statsPrefix+"/self", sh.serveSelf)
mux.HandleFunc(statsPrefix+"/leader", sh.serveLeader)
mux.HandleFunc(statsPath, serveStats)
mux.Handle(membersPrefix, mh)
mux.Handle(membersPrefix+"/", mh)
mux.Handle(deprecatedMachinesPrefix, dmh)
@ -101,7 +107,7 @@ func (h *keysHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(context.Background(), h.timeout)
defer cancel()
rr, err := parseKeyRequest(r, etcdserver.GenID(), clockwork.NewRealClock())
rr, err := parseKeyRequest(r, clockwork.NewRealClock())
if err != nil {
writeError(w, err)
return
@ -158,14 +164,26 @@ func (h *membersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET":
if trimPrefix(r.URL.Path, membersPrefix) != "" {
switch trimPrefix(r.URL.Path, membersPrefix) {
case "":
mc := newMemberCollection(h.clusterInfo.Members())
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(mc); err != nil {
log.Printf("etcdhttp: %v", err)
}
case "leader":
id := h.server.Leader()
if id == 0 {
writeError(w, httptypes.NewHTTPError(http.StatusServiceUnavailable, "During election"))
return
}
m := newMember(h.clusterInfo.Member(id))
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(m); err != nil {
log.Printf("etcdhttp: %v", err)
}
default:
writeError(w, httptypes.NewHTTPError(http.StatusNotFound, "Not found"))
return
}
mc := newMemberCollection(h.clusterInfo.Members())
w.Header().Set("Content-Type", "application/json")
if err := json.NewEncoder(w).Encode(mc); err != nil {
log.Printf("etcdhttp: %v", err)
}
case "POST":
req := httptypes.MemberCreateRequest{}
@ -236,7 +254,7 @@ func (h *membersHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
type statsHandler struct {
stats etcdserver.Stats
stats stats.Stats
}
func (h *statsHandler) serveStore(w http.ResponseWriter, r *http.Request) {
@ -259,21 +277,70 @@ func (h *statsHandler) serveLeader(w http.ResponseWriter, r *http.Request) {
if !allowMethod(w, r.Method, "GET") {
return
}
stats := h.stats.LeaderStats()
if stats == nil {
writeError(w, httptypes.NewHTTPError(http.StatusForbidden, "not current leader"))
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(h.stats.LeaderStats())
w.Write(stats)
}
func serveStats(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
// TODO: getting one key or a prefix of keys based on path
fmt.Fprintf(w, "{\n")
first := true
metrics.Do(func(kv expvar.KeyValue) {
if !first {
fmt.Fprintf(w, ",\n")
}
first = false
fmt.Fprintf(w, "%q: %s", kv.Key, kv.Value)
})
fmt.Fprintf(w, "\n}\n")
}
// TODO: change etcdserver to raft interface when we have it.
// add test for healthHeadler when we have the interface ready.
func healthHandler(server *etcdserver.EtcdServer) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if !allowMethod(w, r.Method, "GET") {
return
}
if uint64(server.Leader()) == raft.None {
http.Error(w, `{"health": "false"}`, http.StatusServiceUnavailable)
return
}
// wait for raft's progress
index := server.Index()
for i := 0; i < 3; i++ {
time.Sleep(250 * time.Millisecond)
if server.Index() > index {
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"health": "true"}`))
return
}
}
http.Error(w, `{"health": "false"}`, http.StatusServiceUnavailable)
return
}
}
func serveVersion(w http.ResponseWriter, r *http.Request) {
if !allowMethod(w, r.Method, "GET") {
return
}
w.Write([]byte("etcd " + version.Version))
fmt.Fprintf(w, `{"releaseVersion":"%s","internalVersion":"%s"}`, version.Version, version.InternalVersion)
}
// parseKeyRequest converts a received http.Request on keysPrefix to
// a server Request, performing validation of supplied fields as appropriate.
// If any validation fails, an empty Request and non-nil error is returned.
func parseKeyRequest(r *http.Request, id uint64, clock clockwork.Clock) (etcdserverpb.Request, error) {
func parseKeyRequest(r *http.Request, clock clockwork.Clock) (etcdserverpb.Request, error) {
emptyReq := etcdserverpb.Request{}
err := r.ParseForm()
@ -388,7 +455,6 @@ func parseKeyRequest(r *http.Request, id uint64, clock clockwork.Clock) (etcdser
}
rr := etcdserverpb.Request{
ID: id,
Method: r.Method,
Path: p,
Val: r.FormValue("value"),

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdhttp
@ -37,6 +35,7 @@ import (
"github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/etcdserver/etcdhttp/httptypes"
"github.com/coreos/etcd/etcdserver/etcdserverpb"
"github.com/coreos/etcd/pkg/testutil"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/raft/raftpb"
"github.com/coreos/etcd/store"
@ -54,7 +53,7 @@ func mustMarshalEvent(t *testing.T, ev *store.Event) string {
// mustNewForm takes a set of Values and constructs a PUT *http.Request,
// with a URL constructed from appending the given path to the standard keysPrefix
func mustNewForm(t *testing.T, p string, vals url.Values) *http.Request {
u := mustNewURL(t, path.Join(keysPrefix, p))
u := testutil.MustNewURL(t, path.Join(keysPrefix, p))
req, err := http.NewRequest("PUT", u.String(), strings.NewReader(vals.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if err != nil {
@ -66,7 +65,7 @@ func mustNewForm(t *testing.T, p string, vals url.Values) *http.Request {
// mustNewPostForm takes a set of Values and constructs a POST *http.Request,
// with a URL constructed from appending the given path to the standard keysPrefix
func mustNewPostForm(t *testing.T, p string, vals url.Values) *http.Request {
u := mustNewURL(t, path.Join(keysPrefix, p))
u := testutil.MustNewURL(t, path.Join(keysPrefix, p))
req, err := http.NewRequest("POST", u.String(), strings.NewReader(vals.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if err != nil {
@ -84,7 +83,7 @@ func mustNewRequest(t *testing.T, p string) *http.Request {
func mustNewMethodRequest(t *testing.T, m, p string) *http.Request {
return &http.Request{
Method: m,
URL: mustNewURL(t, path.Join(keysPrefix, p)),
URL: testutil.MustNewURL(t, path.Join(keysPrefix, p)),
}
}
@ -92,9 +91,10 @@ type serverRecorder struct {
actions []action
}
func (s *serverRecorder) Start() {}
func (s *serverRecorder) Stop() {}
func (s *serverRecorder) ID() types.ID { return types.ID(1) }
func (s *serverRecorder) Start() {}
func (s *serverRecorder) Stop() {}
func (s *serverRecorder) Leader() types.ID { return types.ID(1) }
func (s *serverRecorder) ID() types.ID { return types.ID(1) }
func (s *serverRecorder) Do(_ context.Context, r etcdserverpb.Request) (etcdserver.Response, error) {
s.actions = append(s.actions, action{name: "Do", params: []interface{}{r}})
return etcdserver.Response{}, nil
@ -139,9 +139,10 @@ type resServer struct {
res etcdserver.Response
}
func (rs *resServer) Start() {}
func (rs *resServer) Stop() {}
func (rs *resServer) ID() types.ID { return types.ID(1) }
func (rs *resServer) Start() {}
func (rs *resServer) Stop() {}
func (rs *resServer) ID() types.ID { return types.ID(1) }
func (rs *resServer) Leader() types.ID { return types.ID(1) }
func (rs *resServer) Do(_ context.Context, _ etcdserverpb.Request) (etcdserver.Response, error) {
return rs.res, nil
}
@ -184,7 +185,7 @@ func TestBadParseRequest(t *testing.T) {
{
// bad key prefix
&http.Request{
URL: mustNewURL(t, "/badprefix/"),
URL: testutil.MustNewURL(t, "/badprefix/"),
},
etcdErr.EcodeInvalidForm,
},
@ -312,7 +313,7 @@ func TestBadParseRequest(t *testing.T) {
},
}
for i, tt := range tests {
got, err := parseKeyRequest(tt.in, 1234, clockwork.NewFakeClock())
got, err := parseKeyRequest(tt.in, clockwork.NewFakeClock())
if err == nil {
t.Errorf("#%d: unexpected nil error!", i)
continue
@ -343,7 +344,6 @@ func TestGoodParseRequest(t *testing.T) {
// good prefix, all other values default
mustNewRequest(t, "foo"),
etcdserverpb.Request{
ID: 1234,
Method: "GET",
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
},
@ -356,7 +356,6 @@ func TestGoodParseRequest(t *testing.T) {
url.Values{"value": []string{"some_value"}},
),
etcdserverpb.Request{
ID: 1234,
Method: "PUT",
Val: "some_value",
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -370,7 +369,6 @@ func TestGoodParseRequest(t *testing.T) {
url.Values{"prevIndex": []string{"98765"}},
),
etcdserverpb.Request{
ID: 1234,
Method: "PUT",
PrevIndex: 98765,
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -384,7 +382,6 @@ func TestGoodParseRequest(t *testing.T) {
url.Values{"recursive": []string{"true"}},
),
etcdserverpb.Request{
ID: 1234,
Method: "PUT",
Recursive: true,
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -398,7 +395,6 @@ func TestGoodParseRequest(t *testing.T) {
url.Values{"sorted": []string{"true"}},
),
etcdserverpb.Request{
ID: 1234,
Method: "PUT",
Sorted: true,
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -412,7 +408,6 @@ func TestGoodParseRequest(t *testing.T) {
url.Values{"quorum": []string{"true"}},
),
etcdserverpb.Request{
ID: 1234,
Method: "PUT",
Quorum: true,
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -422,7 +417,6 @@ func TestGoodParseRequest(t *testing.T) {
// wait specified
mustNewRequest(t, "foo?wait=true"),
etcdserverpb.Request{
ID: 1234,
Method: "GET",
Wait: true,
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -432,7 +426,6 @@ func TestGoodParseRequest(t *testing.T) {
// empty TTL specified
mustNewRequest(t, "foo?ttl="),
etcdserverpb.Request{
ID: 1234,
Method: "GET",
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
Expiration: 0,
@ -442,7 +435,6 @@ func TestGoodParseRequest(t *testing.T) {
// non-empty TTL specified
mustNewRequest(t, "foo?ttl=5678"),
etcdserverpb.Request{
ID: 1234,
Method: "GET",
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
Expiration: fc.Now().Add(5678 * time.Second).UnixNano(),
@ -452,7 +444,6 @@ func TestGoodParseRequest(t *testing.T) {
// zero TTL specified
mustNewRequest(t, "foo?ttl=0"),
etcdserverpb.Request{
ID: 1234,
Method: "GET",
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
Expiration: fc.Now().UnixNano(),
@ -462,7 +453,6 @@ func TestGoodParseRequest(t *testing.T) {
// dir specified
mustNewRequest(t, "foo?dir=true"),
etcdserverpb.Request{
ID: 1234,
Method: "GET",
Dir: true,
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -472,7 +462,6 @@ func TestGoodParseRequest(t *testing.T) {
// dir specified negatively
mustNewRequest(t, "foo?dir=false"),
etcdserverpb.Request{
ID: 1234,
Method: "GET",
Dir: false,
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -486,7 +475,6 @@ func TestGoodParseRequest(t *testing.T) {
url.Values{"prevExist": []string{"true"}},
),
etcdserverpb.Request{
ID: 1234,
Method: "PUT",
PrevExist: boolp(true),
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -500,7 +488,6 @@ func TestGoodParseRequest(t *testing.T) {
url.Values{"prevExist": []string{"false"}},
),
etcdserverpb.Request{
ID: 1234,
Method: "PUT",
PrevExist: boolp(false),
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -518,7 +505,6 @@ func TestGoodParseRequest(t *testing.T) {
},
),
etcdserverpb.Request{
ID: 1234,
Method: "PUT",
PrevExist: boolp(true),
PrevValue: "previous value",
@ -534,7 +520,6 @@ func TestGoodParseRequest(t *testing.T) {
url.Values{},
),
etcdserverpb.Request{
ID: 1234,
Method: "PUT",
PrevValue: "woof",
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -550,7 +535,6 @@ func TestGoodParseRequest(t *testing.T) {
},
),
etcdserverpb.Request{
ID: 1234,
Method: "PUT",
PrevValue: "miaow",
Path: path.Join(etcdserver.StoreKeysPrefix, "/foo"),
@ -559,7 +543,7 @@ func TestGoodParseRequest(t *testing.T) {
}
for i, tt := range tests {
got, err := parseKeyRequest(tt.in, 1234, fc)
got, err := parseKeyRequest(tt.in, fc)
if err != nil {
t.Errorf("#%d: err = %v, want %v", i, err, nil)
}
@ -597,7 +581,58 @@ func TestServeMembers(t *testing.T) {
}
for i, tt := range tests {
req, err := http.NewRequest("GET", mustNewURL(t, tt.path).String(), nil)
req, err := http.NewRequest("GET", testutil.MustNewURL(t, tt.path).String(), nil)
if err != nil {
t.Fatal(err)
}
rw := httptest.NewRecorder()
h.ServeHTTP(rw, req)
if rw.Code != tt.wcode {
t.Errorf("#%d: code=%d, want %d", i, rw.Code, tt.wcode)
}
if gct := rw.Header().Get("Content-Type"); gct != tt.wct {
t.Errorf("#%d: content-type = %s, want %s", i, gct, tt.wct)
}
gcid := rw.Header().Get("X-Etcd-Cluster-ID")
wcid := cluster.ID().String()
if gcid != wcid {
t.Errorf("#%d: cid = %s, want %s", i, gcid, wcid)
}
if rw.Body.String() != tt.wbody {
t.Errorf("#%d: body = %q, want %q", i, rw.Body.String(), tt.wbody)
}
}
}
// TODO: consolidate **ALL** fake server implementations and add no leader test case.
func TestServeLeader(t *testing.T) {
memb1 := etcdserver.Member{ID: 1, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8080"}}}
memb2 := etcdserver.Member{ID: 2, Attributes: etcdserver.Attributes{ClientURLs: []string{"http://localhost:8081"}}}
cluster := &fakeCluster{
id: 1,
members: map[uint64]*etcdserver.Member{1: &memb1, 2: &memb2},
}
h := &membersHandler{
server: &serverRecorder{},
clock: clockwork.NewFakeClock(),
clusterInfo: cluster,
}
wmc := string(`{"id":"1","name":"","peerURLs":[],"clientURLs":["http://localhost:8080"]}`)
tests := []struct {
path string
wcode int
wct string
wbody string
}{
{membersPrefix + "leader", http.StatusOK, "application/json", wmc + "\n"},
// TODO: add no leader case
}
for i, tt := range tests {
req, err := http.NewRequest("GET", testutil.MustNewURL(t, tt.path).String(), nil)
if err != nil {
t.Fatal(err)
}
@ -622,7 +657,7 @@ func TestServeMembers(t *testing.T) {
}
func TestServeMembersCreate(t *testing.T) {
u := mustNewURL(t, membersPrefix)
u := testutil.MustNewURL(t, membersPrefix)
b := []byte(`{"peerURLs":["http://127.0.0.1:1"]}`)
req, err := http.NewRequest("POST", u.String(), bytes.NewReader(b))
if err != nil {
@ -676,7 +711,7 @@ func TestServeMembersCreate(t *testing.T) {
func TestServeMembersDelete(t *testing.T) {
req := &http.Request{
Method: "DELETE",
URL: mustNewURL(t, path.Join(membersPrefix, "BEEF")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "BEEF")),
}
s := &serverRecorder{}
h := &membersHandler{
@ -707,7 +742,7 @@ func TestServeMembersDelete(t *testing.T) {
}
func TestServeMembersUpdate(t *testing.T) {
u := mustNewURL(t, path.Join(membersPrefix, "1"))
u := testutil.MustNewURL(t, path.Join(membersPrefix, "1"))
b := []byte(`{"peerURLs":["http://127.0.0.1:1"]}`)
req, err := http.NewRequest("PUT", u.String(), bytes.NewReader(b))
if err != nil {
@ -776,7 +811,7 @@ func TestServeMembersFail(t *testing.T) {
{
// parse body error
&http.Request{
URL: mustNewURL(t, membersPrefix),
URL: testutil.MustNewURL(t, membersPrefix),
Method: "POST",
Body: ioutil.NopCloser(strings.NewReader("bad json")),
Header: map[string][]string{"Content-Type": []string{"application/json"}},
@ -788,7 +823,7 @@ func TestServeMembersFail(t *testing.T) {
{
// bad content type
&http.Request{
URL: mustNewURL(t, membersPrefix),
URL: testutil.MustNewURL(t, membersPrefix),
Method: "POST",
Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
Header: map[string][]string{"Content-Type": []string{"application/bad"}},
@ -800,7 +835,7 @@ func TestServeMembersFail(t *testing.T) {
{
// bad url
&http.Request{
URL: mustNewURL(t, membersPrefix),
URL: testutil.MustNewURL(t, membersPrefix),
Method: "POST",
Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://a"]}`)),
Header: map[string][]string{"Content-Type": []string{"application/json"}},
@ -812,13 +847,13 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.AddMember error
&http.Request{
URL: mustNewURL(t, membersPrefix),
URL: testutil.MustNewURL(t, membersPrefix),
Method: "POST",
Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
Header: map[string][]string{"Content-Type": []string{"application/json"}},
},
&errServer{
errors.New("blah"),
errors.New("Error while adding a member"),
},
http.StatusInternalServerError,
@ -826,7 +861,7 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.AddMember error
&http.Request{
URL: mustNewURL(t, membersPrefix),
URL: testutil.MustNewURL(t, membersPrefix),
Method: "POST",
Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
Header: map[string][]string{"Content-Type": []string{"application/json"}},
@ -840,7 +875,7 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.AddMember error
&http.Request{
URL: mustNewURL(t, membersPrefix),
URL: testutil.MustNewURL(t, membersPrefix),
Method: "POST",
Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
Header: map[string][]string{"Content-Type": []string{"application/json"}},
@ -854,11 +889,11 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.RemoveMember error with arbitrary server error
&http.Request{
URL: mustNewURL(t, path.Join(membersPrefix, "1")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "1")),
Method: "DELETE",
},
&errServer{
errors.New("blah"),
errors.New("Error while removing member"),
},
http.StatusInternalServerError,
@ -866,7 +901,7 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.RemoveMember error with previously removed ID
&http.Request{
URL: mustNewURL(t, path.Join(membersPrefix, "0")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
Method: "DELETE",
},
&errServer{
@ -878,7 +913,7 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.RemoveMember error with nonexistent ID
&http.Request{
URL: mustNewURL(t, path.Join(membersPrefix, "0")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
Method: "DELETE",
},
&errServer{
@ -890,7 +925,7 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.RemoveMember error with badly formed ID
&http.Request{
URL: mustNewURL(t, path.Join(membersPrefix, "bad_id")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "bad_id")),
Method: "DELETE",
},
nil,
@ -900,7 +935,7 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.RemoveMember with no ID
&http.Request{
URL: mustNewURL(t, membersPrefix),
URL: testutil.MustNewURL(t, membersPrefix),
Method: "DELETE",
},
nil,
@ -910,7 +945,7 @@ func TestServeMembersFail(t *testing.T) {
{
// parse body error
&http.Request{
URL: mustNewURL(t, path.Join(membersPrefix, "0")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
Method: "PUT",
Body: ioutil.NopCloser(strings.NewReader("bad json")),
Header: map[string][]string{"Content-Type": []string{"application/json"}},
@ -922,7 +957,7 @@ func TestServeMembersFail(t *testing.T) {
{
// bad content type
&http.Request{
URL: mustNewURL(t, path.Join(membersPrefix, "0")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
Method: "PUT",
Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
Header: map[string][]string{"Content-Type": []string{"application/bad"}},
@ -934,7 +969,7 @@ func TestServeMembersFail(t *testing.T) {
{
// bad url
&http.Request{
URL: mustNewURL(t, path.Join(membersPrefix, "0")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
Method: "PUT",
Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://a"]}`)),
Header: map[string][]string{"Content-Type": []string{"application/json"}},
@ -946,7 +981,7 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.UpdateMember error
&http.Request{
URL: mustNewURL(t, path.Join(membersPrefix, "0")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
Method: "PUT",
Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
Header: map[string][]string{"Content-Type": []string{"application/json"}},
@ -960,7 +995,7 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.UpdateMember error
&http.Request{
URL: mustNewURL(t, path.Join(membersPrefix, "0")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
Method: "PUT",
Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
Header: map[string][]string{"Content-Type": []string{"application/json"}},
@ -974,7 +1009,7 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.UpdateMember error
&http.Request{
URL: mustNewURL(t, path.Join(membersPrefix, "0")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "0")),
Method: "PUT",
Body: ioutil.NopCloser(strings.NewReader(`{"PeerURLs": ["http://127.0.0.1:1"]}`)),
Header: map[string][]string{"Content-Type": []string{"application/json"}},
@ -988,7 +1023,7 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.UpdateMember error with badly formed ID
&http.Request{
URL: mustNewURL(t, path.Join(membersPrefix, "bad_id")),
URL: testutil.MustNewURL(t, path.Join(membersPrefix, "bad_id")),
Method: "PUT",
},
nil,
@ -998,7 +1033,7 @@ func TestServeMembersFail(t *testing.T) {
{
// etcdserver.UpdateMember with no ID
&http.Request{
URL: mustNewURL(t, membersPrefix),
URL: testutil.MustNewURL(t, membersPrefix),
Method: "PUT",
},
nil,
@ -1292,7 +1327,7 @@ func TestServeVersion(t *testing.T) {
if rw.Code != http.StatusOK {
t.Errorf("code=%d, want %d", rw.Code, http.StatusOK)
}
w := fmt.Sprintf("etcd %s", version.Version)
w := fmt.Sprintf(`{"releaseVersion":"%s","internalVersion":"%s"}`, version.Version, version.InternalVersion)
if g := rw.Body.String(); g != w {
t.Fatalf("body = %q, want %q", g, w)
}
@ -1357,7 +1392,7 @@ func TestBadServeKeys(t *testing.T) {
// etcdserver.Server error
mustNewRequest(t, "foo"),
&errServer{
errors.New("blah"),
errors.New("Internal Server Error"),
},
http.StatusInternalServerError,

View File

@ -1,24 +1,23 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdhttp
import (
"errors"
"log"
"math"
"net/http"
"strings"
"time"
@ -29,10 +28,13 @@ import (
const (
// time to wait for response from EtcdServer requests
defaultServerTimeout = 5 * time.Minute
// 5s for disk and network delay + 10*heartbeat for commit and possible
// leader switch
// TODO: use heartbeat set in etcdserver
defaultServerTimeout = 5*time.Second + 10*(100*time.Millisecond)
// time to wait for a Watch request
defaultWatchTimeout = 5 * time.Minute
defaultWatchTimeout = time.Duration(math.MaxInt64)
)
var errClosed = errors.New("etcdhttp: client closed connection")

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdhttp
@ -20,7 +18,6 @@ import (
"errors"
"net/http"
"net/http/httptest"
"net/url"
"sort"
"testing"
@ -32,14 +29,6 @@ import (
"github.com/coreos/etcd/raft/raftpb"
)
func mustNewURL(t *testing.T, s string) *url.URL {
u, err := url.Parse(s)
if err != nil {
t.Fatalf("error creating URL from %q: %v", s, err)
}
return u
}
type fakeCluster struct {
id uint64
clientURLs []string
@ -65,9 +54,10 @@ type errServer struct {
err error
}
func (fs *errServer) Start() {}
func (fs *errServer) Stop() {}
func (fs *errServer) ID() types.ID { return types.ID(1) }
func (fs *errServer) Start() {}
func (fs *errServer) Stop() {}
func (fs *errServer) ID() types.ID { return types.ID(1) }
func (fs *errServer) Leader() types.ID { return types.ID(1) }
func (fs *errServer) Do(ctx context.Context, r etcdserverpb.Request) (etcdserver.Response, error) {
return etcdserver.Response{}, fs.err
}

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package httptypes defines how etcd's HTTP API entities are serialized to and deserialized from JSON.

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package httptypes

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package httptypes

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package httptypes

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package httptypes

View File

@ -1,18 +1,16 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdhttp
@ -30,17 +28,15 @@ const (
)
// NewPeerHandler generates an http.Handler to handle etcd peer (raft) requests.
func NewPeerHandler(server *etcdserver.EtcdServer) http.Handler {
rh := rafthttp.NewHandler(server, server.Cluster.ID())
rsh := rafthttp.NewStreamHandler(server.SenderFinder(), server.ID(), server.Cluster.ID())
func NewPeerHandler(clusterInfo etcdserver.ClusterInfo, raftHandler http.Handler) http.Handler {
mh := &peerMembersHandler{
clusterInfo: server.Cluster,
clusterInfo: clusterInfo,
}
mux := http.NewServeMux()
mux.HandleFunc("/", http.NotFound)
mux.Handle(rafthttp.RaftPrefix, rh)
mux.Handle(rafthttp.RaftStreamPrefix+"/", rsh)
mux.Handle(rafthttp.RaftPrefix, raftHandler)
mux.Handle(rafthttp.RaftPrefix+"/", raftHandler)
mux.Handle(peerMembersPrefix, mh)
return mux
}

View File

@ -1,31 +1,61 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdhttp
import (
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"path"
"testing"
"github.com/coreos/etcd/etcdserver"
"github.com/coreos/etcd/pkg/testutil"
"github.com/coreos/etcd/rafthttp"
)
// TestNewPeerHandler tests that NewPeerHandler returns a handler that
// handles raft-prefix requests well.
func TestNewPeerHandlerOnRaftPrefix(t *testing.T) {
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("test data"))
})
ph := NewPeerHandler(&fakeCluster{}, h)
srv := httptest.NewServer(ph)
defer srv.Close()
tests := []string{
rafthttp.RaftPrefix,
rafthttp.RaftPrefix + "/hello",
}
for i, tt := range tests {
resp, err := http.Get(srv.URL + tt)
if err != nil {
t.Fatalf("unexpected http.Get error: %v", err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("unexpected ioutil.ReadAll error: %v", err)
}
if w := "test data"; string(body) != w {
t.Errorf("#%d: body = %s, want %s", i, body, w)
}
}
}
func TestServeMembersFails(t *testing.T) {
tests := []struct {
method string
@ -79,7 +109,7 @@ func TestServeMembersGet(t *testing.T) {
}
for i, tt := range tests {
req, err := http.NewRequest("GET", mustNewURL(t, tt.path).String(), nil)
req, err := http.NewRequest("GET", testutil.MustNewURL(t, tt.path).String(), nil)
if err != nil {
t.Fatal(err)
}

View File

@ -1,36 +0,0 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package etcdserver
// func Example_Server() {
// flag.Parse() // fills cfg
//
// ss, w, err := LoadState(*statedir)
// if err != nil {
// log.Println("main: unable to load state - %s", err)
// }
//
// s := Server{
// Snapshot: ss,
// WalFile: w,
// Config: cfg,
// }
//
// go func() {
// log.Fatal(http.ListenAndServe(*laddr, s))
// }()
// }

View File

@ -1,149 +0,0 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package etcdserver
import (
"encoding/json"
"log"
"sort"
"github.com/coreos/etcd/pkg/pbutil"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/raft"
"github.com/coreos/etcd/raft/raftpb"
"github.com/coreos/etcd/wal"
)
func restartAsStandaloneNode(cfg *ServerConfig, index uint64, snapshot *raftpb.Snapshot) (types.ID, raft.Node, *wal.WAL) {
w, id, cid, st, ents := readWAL(cfg.WALDir(), index)
cfg.Cluster.SetID(cid)
// discard the previously uncommitted entries
for i, ent := range ents {
if ent.Index > st.Commit {
log.Printf("etcdserver: discarding %d uncommited WAL entries ", len(ents)-i)
ents = ents[:i]
break
}
}
// force append the configuration change entries
toAppEnts := createConfigChangeEnts(getIDs(snapshot, ents), uint64(id), st.Term, st.Commit)
ents = append(ents, toAppEnts...)
// force commit newly appended entries
for _, e := range toAppEnts {
err := w.SaveEntry(&e)
if err != nil {
log.Fatalf("etcdserver: %v", err)
}
}
if len(ents) != 0 {
st.Commit = ents[len(ents)-1].Index
}
log.Printf("etcdserver: forcing restart of member %s in cluster %s at commit index %d", id, cfg.Cluster.ID(), st.Commit)
n := raft.RestartNode(uint64(id), 10, 1, snapshot, st, ents)
return id, n, w
}
// getIDs returns an ordered set of IDs included in the given snapshot and
// the entries. The given snapshot/entries can contain two kinds of
// ID-related entry:
// - ConfChangeAddNode, in which case the contained ID will be added into the set.
// - ConfChangeAddRemove, in which case the contained ID will be removed from the set.
func getIDs(snap *raftpb.Snapshot, ents []raftpb.Entry) []uint64 {
ids := make(map[uint64]bool)
if snap != nil {
for _, id := range snap.Nodes {
ids[id] = true
}
}
for _, e := range ents {
if e.Type != raftpb.EntryConfChange {
continue
}
var cc raftpb.ConfChange
pbutil.MustUnmarshal(&cc, e.Data)
switch cc.Type {
case raftpb.ConfChangeAddNode:
ids[cc.NodeID] = true
case raftpb.ConfChangeRemoveNode:
delete(ids, cc.NodeID)
default:
log.Panicf("ConfChange Type should be either ConfChangeAddNode or ConfChangeRemoveNode!")
}
}
sids := make(types.Uint64Slice, 0)
for id := range ids {
sids = append(sids, id)
}
sort.Sort(sids)
return []uint64(sids)
}
// createConfigChangeEnts creates a series of Raft entries (i.e.
// EntryConfChange) to remove the set of given IDs from the cluster. The ID
// `self` is _not_ removed, even if present in the set.
// If `self` is not inside the given ids, it creates a Raft entry to add a
// default member with the given `self`.
func createConfigChangeEnts(ids []uint64, self uint64, term, index uint64) []raftpb.Entry {
ents := make([]raftpb.Entry, 0)
next := index + 1
found := false
for _, id := range ids {
if id == self {
found = true
continue
}
cc := &raftpb.ConfChange{
Type: raftpb.ConfChangeRemoveNode,
NodeID: id,
}
e := raftpb.Entry{
Type: raftpb.EntryConfChange,
Data: pbutil.MustMarshal(cc),
Term: term,
Index: next,
}
ents = append(ents, e)
next++
}
if !found {
m := Member{
ID: types.ID(self),
RaftAttributes: RaftAttributes{PeerURLs: []string{"http://localhost:7001", "http://localhost:2380"}},
}
ctx, err := json.Marshal(m)
if err != nil {
log.Panicf("marshal member should never fail: %v", err)
}
cc := &raftpb.ConfChange{
Type: raftpb.ConfChangeAddNode,
NodeID: self,
Context: ctx,
}
e := raftpb.Entry{
Type: raftpb.EntryConfChange,
Data: pbutil.MustMarshal(cc),
Term: term,
Index: next,
}
ents = append(ents, e)
}
return ents
}

View File

@ -1,24 +1,23 @@
/*
Copyright 2014 CoreOS, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package etcdserver
import (
"crypto/sha1"
"encoding/binary"
"encoding/json"
"fmt"
"log"
"math/rand"
@ -27,6 +26,7 @@ import (
"time"
"github.com/coreos/etcd/pkg/types"
"github.com/coreos/etcd/store"
)
// RaftAttributes represents the raft related attributes of an etcd member.
@ -121,6 +121,42 @@ func removedMemberStoreKey(id types.ID) string {
return path.Join(storeRemovedMembersPrefix, id.String())
}
// nodeToMember builds member from a key value node.
// the child nodes of the given node MUST be sorted by key.
func nodeToMember(n *store.NodeExtern) (*Member, error) {
m := &Member{ID: mustParseMemberIDFromKey(n.Key)}
attrs := make(map[string][]byte)
raftAttrKey := path.Join(n.Key, raftAttributesSuffix)
attrKey := path.Join(n.Key, attributesSuffix)
for _, nn := range n.Nodes {
if nn.Key != raftAttrKey && nn.Key != attrKey {
return nil, fmt.Errorf("unknown key %q", nn.Key)
}
attrs[nn.Key] = []byte(*nn.Value)
}
if data := attrs[raftAttrKey]; data != nil {
if err := json.Unmarshal(data, &m.RaftAttributes); err != nil {
return nil, fmt.Errorf("unmarshal raftAttributes error: %v", err)
}
} else {
return nil, fmt.Errorf("raftAttributes key doesn't exist")
}
if data := attrs[attrKey]; data != nil {
if err := json.Unmarshal(data, &m.Attributes); err != nil {
return m, fmt.Errorf("unmarshal attributes error: %v", err)
}
}
return m, nil
}
// implement sort by ID interface
type SortableMemberSlice []*Member
func (s SortableMemberSlice) Len() int { return len(s) }
func (s SortableMemberSlice) Less(i, j int) bool { return s[i].ID < s[j].ID }
func (s SortableMemberSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// implement sort by peer urls interface
type SortableMemberSliceByPeerURLs []*Member
func (p SortableMemberSliceByPeerURLs) Len() int { return len(p) }

Some files were not shown because too many files have changed in this diff Show More