Browse Source

e2e: add basic upgrade tests

Gyu-Ho Lee 9 years ago
parent
commit
e8e561e8f5
3 changed files with 149 additions and 11 deletions
  1. 2 2
      e2e/ctl_v3_migrate_test.go
  2. 87 0
      e2e/etcd_release_upgrade_test.go
  3. 60 9
      e2e/etcd_test.go

+ 2 - 2
e2e/ctl_v3_migrate_test.go

@@ -52,7 +52,7 @@ func TestCtlV3Migrate(t *testing.T) {
 	for i := range epc.procs {
 	for i := range epc.procs {
 		dataDirs[i] = epc.procs[i].cfg.dataDirPath
 		dataDirs[i] = epc.procs[i].cfg.dataDirPath
 	}
 	}
-	if err := epc.Stop(); err != nil {
+	if err := epc.StopAll(); err != nil {
 		t.Fatalf("error closing etcd processes (%v)", err)
 		t.Fatalf("error closing etcd processes (%v)", err)
 	}
 	}
 
 
@@ -74,7 +74,7 @@ func TestCtlV3Migrate(t *testing.T) {
 	for i := range epc.procs {
 	for i := range epc.procs {
 		epc.procs[i].cfg.keepDataDir = true
 		epc.procs[i].cfg.keepDataDir = true
 	}
 	}
-	if err := epc.Restart(); err != nil {
+	if err := epc.RestartAll(); err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 
 

+ 87 - 0
e2e/etcd_release_upgrade_test.go

@@ -0,0 +1,87 @@
+// Copyright 2016 The etcd Authors
+//
+// 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 e2e
+
+import (
+	"fmt"
+	"os"
+	"testing"
+	"time"
+
+	"github.com/coreos/etcd/pkg/fileutil"
+	"github.com/coreos/etcd/pkg/testutil"
+)
+
+// TestReleaseUpgrade ensures that changes to master branch does not affect
+// upgrade from latest etcd releases.
+func TestReleaseUpgrade(t *testing.T) {
+	lastReleaseBinary := "../bin/etcd-last-release"
+	if !fileutil.Exist(lastReleaseBinary) {
+		t.Skipf("%q does not exist", lastReleaseBinary)
+	}
+
+	defer testutil.AfterTest(t)
+
+	copiedCfg := configNoTLS
+	copiedCfg.execPath = lastReleaseBinary
+	copiedCfg.snapCount = 3
+
+	epc, err := newEtcdProcessCluster(&copiedCfg)
+	if err != nil {
+		t.Fatalf("could not start etcd process cluster (%v)", err)
+	}
+	defer func() {
+		if errC := epc.Close(); errC != nil {
+			t.Fatalf("error closing etcd processes (%v)", errC)
+		}
+	}()
+
+	os.Setenv("ETCDCTL_API", "3")
+	defer os.Unsetenv("ETCDCTL_API")
+	cx := ctlCtx{
+		t:           t,
+		cfg:         configNoTLS,
+		dialTimeout: 7 * time.Second,
+		quorum:      true,
+		epc:         epc,
+	}
+	var kvs []kv
+	for i := 0; i < 5; i++ {
+		kvs = append(kvs, kv{key: fmt.Sprintf("foo%d", i), val: "bar"})
+	}
+	for i := range kvs {
+		if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil {
+			cx.t.Fatalf("#%d: ctlV3Put error (%v)", i, err)
+		}
+	}
+
+	for i := range epc.procs {
+		if err := epc.procs[i].Stop(); err != nil {
+			t.Fatalf("#%d: error closing etcd process (%v)", i, err)
+		}
+		epc.procs[i].cfg.execPath = "../bin/etcd"
+		epc.procs[i].cfg.keepDataDir = true
+
+		if err := epc.procs[i].Restart(); err != nil {
+			t.Fatalf("error restarting etcd process (%v)", err)
+		}
+
+		for j := range kvs {
+			if err := ctlV3Get(cx, []string{kvs[j].key}, []kv{kvs[j]}...); err != nil {
+				cx.t.Fatalf("#%d-%d: ctlV3Get error (%v)", i, j, err)
+			}
+		}
+	}
+}

+ 60 - 9
e2e/etcd_test.go

@@ -21,6 +21,7 @@ import (
 	"os"
 	"os"
 	"strings"
 	"strings"
 
 
+	"github.com/coreos/etcd/etcdserver"
 	"github.com/coreos/etcd/pkg/expect"
 	"github.com/coreos/etcd/pkg/expect"
 	"github.com/coreos/etcd/pkg/fileutil"
 	"github.com/coreos/etcd/pkg/fileutil"
 )
 )
@@ -122,7 +123,8 @@ type etcdProcess struct {
 }
 }
 
 
 type etcdProcessConfig struct {
 type etcdProcessConfig struct {
-	args []string
+	execPath string
+	args     []string
 
 
 	dataDirPath string
 	dataDirPath string
 	keepDataDir bool
 	keepDataDir bool
@@ -137,12 +139,16 @@ type etcdProcessConfig struct {
 }
 }
 
 
 type etcdProcessClusterConfig struct {
 type etcdProcessClusterConfig struct {
+	execPath    string
 	dataDirPath string
 	dataDirPath string
 	keepDataDir bool
 	keepDataDir bool
 
 
-	clusterSize       int
-	basePort          int
-	proxySize         int
+	clusterSize int
+	basePort    int
+	proxySize   int
+
+	snapCount int // default is 10000
+
 	clientTLS         clientConnType
 	clientTLS         clientConnType
 	isPeerTLS         bool
 	isPeerTLS         bool
 	isPeerAutoTLS     bool
 	isPeerAutoTLS     bool
@@ -175,7 +181,7 @@ func newEtcdProcessCluster(cfg *etcdProcessClusterConfig) (*etcdProcessCluster,
 }
 }
 
 
 func newEtcdProcess(cfg *etcdProcessConfig) (*etcdProcess, error) {
 func newEtcdProcess(cfg *etcdProcessConfig) (*etcdProcess, error) {
-	if !fileutil.Exist("../bin/etcd") {
+	if !fileutil.Exist(cfg.execPath) {
 		return nil, fmt.Errorf("could not find etcd binary")
 		return nil, fmt.Errorf("could not find etcd binary")
 	}
 	}
 
 
@@ -185,7 +191,7 @@ func newEtcdProcess(cfg *etcdProcessConfig) (*etcdProcess, error) {
 		}
 		}
 	}
 	}
 
 
-	child, err := spawnCmd(append([]string{"../bin/etcd"}, cfg.args...))
+	child, err := spawnCmd(append([]string{cfg.execPath}, cfg.args...))
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -197,6 +203,13 @@ func (cfg *etcdProcessClusterConfig) etcdProcessConfigs() []*etcdProcessConfig {
 		cfg.basePort = etcdProcessBasePort
 		cfg.basePort = etcdProcessBasePort
 	}
 	}
 
 
+	if cfg.execPath == "" {
+		cfg.execPath = "../bin/etcd"
+	}
+	if cfg.snapCount == 0 {
+		cfg.snapCount = etcdserver.DefaultSnapCount
+	}
+
 	clientScheme := "http"
 	clientScheme := "http"
 	if cfg.clientTLS == clientTLS {
 	if cfg.clientTLS == clientTLS {
 		clientScheme = "https"
 		clientScheme = "https"
@@ -244,6 +257,7 @@ func (cfg *etcdProcessClusterConfig) etcdProcessConfigs() []*etcdProcessConfig {
 			"--initial-advertise-peer-urls", purl.String(),
 			"--initial-advertise-peer-urls", purl.String(),
 			"--initial-cluster-token", cfg.initialToken,
 			"--initial-cluster-token", cfg.initialToken,
 			"--data-dir", dataDirPath,
 			"--data-dir", dataDirPath,
+			"--snapshot-count", fmt.Sprintf("%d", cfg.snapCount),
 		}
 		}
 		if cfg.forceNewCluster {
 		if cfg.forceNewCluster {
 			args = append(args, "--force-new-cluster")
 			args = append(args, "--force-new-cluster")
@@ -256,6 +270,7 @@ func (cfg *etcdProcessClusterConfig) etcdProcessConfigs() []*etcdProcessConfig {
 
 
 		args = append(args, cfg.tlsArgs()...)
 		args = append(args, cfg.tlsArgs()...)
 		etcdCfgs[i] = &etcdProcessConfig{
 		etcdCfgs[i] = &etcdProcessConfig{
+			execPath:    cfg.execPath,
 			args:        args,
 			args:        args,
 			dataDirPath: dataDirPath,
 			dataDirPath: dataDirPath,
 			keepDataDir: cfg.keepDataDir,
 			keepDataDir: cfg.keepDataDir,
@@ -281,6 +296,7 @@ func (cfg *etcdProcessClusterConfig) etcdProcessConfigs() []*etcdProcessConfig {
 		}
 		}
 		args = append(args, cfg.tlsArgs()...)
 		args = append(args, cfg.tlsArgs()...)
 		etcdCfgs[cfg.clusterSize+i] = &etcdProcessConfig{
 		etcdCfgs[cfg.clusterSize+i] = &etcdProcessConfig{
+			execPath:    cfg.execPath,
 			args:        args,
 			args:        args,
 			dataDirPath: dataDirPath,
 			dataDirPath: dataDirPath,
 			keepDataDir: cfg.keepDataDir,
 			keepDataDir: cfg.keepDataDir,
@@ -351,7 +367,7 @@ func (epc *etcdProcessCluster) Start() (err error) {
 	return nil
 	return nil
 }
 }
 
 
-func (epc *etcdProcessCluster) Restart() error {
+func (epc *etcdProcessCluster) RestartAll() error {
 	for i := range epc.procs {
 	for i := range epc.procs {
 		proc, err := newEtcdProcess(epc.procs[i].cfg)
 		proc, err := newEtcdProcess(epc.procs[i].cfg)
 		if err != nil {
 		if err != nil {
@@ -363,7 +379,29 @@ func (epc *etcdProcessCluster) Restart() error {
 	return epc.Start()
 	return epc.Start()
 }
 }
 
 
-func (epc *etcdProcessCluster) Stop() (err error) {
+func (epr *etcdProcess) Restart() error {
+	proc, err := newEtcdProcess(epr.cfg)
+	if err != nil {
+		epr.Stop()
+		return err
+	}
+	*epr = *proc
+
+	readyStr := "enabled capabilities for version"
+	if proc.cfg.isProxy {
+		readyStr = "httpproxy: endpoints found"
+	}
+
+	if _, err = proc.proc.Expect(readyStr); err != nil {
+		epr.Stop()
+		return err
+	}
+	close(proc.donec)
+
+	return nil
+}
+
+func (epc *etcdProcessCluster) StopAll() (err error) {
 	for _, p := range epc.procs {
 	for _, p := range epc.procs {
 		if p == nil {
 		if p == nil {
 			continue
 			continue
@@ -380,8 +418,21 @@ func (epc *etcdProcessCluster) Stop() (err error) {
 	return err
 	return err
 }
 }
 
 
+func (epr *etcdProcess) Stop() error {
+	if epr == nil {
+		return nil
+	}
+
+	if err := epr.proc.Stop(); err != nil {
+		return err
+	}
+
+	<-epr.donec
+	return nil
+}
+
 func (epc *etcdProcessCluster) Close() error {
 func (epc *etcdProcessCluster) Close() error {
-	err := epc.Stop()
+	err := epc.StopAll()
 	for _, p := range epc.procs {
 	for _, p := range epc.procs {
 		os.RemoveAll(p.cfg.dataDirPath)
 		os.RemoveAll(p.cfg.dataDirPath)
 	}
 	}