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  }