From e8e561e8f5338634bb2e5b18b6f427da68c14b5f Mon Sep 17 00:00:00 2001 From: Gyu-Ho Lee Date: Mon, 11 Jul 2016 10:44:08 -0700 Subject: [PATCH] e2e: add basic upgrade tests --- e2e/ctl_v3_migrate_test.go | 4 +- e2e/etcd_release_upgrade_test.go | 87 ++++++++++++++++++++++++++++++++ e2e/etcd_test.go | 69 +++++++++++++++++++++---- 3 files changed, 149 insertions(+), 11 deletions(-) create mode 100644 e2e/etcd_release_upgrade_test.go diff --git a/e2e/ctl_v3_migrate_test.go b/e2e/ctl_v3_migrate_test.go index 3c6ac15f5de..9d7a0330643 100644 --- a/e2e/ctl_v3_migrate_test.go +++ b/e2e/ctl_v3_migrate_test.go @@ -52,7 +52,7 @@ func TestCtlV3Migrate(t *testing.T) { for i := range epc.procs { 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) } @@ -74,7 +74,7 @@ func TestCtlV3Migrate(t *testing.T) { for i := range epc.procs { epc.procs[i].cfg.keepDataDir = true } - if err := epc.Restart(); err != nil { + if err := epc.RestartAll(); err != nil { t.Fatal(err) } diff --git a/e2e/etcd_release_upgrade_test.go b/e2e/etcd_release_upgrade_test.go new file mode 100644 index 00000000000..4740b0faa85 --- /dev/null +++ b/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) + } + } + } +} diff --git a/e2e/etcd_test.go b/e2e/etcd_test.go index 54d0d0dd727..d020bb3ec9d 100644 --- a/e2e/etcd_test.go +++ b/e2e/etcd_test.go @@ -21,6 +21,7 @@ import ( "os" "strings" + "github.com/coreos/etcd/etcdserver" "github.com/coreos/etcd/pkg/expect" "github.com/coreos/etcd/pkg/fileutil" ) @@ -122,7 +123,8 @@ type etcdProcess struct { } type etcdProcessConfig struct { - args []string + execPath string + args []string dataDirPath string keepDataDir bool @@ -137,12 +139,16 @@ type etcdProcessConfig struct { } type etcdProcessClusterConfig struct { + execPath string dataDirPath string keepDataDir bool - clusterSize int - basePort int - proxySize int + clusterSize int + basePort int + proxySize int + + snapCount int // default is 10000 + clientTLS clientConnType isPeerTLS bool isPeerAutoTLS bool @@ -175,7 +181,7 @@ func newEtcdProcessCluster(cfg *etcdProcessClusterConfig) (*etcdProcessCluster, } 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") } @@ -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 { return nil, err } @@ -197,6 +203,13 @@ func (cfg *etcdProcessClusterConfig) etcdProcessConfigs() []*etcdProcessConfig { cfg.basePort = etcdProcessBasePort } + if cfg.execPath == "" { + cfg.execPath = "../bin/etcd" + } + if cfg.snapCount == 0 { + cfg.snapCount = etcdserver.DefaultSnapCount + } + clientScheme := "http" if cfg.clientTLS == clientTLS { clientScheme = "https" @@ -244,6 +257,7 @@ func (cfg *etcdProcessClusterConfig) etcdProcessConfigs() []*etcdProcessConfig { "--initial-advertise-peer-urls", purl.String(), "--initial-cluster-token", cfg.initialToken, "--data-dir", dataDirPath, + "--snapshot-count", fmt.Sprintf("%d", cfg.snapCount), } if cfg.forceNewCluster { args = append(args, "--force-new-cluster") @@ -256,6 +270,7 @@ func (cfg *etcdProcessClusterConfig) etcdProcessConfigs() []*etcdProcessConfig { args = append(args, cfg.tlsArgs()...) etcdCfgs[i] = &etcdProcessConfig{ + execPath: cfg.execPath, args: args, dataDirPath: dataDirPath, keepDataDir: cfg.keepDataDir, @@ -281,6 +296,7 @@ func (cfg *etcdProcessClusterConfig) etcdProcessConfigs() []*etcdProcessConfig { } args = append(args, cfg.tlsArgs()...) etcdCfgs[cfg.clusterSize+i] = &etcdProcessConfig{ + execPath: cfg.execPath, args: args, dataDirPath: dataDirPath, keepDataDir: cfg.keepDataDir, @@ -351,7 +367,7 @@ func (epc *etcdProcessCluster) Start() (err error) { return nil } -func (epc *etcdProcessCluster) Restart() error { +func (epc *etcdProcessCluster) RestartAll() error { for i := range epc.procs { proc, err := newEtcdProcess(epc.procs[i].cfg) if err != nil { @@ -363,7 +379,29 @@ func (epc *etcdProcessCluster) Restart() error { 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 { if p == nil { continue @@ -380,8 +418,21 @@ func (epc *etcdProcessCluster) Stop() (err error) { 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 { - err := epc.Stop() + err := epc.StopAll() for _, p := range epc.procs { os.RemoveAll(p.cfg.dataDirPath) }