Skip to content

Commit 649361f

Browse files
authored
Mqtt5 Device Defender Support (#646)
* init mqtt5 device defender * fix test * remove packetconnect * no sure why we need disconnect * remove callback severance * device defender sample * update test DDTestRun script * format * update DDTest scritp argument * fix mqtt5 parameter * remove log and add the sample to cmake file * update the readme * update cr
1 parent 4b57e7b commit 649361f

File tree

13 files changed

+731
-38
lines changed

13 files changed

+731
-38
lines changed

.builder/actions/build_samples.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def run(self, env):
4242
# Linux only builds
4343
if sys.platform == "linux" or sys.platform == "linux2":
4444
defender_samples.append('samples/device_defender/basic_report')
45+
defender_samples.append('samples/device_defender/mqtt5_basic_report')
4546

4647
da_samples = [
4748
'deviceadvisor/tests/mqtt_connect',

.github/workflows/ci.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,14 @@ jobs:
422422
with:
423423
role-to-assume: ${{ env.CI_DEVICE_DEFENDER }}
424424
aws-region: ${{ env.AWS_DEFAULT_REGION }}
425-
- name: run DeviceDefemder
425+
- name: run DeviceDefender
426426
run: |
427427
cd ./aws-iot-device-sdk-cpp-v2
428428
python3 ./devicedefender/script/DDTestRun.py
429+
- name: run Mqtt5 DeviceDefender
430+
run: |
431+
cd ./aws-iot-device-sdk-cpp-v2
432+
python3 ./devicedefender/script/DDTestRun.py mqtt5
429433
# Runs the samples and ensures that everything is working
430434
linux-smoke-tests:
431435
runs-on: ubuntu-latest

devicedefender/include/aws/iotdevicedefender/DeviceDefender.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <aws/iotdevicedefender/Exports.h>
88

99
#include <aws/crt/io/EventLoopGroup.h>
10+
#include <aws/crt/mqtt/Mqtt5Client.h>
1011
#include <aws/crt/mqtt/MqttClient.h>
1112

1213
#include <aws/iotdevice/device_defender.h>
@@ -174,6 +175,12 @@ namespace Aws
174175
Crt::Io::EventLoopGroup &eventLoopGroup,
175176
const Crt::String &thingName);
176177

178+
ReportTaskBuilder(
179+
Crt::Allocator *allocator,
180+
std::shared_ptr<Crt::Mqtt5::Mqtt5Client> mqtt5Client,
181+
Crt::Io::EventLoopGroup &eventLoopGroup,
182+
const Crt::String &thingName);
183+
177184
/**
178185
* Sets the device defender report format, or defaults to AWS_IDDRF_JSON.
179186
*/

devicedefender/script/DDTestRun.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
import os
44
import subprocess
55
import platform
6+
import sys
7+
8+
# The script will create an IoT thing with a policy that allows it to run Device Defender, then runs the Device Defender sample
9+
# Usage:
10+
# - python DDTestRun.py : Test Device Defender with Mqtt3 Client
11+
# - python DDTestRun.py mqtt5 : Test Device Defender with Mqtt5 Client
612

713
# On something other than Linux? Pass the test instantly since Device Defender is only supported on Linux
814
if platform.system() != "Linux":
@@ -37,6 +43,10 @@ def delete_thing_with_certi(thingName, certiId, certiArn):
3743
thing_arn = None
3844
client_made_thing = False
3945
client_made_policy = False
46+
use_mqtt5 = False
47+
if len(sys.argv) > 1:
48+
use_mqtt5 = (sys.argv[1] == "mqtt5")
49+
print("Run Device Defender with Mqtt5 Client")
4050

4151
##############################################
4252
# create a test thing
@@ -216,13 +226,24 @@ def delete_thing_with_certi(thingName, certiId, certiArn):
216226
continue
217227

218228
print("[Device Defender]Info: Running sample (this should take ~60 seconds).")
219-
# Run the sample:
220-
exe_path = "build/samples/device_defender/basic_report/"
221-
# If running locally, comment out the line above and uncomment the line below:
222-
#exe_path = "samples/device_defender/basic_report/build/"
223229

224-
# Windows has a different build folder structure, but this ONLY runs on Linux currently so we do not need to worry about it
225-
exe_path = os.path.join(exe_path, "basic-report")
230+
if use_mqtt5:
231+
# Run the sample:
232+
exe_path = "build/samples/device_defender/mqtt5_basic_report/"
233+
# If running locally, comment out the line above and uncomment the line below:
234+
#exe_path = "samples/device_defender/basic_report/build/"
235+
236+
# Windows has a different build folder structure, but this ONLY runs on Linux currently so we do not need to worry about it
237+
exe_path = os.path.join(exe_path, "mqtt5-basic-report")
238+
else:
239+
# Run the sample:
240+
exe_path = "build/samples/device_defender/basic_report/"
241+
# If running locally, comment out the line above and uncomment the line below:
242+
#exe_path = "samples/device_defender/basic_report/build/"
243+
244+
# Windows has a different build folder structure, but this ONLY runs on Linux currently so we do not need to worry about it
245+
exe_path = os.path.join(exe_path, "basic-report")
246+
226247
print("[Device Defender]Info: Start to run: " + exe_path)
227248
# The Device Defender sample will take ~1 minute to run even if successful
228249
# (since samples are sent every minute)

devicedefender/source/DeviceDefender.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,19 @@ namespace Aws
300300
m_cancellationUserdata = nullptr;
301301
}
302302

