EdgeHub Restart Tester (Sender)

Overall changes:
- Introduce EdgeHubRestartTester module
- Introduce edgehub_restart_deployment.template.json
- Introduce EdgeHubRestartTester to Image Build pipeline
- Introduce EdgeHubRestartTester to Container Registry publish
- Make deployment file for connectivity test configurable from VSTS pipeline
- Muffled soon-to-be a new report type for EdgeHubRestartTester

The EdgeHub Restart Test does the following: 
1. The EdgeHubRestartTester sends an edgeHub-restart command to edgeAgent to restart the edgeHub module.
2. The EdgeHubRestartTester spins up parallel tasks (configurable) to 
  2.1 Send messages until a message is sent successfully
  2.2 Send directMethods until a directMethod is sent successfully
3. Record a time period between restart until either message or direct method succeed
4. Generate a test result for each of the sent methods { message, or directMethod }
5. For each corresponding result, trigger an appropriate TestResultCoordinator endpoint.
This commit is contained in:
yophilav 2020-02-05 13:29:50 -08:00 коммит произвёл GitHub
Родитель 0d83f8e2bc
Коммит e206650f63
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
29 изменённых файлов: 1183 добавлений и 13 удалений

Просмотреть файл

@ -213,9 +213,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TwinTester", "test\modules\
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DeploymentTester", "test\modules\DeploymentTester\DeploymentTester.csproj", "{1C1C8203-CD73-4CC1-9112-7315F9DE1AB6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Devices.Edge.Agent.Integration.Test", "edge-agent\test\Microsoft.Azure.Devices.Edge.Agent.Integration.Test\Microsoft.Azure.Devices.Edge.Agent.Integration.Test.csproj", "{B3C68519-B309-42FF-B4DF-9E77B2D4BD50}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MetricsValidator", "test\modules\MetricsValidator\MetricsValidator.csproj", "{5857DB4A-F61A-4467-96F1-003B47B8B2CF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Azure.Devices.Edge.Agent.Integration.Test", "edge-agent\test\Microsoft.Azure.Devices.Edge.Agent.Integration.Test\Microsoft.Azure.Devices.Edge.Agent.Integration.Test.csproj", "{B3C68519-B309-42FF-B4DF-9E77B2D4BD50}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EdgeHubRestartTester", "test\modules\EdgeHubRestartTester\EdgeHubRestartTester.csproj", "{DA073067-83EF-4F4C-8E58-51CA7870C1F6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudToDeviceMessageTester", "test\modules\CloudToDeviceMessageTester\CloudToDeviceMessageTester.csproj", "{446F50F3-3E93-454B-8EBF-4830E92DFE5D}"
EndProject
@ -586,8 +588,10 @@ Global
{1C1C8203-CD73-4CC1-9112-7315F9DE1AB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C1C8203-CD73-4CC1-9112-7315F9DE1AB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C1C8203-CD73-4CC1-9112-7315F9DE1AB6}.Release|Any CPU.Build.0 = Release|Any CPU
{B3C68519-B309-42FF-B4DF-9E77B2D4BD50}.CheckInBuild|Any CPU.ActiveCfg = Debug|Any CPU
{B3C68519-B309-42FF-B4DF-9E77B2D4BD50}.CheckInBuild|Any CPU.Build.0 = Debug|Any CPU
{B3C68519-B309-42FF-B4DF-9E77B2D4BD50}.CheckInBuild|Any CPU.ActiveCfg = CheckInBuild|Any CPU
{B3C68519-B309-42FF-B4DF-9E77B2D4BD50}.CheckInBuild|Any CPU.Build.0 = CheckInBuild|Any CPU
{B3C68519-B309-42FF-B4DF-9E77B2D4BD50}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU
{B3C68519-B309-42FF-B4DF-9E77B2D4BD50}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU
{B3C68519-B309-42FF-B4DF-9E77B2D4BD50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B3C68519-B309-42FF-B4DF-9E77B2D4BD50}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B3C68519-B309-42FF-B4DF-9E77B2D4BD50}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -604,6 +608,14 @@ Global
{5857DB4A-F61A-4467-96F1-003B47B8B2CF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5857DB4A-F61A-4467-96F1-003B47B8B2CF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5857DB4A-F61A-4467-96F1-003B47B8B2CF}.Release|Any CPU.Build.0 = Release|Any CPU
{DA073067-83EF-4F4C-8E58-51CA7870C1F6}.CheckInBuild|Any CPU.ActiveCfg = CheckInBuild|Any CPU
{DA073067-83EF-4F4C-8E58-51CA7870C1F6}.CheckInBuild|Any CPU.Build.0 = CheckInBuild|Any CPU
{DA073067-83EF-4F4C-8E58-51CA7870C1F6}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU
{DA073067-83EF-4F4C-8E58-51CA7870C1F6}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU
{DA073067-83EF-4F4C-8E58-51CA7870C1F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA073067-83EF-4F4C-8E58-51CA7870C1F6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA073067-83EF-4F4C-8E58-51CA7870C1F6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA073067-83EF-4F4C-8E58-51CA7870C1F6}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -688,8 +700,9 @@ Global
{6208102C-151C-45CE-B573-B9C15B551B4D} = {F921339B-32F9-4BF3-B364-2DB01FA2F1A1}
{1447EC5D-DD23-4C51-8941-336A495A48EE} = {F921339B-32F9-4BF3-B364-2DB01FA2F1A1}
{1C1C8203-CD73-4CC1-9112-7315F9DE1AB6} = {F921339B-32F9-4BF3-B364-2DB01FA2F1A1}
{5857DB4A-F61A-4467-96F1-003B47B8B2CF} = {F921339B-32F9-4BF3-B364-2DB01FA2F1A1}
{B3C68519-B309-42FF-B4DF-9E77B2D4BD50} = {F5E37327-3AA9-4CC2-9FE3-B28271ADB5E3}
{5857DB4A-F61A-4467-96F1-003B47B8B2CF} = {F921339B-32F9-4BF3-B364-2DB01FA2F1A1}
{DA073067-83EF-4F4C-8E58-51CA7870C1F6} = {F921339B-32F9-4BF3-B364-2DB01FA2F1A1}
{446F50F3-3E93-454B-8EBF-4830E92DFE5D} = {F921339B-32F9-4BF3-B364-2DB01FA2F1A1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution

Просмотреть файл

@ -102,7 +102,7 @@ jobs:
artifactName: $(images.artifact.name.linux)
itemPattern: |
$(images.artifact.name.linux)/IotEdgeQuickstart.linux-x64.tar.gz
$(images.artifact.name.linux)/e2e_deployment_files/connectivity_deployment.template.json
$(images.artifact.name.linux)/e2e_deployment_files/$(deploymentFileName)
$(images.artifact.name.linux)/scripts/connectivityTest.sh
$(images.artifact.name.linux)/scripts/testHelper.sh
$(images.artifact.name.linux)/artifactInfo.txt
@ -120,8 +120,11 @@ jobs:
container.registry.password: '$(edgebuilds-azurecr-io-pwd)'
iotHub.connectionString: '$(EdgeConnectivityTestHubConnString)'
eventHub.connectionString: '$(EdgeConnectivityEventHubConnString)'
deploymentFileName: '$(deploymentFileName)'
upstream.protocol: '$(upstream.protocol)'
loadGen.message.frequency: '$(loadGen.message.frequency.amd64)'
edgeHubRestartTest.restartPeriod: '$(edgeHubRestartTest.restartPeriod)'
edgeHubRestartTest.sdkOperationTimeout: '$(edgeHubRestartTest.sdkOperationTimeout)'
testDuration: '$(testrun.duration)'
testStartDelay: '$(testStartDelay)'
networkController.frequencies: '$(testrun.network.frequencies)'
@ -182,7 +185,7 @@ jobs:
artifactName: $(images.artifact.name.linux)
itemPattern: |
$(images.artifact.name.linux)/IotEdgeQuickstart.linux-arm.tar.gz
$(images.artifact.name.linux)/e2e_deployment_files/connectivity_deployment.template.json
$(images.artifact.name.linux)/e2e_deployment_files/$(deploymentFileName)
$(images.artifact.name.linux)/scripts/connectivityTest.sh
$(images.artifact.name.linux)/scripts/testHelper.sh
$(images.artifact.name.linux)/artifactInfo.txt

Просмотреть файл

@ -72,6 +72,9 @@ steps:
-metricsEndpointsCSV "${{ parameters['metricsCollector.metricsEndpointsCSV'] }}" \
-metricsScrapeFrequencyInSecs "${{ parameters['metricsCollector.scrapeFrequencyInSecs'] }}" \
-metricsUploadTarget "${{ parameters['metricsCollector.uploadTarget'] }}" \
-deploymentFileName "${{ parameters['deploymentFileName'] }}" \
-EdgeHubRestartTestRestartPeriod "${{ parameters['edgeHubRestartTest.restartPeriod'] }}" \
-EdgeHubRestartTestSdkOperationTimeout "${{ parameters['edgeHubRestartTest.sdkOperationTimeout'] }}" \
-testBuildNumber "${{ parameters['test.buildNumber'] }}" \
-imagesBranchName "${{ parameters['images.branchName'] }}" \
-edgeletBranchName "${{ parameters['edgelet.branchName'] }}" \

Просмотреть файл

@ -140,6 +140,13 @@ jobs:
filePath: scripts/linux/buildImage.sh
arguments: -r $(registry.address) -u $(registry.user) -p $(registry.password) -i azureiotedge-deployment-tester -n microsoft -P DeploymentTester --target-arch aarch64
# EdgeHubRestartTester - Not Using Template for ARM64 because we have 2 different .NET Core.
- task: Bash@3
displayName: Build Image - EdgeHub Restart Tester - aarch64
inputs:
filePath: scripts/linux/buildImage.sh
arguments: -r $(registry.address) -u $(registry.user) -p $(registry.password) -i azureiotedge-edgehub-restart-tester -n microsoft -P EdgeHubRestartTester --target-arch aarch64
# CloudToDeviceMessageTester - Not Using Template for ARM64 because we have 2 different .NET Core.
- task: Bash@3
displayName: Build Image - Cloud To Device Message Receiver Tester - aarch64
@ -291,6 +298,13 @@ jobs:
imageName: azureiotedge-deployment-tester
project: DeploymentTester
# EdgeHub Restart Tester
- template: templates/image-linux.yaml
parameters:
name: EdgeHubRestartTester
imageName: azureiotedge-edgehub-restart-tester
project: EdgeHubRestartTester
# Cloud To Device Message Tester
- template: templates/image-linux.yaml
parameters:
@ -480,6 +494,13 @@ jobs:
imageName: azureiotedge-deployment-tester
project: DeploymentTester
# EdgeHub Restart Tester
- template: templates/image-windows.yaml
parameters:
name: EdgeHubRestartTester
imageName: azureiotedge-edgehub-restart-tester
project: EdgeHubRestartTester
# Cloud To Device Message Tester
- template: templates/image-windows.yaml
parameters:

Просмотреть файл

@ -0,0 +1,217 @@
{
"modulesContent": {
"$edgeAgent": {
"properties.desired": {
"schemaVersion": "1.0",
"runtime": {
"type": "docker",
"settings": {
"minDockerVersion": "v1.25",
"loggingOptions": "",
"registryCredentials": {
"rc1": {
"username": "<CR.Username>",
"password": "<CR.Password>",
"address": "<Container_Registry>"
}
}
}
},
"systemModules": {
"edgeAgent": {
"type": "docker",
"env": {
"UpstreamProtocol": {
"value": "<UpstreamProtocol>"
},
"experimentalfeatures__enabled": {
"value": "true"
},
"experimentalfeatures__enableMetrics": {
"value": "true"
},
"ExperimentalFeatures__EnableUploadLogs": {
"value": "true"
}
},
"settings": {
"image": "<Container_Registry>/microsoft/azureiotedge-agent:<Build.BuildNumber>-linux-<Architecture>",
"createOptions": "{\"ExposedPorts\": {\"9600/tcp\": {}}, \"HostConfig\": {\"PortBindings\": {\"9600/tcp\": [{\"HostPort\": \"9600\"}]}}}"
}
},
"edgeHub": {
"type": "docker",
"settings": {
"image": "<Container_Registry>/microsoft/azureiotedge-hub:<Build.BuildNumber>-linux-<Architecture>",
"createOptions": "{\"ExposedPorts\": {\"9600/tcp\": {}}, \"HostConfig\": {\"PortBindings\": {\"8883/tcp\": [{\"HostPort\": \"8883\"}],\"443/tcp\": [{\"HostPort\": \"443\"}],\"5671/tcp\": [{\"HostPort\": \"5671\"}],\"9600/tcp\": [{\"HostPort\": \"9601\"}]}}}"
},
"env": {
"CollectMetrics": {
"value": "true"
},
"experimentalfeatures__enabled": {
"value": "true"
},
"experimentalfeatures__enableMetrics": {
"value": "true"
}
},
"status": "running",
"restartPolicy": "always"
}
},
"modules": {
"directMethodReceiver1": {
"version": "1.0",
"type": "docker",
"status": "running",
"restartPolicy": "always",
"env": {
"ClientTransportType": {
"value": "Amqp"
},
"ReportingEndpointUrl": {
"value": "http://testResultCoordinator:5001"
},
"trackingId": {
"value": "<TrackingId>"
}
},
"settings": {
"image": "<Container_Registry>/microsoft/azureiotedge-direct-method-receiver:<Build.BuildNumber>-linux-<Architecture>",
"createOptions": ""
}
},
"edgeHubRestartTester1": {
"version": "1.0",
"type": "docker",
"status": "running",
"restartPolicy": "always",
"env": {
"IOT_HUB_CONNECTION_STRING": {
"value": "<IoTHubConnectionString>"
},
"directMethodEnabled": {
"value": "true"
},
"directMethodTargetModuleId": {
"value": "directMethodReceiver1"
},
"messageEnabled": {
"value": "true"
},
"reportingEndpointUrl": {
"value": "http://localhost:5001"
},
"restartPeriod": {
"value": "<EdgeHubRestartTest.RestartPeriod>"
},
"sdkOperationTimeout": {
"value": "<EdgeHubRestartTest.SdkOperationTimeout>"
},
"testDuration": {
"value": "<TestDuration>"
},
"testStartDelay": {
"value": "<TestStartDelay>"
},
"trackingId": {
"value": "<TrackingId>"
}
},
"settings": {
"image": "<Container_Registry>/microsoft/azureiotedge-edgehub-restart-tester:<Build.BuildNumber>-linux-<Architecture>",
"createOptions": "{\"HostConfig\":{\"NetworkMode\":\"host\"},\"NetworkingConfig\":{\"EndpointsConfig\":{\"host\":{}}}}"
}
},
"relayer1": {
"version": "1.0",
"type": "docker",
"status": "running",
"restartPolicy": "always",
"env": {
"transportType": {
"value": "Amqp"
},
"inputName": {
"value": "input1"
},
"outputName": {
"value": "output1"
},
"testResultCoordinatorUrl": {
"value": "http://testResultCoordinator:5001"
}
},
"settings": {
"image": "<Container_Registry>/microsoft/azureiotedge-relayer:<Build.BuildNumber>-linux-<Architecture>",
"createOptions": ""
}
},
"testResultCoordinator": {
"version": "1.0",
"type": "docker",
"status": "running",
"restartPolicy": "always",
"env": {
"Logging:LogLevel:Microsoft": {
"value": "Error"
},
"trackingId": {
"value": "<TrackingId>"
},
"testStartDelay": {
"value": "<TestStartDelay>"
},
"testDuration": {
"value": "<TestDuration>"
},
"verificationDelay": {
"value": "<TestResultCoordinator.VerificationDelay>"
},
"eventHubConnectionString": {
"value": "<TestResultCoordinator.EventHubConnectionString>"
},
"ConsumerGroupName": {
"value": "<TestResultCoordinator.ConsumerGroupId>"
},
"optimizeForPerformance": {
"value": "<TestResultCoordinator.OptimizeForPerformance>"
},
"logAnalyticsWorkspaceId": {
"value": "<LogAnalyticsWorkspaceId>"
},
"logAnalyticsSharedKey": {
"value": "<LogAnalyticsSharedKey>"
},
"logAnalyticsLogType": {
"value": "<TestResultCoordinator.LogAnalyticsLogType>"
},
"IOT_HUB_CONNECTION_STRING": {
"value": "<IoTHubConnectionString>"
},
"STORAGE_ACCOUNT_CONNECTION_STRING": {
"value": "<TestResultCoordinator.StorageAccountConnectionString>"
}
},
"settings": {
"image": "<Container_Registry>/microsoft/azureiotedge-test-result-coordinator:<Build.BuildNumber>-linux-<Architecture>",
"createOptions": "{\"HostConfig\": {\"PortBindings\": {\"5001/tcp\": [{\"HostPort\": \"5001\"}]}}}"
}
}
}
}
},
"$edgeHub": {
"properties.desired": {
"schemaVersion": "1.0",
"routes": {
"edgeHubRestarterToRelayer1": "FROM /messages/modules/loadGen1/outputs/output1 INTO BrokeredEndpoint(\"/modules/relayer1/inputs/input1\")"
},
"storeAndForwardConfiguration": {
"timeToLiveSecs": 86400
}
}
}
}
}

Просмотреть файл

@ -258,6 +258,7 @@ publish_app "MetricsCollector"
publish_app "TestResultCoordinator"
publish_app "NetworkController"
publish_app "DeploymentTester"
publish_app "EdgeHubRestartTester"
publish_app "MetricsValidator"
publish_app "CloudToDeviceMessageTester"

Просмотреть файл

@ -154,6 +154,7 @@ $appProjectList.Add("Relayer.csproj")
$appProjectList.Add("MetricsCollector.csproj")
$appProjectList.Add("TestResultCoordinator.csproj")
$appProjectList.Add("DeploymentTester.csproj")
$appProjectList.Add("EdgeHubRestartTester.csproj")
$appProjectList.Add("MetricsValidator.csproj")
$appProjectList.Add("CloudToDeviceMessageTester.csproj")

Просмотреть файл

@ -52,6 +52,8 @@ function prepare_test_from_artifacts() {
sed -i -e "s@<TestStartDelay>@$TEST_START_DELAY@g" "$deployment_working_file"
sed -i -e "s@<TrackingId>@$tracking_id@g" "$deployment_working_file"
sed -i -e "s@<UpstreamProtocol>@$UPSTREAM_PROTOCOL@g" "$deployment_working_file"
sed -i -e "s@<EdgeHubRestartTest.RestartPeriod>@$RESTART_TEST_RESTART_PERIOD@g" "$deployment_working_file"
sed -i -e "s@<EdgeHubRestartTest.SdkOperationTimeout>@$RESTART_TEST_SDK_OPERATION_TIMEOUT@g" "$deployment_working_file"
sed -i -e "s@<TestResultCoordinator.VerificationDelay>@$VERIFICATION_DELAY@g" "$deployment_working_file"
sed -i -e "s@<TestResultCoordinator.OptimizeForPerformance>@$optimize_for_performance@g" "$deployment_working_file"
@ -243,6 +245,15 @@ function process_args() {
elif [ $saveNextArg -eq 31 ]; then
DEVOPS_BUILDID="$arg"
saveNextArg=0
elif [ $saveNextArg -eq 32 ]; then
DEPLOYMENT_FILE_NAME="$arg"
saveNextArg=0
elif [ $saveNextArg -eq 33 ]; then
RESTART_TEST_RESTART_PERIOD="$arg"
saveNextArg=0
elif [ $saveNextArg -eq 34 ]; then
RESTART_TEST_SDK_OPERATION_TIMEOUT="$arg"
saveNextArg=0
else
case "$arg" in
'-h' | '--help' ) usage;;
@ -277,6 +288,9 @@ function process_args() {
'-storageAccountConnectionString' ) saveNextArg=29;;
'-devOpsAccessToken' ) saveNextArg=30;;
'-devOpsBuildId' ) saveNextArg=31;;
'-deploymentFileName' ) saveNextArg=32;;
'-EdgeHubRestartTestRestartPeriod' ) saveNextArg=33;;
'-EdgeHubRestartTestSdkOperationTimeout' ) saveNextArg=34;;
'-waitForTestComplete' ) WAIT_FOR_TEST_COMPLETE=1;;
'-cleanAll' ) CLEAN_ALL=1;;
* ) usage;;
@ -289,6 +303,7 @@ function process_args() {
[[ -z "$ARTIFACT_IMAGE_BUILD_NUMBER" ]] && { print_error 'Artifact image build number is required'; exit 1; }
[[ -z "$CONTAINER_REGISTRY_USERNAME" ]] && { print_error 'Container registry username is required'; exit 1; }
[[ -z "$CONTAINER_REGISTRY_PASSWORD" ]] && { print_error 'Container registry password is required'; exit 1; }
[[ -z "$DEPLOYMENT_FILE_NAME" ]] && { print_error 'Deployment file name is required'; exit 1; }
[[ -z "$EDGELET_BRANCH_NAME" ]] && { print_error 'Edgelet branch name is required'; exit 1; }
[[ -z "$EVENTHUB_CONNECTION_STRING" ]] && { print_error 'Event hub connection string is required'; exit 1; }
[[ -z "$HOST_PLATFORM" ]] && { print_error 'Host platform is required'; exit 1; }
@ -470,6 +485,9 @@ function usage() {
echo ' -edgeletBranchName Branch name that built the edgelet artifacts'
echo ' -testBuildNumber Unique identifier for the main connectivity test run'
echo ' -hostPlatform Describes the host OS and cpu architecture.'
echo ' -deploymentFileName Deployment file name'
echo ' -EdgeHubRestartTestRestartPeriod EdgeHub restart period (must be greater than 1 minutes)'
echo ' -EdgeHubRestartTestSdkOperationTimeout SDK retry timeout'
echo ' -storageAccountConnectionString Azure storage account connection string with privilege to create blob container.'
echo ' -cleanAll Do docker prune for containers, logs and volumes.'
@ -509,7 +527,7 @@ fi
iotedged_artifact_folder="$(get_iotedged_artifact_folder $E2E_TEST_DIR)"
iotedge_quickstart_artifact_file="$(get_iotedge_quickstart_artifact_file $E2E_TEST_DIR)"
connectivity_deployment_artifact_file="$E2E_TEST_DIR/artifacts/core-linux/e2e_deployment_files/connectivity_deployment.template.json"
connectivity_deployment_artifact_file="$E2E_TEST_DIR/artifacts/core-linux/e2e_deployment_files/$DEPLOYMENT_FILE_NAME"
deployment_working_file="$working_folder/deployment.json"
testRet=0

Просмотреть файл

@ -0,0 +1,65 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Condition="'$(DotNet_Runtime)' != 'netcoreapp3.0'">
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<PropertyGroup Condition="'$(OS)|$(DotNet_Runtime)' == 'Unix|netcoreapp3.0'">
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<Configurations>Debug;Release;CodeCoverage;CheckInBuild</Configurations>
<HighEntropyVA>true</HighEntropyVA>
</PropertyGroup>
<!--
Normally, the 'Debug' configuration would work for code coverage, but Microsoft.CodeCoverage currently requires '<DebugType>full</DebugType>' for .NET Core.
See https://github.com/Microsoft/vstest-docs/blob/06f9dc0aeb47be7204dc4e1a98c110ead3e978c7/docs/analyze.md#setup-a-project.
That setting seems to break the "Open Test" context menu in VS IDE, so we'll use a dedicated configuration for code coverage.
-->
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'CodeCoverage|AnyCPU' ">
<IntermediateOutputPath>obj\CodeCoverage</IntermediateOutputPath>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\CodeCoverage</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Content Include="docker*/**/*.*" CopyToPublishDirectory="Always" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.Devices" Version="1.18.2" />
<PackageReference Include="Microsoft.Azure.Devices.Client" Version="1.22.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\edge-util\src\Microsoft.Azure.Devices.Edge.Util\Microsoft.Azure.Devices.Edge.Util.csproj" />
<ProjectReference Include="..\ModuleLib\Microsoft.Azure.Devices.Edge.ModuleUtil.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="config/appsettings.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\..\..\stylecop.json" Link="stylecop.json" />
</ItemGroup>
<PropertyGroup>
<CodeAnalysisRuleSet>..\..\..\stylecop.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<Import Project="..\..\..\stylecop.props" />
</Project>

Просмотреть файл

@ -0,0 +1,32 @@
{
"IOT_HUB_CONNECTION_STRING": {
"value": ""
},
"directMethodEnabled": {
"value": "true"
},
"directMethodTargetModuleId": {
"value": "directMethodReceiver1"
},
"messageEnabled": {
"value": "true"
},
"reportingEndpointUrl": {
"value": "http://localhost:5001"
},
"restartPeriod": {
"value": "00:05:00.000"
},
"sdkOperationTimeout": {
"value": "00:00:00.020"
},
"testDuration": {
"value": "01:00:00.000"
},
"testStartDelay": {
"value": "00:02:00.000"
},
"trackingId": {
"value": ""
}
}

Просмотреть файл

@ -0,0 +1,17 @@
ARG base_tag=2.1.10-alpine3.7
FROM mcr.microsoft.com/dotnet/core/runtime:${base_tag}
ARG EXE_DIR=.
ENV MODULE_NAME "EdgeHubRestartTester.dll"
WORKDIR /app
COPY $EXE_DIR/ ./
# Add an unprivileged user account for running the module
RUN adduser -Ds /bin/sh moduleuser
USER moduleuser
CMD echo "$(date --utc +"[%Y-%m-%d %H:%M:%S %:z]"): Starting Module" && \
exec /usr/bin/dotnet EdgeHubRestartTester.dll

Просмотреть файл

@ -0,0 +1,15 @@
ARG base_tag=1.0.3-linux-arm32v7
FROM azureiotedge/azureiotedge-module-base:${base_tag}
ARG EXE_DIR=.
ENV MODULE_NAME "EdgeHubRestartTester.dll"
WORKDIR /app
COPY $EXE_DIR/ ./
USER moduleuser
CMD echo "$(date --utc +"[%Y-%m-%d %H:%M:%S %:z]"): Starting Module" && \
exec /usr/bin/dotnet EdgeHubRestartTester.dll

Просмотреть файл

@ -0,0 +1,15 @@
ARG base_tag=1.0.3-linux-arm64v8
FROM azureiotedge/azureiotedge-module-base:${base_tag}
ARG EXE_DIR=.
ENV MODULE_NAME "EdgeHubRestartTester.dll"
WORKDIR /app
COPY $EXE_DIR/ ./
USER moduleuser
CMD echo "$(date --utc +"%Y-%m-%d %H:%M:%S %:z") Starting Module" && \
exec /usr/bin/dotnet EdgeHubRestartTester.dll

Просмотреть файл

@ -0,0 +1,10 @@
ARG base_tag=2.1.10-nanoserver-1809
FROM mcr.microsoft.com/dotnet/core/runtime:${base_tag}
ARG EXE_DIR=.
WORKDIR /app
COPY $EXE_DIR/ ./
CMD ["dotnet", "EdgeHubRestartTester.dll"]

Просмотреть файл

@ -0,0 +1,11 @@
ARG base_tag=1.0.2-windows-arm32v7
ARG base_registry
FROM ${base_registry}/azureiotedge/azureiotedge-module-base:${base_tag}
ARG EXE_DIR=.
WORKDIR /app
COPY $EXE_DIR/ ./
CMD ["dotnet", "EdgeHubRestartTester.dll"]

Просмотреть файл

@ -0,0 +1,159 @@
// Copyright (c) Microsoft. All rights reserved.
namespace EdgeHubRestartTester
{
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Client.Exceptions;
using Microsoft.Azure.Devices.Edge.ModuleUtil;
using Microsoft.Azure.Devices.Edge.ModuleUtil.TestResults;
using Microsoft.Azure.Devices.Edge.Util;
using Microsoft.Extensions.Logging;
class DirectMethodEdgeHubConnectorTest : IEdgeHubConnector
{
readonly ModuleClient dmModuleClient;
readonly Guid batchId;
readonly DateTime runExpirationTime;
readonly CancellationToken cancellationToken;
readonly DateTime edgeHubRestartedTime;
readonly uint restartSequenceNumber;
readonly ILogger logger;
long directMethodCount = 0;
public DirectMethodEdgeHubConnectorTest(
ModuleClient dmModuleClient,
Guid batchId,
DateTime runExpirationTime,
DateTime edgeHubRestartedTime,
uint restartSequenceNumber,
ILogger logger,
CancellationToken cancellationToken)
{
this.dmModuleClient = Preconditions.CheckNotNull(dmModuleClient, nameof(dmModuleClient));
this.batchId = batchId;
this.runExpirationTime = runExpirationTime;
this.cancellationToken = Preconditions.CheckNotNull(cancellationToken, nameof(cancellationToken));
this.edgeHubRestartedTime = edgeHubRestartedTime;
this.restartSequenceNumber = restartSequenceNumber;
this.logger = Preconditions.CheckNotNull(logger, nameof(logger));
}
public async Task StartAsync()
{
(DateTime dmCompletedTime, HttpStatusCode dmStatusCode) = await this.SendDirectMethodAsync(
Settings.Current.DeviceId,
Settings.Current.DirectMethodTargetModuleId,
this.dmModuleClient,
Settings.Current.DirectMethodName,
this.runExpirationTime,
this.cancellationToken,
this.logger);
TestResultBase dmTestResult = new EdgeHubRestartDirectMethodResult(
Settings.Current.ModuleId + "." + TestOperationResultType.EdgeHubRestartDirectMethod.ToString(),
DateTime.UtcNow,
Settings.Current.TrackingId,
this.batchId,
Interlocked.Read(ref this.directMethodCount).ToString(),
this.edgeHubRestartedTime,
dmCompletedTime,
dmStatusCode,
this.restartSequenceNumber);
var reportClient = new TestResultReportingClient { BaseUrl = Settings.Current.ReportingEndpointUrl.AbsoluteUri };
await ModuleUtil.ReportTestResultUntilSuccessAsync(
reportClient,
this.logger,
dmTestResult,
this.cancellationToken);
}
async Task<Tuple<DateTime, HttpStatusCode>> SendDirectMethodAsync(
string deviceId,
string targetModuleId,
ModuleClient moduleClient,
string directMethodName,
DateTime runExpirationTime,
CancellationToken cancellationToken,
ILogger logger)
{
while ((!cancellationToken.IsCancellationRequested) && (DateTime.UtcNow < runExpirationTime))
{
try
{
// Direct Method sequence number is always increasing regardless of sending result.
Interlocked.Increment(ref this.directMethodCount);
MethodRequest request = new MethodRequest(
directMethodName,
Encoding.UTF8.GetBytes($"{{ \"Message\": \"Hello\", \"DirectMethodCount\": \"{Interlocked.Read(ref this.directMethodCount).ToString()}\" }}"),
Settings.Current.SdkOperationTimeout,
Settings.Current.SdkOperationTimeout);
MethodResponse result = await moduleClient.InvokeMethodAsync(deviceId, targetModuleId, request);
logger.LogInformation($"[DirectMethodEdgeHubConnector] Invoke DirectMethod with count {Interlocked.Read(ref this.directMethodCount).ToString()}");
if ((HttpStatusCode)result.Status == HttpStatusCode.OK)
{
logger.LogDebug(result.ResultAsJson);
}
else
{
logger.LogError(result.ResultAsJson);
}
return new Tuple<DateTime, HttpStatusCode>(DateTime.UtcNow, (HttpStatusCode)result.Status);
}
catch (Exception e)
{
// Only handle the exception that relevant to our test case; otherwise, re-throw it.
if (this.IsEdgeHubDownDuringDirectMethodSend(e) || this.IsDirectMethodReceiverNotConnected(e))
{
// swallow exeception and retry until success
logger.LogDebug(e, $"[DirectMethodEdgeHubConnector] Exception caught with SequenceNumber {Interlocked.Read(ref this.directMethodCount).ToString()}");
}
else
{
// TODO: Use the TRC result type
// something is wrong, Log and send report to TRC
logger.LogError(e, $"[DirectMethodEdgeHubConnector] Exception caught with SequenceNumber {Interlocked.Read(ref this.directMethodCount).ToString()}");
}
}
}
return new Tuple<DateTime, HttpStatusCode>(DateTime.UtcNow, HttpStatusCode.InternalServerError);
}
bool IsEdgeHubDownDuringDirectMethodSend(Exception e)
{
// This is a socket exception error code when EdgeHub is down.
const int EdgeHubNotAvailableErrorCode = 111;
if (e is IotHubCommunicationException)
{
if (e?.InnerException?.InnerException is SocketException)
{
int errorCode = ((SocketException)e.InnerException.InnerException).ErrorCode;
return errorCode == EdgeHubNotAvailableErrorCode;
}
}
return false;
}
bool IsDirectMethodReceiverNotConnected(Exception e)
{
if (e is DeviceNotFoundException)
{
string errorMsg = e.Message;
return Regex.IsMatch(errorMsg, $"\\b{Settings.Current.DirectMethodTargetModuleId}\\b");
}
return false;
}
}
}

Просмотреть файл

@ -0,0 +1,10 @@
// Copyright (c) Microsoft. All rights reserved.
namespace EdgeHubRestartTester
{
using System.Threading.Tasks;
public interface IEdgeHubConnector
{
Task StartAsync();
}
}

Просмотреть файл

@ -0,0 +1,112 @@
// Copyright (c) Microsoft. All rights reserved.
namespace EdgeHubRestartTester
{
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Edge.ModuleUtil;
using Microsoft.Azure.Devices.Edge.ModuleUtil.TestResults;
using Microsoft.Azure.Devices.Edge.Util;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
class MessageEdgeHubConnectorTest : IEdgeHubConnector
{
readonly ModuleClient msgModuleClient;
readonly Guid batchId;
readonly DateTime runExpirationTime;
readonly CancellationToken cancellationToken;
readonly DateTime edgeHubRestartedTime;
readonly ILogger logger;
long messageCount = 0;
public MessageEdgeHubConnectorTest(
ModuleClient msgModuleClient,
Guid batchId,
DateTime runExpirationTime,
DateTime edgeHubRestartedTime,
ILogger logger,
CancellationToken cancellationToken)
{
this.msgModuleClient = Preconditions.CheckNotNull(msgModuleClient, nameof(msgModuleClient));
this.batchId = batchId;
this.runExpirationTime = runExpirationTime;
this.cancellationToken = Preconditions.CheckNotNull(cancellationToken, nameof(cancellationToken));
this.edgeHubRestartedTime = edgeHubRestartedTime;
this.logger = Preconditions.CheckNotNull(logger, nameof(logger));
}
public async Task StartAsync()
{
(DateTime msgCompletedTime, HttpStatusCode mgsStatusCode) = await this.SendMessageAsync(
this.msgModuleClient,
Settings.Current.TrackingId,
this.batchId,
Settings.Current.MessageOutputEndpoint,
this.runExpirationTime,
this.cancellationToken);
TestResultBase msgTestResult = new EdgeHubRestartMessageResult(
Settings.Current.ModuleId + "." + TestOperationResultType.EdgeHubRestartMessage.ToString(),
DateTime.UtcNow,
Settings.Current.TrackingId,
this.batchId.ToString(),
Interlocked.Read(ref this.messageCount).ToString(),
this.edgeHubRestartedTime,
msgCompletedTime,
mgsStatusCode);
var reportClient = new TestResultReportingClient { BaseUrl = Settings.Current.ReportingEndpointUrl.AbsoluteUri };
await ModuleUtil.ReportTestResultUntilSuccessAsync(
reportClient,
this.logger,
msgTestResult,
this.cancellationToken);
}
async Task<Tuple<DateTime, HttpStatusCode>> SendMessageAsync(
ModuleClient moduleClient,
string trackingId,
Guid batchId,
string msgOutputEndpoint,
DateTime runExpirationTime,
CancellationToken cancellationToken)
{
Interlocked.Increment(ref this.messageCount);
while ((!cancellationToken.IsCancellationRequested) && (DateTime.UtcNow < runExpirationTime))
{
Message message = new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new { data = DateTime.UtcNow.ToString() })));
message.Properties.Add("sequenceNumber", Interlocked.Read(ref this.messageCount).ToString());
message.Properties.Add("batchId", batchId.ToString());
message.Properties.Add("trackingId", trackingId);
try
{
// Sending the result via edgeHub
await moduleClient.SendEventAsync(msgOutputEndpoint, message);
this.logger.LogInformation($"[SendMessageAsync] Send Message with count {Interlocked.Read(ref this.messageCount).ToString()}: finished.");
return new Tuple<DateTime, HttpStatusCode>(DateTime.UtcNow, HttpStatusCode.OK);
}
catch (Exception ex)
{
if (ex is TimeoutException)
{
// TimeoutException is expected to happen while the EdgeHub is down.
// Let's log the attempt and retry the message send until successful
this.logger.LogDebug(ex, $"[SendMessageAsync] Exception caught with SequenceNumber {this.messageCount}, BatchId: {batchId.ToString()};");
}
else
{
this.logger.LogError(ex, $"[SendMessageAsync] Exception caught with SequenceNumber {this.messageCount}, BatchId: {batchId.ToString()};");
}
}
}
return new Tuple<DateTime, HttpStatusCode>(DateTime.UtcNow, HttpStatusCode.InternalServerError);
}
}
}

