project-akri / akri

A Kubernetes Resource Interface for the Edge
https://docs.akri.sh/
Apache License 2.0
1.1k stars 142 forks source link

[Zeroconf] WebThings are discoverable using mDNS|SD #183

Closed DazWilkin closed 2 years ago

DazWilkin commented 3 years ago

Is your feature request related to a problem? Please describe.

A side-benefit of the Zeroconf Protocol Implementation is that Zeroconf enables discovery of any device (services) published using mDNS|DNS-SD.

Revisiting the new (!) WebThings (formerly Mozilla's WebThings), I was heartened to discover that WebThings' devices are published using mDNS|DNS-SD described in the Draft spec here

Unfortunately, there's an issue with the Rust SDK but devices written using other SDKs work as expected. Here using avahi-browse to observe a device published using the WebThings Python sample:

avahi-browse --all
+ enp5s0 IPv6 LightAndTempDevice                            _webthing._tcp       local
- enp5s0 IPv6 LightAndTempDevice                            _webthing._tcp       local

Is your feature request related to a way you would like Akri extended? Please describe.

Zeroconf Protocol implementation provides the foundation for discovery of WebThings devices.

Describe the solution you'd like

Zeroconf Protocol implementation provides the foundation for discovery of WebThings devices.

Describe alternatives you've considered

N/A

DazWilkin commented 3 years ago

This should now be working for devices developed using any of WebThings SDKs; there was an issue with the Rust SDK that is now resolved.

For example, using the non-TLS example described in the Rust SDK:

cargo run
setting new humidity level: 12.535304069519043
setting new humidity level: 2.093383550643921
setting new humidity level: 14.57203483581543
setting new humidity level: 2.1968395709991455
setting new humidity level: 2.9822685718536377

Then:

avahi-browse --all | grep _webthing._tcp
IPv4 LightAndTempDevice         _webthing._tcp       local

NOTE The WebThings examples creates ThingsType::Multiple(things, "LightAndTempDevice".to_owned())

Add it to your Gateway per the instructions.

The Gateway uses mDNS|SD browsing and should find 2 devices on LightAndTempDevice: My Lamp, My Humidity Sensor

Here's the Lamp:

image

Then, apply the following Akri Zeroconf Configuration to a cluster:

apiVersion: akri.sh/v0
kind: Configuration
metadata:
  name: webthings
spec:
  protocol:
    zeroconf:
      kind: "_webthing._tcp"
      port: 8888
  capacity: 1
  brokerPodSpec:
    containers:
      - name: broker
        image: ghcr.io/deislabs/akri/zeroconf-broker:v1 # This repository does not yet exist
        resources:
          limits:
            "{{PLACEHOLDER}}": "1"

And:

kubectl get configuration
NAME        CAPACITY   AGE
webthings   1          2s

kubectl get instances
NAME               CONFIG      SHARED   NODES    AGE
webthings-d6097c   webthings   true     [akri]   4s

Wait for Akri to create the Broker Pod:

kubectl get instances \
--output=jsonpath="{.items[*].spec.metadata.AKRI_ZEROCONF_DEVICE_NAME}"
LightAndTempDevice

And, lastly:

for INSTANCE in $(kubectl get instances --output=jsonpath="{.items[*].metadata.name}")
do
  POD="pod/akri-${INSTANCE}-pod"
  kubectl logs ${POD}
done
[zeroconf:main] Entered
[zeroconf:new] Entered
[zeroconf:main] Service: kind: _webthing._tcp
name: LightAndTempDevice
host: akri.local
addr: 10.1.1.1
port: 8888
TXTs:
  AKRI_ZEROCONF_DEVICE_PATH: /

[zeroconf:main:loop] Sleep
[zeroconf:main:loop] check_device(Service { kind: "_webthing._tcp", name: "LightAndTempDevice", host: "akri.local", addr: "10.1.1.1", port: 8888, txts: Some({"AKRI_ZEROCONF_DEVICE_PATH": "/"}) })
[zeroconf:read_device] Entered: Service { kind: "_webthing._tcp", name: "LightAndTempDevice", host: "akri.local", addr: "10.1.1.1", port: 8888, txts: Some({"AKRI_ZEROCONF_DEVICE_PATH": "/"}) }
[zeroconf:main:loop] Sleep
[zeroconf:main:loop] check_device(Service { kind: "_webthing._tcp", name: "LightAndTempDevice", host: "akri.local", addr: "10.1.1.1", port: 8888, txts: Some({"AKRI_ZEROCONF_DEVICE_PATH": "/"}) })
[zeroconf:read_device] Entered: Service { kind: "_webthing._tcp", name: "LightAndTempDevice", host: "akri.local", addr: "10.1.1.1", port: 8888, txts: Some({"AKRI_ZEROCONF_DEVICE_PATH": "/"}) }
[zeroconf:main:loop] Sleep

And:

kubectl exec --stdin --tty ${POD} -- env | grep ^AKRI
AKRI_ZEROCONF_DEVICE_NAME=LightAndTempDevice
AKRI_ZEROCONF_DEVICE_KIND=_webthing._tcp
AKRI_ZEROCONF_DEVICE_ADDR=10.1.1.1
AKRI_ZEROCONF_DEVICE_PATH=/
AKRI_ZEROCONF=zeroconf
AKRI_ZEROCONF_DEVICE_HOST=akri.local
AKRI_ZEROCONF_DEVICE_PORT=8888

NOTE WebThings adds a TXT record path="/" to the service name referencing the device's path. The Akri Zeroconf protocol handler transfers this to an environment variable AKRI_ZEROCONF_DEVICE_PATH which we'll use below to construct the correct URL to access the device.

And, you can:

kubectl exec --stdin --tty ${POD} -- sh
apt update && apt install -y curl
curl ${AKRI_ZEROCONF_DEVICE_ADDR}:${AKRI_ZEROCONF_DEVICE_PORT}${AKRI_ZEROCONF_DEVICE_PATH} \
| jq -r .[].title
My Lamp
My Humidity Sensor

NOTE DNS-SD aren't exposed directly as host names so we use AKRI_ZEROCONF_DEVICE_ADDR rather than AKRI_ZEROCONF_DEVICE_NAME with curl. If you use a DNS-SD client, you can use the name.

bfjelds commented 3 years ago

Do I read this correctly that the zeroconf implementation also serves as a WebThings implementation? That is awesome!! I can't wait to get the extensibility model fixed up to merge zeroconf!!

DazWilkin commented 3 years ago

Yes :-)

I'm contemplating (!) implementing the Akri "Distribution of Vaccines" scenario by writing WebThings for e.g. vaccine refrigerators and trucks. It may be overkill but it'd be a double whammy of an industrial application using WebThings and a neat scenario for Akri.

DazWilkin commented 3 years ago

We fixed the WebThings Rust SDK so devices built with it, publish mDNS|SD correctly.

bfjelds commented 3 years ago

Awesome!!

github-actions[bot] commented 3 years ago

Issue has been automatically marked as stale due to inactivity for 45 days. Update the issue to remove label, otherwise it will be automatically closed.

bfjelds commented 2 years ago

Per thread above, this has been fixed.