Skip to content

Commit 3ca534a

Browse files
authored
Merge pull request #11697 from hashicorp/f-raft-state-err
cli: return error from raft commands if db is open
2 parents 55018bd + fa3de73 commit 3ca534a

File tree

2 files changed

+71
-1
lines changed

2 files changed

+71
-1
lines changed

helper/raftutil/state.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,38 @@ package raftutil
22

33
import (
44
"bytes"
5+
"errors"
56
"fmt"
67
"os"
78
"path/filepath"
9+
"strings"
10+
"time"
811

12+
"github.com/boltdb/bolt"
913
"github.com/hashicorp/go-msgpack/codec"
1014
"github.com/hashicorp/nomad/nomad/structs"
1115
"github.com/hashicorp/raft"
1216
raftboltdb "github.com/hashicorp/raft-boltdb"
1317
)
1418

19+
var (
20+
errAlreadyOpen = errors.New("unable to open raft logs that are in use")
21+
)
22+
1523
// RaftStateInfo returns info about the nomad state, as found in the passed data-dir directory
1624
func RaftStateInfo(p string) (store *raftboltdb.BoltStore, firstIdx uint64, lastIdx uint64, err error) {
17-
s, err := raftboltdb.NewBoltStore(p)
25+
opts := raftboltdb.Options{
26+
Path: p,
27+
BoltOptions: &bolt.Options{
28+
ReadOnly: true,
29+
Timeout: 1 * time.Second,
30+
},
31+
}
32+
s, err := raftboltdb.New(opts)
1833
if err != nil {
34+
if strings.HasSuffix(err.Error(), "timeout") {
35+
return nil, 0, 0, errAlreadyOpen
36+
}
1937
return nil, 0, 0, fmt.Errorf("failed to open raft logs: %v", err)
2038
}
2139

helper/raftutil/state_test.go

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package raftutil
2+
3+
import (
4+
"path/filepath"
5+
"testing"
6+
7+
raftboltdb "github.com/hashicorp/raft-boltdb"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
// TestRaftStateInfo_InUse asserts that commands that inspect raft
13+
// state such as "nomad operator raft info" and "nomad operator raft
14+
// logs" fail with a helpful error message when called on an inuse
15+
// database.
16+
func TestRaftStateInfo_InUse(t *testing.T) {
17+
t.Parallel() // since there's a 1s timeout.
18+
19+
// First create an empty raft db
20+
dir := filepath.Join(t.TempDir(), "raft.db")
21+
22+
fakedb, err := raftboltdb.NewBoltStore(dir)
23+
require.NoError(t, err)
24+
25+
// Next try to read the db without closing it
26+
s, _, _, err := RaftStateInfo(dir)
27+
assert.Nil(t, s)
28+
require.EqualError(t, err, errAlreadyOpen.Error())
29+
30+
// LogEntries should produce the same error
31+
_, _, err = LogEntries(dir)
32+
require.EqualError(t, err, "failed to open raft logs: "+errAlreadyOpen.Error())
33+
34+
// Commands should work once the db is closed
35+
require.NoError(t, fakedb.Close())
36+
37+
s, _, _, err = RaftStateInfo(dir)
38+
assert.NotNil(t, s)
39+
require.NoError(t, err)
40+
require.NoError(t, s.Close())
41+
42+
logCh, errCh, err := LogEntries(dir)
43+
require.NoError(t, err)
44+
45+
// Consume entries to cleanly close db
46+
for closed := false; closed; {
47+
select {
48+
case _, closed = <-logCh:
49+
case <-errCh:
50+
}
51+
}
52+
}

0 commit comments

Comments
 (0)