Просмотреть файл

@ -0,0 +1,172 @@
// Copyright (c) Microsoft. All rights reserved.
namespace EdgeHubRestartTester
{
using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Edge.ModuleUtil;
using Microsoft.Azure.Devices.Edge.Util;
using Microsoft.Extensions.Logging;
class Program
{
static readonly ILogger Logger = ModuleUtil.CreateLogger("EdgeHubRestartTester");
public static int Main() => MainAsync().Result;
static async Task<int> MainAsync()
{
uint restartCount = 0;
Guid batchId = Guid.NewGuid();
Logger.LogInformation($"Starting EdgeHubRestartTester ({batchId}) with the following settings:\r\n{Settings.Current}");
Logger.LogInformation($"EdgeHubRestartTester delay start for {Settings.Current.TestStartDelay}.");
await Task.Delay(Settings.Current.TestStartDelay);
(CancellationTokenSource cts, ManualResetEventSlim completed, Option<object> handler) = ShutdownHandler.Init(TimeSpan.FromSeconds(5), Logger);
ServiceClient iotHubServiceClient = null;
ModuleClient msgModuleClient = null;
ModuleClient dmModuleClient = null;
try
{
iotHubServiceClient = ServiceClient.CreateFromConnectionString(Settings.Current.IoTHubConnectionString);
if (Settings.Current.MessageEnabled)
{
msgModuleClient = await ModuleUtil.CreateModuleClientAsync(
Settings.Current.TransportType,
ModuleUtil.DefaultTimeoutErrorDetectionStrategy,
ModuleUtil.DefaultTransientRetryStrategy,
Logger);
msgModuleClient.OperationTimeoutInMilliseconds = (uint)Settings.Current.SdkOperationTimeout.TotalMilliseconds;
}
if (Settings.Current.DirectMethodEnabled)
{
dmModuleClient = await ModuleUtil.CreateModuleClientAsync(
Settings.Current.TransportType,
ModuleUtil.DefaultTimeoutErrorDetectionStrategy,
ModuleUtil.DefaultTransientRetryStrategy,
Logger);
}
DateTime testStart = DateTime.UtcNow;
DateTime testExpirationTime = testStart + Settings.Current.TestDuration;
while ((!cts.IsCancellationRequested) && (DateTime.UtcNow < testExpirationTime))
{
(DateTime restartTime, _) = await RestartEdgeHub(iotHubServiceClient);
DateTime eachTestExpirationTime = restartTime.Add(Settings.Current.RestartPeriod);
// Increment the counter when issue an edgeHub restart
restartCount++;
// Setup Message Task
Task sendMessageTask = Task.CompletedTask;
if (Settings.Current.MessageEnabled)
{
sendMessageTask =
new MessageEdgeHubConnectorTest(
msgModuleClient,
batchId,
eachTestExpirationTime,
restartTime,
Logger,
cts.Token)
.StartAsync();
}
// Setup Direct Method Task
Task directMethodTask = Task.CompletedTask;
if (Settings.Current.DirectMethodEnabled)
{
directMethodTask =
new DirectMethodEdgeHubConnectorTest(
dmModuleClient,
batchId,
eachTestExpirationTime,
restartTime,
restartCount,
Logger,
cts.Token)
.StartAsync();
}
// Wait for the two task to be done before do a restart
await Task.WhenAll(new[] { sendMessageTask, directMethodTask });
// Wait until the specified restart period to do another restart
await Task.Delay((int)(eachTestExpirationTime - DateTime.UtcNow).TotalMilliseconds, cts.Token);
}
}
catch (Exception e)
{
Logger.LogError($"Exception caught: {e}");
throw;
}
finally
{
iotHubServiceClient?.Dispose();
dmModuleClient?.Dispose();
msgModuleClient?.Dispose();
}
await cts.Token.WhenCanceled();
completed.Set();
handler.ForEach(h => GC.KeepAlive(h));
Logger.LogInformation("EdgeHubRestartTester Main() finished.");
return 0;
}
static async Task<Tuple<DateTime, HttpStatusCode>> RestartEdgeHub(
ServiceClient iotHubServiceClient)
{
const uint maxRetry = 3;
uint restartCount = 0;
CloudToDeviceMethod c2dMethod = new CloudToDeviceMethod("RestartModule");
string payloadSchema = "{{ \"SchemaVersion\": \"1.0\", \"Id\": \"{0}\" }}";
string payload = string.Format(payloadSchema, "edgeHub");
Logger.LogInformation("RestartModule Method Payload: {0}", payload);
c2dMethod.SetPayloadJson(payload);
while (restartCount < maxRetry)
{
try
{
restartCount++;
// TODO: Introduce the offline scenario to use docker command.
CloudToDeviceMethodResult response = await iotHubServiceClient.InvokeDeviceMethodAsync(Settings.Current.DeviceId, "$edgeAgent", c2dMethod);
if ((HttpStatusCode)response.Status != HttpStatusCode.OK)
{
Logger.LogError($"Calling EdgeHub restart failed with status code {response.Status} : {response.GetPayloadAsJson()}.");
}
else
{
Logger.LogInformation($"Calling EdgeHub restart succeeded with status code {response.Status}.");
}
return new Tuple<DateTime, HttpStatusCode>(DateTime.UtcNow, (HttpStatusCode)response.Status);
}
catch (Exception e)
{
Logger.LogError($"Exception caught for payload {payload}: {e}");
if (restartCount == maxRetry - 1)
{
throw;
}
}
}
return new Tuple<DateTime, HttpStatusCode>(DateTime.UtcNow, HttpStatusCode.InternalServerError);
}
}
}

