fsm: add Intention operations to transactions for internal use
This commit is contained in:
parent
405db688f8
commit
6f40708aca
|
@ -110,6 +110,18 @@ func (s *Store) txnKVS(tx *memdb.Txn, idx uint64, op *structs.TxnKVOp) (structs.
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// txnIntention handles all Intention-related operations.
|
||||
func (s *Store) txnIntention(tx *memdb.Txn, idx uint64, op *structs.TxnIntentionOp) error {
|
||||
switch op.Op {
|
||||
case structs.IntentionOpCreate, structs.IntentionOpUpdate:
|
||||
return s.intentionSetTxn(tx, idx, op.Intention)
|
||||
case structs.IntentionOpDelete:
|
||||
return s.intentionDeleteTxn(tx, idx, op.Intention.ID)
|
||||
default:
|
||||
return fmt.Errorf("unknown Intention verb %q", op.Op)
|
||||
}
|
||||
}
|
||||
|
||||
// txnDispatch runs the given operations inside the state store transaction.
|
||||
func (s *Store) txnDispatch(tx *memdb.Txn, idx uint64, ops structs.TxnOps) (structs.TxnResults, structs.TxnErrors) {
|
||||
results := make(structs.TxnResults, 0, len(ops))
|
||||
|
@ -119,9 +131,12 @@ func (s *Store) txnDispatch(tx *memdb.Txn, idx uint64, ops structs.TxnOps) (stru
|
|||
var err error
|
||||
|
||||
// Dispatch based on the type of operation.
|
||||
if op.KV != nil {
|
||||
switch {
|
||||
case op.KV != nil:
|
||||
ret, err = s.txnKVS(tx, idx, op.KV)
|
||||
} else {
|
||||
case op.Intention != nil:
|
||||
err = s.txnIntention(tx, idx, op.Intention)
|
||||
default:
|
||||
err = fmt.Errorf("no operation specified")
|
||||
}
|
||||
|
||||
|
|
|
@ -7,8 +7,115 @@ import (
|
|||
|
||||
"github.com/hashicorp/consul/agent/structs"
|
||||
"github.com/hashicorp/consul/api"
|
||||
"github.com/pascaldekloe/goe/verify"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStateStore_Txn_Intention(t *testing.T) {
|
||||
require := require.New(t)
|
||||
s := testStateStore(t)
|
||||
|
||||
// Create some intentions.
|
||||
ixn1 := &structs.Intention{
|
||||
ID: testUUID(),
|
||||
SourceNS: "default",
|
||||
SourceName: "web",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "db",
|
||||
Meta: map[string]string{},
|
||||
}
|
||||
ixn2 := &structs.Intention{
|
||||
ID: testUUID(),
|
||||
SourceNS: "default",
|
||||
SourceName: "db",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "*",
|
||||
Action: structs.IntentionActionDeny,
|
||||
Meta: map[string]string{},
|
||||
}
|
||||
ixn3 := &structs.Intention{
|
||||
ID: testUUID(),
|
||||
SourceNS: "default",
|
||||
SourceName: "foo",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "*",
|
||||
Meta: map[string]string{},
|
||||
}
|
||||
|
||||
// Write the first two to the state store, leave the third
|
||||
// to be created by the transaction operation.
|
||||
require.NoError(s.IntentionSet(1, ixn1))
|
||||
require.NoError(s.IntentionSet(2, ixn2))
|
||||
|
||||
// Set up a transaction that hits every operation.
|
||||
ops := structs.TxnOps{
|
||||
&structs.TxnOp{
|
||||
Intention: &structs.TxnIntentionOp{
|
||||
Op: structs.IntentionOpUpdate,
|
||||
Intention: ixn1,
|
||||
},
|
||||
},
|
||||
&structs.TxnOp{
|
||||
Intention: &structs.TxnIntentionOp{
|
||||
Op: structs.IntentionOpDelete,
|
||||
Intention: ixn2,
|
||||
},
|
||||
},
|
||||
&structs.TxnOp{
|
||||
Intention: &structs.TxnIntentionOp{
|
||||
Op: structs.IntentionOpCreate,
|
||||
Intention: ixn3,
|
||||
},
|
||||
},
|
||||
}
|
||||
results, errors := s.TxnRW(3, ops)
|
||||
if len(errors) > 0 {
|
||||
t.Fatalf("err: %v", errors)
|
||||
}
|
||||
|
||||
// Make sure the response looks as expected.
|
||||
expected := structs.TxnResults{}
|
||||
verify.Values(t, "", results, expected)
|
||||
|
||||
// Pull the resulting state store contents.
|
||||
idx, actual, err := s.Intentions(nil)
|
||||
require.NoError(err)
|
||||
if idx != 3 {
|
||||
t.Fatalf("bad index: %d", idx)
|
||||
}
|
||||
|
||||
// Make sure it looks as expected.
|
||||
intentions := structs.Intentions{
|
||||
&structs.Intention{
|
||||
ID: ixn1.ID,
|
||||
SourceNS: "default",
|
||||
SourceName: "web",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "db",
|
||||
Meta: map[string]string{},
|
||||
Precedence: 9,
|
||||
RaftIndex: structs.RaftIndex{
|
||||
CreateIndex: 1,
|
||||
ModifyIndex: 3,
|
||||
},
|
||||
},
|
||||
&structs.Intention{
|
||||
ID: ixn3.ID,
|
||||
SourceNS: "default",
|
||||
SourceName: "foo",
|
||||
DestinationNS: "default",
|
||||
DestinationName: "*",
|
||||
Meta: map[string]string{},
|
||||
Precedence: 6,
|
||||
RaftIndex: structs.RaftIndex{
|
||||
CreateIndex: 3,
|
||||
ModifyIndex: 3,
|
||||
},
|
||||
},
|
||||
}
|
||||
verify.Values(t, "", actual, intentions)
|
||||
}
|
||||
|
||||
func TestStateStore_Txn_KVS(t *testing.T) {
|
||||
s := testStateStore(t)
|
||||
|
||||
|
|
|
@ -224,6 +224,19 @@ func (x *Intention) String() string {
|
|||
x.ID, x.Precedence)
|
||||
}
|
||||
|
||||
// EstimateSize returns an estimate (in bytes) of the size of this structure when encoded.
|
||||
func (x *Intention) EstimateSize() int {
|
||||
// 60 = 36 (uuid) + 16 (RaftIndex) + 4 (Precedence) + 4 (DefaultPort)
|
||||
size := 60 + len(x.Description) + len(x.SourceNS) + len(x.SourceName) + len(x.DestinationNS) +
|
||||
len(x.DestinationName) + len(x.SourceType) + len(x.Action) + len(x.DefaultAddr)
|
||||
|
||||
for k, v := range x.Meta {
|
||||
size += len(k) + len(v)
|
||||
}
|
||||
|
||||
return size
|
||||
}
|
||||
|
||||
// IntentionAction is the action that the intention represents. This
|
||||
// can be "allow" or "deny" to whitelist or blacklist intentions.
|
||||
type IntentionAction string
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
multierror "github.com/hashicorp/go-multierror"
|
||||
)
|
||||
|
||||
// TxnKVOp is used to define a single operation on the KVS inside a
|
||||
|
@ -17,10 +19,15 @@ type TxnKVOp struct {
|
|||
// inside a transaction.
|
||||
type TxnKVResult *DirEntry
|
||||
|
||||
// TxnKVOp is used to define a single operation on an Intention inside a
|
||||
// transaction.
|
||||
type TxnIntentionOp IntentionRequest
|
||||
|
||||
// TxnOp is used to define a single operation inside a transaction. Only one
|
||||
// of the types should be filled out per entry.
|
||||
type TxnOp struct {
|
||||
KV *TxnKVOp
|
||||
KV *TxnKVOp
|
||||
Intention *TxnIntentionOp
|
||||
}
|
||||
|
||||
// TxnOps is a list of operations within a transaction.
|
||||
|
@ -80,6 +87,15 @@ type TxnResponse struct {
|
|||
Errors TxnErrors
|
||||
}
|
||||
|
||||
// Error returns an aggregate of all errors in this TxnResponse.
|
||||
func (r TxnResponse) Error() error {
|
||||
var errs error
|
||||
for _, err := range r.Errors {
|
||||
errs = multierror.Append(errs, errors.New(err.Error()))
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// TxnReadResponse is the structure returned by a TxnReadRequest.
|
||||
type TxnReadResponse struct {
|
||||
TxnResponse
|
||||
|
|
Loading…
Reference in New Issue