303+
ReportTaskBuilder::ReportTaskBuilder(
304+
Crt::Allocator *allocator,
305+
std::shared_ptr<Crt::Mqtt5::Mqtt5Client> mqtt5Client,
306+
Crt::Io::EventLoopGroup &eventLoopGroup,
307+
const Crt::String &thingName)
308+
: ReportTaskBuilder(
309+
allocator,
310+
Crt::Mqtt::MqttConnection::NewConnectionFromMqtt5Client(std::move(mqtt5Client)),
311+
eventLoopGroup,
312+
thingName)
313+
{
314+
}
315+
303316
ReportTaskBuilder &ReportTaskBuilder::WithReportFormat(ReportFormat reportFormat) noexcept
304317
{
305318
m_reportFormat = reportFormat;

devicedefender/tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,9 @@ if (UNIX AND NOT APPLE)
1414
add_net_test_case(DeviceDefenderFailedTest)
1515
add_net_test_case(DeviceDefenderCustomMetricSuccess)
1616
add_net_test_case(DeviceDefenderCustomMetricFail)
17+
add_net_test_case(Mqtt5DeviceDefenderResourceSafety)
18+
add_net_test_case(Mqtt5DeviceDefenderFailedTest)
19+
add_net_test_case(Mqtt5DeviceDefenderCustomMetricSuccess)
20+
add_net_test_case(Mqtt5DeviceDefenderCustomMetricFail)
1721
generate_cpp_test_driver(${TEST_BINARY_NAME})
1822
endif()

devicedefender/tests/DeviceDefenderMetricsTest.cpp

Lines changed: 190 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
#include "aws/crt/Types.h"
77
#include <aws/crt/Api.h>
88

9+
#include <aws/crt/mqtt/Mqtt5Client.h>
10+
911
#include <aws/iotdevicecommon/IotDevice.h>
1012
#include <aws/iotdevicedefender/DeviceDefender.h>
1113
#include <aws/testing/aws_test_harness.h>
@@ -32,9 +34,8 @@ static int s_TestDeviceDefenderCustomMetricSuccess(Aws::Crt::Allocator *allocato
3234
Aws::Crt::Io::ClientBootstrap clientBootstrap(eventLoopGroup, defaultHostResolver, allocator);
3335
clientBootstrap.EnableBlockingShutdown();
3436
Aws::Crt::Mqtt::MqttClient mqttClient(clientBootstrap, allocator);
35-
Aws::Crt::Mqtt::MqttClient mqttClientMoved = std::move(mqttClient);
3637

37-
auto mqttConnection = mqttClientMoved.NewConnection("www.example.com", 443, socketOptions, tlsContext);
38+
auto mqttConnection = mqttClient.NewConnection("www.example.com", 443, socketOptions, tlsContext);
3839
const Aws::Crt::String thingName("TestThing");
3940
bool callbackSuccess = false;
4041

@@ -114,12 +115,6 @@ static int s_TestDeviceDefenderCustomMetricSuccess(Aws::Crt::Allocator *allocato
114115
}
115116

116117
ASSERT_TRUE(callbackSuccess);
117-
118-
mqttConnection->Disconnect();
119-
ASSERT_TRUE(mqttConnection);
120-
121-
ASSERT_FALSE(mqttClient);
122-
123118
ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped, (int)task->GetStatus());
124119
}
125120

