github.imxd.top/hashicorp/consul@v1.4.5/api/connect_intention.go (about) 1 package api 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "time" 8 ) 9 10 // Intention defines an intention for the Connect Service Graph. This defines 11 // the allowed or denied behavior of a connection between two services using 12 // Connect. 13 type Intention struct { 14 // ID is the UUID-based ID for the intention, always generated by Consul. 15 ID string 16 17 // Description is a human-friendly description of this intention. 18 // It is opaque to Consul and is only stored and transferred in API 19 // requests. 20 Description string 21 22 // SourceNS, SourceName are the namespace and name, respectively, of 23 // the source service. Either of these may be the wildcard "*", but only 24 // the full value can be a wildcard. Partial wildcards are not allowed. 25 // The source may also be a non-Consul service, as specified by SourceType. 26 // 27 // DestinationNS, DestinationName is the same, but for the destination 28 // service. The same rules apply. The destination is always a Consul 29 // service. 30 SourceNS, SourceName string 31 DestinationNS, DestinationName string 32 33 // SourceType is the type of the value for the source. 34 SourceType IntentionSourceType 35 36 // Action is whether this is a whitelist or blacklist intention. 37 Action IntentionAction 38 39 // DefaultAddr, DefaultPort of the local listening proxy (if any) to 40 // make this connection. 41 DefaultAddr string 42 DefaultPort int 43 44 // Meta is arbitrary metadata associated with the intention. This is 45 // opaque to Consul but is served in API responses. 46 Meta map[string]string 47 48 // Precedence is the order that the intention will be applied, with 49 // larger numbers being applied first. This is a read-only field, on 50 // any intention update it is updated. 51 Precedence int 52 53 // CreatedAt and UpdatedAt keep track of when this record was created 54 // or modified. 55 CreatedAt, UpdatedAt time.Time 56 57 CreateIndex uint64 58 ModifyIndex uint64 59 } 60 61 // String returns human-friendly output describing ths intention. 62 func (i *Intention) String() string { 63 return fmt.Sprintf("%s => %s (%s)", 64 i.SourceString(), 65 i.DestinationString(), 66 i.Action) 67 } 68 69 // SourceString returns the namespace/name format for the source, or 70 // just "name" if the namespace is the default namespace. 71 func (i *Intention) SourceString() string { 72 return i.partString(i.SourceNS, i.SourceName) 73 } 74 75 // DestinationString returns the namespace/name format for the source, or 76 // just "name" if the namespace is the default namespace. 77 func (i *Intention) DestinationString() string { 78 return i.partString(i.DestinationNS, i.DestinationName) 79 } 80 81 func (i *Intention) partString(ns, n string) string { 82 // For now we omit the default namespace from the output. In the future 83 // we might want to look at this and show this in a multi-namespace world. 84 if ns != "" && ns != IntentionDefaultNamespace { 85 n = ns + "/" + n 86 } 87 88 return n 89 } 90 91 // IntentionDefaultNamespace is the default namespace value. 92 const IntentionDefaultNamespace = "default" 93 94 // IntentionAction is the action that the intention represents. This 95 // can be "allow" or "deny" to whitelist or blacklist intentions. 96 type IntentionAction string 97 98 const ( 99 IntentionActionAllow IntentionAction = "allow" 100 IntentionActionDeny IntentionAction = "deny" 101 ) 102 103 // IntentionSourceType is the type of the source within an intention. 104 type IntentionSourceType string 105 106 const ( 107 // IntentionSourceConsul is a service within the Consul catalog. 108 IntentionSourceConsul IntentionSourceType = "consul" 109 ) 110 111 // IntentionMatch are the arguments for the intention match API. 112 type IntentionMatch struct { 113 By IntentionMatchType 114 Names []string 115 } 116 117 // IntentionMatchType is the target for a match request. For example, 118 // matching by source will look for all intentions that match the given 119 // source value. 120 type IntentionMatchType string 121 122 const ( 123 IntentionMatchSource IntentionMatchType = "source" 124 IntentionMatchDestination IntentionMatchType = "destination" 125 ) 126 127 // IntentionCheck are the arguments for the intention check API. For 128 // more documentation see the IntentionCheck function. 129 type IntentionCheck struct { 130 // Source and Destination are the source and destination values to 131 // check. The destination is always a Consul service, but the source 132 // may be other values as defined by the SourceType. 133 Source, Destination string 134 135 // SourceType is the type of the value for the source. 136 SourceType IntentionSourceType 137 } 138 139 // Intentions returns the list of intentions. 140 func (h *Connect) Intentions(q *QueryOptions) ([]*Intention, *QueryMeta, error) { 141 r := h.c.newRequest("GET", "/v1/connect/intentions") 142 r.setQueryOptions(q) 143 rtt, resp, err := requireOK(h.c.doRequest(r)) 144 if err != nil { 145 return nil, nil, err 146 } 147 defer resp.Body.Close() 148 149 qm := &QueryMeta{} 150 parseQueryMeta(resp, qm) 151 qm.RequestTime = rtt 152 153 var out []*Intention 154 if err := decodeBody(resp, &out); err != nil { 155 return nil, nil, err 156 } 157 return out, qm, nil 158 } 159 160 // IntentionGet retrieves a single intention. 161 func (h *Connect) IntentionGet(id string, q *QueryOptions) (*Intention, *QueryMeta, error) { 162 r := h.c.newRequest("GET", "/v1/connect/intentions/"+id) 163 r.setQueryOptions(q) 164 rtt, resp, err := h.c.doRequest(r) 165 if err != nil { 166 return nil, nil, err 167 } 168 defer resp.Body.Close() 169 170 qm := &QueryMeta{} 171 parseQueryMeta(resp, qm) 172 qm.RequestTime = rtt 173 174 if resp.StatusCode == 404 { 175 return nil, qm, nil 176 } else if resp.StatusCode != 200 { 177 var buf bytes.Buffer 178 io.Copy(&buf, resp.Body) 179 return nil, nil, fmt.Errorf( 180 "Unexpected response %d: %s", resp.StatusCode, buf.String()) 181 } 182 183 var out Intention 184 if err := decodeBody(resp, &out); err != nil { 185 return nil, nil, err 186 } 187 return &out, qm, nil 188 } 189 190 // IntentionDelete deletes a single intention. 191 func (h *Connect) IntentionDelete(id string, q *WriteOptions) (*WriteMeta, error) { 192 r := h.c.newRequest("DELETE", "/v1/connect/intentions/"+id) 193 r.setWriteOptions(q) 194 rtt, resp, err := requireOK(h.c.doRequest(r)) 195 if err != nil { 196 return nil, err 197 } 198 defer resp.Body.Close() 199 200 qm := &WriteMeta{} 201 qm.RequestTime = rtt 202 203 return qm, nil 204 } 205 206 // IntentionMatch returns the list of intentions that match a given source 207 // or destination. The returned intentions are ordered by precedence where 208 // result[0] is the highest precedence (if that matches, then that rule overrides 209 // all other rules). 210 // 211 // Matching can be done for multiple names at the same time. The resulting 212 // map is keyed by the given names. Casing is preserved. 213 func (h *Connect) IntentionMatch(args *IntentionMatch, q *QueryOptions) (map[string][]*Intention, *QueryMeta, error) { 214 r := h.c.newRequest("GET", "/v1/connect/intentions/match") 215 r.setQueryOptions(q) 216 r.params.Set("by", string(args.By)) 217 for _, name := range args.Names { 218 r.params.Add("name", name) 219 } 220 rtt, resp, err := requireOK(h.c.doRequest(r)) 221 if err != nil { 222 return nil, nil, err 223 } 224 defer resp.Body.Close() 225 226 qm := &QueryMeta{} 227 parseQueryMeta(resp, qm) 228 qm.RequestTime = rtt 229 230 var out map[string][]*Intention 231 if err := decodeBody(resp, &out); err != nil { 232 return nil, nil, err 233 } 234 return out, qm, nil 235 } 236 237 // IntentionCheck returns whether a given source/destination would be allowed 238 // or not given the current set of intentions and the configuration of Consul. 239 func (h *Connect) IntentionCheck(args *IntentionCheck, q *QueryOptions) (bool, *QueryMeta, error) { 240 r := h.c.newRequest("GET", "/v1/connect/intentions/check") 241 r.setQueryOptions(q) 242 r.params.Set("source", args.Source) 243 r.params.Set("destination", args.Destination) 244 if args.SourceType != "" { 245 r.params.Set("source-type", string(args.SourceType)) 246 } 247 rtt, resp, err := requireOK(h.c.doRequest(r)) 248 if err != nil { 249 return false, nil, err 250 } 251 defer resp.Body.Close() 252 253 qm := &QueryMeta{} 254 parseQueryMeta(resp, qm) 255 qm.RequestTime = rtt 256 257 var out struct{ Allowed bool } 258 if err := decodeBody(resp, &out); err != nil { 259 return false, nil, err 260 } 261 return out.Allowed, qm, nil 262 } 263 264 // IntentionCreate will create a new intention. The ID in the given 265 // structure must be empty and a generate ID will be returned on 266 // success. 267 func (c *Connect) IntentionCreate(ixn *Intention, q *WriteOptions) (string, *WriteMeta, error) { 268 r := c.c.newRequest("POST", "/v1/connect/intentions") 269 r.setWriteOptions(q) 270 r.obj = ixn 271 rtt, resp, err := requireOK(c.c.doRequest(r)) 272 if err != nil { 273 return "", nil, err 274 } 275 defer resp.Body.Close() 276 277 wm := &WriteMeta{} 278 wm.RequestTime = rtt 279 280 var out struct{ ID string } 281 if err := decodeBody(resp, &out); err != nil { 282 return "", nil, err 283 } 284 return out.ID, wm, nil 285 } 286 287 // IntentionUpdate will update an existing intention. The ID in the given 288 // structure must be non-empty. 289 func (c *Connect) IntentionUpdate(ixn *Intention, q *WriteOptions) (*WriteMeta, error) { 290 r := c.c.newRequest("PUT", "/v1/connect/intentions/"+ixn.ID) 291 r.setWriteOptions(q) 292 r.obj = ixn 293 rtt, resp, err := requireOK(c.c.doRequest(r)) 294 if err != nil { 295 return nil, err 296 } 297 defer resp.Body.Close() 298 299 wm := &WriteMeta{} 300 wm.RequestTime = rtt 301 return wm, nil 302 }