Skip to content

Commit 3c91d10

Browse files
committed
Add (some) mapper-bridge support for authenticated MQTT connections to local broker
Signed-off-by: James Rhodes <jarhodes314@gmail.com>
1 parent a36f0b2 commit 3c91d10

File tree

2 files changed

+311
-1
lines changed

2 files changed

+311
-1
lines changed

crates/extensions/tedge_mqtt_bridge/src/lib.rs

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub use mqtt_channel::TopicFilter;
4040
use tedge_api::main_device_health_topic;
4141
use tedge_api::MQTT_BRIDGE_DOWN_PAYLOAD;
4242
use tedge_api::MQTT_BRIDGE_UP_PAYLOAD;
43+
use tedge_config::MqttAuthConfig;
4344
use tedge_config::TEdgeConfig;
4445

4546
pub struct MqttBridgeActorBuilder {}
@@ -64,7 +65,38 @@ impl MqttBridgeActorBuilder {
6465
tedge_config.mqtt.client.port.into(),
6566
);
6667
let health_topic = main_device_health_topic(&service_name);
67-
// TODO cope with secured mosquitto
68+
// TODO cope with certs but not ca_dir, or handle that case with an explicit error message?
69+
let auth_config = tedge_config.mqtt_client_auth_config();
70+
let local_tls_config = match auth_config {
71+
MqttAuthConfig {
72+
ca_dir: Some(ca_dir),
73+
client: Some(client),
74+
..
75+
} => Some(
76+
create_tls_config(
77+
ca_dir.into(),
78+
client.key_file.into(),
79+
client.cert_file.into(),
80+
)
81+
.unwrap(),
82+
),
83+
MqttAuthConfig {
84+
ca_file: Some(ca_file),
85+
client: Some(client),
86+
..
87+
} => Some(
88+
create_tls_config(
89+
ca_file.into(),
90+
client.key_file.into(),
91+
client.cert_file.into(),
92+
)
93+
.unwrap(),
94+
),
95+
_ => None,
96+
};
97+
if let Some(tls_config) = local_tls_config {
98+
local_config.set_transport(Transport::tls_with_config(tls_config.into()));
99+
}
68100
local_config.set_manual_acks(true);
69101
local_config.set_last_will(LastWill::new(
70102
&health_topic,
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
*** Settings ***
2+
Resource ../../../resources/common.resource
3+
Library Cumulocity
4+
Library ThinEdgeIO
5+
6+
Test Tags theme:c8y theme:telemetry
7+
Suite Setup Custom Setup
8+
Test Teardown Get Logs
9+
10+
*** Variables ***
11+
${C8Y_TOPIC_PREFIX} custom-c8y-prefix
12+
13+
*** Test Cases ***
14+
Thin-edge devices support sending simple measurements
15+
Execute Command tedge mqtt pub te/device/main///m/ '{ "temperature": 25 }'
16+
${measurements}= Device Should Have Measurements minimum=1 maximum=1 type=ThinEdgeMeasurement value=temperature series=temperature
17+
Log ${measurements}
18+
19+
Bridge stops if mapper stops running
20+
Execute Command tedge mqtt pub ${C8Y_TOPIC_PREFIX}/s/us '200,CustomMeasurement,temperature,25'
21+
${measurements}= Device Should Have Measurements minimum=1 maximum=1 type=CustomMeasurement series=temperature
22+
Log ${measurements}
23+
Execute Command systemctl stop tedge-mapper-c8y
24+
Execute Command tedge mqtt pub ${C8Y_TOPIC_PREFIX}/s/us '200,CustomMeasurement,temperature,25'
25+
${measurements}= Device Should Have Measurements minimum=1 maximum=1 type=CustomMeasurement series=temperature
26+
Log ${measurements}
27+
Execute Command systemctl start tedge-mapper-c8y
28+
29+
Thin-edge devices support sending simple measurements with custom type
30+
Execute Command tedge mqtt pub te/device/main///m/ '{ "type":"CustomType", "temperature": 25 }'
31+
${measurements}= Device Should Have Measurements minimum=1 maximum=1 type=CustomType value=temperature series=temperature
32+
Log ${measurements}
33+
34+
Thin-edge devices support sending simple measurements with custom type in topic
35+
Execute Command tedge mqtt pub te/device/main///m/CustomType_topic '{ "temperature": 25 }'
36+
${measurements}= Device Should Have Measurements minimum=1 maximum=1 type=CustomType_topic value=temperature series=temperature
37+
Log ${measurements}
38+
39+
40+
Thin-edge devices support sending simple measurements with custom type in payload
41+
Execute Command tedge mqtt pub te/device/main///m/CustomType_topic '{ "type":"CustomType_payload","temperature": 25 }'
42+
${measurements}= Device Should Have Measurements minimum=1 maximum=1 type=CustomType_payload value=temperature series=temperature
43+
Log ${measurements}
44+
45+
Thin-edge devices support sending custom measurements
46+
Execute Command tedge mqtt pub te/device/main///m/ '{ "current": {"L1": 9.5, "L2": 1.3} }'
47+
${measurements}= Device Should Have Measurements minimum=1 maximum=1 type=ThinEdgeMeasurement value=current series=L1
48+
Log ${measurements}
49+
50+
51+
Thin-edge devices support sending custom events
52+
Execute Command tedge mqtt pub te/device/main///e/myCustomType1 '{ "text": "Some test event", "someOtherCustomFragment": {"nested":{"value": "extra info"}} }'
53+
${events}= Device Should Have Event/s expected_text=Some test event with_attachment=False minimum=1 maximum=1 type=myCustomType1 fragment=someOtherCustomFragment
54+
Log ${events}
55+
56+
57+
Thin-edge devices support sending large events
58+
Execute Command tedge mqtt pub te/device/main///e/largeEvent "$(printf '{"text":"Large event","large_text_field":"%s"}' "$(yes "x" | head -n 100000 | tr -d '\n')")"
59+
${events}= Device Should Have Event/s expected_text=Large event with_attachment=False minimum=1 maximum=1 type=largeEvent fragment=large_text_field
60+
Length Should Be ${events[0]["large_text_field"]} 100000
61+
Log ${events}
62+
63+
64+
Thin-edge devices support sending large events using legacy api
65+
[Tags] legacy
66+
Execute Command tedge mqtt pub tedge/events/largeEvent2 "$(printf '{"text":"Large event","large_text_field":"%s"}' "$(yes "x" | head -n 100000 | tr -d '\n')")"
67+
${events}= Device Should Have Event/s expected_text=Large event with_attachment=False minimum=1 maximum=1 type=largeEvent2 fragment=large_text_field
68+
Length Should Be ${events[0]["large_text_field"]} 100000
69+
Log ${events}
70+
71+
72+
Thin-edge devices support sending custom events overriding the type
73+
Execute Command tedge mqtt pub te/device/main///e/myCustomType '{"type": "otherType", "text": "Some test event", "someOtherCustomFragment": {"nested":{"value": "extra info"}} }'
74+
${events}= Device Should Have Event/s expected_text=Some test event with_attachment=False minimum=1 maximum=1 type=otherType fragment=someOtherCustomFragment
75+
Log ${events}
76+
77+
78+
Thin-edge devices support sending custom events without type in topic
79+
Execute Command tedge mqtt pub te/device/main///e/ '{"text": "Some test event", "someOtherCustomFragment": {"nested":{"value": "extra info"}} }'
80+
${events}= Device Should Have Event/s expected_text=Some test event with_attachment=False minimum=1 maximum=1 type=ThinEdgeEvent fragment=someOtherCustomFragment
81+
Log ${events}
82+
83+
84+
Thin-edge devices support sending custom alarms #1699
85+
[Tags] \#1699
86+
Execute Command tedge mqtt pub te/device/main///a/myCustomAlarmType '{ "severity": "critical", "text": "Some test alarm", "someOtherCustomFragment": {"nested":{"value": "extra info"}} }'
87+
${alarms}= Device Should Have Alarm/s expected_text=Some test alarm severity=CRITICAL minimum=1 maximum=1 type=myCustomAlarmType
88+
Should Be Equal ${alarms[0]["someOtherCustomFragment"]["nested"]["value"]} extra info
89+
Log ${alarms}
90+
91+
92+
Thin-edge devices support sending custom alarms overriding the type
93+
Execute Command tedge mqtt pub te/device/main///a/myCustomAlarmType '{ "severity": "critical", "text": "Some test alarm", "type": "otherType", "someOtherCustomFragment": {"nested":{"value": "extra info"}} }'
94+
${alarms}= Device Should Have Alarm/s expected_text=Some test alarm severity=CRITICAL minimum=1 maximum=1 type=otherType
95+
Log ${alarms}
96+
97+
98+
Thin-edge devices support sending custom alarms without type in topic
99+
Execute Command tedge mqtt pub te/device/main///a/ '{ "severity": "critical", "text": "Some test alarm", "someOtherCustomFragment": {"nested":{"value": "extra info"}} }'
100+
${alarms}= Device Should Have Alarm/s expected_text=Some test alarm severity=CRITICAL minimum=1 maximum=1 type=ThinEdgeAlarm
101+
Log ${alarms}
102+
103+
104+
Thin-edge devices support sending custom alarms without severity in payload
105+
Execute Command tedge mqtt pub te/device/main///a/myCustomAlarmType2 '{ "text": "Some test alarm" }'
106+
${alarms}= Device Should Have Alarm/s expected_text=Some test alarm severity=MINOR minimum=1 maximum=1 type=myCustomAlarmType2
107+
Log ${alarms}
108+
109+
110+
Thin-edge devices support sending custom alarms with unknown severity in payload
111+
Execute Command tedge mqtt pub te/device/main///a/myCustomAlarmType3 '{ "severity": "invalid", "text": "Some test alarm", "someOtherCustomFragment": {"nested":{"value": "extra info"}} }'
112+
${alarms}= Device Should Have Alarm/s expected_text=Some test alarm severity=MINOR minimum=1 maximum=1 type=myCustomAlarmType3
113+
Log ${alarms}
114+
115+
116+
117+
Thin-edge devices support sending custom alarms without text in payload
118+
Execute Command tedge mqtt pub te/device/main///a/myCustomAlarmType4 '{ "severity": "major", "someOtherCustomFragment": {"nested":{"value": "extra info"}} }'
119+
${alarms}= Device Should Have Alarm/s expected_text=myCustomAlarmType4 severity=MAJOR minimum=1 maximum=1 type=myCustomAlarmType4
120+
Log ${alarms}
121+
122+
123+
Thin-edge devices support sending alarms using text fragment
124+
Execute Command tedge mqtt pub te/device/main///a/parentAlarmType1 '{ "severity": "minor", "text": "Some test alarm" }'
125+
Cumulocity.Set Device ${DEVICE_SN}
126+
${alarms}= Device Should Have Alarm/s expected_text=Some test alarm severity=MINOR minimum=1 maximum=1 type=parentAlarmType1
127+
Log ${alarms}
128+
129+
130+
Thin-edge device supports sending custom Thin-edge device measurements directly to c8y
131+
Execute Command tedge mqtt pub "${C8Y_TOPIC_PREFIX}/measurement/measurements/create" '{"time":"2023-03-20T08:03:56.940907Z","environment":{"temperature":{"value":29.9,"unit":"°C"}},"type":"10min_average","meta":{"sensorLocation":"Brisbane, Australia"}}'
132+
Cumulocity.Set Device ${DEVICE_SN}
133+
${measurements}= Device Should Have Measurements minimum=1 maximum=1 value=environment series=temperature type=10min_average
134+
Should Be Equal As Numbers ${measurements[0]["environment"]["temperature"]["value"]} 29.9
135+
Should Be Equal ${measurements[0]["meta"]["sensorLocation"]} Brisbane, Australia
136+
Should Be Equal ${measurements[0]["type"]} 10min_average
137+
138+
139+
Thin-edge device support sending inventory data via c8y topic
140+
Execute Command tedge mqtt pub "${C8Y_TOPIC_PREFIX}/inventory/managedObjects/update/${DEVICE_SN}" '{"parentInfo":{"nested":{"name":"complex"}},"subType":"customType"}'
141+
Cumulocity.Set Device ${DEVICE_SN}
142+
${mo}= Device Should Have Fragments parentInfo subType
143+
Should Be Equal ${mo["parentInfo"]["nested"]["name"]} complex
144+
Should Be Equal ${mo["subType"]} customType
145+
146+
147+
Thin-edge device support sending inventory data via tedge topic
148+
Execute Command tedge mqtt pub --retain "te/device/main///twin/device_OS" '{"family":"Debian","version":11,"complex":[1,"2",3],"object":{"foo":"bar"}}'
149+
Cumulocity.Set Device ${DEVICE_SN}
150+
${mo}= Device Should Have Fragments device_OS
151+
Should Be Equal ${mo["device_OS"]["family"]} Debian
152+
Should Be Equal As Integers ${mo["device_OS"]["version"]} 11
153+
154+
Should Be Equal As Integers ${mo["device_OS"]["complex"][0]} 1
155+
Should Be Equal As Strings ${mo["device_OS"]["complex"][1]} 2
156+
Should Be Equal As Integers ${mo["device_OS"]["complex"][2]} 3
157+
Should Be Equal ${mo["device_OS"]["object"]["foo"]} bar
158+
159+
# Validate clearing of fragments
160+
Execute Command tedge mqtt pub --retain "te/device/main///twin/device_OS" ''
161+
Managed Object Should Not Have Fragments device_OS
162+
163+
164+
Thin-edge device supports sending inventory data via tedge topic to root fragments
165+
Execute Command tedge mqtt pub --retain "te/device/main///twin/subtype" '"LinuxDeviceA"'
166+
Execute Command tedge mqtt pub --retain "te/device/main///twin/type" '"ShouldBeIgnored"'
167+
Execute Command tedge mqtt pub --retain "te/device/main///twin/name" '"ShouldBeIgnored"'
168+
Cumulocity.Set Device ${DEVICE_SN}
169+
${mo}= Device Should Have Fragments subtype
170+
Should Be Equal ${mo["subtype"]} LinuxDeviceA
171+
Should Be Equal ${mo["type"]} thin-edge.io
172+
Should Be Equal ${mo["name"]} ${DEVICE_SN}
173+
174+
# Validate clearing of fragments
175+
Execute Command tedge mqtt pub --retain "te/device/main///twin/subtype" ''
176+
Managed Object Should Not Have Fragments subtype
177+
178+
# Validate `name` and `type` can't be cleared
179+
Execute Command tedge mqtt pub --retain "te/device/main///twin/type" ''
180+
Execute Command tedge mqtt pub --retain "te/device/main///twin/name" ''
181+
Sleep 5s reason=Wait a minimum period before checking that the fragment has not changed (as it was previously set)
182+
${mo}= Device Should Have Fragments type
183+
Should Be Equal ${mo["type"]} thin-edge.io
184+
Should Be Equal ${mo["name"]} ${DEVICE_SN}
185+
186+
187+
Previously cleared property should be sent to cloud when set again #2365
188+
[Tags] \#2365
189+
Cumulocity.Set Device ${DEVICE_SN}
190+
191+
# set initial value
192+
Execute Command tedge mqtt pub --retain "te/device/main///twin/subtype" '"LinuxDeviceA"'
193+
Device Should Have Fragment Values subtype\=LinuxDeviceA
194+
195+
# Clear
196+
Execute Command tedge mqtt pub --retain "te/device/main///twin/subtype" ''
197+
Managed Object Should Not Have Fragments subtype
198+
199+
# Set to same value prior to clearing it
200+
Execute Command tedge mqtt pub --retain "te/device/main///twin/subtype" '"LinuxDeviceA"'
201+
Device Should Have Fragment Values subtype\=LinuxDeviceA
202+
203+
#
204+
# Services
205+
#
206+
# measurements
207+
Send measurements to an unregistered service
208+
Execute Command tedge mqtt pub te/device/main/service/app1/m/service_type001 '{"temperature": 30.1}'
209+
Cumulocity.Device Should Exist ${DEVICE_SN}
210+
Cumulocity.Should Have Services min_count=1 max_count=1 name=app1
211+
212+
Cumulocity.Device Should Exist ${DEVICE_SN}:device:main:service:app1
213+
${measurements}= Device Should Have Measurements minimum=1 maximum=1 type=service_type001
214+
Should Be Equal ${measurements[0]["type"]} service_type001
215+
Should Be Equal As Numbers ${measurements[0]["temperature"]["temperature"]["value"]} 30.1
216+
217+
Send measurements to a registered service
218+
Execute Command tedge mqtt pub --retain te/device/main/service/app2 '{"@type":"service","@parent":"device/main//"}'
219+
Cumulocity.Device Should Exist ${DEVICE_SN}
220+
Cumulocity.Should Have Services name=app2 min_count=1 max_count=1
221+
222+
Execute Command tedge mqtt pub te/device/main/service/app2/m/service_type002 '{"temperature": 30.1}'
223+
Cumulocity.Device Should Exist ${DEVICE_SN}:device:main:service:app2
224+
${measurements}= Device Should Have Measurements minimum=1 maximum=1 type=service_type002
225+
Should Be Equal ${measurements[0]["type"]} service_type002
226+
Should Be Equal As Numbers ${measurements[0]["temperature"]["temperature"]["value"]} 30.1
227+
228+
# alarms
229+
Send alarms to an unregistered service
230+
Execute Command tedge mqtt pub te/device/main/service/app3/a/alarm_001 '{"text": "test alarm","severity":"major"}'
231+
Cumulocity.Device Should Exist ${DEVICE_SN}
232+
Cumulocity.Should Have Services min_count=1 max_count=1 name=app3
233+
234+
Cumulocity.Device Should Exist ${DEVICE_SN}:device:main:service:app3
235+
${alarms}= Device Should Have Alarm/s expected_text=test alarm type=alarm_001 minimum=1 maximum=1
236+
Should Be Equal ${alarms[0]["type"]} alarm_001
237+
Should Be Equal ${alarms[0]["severity"]} MAJOR
238+
239+
Send alarms to a registered service
240+
Execute Command tedge mqtt pub --retain te/device/main/service/app4 '{"@type":"service","@parent":"device/main//"}'
241+
Cumulocity.Device Should Exist ${DEVICE_SN}
242+
Cumulocity.Should Have Services name=app4 min_count=1 max_count=1
243+
244+
Execute Command tedge mqtt pub te/device/main/service/app4/a/alarm_002 '{"text": "test alarm"}'
245+
Cumulocity.Device Should Exist ${DEVICE_SN}:device:main:service:app4
246+
${alarms}= Device Should Have Alarm/s expected_text=test alarm type=alarm_002 minimum=1 maximum=1
247+
Should Be Equal ${alarms[0]["type"]} alarm_002
248+
249+
# events
250+
Send events to an unregistered service
251+
Execute Command tedge mqtt pub te/device/main/service/app5/e/event_001 '{"text": "test event"}'
252+
Cumulocity.Device Should Exist ${DEVICE_SN}
253+
Cumulocity.Should Have Services name=app5 min_count=1 max_count=1
254+
255+
Cumulocity.Device Should Exist ${DEVICE_SN}:device:main:service:app5
256+
Device Should Have Event/s expected_text=test event type=event_001 minimum=1 maximum=1
257+
258+
Send events to a registered service
259+
Execute Command tedge mqtt pub --retain te/device/main/service/app6 '{"@type":"service","@parent":"device/main//"}'
260+
Cumulocity.Device Should Exist ${DEVICE_SN}
261+
Cumulocity.Should Have Services name=app6 min_count=1 max_count=1
262+
263+
Cumulocity.Device Should Exist ${DEVICE_SN}:device:main:service:app6
264+
Execute Command tedge mqtt pub te/device/main/service/app6/e/event_002 '{"text": "test event"}'
265+
Device Should Have Event/s expected_text=test event type=event_002 minimum=1 maximum=1
266+
267+
*** Keywords ***
268+
269+
Custom Setup
270+
${DEVICE_SN}= Setup
271+
Set Suite Variable $DEVICE_SN
272+
Device Should Exist ${DEVICE_SN}
273+
ThinEdgeIO.Execute Command tedge config set c8y.bridge.built_in true
274+
ThinEdgeIO.Execute Command tedge config set c8y.bridge.topic_prefix custom-c8y-prefix
275+
File Should Exist /etc/tedge/mosquitto-conf/c8y-bridge.conf
276+
ThinEdgeIO.Execute Command tedge reconnect c8y
277+
File Should Not Exist /etc/tedge/mosquitto-conf/c8y-bridge.conf
278+
Service Health Status Should Be Up tedge-mapper-c8y

0 commit comments

Comments
 (0)