mirror of
https://github.com/dragonheim/gagent.git
synced 2025-04-18 14:52:40 -07:00
249 lines
7.3 KiB
Go
249 lines
7.3 KiB
Go
package main
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"time"
|
|
|
|
gs "git.dragonheim.net/dragonheim/gagent/src/gstructs"
|
|
|
|
gc "git.dragonheim.net/dragonheim/gagent/src/client"
|
|
gr "git.dragonheim.net/dragonheim/gagent/src/router"
|
|
gw "git.dragonheim.net/dragonheim/gagent/src/worker"
|
|
|
|
docopt "github.com/aviddiviner/docopt-go"
|
|
hclsimple "github.com/hashicorp/hcl/v2/hclsimple"
|
|
hclwrite "github.com/hashicorp/hcl/v2/hclwrite"
|
|
logutils "github.com/hashicorp/logutils"
|
|
uuid "github.com/nu7hatch/gouuid"
|
|
cty "github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
const VERSION = "0.0.1"
|
|
|
|
var exitCodes = struct {
|
|
m map[string]int
|
|
}{m: map[string]int{
|
|
"SUCCESS": 0,
|
|
"CONFIG_FILE_MISSING": 1,
|
|
"SETUP_FAILED": 2,
|
|
"INVALID_MODE": 3,
|
|
"AGENT_LOAD_FAILED": 4,
|
|
"AGENT_MISSING_TAGS": 5,
|
|
"NO_ROUTERS_DEFINED": 6,
|
|
"NO_WORKERS_DEFINED": 7,
|
|
"AGENT_NOT_DEFINED": 8,
|
|
}}
|
|
|
|
func main() {
|
|
filter := &logutils.LevelFilter{
|
|
Levels: []logutils.LogLevel{"DEBUG", "INFO", "WARN", "ERROR"},
|
|
MinLevel: logutils.LogLevel("WARN"),
|
|
Writer: os.Stderr,
|
|
}
|
|
log.SetOutput(filter)
|
|
|
|
var config gs.GagentConfig
|
|
var configFile string = "/etc/gagent/gagent.hcl"
|
|
|
|
config.Name, _ = os.Hostname()
|
|
config.Mode = "setup"
|
|
|
|
/*
|
|
* Set a default UUID for this node.
|
|
* This is used throughout the G'Agent system to uniquely identify this node.
|
|
* It can be overriden in the configuration file by setting uuid
|
|
*/
|
|
identity, _ := uuid.NewV5(uuid.NamespaceURL, []byte("gagent"+config.Name))
|
|
config.UUID = identity.String()
|
|
|
|
/*
|
|
* By default, we want to listen on all IP addresses. It can be overriden
|
|
* in the configuration file by setting listenaddr
|
|
*/
|
|
config.ListenAddr = "0.0.0.0"
|
|
|
|
/*
|
|
* By default, G'Agent client will use port 35571 to communicate with the
|
|
* routers, but you can override it by setting the clientport in the
|
|
* configuration file
|
|
*/
|
|
config.ClientPort = 35571
|
|
|
|
/*
|
|
* By default, G'Agent router will use port 35572 to communicate with
|
|
* other routers, but you can override it by setting the routerport in
|
|
* the configuration file
|
|
*/
|
|
config.RouterPort = 35570
|
|
|
|
/*
|
|
* By default, G'Agent worker will use port 35570 to communicate with the
|
|
* routers, but you can override it by setting the workerport in the
|
|
* configuration file
|
|
*/
|
|
config.WorkerPort = 35572
|
|
|
|
/*
|
|
* Create a usage variable and then use that to declare the arguments and
|
|
* options. This allows us to use DocOpt for consistency between usage help
|
|
* and available arguments / options. Documentation is available at;
|
|
* http://docopt.org/
|
|
*/
|
|
usage := "G'Agents \n"
|
|
usage += "\n"
|
|
usage += " Go based mobile agent system, loosely inspired by the Agent Tcl / D'Agents \n"
|
|
usage += " system created by Robert S. Gray of Dartmouth college. \n"
|
|
usage += "\n"
|
|
|
|
usage += "Usage: \n"
|
|
usage += " gagent client [--config=<config>] [--agent=<file>] \n"
|
|
usage += " gagent router [--config=<config>] \n"
|
|
usage += " gagent worker [--config=<config>] \n"
|
|
usage += " gagent setup [--config=<config>] \n"
|
|
usage += " gagent --version \n"
|
|
usage += "\n"
|
|
|
|
usage += "Arguments: \n"
|
|
usage += " client -- Start as a G'Agent client \n"
|
|
usage += " router -- Start as a G'Agent router \n"
|
|
usage += " worker -- Start as a G'Agent worker \n"
|
|
usage += " setup -- Write inital configuration file \n"
|
|
usage += "\n"
|
|
|
|
usage += "Options:\n"
|
|
usage += " -h --help -- Show this help screen \n"
|
|
usage += " --version -- Show version \n"
|
|
usage += " --config=<config> -- [default: /etc/gagent/gagent.hcl] \n"
|
|
usage += " --agent=<file> -- filename of the agent to be uploaded to the G'Agent network \n"
|
|
|
|
/*
|
|
* Consume the usage variable and the command line arguments to create a
|
|
* dictionary / map.
|
|
*/
|
|
opts, _ := docopt.ParseArgs(usage, nil, VERSION)
|
|
log.Printf("[DEBUG] Arguments are %v\n", opts)
|
|
|
|
if opts["--config"] != nil {
|
|
configFile = opts["--config"].(string)
|
|
}
|
|
|
|
/*
|
|
* Let the command line mode override the configuration.
|
|
*/
|
|
if opts["setup"] == true {
|
|
config.Mode = "setup"
|
|
} else {
|
|
err := hclsimple.DecodeFile(configFile, nil, &config)
|
|
if err != nil {
|
|
log.Printf("[ERROR] Failed to load configuration file: %s.\n", configFile)
|
|
log.Printf("[ERROR] %s\n", err)
|
|
os.Exit(exitCodes.m["CONFIG_FILE_MISSING"])
|
|
}
|
|
if opts["client"] == true {
|
|
config.Mode = "client"
|
|
}
|
|
if opts["router"] == true {
|
|
config.Mode = "router"
|
|
}
|
|
if opts["worker"] == true {
|
|
config.Mode = "worker"
|
|
}
|
|
}
|
|
log.Printf("[DEBUG] Configuration is %v\n", config)
|
|
|
|
switch config.Mode {
|
|
case "client":
|
|
/*
|
|
* Client mode will send an agent file to a router for processing
|
|
* Clients do not process the agent files, only send them as
|
|
* requests to a router. If started without arguments, the client
|
|
* will contact the router and attempt to retrieve the results
|
|
* of it's most recent request.
|
|
*/
|
|
log.Printf("[INFO] Running in client mode\n")
|
|
|
|
if len(config.Routers) == 0 {
|
|
log.Printf("[ERROR] No routers defined.\n")
|
|
os.Exit(exitCodes.m["NO_ROUTERS_DEFINED"])
|
|
}
|
|
|
|
if opts["--agent"] == nil {
|
|
log.Printf("[ERROR] Agent file not specified")
|
|
os.Exit(exitCodes.m["AGENT_NOT_DEFINED"])
|
|
}
|
|
agent, err := ioutil.ReadFile(opts["--agent"].(string))
|
|
if err != nil {
|
|
log.Printf("[ERROR] Failed to load Agent file: %s", opts["--agent"])
|
|
os.Exit(exitCodes.m["AGENT_LOAD_FAILED"])
|
|
}
|
|
for key := range config.Routers {
|
|
go gc.Main(config, key, string(agent))
|
|
time.Sleep(10 * time.Second)
|
|
}
|
|
|
|
case "router":
|
|
/*
|
|
* The 'router' processes routing requests from the agent. The router does
|
|
* not handle any of the agent activities beyond processing the agent's
|
|
* list of tags and passing the agent and it's storage to either a member
|
|
* or client node. Tags are used by the agent to give hints as to where
|
|
* it should be routed.
|
|
*/
|
|
log.Printf("[INFO] Running in router mode\n")
|
|
|
|
if len(config.Workers) == 0 {
|
|
log.Printf("[ERROR] No workers defined.\n")
|
|
os.Exit(exitCodes.m["NO_WORKERS_DEFINED"])
|
|
}
|
|
|
|
go gr.Main(config)
|
|
select {}
|
|
|
|
case "worker":
|
|
/*
|
|
* The 'worker' processes the agent code. The worker nodes do not know
|
|
* anything about the network structure. Instead they know only to which
|
|
* router(s) they are connected. The worker will execute the agent code and
|
|
* pass the agent and it's results to a router.
|
|
*/
|
|
log.Printf("[INFO] Running in worker mode\n")
|
|
|
|
if len(config.Routers) == 0 {
|
|
log.Printf("[ERROR] No routers defined.\n")
|
|
os.Exit(exitCodes.m["NO_ROUTERS_DEFINED"])
|
|
}
|
|
|
|
for key := range config.Routers {
|
|
go gw.Main(config, key)
|
|
time.Sleep(10 * time.Second)
|
|
}
|
|
|
|
select {}
|
|
|
|
case "setup":
|
|
log.Printf("[INFO] Running in setup mode\n")
|
|
f := hclwrite.NewEmptyFile()
|
|
rootBody := f.Body()
|
|
rootBody.SetAttributeValue("name", cty.StringVal(config.Name))
|
|
rootBody.SetAttributeValue("mode", cty.StringVal("client"))
|
|
rootBody.SetAttributeValue("uuid", cty.StringVal(config.UUID))
|
|
rootBody.AppendNewline()
|
|
|
|
routerBlock1 := rootBody.AppendNewBlock("router", []string{config.Name})
|
|
routerBody1 := routerBlock1.Body()
|
|
routerBody1.SetAttributeValue("routerid", cty.StringVal(config.UUID))
|
|
routerBody1.SetAttributeValue("address", cty.StringVal("127.0.0.1"))
|
|
rootBody.AppendNewline()
|
|
|
|
log.Printf("\n%s", f.Bytes())
|
|
os.Exit(exitCodes.m["SUCCESS"])
|
|
|
|
default:
|
|
log.Printf("[ERROR] Unknown operating mode, exiting.\n")
|
|
os.Exit(exitCodes.m["INVALID_MODE"])
|
|
}
|
|
|
|
os.Exit(exitCodes.m["SUCCESS"])
|
|
}
|