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
|
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.
|
// 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) {
|
func (s *Store) txnDispatch(tx *memdb.Txn, idx uint64, ops structs.TxnOps) (structs.TxnResults, structs.TxnErrors) {
|
||||||
results := make(structs.TxnResults, 0, len(ops))
|
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
|
var err error
|
||||||
|
|
||||||
// Dispatch based on the type of operation.
|
// Dispatch based on the type of operation.
|
||||||
if op.KV != nil {
|
switch {
|
||||||
|
case op.KV != nil:
|
||||||
ret, err = s.txnKVS(tx, idx, op.KV)
|
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")
|
err = fmt.Errorf("no operation specified")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,115 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
"github.com/hashicorp/consul/api"
|
"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) {
|
func TestStateStore_Txn_KVS(t *testing.T) {
|
||||||
s := testStateStore(t)
|
s := testStateStore(t)
|
||||||
|
|
||||||
|
|
|
@ -224,6 +224,19 @@ func (x *Intention) String() string {
|
||||||
x.ID, x.Precedence)
|
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
|
// IntentionAction is the action that the intention represents. This
|
||||||
// can be "allow" or "deny" to whitelist or blacklist intentions.
|
// can be "allow" or "deny" to whitelist or blacklist intentions.
|
||||||
type IntentionAction string
|
type IntentionAction string
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
package structs
|
package structs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/hashicorp/consul/api"
|
"github.com/hashicorp/consul/api"
|
||||||
|
multierror "github.com/hashicorp/go-multierror"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TxnKVOp is used to define a single operation on the KVS inside a
|
// TxnKVOp is used to define a single operation on the KVS inside a
|
||||||
|
@ -17,10 +19,15 @@ type TxnKVOp struct {
|
||||||
// inside a transaction.
|
// inside a transaction.
|
||||||
type TxnKVResult *DirEntry
|
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
|
// TxnOp is used to define a single operation inside a transaction. Only one
|
||||||
// of the types should be filled out per entry.
|
// of the types should be filled out per entry.
|
||||||
type TxnOp struct {
|
type TxnOp struct {
|
||||||
KV *TxnKVOp
|
KV *TxnKVOp
|
||||||
|
Intention *TxnIntentionOp
|
||||||
}
|
}
|
||||||
|
|
||||||
// TxnOps is a list of operations within a transaction.
|
// TxnOps is a list of operations within a transaction.
|
||||||
|
@ -80,6 +87,15 @@ type TxnResponse struct {
|
||||||
Errors TxnErrors
|
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.
|
// TxnReadResponse is the structure returned by a TxnReadRequest.
|
||||||
type TxnReadResponse struct {
|
type TxnReadResponse struct {
|
||||||
TxnResponse
|
TxnResponse
|
||||||
|
|
Loading…
Reference in New Issue