1package api 2 3import ( 4 "net" 5 "strconv" 6) 7 8type Weights struct { 9 Passing int 10 Warning int 11} 12 13type Node struct { 14 ID string 15 Node string 16 Address string 17 Datacenter string 18 TaggedAddresses map[string]string 19 Meta map[string]string 20 CreateIndex uint64 21 ModifyIndex uint64 22} 23 24type ServiceAddress struct { 25 Address string 26 Port int 27} 28 29type CatalogService struct { 30 ID string 31 Node string 32 Address string 33 Datacenter string 34 TaggedAddresses map[string]string 35 NodeMeta map[string]string 36 ServiceID string 37 ServiceName string 38 ServiceAddress string 39 ServiceTaggedAddresses map[string]ServiceAddress 40 ServiceTags []string 41 ServiceMeta map[string]string 42 ServicePort int 43 ServiceWeights Weights 44 ServiceEnableTagOverride bool 45 ServiceProxy *AgentServiceConnectProxyConfig 46 CreateIndex uint64 47 Checks HealthChecks 48 ModifyIndex uint64 49 Namespace string `json:",omitempty"` 50} 51 52type CatalogNode struct { 53 Node *Node 54 Services map[string]*AgentService 55} 56 57type CatalogNodeServiceList struct { 58 Node *Node 59 Services []*AgentService 60} 61 62type CatalogRegistration struct { 63 ID string 64 Node string 65 Address string 66 TaggedAddresses map[string]string 67 NodeMeta map[string]string 68 Datacenter string 69 Service *AgentService 70 Check *AgentCheck 71 Checks HealthChecks 72 SkipNodeUpdate bool 73} 74 75type CatalogDeregistration struct { 76 Node string 77 Address string `json:",omitempty"` // Obsolete. 78 Datacenter string 79 ServiceID string 80 CheckID string 81 Namespace string `json:",omitempty"` 82} 83 84type CompoundServiceName struct { 85 Name string 86 87 // Namespacing is a Consul Enterprise feature. 88 Namespace string `json:",omitempty"` 89} 90 91// GatewayService associates a gateway with a linked service. 92// It also contains service-specific gateway configuration like ingress listener port and protocol. 93type GatewayService struct { 94 Gateway CompoundServiceName 95 Service CompoundServiceName 96 GatewayKind ServiceKind 97 Port int `json:",omitempty"` 98 Protocol string `json:",omitempty"` 99 Hosts []string `json:",omitempty"` 100 CAFile string `json:",omitempty"` 101 CertFile string `json:",omitempty"` 102 KeyFile string `json:",omitempty"` 103 SNI string `json:",omitempty"` 104 FromWildcard bool `json:",omitempty"` 105} 106 107// Catalog can be used to query the Catalog endpoints 108type Catalog struct { 109 c *Client 110} 111 112// Catalog returns a handle to the catalog endpoints 113func (c *Client) Catalog() *Catalog { 114 return &Catalog{c} 115} 116 117func (c *Catalog) Register(reg *CatalogRegistration, q *WriteOptions) (*WriteMeta, error) { 118 r := c.c.newRequest("PUT", "/v1/catalog/register") 119 r.setWriteOptions(q) 120 r.obj = reg 121 rtt, resp, err := requireOK(c.c.doRequest(r)) 122 if err != nil { 123 return nil, err 124 } 125 closeResponseBody(resp) 126 127 wm := &WriteMeta{} 128 wm.RequestTime = rtt 129 130 return wm, nil 131} 132 133func (c *Catalog) Deregister(dereg *CatalogDeregistration, q *WriteOptions) (*WriteMeta, error) { 134 r := c.c.newRequest("PUT", "/v1/catalog/deregister") 135 r.setWriteOptions(q) 136 r.obj = dereg 137 rtt, resp, err := requireOK(c.c.doRequest(r)) 138 if err != nil { 139 return nil, err 140 } 141 closeResponseBody(resp) 142 143 wm := &WriteMeta{} 144 wm.RequestTime = rtt 145 146 return wm, nil 147} 148 149// Datacenters is used to query for all the known datacenters 150func (c *Catalog) Datacenters() ([]string, error) { 151 r := c.c.newRequest("GET", "/v1/catalog/datacenters") 152 _, resp, err := requireOK(c.c.doRequest(r)) 153 if err != nil { 154 return nil, err 155 } 156 defer closeResponseBody(resp) 157 158 var out []string 159 if err := decodeBody(resp, &out); err != nil { 160 return nil, err 161 } 162 return out, nil 163} 164 165// Nodes is used to query all the known nodes 166func (c *Catalog) Nodes(q *QueryOptions) ([]*Node, *QueryMeta, error) { 167 r := c.c.newRequest("GET", "/v1/catalog/nodes") 168 r.setQueryOptions(q) 169 rtt, resp, err := requireOK(c.c.doRequest(r)) 170 if err != nil { 171 return nil, nil, err 172 } 173 defer closeResponseBody(resp) 174 175 qm := &QueryMeta{} 176 parseQueryMeta(resp, qm) 177 qm.RequestTime = rtt 178 179 var out []*Node 180 if err := decodeBody(resp, &out); err != nil { 181 return nil, nil, err 182 } 183 return out, qm, nil 184} 185 186// Services is used to query for all known services 187func (c *Catalog) Services(q *QueryOptions) (map[string][]string, *QueryMeta, error) { 188 r := c.c.newRequest("GET", "/v1/catalog/services") 189 r.setQueryOptions(q) 190 rtt, resp, err := requireOK(c.c.doRequest(r)) 191 if err != nil { 192 return nil, nil, err 193 } 194 defer closeResponseBody(resp) 195 196 qm := &QueryMeta{} 197 parseQueryMeta(resp, qm) 198 qm.RequestTime = rtt 199 200 var out map[string][]string 201 if err := decodeBody(resp, &out); err != nil { 202 return nil, nil, err 203 } 204 return out, qm, nil 205} 206 207// Service is used to query catalog entries for a given service 208func (c *Catalog) Service(service, tag string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) { 209 var tags []string 210 if tag != "" { 211 tags = []string{tag} 212 } 213 return c.service(service, tags, q, false) 214} 215 216// Supports multiple tags for filtering 217func (c *Catalog) ServiceMultipleTags(service string, tags []string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) { 218 return c.service(service, tags, q, false) 219} 220 221// Connect is used to query catalog entries for a given Connect-enabled service 222func (c *Catalog) Connect(service, tag string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) { 223 var tags []string 224 if tag != "" { 225 tags = []string{tag} 226 } 227 return c.service(service, tags, q, true) 228} 229 230// Supports multiple tags for filtering 231func (c *Catalog) ConnectMultipleTags(service string, tags []string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) { 232 return c.service(service, tags, q, true) 233} 234 235func (c *Catalog) service(service string, tags []string, q *QueryOptions, connect bool) ([]*CatalogService, *QueryMeta, error) { 236 path := "/v1/catalog/service/" + service 237 if connect { 238 path = "/v1/catalog/connect/" + service 239 } 240 r := c.c.newRequest("GET", path) 241 r.setQueryOptions(q) 242 if len(tags) > 0 { 243 for _, tag := range tags { 244 r.params.Add("tag", tag) 245 } 246 } 247 rtt, resp, err := requireOK(c.c.doRequest(r)) 248 if err != nil { 249 return nil, nil, err 250 } 251 defer closeResponseBody(resp) 252 253 qm := &QueryMeta{} 254 parseQueryMeta(resp, qm) 255 qm.RequestTime = rtt 256 257 var out []*CatalogService 258 if err := decodeBody(resp, &out); err != nil { 259 return nil, nil, err 260 } 261 return out, qm, nil 262} 263 264// Node is used to query for service information about a single node 265func (c *Catalog) Node(node string, q *QueryOptions) (*CatalogNode, *QueryMeta, error) { 266 r := c.c.newRequest("GET", "/v1/catalog/node/"+node) 267 r.setQueryOptions(q) 268 rtt, resp, err := requireOK(c.c.doRequest(r)) 269 if err != nil { 270 return nil, nil, err 271 } 272 defer closeResponseBody(resp) 273 274 qm := &QueryMeta{} 275 parseQueryMeta(resp, qm) 276 qm.RequestTime = rtt 277 278 var out *CatalogNode 279 if err := decodeBody(resp, &out); err != nil { 280 return nil, nil, err 281 } 282 return out, qm, nil 283} 284 285// NodeServiceList is used to query for service information about a single node. It differs from 286// the Node function only in its return type which will contain a list of services as opposed to 287// a map of service ids to services. This different structure allows for using the wildcard specifier 288// '*' for the Namespace in the QueryOptions. 289func (c *Catalog) NodeServiceList(node string, q *QueryOptions) (*CatalogNodeServiceList, *QueryMeta, error) { 290 r := c.c.newRequest("GET", "/v1/catalog/node-services/"+node) 291 r.setQueryOptions(q) 292 rtt, resp, err := requireOK(c.c.doRequest(r)) 293 if err != nil { 294 return nil, nil, err 295 } 296 defer closeResponseBody(resp) 297 298 qm := &QueryMeta{} 299 parseQueryMeta(resp, qm) 300 qm.RequestTime = rtt 301 302 var out *CatalogNodeServiceList 303 if err := decodeBody(resp, &out); err != nil { 304 return nil, nil, err 305 } 306 return out, qm, nil 307} 308 309// GatewayServices is used to query the services associated with an ingress gateway or terminating gateway. 310func (c *Catalog) GatewayServices(gateway string, q *QueryOptions) ([]*GatewayService, *QueryMeta, error) { 311 r := c.c.newRequest("GET", "/v1/catalog/gateway-services/"+gateway) 312 r.setQueryOptions(q) 313 rtt, resp, err := requireOK(c.c.doRequest(r)) 314 if err != nil { 315 return nil, nil, err 316 } 317 defer closeResponseBody(resp) 318 319 qm := &QueryMeta{} 320 parseQueryMeta(resp, qm) 321 qm.RequestTime = rtt 322 323 var out []*GatewayService 324 if err := decodeBody(resp, &out); err != nil { 325 return nil, nil, err 326 } 327 return out, qm, nil 328} 329 330func ParseServiceAddr(addrPort string) (ServiceAddress, error) { 331 port := 0 332 host, portStr, err := net.SplitHostPort(addrPort) 333 if err == nil { 334 port, err = strconv.Atoi(portStr) 335 } 336 return ServiceAddress{Address: host, Port: port}, err 337} 338