0.0.0-alpha.0 • Published 3 years ago

emperor v0.0.0-alpha.0

Weekly downloads
48
License
MIT
Repository
-
Last release
3 years ago

Emperor

TL;DR

Emperor is a one of a kind network controller allowing you to build a centrally controlled network of node instances. Scroll down to the end to see a cookbook on some common recipes to use

I highly suggest you check this pdf to see a trivial use case of Emperor

https://www.dropbox.com/s/iwqb48g7zuacg9k/Emperor.pdf?dl=1

  • NOTE Some changes have occurred to the API since major updates, but it should still be easy to glean use cases from this document

Installation

npm install --save emperor
//The Master controller on a server somewhere
var emperor=require('emperor').Master;
var emp= new emperor();
emp.open();
//A potential slave on a server or client somewhere
var Slave=require('emperor').Slave;
//$SlaveDriver will be automatically injected into this context
var ctx =global;

var rpc=
{
	namespace:
	{
		hello:function(name)
		{
			ctx.$SlaveDriver.respond('Hi '+name+' I'm Horus');
		}
    }
}


var worker = new Slave({id:'Horus',dynamic:true,ctx:ctx});
worker.open();
e.s.Horus.rpc.namespace.hello();

API

Emperor.Master(opt) Constructor

Creates a new Master controller.opt object flags are:

{
	port:Integer 
	debug:Boolean,//Shows more output
	out:Stream //Stream to output to , defaults to process.stdout
    in:Stream //Stream to provide input, defaults to process.stdin,
    ctx:Object// Context for the REPL instance.Defaults to global
   	noREPL: Boolean//If you are using your own repl already you can set this to true so it doesn't overwrite your current REPL
   	//You can still interact with the master instance using the .e property
}

If using the standard process.input/output, you should be greeted by a REPL shell to control Emperor. NOTE that in the REPL shell, tab is your friend to prevent too much writing e is the shorthand to reference the MasterInstance, so you shouldn't have multiple MasterInstances running. s is the property to access the masterInstances slaves, so you will frequently be doing

e.s

In your REPL shell.You identify a slave by name

e.s.<SLAVE NAME GOES HERE>

This will access the slave wrapper which provides a number of functions, for our example we will call our slave Conan.We assume Conan has dynamic set to true.Note that all of the functions in the Slave wrapper are chainable

//e.s.Conan.dyn(evalString) Dynamic functions.Used to evaluate string expressions on the slave side
e.s.Conan.dyn('global.a=1;');
// Conan>1

//In the dynamic context, we can directly interact with the $SlaveDriver as its automatically injected
e.s.Conan.dyn('$SlaveDriver.respond("Greetings")');
//Conan>Greetings

//e.s.Conan.addRpc(funcName,func); Add an RPC function on Conan
//In the RPC context, we cannot reference the $SlaveDriver instance directly, so we append $SlaveDriver to the 
//end of our argument list. Then when it is executed, it will automatically inject the $SlaveDriver instance slave side
e.s.Conan.addRpc('namespace.test',function(someArg,$SlaveDriver)
{
    $SlaveDriver.respond('urgh '+someArg);
});

//e.s.Conan.rpc Object containing callable rpc functions 
e.s.Conan.rpc.someNamespace.test('potatoes');
//Conan>"urgh potatoes"

//e.s.Conan.data An object to store data in if needed. This will be cleaned on slave exit

//We can also download files directly from our slaves
e.s.Conan.download('someFile.ext');
e.s.Conan.download('how/about/dir/structure.test');

//Once the slave has responded, we can download the file from the offered files object
//e.s.Conan.offeredFiles An object full of offered files by the slave that we can accept,decline,pause,resume and cancel

//Stream the file into the console
e.s.Conan.offeredFiles.someFile_ext.accept(process.stdout);
//Or maybe download it to a a local file
e.s.Conan.offeredFiles.structure_test.accept('./SomeLocalFile');

//Note that once a file has been completely downloaded, it will be removed from the offeredFiles object

//We can also upload to slaves 
e.s.Conan.upload('some/local/path','some/remote/path/to/be/saved','optionalAlias',false);

//e.s.Conan.toUpload.optionalAlias is the network file reader that we can interact with to pause and resume uploads.
// If the optional Alias is not set , then the local path is used . 
//The last parameter is a boolean that determines whether we should return the reader directly . 
//If true the network reader is returned . This is useful when one is not using the REPL to interact with the master and you want to do this programmatically

//e.s.Conan.isDynamic readonly property to tell if the slave allows dynamic functions
//e.s.Conan.allowsDownload readonly property to tell if the slave allows file downloads
//e.s.Conan.allowsUpload readonly property to tell if the slave allows file uploads

//e.s.Conan.info()//Displays connection info

//e.s.Conan.kill Kills the slave and causes it to shutdown
e.s.Conan.kill();

/====================Broadcasting=================//

//e.broadcastUpload(local,remote) Uploads a file to all slaves
e.broadcastUpload('someLocalFile','NameOfFileIWantOnSlave');

//e.broadcastDyn(evalString) Broadcasts dynamic strings
e.broadcastDyn('console.log($SlaveDriver.respond("Hello");');

//e.broadcastRpc(funcname,varargs) Broadcasts A call to an rpc function
e.broadcastRpc('someNamespace.func',1,4,"43");

//e.broadcastAddRpc(name,func) Adds an rpc call to all connected slaves
e.broadcastAddRpc('testFunc',function($SlaveDriver)
{
	var http=require('http');
	var httpServer=http.createServer();
	var port =~~(Math.random()*1000)+2000;
	
	httpServer.listen(port,function()
    {
        $SlaveDriver.respond("I have an http Server running on port :"+port)
    });
);

MasterInstance.open()

Starts running the Emperor server CHAINABLE

MasterInstance.close()

Closes the server

MasterInstance.numSlaves()

Returns the number of slaves we have working for us through

MasterInstance.e

Serves the same purpose as the e property that would normally be on the command line

MasterInstance Events

  • open (masterInstance)
  • error (err)
  • slave (slaveID,newSlave)
  • close (this)
  • slaveLost (slaveID,slave)

Slave Events

These events can be listened to via on with a slave instance in the e.s slave pool, or via the newSlave instance emitted on the slave event from the master

  • newRPC (RPCName,slave)
  • fileOffered (fileName,file,slave)
  • fileDownloaded (fileAlias,slave)
  • fileUploaded (fileAlias,slave)
  • unknownrpc (RPCName,slave)
  • fileUploadErr (err,slave)
  • uploadUnallowed (slave) You have attempted to upload to a slave that does not allow it
  • downloadUnallowed (slave) You have attempted to download from a slave that does not allow it
  • addRPCUnallowed (slave) You have attempted to add an RPC on a slave that does not allow it

Emperor.Slave(opt) Constructor

Object opt flags are as follows

{
    Id:String //An id to identify the slave to the emperor. If none provided , one will be generated
    host:String //The address to the emperor server .Defaults to “localhost”
    port:Integer //Defaults to 9999
    rpc:Object //An object that contains callable RPC methods that the Emperor Master can use
    fileInputRoot // String A path to use relative to which files will be saved if the master tries to upload defaults to ./
    fileOutputRoot// String A path to use relative to which files will be retrieved if the master tries to download defaults to ./
    allowFileUpload//Boolean Whether or not to allow the master to upload files to the slave  Defaults false
    allowFileDownload//Boolean Whether or not to allow master to download files to the slave  Defaults false
    ctx:Object //A context for use by Dynamic calls by the server. Defaults to global
    dynamic:Boolean //Whether we allow dynamic injection of code by the Emperor Master . Defaults false
}

SlaveInstance.open()

Starts running the slave

SlaveInstance.respond(AnyData)

Responds to the emperor master instance.

SlaveInstance.addRPC(name,func,namespace)

Adds an RPC callable function for the emperor master instance to use

SlaveInstance Events

  • error (err)
  • invalidID (slaveInstance) We provided an invalid ID, most likely ID is already in use by another Slave
  • close (slaveInstance)
  • open (slaveInstance)
  • death (slaveInstance) Emperor has requested the slave shut down

CookBook

Note that for the purposes of this cookbook, I have neglected to put in any port or host options unless to prove a point

As a hierachal controller

//Top level controller
var emperor=require('emperor').Master;
var TopEmp= new emperor({in:someStream,out:someStream});
TopEmp.open();


//Mid Level controller
var emperor=require('emperor').Master;
var MidEmp= new emperor({in:someStream,out:someStream});
MidEmp.open();

var slave =require('emperor').Slave;
var MidEmpSlave;
var rpc=
{
	killSubSlaves:function()
	{
		for (var slave in MidEmp.s)	
			slave.kill();
	}
	//...
}

MidEmpSlave= new slave({id:'something',rpc:rpc});
MidEmpSlave.open();

//Low level slaves
var lowSlave =new slave();
lowSlave.open();


//EG usage
TopEmp.s.something.rpc.killSubSlaves();

As a centralized logging service

var emperor=require('emperor').Master;
//In this example we will not go through REPL
var emp= new emperor({in:someStream,out:someLogStream});
emp.open();

//On Slave side
var Slave=require('emperor').Slave;
//No ID needed, let it generate its own
var worker1 = new Slave({id:'httpServer'});
var worker2 = new Slave({id:'databaseController'});


worker1.open().on('opened',function()
{
	worker1.respond('HTTP requests from ...');
	//etc
});
worker2.open().on('opened',function()
{
	worker2.respond('Database requests from ...');
	//etc
});

/*The log file would look something like 

	httpServer> ....
	databaseController> ....
	databaseController> ....
	httpServer> ....
	databaseController> ....
etc
*/

As a file distributor

We shall upload a file on connection, and once its finish, disconnect the slave

var emperor=require('emperor').Master;
//In this example we will not go through REPL
var emp= new emperor({in:someStream,out:someStream});
emp.open().on('slave',function(newSlave)
{
	var uploader=newSlave.upload('update_1.patch','patches/update_1.patch');
	
	uploader.on('finish',function()
	{
		newSlave.kill();
	}).on('error',function()
	{
		console.log('An error occurred uploading the file');
	});
});


//On Slave side
var Slave=require('emperor').Slave;
//No ID needed, let it generate its own
var worker = new Slave({ allowFileUpload:true});
worker.open();
0.0.0-alpha.0

3 years ago

3.0.0

3 years ago

2.0.3

3 years ago

2.0.2

3 years ago

2.0.1

3 years ago

2.0.0

3 years ago

1.1.2

3 years ago

1.1.1

3 years ago

1.1.0

3 years ago

1.0.0

3 years ago

0.1.1

3 years ago

0.1.0

3 years ago