github.com/ergo-services/ergo@v1.999.224/node/node.go (about) 1 package node 2 3 import ( 4 "context" 5 "fmt" 6 "runtime" 7 "strings" 8 "time" 9 10 "github.com/ergo-services/ergo/etf" 11 "github.com/ergo-services/ergo/gen" 12 "github.com/ergo-services/ergo/lib" 13 ) 14 15 const ( 16 appBehaviorGroup = "ergo:applications" 17 remoteBehaviorGroup = "ergo:remote" 18 ) 19 20 // node instance of created node using CreateNode 21 type node struct { 22 coreInternal 23 24 name string 25 creation uint32 26 context context.Context 27 stop context.CancelFunc 28 version Version 29 } 30 31 // StartWithContext create new node with specified context, name and cookie string 32 func StartWithContext(ctx context.Context, name string, cookie string, opts Options) (Node, error) { 33 34 lib.Log("Start node with name %q and cookie %q", name, cookie) 35 36 if len(strings.Split(name, "@")) != 2 { 37 return nil, fmt.Errorf("incorrect FQDN node name (example: node@localhost)") 38 } 39 if opts.Creation == 0 { 40 opts.Creation = uint32(time.Now().Unix()) 41 } 42 43 if opts.Flags.Enable == false { 44 opts.Flags = DefaultFlags() 45 } 46 47 if opts.Handshake == nil { 48 return nil, fmt.Errorf("Handshake must be defined") 49 } 50 if opts.Proto == nil { 51 return nil, fmt.Errorf("Proto must be defined") 52 } 53 if opts.StaticRoutesOnly == false && opts.Registrar == nil { 54 return nil, fmt.Errorf("Registrar must be defined if StaticRoutesOnly == false") 55 } 56 57 nodectx, nodestop := context.WithCancel(ctx) 58 node := &node{ 59 name: name, 60 context: nodectx, 61 stop: nodestop, 62 creation: opts.Creation, 63 } 64 65 // create a copy of envs 66 copyEnv := make(map[gen.EnvKey]interface{}) 67 for k, v := range opts.Env { 68 copyEnv[k] = v 69 } 70 71 // set global variable 'ergo:Node' 72 copyEnv[EnvKeyNode] = Node(node) 73 opts.Env = copyEnv 74 75 core, err := newCore(nodectx, name, cookie, opts) 76 if err != nil { 77 return nil, err 78 } 79 node.coreInternal = core 80 81 for _, app := range opts.Applications { 82 // load applications 83 name, err := node.ApplicationLoad(app) 84 if err != nil { 85 nodestop() 86 return nil, err 87 } 88 // start applications 89 _, err = node.ApplicationStart(name) 90 if err != nil { 91 nodestop() 92 return nil, err 93 } 94 } 95 96 return node, nil 97 } 98 99 // Version returns version of the node 100 func (n *node) Version() Version { 101 return n.version 102 } 103 104 // Spawn 105 func (n *node) Spawn(name string, opts gen.ProcessOptions, object gen.ProcessBehavior, args ...etf.Term) (gen.Process, error) { 106 // process started by node has no parent 107 options := processOptions{ 108 ProcessOptions: opts, 109 } 110 return n.spawn(name, options, object, args...) 111 } 112 113 // RegisterName 114 func (n *node) RegisterName(name string, pid etf.Pid) error { 115 return n.registerName(name, pid) 116 } 117 118 // UnregisterName 119 func (n *node) UnregisterName(name string) error { 120 return n.unregisterName(name) 121 } 122 123 // Stop 124 func (n *node) Stop() { 125 n.coreStop() 126 } 127 128 // Name 129 func (n *node) Name() string { 130 return n.name 131 } 132 133 // IsAlive 134 func (n *node) IsAlive() bool { 135 return n.coreIsAlive() 136 } 137 138 // Uptime 139 func (n *node) Uptime() int64 { 140 return n.coreUptime() 141 } 142 143 // Wait 144 func (n *node) Wait() { 145 n.coreWait() 146 } 147 148 func (n *node) Stats() NodeStats { 149 stats := NodeStats{} 150 151 coreStats := n.coreStats() 152 stats.TotalProcesses = coreStats.totalProcesses 153 stats.TotalReferences = coreStats.totalReferences 154 stats.RunningProcesses = uint64(coreStats.processes) 155 stats.RegisteredNames = uint64(coreStats.names) 156 stats.RegisteredAliases = uint64(coreStats.aliases) 157 158 monStats := n.monitorStats() 159 stats.MonitorsByPid = uint64(monStats.monitorsByPid) 160 stats.MonitorsByName = uint64(monStats.monitorsByName) 161 stats.MonitorsNodes = uint64(monStats.monitorsNodes) 162 stats.Links = uint64(monStats.links) 163 164 stats.LoadedApplications = uint64(len(n.LoadedApplications())) 165 stats.RunningApplications = uint64(len(n.WhichApplications())) 166 167 netStats := n.networkStats() 168 stats.NetworkConnections = uint64(netStats.connections) 169 stats.ProxyConnections = uint64(netStats.proxyConnections) 170 stats.TransitConnections = uint64(netStats.transitConnections) 171 172 return stats 173 } 174 175 // WaitWithTimeout 176 func (n *node) WaitWithTimeout(d time.Duration) error { 177 return n.coreWaitWithTimeout(d) 178 } 179 180 // LoadedApplications returns a list of loaded applications (including running applications) 181 func (n *node) LoadedApplications() []gen.ApplicationInfo { 182 return n.listApplications(false) 183 } 184 185 // WhichApplications returns a list of running applications 186 func (n *node) WhichApplications() []gen.ApplicationInfo { 187 return n.listApplications(true) 188 } 189 190 // WhichApplications returns a list of running applications 191 func (n *node) listApplications(onlyRunning bool) []gen.ApplicationInfo { 192 info := []gen.ApplicationInfo{} 193 for _, rb := range n.RegisteredBehaviorGroup(appBehaviorGroup) { 194 spec, ok := rb.Data.(*gen.ApplicationSpec) 195 if !ok { 196 continue 197 } 198 199 if onlyRunning && spec.Process == nil { 200 // list only started apps 201 continue 202 } 203 204 appInfo := gen.ApplicationInfo{ 205 Name: spec.Name, 206 Description: spec.Description, 207 Version: spec.Version, 208 } 209 if spec.Process != nil { 210 appInfo.PID = spec.Process.Self() 211 } 212 info = append(info, appInfo) 213 } 214 return info 215 } 216 217 // ApplicationInfo returns information about application 218 func (n *node) ApplicationInfo(name string) (gen.ApplicationInfo, error) { 219 rb, err := n.RegisteredBehavior(appBehaviorGroup, name) 220 if err != nil { 221 return gen.ApplicationInfo{}, lib.ErrAppUnknown 222 } 223 spec, ok := rb.Data.(*gen.ApplicationSpec) 224 if !ok { 225 return gen.ApplicationInfo{}, lib.ErrAppUnknown 226 } 227 228 pid := etf.Pid{} 229 if spec.Process != nil { 230 pid = spec.Process.Self() 231 } 232 233 appInfo := gen.ApplicationInfo{ 234 Name: spec.Name, 235 Description: spec.Description, 236 Version: spec.Version, 237 PID: pid, 238 } 239 return appInfo, nil 240 } 241 242 // ApplicationLoad loads the application specification for an application. Returns name of 243 // loaded application. 244 func (n *node) ApplicationLoad(app gen.ApplicationBehavior, args ...etf.Term) (string, error) { 245 246 spec, err := app.Load(args...) 247 if err != nil { 248 return "", err 249 } 250 err = n.RegisterBehavior(appBehaviorGroup, spec.Name, app, &spec) 251 if err != nil { 252 return "", err 253 } 254 return spec.Name, nil 255 } 256 257 // ApplicationUnload unloads given application 258 func (n *node) ApplicationUnload(appName string) error { 259 rb, err := n.RegisteredBehavior(appBehaviorGroup, appName) 260 if err != nil { 261 return lib.ErrAppUnknown 262 } 263 264 spec, ok := rb.Data.(*gen.ApplicationSpec) 265 if !ok { 266 return lib.ErrAppUnknown 267 } 268 if spec.Process != nil { 269 return lib.ErrAppAlreadyStarted 270 } 271 272 return n.UnregisterBehavior(appBehaviorGroup, appName) 273 } 274 275 // ApplicationStartPermanent start Application with start type ApplicationStartPermanent 276 // If this application terminates, all other applications and the entire node are also 277 // terminated 278 func (n *node) ApplicationStartPermanent(appName string, args ...etf.Term) (gen.Process, error) { 279 return n.applicationStart(gen.ApplicationStartPermanent, appName, args...) 280 } 281 282 // ApplicationStartTransient start Application with start type ApplicationStartTransient 283 // If transient application terminates with reason 'normal', this is reported and no 284 // other applications are terminated. Otherwise, all other applications and node 285 // are terminated 286 func (n *node) ApplicationStartTransient(appName string, args ...etf.Term) (gen.Process, error) { 287 return n.applicationStart(gen.ApplicationStartTransient, appName, args...) 288 } 289 290 // ApplicationStartTemporary start Application with start type ApplicationStartTemporary 291 // If an application terminates, this is reported but no other applications 292 // are terminated 293 func (n *node) ApplicationStartTemporary(appName string, args ...etf.Term) (gen.Process, error) { 294 return n.applicationStart(gen.ApplicationStartTemporary, appName, args...) 295 } 296 297 // ApplicationStart start Application with start type defined in the gen.ApplicationSpec.StartType 298 // on the loading application 299 func (n *node) ApplicationStart(appName string, args ...etf.Term) (gen.Process, error) { 300 return n.applicationStart("", appName, args...) 301 } 302 303 func (n *node) applicationStart(startType gen.ApplicationStartType, appName string, args ...etf.Term) (gen.Process, error) { 304 rb, err := n.RegisteredBehavior(appBehaviorGroup, appName) 305 if err != nil { 306 return nil, lib.ErrAppUnknown 307 } 308 309 spec, ok := rb.Data.(*gen.ApplicationSpec) 310 if !ok { 311 return nil, lib.ErrAppUnknown 312 } 313 314 if startType != "" { 315 spec.StartType = startType 316 } 317 318 // to prevent race condition on starting application we should 319 // make sure that nobodyelse starting it 320 spec.Lock() 321 defer spec.Unlock() 322 323 if spec.Process != nil { 324 return nil, lib.ErrAppAlreadyStarted 325 } 326 327 // start dependencies 328 for _, depAppName := range spec.Applications { 329 if _, e := n.ApplicationStart(depAppName); e != nil && e != lib.ErrAppAlreadyStarted { 330 return nil, e 331 } 332 } 333 334 env := map[gen.EnvKey]interface{}{ 335 gen.EnvKeyAppSpec: spec, 336 } 337 options := gen.ProcessOptions{ 338 Env: env, 339 } 340 process, e := n.Spawn("", options, rb.Behavior, args...) 341 if e != nil { 342 return nil, e 343 } 344 345 return process, nil 346 } 347 348 // ApplicationStop stop running application 349 func (n *node) ApplicationStop(name string) error { 350 rb, err := n.RegisteredBehavior(appBehaviorGroup, name) 351 if err != nil { 352 return lib.ErrAppUnknown 353 } 354 355 spec, ok := rb.Data.(*gen.ApplicationSpec) 356 if !ok { 357 return lib.ErrAppUnknown 358 } 359 360 spec.Lock() 361 defer spec.Unlock() 362 if spec.Process == nil { 363 return lib.ErrAppIsNotRunning 364 } 365 366 if e := spec.Process.Exit("normal"); e != nil { 367 return e 368 } 369 // we should wait until children process stopped. 370 if e := spec.Process.WaitWithTimeout(5 * time.Second); e != nil { 371 return lib.ErrProcessBusy 372 } 373 return nil 374 } 375 376 // Links 377 func (n *node) Links(process etf.Pid) []etf.Pid { 378 return n.processLinks(process) 379 } 380 381 // Monitors 382 func (n *node) Monitors(process etf.Pid) []etf.Pid { 383 return n.processMonitors(process) 384 } 385 386 // MonitorsByName 387 func (n *node) MonitorsByName(process etf.Pid) []gen.ProcessID { 388 return n.processMonitorsByName(process) 389 } 390 391 // MonitoredBy 392 func (n *node) MonitoredBy(process etf.Pid) []etf.Pid { 393 return n.processMonitoredBy(process) 394 } 395 396 // ProvideRemoteSpawn 397 func (n *node) ProvideRemoteSpawn(name string, behavior gen.ProcessBehavior) error { 398 return n.RegisterBehavior(remoteBehaviorGroup, name, behavior, nil) 399 } 400 401 // RevokeRemoteSpawn 402 func (n *node) RevokeRemoteSpawn(name string) error { 403 return n.UnregisterBehavior(remoteBehaviorGroup, name) 404 } 405 406 // DefaultFlags 407 func DefaultFlags() Flags { 408 // all features are enabled by default 409 return Flags{ 410 Enable: true, 411 EnableHeaderAtomCache: true, 412 EnableBigCreation: true, 413 EnableBigPidRef: true, 414 EnableFragmentation: true, 415 EnableAlias: true, 416 EnableRemoteSpawn: true, 417 EnableCompression: true, 418 EnableProxy: true, 419 } 420 } 421 422 // DefaultCloudFlags 423 func DefaultCloudFlags() CloudFlags { 424 return CloudFlags{ 425 Enable: true, 426 EnableIntrospection: true, 427 EnableMetrics: true, 428 } 429 } 430 431 func DefaultProxyFlags() ProxyFlags { 432 return ProxyFlags{ 433 Enable: true, 434 EnableLink: true, 435 EnableMonitor: true, 436 EnableRemoteSpawn: true, 437 EnableEncryption: false, 438 } 439 } 440 441 // DefaultProtoOptions 442 func DefaultProtoOptions() ProtoOptions { 443 return ProtoOptions{ 444 NumHandlers: runtime.NumCPU(), 445 MaxMessageSize: 0, // no limit 446 SendQueueLength: DefaultProtoSendQueueLength, 447 RecvQueueLength: DefaultProtoRecvQueueLength, 448 FragmentationUnit: DefaultProtoFragmentationUnit, 449 } 450 } 451 452 func DefaultListener() Listener { 453 return Listener{} 454 }