yggdrasil

package module
v0.0.0-...-1773593 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 6, 2024 License: GPL-3.0 Imports: 5 Imported by: 1

README

godocs.io

yggdrasil

yggdrasil is a system daemon that subscribes to topics on an MQTT broker and routes any data received on the topics to an appropriate child "worker" process, exchanging data with its worker processes through a D-Bus message broker.

Installation

The easiest way to compile and install yggdrasil is using meson. Because yggdrasil runs as a privileged system daemon, systemd unit files and D-Bus policy files must be installed in specific directories in order for the service to start.

Generally, it is recommended to follow your distribution's packaging guidelines for compiling Go programs and installing projects using meson. What follows is a generally acceptable set of steps to setup, compile and install yggdrasil using meson.

# Set up the project according to distribution-specific directory locations
meson setup --prefix /usr/local --sysconfdir /etc --localstatedir /var builddir
# Compile
meson compile -C builddir
# Install
meson install -C builddir

meson includes an optional --destdir to its install subcommand to aid in packaging.

Configuration

Configuration of yggd can be done by specifying values in a configuration file or via command line arguments. Command-line arguments take precedence over configuration file values. The configuration file is TOML.

The system-wide configuration file is located at /etc/yggdrasil/config.toml (assuming SYSCONFDIR=/etc, as the example above). The location of the file may be overridden by passing the --config command-line argument to yggd.

(Optional) Authentication

In order to run yggd under certain conditions (such as connecting to a broker that requires mTLS authentication), a valid certificate must first be created and written to the filesystem.

Red Hat Subscription Manager

One way of generating a valid certificate is to first register the system with an RHSM provider. The simplest way to do this is to create a free Red Hat Developer account. On a Red Hat Enterprise Linux system, run subscription-manager register, using the developer account username and password.

sudo subscription-manager register --username j_developer@company.com --password sw0rdf1sh

Once the system is successfully registered with RHSM, yggd can be launched, using the certificate key pair:

sudo /usr/sbin/yggd --cert-file /etc/pki/consumer/cert.pem --key-file /etc/pki/consumer/key.pem
Tags

A set of tags may be defined to associate additional key/value data with a host when connecting to the broker. To do this, create the file /etc/yggdrasil/tags.toml (assuming SYSCONFDIR=/etc, as the example above). The contents of the file may be any number of TOML key/value pairs. However, a limited number of TOML values are accepted as tag values (strings, integers, booleans, floats, Local Date, Local Time, Offset Date-Time and Local Date-Time).

Running

yggdrasil uses D-Bus as an IPC framework to enable communication between workers and the dispatcher, as well as exporting an API enabling other services to interact with it. yggd follows the normal logic for determining which bus to connect to. It will attempt to connect to the session bus if DBUS_SESSION_BUS_ADDRESS is defined. Otherwise, it will attempt to connect to the system bus. On the system bus, if the process is not running as root, the installed D-Bus security policy will deny the process from claiming the com.redhat.Yggdrasil1 and com.redhat.Yggdrasil1.Dispatcher1 names, and the process will exit.

The systemd unit yggrasil.service starts yggd, using the logic described above.

systemctl enable --now yggdrasil

Multiple instances of yggd can be run by using the templated systemd units. Each instance requires a private D-Bus session. The socket and broker are started with yggdrasil-bus@.socket and yggdrasil-bus@.service, respectively. A templated yggdrasil unit is available to automatically connect yggd to the D-Bus broker started by yggdrasil-bus@.service.

systemctl enable --now yggdrasil-bus@bunnies.socket
systemctl enable --now yggdrasil-bus@bunnies.service
systemctl enable --now yggdrasil@bunnies.service

This will define and create an abstract UNIX domain socket named yggd_bunnies. It will start a dbus-broker process running in the "user" scope, connecting to the yggd_bunnies socket defined previously. Finally, yggd is launched with a specific configuration file as the value of the --config argument: /etc/yggdrasil/yggdrasil-bunnies.toml.

Workers

A functional worker program must connect to the message bus as determined by the DBUS_SESSION_BUS_ADDRESS environment variable, connecting to a session bus if the value is defined, otherwise connecting to the system bus. Once connected to the bus:

  • The program must export an object on the bus that implements the com.redhat.Yggdrasil1.Worker1 interface.
  • The object must be exported at a path under /com/redhat/Yggdrasil1/Worker1 that includes the directive name (i.e. /com/redhat/Yggdrasil1/Worker1/echo).
  • The worker must claim a well-known name that begins with com.redhat.Yggdrasil1.Worker1 and includes its directive as the final segment in reverse-domain-name notation (i.e. com.redhat.Yggdrasil1.Worker1.echo).

A worker can transmit data back to a destination by calling the com.redhat.Yggdrasil1.Dispatcher1.Transmit method.

Package worker implements the above requirements implicitly, enabling workers to be written without needing to worry about much of the D-Bus requirements outlined above.

See worker/echo for a reference implementation of a worker program.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ConfigPath

func ConfigPath() (string, error)

ConfigPath returns an appropriate path to a config file. If the created path does not exist, an empty string is returned.

Types

type Command

type Command struct {
	Command   CommandName       `json:"command"`
	Arguments map[string]string `json:"arguments"`
}

A Command message is published by the server on the "control" topic when it needs to instruct a client to perform an operation.

type CommandName

type CommandName string

CommandName represents accepted values for the "command" field of Command messages.

const (
	// CommandNameReconnect instructs a client to temporarily disconnect and
	// reconnect to the broker.
	CommandNameReconnect CommandName = "reconnect"

	// CommandNamePing instructs a client to respond with a "pong" event.
	CommandNamePing CommandName = "ping"

	// CommandNameDisconnect instructs a client to permanently disconnect.
	CommandNameDisconnect CommandName = "disconnect"

	// CommandNameCancel instructs a client to cancel a previous message.
	CommandNameCancel CommandName = "cancel"
)

type ConnectionState

type ConnectionState string

ConnectionState represents accepted values for the "state" field of ConnectionStatus messages.

const (
	// ConnectionStateOnline indicates a client is online and subscribing to
	// topics.
	ConnectionStateOnline ConnectionState = "online"

	// ConnectionStateOffline indicates a client is no longer online.
	ConnectionStateOffline ConnectionState = "offline"
)

type ConnectionStatus

type ConnectionStatus struct {
	Type       MessageType `json:"type"`
	MessageID  string      `json:"message_id"`
	ResponseTo string      `json:"response_to"`
	Version    int         `json:"version"`
	Sent       time.Time   `json:"sent"`
	Content    struct {
		CanonicalFacts map[string]interface{}       `json:"canonical_facts"`
		Dispatchers    map[string]map[string]string `json:"dispatchers"`
		State          ConnectionState              `json:"state"`
		Tags           map[string]string            `json:"tags,omitempty"`
		ClientVersion  string                       `json:"client_version,omitempty"`
	} `json:"content"`
}

A ConnectionStatus message is published by the client when it connects to the broker. The message is expected to be published as a retained message and its presence is considered an acceptable way to decide whether a client is active and functioning normally.

type Control

type Control struct {
	Type       MessageType     `json:"type"`
	MessageID  string          `json:"message_id"`
	ResponseTo string          `json:"response_to"`
	Version    int             `json:"version"`
	Sent       time.Time       `json:"sent"`
	Content    json.RawMessage `json:"content"`
}

type Data

type Data struct {
	Type       MessageType       `json:"type"`
	MessageID  string            `json:"message_id"`
	ResponseTo string            `json:"response_to"`
	Version    int               `json:"version"`
	Sent       time.Time         `json:"sent"`
	Directive  string            `json:"directive"`
	Metadata   map[string]string `json:"metadata"`
	Content    json.RawMessage   `json:"content"`
}

Data messages are published by both client and server on their respective "data" topic. The client consumes Data messages and routes them to an appropriate worker based on the "Directive" field.

type Event

type Event struct {
	Type       MessageType `json:"type"`
	MessageID  string      `json:"message_id"`
	ResponseTo string      `json:"response_to"`
	Version    int         `json:"version"`
	Sent       time.Time   `json:"sent"`
	Content    string      `json:"content"`
}

An Event message is published by the client on the "control" topic when it wishes to inform the server that a notable event occurred.

type EventName

type EventName string

EventName represents accepted values for the "event" field of an Event message.

const (
	// EventNameDisconnect informs the server that the client will disconnect.
	EventNameDisconnect EventName = "disconnect"

	// EventNamePong informs the server that the client has received a "ping"
	// command.
	EventNamePong EventName = "pong"
)

type MessageType

type MessageType string

MessageType represents accepted values in the "type" field of messages.

const (
	MessageTypeConnectionStatus MessageType = "connection-status"
	MessageTypeCommand          MessageType = "command"
	MessageTypeEvent            MessageType = "event"
	MessageTypeData             MessageType = "data"
)

The supported message types.

type Response

type Response struct {
	Code     int               `json:"code"`
	Metadata map[string]string `json:"metadata"`
	Data     []byte            `json:"data"`
}

Response messages are published by the server as a response to a data message. This is most often used as a receipt to indicate the receiption of a message by a synchronous request/response transport (such as the HTTP polling transport).

type WorkerMessage

type WorkerMessage struct {
	MessageID   string    `json:"message_id"`
	Sent        time.Time `json:"sent"`
	WorkerName  string    `json:"worker_name"`
	ResponseTo  string    `json:"response_to"`
	WorkerEvent struct {
		EventName uint              `json:"event_name"`
		EventData map[string]string `json:"event_data"`
	}
}

A WorkerMessage represents the structure of a journal entry in the optional message journal. These worker messages are created when the dispatcher receives emitted worker event data and when data is dispatched to a worker.

Directories

Path Synopsis
cmd
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL