github.com/KyaXTeam/consul@v1.4.5/agent/consul/txn_endpoint.go (about) 1 package consul 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/armon/go-metrics" 8 "github.com/hashicorp/consul/acl" 9 "github.com/hashicorp/consul/agent/structs" 10 "github.com/hashicorp/consul/api" 11 ) 12 13 // Txn endpoint is used to perform multi-object atomic transactions. 14 type Txn struct { 15 srv *Server 16 } 17 18 // preCheck is used to verify the incoming operations before any further 19 // processing takes place. This checks things like ACLs. 20 func (t *Txn) preCheck(authorizer acl.Authorizer, ops structs.TxnOps) structs.TxnErrors { 21 var errors structs.TxnErrors 22 23 // Perform the pre-apply checks for any KV operations. 24 for i, op := range ops { 25 switch { 26 case op.KV != nil: 27 ok, err := kvsPreApply(t.srv, authorizer, op.KV.Verb, &op.KV.DirEnt) 28 if err != nil { 29 errors = append(errors, &structs.TxnError{ 30 OpIndex: i, 31 What: err.Error(), 32 }) 33 } else if !ok { 34 err = fmt.Errorf("failed to lock key %q due to lock delay", op.KV.DirEnt.Key) 35 errors = append(errors, &structs.TxnError{ 36 OpIndex: i, 37 What: err.Error(), 38 }) 39 } 40 case op.Node != nil: 41 // Skip the pre-apply checks if this is a GET. 42 if op.Node.Verb == api.NodeGet { 43 break 44 } 45 46 node := op.Node.Node 47 if err := nodePreApply(node.Node, string(node.ID)); err != nil { 48 errors = append(errors, &structs.TxnError{ 49 OpIndex: i, 50 What: err.Error(), 51 }) 52 break 53 } 54 55 // Check that the token has permissions for the given operation. 56 if err := vetNodeTxnOp(op.Node, authorizer); err != nil { 57 errors = append(errors, &structs.TxnError{ 58 OpIndex: i, 59 What: err.Error(), 60 }) 61 } 62 case op.Service != nil: 63 // Skip the pre-apply checks if this is a GET. 64 if op.Service.Verb == api.ServiceGet { 65 break 66 } 67 68 service := &op.Service.Service 69 if err := servicePreApply(service, nil); err != nil { 70 errors = append(errors, &structs.TxnError{ 71 OpIndex: i, 72 What: err.Error(), 73 }) 74 break 75 } 76 77 // Check that the token has permissions for the given operation. 78 if err := vetServiceTxnOp(op.Service, authorizer); err != nil { 79 errors = append(errors, &structs.TxnError{ 80 OpIndex: i, 81 What: err.Error(), 82 }) 83 } 84 case op.Check != nil: 85 // Skip the pre-apply checks if this is a GET. 86 if op.Check.Verb == api.CheckGet { 87 break 88 } 89 90 checkPreApply(&op.Check.Check) 91 92 // Check that the token has permissions for the given operation. 93 if err := vetCheckTxnOp(op.Check, authorizer); err != nil { 94 errors = append(errors, &structs.TxnError{ 95 OpIndex: i, 96 What: err.Error(), 97 }) 98 } 99 } 100 } 101 102 return errors 103 } 104 105 // Apply is used to apply multiple operations in a single, atomic transaction. 106 func (t *Txn) Apply(args *structs.TxnRequest, reply *structs.TxnResponse) error { 107 if done, err := t.srv.forward("Txn.Apply", args, args, reply); done { 108 return err 109 } 110 defer metrics.MeasureSince([]string{"txn", "apply"}, time.Now()) 111 112 // Run the pre-checks before we send the transaction into Raft. 113 authorizer, err := t.srv.ResolveToken(args.Token) 114 if err != nil { 115 return err 116 } 117 reply.Errors = t.preCheck(authorizer, args.Ops) 118 if len(reply.Errors) > 0 { 119 return nil 120 } 121 122 // Apply the update. 123 resp, err := t.srv.raftApply(structs.TxnRequestType, args) 124 if err != nil { 125 t.srv.logger.Printf("[ERR] consul.txn: Apply failed: %v", err) 126 return err 127 } 128 if respErr, ok := resp.(error); ok { 129 return respErr 130 } 131 132 // Convert the return type. This should be a cheap copy since we are 133 // just taking the two slices. 134 if txnResp, ok := resp.(structs.TxnResponse); ok { 135 if authorizer != nil { 136 txnResp.Results = FilterTxnResults(authorizer, txnResp.Results) 137 } 138 *reply = txnResp 139 } else { 140 return fmt.Errorf("unexpected return type %T", resp) 141 } 142 return nil 143 } 144 145 // Read is used to perform a read-only transaction that doesn't modify the state 146 // store. This is much more scalable since it doesn't go through Raft and 147 // supports staleness, so this should be preferred if you're just performing 148 // reads. 149 func (t *Txn) Read(args *structs.TxnReadRequest, reply *structs.TxnReadResponse) error { 150 if done, err := t.srv.forward("Txn.Read", args, args, reply); done { 151 return err 152 } 153 defer metrics.MeasureSince([]string{"txn", "read"}, time.Now()) 154 155 // We have to do this ourselves since we are not doing a blocking RPC. 156 t.srv.setQueryMeta(&reply.QueryMeta) 157 if args.RequireConsistent { 158 if err := t.srv.consistentRead(); err != nil { 159 return err 160 } 161 } 162 163 // Run the pre-checks before we perform the read. 164 authorizer, err := t.srv.ResolveToken(args.Token) 165 if err != nil { 166 return err 167 } 168 reply.Errors = t.preCheck(authorizer, args.Ops) 169 if len(reply.Errors) > 0 { 170 return nil 171 } 172 173 // Run the read transaction. 174 state := t.srv.fsm.State() 175 reply.Results, reply.Errors = state.TxnRO(args.Ops) 176 if authorizer != nil { 177 reply.Results = FilterTxnResults(authorizer, reply.Results) 178 } 179 return nil 180 }