ExIncus. EventListener
(ex_incus v1.0.0)
Copy Markdown
Listens to the Incus event stream (/1.0/events websocket) and fans events
out to subscriber processes - the push-based alternative to polling for
dashboards and monitors.
Nothing is started automatically: add one listener per Incus server to your application's supervision tree if (and only if) you want events:
children = [
{ExIncus.EventListener,
name: MyApp.IncusEvents,
connect: [socket_path: "/var/lib/incus/unix.socket"],
types: [:lifecycle, :operation],
all_projects: true}
]Any process can then subscribe and will receive plain messages:
{:ok, status} = ExIncus.EventListener.subscribe(MyApp.IncusEvents, types: [:lifecycle])
# arriving in handle_info/2:
{:ex_incus_event, %ExIncus.Event{action: "instance-started", name: "web1"}}
{:ex_incus_connected, listener_pid}
{:ex_incus_disconnected, listener_pid, reason}Subscribers are monitored and removed automatically when they exit.
Reconnection and the snapshot pattern
The listener reconnects with exponential backoff when the connection drops,
but Incus does not replay missed events. {:ex_incus_connected, pid} is
therefore sent on every (re)connect: treat it as "my picture of the world
may be stale" and re-fetch state, e.g. with ExIncus.Instances.list/2.
A live dashboard should:
subscribe/2(the return tells you whether the stream is currently up)- fetch a snapshot (
ExIncus.Instances.list(client, recursion: 2)) - apply incoming lifecycle events to it
- on
{:ex_incus_connected, _}, go back to step 2
Bridging to Phoenix.PubSub (or anything else)
Instead of (or in addition to) subscribers, pass a :handler function; it
is invoked with the same tuples subscribers receive:
{ExIncus.EventListener,
connect: [socket_path: "/var/lib/incus/unix.socket"],
types: [:lifecycle],
handler: fn message -> Phoenix.PubSub.broadcast(MyApp.PubSub, "incus", message) end}A crashing handler is logged, not fatal.
Options
:connect-ExIncus.Client.new/1-style connection options (socket_path:orbase_url:+certfile:/keyfile:/...):types- event types to request from the server ([:lifecycle, :operation, :logging]);nil(default) means all:project- project to watch (defaults toconnect[:project]):all_projects- watch every project:handler- function invoked with each message (see above):name- GenServer name:backoff_min/:backoff_max- reconnect backoff bounds in ms (defaults 1_000 / 30_000):upgrade_timeout- websocket handshake timeout in ms (default 15_000)
Summary
Functions
Returns a specification to start this module under a supervisor.
Starts the listener. See the module documentation for options.
Returns whether the event stream is currently connected.
Subscribes the calling process to events from this listener.
Removes the calling process's subscription.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec start_link(keyword()) :: GenServer.on_start()
Starts the listener. See the module documentation for options.
@spec status(GenServer.server()) :: :connected | :disconnected
Returns whether the event stream is currently connected.
@spec subscribe( GenServer.server(), keyword() ) :: {:ok, :connected | :disconnected}
Subscribes the calling process to events from this listener.
Returns {:ok, :connected | :disconnected} - the current stream status,
so a dashboard knows whether an immediately-taken snapshot is trustworthy.
Subscribing again replaces the previous subscription (e.g. to change the
types: filter).
Options
:types- only deliver these event types (e.g.[:lifecycle]); defaults to everything the listener receives
@spec unsubscribe(GenServer.server()) :: :ok
Removes the calling process's subscription.