mirror of
https://github.com/dragonheim/gagent.git
synced 2025-04-26 02:18:59 -07:00
fix: Re-initializing after I destroyed the original repository.
This commit is contained in:
parent
5863999e8c
commit
8b54fc32c5
20 changed files with 1359 additions and 0 deletions
44
.drone.yml
Normal file
44
.drone.yml
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: default
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: arm
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
branch:
|
||||||
|
include:
|
||||||
|
- development
|
||||||
|
exclude:
|
||||||
|
- main
|
||||||
|
|
||||||
|
clone:
|
||||||
|
depth: 1
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
- name: dockersock
|
||||||
|
host:
|
||||||
|
path: /run/docker.sock
|
||||||
|
|
||||||
|
image_pull_secrets:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Build ${DRONE_REPO} Container Image
|
||||||
|
image: plugins/docker
|
||||||
|
volumes:
|
||||||
|
- name: dockersock
|
||||||
|
path: /var/run/docker.sock
|
||||||
|
settings:
|
||||||
|
auth:
|
||||||
|
from_secret: Docker
|
||||||
|
auto_tag: true
|
||||||
|
dockerfile: docker/Dockerfile
|
||||||
|
# mirror: "https://registry.dragonheim.net:443"
|
||||||
|
username:
|
||||||
|
from_secret: docker_username
|
||||||
|
repo: ${DRONE_REPO}
|
||||||
|
# target: development
|
||||||
|
|
44
.gitignore
vendored
Normal file
44
.gitignore
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# ---> Go
|
||||||
|
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Folders
|
||||||
|
_obj
|
||||||
|
_test
|
||||||
|
bin/
|
||||||
|
vendor/
|
||||||
|
|
||||||
|
# Architecture specific extensions/prefixes
|
||||||
|
*.[568vq]
|
||||||
|
[568vq].out
|
||||||
|
|
||||||
|
*.cgo1.go
|
||||||
|
*.cgo2.c
|
||||||
|
_cgo_defun.c
|
||||||
|
_cgo_gotypes.go
|
||||||
|
_cgo_export.*
|
||||||
|
|
||||||
|
_testmain.go
|
||||||
|
|
||||||
|
*.exe
|
||||||
|
*.test
|
||||||
|
*.prof
|
||||||
|
|
||||||
|
# Ignore various IDE
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
.vscode
|
||||||
|
debug
|
||||||
|
|
||||||
|
# Ignore various temporary files
|
||||||
|
*.swp
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.log
|
||||||
|
*.pid
|
||||||
|
|
||||||
|
# Ignore G'Agent configuration file.
|
||||||
|
gagent.hcl
|
4
CHANGELOG.md
Normal file
4
CHANGELOG.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
### [0.0.0](https://git.dragonheim.net/dragonheim/gagent/commit/5863999) (20210425)
|
||||||
|
---
|
||||||
|
#### Features
|
||||||
|
* [Initial Commit](https://git.dragonheim.net/dragonheim/gagent/commit/5863999) -- James Wells
|
8
LANGUAGE.md
Normal file
8
LANGUAGE.md
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# G'Agent
|
||||||
|
## AgentTCL Language Extension
|
||||||
|
### TODO
|
||||||
|
Everything. :(
|
||||||
|
|
||||||
|
## G'Agent Language Extension
|
||||||
|
### TODO
|
||||||
|
Everything. :(
|
19
LICENSE
Normal file
19
LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
MIT License
|
||||||
|
Copyright (c) 2017-2021 James Wells <jwells@dragonheim.net>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
the Software without restriction, including without limitation the rights to
|
||||||
|
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
20
docker/Dockerfile
Normal file
20
docker/Dockerfile
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
FROM golang:1.16-alpine3.12 as builder
|
||||||
|
|
||||||
|
WORKDIR /gagent
|
||||||
|
COPY . .
|
||||||
|
RUN apk add --no-cache zeromq-dev build-base git && \
|
||||||
|
go build -o /gagent/bin/gagent gagent/main.go && \
|
||||||
|
strip /gagent/bin/gagent
|
||||||
|
|
||||||
|
|
||||||
|
FROM alpine:3.12
|
||||||
|
LABEL Name="G'Agent" Maintainer="jwells@dragonheim.net" License="MIT License"
|
||||||
|
RUN apk add --no-cache zeromq; mkdir -p -m 0700 /etc/gagent
|
||||||
|
|
||||||
|
COPY --from=builder /gagent/examples/example_gagent.hcl /etc/gagent/gagent.hcl
|
||||||
|
COPY --from=builder /gagent/bin/gagent /usr/bin/
|
||||||
|
|
||||||
|
EXPOSE 35570/tcp
|
||||||
|
VOLUME /etc/gagent
|
||||||
|
|
||||||
|
CMD ["/usr/bin/gagent"]
|
8
examples/add-two.tcl
Normal file
8
examples/add-two.tcl
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env tcl
|
||||||
|
#
|
||||||
|
#
|
||||||
|
set val1 1
|
||||||
|
set val2 2
|
||||||
|
|
||||||
|
set result [expr {val1 + val2}]
|
||||||
|
puts result
|
83
examples/example_gagent.hcl
Normal file
83
examples/example_gagent.hcl
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* This is the name of this node and is only used
|
||||||
|
* for logging purposes.
|
||||||
|
*
|
||||||
|
* Optional.
|
||||||
|
*/
|
||||||
|
name = "gagent-zulu.example.org"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the mode that this node operates in. There
|
||||||
|
* are three modes;
|
||||||
|
* client == Clients read the local agent file and
|
||||||
|
* forwards the contents on to a router
|
||||||
|
*
|
||||||
|
* router == Routers accept agents from clients and
|
||||||
|
* other routers and accepts responses to
|
||||||
|
* agents from workers and other routers.
|
||||||
|
*
|
||||||
|
* worker == Workers collect and process agents and
|
||||||
|
* send responses to routers for return
|
||||||
|
* the requesting client.
|
||||||
|
*
|
||||||
|
* Required.
|
||||||
|
*/
|
||||||
|
mode = "router"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the UUID used throughout the G'Agent system
|
||||||
|
* to uniquely identify this node.
|
||||||
|
*
|
||||||
|
* Required.
|
||||||
|
*/
|
||||||
|
// uuid = "04f97538-270d-4ca3-b782-e09ef35830e9"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a list of known G'Agent routers. At least
|
||||||
|
* one router is required for workers and clients. If
|
||||||
|
* there is more than one router, clients and workers
|
||||||
|
* will connect to them in sequential order.
|
||||||
|
*/
|
||||||
|
// router "alpha" {
|
||||||
|
// routerid = "04f97538-270d-4cb3-b782-e09ef35830e9"
|
||||||
|
// address = "gagent-alpha.example.org"
|
||||||
|
// tags = [ "a", "b", "c", "d" ]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// router "beta" {
|
||||||
|
// routerid = "04f97538-270d-4cc3-b782-e09ef35830e9"
|
||||||
|
// address = "gagent-beta.example.org"
|
||||||
|
// tags = [ "a", "c", "e", "g" ]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// router "charlie" {
|
||||||
|
// routerid = "04f97538-270d-4cd3-b782-e09ef35830e9"
|
||||||
|
// address = "gagent-charlie.example.org"
|
||||||
|
// tags = [ "b", "d", "f", "h" ]
|
||||||
|
// }
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a list of known G'Agent workers. This is only
|
||||||
|
* used by routers to determine which workers are
|
||||||
|
* allowed to accept and respond to agents.
|
||||||
|
*
|
||||||
|
* At least one worker is reuqired for routers.
|
||||||
|
*/
|
||||||
|
// worker "alpha" {
|
||||||
|
// workerid = "04f97538-270d-4ce3-b782-e09ef35830e9"
|
||||||
|
// address = "gagent-alpha.example.org"
|
||||||
|
// tags = [ "a", "b", "c", "d" ]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// worker "beta" {
|
||||||
|
// workerid = "04f97538-270d-4cf3-b782-e09ef35830e9"
|
||||||
|
// address = "gagent-beta.example.org"
|
||||||
|
// tags = [ "a", "c", "e", "g" ]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// worker "charlie" {
|
||||||
|
// workerid = "04f97538-270d-4c04-b782-e09ef35830e9"
|
||||||
|
// address = "gagent-charlie.example.org"
|
||||||
|
// tags = [ "b", "d", "f", "h" ]
|
||||||
|
// }
|
||||||
|
|
4
examples/hello-world.tcl
Normal file
4
examples/hello-world.tcl
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/usr/bin/env tcl
|
||||||
|
#
|
||||||
|
#
|
||||||
|
puts {hello, world}
|
BIN
gagent.png
Normal file
BIN
gagent.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 776 B |
170
gagent/main.go
Normal file
170
gagent/main.go
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
// "math/rand"
|
||||||
|
// "time"
|
||||||
|
|
||||||
|
gs "git.dragonheim.net/dragonheim/gagent/src/gstructs"
|
||||||
|
|
||||||
|
// client "git.dragonheim.net/dragonheim/gagent/src/client"
|
||||||
|
// router "git.dragonheim.net/dragonheim/gagent/src/router"
|
||||||
|
// worker "git.dragonheim.net/dragonheim/gagent/src/worker"
|
||||||
|
|
||||||
|
docopt "github.com/aviddiviner/docopt-go"
|
||||||
|
hclsimple "github.com/hashicorp/hcl/v2/hclsimple"
|
||||||
|
uuid "github.com/nu7hatch/gouuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
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,
|
||||||
|
}}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var config gs.GagentConfig
|
||||||
|
var configFile string = "/etc/gagent/gagent.hcl"
|
||||||
|
|
||||||
|
config.Name, _ = os.Hostname()
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
// rand.Seed(time.Now().UnixNano())
|
||||||
|
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 will use port 35570 to communicate with the routers,
|
||||||
|
* but you can override it by setting the listenport in the configuration
|
||||||
|
* file
|
||||||
|
*/
|
||||||
|
config.ListenPort = 35570
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 [--config=<config>] [--agent=<file>] \n"
|
||||||
|
usage += " gagent setup [--config=<config>] \n"
|
||||||
|
usage += "\n"
|
||||||
|
|
||||||
|
usage += "Arguments: \n"
|
||||||
|
usage += " client -- Start as a G'Agent client \n"
|
||||||
|
usage += " <file> -- filename of the agent to be uploaded to the G'Agent network \n"
|
||||||
|
usage += "\n"
|
||||||
|
|
||||||
|
usage += " setup -- Write inital configuration file \n"
|
||||||
|
usage += "\n"
|
||||||
|
|
||||||
|
usage += "Options:\n"
|
||||||
|
usage += " config=<config> [default: /etc/gagent/gagent.hcl] \n"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Consume the usage variable and the command line arguments to create a
|
||||||
|
* dictionary of the command line arguments.
|
||||||
|
*/
|
||||||
|
arguments, _ := docopt.ParseDoc(usage)
|
||||||
|
fmt.Printf("Arguments are %v\n", arguments)
|
||||||
|
|
||||||
|
if arguments["--config"] != nil {
|
||||||
|
configFile = arguments["--config"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Let the command line mode override the configuration.
|
||||||
|
*/
|
||||||
|
if arguments["setup"] == true {
|
||||||
|
config.Mode = "setup"
|
||||||
|
} else {
|
||||||
|
err := hclsimple.DecodeFile(configFile, nil, &config)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to load configuration file: %s.\n", configFile)
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(exitCodes.m["CONFIG_FILE_MISSING"])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("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.
|
||||||
|
*/
|
||||||
|
fmt.Printf("Running in client mode\n")
|
||||||
|
agent, err := ioutil.ReadFile(arguments["--agent"].(string))
|
||||||
|
if err == nil {
|
||||||
|
fmt.Printf("Agent containts %v\n", string(agent))
|
||||||
|
// fmt.Printf("Forking...\n")
|
||||||
|
// go client.Main(config.Client, string(agent))
|
||||||
|
// fmt.Printf("Forked thread has completed\n")
|
||||||
|
// time.Sleep(10 * time.Second)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Failed to load Agent file: %s.\n", arguments["--agent"].(string))
|
||||||
|
os.Exit(exitCodes.m["AGENT_LOAD_FAILED"])
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
fmt.Printf("Running in router mode\n")
|
||||||
|
// go router.Main(config.Router)
|
||||||
|
// 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.
|
||||||
|
*/
|
||||||
|
fmt.Printf("Running in worker mode\n")
|
||||||
|
// go worker.Main(config.Worker)
|
||||||
|
// select {}
|
||||||
|
|
||||||
|
case "setup":
|
||||||
|
fmt.Printf("Running in setup mode\n")
|
||||||
|
os.Exit(exitCodes.m["SETUP_FAILED"])
|
||||||
|
|
||||||
|
default:
|
||||||
|
fmt.Printf("Unknown operating mode, exiting.\n")
|
||||||
|
os.Exit(exitCodes.m["INVALID_MODE"])
|
||||||
|
}
|
||||||
|
|
||||||
|
os.Exit(exitCodes.m["SUCCESS"])
|
||||||
|
}
|
10
go.mod
Normal file
10
go.mod
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
module git.dragonheim.net/dragonheim/gagent
|
||||||
|
|
||||||
|
go 1.16
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/aviddiviner/docopt-go v0.0.0-20170807220726-d8a1d67efc6a
|
||||||
|
github.com/hashicorp/hcl/v2 v2.8.2
|
||||||
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d
|
||||||
|
github.com/pebbe/zmq4 v1.2.2
|
||||||
|
)
|
53
go.sum
Normal file
53
go.sum
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
||||||
|
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||||
|
github.com/apparentlymart/go-dump v0.0.0-20180507223929-23540a00eaa3/go.mod h1:oL81AME2rN47vu18xqj1S1jPIPuN7afo62yKTNn3XMM=
|
||||||
|
github.com/apparentlymart/go-textseg v1.0.0 h1:rRmlIsPEEhUTIKQb7T++Nz/A5Q6C9IuX2wFoYVvnCs0=
|
||||||
|
github.com/apparentlymart/go-textseg v1.0.0/go.mod h1:z96Txxhf3xSFMPmb5X/1W05FF/Nj9VFpLOpjS5yuumk=
|
||||||
|
github.com/apparentlymart/go-textseg/v12 v12.0.0 h1:bNEQyAGak9tojivJNkoqWErVCQbjdL7GzRt3F8NvfJ0=
|
||||||
|
github.com/apparentlymart/go-textseg/v12 v12.0.0/go.mod h1:S/4uRK2UtaQttw1GenVJEynmyUenKwP++x/+DdGV/Ec=
|
||||||
|
github.com/aviddiviner/docopt-go v0.0.0-20170807220726-d8a1d67efc6a h1:YJuVATwP+Gzk7nys0U/DKjKkoYp1n/sYm0yi5vX8W8M=
|
||||||
|
github.com/aviddiviner/docopt-go v0.0.0-20170807220726-d8a1d67efc6a/go.mod h1:pBRbUcGboHT5qBceq2Cg/WIcDbO78a8wPxTg8zmS3Hs=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
|
||||||
|
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||||
|
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
|
||||||
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/hashicorp/hcl/v2 v2.8.2 h1:wmFle3D1vu0okesm8BTLVDyJ6/OL9DCLUwn0b2OptiY=
|
||||||
|
github.com/hashicorp/hcl/v2 v2.8.2/go.mod h1:bQTN5mpo+jewjJgh8jr0JUguIi7qPHUF6yIfAEN3jqY=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348 h1:MtvEpTB6LX3vkb4ax0b5D2DHbNAUsen0Gx5wZoq3lV4=
|
||||||
|
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
|
||||||
|
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||||
|
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||||
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d h1:VhgPp6v9qf9Agr/56bj7Y/xa04UccTW04VP0Qed4vnQ=
|
||||||
|
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d/go.mod h1:YUTz3bUH2ZwIWBy3CJBeOBEugqcmXREj14T+iG/4k4U=
|
||||||
|
github.com/pebbe/zmq4 v1.2.2 h1:RZ5Ogp0D5S6u+tSxopnI3afAf0ifWbvQOAw9HxXvZP4=
|
||||||
|
github.com/pebbe/zmq4 v1.2.2/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||||
|
github.com/zclconf/go-cty v1.2.0 h1:sPHsy7ADcIZQP3vILvTjrh74ZA175TFP5vqiNK1UmlI=
|
||||||
|
github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8=
|
||||||
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190502175342-a43fa875dd82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
43
src/client/client.go
Normal file
43
src/client/client.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
gs "git.dragonheim.net/dragonheim/gagent/src/gstructs"
|
||||||
|
|
||||||
|
zmq "github.com/pebbe/zmq4"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Main is the initiation function for a Client
|
||||||
|
func Main(config gs.GagentConfig, agent string) {
|
||||||
|
var mu sync.Mutex
|
||||||
|
|
||||||
|
fmt.Printf("Did we make it this far?\n")
|
||||||
|
fmt.Printf("--|%#v|--\n", agent)
|
||||||
|
|
||||||
|
connectString := fmt.Sprintf("tcp://%s", config.Routers[0].RouterAddr)
|
||||||
|
|
||||||
|
sock, _ := zmq.NewSocket(zmq.DEALER)
|
||||||
|
defer sock.Close()
|
||||||
|
|
||||||
|
sock.SetIdentity(config.UUID)
|
||||||
|
sock.Connect(connectString)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
mu.Lock()
|
||||||
|
sock.SendMessage(agent)
|
||||||
|
mu.Unlock()
|
||||||
|
}()
|
||||||
|
|
||||||
|
for {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
mu.Lock()
|
||||||
|
msg, err := sock.RecvMessage(zmq.DONTWAIT)
|
||||||
|
if err == nil {
|
||||||
|
fmt.Println(msg[0], config.UUID)
|
||||||
|
}
|
||||||
|
mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
87
src/gstructs/gstructs.go
Normal file
87
src/gstructs/gstructs.go
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
package gstructs
|
||||||
|
|
||||||
|
// GagentConfig is the primary construct used by all modes
|
||||||
|
type GagentConfig struct {
|
||||||
|
Name string `hcl:"name,optional"`
|
||||||
|
Mode string `hcl:"mode,attr"`
|
||||||
|
UUID string `hcl:"uuid,optional"`
|
||||||
|
ListenAddr string `hc:"listenaddr,optional"`
|
||||||
|
ListenPort int `hc:"listenport,optional"`
|
||||||
|
Clients []*ClientDetails `hcl:"client,block"`
|
||||||
|
Routers []*RouterDetails `hcl:"router,block"`
|
||||||
|
Workers []*WorkerDetails `hcl:"worker,block"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClientDetails is details about known clients
|
||||||
|
type ClientDetails struct {
|
||||||
|
/*
|
||||||
|
* Client name for display purposes in logs and
|
||||||
|
* diagnostics.
|
||||||
|
*/
|
||||||
|
ClientName string `hcl:",label"`
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UUID String for the client node. This is used by
|
||||||
|
* the router to determine which MQ client to send
|
||||||
|
* the agent's results to. This attempts to keep the
|
||||||
|
* clients unique globally.
|
||||||
|
*/
|
||||||
|
ClientID string `hcl:"clientid,attr"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RouterDetails is details about known routers
|
||||||
|
type RouterDetails struct {
|
||||||
|
/*
|
||||||
|
* Router name for display purposes in logs and
|
||||||
|
* diagnostics
|
||||||
|
*/
|
||||||
|
RouterName string `hcl:",label"`
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UUID String for the router node. This is used by
|
||||||
|
* the clients, routers, and workers to determine
|
||||||
|
* which MQ router to send the agent's requests to.
|
||||||
|
* This attempts to keep the routers unique globally.
|
||||||
|
*/
|
||||||
|
RouterID string `hcl:"routerid,attr"`
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the IP Address and port that the router
|
||||||
|
* will listen on. The router will start up a 0MQ
|
||||||
|
* service that clients and workers will connect to.
|
||||||
|
*/
|
||||||
|
RouterAddr string `hcl:"address,attr"`
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These tags will be passed to the router upon
|
||||||
|
* connection. The router will then use these
|
||||||
|
* tags to help determine which worker / client
|
||||||
|
* to send the client's requests and results to.
|
||||||
|
*/
|
||||||
|
RouterTags []string `hcl:"tags,optional"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WorkerDetails is details about known workers
|
||||||
|
type WorkerDetails struct {
|
||||||
|
/*
|
||||||
|
* Router name for display purposes in logs and
|
||||||
|
* diagnostics
|
||||||
|
*/
|
||||||
|
WorkerName string `hcl:",label"`
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UUID String for the worker node. This is used
|
||||||
|
* by the router to determine which MQ client to
|
||||||
|
* send agents to. This attempts to keep the
|
||||||
|
* workers unique globally.
|
||||||
|
*/
|
||||||
|
WorkerID string `hcl:"workerid,attr"`
|
||||||
|
|
||||||
|
/*
|
||||||
|
* These tags will be passed to the router upon
|
||||||
|
* connection. The router will then use these
|
||||||
|
* tags to help determine which worker / client
|
||||||
|
* to send the agent and it's results to.
|
||||||
|
*/
|
||||||
|
WorkerTags []string `hcl:"tags,optional"`
|
||||||
|
}
|
214
src/picol/commands.go
Normal file
214
src/picol/commands.go
Normal file
|
@ -0,0 +1,214 @@
|
||||||
|
package picol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ArityErr(i *Interp, name string, argv []string) error {
|
||||||
|
return fmt.Errorf("Wrong number of args for %s %s", name, argv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandMath(i *Interp, argv []string, pd interface{}) (string, error) {
|
||||||
|
if len(argv) != 3 {
|
||||||
|
return "", ArityErr(i, argv[0], argv)
|
||||||
|
}
|
||||||
|
a, _ := strconv.Atoi(argv[1])
|
||||||
|
b, _ := strconv.Atoi(argv[2])
|
||||||
|
var c int
|
||||||
|
switch {
|
||||||
|
case argv[0] == "+":
|
||||||
|
c = a + b
|
||||||
|
case argv[0] == "-":
|
||||||
|
c = a - b
|
||||||
|
case argv[0] == "*":
|
||||||
|
c = a * b
|
||||||
|
case argv[0] == "/":
|
||||||
|
c = a / b
|
||||||
|
case argv[0] == ">":
|
||||||
|
if a > b {
|
||||||
|
c = 1
|
||||||
|
}
|
||||||
|
case argv[0] == ">=":
|
||||||
|
if a >= b {
|
||||||
|
c = 1
|
||||||
|
}
|
||||||
|
case argv[0] == "<":
|
||||||
|
if a < b {
|
||||||
|
c = 1
|
||||||
|
}
|
||||||
|
case argv[0] == "<=":
|
||||||
|
if a <= b {
|
||||||
|
c = 1
|
||||||
|
}
|
||||||
|
case argv[0] == "==":
|
||||||
|
if a == b {
|
||||||
|
c = 1
|
||||||
|
}
|
||||||
|
case argv[0] == "!=":
|
||||||
|
if a != b {
|
||||||
|
c = 1
|
||||||
|
}
|
||||||
|
default: // FIXME I hate warnings
|
||||||
|
c = 0
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%d", c), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandSet(i *Interp, argv []string, pd interface{}) (string, error) {
|
||||||
|
if len(argv) != 3 {
|
||||||
|
return "", ArityErr(i, argv[0], argv)
|
||||||
|
}
|
||||||
|
i.SetVar(argv[1], argv[2])
|
||||||
|
return argv[2], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandUnset(i *Interp, argv []string, pd interface{}) (string, error) {
|
||||||
|
if len(argv) != 2 {
|
||||||
|
return "", ArityErr(i, argv[0], argv)
|
||||||
|
}
|
||||||
|
i.UnsetVar(argv[1])
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandIf(i *Interp, argv []string, pd interface{}) (string, error) {
|
||||||
|
if len(argv) != 3 && len(argv) != 5 {
|
||||||
|
return "", ArityErr(i, argv[0], argv)
|
||||||
|
}
|
||||||
|
|
||||||
|
result, err := i.Eval(argv[1])
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if r, _ := strconv.Atoi(result); r != 0 {
|
||||||
|
return i.Eval(argv[2])
|
||||||
|
} else if len(argv) == 5 {
|
||||||
|
return i.Eval(argv[4])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandWhile(i *Interp, argv []string, pd interface{}) (string, error) {
|
||||||
|
if len(argv) != 3 {
|
||||||
|
return "", ArityErr(i, argv[0], argv)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
result, err := i.Eval(argv[1])
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if r, _ := strconv.Atoi(result); r != 0 {
|
||||||
|
result, err := i.Eval(argv[2])
|
||||||
|
switch err {
|
||||||
|
case PICOL_CONTINUE, nil:
|
||||||
|
//pass
|
||||||
|
case PICOL_BREAK:
|
||||||
|
return result, nil
|
||||||
|
default:
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandRetCodes(i *Interp, argv []string, pd interface{}) (string, error) {
|
||||||
|
if len(argv) != 1 {
|
||||||
|
return "", ArityErr(i, argv[0], argv)
|
||||||
|
}
|
||||||
|
switch argv[0] {
|
||||||
|
case "break":
|
||||||
|
return "", PICOL_BREAK
|
||||||
|
case "continue":
|
||||||
|
return "", PICOL_CONTINUE
|
||||||
|
}
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandCallProc(i *Interp, argv []string, pd interface{}) (string, error) {
|
||||||
|
var x []string
|
||||||
|
|
||||||
|
if pd, ok := pd.([]string); ok {
|
||||||
|
x = pd
|
||||||
|
} else {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
i.callframe = &CallFrame{vars: make(map[string]Var), parent: i.callframe}
|
||||||
|
defer func() { i.callframe = i.callframe.parent }() // remove the called proc callframe
|
||||||
|
|
||||||
|
arity := 0
|
||||||
|
for _, arg := range strings.Split(x[0], " ") {
|
||||||
|
if len(arg) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
arity++
|
||||||
|
i.SetVar(arg, argv[arity])
|
||||||
|
}
|
||||||
|
|
||||||
|
if arity != len(argv)-1 {
|
||||||
|
return "", fmt.Errorf("Proc '%s' called with wrong arg num", argv[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
body := x[1]
|
||||||
|
result, err := i.Eval(body)
|
||||||
|
if err == PICOL_RETURN {
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandProc(i *Interp, argv []string, pd interface{}) (string, error) {
|
||||||
|
if len(argv) != 4 {
|
||||||
|
return "", ArityErr(i, argv[0], argv)
|
||||||
|
}
|
||||||
|
return "", i.RegisterCommand(argv[1], CommandCallProc, []string{argv[2], argv[3]})
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandReturn(i *Interp, argv []string, pd interface{}) (string, error) {
|
||||||
|
if len(argv) != 1 && len(argv) != 2 {
|
||||||
|
return "", ArityErr(i, argv[0], argv)
|
||||||
|
}
|
||||||
|
var r string
|
||||||
|
if len(argv) == 2 {
|
||||||
|
r = argv[1]
|
||||||
|
}
|
||||||
|
return r, PICOL_RETURN
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandError(i *Interp, argv []string, pd interface{}) (string, error) {
|
||||||
|
if len(argv) != 1 && len(argv) != 2 {
|
||||||
|
return "", ArityErr(i, argv[0], argv)
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf(argv[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommandPuts(i *Interp, argv []string, pd interface{}) (string, error) {
|
||||||
|
if len(argv) != 2 {
|
||||||
|
return "", fmt.Errorf("Wrong number of args for %s %s", argv[0], argv)
|
||||||
|
}
|
||||||
|
fmt.Println(argv[1])
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interp) RegisterCoreCommands() {
|
||||||
|
name := [...]string{"+", "-", "*", "/", ">", ">=", "<", "<=", "==", "!="}
|
||||||
|
for _, n := range name {
|
||||||
|
i.RegisterCommand(n, CommandMath, nil)
|
||||||
|
}
|
||||||
|
i.RegisterCommand("set", CommandSet, nil)
|
||||||
|
i.RegisterCommand("unset", CommandUnset, nil)
|
||||||
|
i.RegisterCommand("if", CommandIf, nil)
|
||||||
|
i.RegisterCommand("while", CommandWhile, nil)
|
||||||
|
i.RegisterCommand("break", CommandRetCodes, nil)
|
||||||
|
i.RegisterCommand("continue", CommandRetCodes, nil)
|
||||||
|
i.RegisterCommand("proc", CommandProc, nil)
|
||||||
|
i.RegisterCommand("return", CommandReturn, nil)
|
||||||
|
i.RegisterCommand("error", CommandError, nil)
|
||||||
|
i.RegisterCommand("puts", CommandPuts, nil)
|
||||||
|
}
|
250
src/picol/parser.go
Normal file
250
src/picol/parser.go
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
package picol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PT_ESC = iota
|
||||||
|
PT_STR
|
||||||
|
PT_CMD
|
||||||
|
PT_VAR
|
||||||
|
PT_SEP
|
||||||
|
PT_EOL
|
||||||
|
PT_EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
type Parser struct {
|
||||||
|
text string
|
||||||
|
p, start, end, ln int
|
||||||
|
insidequote int
|
||||||
|
Type int
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitParser(text string) *Parser {
|
||||||
|
return &Parser{text, 0, 0, 0, len(text), 0, PT_EOL}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) next() {
|
||||||
|
_, w := utf8.DecodeRuneInString(p.text[p.p:])
|
||||||
|
p.p += w
|
||||||
|
p.ln -= w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) current() rune {
|
||||||
|
r, _ := utf8.DecodeRuneInString(p.text[p.p:])
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) token() (t string) {
|
||||||
|
defer recover()
|
||||||
|
return p.text[p.start:p.end]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseSep() string {
|
||||||
|
p.start = p.p
|
||||||
|
for ; p.p < len(p.text); p.next() {
|
||||||
|
if !unicode.IsSpace(p.current()) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.end = p.p
|
||||||
|
p.Type = PT_SEP
|
||||||
|
return p.token()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseEol() string {
|
||||||
|
p.start = p.p
|
||||||
|
|
||||||
|
for ; p.p < len(p.text); p.next() {
|
||||||
|
if p.current() == ';' || unicode.IsSpace(p.current()) {
|
||||||
|
// pass
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.end = p.p
|
||||||
|
p.Type = PT_EOL
|
||||||
|
return p.token()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseCommand() string {
|
||||||
|
level, blevel := 1, 0
|
||||||
|
p.next() // skip
|
||||||
|
p.start = p.p
|
||||||
|
Loop:
|
||||||
|
for {
|
||||||
|
switch {
|
||||||
|
case p.ln == 0:
|
||||||
|
break Loop
|
||||||
|
case p.current() == '[' && blevel == 0:
|
||||||
|
level++
|
||||||
|
case p.current() == ']' && blevel == 0:
|
||||||
|
level--
|
||||||
|
if level == 0 {
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
case p.current() == '\\':
|
||||||
|
p.next()
|
||||||
|
case p.current() == '{':
|
||||||
|
blevel++
|
||||||
|
case p.current() == '}' && blevel != 0:
|
||||||
|
blevel--
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
p.end = p.p
|
||||||
|
p.Type = PT_CMD
|
||||||
|
if p.p < len(p.text) && p.current() == ']' {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
return p.token()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseVar() string {
|
||||||
|
p.next() // skip the $
|
||||||
|
p.start = p.p
|
||||||
|
|
||||||
|
if p.current() == '{' {
|
||||||
|
p.Type = PT_VAR
|
||||||
|
return p.parseBrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
for p.p < len(p.text) {
|
||||||
|
c := p.current()
|
||||||
|
if unicode.IsLetter(c) || ('0' <= c && c <= '9') || c == '_' {
|
||||||
|
p.next()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.start == p.p { // It's just a single char string "$"
|
||||||
|
p.start = p.p - 1
|
||||||
|
p.end = p.p
|
||||||
|
p.Type = PT_STR
|
||||||
|
} else {
|
||||||
|
p.end = p.p
|
||||||
|
p.Type = PT_VAR
|
||||||
|
}
|
||||||
|
return p.token()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseBrace() string {
|
||||||
|
level := 1
|
||||||
|
p.next() // skip
|
||||||
|
p.start = p.p
|
||||||
|
|
||||||
|
Loop:
|
||||||
|
for p.p < len(p.text) {
|
||||||
|
c := p.current()
|
||||||
|
switch {
|
||||||
|
case p.ln >= 2 && c == '\\':
|
||||||
|
p.next()
|
||||||
|
case p.ln == 0 || c == '}':
|
||||||
|
level--
|
||||||
|
if level == 0 || p.ln == 0 {
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
case c == '{':
|
||||||
|
level++
|
||||||
|
}
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
p.end = p.p
|
||||||
|
if p.ln != 0 { // Skip final closed brace
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
return p.token()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseString() string {
|
||||||
|
newword := p.Type == PT_SEP || p.Type == PT_EOL || p.Type == PT_STR
|
||||||
|
|
||||||
|
if c := p.current(); newword && c == '{' {
|
||||||
|
p.Type = PT_STR
|
||||||
|
return p.parseBrace()
|
||||||
|
} else if newword && c == '"' {
|
||||||
|
p.insidequote = 1
|
||||||
|
p.next() // skip
|
||||||
|
}
|
||||||
|
|
||||||
|
p.start = p.p
|
||||||
|
|
||||||
|
Loop:
|
||||||
|
for ; p.ln != 0; p.next() {
|
||||||
|
switch p.current() {
|
||||||
|
case '\\':
|
||||||
|
if p.ln >= 2 {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
case '$', '[':
|
||||||
|
break Loop
|
||||||
|
case '"':
|
||||||
|
if p.insidequote != 0 {
|
||||||
|
p.end = p.p
|
||||||
|
p.Type = PT_ESC
|
||||||
|
p.next()
|
||||||
|
p.insidequote = 0
|
||||||
|
return p.token()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.current() == ';' || unicode.IsSpace(p.current()) {
|
||||||
|
if p.insidequote == 0 {
|
||||||
|
break Loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.end = p.p
|
||||||
|
p.Type = PT_ESC
|
||||||
|
return p.token()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseComment() string {
|
||||||
|
for p.ln != 0 && p.current() != '\n' {
|
||||||
|
p.next()
|
||||||
|
}
|
||||||
|
return p.token()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) GetToken() string {
|
||||||
|
for {
|
||||||
|
if p.ln == 0 {
|
||||||
|
if p.Type != PT_EOL && p.Type != PT_EOF {
|
||||||
|
p.Type = PT_EOL
|
||||||
|
} else {
|
||||||
|
p.Type = PT_EOF
|
||||||
|
}
|
||||||
|
return p.token()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p.current() {
|
||||||
|
case ' ', '\t', '\r':
|
||||||
|
if p.insidequote != 0 {
|
||||||
|
return p.parseString()
|
||||||
|
}
|
||||||
|
return p.parseSep()
|
||||||
|
case '\n', ';':
|
||||||
|
if p.insidequote != 0 {
|
||||||
|
return p.parseString()
|
||||||
|
}
|
||||||
|
return p.parseEol()
|
||||||
|
case '[':
|
||||||
|
return p.parseCommand()
|
||||||
|
case '$':
|
||||||
|
return p.parseVar()
|
||||||
|
case '#':
|
||||||
|
if p.Type == PT_EOL {
|
||||||
|
p.parseComment()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return p.parseString()
|
||||||
|
default:
|
||||||
|
return p.parseString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p.token() /* unreached */
|
||||||
|
}
|
138
src/picol/picol.go
Normal file
138
src/picol/picol.go
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package picol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
PICOL_RETURN = errors.New("RETURN")
|
||||||
|
PICOL_BREAK = errors.New("BREAK")
|
||||||
|
PICOL_CONTINUE = errors.New("CONTINUE")
|
||||||
|
)
|
||||||
|
|
||||||
|
type Var string
|
||||||
|
type CmdFunc func(i *Interp, argv []string, privdata interface{}) (string, error)
|
||||||
|
type Cmd struct {
|
||||||
|
fn CmdFunc
|
||||||
|
privdata interface{}
|
||||||
|
}
|
||||||
|
type CallFrame struct {
|
||||||
|
vars map[string]Var
|
||||||
|
parent *CallFrame
|
||||||
|
}
|
||||||
|
type Interp struct {
|
||||||
|
level int
|
||||||
|
callframe *CallFrame
|
||||||
|
commands map[string]Cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitInterp() *Interp {
|
||||||
|
return &Interp{
|
||||||
|
level: 0,
|
||||||
|
callframe: &CallFrame{vars: make(map[string]Var)},
|
||||||
|
commands: make(map[string]Cmd),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interp) Var(name string) (Var, bool) {
|
||||||
|
for frame := i.callframe; frame != nil; frame = frame.parent {
|
||||||
|
v, ok := frame.vars[name]
|
||||||
|
if ok {
|
||||||
|
return v, ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
func (i *Interp) SetVar(name, val string) {
|
||||||
|
i.callframe.vars[name] = Var(val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interp) UnsetVar(name string) {
|
||||||
|
delete(i.callframe.vars, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interp) Command(name string) *Cmd {
|
||||||
|
v, ok := i.commands[name]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &v
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *Interp) RegisterCommand(name string, fn CmdFunc, privdata interface{}) error {
|
||||||
|
c := i.Command(name)
|
||||||
|
if c != nil {
|
||||||
|
return fmt.Errorf("Command '%s' already defined", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
i.commands[name] = Cmd{fn, privdata}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* EVAL! */
|
||||||
|
func (i *Interp) Eval(t string) (string, error) {
|
||||||
|
p := InitParser(t)
|
||||||
|
var result string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
argv := []string{}
|
||||||
|
|
||||||
|
for {
|
||||||
|
prevtype := p.Type
|
||||||
|
// XXX
|
||||||
|
t = p.GetToken()
|
||||||
|
if p.Type == PT_EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch p.Type {
|
||||||
|
case PT_VAR:
|
||||||
|
v, ok := i.Var(t)
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("No such variable '%s'", t)
|
||||||
|
}
|
||||||
|
t = string(v)
|
||||||
|
case PT_CMD:
|
||||||
|
result, err = i.Eval(t)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
} else {
|
||||||
|
t = result
|
||||||
|
}
|
||||||
|
case PT_ESC:
|
||||||
|
// XXX: escape handling missing!
|
||||||
|
case PT_SEP:
|
||||||
|
prevtype = p.Type
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a complete command + args. Call it!
|
||||||
|
if p.Type == PT_EOL {
|
||||||
|
prevtype = p.Type
|
||||||
|
if len(argv) != 0 {
|
||||||
|
c := i.Command(argv[0])
|
||||||
|
if c == nil {
|
||||||
|
return "", fmt.Errorf("No such command '%s'", argv[0])
|
||||||
|
}
|
||||||
|
result, err = c.fn(i, argv, c.privdata)
|
||||||
|
if err != nil {
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Prepare for the next command
|
||||||
|
argv = []string{}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have a new token, append to the previous or as new arg?
|
||||||
|
if prevtype == PT_SEP || prevtype == PT_EOL {
|
||||||
|
argv = append(argv, t)
|
||||||
|
} else { // Interpolation
|
||||||
|
argv[len(argv)-1] = strings.Join([]string{argv[len(argv)-1], t}, "")
|
||||||
|
}
|
||||||
|
prevtype = p.Type
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
85
src/router/router.go
Normal file
85
src/router/router.go
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
gs "git.dragonheim.net/dragonheim/gagent/src/gstructs"
|
||||||
|
picol "git.dragonheim.net/dragonheim/gagent/src/picol"
|
||||||
|
|
||||||
|
zmq "github.com/pebbe/zmq4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pop(msg []string) (head, tail []string) {
|
||||||
|
if msg[1] == "" {
|
||||||
|
head = msg[:2]
|
||||||
|
tail = msg[2:]
|
||||||
|
} else {
|
||||||
|
head = msg[:1]
|
||||||
|
tail = msg[1:]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main is the initiation function for a Router
|
||||||
|
func Main(config gs.GagentConfig) {
|
||||||
|
/*
|
||||||
|
* This is our router task.
|
||||||
|
*
|
||||||
|
* It uses the multi-threaded server model to deal requests out to a
|
||||||
|
* pool of workers and route replies back to clients. One worker can
|
||||||
|
* handle one request at a time but one client can talk to multiple
|
||||||
|
* workers at once.
|
||||||
|
*
|
||||||
|
* Frontend socket talks to clients over TCP
|
||||||
|
*/
|
||||||
|
frontend, _ := zmq.NewSocket(zmq.ROUTER)
|
||||||
|
defer frontend.Close()
|
||||||
|
|
||||||
|
frontend.Bind(fmt.Sprintf("tcp://%s:%d", config.ListenAddr, config.ListenPort))
|
||||||
|
|
||||||
|
// Backend socket talks to workers over inproc
|
||||||
|
backend, _ := zmq.NewSocket(zmq.DEALER)
|
||||||
|
defer backend.Close()
|
||||||
|
backend.Bind("inproc://backend")
|
||||||
|
|
||||||
|
// Launch pool of worker threads, precise number is not critical
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
go agentRouter(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect backend to frontend via a proxy
|
||||||
|
err := zmq.Proxy(frontend, backend, nil)
|
||||||
|
log.Fatalln("Proxy interrupted:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each worker task works on one request at a time and sends a random number
|
||||||
|
// of replies back, with random delays between replies:
|
||||||
|
|
||||||
|
func agentRouter(workerNum int) {
|
||||||
|
interp := picol.InitInterp()
|
||||||
|
interp.RegisterCoreCommands()
|
||||||
|
|
||||||
|
worker, _ := zmq.NewSocket(zmq.DEALER)
|
||||||
|
defer worker.Close()
|
||||||
|
worker.Connect("inproc://backend")
|
||||||
|
|
||||||
|
for {
|
||||||
|
// The DEALER socket gives us the reply envelope and message
|
||||||
|
msg, _ := worker.RecvMessage(0)
|
||||||
|
identity, content := pop(msg)
|
||||||
|
|
||||||
|
// Send 0..4 replies back
|
||||||
|
replies := rand.Intn(5)
|
||||||
|
for reply := 0; reply < replies; reply++ {
|
||||||
|
// Sleep for some fraction of a second
|
||||||
|
time.Sleep(time.Duration(rand.Intn(1000)+1) * time.Millisecond)
|
||||||
|
|
||||||
|
fmt.Println(fmt.Sprintf("Worker %d: %s", workerNum, identity))
|
||||||
|
fmt.Println(fmt.Sprintf("Worker %d: %s", workerNum, content))
|
||||||
|
worker.SendMessage(identity, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
75
src/worker/worker.go
Normal file
75
src/worker/worker.go
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
package worker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
// "log"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
gs "git.dragonheim.net/dragonheim/gagent/src/gstructs"
|
||||||
|
picol "git.dragonheim.net/dragonheim/gagent/src/picol"
|
||||||
|
|
||||||
|
zmq "github.com/pebbe/zmq4"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pop(msg []string) (head, tail []string) {
|
||||||
|
if msg[1] == "" {
|
||||||
|
head = msg[:2]
|
||||||
|
tail = msg[2:]
|
||||||
|
} else {
|
||||||
|
head = msg[:1]
|
||||||
|
tail = msg[1:]
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main is the initiation function for a Worker
|
||||||
|
func Main(config gs.GagentConfig) {
|
||||||
|
// Frontend socket talks to clients over TCP
|
||||||
|
frontend, _ := zmq.NewSocket(zmq.ROUTER)
|
||||||
|
defer frontend.Close()
|
||||||
|
connectString := fmt.Sprintf("tcp://%s", config.Routers[0].RouterAddr)
|
||||||
|
frontend.Bind(connectString)
|
||||||
|
|
||||||
|
// Backend socket talks to workers over inproc
|
||||||
|
backend, _ := zmq.NewSocket(zmq.DEALER)
|
||||||
|
defer backend.Close()
|
||||||
|
backend.Bind("inproc://backend")
|
||||||
|
|
||||||
|
// Launch pool of agent handlers
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
go agentHandler(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect backend to frontend via a proxy
|
||||||
|
// err := zmq.Proxy(frontend, backend, nil)
|
||||||
|
// log.Fatalln("Proxy interrupted:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each worker task works on one request at a time and sends a random number
|
||||||
|
// of replies back, with random delays between replies:
|
||||||
|
|
||||||
|
func agentHandler(workerNum int) {
|
||||||
|
interp := picol.InitInterp()
|
||||||
|
interp.RegisterCoreCommands()
|
||||||
|
|
||||||
|
worker, _ := zmq.NewSocket(zmq.DEALER)
|
||||||
|
defer worker.Close()
|
||||||
|
worker.Connect("inproc://backend")
|
||||||
|
|
||||||
|
for {
|
||||||
|
// The DEALER socket gives us the reply envelope and message
|
||||||
|
msg, _ := worker.RecvMessage(0)
|
||||||
|
identity, content := pop(msg)
|
||||||
|
|
||||||
|
// Send 0..4 replies back
|
||||||
|
replies := rand.Intn(5)
|
||||||
|
for reply := 0; reply < replies; reply++ {
|
||||||
|
// Sleep for some fraction of a second
|
||||||
|
time.Sleep(time.Duration(rand.Intn(1000)+1) * time.Millisecond)
|
||||||
|
|
||||||
|
fmt.Println(fmt.Sprintf("Worker %d: %s", workerNum, identity))
|
||||||
|
worker.SendMessage(identity, content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue