Skip to content

Commit c6e1af8

Browse files
committed
merge master
2 parents fc562bd + 7875ba2 commit c6e1af8

14 files changed

+158
-66
lines changed

Documentation/internal-protocol-versioning.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ Goal: We want to be able to upgrade an individual peer in an etcd cluster to a n
44
The process will take the form of individual followers upgrading to the latest version until the entire cluster is on the new version.
55

66
Immediate need: etcd is moving too fast to version the internal API right now.
7-
But, we need to keep mixed version clusters from being started by a rollowing upgrade process (e.g. the CoreOS developer alpha).
7+
But, we need to keep mixed version clusters from being started by a rolling upgrade process (e.g. the CoreOS developer alpha).
88

9-
Longer term need: Having a mixed version cluster where all peers are not be running the exact same version of etcd itself but are able to speak one version of the internal protocol.
9+
Longer term need: Having a mixed version cluster where all peers are not running the exact same version of etcd itself but are able to speak one version of the internal protocol.
1010

1111
Solution: The internal protocol needs to be versioned just as the client protocol is.
1212
Initially during the 0.\*.\* series of etcd releases we won't allow mixed versions at all.

README.md

+48-36
Original file line numberDiff line numberDiff line change
@@ -56,26 +56,26 @@ go version
5656
```
5757

5858

59-
### Running a single node
59+
### Running a single machine
6060

61-
These examples will use a single node cluster to show you the basics of the etcd REST API.
61+
These examples will use a single machine cluster to show you the basics of the etcd REST API.
6262
Let's start etcd:
6363

6464
```sh
65-
./etcd -data-dir node0 -name node0
65+
./etcd -data-dir machine0 -name machine0
6666
```
6767

68-
This will bring up an etcd node listening on port 4001 for client communication and on port 7001 for server-to-server communication.
69-
The `-data-dir node0` argument tells etcd to write node configuration, logs and snapshots to the `./node0/` directory.
70-
The `-name node0` tells the rest of the cluster that this node is named node0.
68+
This will bring up etcd listening on port 4001 for client communication and on port 7001 for server-to-server communication.
69+
The `-data-dir machine0` argument tells etcd to write machine configuration, logs and snapshots to the `./machine0/` directory.
70+
The `-name machine` tells the rest of the cluster that this machine is named machine0.
7171

7272

7373

7474
## Usage
7575

7676
### Setting the value to a key
7777

78-
Let’s set the first key-value pair to the node.
78+
Let’s set the first key-value pair to the datastore.
7979
In this case the key is `/message` and the value is `Hello world`.
8080

8181
```sh
@@ -121,7 +121,7 @@ curl -L http://127.0.0.1:4001/v2/keys/message
121121
You can change the value of `/message` from `Hello world` to `Hello etcd` with another `PUT` request to the key:
122122

123123
```sh
124-
curl -L http://127.0.0.1:4001/v1/keys/message -XPUT -d value="Hello etcd"
124+
curl -L http://127.0.0.1:4001/v2/keys/message -XPUT -d value="Hello etcd"
125125
```
126126

