// Register is used register that a node is providing a given service. func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error { if done, err := c.srv.forward("Catalog.Register", args, args, reply); done { return err } defer metrics.MeasureSince([]string{"consul", "catalog", "register"}, time.Now()) // Verify the args if args.Node == "" || args.Address == "" { return fmt.Errorf("Must provide node and address") } if args.Service != nil { // If no service id, but service name, use default if args.Service.ID == "" && args.Service.Service != "" { args.Service.ID = args.Service.Service } // Verify ServiceName provided if ID if args.Service.ID != "" && args.Service.Service == "" { return fmt.Errorf("Must provide service name with ID") } // Apply the ACL policy if any // The 'consul' service is excluded since it is managed // automatically internally. if args.Service.Service != ConsulServiceName { acl, err := c.srv.resolveToken(args.Token) if err != nil { return err } else if acl != nil && !acl.ServiceWrite(args.Service.Service) { c.srv.logger.Printf("[WARN] consul.catalog: Register of service '%s' on '%s' denied due to ACLs", args.Service.Service, args.Node) return permissionDeniedErr } } } if args.Check != nil { args.Checks = append(args.Checks, args.Check) args.Check = nil } for _, check := range args.Checks { if check.CheckID == "" && check.Name != "" { check.CheckID = check.Name } if check.Node == "" { check.Node = args.Node } } _, err := c.srv.raftApply(structs.RegisterRequestType, args) if err != nil { c.srv.logger.Printf("[ERR] consul.catalog: Register failed: %v", err) return err } return nil }
// syncService is used to sync a service to the server func (l *localState) syncService(id string) error { req := structs.RegisterRequest{ Datacenter: l.config.Datacenter, Node: l.config.NodeName, Address: l.config.AdvertiseAddr, TaggedAddresses: l.config.TaggedAddresses, NodeMeta: l.metadata, Service: l.services[id], WriteRequest: structs.WriteRequest{Token: l.serviceToken(id)}, } // If the service has associated checks that are out of sync, // piggyback them on the service sync so they are part of the // same transaction and are registered atomically. var checks structs.HealthChecks for _, check := range l.checks { if check.ServiceID == id { if stat, ok := l.checkStatus[check.CheckID]; !ok || !stat.inSync { checks = append(checks, check) } } } // Backwards-compatibility for Consul < 0.5 if len(checks) == 1 { req.Check = checks[0] } else { req.Checks = checks } var out struct{} err := l.iface.RPC("Catalog.Register", &req, &out) if err == nil { l.serviceStatus[id] = syncStatus{inSync: true} // Given how the register API works, this info is also updated // every time we sync a service. l.nodeInfoInSync = true l.logger.Printf("[INFO] agent: Synced service '%s'", id) for _, check := range checks { l.checkStatus[check.CheckID] = syncStatus{inSync: true} } } else if strings.Contains(err.Error(), permissionDenied) { l.serviceStatus[id] = syncStatus{inSync: true} l.logger.Printf("[WARN] agent: Service '%s' registration blocked by ACLs", id) for _, check := range checks { l.checkStatus[check.CheckID] = syncStatus{inSync: true} } return nil } return err }
// Register is used register that a node is providing a given service. func (c *Catalog) Register(args *structs.RegisterRequest, reply *struct{}) error { if done, err := c.srv.forward("Catalog.Register", args, args, reply); done { return err } defer metrics.MeasureSince([]string{"consul", "catalog", "register"}, time.Now()) // Verify the args. if args.Node == "" || args.Address == "" { return fmt.Errorf("Must provide node and address") } // Fetch the ACL token, if any. acl, err := c.srv.resolveToken(args.Token) if err != nil { return err } // Handle a service registration. if args.Service != nil { // If no service id, but service name, use default if args.Service.ID == "" && args.Service.Service != "" { args.Service.ID = args.Service.Service } // Verify ServiceName provided if ID. if args.Service.ID != "" && args.Service.Service == "" { return fmt.Errorf("Must provide service name with ID") } // Apply the ACL policy if any. The 'consul' service is excluded // since it is managed automatically internally (that behavior // is going away after version 0.8). We check this same policy // later if version 0.8 is enabled, so we can eventually just // delete this and do all the ACL checks down there. if args.Service.Service != ConsulServiceName { if acl != nil && !acl.ServiceWrite(args.Service.Service) { return permissionDeniedErr } } } // Move the old format single check into the slice, and fixup IDs. if args.Check != nil { args.Checks = append(args.Checks, args.Check) args.Check = nil } for _, check := range args.Checks { if check.CheckID == "" && check.Name != "" { check.CheckID = types.CheckID(check.Name) } if check.Node == "" { check.Node = args.Node } } // Check the complete register request against the given ACL policy. if acl != nil && c.srv.config.ACLEnforceVersion8 { state := c.srv.fsm.State() _, ns, err := state.NodeServices(args.Node) if err != nil { return fmt.Errorf("Node lookup failed: %v", err) } if err := vetRegisterWithACL(acl, args, ns); err != nil { return err } } _, err = c.srv.raftApply(structs.RegisterRequestType, args) if err != nil { return err } return nil }