diff --git a/consul/server.go b/consul/server.go index f4a5c90d93..43e9c582d7 100644 --- a/consul/server.go +++ b/consul/server.go @@ -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) diff --git a/consul/snapshot_endpoint.go b/consul/snapshot_endpoint.go index 1a34d393db..cfe946efa0 100644 --- a/consul/snapshot_endpoint.go +++ b/consul/snapshot_endpoint.go @@ -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. diff --git a/consul/snapshot_endpoint_test.go b/consul/snapshot_endpoint_test.go index 1723b8fb52..b72b715bd8 100644 --- a/consul/snapshot_endpoint_test.go +++ b/consul/snapshot_endpoint_test.go @@ -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) - } -} diff --git a/vendor/github.com/hashicorp/raft/inmem_snapshot.go b/vendor/github.com/hashicorp/raft/inmem_snapshot.go new file mode 100644 index 0000000000..3eb6adb982 --- /dev/null +++ b/vendor/github.com/hashicorp/raft/inmem_snapshot.go @@ -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 +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 1ed2256a38..24ce7b2052 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -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=", diff --git a/website/source/docs/agent/http/snapshot.html.markdown b/website/source/docs/agent/http/snapshot.html.markdown index 25fd2d49b4..c94968cc43 100644 --- a/website/source/docs/agent/http/snapshot.html.markdown +++ b/website/source/docs/agent/http/snapshot.html.markdown @@ -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). - ### /v1/snapshot The snapshot endpoint supports the `GET` and `PUT` methods.