127127
```json
@@ -164,7 +164,7 @@ Note the two new fields in response:
164164

165165
2. The `ttl` is the time to live for the key, in seconds.
166166

167-
_NOTE_: Keys can only be expired by a cluster leader so if a node gets disconnected from the cluster, its keys will not expire until it rejoins.
167+
_NOTE_: Keys can only be expired by a cluster leader so if a machine gets disconnected from the cluster, its keys will not expire until it rejoins.
168168

169169
Now you can try to get the key by sending a `GET` request:
170170

@@ -219,9 +219,9 @@ The watch command returns immediately with the same response as previous.
219219

220220
### Atomic Compare-and-Swap (CAS)
221221

222-
Etcd can be used as a centralized coordination service in a cluster and `CompareAndSwap` is the most basic operation to build distributed lock service.
222+
Etcd can be used as a centralized coordination service in a cluster and `CompareAndSwap` is the most basic operation to build distributed lock service.
223223

224-
This command will set the value of a key only if the client-provided conditions are equal to the current conditions.
224+
This command will set the value of a key only if the client-provided conditions are equal to the current conditions.
225225

226226
The current comparable conditions are:
227227

@@ -235,14 +235,26 @@ Here is a simple example.
235235
Let's create a key-value pair first: `foo=one`.
236236

237237
```sh
238-
curl -L http://127.0.0.1:4001/v1/keys/foo -XPUT -d value=one
238+
curl -L http://127.0.0.1:4001/v2/keys/foo -XPUT -d value=one
239239
```
240240

241-
Let's try an invalid `CompareAndSwap` command first.
242-
We can provide the `prevValue` parameter to the set command to make it a `CompareAndSwap` command.
241+
Let's try some invalid `CompareAndSwap` commands first.
243242

243+
Trying to set this existing key with `prevExist=false` fails as expected:
244244
```sh
245-
curl -L http://127.0.0.1:4001/v1/keys/foo?prevValue=two -XPUT -d value=three
245+
curl -L http://127.0.0.1:4001/v2/keys/foo?prevExist=false -XPUT -d value=three
246+
```
247+
248+
The error code explains the problem:
249+
250+
```json
251+
{"errorCode":105,"message":"Already exists","cause":"/foo","index":39776}
252+
```
253+
254+
Now lets provide a `prevValue` parameter:
255+
256+
```sh
257+
curl -L http://127.0.0.1:4001/v2/keys/foo?prevValue=two -XPUT -d value=three
246258
```
247259

248260
This will try to compare the previous value of the key and the previous value we provided. If they are equal, the value of the key will change to three.
@@ -378,12 +390,12 @@ For testing you can use the certificates in the `fixtures/ca` directory.
378390
Let's configure etcd to use this keypair:
379391

380392
```sh
381-
./etcd -f -name node0 -data-dir node0 -cert-file=./fixtures/ca/server.crt -key-file=./fixtures/ca/server.key.insecure
393+
./etcd -f -name machine0 -data-dir machine0 -cert-file=./fixtures/ca/server.crt -key-file=./fixtures/ca/server.key.insecure
382394
```
383395

384396
There are a few new options we're using:
385397

386-
* `-f` - forces a new node configuration, even if an existing configuration is found. (WARNING: data loss!)
398+
* `-f` - forces a new machine configuration, even if an existing configuration is found. (WARNING: data loss!)
387399
* `-cert-file` and `-key-file` specify the location of the cert and key files to be used for for transport layer security between the client and server.
388400

389401
You can now test the configuration using HTTPS:
@@ -413,7 +425,7 @@ We can also do authentication using CA certs.
413425
The clients will provide their cert to the server and the server will check whether the cert is signed by the CA and decide whether to serve the request.
414426

415427
```sh
416-
./etcd -f -name node0 -data-dir node0 -ca-file=./fixtures/ca/ca.crt -cert-file=./fixtures/ca/server.crt -key-file=./fixtures/ca/server.key.insecure
428+
./etcd -f -name machine0 -data-dir machine0 -ca-file=./fixtures/ca/ca.crt -cert-file=./fixtures/ca/server.crt -key-file=./fixtures/ca/server.key.insecure
417429
```
418430

419431
```-ca-file``` is the path to the CA cert.
@@ -435,7 +447,7 @@ routines:SSL3_READ_BYTES:sslv3 alert bad certificate
435447
We need to give the CA signed cert to the server.
436448

437449
```sh
438-
curl --key ./fixtures/ca/server2.key.insecure --cert ./fixtures/ca/server2.crt --cacert ./fixtures/ca/server-chain.pem -L https://127.0.0.1:4001/v1/keys/foo -XPUT -d value=bar -v
450+
curl --key ./fixtures/ca/server2.key.insecure --cert ./fixtures/ca/server2.crt --cacert ./fixtures/ca/server-chain.pem -L https://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar -v
439451
```
440452

441453
You should able to see:
@@ -463,29 +475,29 @@ We use Raft as the underlying distributed protocol which provides consistency an
463475

464476
Let start by creating 3 new etcd instances.
465477

466-
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 node in the cluster:
478+
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:
467479

468480
```sh
469-
./etcd -peer-addr 127.0.0.1:7001 -addr 127.0.0.1:4001 -data-dir nodes/node1 -name node1
481+
./etcd -peer-addr 127.0.0.1:7001 -addr 127.0.0.1:4001 -data-dir machines/machine1 -name machine1
470482
```
471483

472484
**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.
473485
A similar argument `-peer-bind-addr` is used to setup the listening address for the server port.
474486

475-
Let's join two more nodes to this cluster using the `-peers` argument:
487+
Let's join two more machines to this cluster using the `-peers` argument:
476488

477489
```sh
478-
./etcd -peer-addr 127.0.0.1:7002 -addr 127.0.0.1:4002 -peers 127.0.0.1:7001 -data-dir nodes/node2 -name node2
479-
./etcd -peer-addr 127.0.0.1:7003 -addr 127.0.0.1:4003 -peers 127.0.0.1:7001 -data-dir nodes/node3 -name node3
490+
./etcd -peer-addr 127.0.0.1:7002 -addr 127.0.0.1:4002 -peers 127.0.0.1:7001 -data-dir machines/machine2 -name machine2
491+
./etcd -peer-addr 127.0.0.1:7003 -addr 127.0.0.1:4003 -peers 127.0.0.1:7001 -data-dir machines/machine3 -name machine3
480492
```
481493

482494
We can retrieve a list of machines in the cluster using the HTTP API:
483495

484496
```sh
485-
curl -L http://127.0.0.1:4001/v1/machines
497+
curl -L http://127.0.0.1:4001/v2/machines
486498
```
487499

488-
We should see there are three nodes in the cluster
500+
We should see there are three machines in the cluster
489501

490502
```
491503
http://127.0.0.1:4001, http://127.0.0.1:4002, http://127.0.0.1:4003
@@ -494,11 +506,11 @@ http://127.0.0.1:4001, http://127.0.0.1:4002, http://127.0.0.1:4003
494506
The machine list is also available via the main key API:
495507