Просмотреть файл

@ -0,0 +1,150 @@
// Copyright (c) Microsoft. All rights reserved.
namespace EdgeHubRestartTester
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Edge.Util;
using Microsoft.Extensions.Configuration;
class Settings
{
internal static Settings Current = Create();
Settings(
TimeSpan sdkOperationTimeout,
string serviceClientConnectionString,
string deviceId,
string reportingEndpointUrl,
TimeSpan restartPeriod,
TimeSpan testStartDelay,
TimeSpan testDuration,
bool directMethodEnabled,
string directMethodName,
string directMethodTargetModuleId,
bool messageEnabled,
string messageOutputEndpoint,
TransportType transportType,
string moduleId,
string trackingId)
{
Preconditions.CheckRange(sdkOperationTimeout.Ticks, 0);
Preconditions.CheckRange(restartPeriod.Ticks, 0);
Preconditions.CheckRange(testStartDelay.Ticks, 0);
Preconditions.CheckRange(testDuration.Ticks, 0);
this.DeviceId = Preconditions.CheckNonWhiteSpace(deviceId, nameof(deviceId));
this.DirectMethodEnabled = directMethodEnabled;
this.DirectMethodName = this.DirectMethodEnabled ? Preconditions.CheckNonWhiteSpace(directMethodName, nameof(directMethodName)) : string.Empty;
this.DirectMethodTargetModuleId = this.DirectMethodEnabled ? Preconditions.CheckNonWhiteSpace(directMethodTargetModuleId, nameof(directMethodTargetModuleId)) : string.Empty;
this.MessageEnabled = messageEnabled;
this.MessageOutputEndpoint = this.MessageEnabled ? Preconditions.CheckNonWhiteSpace(messageOutputEndpoint, nameof(messageOutputEndpoint)) : string.Empty;
this.TransportType = transportType;
this.ModuleId = Preconditions.CheckNonWhiteSpace(moduleId, nameof(moduleId));
this.ReportingEndpointUrl = new Uri(Preconditions.CheckNonWhiteSpace(reportingEndpointUrl, nameof(reportingEndpointUrl)));
this.RestartPeriod = restartPeriod;
this.SdkOperationTimeout = sdkOperationTimeout;
this.IoTHubConnectionString = Preconditions.CheckNonWhiteSpace(serviceClientConnectionString, nameof(serviceClientConnectionString));
this.TestDuration = testDuration;
this.TestStartDelay = testStartDelay;
this.TrackingId = Preconditions.CheckNonWhiteSpace(trackingId, nameof(trackingId));
if (!(this.DirectMethodEnabled || this.MessageEnabled))
{
throw new NotSupportedException("EdgeHubRestartTester requires at least one of the sending methods {DirectMethodEnabled, MessageEnabled} to be enabled to perform the EdgeHub restarting test.");
}
if (restartPeriod < sdkOperationTimeout)
{
throw new InvalidDataException("sdkOperationTimeout period must be less than restartInterval period.");
}
if (this.RestartPeriod.Ticks < TimeSpan.FromMinutes(1).Ticks)
{
throw new InvalidDataException("RestartPeriod period must be at least one minute");
}
}
static Settings Create()
{
IConfiguration configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("config/settings.json", optional: true)
.AddEnvironmentVariables()
.Build();
return new Settings(
configuration.GetValue("sdkOperationTimeout", TimeSpan.FromMilliseconds(20)),
configuration.GetValue<string>("IOT_HUB_CONNECTION_STRING", string.Empty),
configuration.GetValue<string>("IOTEDGE_DEVICEID", string.Empty),
configuration.GetValue<string>("reportingEndpointUrl"),
configuration.GetValue("restartPeriod", TimeSpan.FromMinutes(5)),
configuration.GetValue("testStartDelay", TimeSpan.FromMinutes(2)),
configuration.GetValue("testDuration", TimeSpan.Zero),
configuration.GetValue<bool>("directMethodEnabled", false),
configuration.GetValue<string>("directMethodName", "HelloWorldMethod"),
configuration.GetValue<string>("directMethodTargetModuleId", "DirectMethodReceiver"),
configuration.GetValue<bool>("messageEnabled", false),
configuration.GetValue("messageOutputEndpoint", "output1"),
configuration.GetValue("transportType", TransportType.Amqp_Tcp_Only),
configuration.GetValue<string>("IOTEDGE_MODULEID"),
configuration.GetValue("trackingId", string.Empty));
}
public string IoTHubConnectionString { get; }
public string DeviceId { get; }
public bool DirectMethodEnabled { get; }
public string DirectMethodName { get; }
public string DirectMethodTargetModuleId { get; }
public bool MessageEnabled { get; }
public string MessageOutputEndpoint { get; }
public TransportType TransportType { get; }
public string ModuleId { get; }
public Uri ReportingEndpointUrl { get; }
public TimeSpan RestartPeriod { get; }
public TimeSpan SdkOperationTimeout { get; }
public TimeSpan TestStartDelay { get; }
public TimeSpan TestDuration { get; }
public string TrackingId { get; }
public override string ToString()
{
// serializing in this pattern so that secrets don't accidentally get added anywhere in the future
var fields = new Dictionary<string, string>
{
{ nameof(this.DeviceId), this.DeviceId },
{ nameof(this.DirectMethodEnabled), this.DirectMethodEnabled.ToString() },
{ nameof(this.DirectMethodName), this.DirectMethodName },
{ nameof(this.DirectMethodTargetModuleId), this.DirectMethodTargetModuleId },
{ nameof(this.MessageEnabled), this.MessageEnabled.ToString() },
{ nameof(this.MessageOutputEndpoint), this.MessageOutputEndpoint },
{ nameof(this.TransportType), this.TransportType.ToString() },
{ nameof(this.ModuleId), this.ModuleId },
{ nameof(this.ReportingEndpointUrl), this.ReportingEndpointUrl.ToString() },
{ nameof(this.RestartPeriod), this.RestartPeriod.ToString() },
{ nameof(this.SdkOperationTimeout), this.SdkOperationTimeout.ToString() },
{ nameof(this.TestStartDelay), this.TestStartDelay.ToString() },
{ nameof(this.TestDuration), this.TestDuration.ToString() },
{ nameof(this.TrackingId), this.TrackingId }
};
return $"Settings:{Environment.NewLine}{string.Join(Environment.NewLine, fields.Select(f => $"{f.Key}={f.Value}"))}";
}
}
}

