server: remove config entry CAS in legacy intention API bridge code (#9151)

Change so line-item intention edits via the API are handled via the state store instead of via CAS operations.

Fixes #9143
This commit is contained in:
R.B. Boyer 2020-11-13 14:42:21 -06:00 committed by GitHub
parent 6300abed18
commit e323014faf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 1182 additions and 353 deletions

3
.changelog/9151.txt Normal file
View File

@ -0,0 +1,3 @@
```release-note:improvement
server: remove config entry CAS in legacy intention API bridge code
```

View File

@ -19,10 +19,6 @@ type ConfigEntry struct {
// Apply does an upsert of the given config entry.
func (c *ConfigEntry) Apply(args *structs.ConfigEntryRequest, reply *bool) error {
return c.applyInternal(args, reply, nil)
}
func (c *ConfigEntry) applyInternal(args *structs.ConfigEntryRequest, reply *bool, normalizeAndValidateFn func(structs.ConfigEntry) error) error {
if err := c.srv.validateEnterpriseRequest(args.Entry.GetEnterpriseMeta(), true); err != nil {
return err
}
@ -47,18 +43,12 @@ func (c *ConfigEntry) applyInternal(args *structs.ConfigEntryRequest, reply *boo
}
// Normalize and validate the incoming config entry as if it came from a user.
if normalizeAndValidateFn == nil {
if err := args.Entry.Normalize(); err != nil {
return err
}
if err := args.Entry.Validate(); err != nil {
return err
}
} else {
if err := normalizeAndValidateFn(args.Entry); err != nil {
return err
}
}
if authz != nil && !args.Entry.CanWrite(authz) {
return acl.ErrPermissionDenied

View File

@ -291,6 +291,11 @@ func (c *FSM) applyIntentionOperation(buf []byte, index uint64) interface{} {
[]metrics.Label{{Name: "op", Value: string(req.Op)}})
defer metrics.MeasureSinceWithLabels([]string{"fsm", "intention"}, time.Now(),
[]metrics.Label{{Name: "op", Value: string(req.Op)}})
if req.Mutation != nil {
return c.state.IntentionMutation(index, req.Op, req.Mutation)
}
switch req.Op {
case structs.IntentionOpCreate, structs.IntentionOpUpdate:
//nolint:staticcheck

View File

@ -21,22 +21,11 @@ var (
ErrIntentionNotFound = errors.New("Intention not found")
)
// NewIntentionEndpoint returns a new Intention endpoint.
func NewIntentionEndpoint(srv *Server, logger hclog.Logger) *Intention {
return &Intention{
srv: srv,
logger: logger,
configEntryEndpoint: &ConfigEntry{srv},
}
}
// Intention manages the Connect intentions.
type Intention struct {
// srv is a pointer back to the server.
srv *Server
logger hclog.Logger
configEntryEndpoint *ConfigEntry
}
func (s *Intention) checkIntentionID(id string) (bool, error) {
@ -69,10 +58,7 @@ func (s *Intention) legacyUpgradeCheck() error {
}
// Apply creates or updates an intention in the data store.
func (s *Intention) Apply(
args *structs.IntentionRequest,
reply *string) error {
func (s *Intention) Apply(args *structs.IntentionRequest, reply *string) error {
// Ensure that all service-intentions config entry writes go to the primary
// datacenter. These will then be replicated to all the other datacenters.
args.Datacenter = s.srv.config.PrimaryDatacenter
@ -87,6 +73,10 @@ func (s *Intention) Apply(
return err
}
if args.Mutation != nil {
return fmt.Errorf("Mutation field is internal only and must not be set via RPC")
}
// Always set a non-nil intention to avoid nil-access below
if args.Intention == nil {
args.Intention = &structs.Intention{}
@ -105,27 +95,26 @@ func (s *Intention) Apply(
}
var (
configOp structs.ConfigEntryOp
configEntry *structs.ServiceIntentionsConfigEntry
mut *structs.IntentionMutation
legacyWrite bool
)
switch args.Op {
case structs.IntentionOpCreate:
legacyWrite = true
configOp, configEntry, err = s.computeApplyChangesLegacyCreate(accessorID, authz, &entMeta, args)
mut, err = s.computeApplyChangesLegacyCreate(accessorID, authz, &entMeta, args)
case structs.IntentionOpUpdate:
legacyWrite = true
configOp, configEntry, err = s.computeApplyChangesLegacyUpdate(accessorID, authz, &entMeta, args)
mut, err = s.computeApplyChangesLegacyUpdate(accessorID, authz, &entMeta, args)
case structs.IntentionOpUpsert:
legacyWrite = false
configOp, configEntry, err = s.computeApplyChangesUpsert(&entMeta, args)
mut, err = s.computeApplyChangesUpsert(accessorID, authz, &entMeta, args)
case structs.IntentionOpDelete:
if args.Intention.ID == "" {
legacyWrite = false
configOp, configEntry, err = s.computeApplyChangesDelete(&entMeta, args)
mut, err = s.computeApplyChangesDelete(accessorID, authz, &entMeta, args)
} else {
legacyWrite = true
configOp, configEntry, err = s.computeApplyChangesLegacyDelete(accessorID, authz, &entMeta, args)
mut, err = s.computeApplyChangesLegacyDelete(accessorID, authz, &entMeta, args)
}
case structs.IntentionOpDeleteAll:
// This is an internal operation initiated by the leader and is not
@ -145,54 +134,16 @@ func (s *Intention) Apply(
*reply = ""
}
if configOp == "" {
return nil // no-op
}
// Switch to the config entry manipulating flavor:
args.Mutation = mut
args.Intention = nil
// Commit indirectly by invoking the other RPC handler directly.
if configOp == structs.ConfigEntryDelete {
configReq := &structs.ConfigEntryRequest{
Datacenter: args.Datacenter,
WriteRequest: args.WriteRequest,
Op: structs.ConfigEntryDelete,
Entry: configEntry,
}
var ignored struct{}
return s.configEntryEndpoint.Delete(configReq, &ignored)
}
if configOp != structs.ConfigEntryUpsertCAS {
return fmt.Errorf("Invalid Intention config entry operation: %v", configOp)
}
configReq := &structs.ConfigEntryRequest{
Datacenter: args.Datacenter,
WriteRequest: args.WriteRequest,
Op: structs.ConfigEntryUpsertCAS,
Entry: configEntry,
}
var normalizeAndValidateFn func(raw structs.ConfigEntry) error
if legacyWrite {
normalizeAndValidateFn = func(raw structs.ConfigEntry) error {
entry := raw.(*structs.ServiceIntentionsConfigEntry)
if err := entry.LegacyNormalize(); err != nil {
resp, err := s.srv.raftApply(structs.IntentionRequestType, args)
if err != nil {
return err
}
return entry.LegacyValidate()
}
}
var applied bool
if err = s.configEntryEndpoint.applyInternal(configReq, &applied, normalizeAndValidateFn); err != nil {
return err
}
if !applied {
return fmt.Errorf("config entry failed to persist due to CAS failure: kind=%q, name=%q", configEntry.Kind, configEntry.Name)
if respErr, ok := resp.(error); ok {
return respErr
}
return nil
@ -203,14 +154,11 @@ func (s *Intention) computeApplyChangesLegacyCreate(
authz acl.Authorizer,
entMeta *structs.EnterpriseMeta,
args *structs.IntentionRequest,
) (structs.ConfigEntryOp, *structs.ServiceIntentionsConfigEntry, error) {
) (*structs.IntentionMutation, error) {
// This variant is just for legacy UUID-based intentions.
args.Intention.DefaultNamespaces(entMeta)
// Even though the eventual config entry RPC will do an authz check and
// validation, if we do them here too we can generate error messages that
// make more sense for legacy edits.
if !args.Intention.CanWrite(authz) {
sn := args.Intention.SourceServiceName()
dn := args.Intention.DestinationServiceName()
@ -219,7 +167,7 @@ func (s *Intention) computeApplyChangesLegacyCreate(
"source", sn.String(),
"destination", dn.String(),
"accessorID", accessorID)
return "", nil, acl.ErrPermissionDenied
return nil, acl.ErrPermissionDenied
}
// If no ID is provided, generate a new ID. This must be done prior to
@ -227,13 +175,13 @@ func (s *Intention) computeApplyChangesLegacyCreate(
// the entry is in the log, the state update MUST be deterministic or
// the followers will not converge.
if args.Intention.ID != "" {
return "", nil, fmt.Errorf("ID must be empty when creating a new intention")
return nil, fmt.Errorf("ID must be empty when creating a new intention")
}
var err error
args.Intention.ID, err = lib.GenerateUUID(s.checkIntentionID)
if err != nil {
return "", nil, err
return nil, err
}
// Set the created at
args.Intention.CreatedAt = time.Now().UTC()
@ -245,36 +193,30 @@ func (s *Intention) computeApplyChangesLegacyCreate(
}
if err := s.validateEnterpriseIntention(args.Intention); err != nil {
return "", nil, err
return nil, err
}
//nolint:staticcheck
if err := args.Intention.Validate(); err != nil {
return "", nil, err
return nil, err
}
_, configEntry, err := s.srv.fsm.State().ConfigEntry(nil, structs.ServiceIntentions, args.Intention.DestinationName, args.Intention.DestinationEnterpriseMeta())
if err != nil {
return "", nil, fmt.Errorf("service-intentions config entry lookup failed: %v", err)
}
if configEntry == nil {
return structs.ConfigEntryUpsertCAS, args.Intention.ToConfigEntry(true), nil
}
prevEntry := configEntry.(*structs.ServiceIntentionsConfigEntry)
if err := checkLegacyIntentionApplyAllowed(prevEntry); err != nil {
return "", nil, err
}
upsertEntry := prevEntry.Clone()
upsertEntry.Sources = append(upsertEntry.Sources, args.Intention.ToSourceIntention(true))
// NOTE: if the append of this source causes a duplicate source name the
// config entry validation will fail so we don't have to check that
// explicitly here.
return structs.ConfigEntryUpsertCAS, upsertEntry, nil
mut := &structs.IntentionMutation{
Destination: args.Intention.DestinationServiceName(),
Value: args.Intention.ToSourceIntention(true),
}
// Set the created/updated times. If this is an update instead of an insert
// the UpdateOver() will fix it up appropriately.
now := time.Now().UTC()
mut.Value.LegacyCreateTime = timePointer(now)
mut.Value.LegacyUpdateTime = timePointer(now)
return mut, nil
}
func (s *Intention) computeApplyChangesLegacyUpdate(
@ -282,28 +224,21 @@ func (s *Intention) computeApplyChangesLegacyUpdate(
authz acl.Authorizer,
entMeta *structs.EnterpriseMeta,
args *structs.IntentionRequest,
) (structs.ConfigEntryOp, *structs.ServiceIntentionsConfigEntry, error) {
) (*structs.IntentionMutation, error) {
// This variant is just for legacy UUID-based intentions.
_, prevEntry, ixn, err := s.srv.fsm.State().IntentionGet(nil, args.Intention.ID)
_, _, ixn, err := s.srv.fsm.State().IntentionGet(nil, args.Intention.ID)
if err != nil {
return "", nil, fmt.Errorf("Intention lookup failed: %v", err)
return nil, fmt.Errorf("Intention lookup failed: %v", err)
}
if ixn == nil || prevEntry == nil {
return "", nil, fmt.Errorf("Cannot modify non-existent intention: '%s'", args.Intention.ID)
if ixn == nil {
return nil, fmt.Errorf("Cannot modify non-existent intention: '%s'", args.Intention.ID)
}
if err := checkLegacyIntentionApplyAllowed(prevEntry); err != nil {
return "", nil, err
}
// Even though the eventual config entry RPC will do an authz check and
// validation, if we do them here too we can generate error messages that
// make more sense for legacy edits.
if !ixn.CanWrite(authz) {
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("Update operation on intention denied due to ACLs", "intention", args.Intention.ID, "accessorID", accessorID)
return "", nil, acl.ErrPermissionDenied
return nil, acl.ErrPermissionDenied
}
args.Intention.DefaultNamespaces(entMeta)
@ -311,76 +246,77 @@ func (s *Intention) computeApplyChangesLegacyUpdate(
// Prior to v1.9.0 renames of the destination side of an intention were
// allowed, but that behavior doesn't work anymore.
if ixn.DestinationServiceName() != args.Intention.DestinationServiceName() {
return "", nil, fmt.Errorf("Cannot modify DestinationNS or DestinationName for an intention once it exists.")
return nil, fmt.Errorf("Cannot modify DestinationNS or DestinationName for an intention once it exists.")
}
// We always update the updatedat field.
args.Intention.UpdatedAt = time.Now().UTC()
// Default source type
if args.Intention.SourceType == "" {
args.Intention.SourceType = structs.IntentionSourceConsul
}
if err := s.validateEnterpriseIntention(args.Intention); err != nil {
return "", nil, err
return nil, err
}
// Validate. We do not validate on delete since it is valid to only
// send an ID in that case.
//nolint:staticcheck
if err := args.Intention.Validate(); err != nil {
return "", nil, err
return nil, err
}
upsertEntry := prevEntry.Clone()
foundMatch := upsertEntry.UpdateSourceByLegacyID(
args.Intention.ID,
args.Intention.ToSourceIntention(true),
)
if !foundMatch {
return "", nil, fmt.Errorf("Cannot modify non-existent intention: '%s'", args.Intention.ID)
mut := &structs.IntentionMutation{
ID: args.Intention.ID,
Value: args.Intention.ToSourceIntention(true),
}
return structs.ConfigEntryUpsertCAS, upsertEntry, nil
// Set the created/updated times. If this is an update instead of an insert
// the UpdateOver() will fix it up appropriately.
now := time.Now().UTC()
mut.Value.LegacyCreateTime = timePointer(now)
mut.Value.LegacyUpdateTime = timePointer(now)
return mut, nil
}
func (s *Intention) computeApplyChangesUpsert(
accessorID string,
authz acl.Authorizer,
entMeta *structs.EnterpriseMeta,
args *structs.IntentionRequest,
) (structs.ConfigEntryOp, *structs.ServiceIntentionsConfigEntry, error) {
) (*structs.IntentionMutation, error) {
// This variant is just for config-entry based intentions.
if args.Intention.ID != "" {
// This is a new-style only endpoint
return "", nil, fmt.Errorf("ID must not be specified")
return nil, fmt.Errorf("ID must not be specified")
}
args.Intention.DefaultNamespaces(entMeta)
prevEntry, err := s.getServiceIntentionsConfigEntry(args.Intention.DestinationName, args.Intention.DestinationEnterpriseMeta())
if err != nil {
return "", nil, err
if !args.Intention.CanWrite(authz) {
sn := args.Intention.SourceServiceName()
dn := args.Intention.DestinationServiceName()
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("Intention upsert denied due to ACLs",
"source", sn.String(),
"destination", dn.String(),
"accessorID", accessorID)
return nil, acl.ErrPermissionDenied
}
_, prevEntry, err := s.srv.fsm.State().ConfigEntry(nil, structs.ServiceIntentions, args.Intention.DestinationName, args.Intention.DestinationEnterpriseMeta())
if err != nil {
return nil, fmt.Errorf("Intention lookup failed: %v", err)
}
// TODO(intentions): have service-intentions validation functions
// return structured errors so that we can rewrite the field prefix
// here so that the validation errors are not misleading.
if prevEntry == nil {
// Meta is NOT permitted here, as it would need to be persisted on
// the enclosing config entry.
if len(args.Intention.Meta) > 0 {
return "", nil, fmt.Errorf("Meta must not be specified")
return nil, fmt.Errorf("Meta must not be specified")
}
upsertEntry := args.Intention.ToConfigEntry(false)
return structs.ConfigEntryUpsertCAS, upsertEntry, nil
}
upsertEntry := prevEntry.Clone()
} else {
if len(args.Intention.Meta) > 0 {
// Meta is NOT permitted here, but there is one exception. If
// you are updating a previous record, but that record lives
@ -390,19 +326,20 @@ func (s *Intention) computeApplyChangesUpsert(
// In that case if Meta is provided, it has to be a perfect
// match for what is already on the enclosing config entry so
// it's safe to discard.
if !equalStringMaps(upsertEntry.Meta, args.Intention.Meta) {
return "", nil, fmt.Errorf("Meta must not be specified, or should be unchanged during an update.")
if !equalStringMaps(prevEntry.GetMeta(), args.Intention.Meta) {
return nil, fmt.Errorf("Meta must not be specified, or should be unchanged during an update.")
}
// Now it is safe to discard
args.Intention.Meta = nil
}
}
sn := args.Intention.SourceServiceName()
upsertEntry.UpsertSourceByName(sn, args.Intention.ToSourceIntention(false))
return structs.ConfigEntryUpsertCAS, upsertEntry, nil
return &structs.IntentionMutation{
Destination: args.Intention.DestinationServiceName(),
Source: args.Intention.SourceServiceName(),
Value: args.Intention.ToSourceIntention(false),
}, nil
}
func (s *Intention) computeApplyChangesLegacyDelete(
@ -410,93 +347,58 @@ func (s *Intention) computeApplyChangesLegacyDelete(
authz acl.Authorizer,
entMeta *structs.EnterpriseMeta,
args *structs.IntentionRequest,
) (structs.ConfigEntryOp, *structs.ServiceIntentionsConfigEntry, error) {
_, prevEntry, ixn, err := s.srv.fsm.State().IntentionGet(nil, args.Intention.ID)
) (*structs.IntentionMutation, error) {
_, _, ixn, err := s.srv.fsm.State().IntentionGet(nil, args.Intention.ID)
if err != nil {
return "", nil, fmt.Errorf("Intention lookup failed: %v", err)
return nil, fmt.Errorf("Intention lookup failed: %v", err)
}
if ixn == nil || prevEntry == nil {
return "", nil, fmt.Errorf("Cannot delete non-existent intention: '%s'", args.Intention.ID)
if ixn == nil {
return nil, fmt.Errorf("Cannot delete non-existent intention: '%s'", args.Intention.ID)
}
if err := checkLegacyIntentionApplyAllowed(prevEntry); err != nil {
return "", nil, err
}
// Even though the eventual config entry RPC will do an authz check and
// validation, if we do them here too we can generate error messages that
// make more sense for legacy edits.
if !ixn.CanWrite(authz) {
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("Deletion operation on intention denied due to ACLs", "intention", args.Intention.ID, "accessorID", accessorID)
return "", nil, acl.ErrPermissionDenied
return nil, acl.ErrPermissionDenied
}
upsertEntry := prevEntry.Clone()
deleted := upsertEntry.DeleteSourceByLegacyID(args.Intention.ID)
if !deleted {
return "", nil, fmt.Errorf("Cannot delete non-existent intention: '%s'", args.Intention.ID)
}
if upsertEntry == nil || len(upsertEntry.Sources) == 0 {
return structs.ConfigEntryDelete, &structs.ServiceIntentionsConfigEntry{
Kind: structs.ServiceIntentions,
Name: prevEntry.Name,
EnterpriseMeta: prevEntry.EnterpriseMeta,
return &structs.IntentionMutation{
ID: args.Intention.ID,
}, nil
}
return structs.ConfigEntryUpsertCAS, upsertEntry, nil
}
func (s *Intention) computeApplyChangesDelete(
accessorID string,
authz acl.Authorizer,
entMeta *structs.EnterpriseMeta,
args *structs.IntentionRequest,
) (structs.ConfigEntryOp, *structs.ServiceIntentionsConfigEntry, error) {
) (*structs.IntentionMutation, error) {
args.Intention.DefaultNamespaces(entMeta)
prevEntry, err := s.getServiceIntentionsConfigEntry(args.Intention.DestinationName, args.Intention.DestinationEnterpriseMeta())
if err != nil {
return "", nil, err
}
if prevEntry == nil {
return "", nil, nil // no op means no-op
}
// NOTE: validation errors may be misleading!
upsertEntry := prevEntry.Clone()
if !args.Intention.CanWrite(authz) {
sn := args.Intention.SourceServiceName()
deleted := upsertEntry.DeleteSourceByName(sn)
if !deleted {
return "", nil, nil // no op means no-op
dn := args.Intention.DestinationServiceName()
// todo(kit) Migrate intention access denial logging over to audit logging when we implement it
s.logger.Warn("Intention delete denied due to ACLs",
"source", sn.String(),
"destination", dn.String(),
"accessorID", accessorID)
return nil, acl.ErrPermissionDenied
}
if upsertEntry == nil || len(upsertEntry.Sources) == 0 {
return structs.ConfigEntryDelete, &structs.ServiceIntentionsConfigEntry{
Kind: structs.ServiceIntentions,
Name: prevEntry.Name,
EnterpriseMeta: prevEntry.EnterpriseMeta,
// Pre-flight to avoid pointless raft operations.
_, _, ixn, err := s.srv.fsm.State().IntentionGetExact(nil, args.Intention.ToExact())
if err != nil {
return nil, fmt.Errorf("Intention lookup failed: %v", err)
}
if ixn == nil {
return nil, nil
}
return &structs.IntentionMutation{
Destination: args.Intention.DestinationServiceName(),
Source: args.Intention.SourceServiceName(),
}, nil
}
return structs.ConfigEntryUpsertCAS, upsertEntry, nil
}
func checkLegacyIntentionApplyAllowed(prevEntry *structs.ServiceIntentionsConfigEntry) error {
if prevEntry == nil {
return nil
}
if prevEntry.LegacyIDFieldsAreAllSet() {
return nil
}
sn := prevEntry.DestinationServiceName()
return fmt.Errorf("cannot use legacy intention API to edit intentions with a destination of %q after editing them via a service-intentions config entry", sn.String())
}
// Get returns a single intention by ID.
@ -835,23 +737,6 @@ func (s *Intention) validateEnterpriseIntention(ixn *structs.Intention) error {
return nil
}
func (s *Intention) getServiceIntentionsConfigEntry(name string, entMeta *structs.EnterpriseMeta) (*structs.ServiceIntentionsConfigEntry, error) {
_, raw, err := s.srv.fsm.State().ConfigEntry(nil, structs.ServiceIntentions, name, entMeta)
if err != nil {
return nil, fmt.Errorf("Intention lookup failed: %v", err)
}
if raw == nil {
return nil, nil
}
configEntry, ok := raw.(*structs.ServiceIntentionsConfigEntry)
if !ok {
return nil, fmt.Errorf("invalid service config type %T", raw)
}
return configEntry, nil
}
func equalStringMaps(a, b map[string]string) bool {
if len(a) != len(b) {
return false

View File

@ -11,7 +11,7 @@ func init() {
registerEndpoint(func(s *Server) interface{} { return &FederationState{s} })
registerEndpoint(func(s *Server) interface{} { return &DiscoveryChain{s} })
registerEndpoint(func(s *Server) interface{} { return &Health{s} })
registerEndpoint(func(s *Server) interface{} { return NewIntentionEndpoint(s, s.loggers.Named(logging.Intentions)) })
registerEndpoint(func(s *Server) interface{} { return &Intention{s, s.loggers.Named(logging.Intentions)} })
registerEndpoint(func(s *Server) interface{} { return &Internal{s, s.loggers.Named(logging.Internal)} })
registerEndpoint(func(s *Server) interface{} { return &KVS{s, s.loggers.Named(logging.KV)} })
registerEndpoint(func(s *Server) interface{} { return &Operator{s, s.loggers.Named(logging.Operator)} })

View File

@ -180,7 +180,7 @@ func (s *Store) EnsureConfigEntry(idx uint64, conf structs.ConfigEntry, entMeta
}
// ensureConfigEntryTxn upserts a config entry inside of a transaction.
func ensureConfigEntryTxn(tx *txn, idx uint64, conf structs.ConfigEntry, entMeta *structs.EnterpriseMeta) error {
func ensureConfigEntryTxn(tx WriteTxn, idx uint64, conf structs.ConfigEntry, entMeta *structs.EnterpriseMeta) error {
// Check for existing configuration.
existing, err := firstConfigEntryWithTxn(tx, conf.GetKind(), conf.GetName(), entMeta)
if err != nil {
@ -254,6 +254,14 @@ func (s *Store) DeleteConfigEntry(idx uint64, kind, name string, entMeta *struct
tx := s.db.WriteTxn(idx)
defer tx.Abort()
if err := deleteConfigEntryTxn(tx, idx, kind, name, entMeta); err != nil {
return err
}
return tx.Commit()
}
func deleteConfigEntryTxn(tx WriteTxn, idx uint64, kind, name string, entMeta *structs.EnterpriseMeta) error {
// Try to retrieve the existing config entry.
existing, err := firstConfigEntryWithTxn(tx, kind, name, entMeta)
if err != nil {
@ -298,10 +306,10 @@ func (s *Store) DeleteConfigEntry(idx uint64, kind, name string, entMeta *struct
return fmt.Errorf("failed updating index: %s", err)
}
return tx.Commit()
return nil
}
func insertConfigEntryWithTxn(tx *txn, idx uint64, conf structs.ConfigEntry) error {
func insertConfigEntryWithTxn(tx WriteTxn, idx uint64, conf structs.ConfigEntry) error {
if conf == nil {
return fmt.Errorf("cannot insert nil config entry")
}

View File

@ -207,6 +207,294 @@ func (s *Store) legacyIntentionsListTxn(tx ReadTxn, ws memdb.WatchSet, entMeta *
var ErrLegacyIntentionsAreDisabled = errors.New("Legacy intention modifications are disabled after the config entry migration.")
func (s *Store) IntentionMutation(idx uint64, op structs.IntentionOp, mut *structs.IntentionMutation) error {
tx := s.db.WriteTxn(idx)
defer tx.Abort()
usingConfigEntries, err := areIntentionsInConfigEntries(tx, nil)
if err != nil {
return err
}
if !usingConfigEntries {
return errors.New("state: IntentionMutation() is not allowed when intentions are not stored in config entries")
}
switch op {
case structs.IntentionOpCreate:
if err := s.intentionMutationLegacyCreate(tx, idx, mut.Destination, mut.Value); err != nil {
return err
}
case structs.IntentionOpUpdate:
if err := s.intentionMutationLegacyUpdate(tx, idx, mut.ID, mut.Value); err != nil {
return err
}
case structs.IntentionOpDelete:
if mut.ID == "" {
if err := s.intentionMutationDelete(tx, idx, mut.Destination, mut.Source); err != nil {
return err
}
} else {
if err := s.intentionMutationLegacyDelete(tx, idx, mut.ID); err != nil {
return err
}
}
case structs.IntentionOpUpsert:
if err := s.intentionMutationUpsert(tx, idx, mut.Destination, mut.Source, mut.Value); err != nil {
return err
}
case structs.IntentionOpDeleteAll:
// This is an internal operation initiated by the leader and is not
// exposed for general RPC use.
return fmt.Errorf("Invalid Intention mutation operation '%s'", op)
default:
return fmt.Errorf("Invalid Intention mutation operation '%s'", op)
}
return tx.Commit()
}
func (s *Store) intentionMutationLegacyCreate(
tx WriteTxn,
idx uint64,
dest structs.ServiceName,
<