github.com/clly/consul@v1.4.5/agent/consul/catalog_endpoint.go (about) 1 package consul 2 3 import ( 4 "fmt" 5 "sort" 6 "time" 7 8 "github.com/armon/go-metrics" 9 "github.com/hashicorp/consul/acl" 10 "github.com/hashicorp/consul/agent/consul/state" 11 "github.com/hashicorp/consul/agent/structs" 12 "github.com/hashicorp/consul/ipaddr" 13 "github.com/hashicorp/consul/types" 14 "github.com/hashicorp/go-memdb" 15 "github.com/hashicorp/go-uuid" 16 ) 17 18 // Catalog endpoint is used to manipulate the service catalog 19 type Catalog struct { 20 srv *Server 21 } 22 23 // nodePreApply does the verification of a node before it is applied to Raft. 24 func nodePreApply(nodeName, nodeID string) error { 25 if nodeName == "" { 26 return fmt.Errorf("Must provide node") 27 } 28 if nodeID != "" { 29 if _, err := uuid.ParseUUID(nodeID); err != nil { 30 return fmt.Errorf("Bad node ID: %v", err) 31 } 32 } 33 34 return nil 35 } 36 37 func servicePreApply(service *structs.NodeService, rule acl.Authorizer) error { 38 // Validate the service. This is in addition to the below since 39 // the above just hasn't been moved over yet. We should move it over 40 // in time. 41 if err := service.Validate(); err != nil { 42 return err 43 } 44 45 // If no service id, but service name, use default 46 if service.ID == "" && service.Service != "" { 47 service.ID = service.Service 48 } 49 50 // Verify ServiceName provided if ID. 51 if service.ID != "" && service.Service == "" { 52 return fmt.Errorf("Must provide service name with ID") 53 } 54 55 // Check the service address here and in the agent endpoint 56 // since service registration isn't synchronous. 57 if ipaddr.IsAny(service.Address) { 58 return fmt.Errorf("Invalid service address") 59 } 60 61 // Apply the ACL policy if any. The 'consul' service is excluded 62 // since it is managed automatically internally (that behavior 63 // is going away after version 0.8). We check this same policy 64 // later if version 0.8 is enabled, so we can eventually just 65 // delete this and do all the ACL checks down there. 66 if service.Service != structs.ConsulServiceName { 67 if rule != nil && !rule.ServiceWrite(service.Service, nil) { 68 return acl.ErrPermissionDenied 69 } 70 } 71 72 // Proxies must have write permission on their destination 73 if service.Kind == structs.ServiceKindConnectProxy { 74 if rule != nil && !rule.ServiceWrite(service.Proxy.DestinationServiceName, nil) { 75 return acl.ErrPermissionDenied 76 } 77 } 78 79 return nil 80 } 81 82 // checkPreApply does the verification of a check before it is applied to Raft. 83 func checkPreApply(check *structs.HealthCheck) { 84 if check.CheckID == "" && check.Name != "" { 85 check.CheckID = types.CheckID(check.Name) 86 } 87 } 88 89 // Register is used register that a node is providing a given service. 90 func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error { 91 if done, err := c.srv.forward("Catalog.Register", args, args, reply); done { 92 return err 93 } 94 defer metrics.MeasureSince([]string{"catalog", "register"}, time.Now()) 95 96 // Fetch the ACL token, if any. 97 rule, err := c.srv.ResolveToken(args.Token) 98 if err != nil { 99 return err 100 } 101 102 // Verify the args. 103 if err := nodePreApply(args.Node, string(args.ID)); err != nil { 104 return err 105 } 106 if args.Address == "" && !args.SkipNodeUpdate { 107 return fmt.Errorf("Must provide address if SkipNodeUpdate is not set") 108 } 109 110 // Handle a service registration. 111 if args.Service != nil { 112 if err := servicePreApply(args.Service, rule); err != nil { 113 return err 114 } 115 } 116 117 // Move the old format single check into the slice, and fixup IDs. 118 if args.Check != nil { 119 args.Checks = append(args.Checks, args.Check) 120 args.Check = nil 121 } 122 for _, check := range args.Checks { 123 if check.Node == "" { 124 check.Node = args.Node 125 } 126 checkPreApply(check) 127 } 128 129 // Check the complete register request against the given ACL policy. 130 if rule != nil && c.srv.config.ACLEnforceVersion8 { 131 state := c.srv.fsm.State() 132 _, ns, err := state.NodeServices(nil, args.Node) 133 if err != nil { 134 return fmt.Errorf("Node lookup failed: %v", err) 135 } 136 if err := vetRegisterWithACL(rule, args, ns); err != nil { 137 return err 138 } 139 } 140 141 resp, err := c.srv.raftApply(structs.RegisterRequestType, args) 142 if err != nil { 143 return err 144 } 145 if respErr, ok := resp.(error); ok { 146 return respErr 147 } 148 return nil 149 } 150 151 // Deregister is used to remove a service registration for a given node. 152 func (c *Catalog) Deregister(args *structs.DeregisterRequest, reply *struct{}) error { 153 if done, err := c.srv.forward("Catalog.Deregister", args, args, reply); done { 154 return err 155 } 156 defer metrics.MeasureSince([]string{"catalog", "deregister"}, time.Now()) 157 158 // Verify the args 159 if args.Node == "" { 160 return fmt.Errorf("Must provide node") 161 } 162 163 // Fetch the ACL token, if any. 164 rule, err := c.srv.ResolveToken(args.Token) 165 if err != nil { 166 return err 167 } 168 169 // Check the complete deregister request against the given ACL policy. 170 if rule != nil && c.srv.config.ACLEnforceVersion8 { 171 state := c.srv.fsm.State() 172 173 var ns *structs.NodeService 174 if args.ServiceID != "" { 175 _, ns, err = state.NodeService(args.Node, args.ServiceID) 176 if err != nil { 177 return fmt.Errorf("Service lookup failed: %v", err) 178 } 179 } 180 181 var nc *structs.HealthCheck 182 if args.CheckID != "" { 183 _, nc, err = state.NodeCheck(args.Node, args.CheckID) 184 if err != nil { 185 return fmt.Errorf("Check lookup failed: %v", err) 186 } 187 } 188 189 if err := vetDeregisterWithACL(rule, args, ns, nc); err != nil { 190 return err 191 } 192 193 } 194 195 if _, err := c.srv.raftApply(structs.DeregisterRequestType, args); err != nil { 196 return err 197 } 198 return nil 199 } 200 201 // ListDatacenters is used to query for the list of known datacenters 202 func (c *Catalog) ListDatacenters(args *struct{}, reply *[]string) error { 203 dcs, err := c.srv.router.GetDatacentersByDistance() 204 if err != nil { 205 return err 206 } 207 208 if len(dcs) == 0 { // no WAN federation, so return the local data center name 209 dcs = []string{c.srv.config.Datacenter} 210 } 211 212 *reply = dcs 213 return nil 214 } 215 216 // ListNodes is used to query the nodes in a DC 217 func (c *Catalog) ListNodes(args *structs.DCSpecificRequest, reply *structs.IndexedNodes) error { 218 if done, err := c.srv.forward("Catalog.ListNodes", args, args, reply); done { 219 return err 220 } 221 222 return c.srv.blockingQuery( 223 &args.QueryOptions, 224 &reply.QueryMeta, 225 func(ws memdb.WatchSet, state *state.Store) error { 226 var index uint64 227 var nodes structs.Nodes 228 var err error 229 if len(args.NodeMetaFilters) > 0 { 230 index, nodes, err = state.NodesByMeta(ws, args.NodeMetaFilters) 231 } else { 232 index, nodes, err = state.Nodes(ws) 233 } 234 if err != nil { 235 return err 236 } 237 238 reply.Index, reply.Nodes = index, nodes 239 if err := c.srv.filterACL(args.Token, reply); err != nil { 240 return err 241 } 242 return c.srv.sortNodesByDistanceFrom(args.Source, reply.Nodes) 243 }) 244 } 245 246 // ListServices is used to query the services in a DC 247 func (c *Catalog) ListServices(args *structs.DCSpecificRequest, reply *structs.IndexedServices) error { 248 if done, err := c.srv.forward("Catalog.ListServices", args, args, reply); done { 249 return err 250 } 251 252 return c.srv.blockingQuery( 253 &args.QueryOptions, 254 &reply.QueryMeta, 255 func(ws memdb.WatchSet, state *state.Store) error { 256 var index uint64 257 var services structs.Services 258 var err error 259 if len(args.NodeMetaFilters) > 0 { 260 index, services, err = state.ServicesByNodeMeta(ws, args.NodeMetaFilters) 261 } else { 262 index, services, err = state.Services(ws) 263 } 264 if err != nil { 265 return err 266 } 267 268 reply.Index, reply.Services = index, services 269 return c.srv.filterACL(args.Token, reply) 270 }) 271 } 272 273 // ServiceNodes returns all the nodes registered as part of a service 274 func (c *Catalog) ServiceNodes(args *structs.ServiceSpecificRequest, reply *structs.IndexedServiceNodes) error { 275 if done, err := c.srv.forward("Catalog.ServiceNodes", args, args, reply); done { 276 return err 277 } 278 279 // Verify the arguments 280 if args.ServiceName == "" && args.ServiceAddress == "" { 281 return fmt.Errorf("Must provide service name") 282 } 283 284 // Determine the function we'll call 285 var f func(memdb.WatchSet, *state.Store) (uint64, structs.ServiceNodes, error) 286 switch { 287 case args.Connect: 288 f = func(ws memdb.WatchSet, s *state.Store) (uint64, structs.ServiceNodes, error) { 289 return s.ConnectServiceNodes(ws, args.ServiceName) 290 } 291 292 default: 293 f = func(ws memdb.WatchSet, s *state.Store) (uint64, structs.ServiceNodes, error) { 294 if args.ServiceAddress != "" { 295 return s.ServiceAddressNodes(ws, args.ServiceAddress) 296 } 297 298 if args.TagFilter { 299 tags := args.ServiceTags 300 // DEPRECATED (singular-service-tag) - remove this when backwards RPC compat 301 // with 1.2.x is not required. 302 // Agents < v1.3.0 populate the ServiceTag field. In this case, 303 // use ServiceTag instead of the ServiceTags field. 304 if args.ServiceTag != "" { 305 tags = []string{args.ServiceTag} 306 } 307 308 return s.ServiceTagNodes(ws, args.ServiceName, tags) 309 } 310 311 return s.ServiceNodes(ws, args.ServiceName) 312 } 313 } 314 315 // If we're doing a connect query, we need read access to the service 316 // we're trying to find proxies for, so check that. 317 if args.Connect { 318 // Fetch the ACL token, if any. 319 rule, err := c.srv.ResolveToken(args.Token) 320 if err != nil { 321 return err 322 } 323 324 if rule != nil && !rule.ServiceRead(args.ServiceName) { 325 // Just return nil, which will return an empty response (tested) 326 return nil 327 } 328 } 329 330 err := c.srv.blockingQuery( 331 &args.QueryOptions, 332 &reply.QueryMeta, 333 func(ws memdb.WatchSet, state *state.Store) error { 334 index, services, err := f(ws, state) 335 if err != nil { 336 return err 337 } 338 339 reply.Index, reply.ServiceNodes = index, services 340 if len(args.NodeMetaFilters) > 0 { 341 var filtered structs.ServiceNodes 342 for _, service := range services { 343 if structs.SatisfiesMetaFilters(service.NodeMeta, args.NodeMetaFilters) { 344 filtered = append(filtered, service) 345 } 346 } 347 reply.ServiceNodes = filtered 348 } 349 if err := c.srv.filterACL(args.Token, reply); err != nil { 350 return err 351 } 352 return c.srv.sortNodesByDistanceFrom(args.Source, reply.ServiceNodes) 353 }) 354 355 // Provide some metrics 356 if err == nil { 357 // For metrics, we separate Connect-based lookups from non-Connect 358 key := "service" 359 if args.Connect { 360 key = "connect" 361 } 362 363 metrics.IncrCounterWithLabels([]string{"catalog", key, "query"}, 1, 364 []metrics.Label{{Name: "service", Value: args.ServiceName}}) 365 // DEPRECATED (singular-service-tag) - remove this when backwards RPC compat 366 // with 1.2.x is not required. 367 if args.ServiceTag != "" { 368 metrics.IncrCounterWithLabels([]string{"catalog", key, "query-tag"}, 1, 369 []metrics.Label{{Name: "service", Value: args.ServiceName}, {Name: "tag", Value: args.ServiceTag}}) 370 } 371 if len(args.ServiceTags) > 0 { 372 // Sort tags so that the metric is the same even if the request 373 // tags are in a different order 374 sort.Strings(args.ServiceTags) 375 376 // Build metric labels 377 labels := []metrics.Label{{Name: "service", Value: args.ServiceName}} 378 for _, tag := range args.ServiceTags { 379 labels = append(labels, metrics.Label{Name: "tag", Value: tag}) 380 } 381 metrics.IncrCounterWithLabels([]string{"catalog", key, "query-tags"}, 1, labels) 382 } 383 if len(reply.ServiceNodes) == 0 { 384 metrics.IncrCounterWithLabels([]string{"catalog", key, "not-found"}, 1, 385 []metrics.Label{{Name: "service", Value: args.ServiceName}}) 386 } 387 } 388 389 return err 390 } 391 392 // NodeServices returns all the services registered as part of a node 393 func (c *Catalog) NodeServices(args *structs.NodeSpecificRequest, reply *structs.IndexedNodeServices) error { 394 if done, err := c.srv.forward("Catalog.NodeServices", args, args, reply); done { 395 return err 396 } 397 398 // Verify the arguments 399 if args.Node == "" { 400 return fmt.Errorf("Must provide node") 401 } 402 403 return c.srv.blockingQuery( 404 &args.QueryOptions, 405 &reply.QueryMeta, 406 func(ws memdb.WatchSet, state *state.Store) error { 407 index, services, err := state.NodeServices(ws, args.Node) 408 if err != nil { 409 return err 410 } 411 412 reply.Index, reply.NodeServices = index, services 413 return c.srv.filterACL(args.Token, reply) 414 }) 415 }