496508
```sh
497-
curl -L http://127.0.0.1:4001/v1/keys/_etcd/machines
509+
curl -L http://127.0.0.1:4001/v2/keys/_etcd/machines
498510
```
499511

500512
```json
501-
[{"action":"get","key":"/_etcd/machines/node1","value":"raft=http://127.0.0.1:7001\u0026etcd=http://127.0.0.1:4001","index":1},{"action":"get","key":"/_etcd/machines/node2","value":"raft=http://127.0.0.1:7002\u0026etcd=http://127.0.0.1:4002","index":1},{"action":"get","key":"/_etcd/machines/node3","value":"raft=http://127.0.0.1:7003\u0026etcd=http://127.0.0.1:4003","index":1}]
513+
[{"action":"get","key":"/_etcd/machines/machine1","value":"raft=http://127.0.0.1:7001\u0026etcd=http://127.0.0.1:4001","index":1},{"action":"get","key":"/_etcd/machines/machine2","value":"raft=http://127.0.0.1:7002\u0026etcd=http://127.0.0.1:4002","index":1},{"action":"get","key":"/_etcd/machines/machine3","value":"raft=http://127.0.0.1:7003\u0026etcd=http://127.0.0.1:4003","index":1}]
502514
```
503515

504516
We can also get the current leader in the cluster:
@@ -529,13 +541,13 @@ curl -L http://127.0.0.1:4001/v2/keys/foo -XPUT -d value=bar
529541
Now if we kill the leader of the cluster, we can get the value from one of the other two machines:
530542

531543
```sh
532-
curl -L http://127.0.0.1:4002/v1/keys/foo
544+
curl -L http://127.0.0.1:4002/v2/keys/foo
533545
```
534546