Просмотреть файл

@ -2,6 +2,7 @@
namespace Microsoft.Azure.Devices.Edge.ModuleUtil
{
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Devices.Client;
using Microsoft.Azure.Devices.Client.Transport.Mqtt;
@ -62,6 +63,25 @@ namespace Microsoft.Azure.Devices.Edge.ModuleUtil
await apiClient.ReportResultAsync(testResult.ToTestOperationResultDto());
}
// TODO: Remove this function once the TRC support the two new endpoint properly.
public static async Task ReportTestResultUntilSuccessAsync(TestResultReportingClient apiClient, ILogger logger, TestResultBase testResult, CancellationToken cancellationToken)
{
bool isSuccessful = false;
while (!isSuccessful && !cancellationToken.IsCancellationRequested)
{
try
{
logger.LogInformation($"Sending test result: Source={testResult.Source}, Type={testResult.ResultType}, CreatedAt={testResult.CreatedAt}, Result={testResult.GetFormattedResult()}");
await apiClient.ReportResultAsync(testResult.ToTestOperationResultDto(), cancellationToken);
isSuccessful = true;
}
catch (Exception ex)
{
logger.LogDebug(ex, "Exception caught in ReportTestResultAsync()");
}
}
}
static async Task<ModuleClient> InitializeModuleClientAsync(TransportType transportType, ILogger logger)
{
ITransportSettings[] GetTransportSettings()

Просмотреть файл

@ -10,6 +10,8 @@ namespace Microsoft.Azure.Devices.Edge.ModuleUtil
Twin,
Network,
Deployment,
EdgeHubRestartMessage,
EdgeHubRestartDirectMethod,
Error
}
}

