This repository provides an Arrowhead Golang SDK along with a CLI tool that simplifies the management of Arrowhead systems and facilitates the development of Arrowhead applications.
This document contains a short tutorial on how to develop and deploy car provider and consumer services, similar to the Java-based example in the guide: Arrowhead SOS Examples - Demo Car.
It should be possible to set up and run the car provider and consumer services simply by executing the commands in a terminal.
Download and install Golang.
Clone this repository and build the arrowhead CLI tool:
git clone git@github.com:your-username/arrowhead-client-go.git
cd arrowhead-golang-sdk
make
sudo make installFollow the setup instructions in https://github.com/eislab-cps/arrowhead-core-docker repository. Note: You must modify /etc/hosts as described in the instructions.
git clone git@github.com:eislab-cps/arrowhead-core-docker.git
cd arrowhead-core-docker
docker-compose upAll configurations are managed using environment variables.
Create a file called arrowhead.env and add the following content, replacing XXXXX with the actual path to your Arrowhead core services certificates:
export ARROWHEAD_VERBOSE="true"
export ARROWHEAD_ASCII="true"
# Certificates configuration
export ARROWHEAD_KEYSTORE_PASSWORD="123456"
export ARROWHEAD_ROOT_KEYSTORE="/XXXXX/arrowhead-core-docker/c1/certificates/master.p12"
export ARROWHEAD_ROOT_KEYSTORE_ALIAS="arrowhead.eu"
export ARROWHEAD_CLOUD_KEYSTORE="/XXXXX/arrowhead-core-docker/c1/certificates/c1.p12"
export ARROWHEAD_CLOUD_KEYSTORE_ALIAS="c1.ltu.arrowhead.eu"
export ARROWHEAD_SYSOPS_KEYSTORE="/XXXXX/arrowhead-core-docker/c1/certificates/sysop.p12"
export ARROWHEAD_TRUSTSTORE="/XXXXX/arrowhead-core-docker/c1/certificates/truststore.pem"
# Arrowhead Core Services configuration
export ARROWHEAD_TLS="true"
export ARROWHEAD_AUTHORIZATION_HOST="localhost"
export ARROWHEAD_AUTHORIZATION_PORT="8445"
export ARROWHEAD_SERVICEREGISTRY_HOST="localhost"
export ARROWHEAD_SERVICEREGISTRY_PORT="8443"
export ARROWHEAD_ORCHESTRATOR_HOST="localhost"
export ARROWHEAD_ORCHESTRATOR_PORT="8441"Remember to source the arrowhead.env file to load all configurations:
source arrowhead.envTry the Arrowhead CLI tool to see if it works:
arrowhead systems lsโญโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโฎ
โ ID โ SYSTEM NAME โ ADDRESS โ PORT โ
โโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโค
โ 1 โ serviceregistry โ c1-serviceregistry โ 8443 โ
โ 2 โ gateway โ c1-gateway โ 8453 โ
โ 3 โ eventhandler โ c1-eventhandler โ 8455 โ
โ 4 โ orchestrator โ c1-orchestrator โ 8441 โ
โ 5 โ authorization โ c1-authorization โ 8445 โ
โ 6 โ gatekeeper โ c1-gatekeeper โ 8449 โ
โฐโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโดโโโโโโโฏWe are going to:
-
Register the
carprovidersystem using the arrowhead CLI.- The CLI will automatically generate a PKCS#12 certificate.
-
Register the
carconsumersystem using the arrowhead CLI. -
Register the
create-carservice to thecarproviderand register theget-carservice to thecarprovider. -
Add authorization rules allowing the
carconsumerto access thecarproviderservices. -
Implement the
carproviderandcarconsumerapplications. -
Start the
carproviderandcarconsumer.- The
carconsumerwill use the Arrowhead orchestration core service to find the endpoint of thecarprovider. - It will then send an HTTP request to interact with it.
- The
mkdir carprovider;cd carproviderarrowhead systems register --name carprovider --address localhost --port 8880INFO[0000] Using openssl as certificate manager
Certificate request self-signature ok
subject=CN = carprovider.c1.ltu.arrowhead.eu
INFO[0000] System registered successfully, certificate stored in ./carprovider.p12 and ./carprovider.pub, config file stored in ./carprovider.envThe command will register a new system at the Arrowhead Service Registry core service, and generate a corresponding PKCS#12 certificate with CN carprovider.c1.ltu.arrowhead.eu.
ls.rw------- 4.2k johan 15 Feb 09:04 -N ๏
carprovider.p12
.rw-r--r-- 451 johan 15 Feb 09:04 -N ๏
carprovider.pubType the command below to list available systems:
arrowhead systems lsโญโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโฎ
โ ID โ SYSTEM NAME โ ADDRESS โ PORT โ
โโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโค
โ 1 โ serviceregistry โ c1-serviceregistry โ 8443 โ
โ 2 โ gateway โ c1-gateway โ 8453 โ
โ 3 โ eventhandler โ c1-eventhandler โ 8455 โ
โ 4 โ orchestrator โ c1-orchestrator โ 8441 โ
โ 5 โ authorization โ c1-authorization โ 8445 โ
โ 6 โ gatekeeper โ c1-gatekeeper โ 8449 โ
โ 103 โ carprovider โ localhost โ 8880 โ
โฐโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโดโโโโโโโฏWe can also filter to only list the car systems.
arrowhead systems ls --filter carโญโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโฎ
โ ID โ SYSTEM NAME โ ADDRESS โ PORT โ
โโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโค
โ 103 โ carprovider โ localhost โ 8880 โ
โฐโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโฏcd ..;mkdir carconsumer;cd carconsumerarrowhead systems register --name carconsumer --address localhost --port 8881INFO[0000] Using openssl as certificate manager
INFO[0000] System registered successfully, PKCS#12 certificate stored in ./carconsumer.p12 and ./carconsumer.pub, config file stored in ./carconsumer.envarrowhead systems ls --filter carโญโโโโโโฌโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโฎ
โ ID โ SYSTEM NAME โ ADDRESS โ PORT โ
โโโโโโโผโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโค
โ 103 โ carprovider โ localhost โ 8880 โ
โ 104 โ carconsumer โ localhost โ 8881 โ
โฐโโโโโโดโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโฏFirst let's register a function to create cars.
arrowhead services register --system carprovider --definition create-car --uri /carfactory -m POSTINFO[0000] Service registered HTTPMethod=POST ServiceDefinition=create-car ServiceURI=/carfactory SystemName=carproviderAlso, register a function to fetch cars.
arrowhead services register --system carprovider --definition get-car --uri /carfactory -m GETINFO[0000] Service registered HTTPMethod=GET ServiceDefinition=get-car ServiceURI=/carfactory SystemName=carproviderLets list all registered services:
arrowhead services ls --filter carโญโโโโโฌโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโฌโโโโโโโฌโโโโโโโโโโโโโโโโโโโโฎ
โ ID โ PROVIDER NAME โ URI โ SERVICE DEFINITION โ ADDRESS โ PORT โ METADATA โ
โโโโโโผโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโผโโโโโโโโโโโโโโโโโโโโค
โ 84 โ carprovider โ /carfactory โ create-car โ localhost โ 8880 โ http-method: POST โ
โ 85 โ carprovider โ /carfactory โ get-car โ localhost โ 8880 โ http-method: GET โ
โฐโโโโโดโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโดโโโโโโโดโโโโโโโโโโโโโโโโโโโโฏThe next step is to add an anothorization rule allowing the carconsumer to acccess the carprovider.
arrowhead auths add --consumer carconsumer --provider carprovider --service create-carNFO[0000] Authorization added AuthID=14arrowhead auths add --consumer carconsumer --provider carprovider --service get-carNFO[0000] Authorization added AuthID=15The carconsumer can now access the carprovider. List list all authorization rules.
arrowhead auths lsโญโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโฎ
โ ID โ CONSUMER SYSTEM NAME โ PROVIDER SYSTEM NAME โ SERVICE DEFINITION โ INTERFACES โ
โโโโโโผโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโค
โ 14 โ carconsumer โ carprovider โ create-car โ HTTP-SECURE-JSON โ
โ 15 โ carconsumer โ carprovider โ get-car โ HTTP-SECURE-JSON โ
โฐโโโโโดโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโฏLet's try to find the carprovider. Note that the we must use the certificate keystore (carconsumer.p12) of the carconsumer to run the command below.
arrowhead orchestrate --system carconsumer --address localhost --port 8881 --keystore ./carconsumer.p12 --password 123456 --service create-car --compactโญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ ORCHESTRATION RESULT โ
โโโโโโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโค
โ FIELD โ VALUE โ
โโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโค
โ Provider Address โ localhost โ
โ Provider Port โ 8880 โ
โ Service URI โ /carfactory โ
โ Service Definition โ create-car โ
โ Provider System Name โ carprovider โ
โฐโโโโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโฏ
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ AUTHORIZATION TOKENS โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ FIELD โ VALUE โ
โโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ HTTP-SECURE-JSON โ eyJhbGciOiJSU0EtT0FFUC0yNTYiLCJlbmMiOiJBMjU2Q0JDLU... โ
โฐโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ
โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ
โ INTERFACE โ
โโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโค
โ FIELD โ VALUE โ
โโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโค
โ Updated At โ 2023-06-08 12:01:21 โ
โ ID โ 1 โ
โ Interface Name โ HTTP-SECURE-JSON โ
โ Created At โ 2023-06-08 12:01:21 โ
โฐโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโฏCreate a file the provider directory called provider.go.
package main
import (
"encoding/json"
"fmt"
arrowhead "github.com/johankristianss/arrowhead/pkg/arrowhead"
"github.com/johankristianss/arrowhead/pkg/rpc"
)
type Car struct {
Brand string `json:"brand"`
Color string `json:"color"`
}
type InMemoryCarRepository struct {
cars []Car
}
type CreateCarService struct {
inMemoryCarRepository *InMemoryCarRepository
}
func (s *CreateCarService) HandleRequest(params *arrowhead.Params) ([]byte, error) {
fmt.Println("CreateCarService called, creating car")
car := Car{}
err := json.Unmarshal(params.Payload, &car)
if err != nil {
return nil, err
}
fmt.Println("Car: ", car)
s.inMemoryCarRepository.cars = append(s.inMemoryCarRepository.cars, car)
return nil, nil
}
type GetCarService struct {
inMemoryCarRepository *InMemoryCarRepository
}
func (s *GetCarService) HandleRequest(params *arrowhead.Params) ([]byte, error) {
carsJSON, err := json.Marshal(s.inMemoryCarRepository.cars)
if err != nil {
return nil, err
}
return carsJSON, nil
}
func checkError(err error) {
if err != nil {
fmt.Println(err)
}
}
func main() {
framework, err := arrowhead.CreateFramework()
checkError(err)
inMemoryCarRepository := &InMemoryCarRepository{}
createCarService := &CreateCarService{inMemoryCarRepository: inMemoryCarRepository}
getCarService := &GetCarService{inMemoryCarRepository: inMemoryCarRepository}
framework.HandleService(createCarService, rpc.POST, "create-car", "/carfactory")
framework.HandleService(getCarService, rpc.GET, "get-car", "/carfactory")
err = framework.ServeForever()
checkError(err)
}Paste this source code to a file called consumer.go in the consumer directory.
package main
import (
"encoding/json"
"fmt"
arrowhead "github.com/johankristianss/arrowhead/pkg/arrowhead"
)
type Car struct {
Brand string `json:"brand"`
Color string `json:"color"`
}
func checkError(err error) {
if err != nil {
fmt.Println(err)
}
}
func main() {
framework, err := arrowhead.CreateFramework()
checkError(err)
// Buiid the request
params := arrowhead.EmptyParams()
car := Car{Brand: "Toyota", Color: "Red"}
carJSON, err := json.Marshal(car)
checkError(err)
params.Payload = carJSON
// Send the request
res, err := framework.SendRequest("create-car", params)
checkError(err)
// Fetch cars
res, err = framework.SendRequest("get-car", arrowhead.EmptyParams())
checkError(err)
// Print the response
cars := []Car{}
err = json.Unmarshal(res, &cars)
checkError(err)
for _, car := range cars {
fmt.Println(car.Brand, car.Color)
}
}Open a new terminal window and type to start the carprovider:
cd carprovider
source carprovider.env
go run provider.goOpen an another terminal and start the carconsumer:
cd carconsumer
source carconsumer.env
go run consumer.go๐ Congratulations! You have successfully implemented and deployed Arrowhead-based car provider and consumer services in Golang! ๐
This diagram shows the overall architecture of the car demo system, including all Arrowhead core services and the interaction between the car provider and consumer.
flowchart TB
subgraph "Arrowhead Core Services"
SR[Service Registry<br/>Port: 8443]
ORCH[Orchestrator<br/>Port: 8441]
AUTH[Authorization<br/>Port: 8445]
GW[Gateway<br/>Port: 8453]
EH[Event Handler<br/>Port: 8455]
GK[Gatekeeper<br/>Port: 8449]
end
subgraph "Car Provider System"
CP[Car Provider<br/>localhost:8880]
REPO[(In-Memory<br/>Car Repository)]
subgraph "Provider Services"
CREATE[create-car<br/>POST /carfactory]
GET[get-car<br/>GET /carfactory]
end
CP --> CREATE
CP --> GET
CREATE --> REPO
GET --> REPO
end
subgraph "Car Consumer System"
CC[Car Consumer<br/>localhost:8881]
end
%% Registration flows
CP -.->|1. Register System| SR
CP -.->|2. Register Services| SR
CC -.->|3. Register System| SR
%% Authorization flow
AUTH -.->|4. Add Auth Rules| SR
%% Runtime orchestration flow
CC -->|5. Request Orchestration<br/>for create-car/get-car| ORCH
ORCH -->|6. Query Services| SR
ORCH -->|7. Check Authorization| AUTH
ORCH -->|8. Return Provider<br/>Endpoint + Token| CC
%% Service invocation
CC ==>|9. HTTP Request<br/>with Auth Token| CP
style CP fill:#90EE90
style CC fill:#87CEEB
style SR fill:#FFB6C1
style ORCH fill:#FFB6C1
style AUTH fill:#FFB6C1
style CREATE fill:#98FB98
style GET fill:#98FB98
style REPO fill:#DDA0DD
This diagram illustrates the complete sequence of interactions from system registration through service invocation.
sequenceDiagram
autonumber
participant CLI as Arrowhead CLI
participant CP as Car Provider<br/>(localhost:8880)
participant CC as Car Consumer<br/>(localhost:8881)
participant SR as Service Registry<br/>(Port 8443)
participant AUTH as Authorization<br/>(Port 8445)
participant ORCH as Orchestrator<br/>(Port 8441)
rect rgb(240, 248, 255)
Note over CLI,SR: Setup Phase: System & Service Registration
CLI->>SR: Register carprovider system<br/>(localhost:8880)
SR-->>CLI: System registered<br/>Generate certificate (carprovider.p12)
CLI->>SR: Register carconsumer system<br/>(localhost:8881)
SR-->>CLI: System registered<br/>Generate certificate (carconsumer.p12)
CLI->>SR: Register service: create-car<br/>(POST /carfactory)
SR-->>CLI: Service registered
CLI->>SR: Register service: get-car<br/>(GET /carfactory)
SR-->>CLI: Service registered
end
rect rgb(255, 250, 240)
Note over CLI,AUTH: Setup Phase: Authorization Rules
CLI->>AUTH: Add authorization rule<br/>(carconsumer โ carprovider: create-car)
AUTH-->>CLI: Authorization added (ID: 14)
CLI->>AUTH: Add authorization rule<br/>(carconsumer โ carprovider: get-car)
AUTH-->>CLI: Authorization added (ID: 15)
end
rect rgb(240, 255, 240)
Note over CP,CC: Runtime Phase: Service Execution
CP->>CP: Start provider<br/>framework.ServeForever()
Note over CP: Listening on port 8880<br/>Handles: create-car, get-car
CC->>CC: Start consumer<br/>Build Car{Brand: Toyota, Color: Red}
end
rect rgb(255, 240, 245)
Note over CC,ORCH: Runtime Phase: Service Discovery (create-car)
CC->>ORCH: Request orchestration for "create-car"<br/>with carconsumer certificate
ORCH->>SR: Query service: create-car
SR-->>ORCH: Service details: carprovider<br/>(localhost:8880, /carfactory, POST)
ORCH->>AUTH: Check authorization<br/>(carconsumer โ carprovider: create-car)
AUTH-->>ORCH: Authorized + Auth Token
ORCH-->>CC: Provider endpoint + Auth Token<br/>(localhost:8880/carfactory)
end
rect rgb(255, 255, 224)
Note over CC,CP: Runtime Phase: Service Invocation
CC->>CP: POST /carfactory<br/>Body: {Brand: "Toyota", Color: "Red"}<br/>Header: Auth Token
CP->>CP: CreateCarService.HandleRequest()<br/>Store car in repository
CP-->>CC: 200 OK
CC->>ORCH: Request orchestration for "get-car"<br/>with carconsumer certificate
ORCH->>SR: Query service: get-car
SR-->>ORCH: Service details
ORCH->>AUTH: Check authorization
AUTH-->>ORCH: Authorized + Auth Token
ORCH-->>CC: Provider endpoint + Auth Token
CC->>CP: GET /carfactory<br/>Header: Auth Token
CP->>CP: GetCarService.HandleRequest()<br/>Retrieve cars from repository
CP-->>CC: 200 OK<br/>[{Brand: "Toyota", Color: "Red"}]
CC->>CC: Print: Toyota Red
end
Arrowhead Core Services:
- Service Registry: Stores information about all registered systems and services
- Orchestrator: Helps consumers discover providers and returns service endpoints
- Authorization: Manages access control rules between systems
Car Provider:
- Implements two services:
create-car(POST) andget-car(GET) - Stores cars in an in-memory repository
- Listens on port 8880 at
/carfactoryendpoint
Car Consumer:
- Requests orchestration to discover the provider
- Creates a car using the
create-carservice - Retrieves cars using the
get-carservice
Security:
- All systems use mutual TLS (mTLS) with PKCS#12 certificates
- Authorization tokens ensure only authorized systems can access services