func TestClient_SnapshotRPC_TLS(t *testing.T) { dir1, conf1 := testServerConfig(t, "a.testco.internal") conf1.VerifyIncoming = true conf1.VerifyOutgoing = true configureTLS(conf1) s1, err := NewServer(conf1) if err != nil { t.Fatalf("err: %v", err) } defer os.RemoveAll(dir1) defer s1.Shutdown() dir2, conf2 := testClientConfig(t, "b.testco.internal") conf2.VerifyOutgoing = true configureTLS(conf2) c1, err := NewClient(conf2) if err != nil { t.Fatalf("err: %v", err) } defer os.RemoveAll(dir2) defer c1.Shutdown() // Wait for the leader testutil.WaitForLeader(t, s1.RPC, "dc1") // Try to join. addr := fmt.Sprintf("127.0.0.1:%d", s1.config.SerfLANConfig.MemberlistConfig.BindPort) if _, err := c1.JoinLAN([]string{addr}); err != nil { t.Fatalf("err: %v", err) } if len(s1.LANMembers()) != 2 || len(c1.LANMembers()) != 2 { t.Fatalf("Server has %v of %v expected members; Client has %v of %v expected members.", len(s1.LANMembers()), 2, len(c1.LANMembers()), 2) } // Wait until we've got a healthy server. testutil.WaitForResult(func() (bool, error) { return c1.servers.NumServers() == 1, nil }, func(err error) { t.Fatalf("expected consul server") }) // Take a snapshot. var snap bytes.Buffer args := structs.SnapshotRequest{ Datacenter: "dc1", Op: structs.SnapshotSave, } if err := c1.SnapshotRPC(&args, bytes.NewReader([]byte("")), &snap, nil); err != nil { t.Fatalf("err: %v", err) } // Restore a snapshot. args.Op = structs.SnapshotRestore if err := c1.SnapshotRPC(&args, &snap, nil, nil); err != nil { t.Fatalf("err: %v", err) } }
// Snapshot handles requests to take and restore snapshots. This uses a special // mechanism to make the RPC since we potentially stream large amounts of data // as part of these requests. func (s *HTTPServer) Snapshot(resp http.ResponseWriter, req *http.Request) (interface{}, error) { var args structs.SnapshotRequest s.parseDC(req, &args.Datacenter) s.parseToken(req, &args.Token) if _, ok := req.URL.Query()["stale"]; ok { args.AllowStale = true } switch req.Method { case "GET": args.Op = structs.SnapshotSave // Headers need to go out before we stream the body. replyFn := func(reply *structs.SnapshotResponse) error { setMeta(resp, &reply.QueryMeta) return nil } // Don't bother sending any request body through since it will // be ignored. var null bytes.Buffer if err := s.agent.SnapshotRPC(&args, &null, resp, replyFn); err != nil { return nil, err } return nil, nil case "PUT": args.Op = structs.SnapshotRestore if err := s.agent.SnapshotRPC(&args, req.Body, resp, nil); err != nil { return nil, err } return nil, nil default: resp.WriteHeader(http.StatusMethodNotAllowed) return nil, nil } }
func TestSnapshot_LeaderState(t *testing.T) { dir1, s1 := testServer(t) defer os.RemoveAll(dir1) defer s1.Shutdown() testutil.WaitForLeader(t, s1.RPC, "dc1") codec := rpcClient(t, s1) defer codec.Close() // Make a before session. var before string { args := structs.SessionRequest{ Datacenter: s1.config.Datacenter, Op: structs.SessionCreate, Session: structs.Session{ Node: s1.config.NodeName, TTL: "60s", }, } if err := msgpackrpc.CallWithCodec(codec, "Session.Apply", &args, &before); err != nil { t.Fatalf("err: %v", err) } } // Take a snapshot. args := structs.SnapshotRequest{ Datacenter: s1.config.Datacenter, Op: structs.SnapshotSave, } var reply structs.SnapshotResponse snap, err := SnapshotRPC(s1.connPool, s1.config.Datacenter, s1.config.RPCAddr, &args, bytes.NewReader([]byte("")), &reply) if err != nil { t.Fatalf("err: %v", err) } defer snap.Close() // Make an after session. var after string { args := structs.SessionRequest{ Datacenter: s1.config.Datacenter, Op: structs.SessionCreate, Session: structs.Session{ Node: s1.config.NodeName, TTL: "60s", }, } if err := msgpackrpc.CallWithCodec(codec, "Session.Apply", &args, &after); err != nil { t.Fatalf("err: %v", err) } } // Make sure the leader has timers setup. if _, ok := s1.sessionTimers[before]; !ok { t.Fatalf("missing session timer") } if _, ok := s1.sessionTimers[after]; !ok { t.Fatalf("missing session timer") } // Restore the snapshot. args.Op = structs.SnapshotRestore restore, err := SnapshotRPC(s1.connPool, s1.config.Datacenter, s1.config.RPCAddr, &args, snap, &reply) if err != nil { t.Fatalf("err: %v", err) } defer restore.Close() // Make sure the before time is still there, and that the after timer // got reverted. This proves we fully cycled the leader state. if _, ok := s1.sessionTimers[before]; !ok { t.Fatalf("missing session timer") } if _, ok := s1.sessionTimers[after]; ok { t.Fatalf("unexpected session timer") } }
// verifySnapshot is a helper that does a snapshot and restore. func verifySnapshot(t *testing.T, s *Server, dc, token string) { codec := rpcClient(t, s) defer codec.Close() // Set a key to a before value. { args := structs.KVSRequest{ Datacenter: dc, Op: structs.KVSSet, DirEnt: structs.DirEntry{ Key: "test", Value: []byte("hello"), }, WriteRequest: structs.WriteRequest{ Token: token, }, } var out bool if err := msgpackrpc.CallWithCodec(codec, "KVS.Apply", &args, &out); err != nil { t.Fatalf("err: %v", err) } } // Take a snapshot. args := structs.SnapshotRequest{ Datacenter: dc, Token: token, Op: structs.SnapshotSave, } var reply structs.SnapshotResponse snap, err := SnapshotRPC(s.connPool, s.config.Datacenter, s.config.RPCAddr, &args, bytes.NewReader([]byte("")), &reply) if err != nil { t.Fatalf("err: %v", err) } defer snap.Close() // Read back the before value. { getR := structs.KeyRequest{ Datacenter: dc, Key: "test", QueryOptions: structs.QueryOptions{ Token: token, }, } var dirent structs.IndexedDirEntries if err := msgpackrpc.CallWithCodec(codec, "KVS.Get", &getR, &dirent); err != nil { t.Fatalf("err: %v", err) } if len(dirent.Entries) != 1 { t.Fatalf("Bad: %v", dirent) } d := dirent.Entries[0] if string(d.Value) != "hello" { t.Fatalf("bad: %v", d) } } // Set a key to an after value. { args := structs.KVSRequest{ Datacenter: dc, Op: structs.KVSSet, DirEnt: structs.DirEntry{ Key: "test", Value: []byte("goodbye"), }, WriteRequest: structs.WriteRequest{ Token: token, }, } var out bool if err := msgpackrpc.CallWithCodec(codec, "KVS.Apply", &args, &out); err != nil { t.Fatalf("err: %v", err) } } // Read back the before value. { getR := structs.KeyRequest{ Datacenter: dc, Key: "test", QueryOptions: structs.QueryOptions{ Token: token, }, } var dirent structs.IndexedDirEntries if err := msgpackrpc.CallWithCodec(codec, "KVS.Get", &getR, &dirent); err != nil { t.Fatalf("err: %v", err) } if len(dirent.Entries) != 1 { t.Fatalf("Bad: %v", dirent) } d := dirent.Entries[0] if string(d.Value) != "goodbye" { t.Fatalf("bad: %v", d) } } // Restore the snapshot. args.Op = structs.SnapshotRestore restore, err := SnapshotRPC(s.connPool, s.config.Datacenter, s.config.RPCAddr, &args, snap, &reply) if err != nil { t.Fatalf("err: %v", err) } defer restore.Close() // Read back the before value post-snapshot. { getR := structs.KeyRequest{ Datacenter: dc, Key: "test", QueryOptions: structs.QueryOptions{ Token: token, }, } var dirent structs.IndexedDirEntries if err := msgpackrpc.CallWithCodec(codec, "KVS.Get", &getR, &dirent); err != nil { t.Fatalf("err: %v", err) } if len(dirent.Entries) != 1 { t.Fatalf("Bad: %v", dirent) } d := dirent.Entries[0] if string(d.Value) != "hello" { t.Fatalf("bad: %v", d) } } }