ctl_v3_snapshot_test.go 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  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. package e2e
  15. import (
  16. "encoding/json"
  17. "fmt"
  18. "io"
  19. "io/ioutil"
  20. "os"
  21. "path/filepath"
  22. "strings"
  23. "testing"
  24. "time"
  25. "github.com/coreos/etcd/pkg/expect"
  26. "github.com/coreos/etcd/pkg/testutil"
  27. )
  28. func TestCtlV3Snapshot(t *testing.T) { testCtl(t, snapshotTest) }
  29. func snapshotTest(cx ctlCtx) {
  30. maintenanceInitKeys(cx)
  31. leaseID, err := ctlV3LeaseGrant(cx, 100)
  32. if err != nil {
  33. cx.t.Fatalf("snapshot: ctlV3LeaseGrant error (%v)", err)
  34. }
  35. if err = ctlV3Put(cx, "withlease", "withlease", leaseID); err != nil {
  36. cx.t.Fatalf("snapshot: ctlV3Put error (%v)", err)
  37. }
  38. fpath := "test.snapshot"
  39. defer os.RemoveAll(fpath)
  40. if err = ctlV3SnapshotSave(cx, fpath); err != nil {
  41. cx.t.Fatalf("snapshotTest ctlV3SnapshotSave error (%v)", err)
  42. }
  43. st, err := getSnapshotStatus(cx, fpath)
  44. if err != nil {
  45. cx.t.Fatalf("snapshotTest getSnapshotStatus error (%v)", err)
  46. }
  47. if st.Revision != 5 {
  48. cx.t.Fatalf("expected 4, got %d", st.Revision)
  49. }
  50. if st.TotalKey < 4 {
  51. cx.t.Fatalf("expected at least 4, got %d", st.TotalKey)
  52. }
  53. }
  54. func TestCtlV3SnapshotCorrupt(t *testing.T) { testCtl(t, snapshotCorruptTest) }
  55. func snapshotCorruptTest(cx ctlCtx) {
  56. fpath := "test.snapshot"
  57. defer os.RemoveAll(fpath)
  58. if err := ctlV3SnapshotSave(cx, fpath); err != nil {
  59. cx.t.Fatalf("snapshotTest ctlV3SnapshotSave error (%v)", err)
  60. }
  61. // corrupt file
  62. f, oerr := os.OpenFile(fpath, os.O_WRONLY, 0)
  63. if oerr != nil {
  64. cx.t.Fatal(oerr)
  65. }
  66. if _, err := f.Write(make([]byte, 512)); err != nil {
  67. cx.t.Fatal(err)
  68. }
  69. f.Close()
  70. defer os.RemoveAll("snap.etcd")
  71. serr := spawnWithExpect(
  72. append(cx.PrefixArgs(), "snapshot", "restore",
  73. "--data-dir", "snap.etcd",
  74. fpath),
  75. "expected sha256")
  76. if serr != nil {
  77. cx.t.Fatal(serr)
  78. }
  79. }
  80. // This test ensures that the snapshot status does not modify the snapshot file
  81. func TestCtlV3SnapshotStatusBeforeRestore(t *testing.T) { testCtl(t, snapshotStatusBeforeRestoreTest) }
  82. func snapshotStatusBeforeRestoreTest(cx ctlCtx) {
  83. fpath := "test.snapshot"
  84. defer os.RemoveAll(fpath)
  85. if err := ctlV3SnapshotSave(cx, fpath); err != nil {
  86. cx.t.Fatalf("snapshotTest ctlV3SnapshotSave error (%v)", err)
  87. }
  88. // snapshot status on the fresh snapshot file
  89. _, err := getSnapshotStatus(cx, fpath)
  90. if err != nil {
  91. cx.t.Fatalf("snapshotTest getSnapshotStatus error (%v)", err)
  92. }
  93. defer os.RemoveAll("snap.etcd")
  94. serr := spawnWithExpect(
  95. append(cx.PrefixArgs(), "snapshot", "restore",
  96. "--data-dir", "snap.etcd",
  97. fpath),
  98. "added member")
  99. if serr != nil {
  100. cx.t.Fatal(serr)
  101. }
  102. }
  103. func ctlV3SnapshotSave(cx ctlCtx, fpath string) error {
  104. cmdArgs := append(cx.PrefixArgs(), "snapshot", "save", fpath)
  105. return spawnWithExpect(cmdArgs, fmt.Sprintf("Snapshot saved at %s", fpath))
  106. }
  107. type snapshotStatus struct {
  108. Hash uint32 `json:"hash"`
  109. Revision int64 `json:"revision"`
  110. TotalKey int `json:"totalKey"`
  111. TotalSize int64 `json:"totalSize"`
  112. }
  113. func getSnapshotStatus(cx ctlCtx, fpath string) (snapshotStatus, error) {
  114. cmdArgs := append(cx.PrefixArgs(), "--write-out", "json", "snapshot", "status", fpath)
  115. proc, err := spawnCmd(cmdArgs)
  116. if err != nil {
  117. return snapshotStatus{}, err
  118. }
  119. var txt string
  120. txt, err = proc.Expect("totalKey")
  121. if err != nil {
  122. return snapshotStatus{}, err
  123. }
  124. if err = proc.Close(); err != nil {
  125. return snapshotStatus{}, err
  126. }
  127. resp := snapshotStatus{}
  128. dec := json.NewDecoder(strings.NewReader(txt))
  129. if err := dec.Decode(&resp); err == io.EOF {
  130. return snapshotStatus{}, err
  131. }
  132. return resp, nil
  133. }
  134. // TestIssue6361 ensures new member that starts with snapshot correctly
  135. // syncs up with other members and serve correct data.
  136. func TestIssue6361(t *testing.T) {
  137. defer testutil.AfterTest(t)
  138. mustEtcdctl(t)
  139. os.Setenv("ETCDCTL_API", "3")
  140. defer os.Unsetenv("ETCDCTL_API")
  141. epc, err := newEtcdProcessCluster(&etcdProcessClusterConfig{
  142. clusterSize: 1,
  143. initialToken: "new",
  144. keepDataDir: true,
  145. })
  146. if err != nil {
  147. t.Fatalf("could not start etcd process cluster (%v)", err)
  148. }
  149. defer func() {
  150. if errC := epc.Close(); errC != nil {
  151. t.Fatalf("error closing etcd processes (%v)", errC)
  152. }
  153. }()
  154. dialTimeout := 7 * time.Second
  155. prefixArgs := []string{ctlBinPath, "--endpoints", strings.Join(epc.grpcEndpoints(), ","), "--dial-timeout", dialTimeout.String()}
  156. // write some keys
  157. kvs := []kv{{"foo1", "val1"}, {"foo2", "val2"}, {"foo3", "val3"}}
  158. for i := range kvs {
  159. if err = spawnWithExpect(append(prefixArgs, "put", kvs[i].key, kvs[i].val), "OK"); err != nil {
  160. t.Fatal(err)
  161. }
  162. }
  163. fpath := filepath.Join(os.TempDir(), "test.snapshot")
  164. defer os.RemoveAll(fpath)
  165. // etcdctl save snapshot
  166. if err = spawnWithExpect(append(prefixArgs, "snapshot", "save", fpath), fmt.Sprintf("Snapshot saved at %s", fpath)); err != nil {
  167. t.Fatal(err)
  168. }
  169. if err = epc.processes()[0].Stop(); err != nil {
  170. t.Fatal(err)
  171. }
  172. newDataDir := filepath.Join(os.TempDir(), "test.data")
  173. defer os.RemoveAll(newDataDir)
  174. // etcdctl restore the snapshot
  175. err = spawnWithExpect([]string{ctlBinPath, "snapshot", "restore", fpath, "--name", epc.procs[0].cfg.name, "--initial-cluster", epc.procs[0].cfg.initialCluster, "--initial-cluster-token", epc.procs[0].cfg.initialToken, "--initial-advertise-peer-urls", epc.procs[0].cfg.purl.String(), "--data-dir", newDataDir}, "membership: added member")
  176. if err != nil {
  177. t.Fatal(err)
  178. }
  179. // start the etcd member using the restored snapshot
  180. epc.procs[0].cfg.dataDirPath = newDataDir
  181. for i := range epc.procs[0].cfg.args {
  182. if epc.procs[0].cfg.args[i] == "--data-dir" {
  183. epc.procs[0].cfg.args[i+1] = newDataDir
  184. }
  185. }
  186. if err = epc.processes()[0].Restart(); err != nil {
  187. t.Fatal(err)
  188. }
  189. // ensure the restored member has the correct data
  190. for i := range kvs {
  191. if err = spawnWithExpect(append(prefixArgs, "get", kvs[i].key), kvs[i].val); err != nil {
  192. t.Fatal(err)
  193. }
  194. }
  195. // add a new member into the cluster
  196. clientURL := fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+30)
  197. peerURL := fmt.Sprintf("http://localhost:%d", etcdProcessBasePort+31)
  198. err = spawnWithExpect(append(prefixArgs, "member", "add", "newmember", fmt.Sprintf("--peer-urls=%s", peerURL)), " added to cluster ")
  199. if err != nil {
  200. t.Fatal(err)
  201. }
  202. var newDataDir2 string
  203. newDataDir2, err = ioutil.TempDir("", "newdata2")
  204. if err != nil {
  205. t.Fatal(err)
  206. }
  207. defer os.RemoveAll(newDataDir2)
  208. name2 := "infra2"
  209. initialCluster2 := epc.procs[0].cfg.initialCluster + fmt.Sprintf(",%s=%s", name2, peerURL)
  210. // start the new member
  211. var nepc *expect.ExpectProcess
  212. nepc, err = spawnCmd([]string{epc.procs[0].cfg.execPath, "--name", name2,
  213. "--listen-client-urls", clientURL, "--advertise-client-urls", clientURL,
  214. "--listen-peer-urls", peerURL, "--initial-advertise-peer-urls", peerURL,
  215. "--initial-cluster", initialCluster2, "--initial-cluster-state", "existing", "--data-dir", newDataDir2})
  216. if err != nil {
  217. t.Fatal(err)
  218. }
  219. if _, err = nepc.Expect("enabled capabilities for version"); err != nil {
  220. t.Fatal(err)
  221. }
  222. prefixArgs = []string{ctlBinPath, "--endpoints", clientURL, "--dial-timeout", dialTimeout.String()}
  223. // ensure added member has data from incoming snapshot
  224. for i := range kvs {
  225. if err = spawnWithExpect(append(prefixArgs, "get", kvs[i].key), kvs[i].val); err != nil {
  226. t.Fatal(err)
  227. }
  228. }
  229. if err = nepc.Stop(); err != nil {
  230. t.Fatal(err)
  231. }
  232. }
  233. func TestCtlV3SnapshotWithAuth(t *testing.T) { testCtl(t, snapshotTestWithAuth) }
  234. func snapshotTestWithAuth(cx ctlCtx) {
  235. maintenanceInitKeys(cx)
  236. if err := authEnable(cx); err != nil {
  237. cx.t.Fatal(err)
  238. }
  239. cx.user, cx.pass = "root", "root"
  240. authSetupTestUser(cx)
  241. fpath := "test.snapshot"
  242. defer os.RemoveAll(fpath)
  243. // ordinary user cannot save a snapshot
  244. cx.user, cx.pass = "test-user", "pass"
  245. if err := ctlV3SnapshotSave(cx, fpath); err == nil {
  246. cx.t.Fatal("ordinary user should not be able to save a snapshot")
  247. }
  248. // root can save a snapshot
  249. cx.user, cx.pass = "root", "root"
  250. if err := ctlV3SnapshotSave(cx, fpath); err != nil {
  251. cx.t.Fatalf("snapshotTest ctlV3SnapshotSave error (%v)", err)
  252. }
  253. st, err := getSnapshotStatus(cx, fpath)
  254. if err != nil {
  255. cx.t.Fatalf("snapshotTest getSnapshotStatus error (%v)", err)
  256. }
  257. if st.Revision != 4 {
  258. cx.t.Fatalf("expected 4, got %d", st.Revision)
  259. }
  260. if st.TotalKey < 3 {
  261. cx.t.Fatalf("expected at least 3, got %d", st.TotalKey)
  262. }
  263. }