Skip to content

Commit 1d3e206

Browse files
authored
Run jobs in CI service tests (#535)
* Run jobs in CI service tests * Add pythong path * Add iot thing to create and delete things * fix pythong argument * add working directory * fix paths * Fix directory name * fix config path * add utils path * Fix utils path * Fix utils path * Add mqtt_version parameter * Fix syntax * Fix arguments mqtt_version * print start program * execute jobs * run jobs * Fix syntax error * print available jobs * fix * don't wait for further jobs * Fix exit * Fix * end when job is done executing * exit when job execution is done * fix config path * Fix mqtt5 issues * Remove mqtt5 jobs * Remove commented code
1 parent 17669d6 commit 1d3e206

File tree

8 files changed

+1071
-0
lines changed

8 files changed

+1071
-0
lines changed

.github/workflows/ci.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ env:
3232
CI_DEVICE_ADVISOR: arn:aws:iam::180635532705:role/CI_DeviceAdvisor_Role
3333
CI_MQTT5_ROLE: arn:aws:iam::180635532705:role/CI_MQTT5_Role
3434
CI_BUILD_AND_TEST_ROLE: arn:aws:iam::180635532705:role/V2_SDK_Unit_Testing
35+
CI_JOBS_SERVICE_CLIENT_ROLE: arn:aws:iam::180635532705:role/CI_JobsServiceClient_Role
36+
CI_SERVICE_ROLE_CFG_FOLDER: "./aws-iot-device-sdk-python-v2/servicetests/test_cases"
3537

3638
jobs:
3739

@@ -204,6 +206,23 @@ jobs:
204206
python3 -c "from urllib.request import urlretrieve; urlretrieve('${{ env.BUILDER_HOST }}/${{ env.BUILDER_SOURCE }}/${{ env.BUILDER_VERSION }}/builder.pyz?run=${{ env.RUN }}', 'builder')"
205207
chmod a+x builder
206208
./builder build -p ${{ env.PACKAGE_NAME }}
209+
210+
- name: configure AWS credentials (service tests Jobs)
211+
uses: aws-actions/configure-aws-credentials@v2
212+
with:
213+
role-to-assume: ${{ env.CI_JOBS_SERVICE_CLIENT_ROLE}}
214+
aws-region: ${{ env.AWS_DEFAULT_REGION }}
215+
- name: run MQTT3 Jobs servicetests
216+
working-directory: ./aws-iot-device-sdk-python-v2/servicetests
217+
run: |
218+
export PYTHONPATH=${{ github.workspace }}/aws-iot-device-sdk-python-v2/utils:${{ github.workspace }}/aws-iot-device-sdk-python-v2/samples
219+
python3 ./test_cases/test_jobs_execution.py --config-file test_cases/mqtt3_jobs_cfg.json
220+
- name: run MQTT5 Jobs servicetests
221+
working-directory: ./aws-iot-device-sdk-python-v2/servicetests
222+
run: |
223+
export PYTHONPATH=${{ github.workspace }}/aws-iot-device-sdk-python-v2/utils:${{ github.workspace }}/aws-iot-device-sdk-python-v2/samples
224+
python3 ./test_cases/test_jobs_execution.py --config-file test_cases/mqtt5_jobs_cfg.json
225+
207226
- name: configure AWS credentials (Connect and PubSub)
208227
uses: aws-actions/configure-aws-credentials@v1
209228
with:

samples/utils/command_line_utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ def parse_sample_input_jobs():
461461
cmdUtils.register_command(CommandLineUtils.m_cmd_port, "<int>", "Connection port. AWS IoT supports 443 and 8883 (optional, default=8883).", type=int)
462462
cmdUtils.register_command(CommandLineUtils.m_cmd_thing_name, "<str>", "The name assigned to your IoT Thing", required=True)
463463
cmdUtils.register_command(CommandLineUtils.m_cmd_job_time, "<int>", "Emulate working on a job by sleeping this many seconds (optional, default='5')", default=5, type=int)
464+
cmdUtils.register_command(CommandLineUtils.m_cmd_mqtt_version, "<int>", "mqtt version (optional, default='5')", default=5, type=int)
464465
cmdUtils.get_args()
465466

466467
cmdData = CommandLineUtils.CmdData()
@@ -475,6 +476,7 @@ def parse_sample_input_jobs():
475476
cmdData.input_thing_name = cmdUtils.get_command_required(CommandLineUtils.m_cmd_thing_name)
476477
cmdData.input_job_time = int(cmdUtils.get_command(CommandLineUtils.m_cmd_job_time, 5))
477478
cmdData.input_is_ci = cmdUtils.get_command(CommandLineUtils.m_cmd_is_ci, None) != None
479+
cmdData.input_mqtt_version = int(cmdUtils.get_command(CommandLineUtils.m_cmd_mqtt_version, 5))
478480
return cmdData
479481

480482
def parse_sample_input_mqtt5_custom_authorizer_connect():
@@ -877,3 +879,4 @@ def parse_sample_input_pkcs12_connect():
877879
m_cmd_pkcs12_file = "pkcs12_file"
878880
m_cmd_pkcs12_password = "pkcs12_password"
879881
m_cmd_region = "region"
882+
m_cmd_mqtt_version = "mqtt_version"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"language": "Python",
3+
"runnable_file": "./tests/JobsExecution/jobs.py",
4+
"runnable_region": "us-east-1",
5+
"runnable_main_class": "",
6+
"arguments": [
7+
{
8+
"name": "--mqtt_version",
9+
"data": "3"
10+
},
11+
{
12+
"name": "--endpoint",
13+
"secret": "ci/endpoint"
14+
},
15+
{
16+
"name": "--cert",
17+
"data": "tests/JobsExecution/certificate.pem.crt"
18+
},
19+
{
20+
"name": "--key",
21+
"data": "tests/JobsExecution/private.pem.key"
22+
},
23+
{
24+
"name": "--thing_name",
25+
"data": "ServiceTest_Jobs_$INPUT_UUID"
26+
}
27+
]
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"language": "Python",
3+
"runnable_file": "./tests/JobsExecution/jobs.py",
4+
"runnable_region": "us-east-1",
5+
"runnable_main_class": "",
6+
"arguments": [
7+
{
8+
"name": "--mqtt_version",
9+
"data": "5"
10+
},
11+
{
12+
"name": "--endpoint",
13+
"secret": "ci/endpoint"
14+
},
15+
{
16+
"name": "--cert",
17+
"data": "tests/JobsExecution/certificate.pem.crt"
18+
},
19+
{
20+
"name": "--key",
21+
"data": "tests/JobsExecution/private.pem.key"
22+
},
23+
{
24+
"name": "--thing_name",
25+
"data": "ServiceTest_Jobs_$INPUT_UUID"
26+
}
27+
]
28+
}
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0.
3+
4+
import argparse
5+
import json
6+
import os
7+
import sys
8+
import uuid
9+
import time
10+
11+
import boto3
12+
13+
import run_in_ci
14+
import ci_iot_thing
15+
16+
17+
def main():
18+
argument_parser = argparse.ArgumentParser(
19+
description="Run Jobs test in CI")
20+
argument_parser.add_argument(
21+
"--config-file", required=True, help="JSON file providing command-line arguments for a test")
22+
argument_parser.add_argument(
23+
"--input-uuid", required=False, help="UUID for thing name. UUID will be generated if this option is omit")
24+
argument_parser.add_argument(
25+
"--region", required=False, default="us-east-1", help="The name of the region to use")
26+
parsed_commands = argument_parser.parse_args()
27+
28+
try:
29+
iot_client = boto3.client('iot', region_name=parsed_commands.region)
30+
secrets_client = boto3.client("secretsmanager", region_name=parsed_commands.region)
31+
except Exception as e:
32+
print(f"ERROR: Could not make Boto3 iot-data client. Credentials likely could not be sourced. Exception: {e}",
33+
file=sys.stderr)
34+
return -1
35+
36+
input_uuid = parsed_commands.input_uuid if parsed_commands.input_uuid else str(uuid.uuid4())
37+
38+
thing_name = "ServiceTest_Jobs_" + input_uuid
39+
policy_name = secrets_client.get_secret_value(
40+
SecretId="ci/JobsServiceClientTest/policy_name")["SecretString"]
41+
42+
# Temporary certificate/key file path.
43+
certificate_path = os.path.join(os.getcwd(), "tests/JobsExecution/certificate.pem.crt")
44+
key_path = os.path.join(os.getcwd(), "tests/JobsExecution/private.pem.key")
45+
46+
try:
47+
ci_iot_thing.create_iot_thing(
48+
thing_name=thing_name,
49+
thing_group="CI_ServiceClient_Thing_Group",
50+
region=parsed_commands.region,
51+
policy_name=policy_name,
52+
certificate_path=certificate_path,
53+
key_path=key_path)
54+
except Exception as e:
55+
print(f"ERROR: Failed to create IoT thing: {e}")
56+
sys.exit(-1)
57+
58+
thing_job = 'ERROR'
59+
i = 0;
60+
while 'ERROR' in thing_job and i <= 4:
61+
try:
62+
job_id = secrets_client.get_secret_value(SecretId="ci/JobsServiceClientTest/job_id")["SecretString"]
63+
thing_job = iot_client.describe_job_execution(jobId=job_id, thingName=thing_name)
64+
print(f'thing job is {thing_job}');
65+
if 'ERROR' in thing_job:
66+
i = i + 1;
67+
else:
68+
break;
69+
except Exception as e:
70+
print(f"Waiting for a newly created thing to be ready for the Job {e}")
71+
i = i + 1;
72+
time.sleep(1);
73+
74+
# Perform Jobs test. If it's successful, the Job execution should be marked as SUCCEEDED for the thing.
75+
try:
76+
test_result = run_in_ci.setup_and_launch(parsed_commands.config_file, input_uuid)
77+
except Exception as e:
78+
print(f"ERROR: Failed to execute Jobs test: {e}")
79+
test_result = -1
80+
81+
# Test reported success, verify that Job was indeed executed by the thing.
82+
if test_result == 0:
83+
print("Verifying that Job was executed")
84+
try:
85+
job_id = secrets_client.get_secret_value(SecretId="ci/JobsServiceClientTest/job_id")["SecretString"]
86+
thing_job = iot_client.describe_job_execution(jobId=job_id, thingName=thing_name)
87+
job_status = thing_job.get('execution', {}).get('status', {})
88+
if job_status != 'SUCCEEDED':
89+
print(f"ERROR: Could not verify Job execution; Job info: {thing_job}")
90+
test_result = -1
91+
except Exception as e:
92+
print(f"ERROR: Could not verify Job execution: {e}")
93+
test_result = -1
94+
95+
if test_result == 0:
96+
print("Test succeeded")
97+
98+
# Delete a thing created for this test run.
99+
# NOTE We want to try to delete thing even if test was unsuccessful.
100+
try:
101+
ci_iot_thing.delete_iot_thing(thing_name, parsed_commands.region)
102+
except Exception as e:
103+
print(f"ERROR: Failed to delete thing: {e}")
104+
# Fail the test if unable to delete thing, so this won't remain unnoticed.
105+
test_result = -1
106+
107+
try:
108+
if os.path.isfile(certificate_path):
109+
os.remove(certificate_path)
110+
if os.path.isfile(key_path):
111+
os.remove(key_path)
112+
except Exception as e:
113+
print(f"WARNING: Failed to delete local files: {e}")
114+
115+
if test_result != 0:
116+
sys.exit(-1)
117+
118+
119+
if __name__ == "__main__":
120+
main()

0 commit comments

Comments
 (0)