Просмотреть файл

@ -14,8 +14,9 @@ namespace Microsoft.Azure.Devices.Edge.ModuleUtil.TestResults
string trackingId,
Guid batchId,
string sequenceNumber,
HttpStatusCode result)
: base(source, TestOperationResultType.DirectMethod, createdAt)
HttpStatusCode result,
TestOperationResultType testOperationResultType = TestOperationResultType.DirectMethod)
: base(source, testOperationResultType, createdAt)
{
this.TrackingId = trackingId ?? string.Empty;
this.BatchId = Preconditions.CheckNotNull(batchId, nameof(batchId)).ToString();

Просмотреть файл

@ -0,0 +1,48 @@
// Copyright (c) Microsoft. All rights reserved.
namespace Microsoft.Azure.Devices.Edge.ModuleUtil.TestResults
{
using System;
using System.Net;
using Newtonsoft.Json;
public class EdgeHubRestartDirectMethodResult : DirectMethodTestResult
{
public EdgeHubRestartDirectMethodResult(
string source,
DateTime createdAt,
string trackingId,
Guid batchId,
string sequenceNumber,
DateTime edgeHubRestartedTime,
DateTime directMethodCompletedTime,
HttpStatusCode directMethodCompletedStatusCode,
uint restartSequenceNumber)
: base(
source,
createdAt,
trackingId,
batchId,
sequenceNumber,
directMethodCompletedStatusCode,
TestOperationResultType.EdgeHubRestartDirectMethod)
{
this.EdgeHubRestartedTime = edgeHubRestartedTime;
this.DirectMethodCompletedTime = directMethodCompletedTime;
this.DirectMethodCompletedStatusCode = directMethodCompletedStatusCode;
this.RestartSequenceNumber = restartSequenceNumber;
}
DateTime EdgeHubRestartedTime { get; set; }
public DateTime DirectMethodCompletedTime { get; set; }
public HttpStatusCode DirectMethodCompletedStatusCode { get; set; }
public uint RestartSequenceNumber { get; set; }
public override string GetFormattedResult()
{
return JsonConvert.SerializeObject(this);
}
}
}

Просмотреть файл

@ -0,0 +1,46 @@
// Copyright (c) Microsoft. All rights reserved.
namespace Microsoft.Azure.Devices.Edge.ModuleUtil.TestResults
{
using System;
using System.Net;
using Microsoft.Azure.Devices.Edge.Util;
using Newtonsoft.Json;
public class EdgeHubRestartMessageResult : MessageTestResult
{
public EdgeHubRestartMessageResult(
string source,
DateTime createdAt,
string trackingId,
string batchId,
string sequenceNumber,
DateTime edgeHubRestartedTime,
DateTime messageCompletedTime,
HttpStatusCode messageCompletedStatusCode)
: base(source, createdAt, TestOperationResultType.EdgeHubRestartMessage)
{
this.TrackingId = trackingId;
this.BatchId = batchId;
this.SequenceNumber = sequenceNumber;
this.EdgeHubRestartedTime = edgeHubRestartedTime;
this.MessageCompletedTime = messageCompletedTime;
this.MessageCompletedStatusCode = messageCompletedStatusCode;
}
DateTime EdgeHubRestartedTime { get; set; }
public DateTime MessageCompletedTime { get; set; }
public HttpStatusCode MessageCompletedStatusCode { get; set; }
public string GetMessageTestResult()
{
return base.GetFormattedResult();
}
public override string GetFormattedResult()
{
return JsonConvert.SerializeObject(this);
}
}
}

Просмотреть файл

@ -5,8 +5,8 @@ namespace Microsoft.Azure.Devices.Edge.ModuleUtil.TestResults
public class MessageTestResult : TestResultBase
{
public MessageTestResult(string source, DateTime createdAt)
: base(source, TestOperationResultType.Messages, createdAt)
public MessageTestResult(string source, DateTime createdAt, TestOperationResultType testOperationResultType = TestOperationResultType.Messages)
: base(source, testOperationResultType, createdAt)
{
}

Просмотреть файл

@ -28,7 +28,7 @@ namespace TestResultCoordinator.Reports.DirectMethod
string resultType,
NetworkStatusTimeline networkStatusTimeline)
{
if ((receiverSource.HasValue && !receiverTestResults.HasValue) || (!receiverSource.HasValue && receiverTestResults.HasValue))
if (receiverSource.HasValue ^ receiverTestResults.HasValue)
{
throw new ArgumentException("Provide both receiverSource and receiverTestResults or neither.");
}

Просмотреть файл

@ -4,6 +4,8 @@ namespace TestResultCoordinator.Reports
public enum TestReportType
{
CountingReport,
EdgeHubRestartDirectMethodReport,
EdgeHubRestartMessageReport,
TwinCountingReport,
DeploymentTestReport,
DirectMethodReport,

Просмотреть файл

@ -153,7 +153,13 @@ namespace TestResultCoordinator
internal async Task<HashSet<string>> GetResultSourcesAsync(ILogger logger)
{
HashSet<string> sources = (await this.GetReportMetadataListAsync(logger)).SelectMany(r => r.ResultSources).ToHashSet();
string[] additionalResultSources = new string[] { };
string[] additionalResultSources = new string[]
{
"edgeHubRestartTester1.EdgeHubRestartDirectMethod",
"directMethodReceiver1.receive",
"edgeHubRestartTester1.EdgeHubRestartMessage",
"relayer1.receive"
};
foreach (string rs in additionalResultSources)
{