go.etcd.io/etcd@v3.3.27+incompatible/tests/e2e/etcd_release_upgrade_test.go (about)

     1  // Copyright 2016 The etcd Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package e2e
    16  
    17  import (
    18  	"fmt"
    19  	"math/rand"
    20  	"os"
    21  	"strings"
    22  	"sync"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/coreos/etcd/pkg/fileutil"
    27  	"github.com/coreos/etcd/pkg/testutil"
    28  	"github.com/coreos/etcd/version"
    29  )
    30  
    31  // TestReleaseUpgrade ensures that changes to master branch does not affect
    32  // upgrade from latest etcd releases.
    33  func TestReleaseUpgrade(t *testing.T) {
    34  	lastReleaseBinary := binDir + "/etcd-last-release"
    35  	if !fileutil.Exist(lastReleaseBinary) {
    36  		t.Skipf("%q does not exist", lastReleaseBinary)
    37  	}
    38  
    39  	defer testutil.AfterTest(t)
    40  
    41  	copiedCfg := configNoTLS
    42  	copiedCfg.execPath = lastReleaseBinary
    43  	copiedCfg.snapshotCount = 3
    44  	copiedCfg.baseScheme = "unix" // to avoid port conflict
    45  
    46  	epc, err := newEtcdProcessCluster(&copiedCfg)
    47  	if err != nil {
    48  		t.Fatalf("could not start etcd process cluster (%v)", err)
    49  	}
    50  	defer func() {
    51  		if errC := epc.Close(); errC != nil {
    52  			t.Fatalf("error closing etcd processes (%v)", errC)
    53  		}
    54  	}()
    55  	// 3.0 boots as 2.3 then negotiates up to 3.0
    56  	// so there's a window at boot time where it doesn't have V3rpcCapability enabled
    57  	// poll /version until etcdcluster is >2.3.x before making v3 requests
    58  	for i := 0; i < 7; i++ {
    59  		if err = cURLGet(epc, cURLReq{endpoint: "/version", expected: `"etcdcluster":"` + version.Cluster(version.Version)}); err != nil {
    60  			t.Logf("#%d: v3 is not ready yet (%v)", i, err)
    61  			time.Sleep(time.Second)
    62  			continue
    63  		}
    64  		break
    65  	}
    66  	if err != nil {
    67  		t.Fatalf("cannot pull version (%v)", err)
    68  	}
    69  
    70  	os.Setenv("ETCDCTL_API", "3")
    71  	defer os.Unsetenv("ETCDCTL_API")
    72  	cx := ctlCtx{
    73  		t:           t,
    74  		cfg:         configNoTLS,
    75  		dialTimeout: 7 * time.Second,
    76  		quorum:      true,
    77  		epc:         epc,
    78  	}
    79  	var kvs []kv
    80  	for i := 0; i < 5; i++ {
    81  		kvs = append(kvs, kv{key: fmt.Sprintf("foo%d", i), val: "bar"})
    82  	}
    83  	for i := range kvs {
    84  		if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil {
    85  			cx.t.Fatalf("#%d: ctlV3Put error (%v)", i, err)
    86  		}
    87  	}
    88  
    89  	for i := range epc.procs {
    90  		if err := epc.procs[i].Stop(); err != nil {
    91  			t.Fatalf("#%d: error closing etcd process (%v)", i, err)
    92  		}
    93  		epc.procs[i].Config().execPath = binDir + "/etcd"
    94  		epc.procs[i].Config().keepDataDir = true
    95  
    96  		if err := epc.procs[i].Restart(); err != nil {
    97  			t.Fatalf("error restarting etcd process (%v)", err)
    98  		}
    99  
   100  		for j := range kvs {
   101  			if err := ctlV3Get(cx, []string{kvs[j].key}, []kv{kvs[j]}...); err != nil {
   102  				cx.t.Fatalf("#%d-%d: ctlV3Get error (%v)", i, j, err)
   103  			}
   104  		}
   105  	}
   106  
   107  	// expect upgraded cluster version
   108  	if err := cURLGet(cx.epc, cURLReq{endpoint: "/metrics", expected: fmt.Sprintf(`etcd_cluster_version{cluster_version="%s"} 1`, version.Cluster(version.Version)), metricsURLScheme: cx.cfg.metricsURLScheme}); err != nil {
   109  		cx.t.Fatalf("failed get with curl (%v)", err)
   110  	}
   111  }
   112  
   113  func TestReleaseUpgradeWithRestart(t *testing.T) {
   114  	lastReleaseBinary := binDir + "/etcd-last-release"
   115  	if !fileutil.Exist(lastReleaseBinary) {
   116  		t.Skipf("%q does not exist", lastReleaseBinary)
   117  	}
   118  
   119  	defer testutil.AfterTest(t)
   120  
   121  	copiedCfg := configNoTLS
   122  	copiedCfg.execPath = lastReleaseBinary
   123  	copiedCfg.snapshotCount = 10
   124  	copiedCfg.baseScheme = "unix"
   125  
   126  	epc, err := newEtcdProcessCluster(&copiedCfg)
   127  	if err != nil {
   128  		t.Fatalf("could not start etcd process cluster (%v)", err)
   129  	}
   130  	defer func() {
   131  		if errC := epc.Close(); errC != nil {
   132  			t.Fatalf("error closing etcd processes (%v)", errC)
   133  		}
   134  	}()
   135  
   136  	os.Setenv("ETCDCTL_API", "3")
   137  	defer os.Unsetenv("ETCDCTL_API")
   138  	cx := ctlCtx{
   139  		t:           t,
   140  		cfg:         configNoTLS,
   141  		dialTimeout: 7 * time.Second,
   142  		quorum:      true,
   143  		epc:         epc,
   144  	}
   145  	var kvs []kv
   146  	for i := 0; i < 50; i++ {
   147  		kvs = append(kvs, kv{key: fmt.Sprintf("foo%d", i), val: "bar"})
   148  	}
   149  	for i := range kvs {
   150  		if err := ctlV3Put(cx, kvs[i].key, kvs[i].val, ""); err != nil {
   151  			cx.t.Fatalf("#%d: ctlV3Put error (%v)", i, err)
   152  		}
   153  	}
   154  
   155  	for i := range epc.procs {
   156  		if err := epc.procs[i].Stop(); err != nil {
   157  			t.Fatalf("#%d: error closing etcd process (%v)", i, err)
   158  		}
   159  	}
   160  
   161  	var wg sync.WaitGroup
   162  	wg.Add(len(epc.procs))
   163  	for i := range epc.procs {
   164  		go func(i int) {
   165  			epc.procs[i].Config().execPath = binDir + "/etcd"
   166  			epc.procs[i].Config().keepDataDir = true
   167  			if err := epc.procs[i].Restart(); err != nil {
   168  				t.Fatalf("error restarting etcd process (%v)", err)
   169  			}
   170  			wg.Done()
   171  		}(i)
   172  	}
   173  	wg.Wait()
   174  
   175  	if err := ctlV3Get(cx, []string{kvs[0].key}, []kv{kvs[0]}...); err != nil {
   176  		t.Fatal(err)
   177  	}
   178  }
   179  
   180  type cURLReq struct {
   181  	username string
   182  	password string
   183  
   184  	isTLS   bool
   185  	timeout int
   186  
   187  	endpoint string
   188  
   189  	value    string
   190  	expected string
   191  	header   string
   192  
   193  	metricsURLScheme string
   194  
   195  	ciphers string
   196  }
   197  
   198  // cURLPrefixArgs builds the beginning of a curl command for a given key
   199  // addressed to a random URL in the given cluster.
   200  func cURLPrefixArgs(clus *etcdProcessCluster, method string, req cURLReq) []string {
   201  	var (
   202  		cmdArgs = []string{"curl"}
   203  		acurl   = clus.procs[rand.Intn(clus.cfg.clusterSize)].Config().acurl
   204  	)
   205  	if req.metricsURLScheme != "https" {
   206  		if req.isTLS {
   207  			if clus.cfg.clientTLS != clientTLSAndNonTLS {
   208  				panic("should not use cURLPrefixArgsUseTLS when serving only TLS or non-TLS")
   209  			}
   210  			cmdArgs = append(cmdArgs, "--cacert", caPath, "--cert", certPath, "--key", privateKeyPath)
   211  			acurl = toTLS(clus.procs[rand.Intn(clus.cfg.clusterSize)].Config().acurl)
   212  		} else if clus.cfg.clientTLS == clientTLS {
   213  			if !clus.cfg.noCN {
   214  				cmdArgs = append(cmdArgs, "--cacert", caPath, "--cert", certPath, "--key", privateKeyPath)
   215  			} else {
   216  				cmdArgs = append(cmdArgs, "--cacert", caPath, "--cert", certPath3, "--key", privateKeyPath3)
   217  			}
   218  		}
   219  	}
   220  	if req.metricsURLScheme != "" {
   221  		acurl = clus.procs[rand.Intn(clus.cfg.clusterSize)].EndpointsMetrics()[0]
   222  	}
   223  	ep := acurl + req.endpoint
   224  
   225  	if req.username != "" || req.password != "" {
   226  		cmdArgs = append(cmdArgs, "-L", "-u", fmt.Sprintf("%s:%s", req.username, req.password), ep)
   227  	} else {
   228  		cmdArgs = append(cmdArgs, "-L", ep)
   229  	}
   230  	if req.timeout != 0 {
   231  		cmdArgs = append(cmdArgs, "-m", fmt.Sprintf("%d", req.timeout))
   232  	}
   233  
   234  	if req.header != "" {
   235  		cmdArgs = append(cmdArgs, "-H", req.header)
   236  	}
   237  
   238  	if req.ciphers != "" {
   239  		cmdArgs = append(cmdArgs, "--ciphers", req.ciphers)
   240  	}
   241  
   242  	switch method {
   243  	case "POST", "PUT":
   244  		dt := req.value
   245  		if !strings.HasPrefix(dt, "{") { // for non-JSON value
   246  			dt = "value=" + dt
   247  		}
   248  		cmdArgs = append(cmdArgs, "-X", method, "-d", dt)
   249  	}
   250  	return cmdArgs
   251  }
   252  
   253  func cURLPost(clus *etcdProcessCluster, req cURLReq) error {
   254  	return spawnWithExpect(cURLPrefixArgs(clus, "POST", req), req.expected)
   255  }
   256  
   257  func cURLPut(clus *etcdProcessCluster, req cURLReq) error {
   258  	return spawnWithExpect(cURLPrefixArgs(clus, "PUT", req), req.expected)
   259  }
   260  
   261  func cURLGet(clus *etcdProcessCluster, req cURLReq) error {
   262  	return spawnWithExpect(cURLPrefixArgs(clus, "GET", req), req.expected)
   263  }