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 84// Catalog can be used to query the Catalog endpoints 85type Catalog struct { 86 c *Client 87} 88 89// Catalog returns a handle to the catalog endpoints 90func (c *Client) Catalog() *Catalog { 91 return &Catalog{c} 92} 93 94func (c *Catalog) Register(reg *CatalogRegistration, q *WriteOptions) (*WriteMeta, error) { 95 r := c.c.newRequest("PUT", "/v1/catalog/register") 96 r.setWriteOptions(q) 97 r.obj = reg 98 rtt, resp, err := requireOK(c.c.doRequest(r)) 99 if err != nil { 100 return nil, err 101 } 102 resp.Body.Close() 103 104 wm := &WriteMeta{} 105 wm.RequestTime = rtt 106 107 return wm, nil 108} 109 110func (c *Catalog) Deregister(dereg *CatalogDeregistration, q *WriteOptions) (*WriteMeta, error) { 111 r := c.c.newRequest("PUT", "/v1/catalog/deregister") 112 r.setWriteOptions(q) 113 r.obj = dereg 114 rtt, resp, err := requireOK(c.c.doRequest(r)) 115 if err != nil { 116 return nil, err 117 } 118 resp.Body.Close() 119 120 wm := &WriteMeta{} 121 wm.RequestTime = rtt 122 123 return wm, nil 124} 125 126// Datacenters is used to query for all the known datacenters 127func (c *Catalog) Datacenters() ([]string, error) { 128 r := c.c.newRequest("GET", "/v1/catalog/datacenters") 129 _, resp, err := requireOK(c.c.doRequest(r)) 130 if err != nil { 131 return nil, err 132 } 133 defer resp.Body.Close() 134 135 var out []string 136 if err := decodeBody(resp, &out); err != nil { 137 return nil, err 138 } 139 return out, nil 140} 141 142// Nodes is used to query all the known nodes 143func (c *Catalog) Nodes(q *QueryOptions) ([]*Node, *QueryMeta, error) { 144 r := c.c.newRequest("GET", "/v1/catalog/nodes") 145 r.setQueryOptions(q) 146 rtt, resp, err := requireOK(c.c.doRequest(r)) 147 if err != nil { 148 return nil, nil, err 149 } 150 defer resp.Body.Close() 151 152 qm := &QueryMeta{} 153 parseQueryMeta(resp, qm) 154 qm.RequestTime = rtt 155 156 var out []*Node 157 if err := decodeBody(resp, &out); err != nil { 158 return nil, nil, err 159 } 160 return out, qm, nil 161} 162 163// Services is used to query for all known services 164func (c *Catalog) Services(q *QueryOptions) (map[string][]string, *QueryMeta, error) { 165 r := c.c.newRequest("GET", "/v1/catalog/services") 166 r.setQueryOptions(q) 167 rtt, resp, err := requireOK(c.c.doRequest(r)) 168 if err != nil { 169 return nil, nil, err 170 } 171 defer resp.Body.Close() 172 173 qm := &QueryMeta{} 174 parseQueryMeta(resp, qm) 175 qm.RequestTime = rtt 176 177 var out map[string][]string 178 if err := decodeBody(resp, &out); err != nil { 179 return nil, nil, err 180 } 181 return out, qm, nil 182} 183 184// Service is used to query catalog entries for a given service 185func (c *Catalog) Service(service, tag string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) { 186 var tags []string 187 if tag != "" { 188 tags = []string{tag} 189 } 190 return c.service(service, tags, q, false) 191} 192 193// Supports multiple tags for filtering 194func (c *Catalog) ServiceMultipleTags(service string, tags []string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) { 195 return c.service(service, tags, q, false) 196} 197 198// Connect is used to query catalog entries for a given Connect-enabled service 199func (c *Catalog) Connect(service, tag string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) { 200 var tags []string 201 if tag != "" { 202 tags = []string{tag} 203 } 204 return c.service(service, tags, q, true) 205} 206 207// Supports multiple tags for filtering 208func (c *Catalog) ConnectMultipleTags(service string, tags []string, q *QueryOptions) ([]*CatalogService, *QueryMeta, error) { 209 return c.service(service, tags, q, true) 210} 211 212func (c *Catalog) service(service string, tags []string, q *QueryOptions, connect bool) ([]*CatalogService, *QueryMeta, error) { 213 path := "/v1/catalog/service/" + service 214 if connect { 215 path = "/v1/catalog/connect/" + service 216 } 217 r := c.c.newRequest("GET", path) 218 r.setQueryOptions(q) 219 if len(tags) > 0 { 220 for _, tag := range tags { 221 r.params.Add("tag", tag) 222 } 223 } 224 rtt, resp, err := requireOK(c.c.doRequest(r)) 225 if err != nil { 226 return nil, nil, err 227 } 228 defer resp.Body.Close() 229 230 qm := &QueryMeta{} 231 parseQueryMeta(resp, qm) 232 qm.RequestTime = rtt 233 234 var out []*CatalogService 235 if err := decodeBody(resp, &out); err != nil { 236 return nil, nil, err 237 } 238 return out, qm, nil 239} 240 241// Node is used to query for service information about a single node 242func (c *Catalog) Node(node string, q *QueryOptions) (*CatalogNode, *QueryMeta, error) { 243 r := c.c.newRequest("GET", "/v1/catalog/node/"+node) 244 r.setQueryOptions(q) 245 rtt, resp, err := requireOK(c.c.doRequest(r)) 246 if err != nil { 247 return nil, nil, err 248 } 249 defer resp.Body.Close() 250 251 qm := &QueryMeta{} 252 parseQueryMeta(resp, qm) 253 qm.RequestTime = rtt 254 255 var out *CatalogNode 256 if err := decodeBody(resp, &out); err != nil { 257 return nil, nil, err 258 } 259 return out, qm, nil 260} 261 262// NodeServiceList is used to query for service information about a single node. It differs from 263// the Node function only in its return type which will contain a list of services as opposed to 264// a map of service ids to services. This different structure allows for using the wildcard specifier 265// '*' for the Namespace in the QueryOptions. 266func (c *Catalog) NodeServiceList(node string, q *QueryOptions) (*CatalogNodeServiceList, *QueryMeta, error) { 267 r := c.c.newRequest("GET", "/v1/catalog/node-services/"+node) 268 r.setQueryOptions(q) 269 rtt, resp, err := requireOK(c.c.doRequest(r)) 270 if err != nil { 271 return nil, nil, err 272 } 273 defer resp.Body.Close() 274 275 qm := &QueryMeta{} 276 parseQueryMeta(resp, qm) 277 qm.RequestTime = rtt 278 279 var out *CatalogNodeServiceList 280 if err := decodeBody(resp, &out); err != nil { 281 return nil, nil, err 282 } 283 return out, qm, nil 284} 285 286func ParseServiceAddr(addrPort string) (ServiceAddress, error) { 287 port := 0 288 host, portStr, err := net.SplitHostPort(addrPort) 289 if err == nil { 290 port, err = strconv.Atoi(portStr) 291 } 292 return ServiceAddress{Address: host, Port: port}, err 293} 294