github.phpd.cn/hashicorp/consul@v1.4.5/agent/consul/state/txn.go (about)

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/consul/agent/structs"
     7  	"github.com/hashicorp/consul/api"
     8  	"github.com/hashicorp/go-memdb"
     9  )
    10  
    11  // txnKVS handles all KV-related operations.
    12  func (s *Store) txnKVS(tx *memdb.Txn, idx uint64, op *structs.TxnKVOp) (structs.TxnResults, error) {
    13  	var entry *structs.DirEntry
    14  	var err error
    15  
    16  	switch op.Verb {
    17  	case api.KVSet:
    18  		entry = &op.DirEnt
    19  		err = s.kvsSetTxn(tx, idx, entry, false)
    20  
    21  	case api.KVDelete:
    22  		err = s.kvsDeleteTxn(tx, idx, op.DirEnt.Key)
    23  
    24  	case api.KVDeleteCAS:
    25  		var ok bool
    26  		ok, err = s.kvsDeleteCASTxn(tx, idx, op.DirEnt.ModifyIndex, op.DirEnt.Key)
    27  		if !ok && err == nil {
    28  			err = fmt.Errorf("failed to delete key %q, index is stale", op.DirEnt.Key)
    29  		}
    30  
    31  	case api.KVDeleteTree:
    32  		err = s.kvsDeleteTreeTxn(tx, idx, op.DirEnt.Key)
    33  
    34  	case api.KVCAS:
    35  		var ok bool
    36  		entry = &op.DirEnt
    37  		ok, err = s.kvsSetCASTxn(tx, idx, entry)
    38  		if !ok && err == nil {
    39  			err = fmt.Errorf("failed to set key %q, index is stale", op.DirEnt.Key)
    40  		}
    41  
    42  	case api.KVLock:
    43  		var ok bool
    44  		entry = &op.DirEnt
    45  		ok, err = s.kvsLockTxn(tx, idx, entry)
    46  		if !ok && err == nil {
    47  			err = fmt.Errorf("failed to lock key %q, lock is already held", op.DirEnt.Key)
    48  		}
    49  
    50  	case api.KVUnlock:
    51  		var ok bool
    52  		entry = &op.DirEnt
    53  		ok, err = s.kvsUnlockTxn(tx, idx, entry)
    54  		if !ok && err == nil {
    55  			err = fmt.Errorf("failed to unlock key %q, lock isn't held, or is held by another session", op.DirEnt.Key)
    56  		}
    57  
    58  	case api.KVGet:
    59  		_, entry, err = s.kvsGetTxn(tx, nil, op.DirEnt.Key)
    60  		if entry == nil && err == nil {
    61  			err = fmt.Errorf("key %q doesn't exist", op.DirEnt.Key)
    62  		}
    63  
    64  	case api.KVGetTree:
    65  		var entries structs.DirEntries
    66  		_, entries, err = s.kvsListTxn(tx, nil, op.DirEnt.Key)
    67  		if err == nil {
    68  			results := make(structs.TxnResults, 0, len(entries))
    69  			for _, e := range entries {
    70  				result := structs.TxnResult{KV: e}
    71  				results = append(results, &result)
    72  			}
    73  			return results, nil
    74  		}
    75  
    76  	case api.KVCheckSession:
    77  		entry, err = s.kvsCheckSessionTxn(tx, op.DirEnt.Key, op.DirEnt.Session)
    78  
    79  	case api.KVCheckIndex:
    80  		entry, err = s.kvsCheckIndexTxn(tx, op.DirEnt.Key, op.DirEnt.ModifyIndex)
    81  
    82  	case api.KVCheckNotExists:
    83  		_, entry, err = s.kvsGetTxn(tx, nil, op.DirEnt.Key)
    84  		if entry != nil && err == nil {
    85  			err = fmt.Errorf("key %q exists", op.DirEnt.Key)
    86  		}
    87  
    88  	default:
    89  		err = fmt.Errorf("unknown KV verb %q", op.Verb)
    90  	}
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	// For a GET we keep the value, otherwise we clone and blank out the
    96  	// value (we have to clone so we don't modify the entry being used by
    97  	// the state store).
    98  	if entry != nil {
    99  		if op.Verb == api.KVGet {
   100  			result := structs.TxnResult{KV: entry}
   101  			return structs.TxnResults{&result}, nil
   102  		}
   103  
   104  		clone := entry.Clone()
   105  		clone.Value = nil
   106  		result := structs.TxnResult{KV: clone}
   107  		return structs.TxnResults{&result}, nil
   108  	}
   109  
   110  	return nil, nil
   111  }
   112  
   113  // txnIntention handles all Intention-related operations.
   114  func (s *Store) txnIntention(tx *memdb.Txn, idx uint64, op *structs.TxnIntentionOp) error {
   115  	switch op.Op {
   116  	case structs.IntentionOpCreate, structs.IntentionOpUpdate:
   117  		return s.intentionSetTxn(tx, idx, op.Intention)
   118  	case structs.IntentionOpDelete:
   119  		return s.intentionDeleteTxn(tx, idx, op.Intention.ID)
   120  	default:
   121  		return fmt.Errorf("unknown Intention op %q", op.Op)
   122  	}
   123  }
   124  
   125  // txnNode handles all Node-related operations.
   126  func (s *Store) txnNode(tx *memdb.Txn, idx uint64, op *structs.TxnNodeOp) (structs.TxnResults, error) {
   127  	var entry *structs.Node
   128  	var err error
   129  
   130  	getNode := func() (*structs.Node, error) {
   131  		if op.Node.ID != "" {
   132  			return getNodeIDTxn(tx, op.Node.ID)
   133  		} else {
   134  			return getNodeTxn(tx, op.Node.Node)
   135  		}
   136  	}
   137  
   138  	switch op.Verb {
   139  	case api.NodeGet:
   140  		entry, err = getNode()
   141  		if entry == nil && err == nil {
   142  			err = fmt.Errorf("node %q doesn't exist", op.Node.Node)
   143  		}
   144  
   145  	case api.NodeSet:
   146  		err = s.ensureNodeTxn(tx, idx, &op.Node)
   147  		if err == nil {
   148  			entry, err = getNode()
   149  		}
   150  
   151  	case api.NodeCAS:
   152  		var ok bool
   153  		ok, err = s.ensureNodeCASTxn(tx, idx, &op.Node)
   154  		if !ok && err == nil {
   155  			err = fmt.Errorf("failed to set node %q, index is stale", op.Node.Node)
   156  			break
   157  		}
   158  		entry, err = getNode()
   159  
   160  	case api.NodeDelete:
   161  		err = s.deleteNodeTxn(tx, idx, op.Node.Node)
   162  
   163  	case api.NodeDeleteCAS:
   164  		var ok bool
   165  		ok, err = s.deleteNodeCASTxn(tx, idx, op.Node.ModifyIndex, op.Node.Node)
   166  		if !ok && err == nil {
   167  			err = fmt.Errorf("failed to delete node %q, index is stale", op.Node.Node)
   168  		}
   169  
   170  	default:
   171  		err = fmt.Errorf("unknown Node verb %q", op.Verb)
   172  	}
   173  	if err != nil {
   174  		return nil, err
   175  	}
   176  
   177  	// For a GET we keep the value, otherwise we clone and blank out the
   178  	// value (we have to clone so we don't modify the entry being used by
   179  	// the state store).
   180  	if entry != nil {
   181  		if op.Verb == api.NodeGet {
   182  			result := structs.TxnResult{Node: entry}
   183  			return structs.TxnResults{&result}, nil
   184  		}
   185  
   186  		clone := *entry
   187  		result := structs.TxnResult{Node: &clone}
   188  		return structs.TxnResults{&result}, nil
   189  	}
   190  
   191  	return nil, nil
   192  }
   193  
   194  // txnService handles all Service-related operations.
   195  func (s *Store) txnService(tx *memdb.Txn, idx uint64, op *structs.TxnServiceOp) (structs.TxnResults, error) {
   196  	var entry *structs.NodeService
   197  	var err error
   198  
   199  	switch op.Verb {
   200  	case api.ServiceGet:
   201  		entry, err = s.getNodeServiceTxn(tx, op.Node, op.Service.ID)
   202  		if entry == nil && err == nil {
   203  			err = fmt.Errorf("service %q on node %q doesn't exist", op.Service.ID, op.Node)
   204  		}
   205  
   206  	case api.ServiceSet:
   207  		err = s.ensureServiceTxn(tx, idx, op.Node, &op.Service)
   208  		entry, err = s.getNodeServiceTxn(tx, op.Node, op.Service.ID)
   209  
   210  	case api.ServiceCAS:
   211  		var ok bool
   212  		ok, err = s.ensureServiceCASTxn(tx, idx, op.Node, &op.Service)
   213  		if !ok && err == nil {
   214  			err = fmt.Errorf("failed to set service %q on node %q, index is stale", op.Service.ID, op.Node)
   215  			break
   216  		}
   217  		entry, err = s.getNodeServiceTxn(tx, op.Node, op.Service.ID)
   218  
   219  	case api.ServiceDelete:
   220  		err = s.deleteServiceTxn(tx, idx, op.Node, op.Service.ID)
   221  
   222  	case api.ServiceDeleteCAS:
   223  		var ok bool
   224  		ok, err = s.deleteServiceCASTxn(tx, idx, op.Service.ModifyIndex, op.Node, op.Service.ID)
   225  		if !ok && err == nil {
   226  			err = fmt.Errorf("failed to delete service %q on node %q, index is stale", op.Service.ID, op.Node)
   227  		}
   228  
   229  	default:
   230  		err = fmt.Errorf("unknown Service verb %q", op.Verb)
   231  	}
   232  	if err != nil {
   233  		return nil, err
   234  	}
   235  
   236  	// For a GET we keep the value, otherwise we clone and blank out the
   237  	// value (we have to clone so we don't modify the entry being used by
   238  	// the state store).
   239  	if entry != nil {
   240  		if op.Verb == api.ServiceGet {
   241  			result := structs.TxnResult{Service: entry}
   242  			return structs.TxnResults{&result}, nil
   243  		}
   244  
   245  		clone := *entry
   246  		result := structs.TxnResult{Service: &clone}
   247  		return structs.TxnResults{&result}, nil
   248  	}
   249  
   250  	return nil, nil
   251  }
   252  
   253  // txnCheck handles all Check-related operations.
   254  func (s *Store) txnCheck(tx *memdb.Txn, idx uint64, op *structs.TxnCheckOp) (structs.TxnResults, error) {
   255  	var entry *structs.HealthCheck
   256  	var err error
   257  
   258  	switch op.Verb {
   259  	case api.CheckGet:
   260  		_, entry, err = s.getNodeCheckTxn(tx, op.Check.Node, op.Check.CheckID)
   261  		if entry == nil && err == nil {
   262  			err = fmt.Errorf("check %q on node %q doesn't exist", op.Check.CheckID, op.Check.Node)
   263  		}
   264  
   265  	case api.CheckSet:
   266  		err = s.ensureCheckTxn(tx, idx, &op.Check)
   267  		if err == nil {
   268  			_, entry, err = s.getNodeCheckTxn(tx, op.Check.Node, op.Check.CheckID)
   269  		}
   270  
   271  	case api.CheckCAS:
   272  		var ok bool
   273  		entry = &op.Check
   274  		ok, err = s.ensureCheckCASTxn(tx, idx, entry)
   275  		if !ok && err == nil {
   276  			err = fmt.Errorf("failed to set check %q on node %q, index is stale", entry.CheckID, entry.Node)
   277  			break
   278  		}
   279  		_, entry, err = s.getNodeCheckTxn(tx, op.Check.Node, op.Check.CheckID)
   280  
   281  	case api.CheckDelete:
   282  		err = s.deleteCheckTxn(tx, idx, op.Check.Node, op.Check.CheckID)
   283  
   284  	case api.CheckDeleteCAS:
   285  		var ok bool
   286  		ok, err = s.deleteCheckCASTxn(tx, idx, op.Check.ModifyIndex, op.Check.Node, op.Check.CheckID)
   287  		if !ok && err == nil {
   288  			err = fmt.Errorf("failed to delete check %q on node %q, index is stale", op.Check.CheckID, op.Check.Node)
   289  		}
   290  
   291  	default:
   292  		err = fmt.Errorf("unknown Check verb %q", op.Verb)
   293  	}
   294  	if err != nil {
   295  		return nil, err
   296  	}
   297  
   298  	// For a GET we keep the value, otherwise we clone and blank out the
   299  	// value (we have to clone so we don't modify the entry being used by
   300  	// the state store).
   301  	if entry != nil {
   302  		if op.Verb == api.CheckGet {
   303  			result := structs.TxnResult{Check: entry}
   304  			return structs.TxnResults{&result}, nil
   305  		}
   306  
   307  		clone := entry.Clone()
   308  		result := structs.TxnResult{Check: clone}
   309  		return structs.TxnResults{&result}, nil
   310  	}
   311  
   312  	return nil, nil
   313  }
   314  
   315  // txnDispatch runs the given operations inside the state store transaction.
   316  func (s *Store) txnDispatch(tx *memdb.Txn, idx uint64, ops structs.TxnOps) (structs.TxnResults, structs.TxnErrors) {
   317  	results := make(structs.TxnResults, 0, len(ops))
   318  	errors := make(structs.TxnErrors, 0, len(ops))
   319  	for i, op := range ops {
   320  		var ret structs.TxnResults
   321  		var err error
   322  
   323  		// Dispatch based on the type of operation.
   324  		switch {
   325  		case op.KV != nil:
   326  			ret, err = s.txnKVS(tx, idx, op.KV)
   327  		case op.Intention != nil:
   328  			err = s.txnIntention(tx, idx, op.Intention)
   329  		case op.Node != nil:
   330  			ret, err = s.txnNode(tx, idx, op.Node)
   331  		case op.Service != nil:
   332  			ret, err = s.txnService(tx, idx, op.Service)
   333  		case op.Check != nil:
   334  			ret, err = s.txnCheck(tx, idx, op.Check)
   335  		default:
   336  			err = fmt.Errorf("no operation specified")
   337  		}
   338  
   339  		// Accumulate the results.
   340  		results = append(results, ret...)
   341  
   342  		// Capture any error along with the index of the operation that
   343  		// failed.
   344  		if err != nil {
   345  			errors = append(errors, &structs.TxnError{
   346  				OpIndex: i,
   347  				What:    err.Error(),
   348  			})
   349  		}
   350  	}
   351  
   352  	if len(errors) > 0 {
   353  		return nil, errors
   354  	}
   355  
   356  	return results, nil
   357  }
   358  
   359  // TxnRW tries to run the given operations all inside a single transaction. If
   360  // any of the operations fail, the entire transaction will be rolled back. This
   361  // is done in a full write transaction on the state store, so reads and writes
   362  // are possible
   363  func (s *Store) TxnRW(idx uint64, ops structs.TxnOps) (structs.TxnResults, structs.TxnErrors) {
   364  	tx := s.db.Txn(true)
   365  	defer tx.Abort()
   366  
   367  	results, errors := s.txnDispatch(tx, idx, ops)
   368  	if len(errors) > 0 {
   369  		return nil, errors
   370  	}
   371  
   372  	tx.Commit()
   373  	return results, nil
   374  }
   375  
   376  // TxnRO runs the given operations inside a single read transaction in the state
   377  // store. You must verify outside this function that no write operations are
   378  // present, otherwise you'll get an error from the state store.
   379  func (s *Store) TxnRO(ops structs.TxnOps) (structs.TxnResults, structs.TxnErrors) {
   380  	tx := s.db.Txn(false)
   381  	defer tx.Abort()
   382  
   383  	results, errors := s.txnDispatch(tx, 0, ops)
   384  	if len(errors) > 0 {
   385  		return nil, errors
   386  	}
   387  
   388  	return results, nil
   389  }