2016-05-11 04:41:47 +00:00
|
|
|
package consul
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/armon/go-metrics"
|
|
|
|
"github.com/hashicorp/consul/consul/structs"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Txn endpoint is used to perform multi-object atomic transactions.
|
|
|
|
type Txn struct {
|
|
|
|
srv *Server
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply is used to apply multiple operations in a single, atomic transaction.
|
2016-05-11 08:35:27 +00:00
|
|
|
func (t *Txn) Apply(args *structs.TxnRequest, reply *structs.TxnResponse) error {
|
2016-05-11 04:41:47 +00:00
|
|
|
if done, err := t.srv.forward("Txn.Apply", args, args, reply); done {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer metrics.MeasureSince([]string{"consul", "txn", "apply"}, time.Now())
|
|
|
|
|
2016-05-11 17:58:27 +00:00
|
|
|
// Perform the pre-apply checks for any KV operations.
|
2016-05-11 04:41:47 +00:00
|
|
|
acl, err := t.srv.resolveToken(args.Token)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for i, op := range args.Ops {
|
2016-05-11 17:58:27 +00:00
|
|
|
if op.KV != nil {
|
|
|
|
ok, err := kvsPreApply(t.srv, acl, op.KV.Verb, &op.KV.DirEnt)
|
2016-05-11 08:35:27 +00:00
|
|
|
if err != nil {
|
|
|
|
reply.Errors = append(reply.Errors, &structs.TxnError{i, err.Error()})
|
|
|
|
} else if !ok {
|
2016-05-11 17:58:27 +00:00
|
|
|
err = fmt.Errorf("failed to lock key %q due to lock delay", op.KV.DirEnt.Key)
|
2016-05-11 08:35:27 +00:00
|
|
|
reply.Errors = append(reply.Errors, &structs.TxnError{i, err.Error()})
|
|
|
|
}
|
2016-05-11 04:41:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if len(reply.Errors) > 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply the update.
|
2016-05-11 08:35:27 +00:00
|
|
|
resp, err := t.srv.raftApply(structs.TxnRequestType, args)
|
2016-05-11 04:41:47 +00:00
|
|
|
if err != nil {
|
2016-05-11 08:35:27 +00:00
|
|
|
t.srv.logger.Printf("[ERR] consul.txn: Apply failed: %v", err)
|
2016-05-11 04:41:47 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
if respErr, ok := resp.(error); ok {
|
|
|
|
return respErr
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert the return type. This should be a cheap copy since we are
|
|
|
|
// just taking the two slices.
|
2016-05-11 08:35:27 +00:00
|
|
|
if txnResp, ok := resp.(structs.TxnResponse); ok {
|
|
|
|
*reply = txnResp
|
2016-05-11 04:41:47 +00:00
|
|
|
} else {
|
|
|
|
return fmt.Errorf("unexpected return type %T", resp)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|