Skip to content

Implement fleet provisioning test #501

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 38 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
3d2f290
Implemented fleet provisioning test
sfodagain Oct 29, 2023
b6a19ee
Merge branch 'main' into add-fleet-provisioning-test
sfod Oct 30, 2023
9c7b40c
Merge branch 'main' into add-fleet-provisioning-test
sfod Nov 1, 2023
378a351
Move deleting thing to separate module
sfodagain Nov 6, 2023
0c6fc93
Fix error message
sfodagain Nov 6, 2023
6ca2256
Cleanup code
sfodagain Nov 6, 2023
f10641b
Fix log message
sfodagain Nov 6, 2023
7a74ae8
fixup
sfodagain Nov 6, 2023
0e1a11f
fixup
sfodagain Nov 6, 2023
5e1306e
Mqtt3 and mqtt5 fleet provisioning
sfodagain Nov 7, 2023
912e098
Working version
sfodagain Nov 7, 2023
8a5c6ea
Fix CI
sfodagain Nov 7, 2023
f2fec2a
wip
sfodagain Nov 7, 2023
8ee50d2
wip
sfodagain Nov 7, 2023
21ea38a
Merge branch 'main' into add-fleet-provisioning-test
sfodagain Nov 14, 2023
c5f377d
Simplify test
sfodagain Nov 14, 2023
e1bd1d8
FIx CI
sfodagain Nov 14, 2023
b1098dd
FIx CI
sfodagain Nov 14, 2023
da04c2b
Reorganize service tests
sfodagain Nov 14, 2023
111b98e
Fix CI
sfodagain Nov 14, 2023
c10b373
Fix CI
sfodagain Nov 14, 2023
c8ab0aa
Fix CI
sfodagain Nov 14, 2023
953941e
Use cmdutils from samples
sfodagain Nov 14, 2023
711f786
Fix CI
sfodagain Nov 14, 2023
e3b4ef4
Merge branch 'main' into add-fleet-provisioning-test
sfodagain Nov 14, 2023
9117603
Experiment
sfodagain Nov 15, 2023
77fb38b
Experiment
sfodagain Nov 15, 2023
fda955b
Revert experiment
sfodagain Nov 15, 2023
5138b1c
Extract connection creation
sfodagain Nov 15, 2023
b222b3f
CLen code
sfodagain Nov 15, 2023
a1369b5
Clean code
sfodagain Nov 15, 2023
d2615ba
Move main function
sfodagain Nov 15, 2023
bf6f3ad
cleanup
sfodagain Nov 15, 2023
c23d7bb
cleanup
sfodagain Nov 16, 2023
4444d01
Add CSR
sfodagain Nov 16, 2023
ba2d494
Fix CI
sfodagain Nov 16, 2023
c76ec17
Use exceptions
sfodagain Nov 16, 2023
18f70b7
Fix deleting thing
sfodagain Nov 16, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,14 @@ jobs:
sudo apt-get update -y
sudo apt-get install softhsm -y
softhsm2-util --version
- name: configure AWS credentials (Fleet provisioning)
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ env.CI_FLEET_PROVISIONING_ROLE }}
aws-region: ${{ env.AWS_DEFAULT_REGION }}
- name: run Fleet Provisioning test
run: |
python3 ./servicetests/script/test_fleet_provisioning.py --thing-name-prefix Fleet_Thing_
- name: configure AWS credentials (Connect and PubSub)
uses: aws-actions/configure-aws-credentials@v2
with:
Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<module>samples/FleetProvisioning</module>
<module>samples/Mqtt5/PubSub</module>
<module>samples/Mqtt5/SharedSubscription</module>
<module>servicetests/tests/FleetProvisioning</module>
</modules>

<build>
Expand Down
30 changes: 30 additions & 0 deletions servicetests/script/fleet_provisioning_cfg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"language": "Java",
"service_test_file": "servicetests/tests/FleetProvisioning",
"service_test_region": "us-east-1",
"service_test_main_class": "fleetProvisioning.FleetProvisioning",
"arguments": [
{
"name": "--endpoint",
"secret": "ci/endpoint"
},
{
"name": "--cert",
"secret": "ci/FleetProvisioning/cert",
"filename": "tmp_certificate.pem"
},
{
"name": "--key",
"secret": "ci/FleetProvisioning/key",
"filename": "tmp_key.pem"
},
{
"name": "--template_name",
"data": "CI_FleetProvisioning_Template"
},
{
"name": "--template_parameters",
"data": "{SerialNumber:$INPUT_UUID}"
}
]
}
193 changes: 193 additions & 0 deletions servicetests/script/run_service_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0.

