Websocket Access¶
A web socket is provided to retrieve a stream of events. This can be accessed by hitting a URL in the form of:
The URL will require authentication, but once authorized, the resulting web socket will receive events.
Events can be filtered by submitting register
filters to the websocket listener.
Register for Events¶
The events desired must be registered for. This is done by sending a register request to the server. By
sending, register *.*.*
, this will cause all events that the authorized user can see to be delivered to the websocket
receiver.
Multiple registers (and the corresponding deregister) are allowed.
The general form is:
register type.action.key
or register type.action.key.filter
The fields are:
type
- the type of object (e.g. profiles, machines, ...)action
- the action of the object (e.g. create, save, update, destroy, ...)key
- the specific key of the object (e.g. machine uuid, profile name, ...)filter
- A filter that can be used for more specific matching of events beyond thetype.action.key
matches.
Some simple example are provided in the DRP source tree:
Event specifiers¶
The type
, action
, and key
fields in an event specifier can have the following values:
*
to indicate all possible values of that field count towards a match.- A comma-separated list of values appropriate to the field with no intervening spaces -- object types for the
type
field, action names for theaction
field, and key values for thekey
field.
There are a couple of special cases to enable efficient handling of certain job-related events:
- If the
type
ismachine_jobs
, then ajob
event whoseMachine
field matches thekey
of the event specifier will match the event assuming the other fields also match. - If the
type
iswork_order_jobs
, then ajob
event whoseWorkOrder
field matches thekey
of the event specifier will match the event assuming the other fields match.
The filter
field in the event specifier is optional. If it is present, it should contain a filter defined by the
client-side compatible filter language.
Client compatible filter language¶
This language is designed to be easily implementable in client API libraries and operate identically on the native Go
objects and their JSON serialization. It is available on the server side if the server advertises the
websocket-changed-filter
feature flag, otherwise it can also run on the client side as an event post-processor.
value
is the JSON representation of the value you want to filter on.fields
is either a dot-separated string or an RFC6901 compliant JSON pointer specifying the field in a potentially nested struct or array to look up.term
is any syntactically correct filter expression.terms
is a comma separated list of syntactically correct filter expressions.And(terms)
will match of all of the terms match. Terms are tested from left to right, and the first one that does not match stops testing.Or(terms)
will match if any of the terms match. Terms are tested from left to right, and the first one that matches stops testing.Not(term)
returns the opposite of the term.fields=Exists()
checks to see if there is a value at fields at all, no matter what the value may be.fields=Eq(value)
checks to see if the value at fields is equal to the passed in value.fields=Ne(value)
checks to see if the value at fields is not equal to the passed in value.fields=Re(value)
checks to see if the value at fields matches the passed-in regular expression.fields=In(value,value...)
checks to see if the value at fields matches any of the passed-in values. It is equivalent toOr(fields=Eq(value1),fields=Eq(value2),...)
.fields=Nin(value,value...)
checks to see if the value at fields does not match any of the passed-in values. It is equivalent toNot(Or(fields=Eq(value1),fields=Eq(value2),...))
.fields=Changed(from,to)
behaves specially if it is matching against either a models.Event or an api.RecievedEvent.from
andto
must both be valid JSON values, the same asvalue
.- If the event being tested has non-nil
Object
andOriginal
fields,Changed
will return false if fields is not present in both objects or if it is present and identical in both objects. - If
from
is specified and the value atfields
inOriginal
is notEq
to it, Changed will return false. - If
to
is specified and the value atfields
inObject
is notEq
to it, Changed will return false. - Otherwise,
Changed
will return true. If Changed is testing anything besides a models.Event or an api.RecievedEvent, it returns true ifto
is not specified orEq(to)
returns true.
Deregister Events¶
If you no longer wish to receive specific events you have registered for, you may use the deregister
command. The
command syntax is exactly like the register
command.
The general form is:
deregister type.action.key
or deregister type.action.key.filter
The deregister
message must correspond to a previous register
message.
Websocket Tools¶
Most modern languages provide websocket libraries that you can use to create listeners in a given language. Some examples include (this is not an exhaustive list):
- Golang: https://github.com/gorilla/websocket
- Golang: https://github.com/gobwas/ws
- Python: https://www.willmcgugan.com/blog/tech/post/announcing-lomond/
- Python: https://github.com/Lawouach/WebSocket-for-Python/tree/master/requirements
- Python: https://github.com/websocket-client/websocket-client
- Javascript: https://github.com/websockets/ws
- google is your friend ...
There are several extensions/add-ons for web browsers that will allow you to do basic testing of websocket listening. Here at RackN, we have used the following with some success:
- Chrome/Firefox: https://github.com/WangFenjin/Simple-WebSocket-Client
- Chrome: https://chrome.google.com/webstore/detail/smart-websocket-client/omalebghpgejjiaoknljcfmglgbpocdp?hl=en
There is a simple sample Python script available in the Digital Rebar Provision repo for reference, see the Websocket Integrations: page for further details.
Monitoring Connections¶
Digital Rebar has an API endpoint for listing active websocket and REST Connections under GET /api/v3/connections
.
This list of connections contains information such as create time, principal, address, and if the connection is a
websocket.
Connections for specific machines can be accessed via
GET /api/v3/machines/:uuid/connections
. Clusters and Resource brokers have the similarly named API endpoints.
New connections and disconnections can be monitored by the
connections.create.<principal>
event. This is useful as individual runner connections can be monitored
with connections.create.runner:<uuid>
A combination of the machine connections API endpoint and connection events can be used to monitor a relative "online" state of a machine's runner, a user's portal session, or the connectivity of a plugin.
Note
While connection events come in with Principal
for the event key, connections are unique by RemoteAddr
.
This is because Principal
, while not unique, is more useful. RemoteAddr
is used as the key for
GET /api/v3/connections/:RemoteAddr
Example Information¶
Here is a simple walk through of basic testing on how to use websockets with Digital Rebar. Please note this is fairly basic, but it should get you started on how to interact with and use websockets. This example was tested, using the " Simple Websocket Client" in both Chrome and Firefox that is listed above.
We assume you have the DRP endpoint installed on your localhost in these examples. You can adjust the IP address/hostname to point to a remote DRP Endpoint, just ensure you have access to Port 8092 (by default, or the API port you specify if you changed the default).
URL: wss://127.0.0.1:8092/api/v3/ws?token=rocketskates:r0cketsk8ts
Note that the token information is a set of credentials with permissions to view events. This example uses the default username/password pair. You may also create and specify access Tokens for the websocket client to use.
In the Request input box, enter your register filter you'd like to receive events for.
Request: register profiles.*.*
This example will only output websocket events related to Parameters. Now create and delete a few test parameters
# now create a `bar` param on the `global` profile
drpcli profiles set global param bar to blatz
# now remove the param from the `global` profile
drpcli profiles remove global param bar
...and you should see events like:
{"Time":"2017-12-21T23:26:43.412554192Z","Type":"profiles","Action":"save","Key":"global","Object":{"Validated":true,"Available":true,"Errors":[],"ReadOnly":false,"Meta":{"color":"blue","icon":"world","title":"Digital Rebar Provision"},"Name":"global","Description":"Global profile attached automatically to all machines.","Params":{"bar":"blatz","change-stage/map":{"centos-8-install":"packet-ssh-keys:Success","discover":"packet-discover:Success","packet-discover":"centos-8-install:Reboot","packet-ssh-keys":"complete-nowait:Success"},"kernel-console":"console=ttyS1,115200"}}}
{"Time":"2017-12-21T23:27:15.218761478Z","Type":"profiles","Action":"save","Key":"global","Object":{"Validated":true,"Available":true,"Errors":[],"ReadOnly":false,"Meta":{"color":"blue","icon":"world","title":"Digital Rebar Provision"},"Name":"global","Description":"Global profile attached automatically to all machines.","Params":{"change-stage/map":{"centos-8-install":"packet-ssh-keys:Success","discover":"packet-discover:Success","packet-discover":"centos-8-install:Reboot","packet-ssh-keys":"complete-nowait:Success"},"kernel-console":"console=ttyS1,115200"}}}