c0c0n3 / kitt4sme.live

On a mission to bring AI to the shop floor: https://kitt4sme.eu/
MIT License
1 stars 28 forks source link

UL Agent #182

Closed c0c0n3 closed 1 year ago

c0c0n3 commented 1 year ago

This PR implements a fully-fledged UL Agent deployment with HTTP and MQTT transports as #14 requested. Specifically, the following got implemented.

c0c0n3 commented 1 year ago

Demo

Here's a sum up of how to demo most of what this PR implemented.

First off, build your own KITT4SME cluster in a Multipass VM as explained in the bootstrap procedure. Wait a bit until all services are ready. The commands below use the 192.168.64.20 IP address to reach the Multipass VM; replace 192.168.64.20 in each command with your VM's IP.

IaC

Log into Argo CD. There's a ul-agent app among the platform infra services. Check out all the resources deployed and have a look at the logs. Open the config map, you should be able to see the exact same content as in the config.js included in this PR.

Provisioning

Provision a service with two devices, one sending data over MQTT and the other over HTTP. Notice it's best to do that in config.js, but we'll do that manually here to speed things up. So as a rule, there would be no need to expose UL Agent's provisioning API. Since we're bending the rules here, port-forward the agent's provisioning port.

$ kubectl port-forward svc/ulagent 4041:4041

On to the provisioning business. Create a service with an API key of gr33t, entity type of Greeting and HTTP endpoint of /iot/d.

$ curl -iX POST 'http://localhost:4041/iot/services' \
  -H 'Content-Type: application/json' \
  -H 'fiware-service: greeting' \
  -H 'fiware-servicepath: /' \
  -d '{
 "services": [
   {
     "apikey":      "gr33t",
     "entity_type": "Greeting",
     "resource":    "/iot/d"
   }
 ]
}'

Create two devices to send a greeting message. The message is in the UL format: w|data, where data is a greeting the device sends. The corresponding NGSI entity has type Greeting and a words attribute holding the actual greeting. The first device has an ID of greeter001 and sends its data over MQTT, whereas the second has ID greeter002 and sends data over HTTP.

$ curl -iX POST \
  'http://localhost:4041/iot/devices' \
  -H 'Content-Type: application/json' \
  -H 'fiware-service: greeting' \
  -H 'fiware-servicepath: /' \
  -d '{
 "devices": [
   {
     "device_id":   "greeter001",
     "entity_name": "urn:ngsi-ld:Greeting:001",
     "entity_type": "Greeting",
     "protocol":    "PDI-IoTA-UltraLight",
     "transport":   "MQTT",
     "attributes": [
       { "object_id": "w", "name": "words", "type": "Text" }
     ]
   }
 ]
}
'

$ curl -iX POST \
  'http://localhost:4041/iot/devices' \
  -H 'Content-Type: application/json' \
  -H 'fiware-service: greeting' \
  -H 'fiware-servicepath: /' \
  -d '{
 "devices": [
   {
     "device_id":   "greeter002",
     "entity_name": "urn:ngsi-ld:Greeting:002",
     "entity_type": "Greeting",
     "protocol":    "PDI-IoTA-UltraLight",
     "transport":   "HTTP",
     "attributes": [
       { "object_id": "w", "name": "words", "type": "Text" }
     ]
   }
 ]
}
'

With this setup, greeter001 is expected to send its UL payload to the MQTT topic

whereas greeter002 is supposed to POST its UL payload to the URL

since our Istio config routes /ulagent/<rest> to /<rest> on port 7896 of the ulagent service.

Finally, check you can retrieve the service and devices you've just created.

$ curl 'http://localhost:4041/iot/services' \
  -H 'fiware-service: greeting' \
  -H 'fiware-servicepath: /'

{"count":1,"services":[{"apikey":"gr33t","resource":"/iot/d","service":"greeting","subservice":"/","_id":1,"creationDate":1667762295227,"entity_type":"Greeting"}]}
$ curl 'http://localhost:4041/iot/devices' \
  -H 'fiware-service: greeting' \
  -H 'fiware-servicepath: /'

{"count":2,"devices":[{"device_id":"greeter001","service":"greeting","service_path":"/","entity_name":"urn:ngsi-ld:Greeting:001","entity_type":"Greeting","transport":"MQTT","attributes":[{"object_id":"w","name":"words","type":"Text"}],"commands":[],"static_attributes":[],"protocol":"PDI-IoTA-UltraLight","explicitAttrs":false},{"device_id":"greeter002","service":"greeting","service_path":"/","entity_name":"urn:ngsi-ld:Greeting:002","entity_type":"Greeting","polling":true,"transport":"HTTP","attributes":[{"object_id":"w","name":"words","type":"Text"}],"commands":[],"static_attributes":[],"protocol":"PDI-IoTA-UltraLight","explicitAttrs":false}]}

Sending device data over MQTT

We're going to use an external WebSocket client to simulate device data coming in over MQTT.

Browse to http://www.emqx.io/online-mqtt-client. Hit the "New Connection" button and enter the following data: name=kitt4sme, client-id=tasty, host=ws://192.168.64.20, path=/mqtt/, port=80, username=iot. You also need to enter the "iot" user's password, which I can't type here obviously. Hit connect, then send the following UL message to the ul/gr33t/greeter001/attrs topic: w|howzit!.

Check the "howzit!" greeting trekked all the way to Orion. It should be stored in the "Greeting" entity having an ID of urn:ngsi-ld:Greeting:001.

$ curl \
  'http://192.168.64.20/orion/v2/entities/urn:ngsi-ld:Greeting:001/attrs/words/value' \
  -H 'fiware-service: greeting' \
  -H 'fiware-servicepath: /'

"howzit!"

Sending device data over HTTP

Let's also send a greeting from greeter002. This device sends its data over HTTP

$ curl -iX POST \
  'http://192.168.64.20/ulagent/iot/d?k=gr33t&i=greeter002' \
  -H 'Content-Type: text/plain' \
  -d 'w|ahoy, matey!'

HTTP/1.1 403 Forbidden
date: Sun, 06 Nov 2022 21:17:46 GMT
server: istio-envoy
content-length: 0

What?! Yep, that's right. Our FIWARE OPA policy checks you've got a valid JWT token, since we didn't have one, we got shown the door. How rude. Well, it's too much of a mission to get a token, so let's zap the security policy

$ kubectl -n istio-system delete authorizationpolicy/fiware-opa

and try again

$ curl -iX POST \
  'http://192.168.64.20/ulagent/iot/d?k=gr33t&i=greeter002' \
  -H 'Content-Type: text/plain' \
  -d 'w|ahoy, matey!'

This time the POST goes through and Orion gets our friendly greeting

$ curl \
  'http://192.168.64.20/orion/v2/entities/urn:ngsi-ld:Greeting:002/attrs/words/value' \
  -H 'fiware-service: greeting' \
  -H 'fiware-servicepath: /'

"ahoy, matey!"

As expected, the greeting got stored in the "Greeting" entity having an ID of urn:ngsi-ld:Greeting:002.

Cool bananas!