535547
We can also see that a new leader has been elected:
536548

537549
```
538-
curl -L http://127.0.0.1:4002/v1/leader
550+
curl -L http://127.0.0.1:4002/v2/leader
539551
```
540552

541553
```
@@ -551,13 +563,13 @@ http://127.0.0.1:7003
551563

552564
### Testing Persistence
553565

554-
Next we'll kill all the nodes to test persistence.
555-
Type `CTRL-C` on each terminal and then rerun the same command you used to start each node.
566+
Next we'll kill all the machines to test persistence.
567+
Type `CTRL-C` on each terminal and then rerun the same command you used to start each machine.
556568

557569
Your request for the `foo` key will return the correct value:
558570

559571
```sh
560-
curl -L http://127.0.0.1:4002/v1/keys/foo
572+
curl -L http://127.0.0.1:4002/v2/keys/foo
561573
```
562574

563575
```json
@@ -654,8 +666,8 @@ The command is not committed until the majority of the cluster peers receive tha
654666
Because of this majority voting property, the ideal cluster should be kept small to keep speed up and be made up of an odd number of peers.
655667

656668
Odd numbers are good because if you have 8 peers the majority will be 5 and if you have 9 peers the majority will still be 5.
657-
The result is that an 8 peer cluster can tolerate 3 peer failures and a 9 peer cluster can tolerate 4 nodes failures.
658-
And in the best case when all 9 peers are responding the cluster will perform at the speed of the fastest 5 nodes.
669+
The result is that an 8 peer cluster can tolerate 3 peer failures and a 9 peer cluster can tolerate 4 machine failures.
670+
And in the best case when all 9 peers are responding the cluster will perform at the speed of the fastest 5 machines.
659671

660672

661673
### Why SSLv3 alert handshake failure when using SSL client auth?
@@ -677,7 +689,7 @@ Add the following section to your openssl.cnf:
677689
When creating the cert be sure to reference it in the `-extensions` flag:
678690

679691
```
680-
openssl ca -config openssl.cnf -policy policy_anything -extensions ssl_client -out certs/node.crt -infiles node.csr
692+
openssl ca -config openssl.cnf -policy policy_anything -extensions ssl_client -out certs/machine.crt -infiles machine.csr
681693
```
682694

683695

error/error.go