@@ -142,9 +137,8 @@ static int s_TestDeviceDefenderCustomMetricFail(Aws::Crt::Allocator *allocator,
142137
Aws::Crt::Io::ClientBootstrap clientBootstrap(eventLoopGroup, defaultHostResolver, allocator);
143138
clientBootstrap.EnableBlockingShutdown();
144139
Aws::Crt::Mqtt::MqttClient mqttClient(clientBootstrap, allocator);
145-
Aws::Crt::Mqtt::MqttClient mqttClientMoved = std::move(mqttClient);
146140

147-
auto mqttConnection = mqttClientMoved.NewConnection("www.example.com", 443, socketOptions, tlsContext);
141+
auto mqttConnection = mqttClient.NewConnection("www.example.com", 443, socketOptions, tlsContext);
148142
const Aws::Crt::String thingName("TestThing");
149143
bool callbackSuccess = false;
150144

@@ -187,13 +181,195 @@ static int s_TestDeviceDefenderCustomMetricFail(Aws::Crt::Allocator *allocator,
187181

188182
ASSERT_TRUE(callbackSuccess);
189183

190-
mqttConnection->Disconnect();
191-
ASSERT_TRUE(mqttConnection);
184+
ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped, (int)task->GetStatus());
185+
}
186+
return AWS_ERROR_SUCCESS;
187+
}
188+
AWS_TEST_CASE(DeviceDefenderCustomMetricFail, s_TestDeviceDefenderCustomMetricFail)
189+
190+
static int s_TestMqtt5DeviceDefenderCustomMetricSuccess(Aws::Crt::Allocator *allocator, void *ctx)
191+
{
192+
(void)ctx;
193+
{
194+
Aws::Crt::ApiHandle apiHandle(allocator);
195+
Aws::Iotdevicecommon::DeviceApiHandle deviceApiHandle(allocator);
196+
Aws::Crt::Io::TlsContextOptions tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient();
197+
Aws::Crt::Io::TlsContext tlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT, allocator);
198+
Aws::Crt::Io::SocketOptions socketOptions;
199+
socketOptions.SetConnectTimeoutMs(3000);
200+
Aws::Crt::Io::EventLoopGroup eventLoopGroup(0, allocator);
201+
Aws::Crt::Io::DefaultHostResolver defaultHostResolver(eventLoopGroup, 8, 30, allocator);
202+
Aws::Crt::Io::ClientBootstrap clientBootstrap(eventLoopGroup, defaultHostResolver, allocator);
203+
clientBootstrap.EnableBlockingShutdown();
204+
205+
Aws::Crt::Mqtt5::Mqtt5ClientOptions mqtt5Options(allocator);
206+
mqtt5Options.WithBootstrap(&clientBootstrap);
207+
mqtt5Options.WithSocketOptions(socketOptions);
208+
mqtt5Options.WithTlsConnectionOptions(tlsContext.NewConnectionOptions());
209+
mqtt5Options.WithHostName("www.example.come");
210+
mqtt5Options.WithPort(443);
211+
212+
std::shared_ptr<Aws::Crt::Mqtt5::Mqtt5Client> mqtt5Client =
213+
Aws::Crt::Mqtt5::Mqtt5Client::NewMqtt5Client(mqtt5Options, allocator);
214+
ASSERT_TRUE(mqtt5Client);
215+
216+
const Aws::Crt::String thingName("TestThing");
217+
bool callbackSuccess = false;
218+
219+
std::mutex mutex;
220+
std::condition_variable cv;
221+
bool taskStopped = false;
222+
223+
auto onCancelled = [&](void *a) -> void {
224+
auto *data = reinterpret_cast<bool *>(a);
225+
*data = true;
226+
taskStopped = true;
227+
cv.notify_one();
228+
};
229+
230+
Aws::Iotdevicedefenderv1::ReportTaskBuilder taskBuilder(allocator, mqtt5Client, eventLoopGroup, thingName);
231+
taskBuilder.WithTaskPeriodSeconds((uint32_t)1UL)
232+
.WithNetworkConnectionSamplePeriodSeconds((uint32_t)1UL)
233+
.WithTaskCancelledHandler(onCancelled)
234+
.WithTaskCancellationUserData(&callbackSuccess);
235+
236+
std::shared_ptr<Aws::Iotdevicedefenderv1::ReportTask> task = taskBuilder.Build();
237+
238+
// ================
239+
// Add the custom metrics
240+
std::function<int(double *)> local_metric_number_func = [](double *output) {
241+
*output = 10;
242+
return AWS_OP_SUCCESS;
243+
};
244+
task->RegisterCustomMetricNumber("CustomNumberOne", std::move(local_metric_number_func));
245+
std::function<int(double *)> global_metric_number_func_ref = global_metric_number_func;
246+
task->RegisterCustomMetricNumber("CustomNumberTwo", std::move(global_metric_number_func_ref));
247+
248+
std::function<int(Aws::Crt::Vector<double> *)> local_metric_number_list_func =
249+
[](Aws::Crt::Vector<double> *output) {
250+
output->push_back(101);
251+
output->push_back(102);
252+
output->push_back(103);
253+
return AWS_OP_SUCCESS;
254+
};
255+
task->RegisterCustomMetricNumberList("CustomNumberList", std::move(local_metric_number_list_func));
256+
257+
std::function<int(Aws::Crt::Vector<Aws::Crt::String> *)> local_metric_str_list_func =
258+
[](Aws::Crt::Vector<Aws::Crt::String> *output) {
259+
output->push_back("One Fish");
260+
output->push_back("Two Fish");
261+
output->push_back("Red Fish");
262+
output->push_back("Blue Fish");
263+
return AWS_OP_SUCCESS;
264+
};
265+
task->RegisterCustomMetricStringList("CustomStringList", std::move(local_metric_str_list_func));
192266

193-
ASSERT_FALSE(mqttClient);
267+
std::function<int(Aws::Crt::Vector<Aws::Crt::String> *)> local_metric_ip_list_func =
268+
[](Aws::Crt::Vector<Aws::Crt::String> *output) {
269+
output->push_back("192.0.2.0");
270+
output->push_back("198.51.100.0");
271+
output->push_back("203.0.113.0");
272+
output->push_back("233.252.0.0");
273+
return AWS_OP_SUCCESS;
274+
};
275+
task->RegisterCustomMetricIpAddressList("CustomIPList", std::move(local_metric_ip_list_func));
276+
277+
// ================
278+
279+
ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Ready, (int)task->GetStatus());
280+
281+
ASSERT_SUCCESS(task->StartTask());
282+
ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Running, (int)task->GetStatus());
283+
ASSERT_FAILS(task->StartTask());
284+
ASSERT_TRUE(aws_last_error() == AWS_ERROR_INVALID_STATE);
285+
task->StopTask();
286+
287+
ASSERT_TRUE(task->GetStatus() == Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped);
288+
289+
{
290+
std::unique_lock<std::mutex> lock(mutex);
291+
cv.wait(lock, [&]() { return taskStopped; });
292+
}
293+
294+
ASSERT_TRUE(callbackSuccess);
194295

