This page describes the development environment installation of lorhammer and gives some architecture explanations.
cd $GOPATH/src
git clone git@gitlab.com:itk.fr/lorhammer.git
cd lorhammer
make
The binaries of lorhammers are created in ./build
directory.
Follow the quickstart and be sure to have lorhammer, orchestrator and tools working.
We use hugo to generate static html from markdown.
You can find documentation files in multiple directory.
All .md
files at root path (README, CHANGELOG…) are used. We also use doc/content/*.md
.
The theme can be find in doc/themes/hugorha
after the first call to the make doc
(see below).
make doc
This script will install all requirements and generate the doc.
make doc-dev
To launch a standalone web browser add -dev
flag and open http://localhost:1313/.
Each time you modify a doc file, the doc will be refresh in your browser.
A test type is a launcher of gateways. It describes how the orchestrator will build gateways to trigger the scenario.
Today we have 3 types of tests, the ‘none’ type does nothing, the ‘one shot’ type builds and launches nbGateway, the ‘repeat’ type is the same as ‘one shot’ but creates nbGateway of gateways every repeatTime.
A test type is a function that takes a Test, a model.Init and a client tool.mqtt. This function is started in a go routine and must call command.LaunchScenario(mqttClient, model.init) when nbGateway need to be launched.
The ‘none’ implementation is the most simple :
func startNone(_ Test, _ model.Init, _ tools.Mqtt) {
LOG_NONE.WithField("type", "none").Warn("Nothing to test")
}
You can find other implementations in src/orchestrator/testType
package.
A test type is also represented by a testType.Type type like that :
const TypeNone Type = "none"
To finish you need to add your implementation in the map hosted by src/orchestrator/testType/testType.go
:
var testers = make(map[Type]func(test Test, init model.Init, mqttClient tools.Mqtt))
func init() {
testers[TypeNone] = startNone
}
Test it by creating a scenario file with your test type.
We can imagine make pic
test with creation and deletion of gateways over the time. Or a ramp node test which build node in existing gateway over the time.
A provisioner permit to register sensors and gateways to a network-server. Like that the network-server can accept messages from sensors and gateways.
Today we have 3 kind of provisioner : none to not provision, loraserver to provision a loraserver network server and a generic HTTP provisioner.
The HTTP provisioner send an http post to the creationApiUrl, its body is the model godoc in JSON format.
A provisioner must have a fabric function which take config json.RawMessage in parameters and return an implementation of Provisioner
interface :
type provisioner interface {
Provision(sensorsToRegister model.Register) error
DeProvision() error
}
The simpler implementation is the NoneProvisioner :
type none struct{}
func NewNone(_ json.RawMessage) (provisioner, error) { return none{}, nil }
func (_ none) Provision(sensorsToRegister model.Register) error { return nil }
func (_ none) DeProvision() error { return nil }
The fabric function must have a provisioning.Type :
const NoneType = Type("none")
And you need to register your implementation in the map hosted by src/orchestrator/provisioning/provisioning.go
:
var provisioners = make(map[Type]func(config json.RawMessage) (provisioner, error))
func init() {
provisioners[NoneType] = NewNone
provisioners[LoraserverType] = NewLoraserver
provisioners[HttpType] = NewHttpProvisioner
}
Test it by creating a scenario file with your provisioning type and the configuration required by it.
We will happy to see lot of implementations of provisioner for different network-server open-source or proprietary.
A deployer permit to the orchestrator to deploy and instantiate lorhammers.
Today we have 5 kind of deployer. None to do nothing. Local to launch a local (sub-process) instance of lorhammer. Distant to scp and start over ssh lorhammers on other server. And amazon to provision server on aws, deploy and launch lorhammers.
To add a deployer you need to have a fabric function which take config json.RawMessage and a mqtt client in parameters and return an implementation of Deployer
interface :
type Deployer interface {
RunBefore() error
Deploy() error
RunAfter() error
}
The None implementation is really simple :
type none struct{}
func (_ none) RunBefore() error { return nil }
func (_ none) Deploy() error { return nil }
func (_ none) RunAfter() error { return nil }
func NewNone(_ json.RawMessage, _ tools.Mqtt) (Deployer, error) {
return none{}, nil
}
A deployer need to have a type :
const TypeNone = Type("none")
And you need to register your implementation in the map hosted by src/orchestrator/deploy/deploy.go
:
var deployers = make(map[Type]func(config json.RawMessage, mqttClient tools.Mqtt) (Deployer, error))
func init() {
deployers[TypeNone] = NewNone
deployers[TypeDistant] = NewDistantFromJson
deployers[TypeAmazon] = NewAmazonFromJson
deployers[TypeLocal] = NewLocalFromJson
}
We can image adding new deployer like DigitalOcean, Kubernetes or Swarm…
A checker is a way to verify that what have been sent by lorhammers have been correctly received by the IOT plateform. It aims to count the messages sent by lorhammers and check if result is equal to the expected value. Checkers also allow to test if platform has accepted x messages (and not only received them). Useful for continious integration, orchestrator will exit(1) if all checks don’t pass.
Today we have 3 kinds of checkers : ‘none’ for no checker, ‘prometheus’ to check number of messages sent againt the ones recieved by lorhammers and ‘kafka’ to check the content of messages.
To add a checker you need to have a factory function that takes a config json.RawMessage as parameter and returns an implementation of Checker
interface :
type CheckerSuccess interface {
Details() map[string]interface{}
}
type CheckerError interface {
Details() map[string]interface{}
}
type Checker interface {
Check() ([]CheckerSuccess, []CheckerError)
}
The ‘none’ implementation example :
type none struct{}
func newNone(_ json.RawMessage) (Checker, error) {
return none{}, nil
}
func (_ none) Check() ([]CheckerSuccess, []CheckerError) {
return make([]CheckerSuccess, 0), make([]CheckerError, 0)
}
A checker needs to have a type :
const NoneType = Type("none")
A registration is needed for your implementation in the map hosted by src/orchestrator/checker/checker.go
:
var checkers = make(map[Type]func(config json.RawMessage) (Checker, error))
func init() {
checkers[NoneType] = newNone
checkers[PrometheusType] = newPrometheus
checkers[kafkaType] = newKafka
}
We can imagine adding a file checker, if your application outputs processing on csv format, you will be abble to check if the csv is confirmed with the expected results. Execute it every time you push new code on your platform what enables you to have a continuous integration testing suite.