-
Notifications
You must be signed in to change notification settings - Fork 37
Worker Developer Guide
Yggdrasil uses D-Bus to facilitate communication between workers and yggd
. It defines the interfaces over which yggd
and its workers communicate:
-
com.redhat.Yggdrasil1.Dispatcher1
is the interface implemented byyggd
and used by workers to communicate withyggd
. -
com.redhat.Yggdrasil1.Worker1
is the interface implemented by workers and used byyggd
to communicate with workers.
A worker is any program that:
- Connects to the D-Bus broker (either the system bus or a session bus)
- Exports an object implementing the
com.redhat.Yggdrasil1.Worker1
interface - Claims the well-known name
com.redhat.Yggdrasil1.Worker1.NAME
, whereNAME
is the identification string used to route messages received byyggd
to the worker
-
yggdrasil
Workers can be written in any programming language, so long as the language can communicate using the D-Bus protocol. Many modern programming languages have libraries that implement the D-Bus protocol, making the decision about which programming language to use a matter of mostly personal preference. However, yggdrasil offers two libraries to support worker development: a Go package and a GObject C library (
yggdrasil
and libygg respectively). Both of these libraries handle all of the D-Bus specific "boilerplate" code, enabling the worker author to spend their time developing their application code instead. The GObject C library provides GObject Introspection files, allowing programming languages such as Python and JavaScript to use GObject Introspection to call functions in the C library.
The following section documents how to develop and run a worker in Python, assuming yggdrasil is installed and running on the system. You are free to organize your code into any structure you prefer, so long as the outcome conforms to the 3 expectations above. This example worker will print any strings it receives to stdout, prefixing the string with "hello"; a classic "hello world" application.
A worker object is initialized by calling the Ygg.Worker
initializer:
worker = Ygg.Worker(directive="hello", remote_content=False, features=None)
Define a function that will be called each time the worker receives data from yggd
over D-Bus and pass a handle to the worker.
def handle_rx(worker, addr, id, response_to, meta_data, data):
print("Hello {}".format(data)
worker.set_rx_func(handle_rx)
Start the worker; this will connect to the D-Bus broker, export an object implementing the com.redhat.Yggdrasil1.Worker1
interface and claim the name com.redhat.Yggdrasil1.Worker1.hello
.
worker.connect()
Finally, start the main run loop to begin receiving messages on the D-Bus broker.
loop = GLib.MainLoop()
loop.run()
Workers implemented in Go language can use the yggdrasil
library.
Define a function that will be called each time the worker receives data from yggd
over D-Bus and pass a handle to the worker.
func handleRx(
w *worker.Worker,
addr string,
rcvId string,
responseTo string,
metadata map[string]string,
data []byte,
) error {
fmt.Printf("Hello %v", data)
}
The cancellation handler is also defined with and rx function.
Note: At this moment it is only implemented in yggdrasil
func cancelRx(w *worker.Worker, addr string, id string, cancelID string) error {
fmt.Printf("cancelling message with id %v", cancelID)
}
Initialize the worker with the NewWorker
function.
w, err := worker.NewWorker(
directive,
remoteContent,
features,
cancelEcho,
handleRx,
)
Finally, set a channel to receive exit syscalls, and connect the
worker to a D-Bus session. This will export an object implementing the com.redhat.Yggdrasil1.Worker1
interface and claim the name com.redhat.Yggdrasil1.Worker1.hello
. It is ready to start receiving message through D-Bus worker.
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT)
if err := w.Connect(quit); err != nil {
log.Fatalf("error: cannot connect: %v", err)
}
If your worker is likely to be run on the system bus, you must install a policy defining how messages can be sent. Install the following into a file in the directory /usr/share/dbus-1/system.d
. Conventionally, the files are named after the destination name, so in this case, the file would be named com.redhat.Yggdrasil1.Worker1.hello.conf
.
<busconfig>
<policy user="root">
<!-- Only root can send messages to the Worker1.hello destination. -->
<allow send_destination="com.redhat.Yggdrasil1.Worker1.hello" send_interface="com.redhat.Yggdrasil1.Worker1"/>
<allow send_destination="com.redhat.Yggdrasil1.Worker1.hello" send_interface="org.freedesktop.DBus.Properties"/>
<allow send_destination="com.redhat.Yggdrasil1.Worker1.hello" send_interface="org.freedesktop.DBus.Introspectable"/>
</policy>
</busconfig>
A worker may optionally install a D-Bus service file to enable bus activation; when the D-Bus broker receives a message for the address specified by the Name=
directive, the worker will be started before the message is delivered.
[D-BUS Service]
Name=com.redhat.Yggdrasil1.Worker1.hello
Exec=/usr/libexec/hello-worker
Should your worker require more advanced start up functionality, it is possible to specify a value for the SystemdService=
directive. With such a directive in the service activation file, the D-Bus broker will ask systemd to start the named service. Within that system service file, you can define any resource limitations or environment variables needed for your worker. For example:
[Unit]
Description=yggdrasil hello worker
[Service]
Type=dbus
BusName=com.redhat.Yggdrasil1.Worker1.hello
ExecStart=/usr/libexec/hello-worker
Note: There is no [Install]
section; this is because the service is D-Bus activatable.
See the systemd service examples for detail.
- Fully functional example workers can be seen in the libygg examples directory, as well as yggdrasil's worker package
- A list of active workers is maintained on this wiki as well