+2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ const (
3232
EcodeNotDir = 104
3333
EcodeNodeExist = 105
3434
EcodeKeyIsPreserved = 106
35+
EcodeRootROnly = 107
3536

3637
EcodeValueRequired = 200
3738
EcodePrevValueRequired = 201
@@ -56,6 +57,7 @@ func init() {
5657
errors[EcodeNoMorePeer] = "Reached the max number of peers in the cluster"
5758
errors[EcodeNotDir] = "Not A Directory"
5859
errors[EcodeNodeExist] = "Already exists" // create
60+
errors[EcodeRootROnly] = "Root is read only"
5961
errors[EcodeKeyIsPreserved] = "The prefix of given key is a keyword in etcd"
6062

6163
// Post form related errors

server/server.go

+5
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ func (s *Server) PeerURL(name string) (string, bool) {
9696
return s.registry.PeerURL(name)
9797
}
9898

99+
// ClientURL retrieves the Client URL for a given node name.
100+
func (s *Server) ClientURL(name string) (string, bool) {
101+
return s.registry.ClientURL(name)
102+
}
103+
99104
// Returns a reference to the Store.
100105
func (s *Server) Store() store.Store {
101106
return s.store

server/usage.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ Options:
2626
-vv Enabled very verbose logging.
2727
2828
Cluster Configuration Options:
29-
-peers=<peers> Comma-separated list of peers (ip + port) in the cluster.
30-
-peers-file=<path> Path to a file containing the peer list.
29+
-peers-file=<path> Path to a file containing the peer list.
30+
-peers=<host:port>,<host:port> Comma-separated list of peers. The members
31+
should match the peer's '-peer-addr' flag.
3132
3233
Client Communication Options:
3334
-addr=<host:port> The public host:port used for client communication.

server/v2/get_handler.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func GetHandler(w http.ResponseWriter, req *http.Request, s Server) error {
2323
// Help client to redirect the request to the current leader
2424
if req.FormValue("consistent") == "true" && s.State() != raft.Leader {
2525
leader := s.Leader()
26-
hostname, _ := s.PeerURL(leader)
26+
hostname, _ := s.ClientURL(leader)
2727
url := hostname + req.URL.Path
2828
log.Debugf("Redirect consistent get to %s", url)
2929
http.Redirect(w, req, url, http.StatusTemporaryRedirect)

server/v2/v2.go

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type Server interface {
1313
CommitIndex() uint64
1414
Term() uint64
1515
PeerURL(string) (string, bool)
16+
ClientURL(string) (string, bool)
1617
Store() store.Store
1718
Dispatch(raft.Command, http.ResponseWriter, *http.Request) error
1819
}

store/event_history.go

+16-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package store
22

33
import (
44
"fmt"
5+
"path"
56
"strings"
67
"sync"
78

@@ -39,8 +40,8 @@ func (eh *EventHistory) addEvent(e *Event) *Event {
3940
}
4041

4142
// scan function is enumerating events from the index in history and
42-
// stops till the first point where the key has identified prefix
43-
func (eh *EventHistory) scan(prefix string, index uint64) (*Event, *etcdErr.Error) {
43+
// stops till the first point where the key has identified key
44+
func (eh *EventHistory) scan(key string, recursive bool, index uint64) (*Event, *etcdErr.Error) {
4445
eh.rwl.RLock()
4546
defer eh.rwl.RUnlock()
4647

@@ -62,7 +63,19 @@ func (eh *EventHistory) scan(prefix string, index uint64) (*Event, *etcdErr.Erro
6263
for {
6364
e := eh.Queue.Events[i]
6465

65-
if strings.HasPrefix(e.Node.Key, prefix) && index <= e.Index() { // make sure we bypass the smaller one
66+
ok := (e.Node.Key == key)
67+
68+
if recursive {
69+
// add tailing slash
70+
key := path.Clean(key)
71+
if key[len(key)-1] != '/' {
72+
key = key + "/"
73+
}
74+
75+
ok = ok || strings.HasPrefix(e.Node.Key, key)
76+
}
77+
78+
if ok && index <= e.Index() { // make sure we bypass the smaller one
6679
return e, nil
6780
}
6881

store/event_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,24 +41,24 @@ func TestScanHistory(t *testing.T) {
4141
eh.addEvent(newEvent(Create, "/foo/bar/bar", 4, 4))
4242
eh.addEvent(newEvent(Create, "/foo/foo/foo", 5, 5))
4343

44-
e, err := eh.scan("/foo", 1)
44+
e, err := eh.scan("/foo", false, 1)
4545
if err != nil || e.Index() != 1 {
4646
t.Fatalf("scan error [/foo] [1] %v", e.Index)
4747
}
4848

49-
e, err = eh.scan("/foo/bar", 1)
49+
e, err = eh.scan("/foo/bar", false, 1)
5050

5151
if err != nil || e.Index() != 2 {
5252
t.Fatalf("scan error [/foo/bar] [2] %v", e.Index)
5353
}
5454

55-
e, err = eh.scan("/foo/bar", 3)
55+
e, err = eh.scan("/foo/bar", true, 3)
5656

5757
if err != nil || e.Index() != 4 {
5858
t.Fatalf("scan error [/foo/bar/bar] [4] %v", e.Index)
5959
}
6060

61-
e, err = eh.scan("/foo/bar", 6)
61+
e, err = eh.scan("/foo/bar", true, 6)
6262

6363
if e != nil {
6464
t.Fatalf("bad index shoud reuturn nil")

0 commit comments

Comments
 (0)