195296
ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped, (int)task->GetStatus());
196297
}
298+
197299
return AWS_ERROR_SUCCESS;
198300
}
199-
AWS_TEST_CASE(DeviceDefenderCustomMetricFail, s_TestDeviceDefenderCustomMetricFail)
301+
AWS_TEST_CASE(Mqtt5DeviceDefenderCustomMetricSuccess, s_TestMqtt5DeviceDefenderCustomMetricSuccess)
302+
303+
static int s_TestMqtt5DeviceDefenderCustomMetricFail(Aws::Crt::Allocator *allocator, void *ctx)
304+
{
305+
(void)ctx;
306+
{
307+
Aws::Crt::ApiHandle apiHandle(allocator);
308+
Aws::Iotdevicecommon::DeviceApiHandle deviceApiHandle(allocator);
309+
Aws::Crt::Io::TlsContextOptions tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient();
310+
Aws::Crt::Io::TlsContext tlsContext(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT, allocator);
311+
Aws::Crt::Io::SocketOptions socketOptions;
312+
socketOptions.SetConnectTimeoutMs(3000);
313+
Aws::Crt::Io::EventLoopGroup eventLoopGroup(0, allocator);
314+
Aws::Crt::Io::DefaultHostResolver defaultHostResolver(eventLoopGroup, 8, 30, allocator);
315+
Aws::Crt::Io::ClientBootstrap clientBootstrap(eventLoopGroup, defaultHostResolver, allocator);
316+
clientBootstrap.EnableBlockingShutdown();
317+
318+
Aws::Crt::Mqtt5::Mqtt5ClientOptions mqtt5Options(allocator);
319+
mqtt5Options.WithBootstrap(&clientBootstrap);
320+
mqtt5Options.WithSocketOptions(socketOptions);
321+
mqtt5Options.WithTlsConnectionOptions(tlsContext.NewConnectionOptions());
322+
mqtt5Options.WithHostName("www.example.come");
323+
mqtt5Options.WithPort(443);
324+
325+
std::shared_ptr<Aws::Crt::Mqtt5::Mqtt5Client> mqtt5Client =
326+
Aws::Crt::Mqtt5::Mqtt5Client::NewMqtt5Client(mqtt5Options, allocator);
327+
ASSERT_TRUE(mqtt5Client);
328+
329+
const Aws::Crt::String thingName("TestThing");
330+
bool callbackSuccess = false;
331+
332+
std::mutex mutex;
333+
std::condition_variable cv;
334+
bool taskStopped = false;
335+
336+
auto onCancelled = [&](void *a) -> void {
337+
auto *data = reinterpret_cast<bool *>(a);
338+
*data = true;
339+
taskStopped = true;
340+
cv.notify_one();
341+
};
342+
343+
Aws::Iotdevicedefenderv1::ReportTaskBuilder taskBuilder(allocator, mqtt5Client, eventLoopGroup, thingName);
344+
taskBuilder.WithTaskPeriodSeconds((uint32_t)1UL)
345+
.WithNetworkConnectionSamplePeriodSeconds((uint32_t)1UL)
346+
.WithTaskCancelledHandler(onCancelled)
347+
.WithTaskCancellationUserData(&callbackSuccess);
348+
349+
std::shared_ptr<Aws::Iotdevicedefenderv1::ReportTask> task = taskBuilder.Build();
350+
351+
// Add the error custom metric
352+
std::function<int(double *)> number_metric_func = [](double *output) {
353+
*output = 10;
354+
return AWS_OP_ERR;
355+
};
356+
task->RegisterCustomMetricNumber("CustomNumberOne", std::move(number_metric_func));
357+
358+
ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Ready, (int)task->GetStatus());
359+
360+
task->StartTask();
361+
task->StopTask();
362+
363+
ASSERT_TRUE(task->GetStatus() == Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped);
364+
{
365+
std::unique_lock<std::mutex> lock(mutex);
366+
cv.wait(lock, [&]() { return taskStopped; });
367+
}
368+
369+
ASSERT_TRUE(callbackSuccess);
370+
371+
ASSERT_INT_EQUALS((int)Aws::Iotdevicedefenderv1::ReportTaskStatus::Stopped, (int)task->GetStatus());
372+
}
373+
return AWS_ERROR_SUCCESS;
374+
}
375+
AWS_TEST_CASE(Mqtt5DeviceDefenderCustomMetricFail, s_TestMqtt5DeviceDefenderCustomMetricFail)

0 commit comments

Comments
 (0)