An MQTT broker, should you wish to publish messages locally.
mosquitto
is extremely easy to set up.
An optional HTTP server, should you need to request payloads from localhost. This does not need to be more complicated than Python's SimpleHTTPServer module.
yggd
requires D-Bus and systemd in order to run locally. The header files from
your distribution's "devel" packages must be installed in order to compile
yggd
. A current list of required packages can be found in the top-level
meson.build
file. The package names will vary depending on your distribution.
mqttcli
is recommended to make use of the pub
and sub
programs.
This quickstart assumes an unencrypted MQTT broker is listening on localhost:1883
.
Typically, all that is required to start the mosquitto
broker is: systemctl start mosquitto
.
Start yggd
on the user's session bus, connecting it to the MQTT broker
localhost
over an unencrypted TCP connection, log judiciously to
stdout
.
go run ./cmd/yggd --server tcp://localhost:1883 --log-level trace --client-id $(hostname)
Start an echo
worker.
go run ./worker/echo -log-level trace
Subscribe to all the MQTT topics the yggd
client will publish and subscribe
to monitor the MQTT traffic.
sub -broker tcp://localhost:1883 -topic yggdrasil/$(hostname)/#
Publish a "PING" command to the yggd
"control/in" topic to test sending
an MQTT message to yggd
.
echo '{"command":"ping"}' | \
go run ./cmd/yggctl generate control-message --type command - | \
pub -broker tcp://localhost:1883 -topic yggdrasil/$(hostname)/control/in
You should see the message logged to the output of sub
in Terminal
3 and receipt of the message logged in the output of yggd
in
Terminal 1.
Now publish a data message containing the string "hello" to the echo
worker.
echo '"hello"' | \
go run ./cmd/yggctl generate data-message --directive echo - | \
pub -broker tcp://localhost:1883 -topic yggdrasil/$(hostname)/data/in
Again, you should see the message logged by the sub
command in Terminal
3, the receipt of the message logged in the output of yggd
in
Terminal 1. This time, you should see output in Terminal
2 when the echo worker receives the message.
This quickstart assumes yggdrasil will communicate with workers over the user's
private session D-Bus service. To run yggd
on the system bus, install the
D-Bus security policy allowing root to own the appropriate names on the system
bus.
install -D -m644 ./data/dbus/yggd.conf /usr/share/dbus-1/system.d/yggd.conf
Then start yggd
, ensuring the environment variable DBUS_SESSION_BUS_ADDRESS
is undefined.
sudo go run ./cmd/yggd --server tcp://localhost:1883 --log-level trace --client-id $(hostname)
yggd
can be compiled using meson, or can be run directly with the go run
command. It can read configuration values from a file by running
yggd
with the --config
option. A sample configuration file is included in
the data/yggdrasil
directory.
sudo go run ./cmd/yggd --config ./data/yggdrasil/config.toml
Many default paths (such as Prefix, BinDir, LocalstateDir, etc), as well as some
other compile-time constants, can be specified by providing a linker -X
flag
argument. See the meson built-in
options or
internal/constants/constants.go
for a complete list.
It is also possible to compile yggd
and all additional programs in this
project into an RPM suitable for installation on Fedora-based distributions. See
the README in dist/srpm
for details.
yggd
can be run within the Delve debugger to make development easier. Install
dlv
in the guest if it is not already installed:
sudo go install github.com/go-delve/delve/cmd/dlv@latest
You may need to open TCP port 2345 on the guest. For example, to open the port using firewalld, run:
sudo firewall-cmd --zone public --add-port 2345/tcp --permanent
Start dlv
using the debug
command:
sudo /root/go/bin/dlv debug --api-version 2 --headless --listen 0.0.0.0:2345 \
./cmd/yggd -- --config ./data/yggdrasil/config.toml
Next, from your host, connect to the dlv server, using either dlv attach
or by
creating a launch configuration in Visual Studio Code:
{
"name": "Connect to server",
"type": "go",
"request": "attach",
"mode": "remote",
"remotePath": "${workspaceFolder}",
"port": 2345,
"host": "192.168.122.98"
}
Make sure to replace "host" with your virtual machine's IP address.
yggctl
is a program that can interact with a running yggd
process over an
RPC interface. It is currently very limited in its functionality. Until this
program provides usefulness to users, rather than just to developers, it will
not be installed by default. It can be run directly with go run
, or installed
with go install
.
go install ./cmd/yggctl
See the output of yggctl --help
for available commands.
echo
is a very simple reference implementation of a worker written in Go.
If you ran yggd
on a private session bus, you must run the echo
worker on
the same bus by specifying the DBUS_SESSION_BUS_ADDRESS
environment variable:
sudo DBUS_SESSION_BUS_ADDRESS=unix:abstract=yggd_demo go run ./worker/echo
mqttcli
is a separate program that is useful
for publishing messages and subscribing to topics on an MQTT broker. mqttcli
can be installed with go install
:
go install git.sr.ht/~spc/mqttcli/cmd/...
Or if you're running Fedora 34 or later, it can be installed directly with
dnf
:
dnf install mqttcli
If the environment variable MQTT_DEBUG
is set, yggd
will log all MQTT
messages sent and received. This can be useful for debugging.
With a running yggd
and echo
worker, it should be possible to publish a
message to the broker, destined for one of the topics yggd
is subscribed to.
To watch a topic for messages, subscribe to it with sub
:
sub -broker tcp://localhost:1883 -topic yggdrasil/$(hostname)/data/in \
-topic yggdrasil/$(hostname)/data/out -topic yggdrasil/$(hostname)/control/out
A client can be sent a PING
command by generating a control message and
publishing it to the client's "control/in" topic:
echo '{"command":"ping"}' | \
yggctl generate control-message --type command - | \
pub -broker tcp://localhost:1883 -topic yggdrasil/$(hostname)/control/in
Activity should occur on the terminal attached to yggd
, and a PONG
event
message should be received on the "control/out" topic, subscribed to in
Monitoring topics.
Similarly, a data message can be published to a client using yggctl generate
and pub
.
echo '"hello"' | \
yggctl generate data-message --directive echo - | \
pub -broker tcp://localhost:1883 -topic yggdrasil/$(hostname)/data/in
It is also possible to send data directly to a worker. This method does not publish a message to the MQTT broker. Instead, it calls a D-Bus method:
echo -n '"hello"' | yggctl dispatch -w "echo" -
- Commits follow the Conventional Commits pattern.
- Commit messages should include a concise subject line that completes the following phrase: "when applied, this commit will...". The body of the commit should further expand on this statement with additional relevant details.
- Communicate errors through return values, not logging. Library functions in particular should follow this guideline. You never know under which condition a library function will be called, so excessive logging should be avoided.
- Code useful to
cmd/*
packages or external third-party packages should exist in the top-level package. - Code useful to
cmd/*
packages, but not external packages should exist in the top-levelinternal
package. - Code should exist in a package only if it can be useful when imported exclusively.
- Code can exist in a package if it provides an alternative interface to another package, and the two packages cannot be imported together.
- Code should be formatted using
gofmt
andgoimports
before committing. As the saying goes "gofmt's style is no one's favorite, yet gofmt is everyone's favorite.". - Code should be formatted using
golines
, to wrap long lines.
In addition to these 3 "classics", A collection of Go style guides contains a wealth of resources on writing idiomatic Go.
Chat on Matrix: #yggd:matrix.org.