hub.fastgit.org/hashicorp/consul.git@v1.4.5/api/txn.go (about) 1 package api 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "net/http" 8 ) 9 10 // Txn is used to manipulate the Txn API 11 type Txn struct { 12 c *Client 13 } 14 15 // Txn is used to return a handle to the K/V apis 16 func (c *Client) Txn() *Txn { 17 return &Txn{c} 18 } 19 20 // TxnOp is the internal format we send to Consul. Currently only K/V and 21 // check operations are supported. 22 type TxnOp struct { 23 KV *KVTxnOp 24 Node *NodeTxnOp 25 Service *ServiceTxnOp 26 Check *CheckTxnOp 27 } 28 29 // TxnOps is a list of transaction operations. 30 type TxnOps []*TxnOp 31 32 // TxnResult is the internal format we receive from Consul. 33 type TxnResult struct { 34 KV *KVPair 35 Node *Node 36 Service *CatalogService 37 Check *HealthCheck 38 } 39 40 // TxnResults is a list of TxnResult objects. 41 type TxnResults []*TxnResult 42 43 // TxnError is used to return information about an operation in a transaction. 44 type TxnError struct { 45 OpIndex int 46 What string 47 } 48 49 // TxnErrors is a list of TxnError objects. 50 type TxnErrors []*TxnError 51 52 // TxnResponse is the internal format we receive from Consul. 53 type TxnResponse struct { 54 Results TxnResults 55 Errors TxnErrors 56 } 57 58 // KVOp constants give possible operations available in a transaction. 59 type KVOp string 60 61 const ( 62 KVSet KVOp = "set" 63 KVDelete KVOp = "delete" 64 KVDeleteCAS KVOp = "delete-cas" 65 KVDeleteTree KVOp = "delete-tree" 66 KVCAS KVOp = "cas" 67 KVLock KVOp = "lock" 68 KVUnlock KVOp = "unlock" 69 KVGet KVOp = "get" 70 KVGetTree KVOp = "get-tree" 71 KVCheckSession KVOp = "check-session" 72 KVCheckIndex KVOp = "check-index" 73 KVCheckNotExists KVOp = "check-not-exists" 74 ) 75 76 // KVTxnOp defines a single operation inside a transaction. 77 type KVTxnOp struct { 78 Verb KVOp 79 Key string 80 Value []byte 81 Flags uint64 82 Index uint64 83 Session string 84 } 85 86 // KVTxnOps defines a set of operations to be performed inside a single 87 // transaction. 88 type KVTxnOps []*KVTxnOp 89 90 // KVTxnResponse has the outcome of a transaction. 91 type KVTxnResponse struct { 92 Results []*KVPair 93 Errors TxnErrors 94 } 95 96 // NodeOp constants give possible operations available in a transaction. 97 type NodeOp string 98 99 const ( 100 NodeGet NodeOp = "get" 101 NodeSet NodeOp = "set" 102 NodeCAS NodeOp = "cas" 103 NodeDelete NodeOp = "delete" 104 NodeDeleteCAS NodeOp = "delete-cas" 105 ) 106 107 // NodeTxnOp defines a single operation inside a transaction. 108 type NodeTxnOp struct { 109 Verb NodeOp 110 Node Node 111 } 112 113 // ServiceOp constants give possible operations available in a transaction. 114 type ServiceOp string 115 116 const ( 117 ServiceGet ServiceOp = "get" 118 ServiceSet ServiceOp = "set" 119 ServiceCAS ServiceOp = "cas" 120 ServiceDelete ServiceOp = "delete" 121 ServiceDeleteCAS ServiceOp = "delete-cas" 122 ) 123 124 // ServiceTxnOp defines a single operation inside a transaction. 125 type ServiceTxnOp struct { 126 Verb ServiceOp 127 Node string 128 Service AgentService 129 } 130 131 // CheckOp constants give possible operations available in a transaction. 132 type CheckOp string 133 134 const ( 135 CheckGet CheckOp = "get" 136 CheckSet CheckOp = "set" 137 CheckCAS CheckOp = "cas" 138 CheckDelete CheckOp = "delete" 139 CheckDeleteCAS CheckOp = "delete-cas" 140 ) 141 142 // CheckTxnOp defines a single operation inside a transaction. 143 type CheckTxnOp struct { 144 Verb CheckOp 145 Check HealthCheck 146 } 147 148 // Txn is used to apply multiple Consul operations in a single, atomic transaction. 149 // 150 // Note that Go will perform the required base64 encoding on the values 151 // automatically because the type is a byte slice. Transactions are defined as a 152 // list of operations to perform, using the different fields in the TxnOp structure 153 // to define operations. If any operation fails, none of the changes are applied 154 // to the state store. 155 // 156 // Even though this is generally a write operation, we take a QueryOptions input 157 // and return a QueryMeta output. If the transaction contains only read ops, then 158 // Consul will fast-path it to a different endpoint internally which supports 159 // consistency controls, but not blocking. If there are write operations then 160 // the request will always be routed through raft and any consistency settings 161 // will be ignored. 162 // 163 // Here's an example: 164 // 165 // ops := KVTxnOps{ 166 // &KVTxnOp{ 167 // Verb: KVLock, 168 // Key: "test/lock", 169 // Session: "adf4238a-882b-9ddc-4a9d-5b6758e4159e", 170 // Value: []byte("hello"), 171 // }, 172 // &KVTxnOp{ 173 // Verb: KVGet, 174 // Key: "another/key", 175 // }, 176 // &CheckTxnOp{ 177 // Verb: CheckSet, 178 // HealthCheck: HealthCheck{ 179 // Node: "foo", 180 // CheckID: "redis:a", 181 // Name: "Redis Health Check", 182 // Status: "passing", 183 // }, 184 // } 185 // } 186 // ok, response, _, err := kv.Txn(&ops, nil) 187 // 188 // If there is a problem making the transaction request then an error will be 189 // returned. Otherwise, the ok value will be true if the transaction succeeded 190 // or false if it was rolled back. The response is a structured return value which 191 // will have the outcome of the transaction. Its Results member will have entries 192 // for each operation. For KV operations, Deleted keys will have a nil entry in the 193 // results, and to save space, the Value of each key in the Results will be nil 194 // unless the operation is a KVGet. If the transaction was rolled back, the Errors 195 // member will have entries referencing the index of the operation that failed 196 // along with an error message. 197 func (t *Txn) Txn(txn TxnOps, q *QueryOptions) (bool, *TxnResponse, *QueryMeta, error) { 198 return t.c.txn(txn, q) 199 } 200 201 func (c *Client) txn(txn TxnOps, q *QueryOptions) (bool, *TxnResponse, *QueryMeta, error) { 202 r := c.newRequest("PUT", "/v1/txn") 203 r.setQueryOptions(q) 204 205 r.obj = txn 206 rtt, resp, err := c.doRequest(r) 207 if err != nil { 208 return false, nil, nil, err 209 } 210 defer resp.Body.Close() 211 212 qm := &QueryMeta{} 213 parseQueryMeta(resp, qm) 214 qm.RequestTime = rtt 215 216 if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusConflict { 217 var txnResp TxnResponse 218 if err := decodeBody(resp, &txnResp); err != nil { 219 return false, nil, nil, err 220 } 221 222 return resp.StatusCode == http.StatusOK, &txnResp, qm, nil 223 } 224 225 var buf bytes.Buffer 226 if _, err := io.Copy(&buf, resp.Body); err != nil { 227 return false, nil, nil, fmt.Errorf("Failed to read response: %v", err) 228 } 229 return false, nil, nil, fmt.Errorf("Failed request: %s", buf.String()) 230 }