Skip to content

Commit

Permalink
e2e: add basic release migration test
Browse files Browse the repository at this point in the history
For #5697.

```
MIGRATION_FROM=v3.0.2 go test -v -run TestReleaseMigrate
```
  • Loading branch information
gyuho committed Jul 9, 2016
1 parent 474eb1b commit f3b23ca
Show file tree
Hide file tree
Showing 4 changed files with 270 additions and 8 deletions.
4 changes: 2 additions & 2 deletions e2e/ctl_v3_migrate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand All @@ -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)
}

Expand Down
81 changes: 81 additions & 0 deletions e2e/etcd_release_migrate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package e2e

import (
"os"
"path/filepath"
"testing"
"time"

"github.com/coreos/etcd/pkg/testutil"
)

// TestReleaseMigrate ensures that changes to master branch does not affect the
// migration from latest etcd releases.
func TestReleaseMigrate(t *testing.T) {
ver := os.Getenv("MIGRATION_FROM")
if ver == "" {
t.Skipf("skipping release migration tests (version %q)", ver)
}

const (
fileNameTmpl = "etcd-{{.Version}}-linux-amd64.tar.gz"
extractedDirTmpl = "etcd-{{.Version}}-linux-amd64"
googleTmpl = "https://storage.googleapis.com/etcd/{{.Version}}/etcd-{{.Version}}-linux-amd64.tar.gz"
githubTmpl = "https://github.com/coreos/etcd/releases/download/{{.Version}}/etcd-{{.Version}}-linux-amd64.tar.gz"
)
var (
fileName = insertVersion(fileNameTmpl, ver)
extractedDir = insertVersion(extractedDirTmpl, ver)
releaseBinaryPath = filepath.Join(extractedDir, "etcd")
)
os.RemoveAll(fileName)
os.RemoveAll(extractedDir)
if err := downloadExtract(fileName, insertVersion(googleTmpl, ver), insertVersion(githubTmpl, ver)); err != nil {
t.Fatal(err)
}
defer func() {
os.RemoveAll(fileName)
os.RemoveAll(extractedDir)
}()

defer testutil.AfterTest(t)

copiedCfg := configNoTLS
copiedCfg.execPath = releaseBinaryPath
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,
}
if err := ctlV3Put(cx, "foo", "bar", ""); err != nil {
cx.t.Fatalf("ctlV3Put error (%v)", err)
}

for i := range epc.backends() {
if err := epc.Stop(i); err != nil {
t.Fatalf("error closing etcd process (%v)", err)
}
epc.procs[i].cfg.execPath = "../bin/etcd"
epc.procs[i].cfg.keepDataDir = true
if err := epc.Restart(i); err != nil {
t.Fatalf("error restarting etcd process (%v)", err)
}
if err := ctlV3Get(cx, []string{"foo"}, []kv{{"foo", "bar"}}...); err != nil {
cx.t.Fatalf("ctlV3Get error (%v)", err)
}
}
}
131 changes: 131 additions & 0 deletions e2e/etcd_release_migrate_util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// 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 (
"archive/tar"
"bytes"
"compress/gzip"
"fmt"
"html/template"
"io"
"net/http"
"os"
"path/filepath"
"runtime"
)

func downloadExtract(fileName string, urls ...string) error {
f, err := os.Create(fileName)
if err != nil {
return err
}
defer f.Close()

for _, url := range urls {
var resp *http.Response
resp, err = http.Get(url)
if err != nil {
continue
}
defer resp.Body.Close()

_, err = io.Copy(f, resp.Body)
if err != nil {
continue
}
break
}
if err != nil {
return err
}
return extractTarGz(fileName)
}

func insertVersion(tmpl, ver string) string {
buf := new(bytes.Buffer)
if err := template.Must(template.New("tmpl").Parse(tmpl)).Execute(buf, struct{ Version string }{ver}); err != nil {
panic(err)
}
return buf.String()
}

func extractTarGz(target string) error {
f, err := os.Open(target)
if err != nil {
return err
}
defer f.Close()

gr, err := gzip.NewReader(f)
if err != nil {
return err
}
defer gr.Close()

tr := tar.NewReader(gr)
for {
header, err := tr.Next()
if err == io.EOF {
break
} else if err != nil {
return err
}

if err := untar(tr, header); err != nil {
return err
}
}
return nil
}

func untar(tr *tar.Reader, header *tar.Header) error {
switch header.Typeflag {
case tar.TypeDir:
return os.MkdirAll(header.Name, 0700)
case tar.TypeReg, tar.TypeRegA:
return writeFile(header.Name, tr, header.FileInfo().Mode())
case tar.TypeSymlink:
return writeSymlink(header.Name, header.Linkname)
default:
return fmt.Errorf("%s has unknown type %v", header.Name, header.Typeflag)
}
}

func writeFile(fpath string, rd io.Reader, mode os.FileMode) error {
if err := os.MkdirAll(filepath.Dir(fpath), 0700); err != nil {
return err
}

f, err := os.Create(fpath)
if err != nil {
return err
}
defer f.Close()

if err = f.Chmod(mode); err != nil && runtime.GOOS != "windows" {
return err
}

_, err = io.Copy(f, rd)
return err
}

func writeSymlink(fpath string, target string) error {
if err := os.MkdirAll(filepath.Dir(fpath), 0700); err != nil {
return err
}
return os.Symlink(target, fpath)
}
Loading

0 comments on commit f3b23ca

Please sign in to comment.