Browse Source

Merge pull request #298 from benbjohnson/migration-test

Migration test
Ben Johnson 12 years ago
parent
commit
a0bfe411f8

+ 2 - 0
release_version.go

@@ -0,0 +1,2 @@
+package main
+const releaseVersion = "v0.1.2-33-g1a2a9d6"

+ 13 - 0
store/store.go

@@ -32,6 +32,12 @@ import (
 // The default version to set when the store is first initialized.
 const defaultVersion = 2
 
+var minExpireTime time.Time
+
+func init() {
+	minExpireTime, _ = time.Parse(time.RFC3339, "2000-01-01T00:00:00Z")
+}
+
 type Store interface {
 	Version() int
 	CommandFactory() CommandFactory
@@ -384,6 +390,13 @@ func (s *store) internalCreate(nodePath string, value string, unique bool, repla
 
 	nodePath = path.Clean(path.Join("/", nodePath))
 
+	// Assume expire times that are way in the past are not valid.
+	// This can occur when the time is serialized to JSON and read back in.
+	if expireTime.Before(minExpireTime) {
+		expireTime = Permanent
+	}
+
+
 	dir, newNodeName := path.Split(nodePath)
 
 	// walk through the nodePath, create dirs and get the last directory node

+ 15 - 0
tests/fixtures/v1.cluster/README

@@ -0,0 +1,15 @@
+README
+
+The scripts in this directory should be run from the project root:
+
+$ cd $GOPATH/src/github.com/coreos/etcd
+$ tests/fixtures/v1/run.1.sh
+
+Scripts with numbers should be run in separate terminal windows (in order):
+
+$ tests/fixtures/v1/run.1.sh
+$ tests/fixtures/v1/run.2.sh
+$ tests/fixtures/v1/run.3.sh
+$ tests/fixtures/v1/run.4.sh
+
+The resulting server state data can be found in tmp/node*.

+ 1 - 0
tests/fixtures/v1.cluster/node0/conf

@@ -0,0 +1 @@
+{"commitIndex":15,"peers":[{"name":"node2","connectionString":""}]}

+ 18 - 0
tests/fixtures/v1.cluster/node0/info

@@ -0,0 +1,18 @@
+{
+ "name": "node0",
+ "raftURL": "http://127.0.0.1:7001",
+ "etcdURL": "http://127.0.0.1:4001",
+ "webURL": "",
+ "raftListenHost": "127.0.0.1:7001",
+ "etcdListenHost": "127.0.0.1:4001",
+ "raftTLS": {
+  "CertFile": "",
+  "KeyFile": "",
+  "CAFile": ""
+ },
+ "etcdTLS": {
+  "CertFile": "",
+  "KeyFile": "",
+  "CAFile": ""
+ }
+}

BIN
tests/fixtures/v1.cluster/node0/log


+ 1 - 0
tests/fixtures/v1.cluster/node2/conf

@@ -0,0 +1 @@
+{"commitIndex":15,"peers":[{"name":"node0","connectionString":""}]}

+ 18 - 0
tests/fixtures/v1.cluster/node2/info

@@ -0,0 +1,18 @@
+{
+ "name": "node2",
+ "raftURL": "http://127.0.0.1:7002",
+ "etcdURL": "http://127.0.0.1:4002",
+ "webURL": "",
+ "raftListenHost": "127.0.0.1:7002",
+ "etcdListenHost": "127.0.0.1:4002",
+ "raftTLS": {
+  "CertFile": "",
+  "KeyFile": "",
+  "CAFile": ""
+ },
+ "etcdTLS": {
+  "CertFile": "",
+  "KeyFile": "",
+  "CAFile": ""
+ }
+}

BIN
tests/fixtures/v1.cluster/node2/log


+ 1 - 0
tests/fixtures/v1.cluster/node3/conf

@@ -0,0 +1 @@
+{"commitIndex":15,"peers":[{"name":"node0","connectionString":""},{"name":"node2","connectionString":""}]}

+ 18 - 0
tests/fixtures/v1.cluster/node3/info

@@ -0,0 +1,18 @@
+{
+ "name": "node3",
+ "raftURL": "http://127.0.0.1:7003",
+ "etcdURL": "http://127.0.0.1:4003",
+ "webURL": "",
+ "raftListenHost": "127.0.0.1:7003",
+ "etcdListenHost": "127.0.0.1:4003",
+ "raftTLS": {
+  "CertFile": "",
+  "KeyFile": "",
+  "CAFile": ""
+ },
+ "etcdTLS": {
+  "CertFile": "",
+  "KeyFile": "",
+  "CAFile": ""
+ }
+}

BIN
tests/fixtures/v1.cluster/node3/log


+ 4 - 0
tests/fixtures/v1.cluster/run.1.sh

@@ -0,0 +1,4 @@
+#!/bin/sh
+
+./build
+./etcd -d tmp/node0 -n node0

+ 3 - 0
tests/fixtures/v1.cluster/run.2.sh

@@ -0,0 +1,3 @@
+#!/bin/sh
+
+./etcd -s 127.0.0.1:7002 -c 127.0.0.1:4002 -C 127.0.0.1:7001 -d tmp/node2 -n node2

+ 3 - 0
tests/fixtures/v1.cluster/run.3.sh

@@ -0,0 +1,3 @@
+#!/bin/sh
+
+./etcd -s 127.0.0.1:7003 -c 127.0.0.1:4003 -C 127.0.0.1:7001 -d tmp/node3 -n node3

+ 13 - 0
tests/fixtures/v1.cluster/run.4.sh

@@ -0,0 +1,13 @@
+#!/bin/sh
+
+curl -L http://127.0.0.1:4001/v1/keys/message -d value="Hello world"
+curl -L http://127.0.0.1:4001/v1/keys/message -d value="Hello etcd"
+curl -L http://127.0.0.1:4001/v1/keys/message -X DELETE
+curl -L http://127.0.0.1:4001/v1/keys/message2 -d value="Hola"
+curl -L http://127.0.0.1:4001/v1/keys/expiring -d value=bar -d ttl=5
+curl -L http://127.0.0.1:4001/v1/keys/foo -d value=one
+curl -L http://127.0.0.1:4001/v1/keys/foo -d prevValue=two -d value=three
+curl -L http://127.0.0.1:4001/v1/keys/foo -d prevValue=one -d value=two
+curl -L http://127.0.0.1:4001/v1/keys/bar -d prevValue= -d value=four
+curl -L http://127.0.0.1:4001/v1/keys/bar -d prevValue= -d value=five
+curl -X DELETE http://127.0.0.1:7001/remove/node2

+ 13 - 0
tests/fixtures/v1.solo/README

@@ -0,0 +1,13 @@
+README
+
+The scripts in this directory should be run from the project root:
+
+$ cd $GOPATH/src/github.com/coreos/etcd
+$ tests/fixtures/v1.solo/run.1.sh
+
+Scripts with numbers should be run in separate terminal windows (in order):
+
+$ tests/fixtures/v1/run.1.sh
+$ tests/fixtures/v1/run.2.sh
+
+The resulting server state data can be found in tmp/node0.

+ 1 - 0
tests/fixtures/v1.solo/node0/conf

@@ -0,0 +1 @@
+{"commitIndex":1,"peers":[]}

+ 18 - 0
tests/fixtures/v1.solo/node0/info

@@ -0,0 +1,18 @@
+{
+ "name": "node0",
+ "raftURL": "http://127.0.0.1:7001",
+ "etcdURL": "http://127.0.0.1:4001",
+ "webURL": "",
+ "raftListenHost": "127.0.0.1:7001",
+ "etcdListenHost": "127.0.0.1:4001",
+ "raftTLS": {
+  "CertFile": "",
+  "KeyFile": "",
+  "CAFile": ""
+ },
+ "etcdTLS": {
+  "CertFile": "",
+  "KeyFile": "",
+  "CAFile": ""
+ }
+}

BIN
tests/fixtures/v1.solo/node0/log


+ 4 - 0
tests/fixtures/v1.solo/run.1.sh

@@ -0,0 +1,4 @@
+#!/bin/sh
+
+./build
+./etcd -d tmp/node0 -n node0

+ 3 - 0
tests/fixtures/v1.solo/run.2.sh

@@ -0,0 +1,3 @@
+#!/bin/sh
+
+curl -L http://127.0.0.1:4001/v1/keys/message -d value="Hello world"

+ 103 - 0
tests/functional/v1_migration_test.go

@@ -0,0 +1,103 @@
+package test
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"testing"
+	"time"
+
+	"github.com/coreos/etcd/tests"
+	"github.com/stretchr/testify/assert"
+)
+
+// Ensure that we can start a v2 node from the log of a v1 node.
+func TestV1SoloMigration(t *testing.T) {
+	path, _ := ioutil.TempDir("", "etcd-")
+	os.MkdirAll(path, 0777)
+	defer os.RemoveAll(path)
+
+	nodepath := filepath.Join(path, "node0")
+	fixturepath, _ := filepath.Abs("../fixtures/v1.solo/node0")
+	fmt.Println("DATA_DIR =", nodepath)
+
+	// Copy over fixture files.
+	c := exec.Command("cp", "-rf", fixturepath, nodepath)
+	if out, err := c.CombinedOutput(); err != nil {
+		fmt.Println(">>>>>>\n", string(out), "<<<<<<")
+		panic("Fixture initialization error:" + err.Error())
+	}
+
+	procAttr := new(os.ProcAttr)
+	procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr}
+
+	args := []string{"etcd", fmt.Sprintf("-d=%s", nodepath)}
+	args = append(args, "-c", "127.0.0.1:4001")
+	args = append(args, "-s", "127.0.0.1:7001")
+	process, err := os.StartProcess(EtcdBinPath, args, procAttr)
+	if err != nil {
+		t.Fatal("start process failed:" + err.Error())
+		return
+	}
+	defer process.Kill()
+	time.Sleep(time.Second)
+
+	// Ensure deleted message is removed.
+	resp, err := tests.Get("http://localhost:4001/v2/keys/message")
+	tests.ReadBody(resp)
+	assert.Nil(t, err, "")
+	assert.Equal(t, resp.StatusCode, 200, "")
+}
+
+// Ensure that we can start a v2 cluster from the logs of a v1 cluster.
+func TestV1ClusterMigration(t *testing.T) {
+	path, _ := ioutil.TempDir("", "etcd-")
+	os.RemoveAll(path)
+	defer os.RemoveAll(path)
+
+	nodes := []string{"node0", "node2"}
+	for i, node := range nodes {
+		nodepath := filepath.Join(path, node)
+		fixturepath, _ := filepath.Abs(filepath.Join("../fixtures/v1.cluster/", node))
+		fmt.Println("FIXPATH  =", fixturepath)
+		fmt.Println("NODEPATH =", nodepath)
+		os.MkdirAll(filepath.Dir(nodepath), 0777)
+
+		// Copy over fixture files.
+		c := exec.Command("cp", "-rf", fixturepath, nodepath)
+		if out, err := c.CombinedOutput(); err != nil {
+			fmt.Println(">>>>>>\n", string(out), "<<<<<<")
+			panic("Fixture initialization error:" + err.Error())
+		}
+
+		procAttr := new(os.ProcAttr)
+		procAttr.Files = []*os.File{nil, os.Stdout, os.Stderr}
+
+		args := []string{"etcd", fmt.Sprintf("-d=%s", nodepath)}
+		args = append(args, "-c", fmt.Sprintf("127.0.0.1:%d", 4001 + i))
+		args = append(args, "-s", fmt.Sprintf("127.0.0.1:%d", 7001 + i))
+		process, err := os.StartProcess(EtcdBinPath, args, procAttr)
+		if err != nil {
+			t.Fatal("start process failed:" + err.Error())
+			return
+		}
+		defer process.Kill()
+		time.Sleep(time.Second)
+	}
+
+	// Ensure deleted message is removed.
+	resp, err := tests.Get("http://localhost:4001/v2/keys/message")
+	body := tests.ReadBody(resp)
+	assert.Nil(t, err, "")
+	assert.Equal(t, resp.StatusCode, 400, )
+	assert.Equal(t, string(body), `{"errorCode":100,"message":"Key Not Found","cause":"/message","index":11}`+"\n")
+
+	// Ensure TTL'd message is removed.
+	resp, err = tests.Get("http://localhost:4001/v2/keys/foo")
+	body = tests.ReadBody(resp)
+	assert.Nil(t, err, "")
+	assert.Equal(t, resp.StatusCode, 200, "")
+	assert.Equal(t, string(body), `{"action":"get","key":"/foo","value":"one","modifiedIndex":9}`)
+}