agent/consul/fsm,state: snapshot/restore for CA roots
This commit is contained in:
parent
17d6b437d2
commit
2dfca5dbc2
|
@ -21,6 +21,7 @@ func init() {
|
||||||
registerRestorer(structs.PreparedQueryRequestType, restorePreparedQuery)
|
registerRestorer(structs.PreparedQueryRequestType, restorePreparedQuery)
|
||||||
registerRestorer(structs.AutopilotRequestType, restoreAutopilot)
|
registerRestorer(structs.AutopilotRequestType, restoreAutopilot)
|
||||||
registerRestorer(structs.IntentionRequestType, restoreIntention)
|
registerRestorer(structs.IntentionRequestType, restoreIntention)
|
||||||
|
registerRestorer(structs.ConnectCARequestType, restoreConnectCA)
|
||||||
}
|
}
|
||||||
|
|
||||||
func persistOSS(s *snapshot, sink raft.SnapshotSink, encoder *codec.Encoder) error {
|
func persistOSS(s *snapshot, sink raft.SnapshotSink, encoder *codec.Encoder) error {
|
||||||
|
@ -48,6 +49,9 @@ func persistOSS(s *snapshot, sink raft.SnapshotSink, encoder *codec.Encoder) err
|
||||||
if err := s.persistIntentions(sink, encoder); err != nil {
|
if err := s.persistIntentions(sink, encoder); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := s.persistConnectCA(sink, encoder); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,6 +266,24 @@ func (s *snapshot) persistAutopilot(sink raft.SnapshotSink,
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *snapshot) persistConnectCA(sink raft.SnapshotSink,
|
||||||
|
encoder *codec.Encoder) error {
|
||||||
|
roots, err := s.state.CARoots()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, r := range roots {
|
||||||
|
if _, err := sink.Write([]byte{byte(structs.ConnectCARequestType)}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := encoder.Encode(r); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *snapshot) persistIntentions(sink raft.SnapshotSink,
|
func (s *snapshot) persistIntentions(sink raft.SnapshotSink,
|
||||||
encoder *codec.Encoder) error {
|
encoder *codec.Encoder) error {
|
||||||
ixns, err := s.state.Intentions()
|
ixns, err := s.state.Intentions()
|
||||||
|
@ -397,3 +419,14 @@ func restoreIntention(header *snapshotHeader, restore *state.Restore, decoder *c
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func restoreConnectCA(header *snapshotHeader, restore *state.Restore, decoder *codec.Decoder) error {
|
||||||
|
var req structs.CARoot
|
||||||
|
if err := decoder.Decode(&req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := restore.CARoot(&req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/hashicorp/consul/agent/connect"
|
||||||
"github.com/hashicorp/consul/agent/consul/autopilot"
|
"github.com/hashicorp/consul/agent/consul/autopilot"
|
||||||
"github.com/hashicorp/consul/agent/consul/state"
|
"github.com/hashicorp/consul/agent/consul/state"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
@ -110,6 +111,18 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
|
||||||
}
|
}
|
||||||
assert.Nil(fsm.state.IntentionSet(14, ixn))
|
assert.Nil(fsm.state.IntentionSet(14, ixn))
|
||||||
|
|
||||||
|
// CA Roots
|
||||||
|
roots := []*structs.CARoot{
|
||||||
|
connect.TestCA(t, nil),
|
||||||
|
connect.TestCA(t, nil),
|
||||||
|
}
|
||||||
|
for _, r := range roots[1:] {
|
||||||
|
r.Active = false
|
||||||
|
}
|
||||||
|
ok, err := fsm.state.CARootSetCAS(15, 0, roots)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.True(ok)
|
||||||
|
|
||||||
// Snapshot
|
// Snapshot
|
||||||
snap, err := fsm.Snapshot()
|
snap, err := fsm.Snapshot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -278,6 +291,11 @@ func TestFSM_SnapshotRestore_OSS(t *testing.T) {
|
||||||
assert.Len(ixns, 1)
|
assert.Len(ixns, 1)
|
||||||
assert.Equal(ixn, ixns[0])
|
assert.Equal(ixn, ixns[0])
|
||||||
|
|
||||||
|
// Verify CA roots are restored.
|
||||||
|
_, roots, err = fsm2.state.CARoots(nil)
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.Len(roots, 2)
|
||||||
|
|
||||||
// Snapshot
|
// Snapshot
|
||||||
snap, err = fsm2.Snapshot()
|
snap, err = fsm2.Snapshot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -33,6 +33,34 @@ func init() {
|
||||||
registerSchema(caRootTableSchema)
|
registerSchema(caRootTableSchema)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CARoots is used to pull all the CA roots for the snapshot.
|
||||||
|
func (s *Snapshot) CARoots() (structs.CARoots, error) {
|
||||||
|
ixns, err := s.tx.Get(caRootTableName, "id")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret structs.CARoots
|
||||||
|
for wrapped := ixns.Next(); wrapped != nil; wrapped = ixns.Next() {
|
||||||
|
ret = append(ret, wrapped.(*structs.CARoot))
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CARoots is used when restoring from a snapshot.
|
||||||
|
func (s *Restore) CARoot(r *structs.CARoot) error {
|
||||||
|
// Insert
|
||||||
|
if err := s.tx.Insert(caRootTableName, r); err != nil {
|
||||||
|
return fmt.Errorf("failed restoring CA root: %s", err)
|
||||||
|
}
|
||||||
|
if err := indexUpdateMaxTxn(s.tx, r.ModifyIndex, caRootTableName); err != nil {
|
||||||
|
return fmt.Errorf("failed updating index: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CARoots returns the list of all CA roots.
|
// CARoots returns the list of all CA roots.
|
||||||
func (s *Store) CARoots(ws memdb.WatchSet) (uint64, structs.CARoots, error) {
|
func (s *Store) CARoots(ws memdb.WatchSet) (uint64, structs.CARoots, error) {
|
||||||
tx := s.db.Txn(false)
|
tx := s.db.Txn(false)
|
||||||
|
|
|
@ -113,92 +113,60 @@ func TestStore_CARootActive_none(t *testing.T) {
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
func TestStore_CARoot_Snapshot_Restore(t *testing.T) {
|
||||||
func TestStore_Intention_Snapshot_Restore(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
s := testStateStore(t)
|
s := testStateStore(t)
|
||||||
|
|
||||||
// Create some intentions.
|
// Create some intentions.
|
||||||
ixns := structs.Intentions{
|
roots := structs.CARoots{
|
||||||
&structs.Intention{
|
connect.TestCA(t, nil),
|
||||||
DestinationName: "foo",
|
connect.TestCA(t, nil),
|
||||||
},
|
connect.TestCA(t, nil),
|
||||||
&structs.Intention{
|
}
|
||||||
DestinationName: "bar",
|
for _, r := range roots[1:] {
|
||||||
},
|
r.Active = false
|
||||||
&structs.Intention{
|
|
||||||
DestinationName: "baz",
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force the sort order of the UUIDs before we create them so the
|
// Force the sort order of the UUIDs before we create them so the
|
||||||
// order is deterministic.
|
// order is deterministic.
|
||||||
id := testUUID()
|
id := testUUID()
|
||||||
ixns[0].ID = "a" + id[1:]
|
roots[0].ID = "a" + id[1:]
|
||||||
ixns[1].ID = "b" + id[1:]
|
roots[1].ID = "b" + id[1:]
|
||||||
ixns[2].ID = "c" + id[1:]
|
roots[2].ID = "c" + id[1:]
|
||||||
|
|
||||||
// Now create
|
// Now create
|
||||||
for i, ixn := range ixns {
|
ok, err := s.CARootSetCAS(1, 0, roots)
|
||||||
assert.Nil(s.IntentionSet(uint64(4+i), ixn))
|
assert.Nil(err)
|
||||||
}
|
assert.True(ok)
|
||||||
|
|
||||||
// Snapshot the queries.
|
// Snapshot the queries.
|
||||||
snap := s.Snapshot()
|
snap := s.Snapshot()
|
||||||
defer snap.Close()
|
defer snap.Close()
|
||||||
|
|
||||||
// Alter the real state store.
|
// Alter the real state store.
|
||||||
assert.Nil(s.IntentionDelete(7, ixns[0].ID))
|
ok, err = s.CARootSetCAS(2, 1, roots[:1])
|
||||||
|
assert.Nil(err)
|
||||||
|
assert.True(ok)
|
||||||
|
|
||||||
// Verify the snapshot.
|
// Verify the snapshot.
|
||||||
assert.Equal(snap.LastIndex(), uint64(6))
|
assert.Equal(snap.LastIndex(), uint64(1))
|
||||||
expected := structs.Intentions{
|
dump, err := snap.CARoots()
|
||||||
&structs.Intention{
|
|
||||||
ID: ixns[0].ID,
|
|
||||||
DestinationName: "foo",
|
|
||||||
Meta: map[string]string{},
|
|
||||||
RaftIndex: structs.RaftIndex{
|
|
||||||
CreateIndex: 4,
|
|
||||||
ModifyIndex: 4,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&structs.Intention{
|
|
||||||
ID: ixns[1].ID,
|
|
||||||
DestinationName: "bar",
|
|
||||||
Meta: map[string]string{},
|
|
||||||
RaftIndex: structs.RaftIndex{
|
|
||||||
CreateIndex: 5,
|
|
||||||
ModifyIndex: 5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&structs.Intention{
|
|
||||||
ID: ixns[2].ID,
|
|
||||||
DestinationName: "baz",
|
|
||||||
Meta: map[string]string{},
|
|
||||||
RaftIndex: structs.RaftIndex{
|
|
||||||
CreateIndex: 6,
|
|
||||||
ModifyIndex: 6,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
dump, err := snap.Intentions()
|
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(expected, dump)
|
assert.Equal(roots, dump)
|
||||||
|
|
||||||
// Restore the values into a new state store.
|
// Restore the values into a new state store.
|
||||||
func() {
|
func() {
|
||||||
s := testStateStore(t)
|
s := testStateStore(t)
|
||||||
restore := s.Restore()
|
restore := s.Restore()
|
||||||
for _, ixn := range dump {
|
for _, r := range dump {
|
||||||
assert.Nil(restore.Intention(ixn))
|
assert.Nil(restore.CARoot(r))
|
||||||
}
|
}
|
||||||
restore.Commit()
|
restore.Commit()
|
||||||
|
|
||||||
// Read the restored values back out and verify that they match.
|
// Read the restored values back out and verify that they match.
|
||||||
idx, actual, err := s.Intentions(nil)
|
idx, actual, err := s.CARoots(nil)
|
||||||
assert.Nil(err)
|
assert.Nil(err)
|
||||||
assert.Equal(idx, uint64(6))
|
assert.Equal(idx, uint64(2))
|
||||||
assert.Equal(expected, actual)
|
assert.Equal(roots, actual)
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
Loading…
Reference in New Issue