github.com/moleculer-go/moleculer@v0.3.3/registry/nodeService.go (about) 1 package registry 2 3 import ( 4 "strings" 5 "time" 6 7 "github.com/moleculer-go/moleculer" 8 "github.com/moleculer-go/moleculer/service" 9 ) 10 11 // createNodeService create the local node service -> $node. 12 func createNodeService(registry *ServiceRegistry) *service.Service { 13 var startedTime time.Time 14 isAvailable := func(nodeID string) bool { 15 node, exists := registry.nodes.findNode(nodeID) 16 return exists && node.IsAvailable() 17 } 18 19 isLocal := func(nodeID string) bool { 20 return registry.localNode.GetID() == nodeID 21 } 22 23 without := func(in map[string]interface{}, key string) map[string]interface{} { 24 delete(in, key) 25 return in 26 } 27 return service.FromSchema(moleculer.ServiceSchema{ 28 Name: "$node", 29 Started: func(moleculer.BrokerContext, moleculer.ServiceSchema) { 30 startedTime = time.Now() 31 }, 32 Actions: []moleculer.Action{ 33 { 34 Name: "events", 35 Handler: func(context moleculer.Context, params moleculer.Payload) interface{} { 36 onlyLocal := params.Get("onlyLocal").Exists() && params.Get("onlyLocal").Bool() 37 onlyAvailable := params.Get("onlyAvailable").Exists() && params.Get("onlyAvailable").Bool() 38 skipInternal := params.Get("skipInternal").Exists() && params.Get("skipInternal").Bool() 39 withEndpoints := params.Get("withEndpoints").Exists() && params.Get("withEndpoints").Bool() 40 41 result := make([]map[string]interface{}, 0) 42 for name, entries := range registry.events.listByName() { 43 has := func(check func(nodeID string) bool) bool { 44 for _, item := range entries { 45 if check(item.service.NodeID()) { 46 return true 47 } 48 } 49 return false 50 } 51 endpoints := func() []map[string]interface{} { 52 list := make([]map[string]interface{}, 0) 53 for _, item := range entries { 54 nodeID := item.service.NodeID() 55 list = append(list, map[string]interface{}{ 56 "nodeID": nodeID, 57 "available": isAvailable(nodeID), 58 }) 59 } 60 return list 61 } 62 if onlyLocal && !has(isLocal) { 63 continue 64 } 65 if onlyAvailable && !has(isAvailable) { 66 continue 67 } 68 if skipInternal && strings.Index(name, "$") == 0 { 69 continue 70 } 71 item := map[string]interface{}{ 72 "name": name, 73 "group": entries[0].event.Group(), 74 "count": len(entries), 75 "hasLocal": has(isLocal), 76 "available": has(isAvailable), 77 } 78 if withEndpoints { 79 item["endpoints"] = endpoints() 80 } 81 result = append(result, item) 82 } 83 84 return result 85 }, 86 }, 87 { 88 Name: "actions", 89 Description: "Find and return a list of actions in the registry of this service broker.", 90 Handler: func(context moleculer.Context, params moleculer.Payload) interface{} { 91 onlyLocal := params.Get("onlyLocal").Exists() && params.Get("onlyLocal").Bool() 92 onlyAvailable := params.Get("onlyAvailable").Exists() && params.Get("onlyAvailable").Bool() 93 skipInternal := params.Get("skipInternal").Exists() && params.Get("skipInternal").Bool() 94 withEndpoints := params.Get("withEndpoints").Exists() && params.Get("withEndpoints").Bool() 95 96 result := make([]map[string]interface{}, 0) 97 for name, entries := range registry.actions.listByName() { 98 has := func(check func(nodeID string) bool) bool { 99 for _, item := range entries { 100 if check(item.service.NodeID()) { 101 return true 102 } 103 } 104 return false 105 } 106 endpoints := func() []map[string]interface{} { 107 list := make([]map[string]interface{}, 0) 108 for _, item := range entries { 109 list = append(list, map[string]interface{}{ 110 "nodeID": item.service.NodeID(), 111 "available": isAvailable(item.service.NodeID()), 112 }) 113 } 114 return list 115 } 116 context.Logger().Debug("$node.actions name: ", name) 117 if onlyLocal && !has(isLocal) { 118 continue 119 } 120 if onlyAvailable && !has(isAvailable) { 121 continue 122 } 123 if skipInternal && strings.Index(name, "$") == 0 { 124 continue 125 } 126 item := map[string]interface{}{ 127 "name": name, 128 "count": len(entries), 129 "hasLocal": has(isLocal), 130 "available": has(isAvailable), 131 } 132 if withEndpoints { 133 item["endpoints"] = endpoints() 134 } 135 result = append(result, item) 136 context.Logger().Debug("$node.actions name: ", name, " contents: ", item) 137 } 138 139 return result 140 }, 141 }, 142 { 143 Name: "services", 144 Description: "Find and return a list of services in the registry of this service broker.", 145 Schema: moleculer.ObjectSchema{ 146 Source: struct { 147 withActions bool 148 withEndpoints bool 149 withEvents bool `required:"true"` 150 skipInternal bool 151 onlyAvailable bool 152 onlyLocal bool 153 result []map[string]interface{} 154 }{withActions: true}, 155 }, 156 Handler: func(context moleculer.Context, params moleculer.Payload) interface{} { 157 context.Logger().Debug("$node.services params: ", params.Value()) 158 159 //TODO simplify this by removing the .Exists() check once we have action schema validation and default values assignment. 160 skipInternal := params.Get("skipInternal").Exists() && params.Get("skipInternal").Bool() 161 onlyAvailable := params.Get("onlyAvailable").Exists() && params.Get("onlyAvailable").Bool() 162 onlyLocal := params.Get("onlyLocal").Exists() && params.Get("onlyLocal").Bool() 163 withActions := params.Get("withActions").Exists() && params.Get("withActions").Bool() 164 withEvents := params.Get("withEvents").Exists() && params.Get("withEvents").Bool() 165 withEndpoints := params.Get("withEndpoints").Exists() && params.Get("withEndpoints").Bool() 166 167 result := make([]map[string]interface{}, 0) 168 for name, entries := range registry.services.listByName() { 169 has := func(check func(nodeID string) bool) bool { 170 for _, item := range entries { 171 if check(item.nodeID) { 172 return true 173 } 174 } 175 return false 176 } 177 endpoints := func() []map[string]interface{} { 178 list := make([]map[string]interface{}, 0) 179 for _, item := range entries { 180 list = append(list, map[string]interface{}{ 181 "nodeID": item.nodeID, 182 "available": isAvailable(item.nodeID), 183 }) 184 } 185 return list 186 } 187 if onlyLocal && !has(isLocal) { 188 continue 189 } 190 if onlyAvailable && !has(isAvailable) { 191 continue 192 } 193 if skipInternal && strings.Index(name, "$") == 0 { 194 continue 195 } 196 smap := entries[0].service.AsMap() 197 smap["available"] = has(isAvailable) 198 smap["hasLocal"] = has(isLocal) 199 smap = without(smap, "nodeID") 200 if withEndpoints { 201 smap["endpoints"] = endpoints() 202 } 203 if !withActions { 204 smap = without(smap, "actions") 205 } 206 if !withEvents { 207 smap = without(smap, "events") 208 } 209 result = append(result, smap) 210 } 211 return result 212 }, 213 }, 214 { 215 Name: "list", 216 Description: "Find and return a list of nodes in the registry of this service broker.", 217 Handler: func(context moleculer.Context, params moleculer.Payload) interface{} { 218 withServices := params.Get("withServices").Exists() && params.Get("withServices").Bool() 219 onlyAvailable := params.Get("onlyAvailable").Exists() && params.Get("onlyAvailable").Bool() 220 221 nodes := registry.nodes.list() 222 result := make([]map[string]interface{}, 0) 223 for _, node := range nodes { 224 if onlyAvailable && !node.IsAvailable() { 225 continue 226 } 227 maps := node.ExportAsMap() 228 if withServices { 229 if !isLocal(node.GetID()) { 230 maps["services"] = filterLocal(maps["services"].([]map[string]interface{})) 231 } 232 } else { 233 delete(maps, "services") 234 } 235 result = append(result, maps) 236 } 237 238 return result 239 }, 240 }, 241 // 242 /* FIXME: currently returns incomplete payload */ 243 { 244 Name: "health", 245 Description: "Return health status of local node including transit, os, cpu, memory, process, network, client information.", 246 Handler: func(context moleculer.Context, params moleculer.Payload) interface{} { 247 /* TODO: map as JSON which follows standard structure 248 { cpu: 249 { load1: 1.802734375, 250 load5: 1.8603515625, 251 load15: 1.82666015625, 252 cores: 16, 253 utilization: 11 }, 254 mem: 255 { free: 886513664, 256 total: 68719476736, 257 percent: 1.2900471687316895 }, 258 os: 259 { uptime: 1507874, 260 type: 'Darwin', 261 release: '18.5.0', 262 hostname: 'imac.local', 263 arch: 'x64', 264 platform: 'darwin', 265 user: 266 { uid: 501, 267 gid: 20, 268 username: 'dehypnosis', 269 homedir: '/Users/dehypnosis', 270 shell: '/bin/zsh' } }, 271 process: 272 { pid: 67218, 273 memory: 274 { rss: 60497920, 275 heapTotal: 32743424, 276 heapUsed: 27003144, 277 external: 104085 }, 278 uptime: 80.434, 279 argv: 280 [ '/usr/local/bin/node', 281 '/usr/local/bin/moleculer', 282 'connect', 283 'nats://dev.nats.svc.cluster.local:4222' ] }, 284 client: { type: 'nodejs', version: '0.13.8', langVersion: 'v8.16.0' }, 285 net: { ip: [ '222.107.184.34', '172.30.1.7' ] }, 286 transit: 287 { stat: 288 { packets: 289 { sent: { count: 28, bytes: 13409 }, 290 received: { count: 158, bytes: 199426 } } } }, 291 time: 292 { now: 1556737026387, 293 iso: '2019-05-01T18:57:06.387Z', 294 utc: 'Wed, 01 May 2019 18:57:06 GMT' } } 295 */ 296 nodeInfo := registry.localNode.ExportAsMap() 297 return map[string]interface{}{ 298 "cpu": map[string]interface{}{}, 299 "mem": map[string]interface{}{}, 300 "os": map[string]interface{}{}, 301 "process": map[string]interface{}{ 302 "uptime": time.Since(startedTime), 303 }, 304 "client": nodeInfo["client"], 305 "net": map[string]interface{}{ 306 "ip": nodeInfo["ipList"], 307 }, 308 "transit": map[string]interface{}{ 309 // TODO 310 }, 311 "time": map[string]interface{}{ 312 // TODO 313 }, 314 } 315 }, 316 }, 317 /* TODO: support $node.options */ 318 { 319 Name: "options", 320 Description: "Return broker configuration of local node.", 321 Handler: func(context moleculer.Context, params moleculer.Payload) interface{} { 322 /* TODO: map as JSON which follows standard structure 323 { logger: true, 324 transporter: 'nats://dev.nats.svc.cluster.local:4222', 325 nodeID: 'cli-imac.local-67218', 326 namespace: '', 327 logLevel: null, 328 logFormatter: 'default', 329 logObjectPrinter: null, 330 requestTimeout: 0, 331 retryPolicy: 332 { enabled: false, 333 retries: 5, 334 delay: 100, 335 maxDelay: 1000, 336 factor: 2, 337 check: [Function: check] }, 338 maxCallLevel: 0, 339 heartbeatInterval: 5, 340 heartbeatTimeout: 15, 341 tracking: { enabled: false, shutdownTimeout: 5000 }, 342 disableBalancer: false, 343 registry: { strategy: 'RoundRobin', preferLocal: true }, 344 circuitBreaker: 345 { enabled: false, 346 threshold: 0.5, 347 windowTime: 60, 348 minRequestCount: 20, 349 halfOpenTime: 10000, 350 check: [Function: check] }, 351 bulkhead: { enabled: false, concurrency: 10, maxQueueSize: 100 }, 352 transit: 353 { maxQueueSize: 50000, 354 packetLogFilter: [], 355 disableReconnect: false, 356 disableVersionCheck: false }, 357 cacher: null, 358 serializer: null, 359 validation: true, 360 validator: null, 361 metrics: false, 362 metricsRate: 1, 363 internalServices: true, 364 internalMiddlewares: true, 365 hotReload: false, 366 middlewares: null, 367 replCommands: null } 368 */ 369 // from registry.broker.Config ? 370 return map[string]interface{}{} 371 }, 372 }, 373 }, 374 }, registry.broker) 375 } 376 377 func filterLocal(in []map[string]interface{}) []map[string]interface{} { 378 out := make([]map[string]interface{}, 0) 379 for _, item := range in { 380 if strings.Index(item["name"].(string), "$") == 0 { 381 continue 382 } 383 out = append(out, item) 384 } 385 return out 386 }