import argparse
import os
import subprocess
import pathlib
import sys
import json
import boto3

current_folder = os.path.dirname(pathlib.Path(__file__).resolve())
if sys.platform == "win32" or sys.platform == "cygwin":
current_folder += "\\"
else:
current_folder += "/"

config_json = None
config_json_arguments_list = []

def setup_json_arguments_list(file, input_uuid=None):
global config_json
global config_json_arguments_list

print("Attempting to get credentials from secrets using Boto3...")
secrets_client = boto3.client(
"secretsmanager", region_name=config_json['service_test_region'])
print("Processing arguments...")

for argument in config_json['arguments']:
# Add the name of the argument
config_json_arguments_list.append(argument['name'])

# Based on the data present, we need to process and add the data differently
try:

# Is there a secret? If so, decode it!
if 'secret' in argument:
secret_data = secrets_client.get_secret_value(
SecretId=argument['secret'])["SecretString"]

# Is this supposed to be stored in a file?
if 'filename' in argument:
with open(str(current_folder) + argument['filename'], "w") as file:
file.write(secret_data)
config_json_arguments_list.append(
str(current_folder) + argument['filename'])
else:
config_json_arguments_list.append(secret_data)

# Raw data? just add it directly!
elif 'data' in argument:
tmp_value = argument['data']
if isinstance(tmp_value, str) and input_uuid is not None:
if ("$INPUT_UUID" in tmp_value):
tmp_value = tmp_value.replace("$INPUT_UUID", input_uuid)
if (tmp_value != None and tmp_value != ""):
config_json_arguments_list.append(tmp_value)

# None of the above? Just print an error
else:
print("ERROR - unknown or missing argument value!")

except Exception as e:
print(f"Something went wrong processing {argument['name']}: {e}!")
return -1
return 0


def setup_service_test(file, input_uuid=None):
global config_json

file_absolute = pathlib.Path(file).resolve()
json_file_data = ""
with open(file_absolute, "r") as json_file:
json_file_data = json_file.read()

# Load the JSON data
config_json = json.loads(json_file_data)

# Make sure required parameters are all there
if not 'language' in config_json or not 'service_test_file' in config_json \
or not 'service_test_region' in config_json or not 'service_test_main_class' in config_json:
return -1

# Preprocess service test arguments (get secret data, etc)
setup_result = setup_json_arguments_list(file, input_uuid)
if setup_result != 0:
return setup_result

print("JSON config file loaded!")
return 0


def cleanup_service_test():
global config_json
global config_json_arguments_list

for argument in config_json['arguments']:
config_json_arguments_list.append(argument['name'])

# Based on the data present, we need to process and add the data differently
try:
# Is there a file? If so, clean it!
if 'filename' in argument:
if (os.path.isfile(str(current_folder) + argument['filename'])):
os.remove(str(current_folder) + argument['filename'])

# Windows 10 certificate store data?
if 'windows_cert_certificate' in argument and 'windows_cert_certificate_path' in argument \
and 'windows_cert_key' in argument and 'windows_cert_key_path' in argument \
and 'windows_cert_pfx_key_path' in argument:

if (os.path.isfile(str(current_folder) + argument['windows_cert_certificate_path'])):
os.remove(str(current_folder) +
argument['windows_cert_certificate_path'])
if (os.path.isfile(str(current_folder) + argument['windows_cert_key_path'])):
os.remove(str(current_folder) +
argument['windows_cert_key_path'])
if (os.path.isfile(str(current_folder) + argument['windows_cert_pfx_key_path'])):
os.remove(str(current_folder) +
argument['windows_cert_pfx_key_path'])

except Exception as e:
print(f"Something went wrong cleaning {argument['name']}!")
return -1


def launch_service_test():
global config_json
global config_json_arguments_list

if (config_json == None):
print("No configuration JSON file data found!")
return -1

exit_code = 0

print("Launching service test...")

# Flatten arguments down into a single string
arguments_as_string = ""
for i in range(0, len(config_json_arguments_list)):
arguments_as_string += str(config_json_arguments_list[i])
if (i+1 < len(config_json_arguments_list)):
arguments_as_string += " "

