Enable snapshots in dev mode (#2453)

This commit is contained in:
Kyle Havlovitz 2016-10-31 14:39:47 -04:00 committed by GitHub
parent dce6702268
commit 3be132863f
6 changed files with 97 additions and 43 deletions

View File

@ -386,7 +386,7 @@ func (s *Server) setupRaft() error {
s.raftInmem = store
stable = store
log = store
snap = raft.NewDiscardSnapshotStore()
snap = raft.NewInmemSnapshotStore()
} else {
// Create the base raft path.
path := filepath.Join(s.config.DataDir, raftState)

View File

@ -20,12 +20,6 @@ import (
"github.com/hashicorp/go-msgpack/codec"
)
const (
// noSnapshotsInDevMode indicates that snapshots aren't available for a
// server in dev mode.
noSnapshotsInDevMode = "Snapshot operations not available in dev mode"
)
// dispatchSnapshotRequest takes an incoming request structure with possibly some
// streaming data (for a restore) and returns possibly some streaming data (for
// a snapshot save). We can't use the normal RPC mechanism in a streaming manner
@ -52,13 +46,6 @@ func (s *Server) dispatchSnapshotRequest(args *structs.SnapshotRequest, in io.Re
}
}
// Snapshots don't work in dev mode because we need Raft's snapshots to
// be readable. Better to present a clear error than one from deep down
// in the Raft snapshot store.
if s.config.DevMode {
return nil, errors.New(noSnapshotsInDevMode)
}
// Verify token is allowed to operate on snapshots. There's only a
// single ACL sense here (not read and write) since reading gets you
// all the ACLs and you could escalate from there.

View File

@ -391,26 +391,3 @@ func TestSnapshot_AllowStale(t *testing.T) {
}
}
}
func TestSnapshot_DevMode(t *testing.T) {
dir1, s1 := testServerWithConfig(t, func(c *Config) {
c.DevMode = true
})
defer os.RemoveAll(dir1)
defer s1.Shutdown()
codec := rpcClient(t, s1)
defer codec.Close()
testutil.WaitForLeader(t, s1.RPC, "dc1")
args := structs.SnapshotRequest{
Datacenter: s1.config.Datacenter,
Op: structs.SnapshotSave,
}
var reply structs.SnapshotResponse
_, err := SnapshotRPC(s1.connPool, s1.config.Datacenter, s1.config.RPCAddr,
&args, bytes.NewReader([]byte("")), &reply)
if err == nil || !strings.Contains(err.Error(), noSnapshotsInDevMode) {
t.Fatalf("err: %v", err)
}
}

93
vendor/github.com/hashicorp/raft/inmem_snapshot.go generated vendored Normal file
View File

@ -0,0 +1,93 @@
package raft
import (
"bytes"
"fmt"
"io"
"io/ioutil"
)
// InmemSnapshotStore implements the SnapshotStore interface and
// retains only the most recent snapshot
type InmemSnapshotStore struct {
latest *InmemSnapshotSink
hasSnapshot bool
}
// InmemSnapshotSink implements SnapshotSink in memory
type InmemSnapshotSink struct {
meta SnapshotMeta
contents *bytes.Buffer
}
// NewInmemSnapshotStore creates a blank new InmemSnapshotStore
func NewInmemSnapshotStore() *InmemSnapshotStore {
return &InmemSnapshotStore{
latest: &InmemSnapshotSink{
contents: &bytes.Buffer{},
},
}
}
// Create replaces the stored snapshot with a new one using the given args
func (m *InmemSnapshotStore) Create(version SnapshotVersion, index, term uint64,
configuration Configuration, configurationIndex uint64, trans Transport) (SnapshotSink, error) {
// We only support version 1 snapshots at this time.
if version != 1 {
return nil, fmt.Errorf("unsupported snapshot version %d", version)
}
name := snapshotName(term, index)
sink := m.latest
sink.meta = SnapshotMeta{
Version: version,
ID: name,
Index: index,
Term: term,
Peers: encodePeers(configuration, trans),
Configuration: configuration,
ConfigurationIndex: configurationIndex,
}
sink.contents = &bytes.Buffer{}
m.hasSnapshot = true
return sink, nil
}
// List returns the latest snapshot taken
func (m *InmemSnapshotStore) List() ([]*SnapshotMeta, error) {
if !m.hasSnapshot {
return []*SnapshotMeta{}, nil
}
return []*SnapshotMeta{&m.latest.meta}, nil
}
// Open wraps an io.ReadCloser around the snapshot contents
func (m *InmemSnapshotStore) Open(id string) (*SnapshotMeta, io.ReadCloser, error) {
if m.latest.meta.ID != id {
return nil, nil, fmt.Errorf("[ERR] snapshot: failed to open snapshot id: %s", id)
}
return &m.latest.meta, ioutil.NopCloser(m.latest.contents), nil
}
// Write appends the given bytes to the snapshot contents
func (s *InmemSnapshotSink) Write(p []byte) (n int, err error) {
written, err := io.Copy(s.contents, bytes.NewReader(p))
s.meta.Size += written
return int(written), err
}
// Close updates the Size and is otherwise a no-op
func (s *InmemSnapshotSink) Close() error {
return nil
}
func (s *InmemSnapshotSink) ID() string {
return s.meta.ID
}
func (s *InmemSnapshotSink) Cancel() error {
return nil
}

6
vendor/vendor.json vendored
View File

@ -350,10 +350,10 @@
"revisionTime": "2015-11-16T02:03:38Z"
},
{
"checksumSHA1": "tHzyGCXkf8PnmBrTk1Z01JIv/5Q=",
"checksumSHA1": "ed1YY/S0BSb57IRRSDUbFp7r0IE=",
"path": "github.com/hashicorp/raft",
"revision": "e1d3debe52b9152e8db5c3a77b7f7cf9b2a8b404",
"revisionTime": "2016-10-26T00:17:15Z"
"revision": "def7451ceceb8a919cbb1f6d1c0f7648e9311879",
"revisionTime": "2016-10-31T16:57:40Z"
},
{
"checksumSHA1": "QAxukkv54/iIvLfsUP6IK4R0m/A=",

View File

@ -34,9 +34,6 @@ The endpoints support the use of ACL Tokens. Because snapshots contain all
server state, including ACLs, a management token is required to perform snapshot
operations is ACLs are enabled.
-> Snapshot operations are not available for servers running in
[dev mode](/docs/agent/options.html#_dev).
### <a name="snapshot"></a> /v1/snapshot
The snapshot endpoint supports the `GET` and `PUT` methods.