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 }