arguments = ["mvn", "compile", "exec:java"]
arguments.append("-pl")
arguments.append(config_json['service_test_file'])
arguments.append("-Dexec.mainClass=" +
config_json['service_test_main_class'])
arguments.append("-Daws.crt.ci=True")

# We have to do this as a string, unfortunately, due to how -Dexec.args= works...
argument_string = subprocess.list2cmdline(
arguments) + " -Dexec.args=\"" + arguments_as_string + "\""
print(f"Running cmd: {argument_string}")
service_test_return = subprocess.run(argument_string, shell=True)
exit_code = service_test_return.returncode

cleanup_service_test()
return exit_code


def setup_service_test_and_launch(file, input_uuid=None):
setup_result = setup_service_test(file, input_uuid)
if setup_result != 0:
return setup_result

print("About to launch service test...")
return launch_service_test()


def main():
argument_parser = argparse.ArgumentParser(
description="Run service test in CI")
argument_parser.add_argument(
"--file", required=True, help="Configuration file to pull CI data from")
argument_parser.add_argument("--input_uuid", required=False,
help="UUID data to replace '$INPUT_UUID' with. Only works in Data field")
parsed_commands = argument_parser.parse_args()

file = parsed_commands.file
input_uuid = parsed_commands.input_uuid

print(f"Starting to launch service test: config {file}; input UUID: {input_uuid}")
test_result = setup_service_test_and_launch(file, input_uuid)
sys.exit(test_result)


if __name__ == "__main__":
main()
76 changes: 76 additions & 0 deletions servicetests/script/test_fleet_provisioning.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import argparse
import boto3
import uuid
import os
import sys
import run_service_test


def delete_thing(thing_name, region):
try:
iot_client = boto3.client('iot', region_name=region)
except Exception:
print("Error - could not make Boto3 client. Credentials likely could not be sourced")
return -1

thing_principals = None
try:
thing_principals = iot_client.list_thing_principals(thingName=thing_name)
except Exception:
print ("Could not get thing principals!")
return -1

try:
if thing_principals != None:
if thing_principals["principals"] != None:
if len(thing_principals["principals"]) > 0:
for principal in thing_principals["principals"]:
certificate_id = principal.split("/")[1]
iot_client.detach_thing_principal(thingName=thing_name, principal=principal)
iot_client.update_certificate(certificateId=certificate_id, newStatus ='INACTIVE')
iot_client.delete_certificate(certificateId=certificate_id, forceDelete=True)
except Exception as exception:
print (exception)
print ("Could not delete certificate!")
return -1

try:
iot_client.delete_thing(thingName=thing_name)
except Exception as exception:
print (exception)
print ("Could not delete IoT thing!")
return -1

print ("IoT thing deleted successfully")
return 0


def main():
argument_parser = argparse.ArgumentParser(
description="Run service test in CI")
argument_parser.add_argument(
"--input-uuid", required=False, help="UUID for thing name")
argument_parser.add_argument(
"--thing-name-prefix", required=False, default="", help="Prefix for a thing name")
argument_parser.add_argument(
"--region", required=False, default="us-east-1", help="The name of the region to use")
argument_parser.add_argument("--input_uuid", required=False,
help="UUID data to replace '$INPUT_UUID' with. Only works in Data field")
parsed_commands = argument_parser.parse_args()

current_path = os.path.dirname(os.path.realpath(__file__))
cfg_file = os.path.join(current_path, "fleet_provisioning_cfg.json")
input_uuid = parsed_commands.input_uuid if parsed_commands.input_uuid else str(uuid.uuid4())
# Perform fleet provisioning. If it's successful, a newly created thing should appear.
result = run_service_test.setup_service_test_and_launch(cfg_file, input_uuid)
if result != 0:
sys.exit(result)

thing_name = parsed_commands.thing_name_prefix + input_uuid
# Delete a thing created by fleet provisioning.
result = delete_thing(thing_name, parsed_commands.region)
sys.exit(result)


if __name__ == "__main__":
main()
46 changes: 46 additions & 0 deletions servicetests/tests/FleetProvisioning/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>software.amazon.awssdk.iotdevicesdk</groupId>
<artifactId>FleetProvisioningServiceTest</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>${project.groupId}:${project.artifactId}</name>
<description>Java bindings for the AWS IoT Core Service</description>
<url>https://github.com/awslabs/aws-iot-device-sdk-java-v2</url>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk.iotdevicesdk</groupId>
<artifactId>aws-iot-device-sdk</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>../Utils/CommandLineUtils</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Loading