Шаблон тестирования интеграции Azure IoT Edge Integration Test — Часть.3


Резюме

Эта статья является частью цикла статей о azure-iot-edge-integration-test-template. После части.2, в этой статье я собираюсь сосредоточиться на ManifestGenerator.

  • Шаблон Azure IoT Edge Integration Test — часть.1
  • Тестовый шаблон Azure IoT Edge Integration Test — часть.2

TOC

  • Манифест развертывания
  • Что делает ManifestGenerator
  • Конфигурации
    • Извлечение учетных данных
    • Переменные окружения
    • appsettings.json
  • NuGet-пакет IoTEdgeObjectModel
  • SAS-токен

Манифест развертывания

В этом примере шаблона приложение .NET ManifestGenerator, запущенное на агенте Azure Pipelines, генерирует манифест развертывания Azure IoT Edge и развертывает его на Azure IoT Hub. Модули IoT Edge, подключенные к IoT Hub, будут извлекать образы контейнеров, указанные в манифесте.

Что делает ManifestGenerator

Основная цель приложения ManifestGenerator — генерировать manifest.json. Он включает информацию обо всех модулях IoT Edge, включая системные модули по умолчанию и пользовательские.

  • Модуль по умолчанию: edgeAgent и edgeHub — модули по умолчанию, которые управляют модулями и их коммуникациями.
  • Пользовательский модуль: Вы можете указать, какие модули вы хотите развернуть в среде IoT Edge.
  • Маршрут связи IoT Edge: Здесь необходимо указать имя маршрута. Очень важно, чтобы эти имена были синхронизированы с маршрутами, определенными в каждом приложении модуля. Например, маршрут, указанный в манифесте, имеет вид FROM /messages/modules/IothubConnector/outputs/reportRequest INTO BrokeredEndpoint("/modules/WeatherObserver/inputs/reportRequest"). Модуль IothubConnector должен указать имя маршрута reportRequest в своем методе отправки сообщения, а WeatherObserver должен указать имя маршрута reportRequest в своем методе обратного вызова.
  • URL изображения, переменные окружения, каталог привязки монтирования: Вы должны указать URL изображения, переменные окружения, директорию для привязки монтирования и другие конфигурации, как показано ниже.

Вы можете посмотреть пример здесь — manifest.example.json

"FileGenerator": {
  "version": "1.0",
  "type": "docker",
  "status": "running",
  "restartPolicy": "always",
  "settings": {
    "image": "crsample1.azurecr.io/file-generator:20220818.2",
    "createOptions": "{"HostConfig":{"Binds":["/edge/upload/reports:/genroot"]}}"
  },
  "env": {
    "OUTPUT_DIRECTORY_PATH": {
      "value": "/genroot"
    },
    "ROS_TOPIC_NAME": {
      "value": "ros2_topic_download"
    }
  }
},
Войти в полноэкранный режим Выйти из полноэкранного режима

Конфигурации

В этом разделе я опишу, как задать конфигурации для manifest.json.

Извлечение учетных данных

manifest.json требует некоторых учетных данных, включая строку подключения IoT Hub, ключ реестра контейнеров, ключ учетной записи хранилища, ключ локального хранилища блобов. Первые три можно извлечь с помощью команды Azure CLI, поскольку агент Azure Pipelines имеет право доступа к Azure Service Connection. Для ключа локального блоб-хранилища это может быть любая строка base64 длиной 16 байт. Агент Azure Pipelines генерирует его случайным образом и устанавливает в переменные окружения.

az extension add --name azure-iot
iothubcs=$(az iot hub connection-string show --hub-name $(IOTHUB_NAME) -o tsv)
echo "##vso[task.setvariable variable=iotHubCs]$iothubcs"
acrkey=$(az acr credential show --name $(ACR_NAME) --query passwords[0].value -o tsv)
echo "##vso[task.setvariable variable=AcrKey]$acrkey"
storagekey=$(az storage account keys list --resource-group $(RESOURCE_GROUP_NAME) --account-name $(STORAGE_ACCOUNT_NAME) --query [0].value -o tsv)
echo "##vso[task.setvariable variable=storageAccountKey]$storagekey"
localstoragekey=$(openssl rand -base64 16)
echo "##vso[task.setvariable variable=LocalStorageKey]$localstoragekey"
Вход в полноэкранный режим Выход из полноэкранного режима

Переменные окружения

С помощью задачи Azure Pipelines .NET Core CLI вы можете передать необходимые переменные в качестве переменных окружения .NET приложению ManifestGenerator при его запуске. Эти переменные включают учетные данные агента Azure Pipelines, сгенерированные в предыдущем шаге.

- task: DotNetCoreCLI@2
  displayName: Generate/deploy IoT Edge manifest
  inputs:
    command: run
    projects: $(Build.SourcesDirectory)/src/apps/ManifestGenerator/ManifestGenerator/ManifestGenerator.csproj
    arguments: --configuration Release
  env:
    STORAGE_ACCOUNT_NAME: $(STORAGE_ACCOUNT_NAME)
    STORAGE_ACCOUNT_KEY: $(storageAccountKey)
    ACR_NAME: $(ACR_NAME)
    ACR_PASS: $(AcrKey)
    IOTHUB_CONNECTOR_IMAGE: $(ACR_NAME).azurecr.io/${{ parameters.EdgeImages.module1.repository }}:${{ parameters.EdgeImages.module1.tag }}
    WEATHER_OBSERVER_IMAGE: $(ACR_NAME).azurecr.io/${{ parameters.EdgeImages.module2.repository }}:${{ parameters.EdgeImages.module2.tag }}
    FILE_GENERATOR_IMAGE: $(ACR_NAME).azurecr.io/${{ parameters.EdgeImages.module3.repository }}:${{ parameters.EdgeImages.module3.tag }}
    FILE_UPLOADER_IMAGE: $(ACR_NAME).azurecr.io/${{ parameters.EdgeImages.module4.repository }}:${{ parameters.EdgeImages.module4.tag }}
    FILE_UPDATER_IMAGE: $(ACR_NAME).azurecr.io/${{ parameters.EdgeImages.module5.repository }}:${{ parameters.EdgeImages.module5.tag }}
    IOTHUB_DEVICE_ID: $(IOTHUB_DEVICE_ID)
    IOTHUB_CONNECTION_STRING: $(iotHubCs)
    LOCAL_STORAGE_KEY: $(LocalStorageKey)
    ORGANIZATION_NAME: $(TEST_ORGANIZATION_NAME)
Вход в полноэкранный режим Выход из полноэкранного режима

appsettings.json

Приложение ManifestGenerator сохраняет файл appsettings.json, в котором указаны значения дизайна. Его не нужно изменять, если вы не меняете дизайн, например, пути монтирования привязки или срок действия маркера SAS.

{
  "RouteTelemetry": "telemetry",
  "RouteReportRequest": "reportRequest",
  "RouteReportResponse": "reportResponse",
  "RouteUpdateRequest": "updateRequest",
  "RouteUpdateResponse": "updateResponse",
  "Ros2Topic": "ros2_topic_download",
  "FileGeneratorContainerBind": "/edge/upload/reports:/genroot",
  "FileUploaderContainerBind": "/edge/upload:/uploadroot",
  "FileUpdaterContainerBind": "/edge/download:/downloadroot",
  "LocalBlobStorageBind": "/edge/localblob:/blobroot",
  "CloudBlobContainerName": "weather",
  "LocalBlobContainerName": "weather",
  "LocalBlobAccountName": "stlocal",
  "LocalBlobEndpoint": "http://LocalBlobStorage:11002",
  "FileGeneratorWorkdir": "/genroot",
  "FileUploaderWorkdir": "/uploadroot",
  "FileUpdaterWorkdir": "/downloadroot",
  "SasExpirationMonths": 6
}
Вход в полноэкранный режим Выход из полноэкранного режима

NuGet-пакет IoTEdgeObjectModel

В этом примере шаблона используется NuGet-пакет IoTEdgeObjectModel. Этот пакет помогает сократить количество строк кода, которые вы пишете для manifest.json. Вам не нужно указывать конфигурацию системного модуля или другие конфигурации по умолчанию. По моему опыту в последнем проекте я написал manifest.json в одиночку с нуля, используя экземпляры словарей C#. Используя пакет IoTEdgeObjectModel, вы можете сократить примерно 50% кода.

Три основных класса этого пакета: EdgeAgentDesiredProperties, EdgeHubDesiredProperties, ModuleSpecificationDesiredProperties.

Вы определяете свойства edgeAgent как показано ниже с помощью класса EdgeModuleSpecification.

EdgeAgentDesiredProperties edgeAgentDesiredProperties = new ()
{
    SystemModuleVersion = "1.3",
    RegistryCredentials = new List<RegistryCredential>()
    {
        new RegistryCredential(acrName, $"{acrName}.azurecr.io", acrName, acrPass),
    },
    EdgeModuleSpecifications = new List<EdgeModuleSpecification>()
    {
        new EdgeModuleSpecification(name:"IothubConnector", image:iothubConnectorImage, environmentVariables:iothubConnectorEnv),
        new EdgeModuleSpecification(name:"WeatherObserver", image:weatherObserverImage),
        new EdgeModuleSpecification(name:"FileGenerator", image:fileGeneratorImage, createOptions:fileGeneratorCreateOptions, environmentVariables:fileGeneratorEnv),
        new EdgeModuleSpecification(name:"FileUploader", image:fileUploaderImage, createOptions:fileUploaderCreateOptions, environmentVariables:fileUploaderEnv),
        new EdgeModuleSpecification(name:"FileUpdater", image:fileUpdaterImage, createOptions:fileUpdaterCreateOptions, environmentVariables:fileUpdaterEnv),
        new EdgeModuleSpecification(name:"LocalBlobStorage", image:localBlobStorageImage, createOptions:localBlobStorageCreateOptions, environmentVariables:localBlobStorageEnv),
    },
};
Вход в полноэкранный режим Выход из полноэкранного режима

EdgeHubDesiredProperties в основном определяет маршрутную связь Azure IoT Edge.

EdgeHubDesiredProperties edgeHubConfig = new ()
{
    Routes = new List<Route>()
    {
        new Route("route_telemetry", route_telemetry),
        new Route("route_c2w", route_c2w),
        new Route("route_w2c", route_w2c),
        new Route("route_w2u", route_w2u),
        new Route("route_u2w", route_u2w),
    },
};
Войти в полноэкранный режим Выход из полноэкранного режима

ModuleSpecificationDesiredProperties определяет пользовательские модули и желаемые свойства их двойников.

ModuleSpecificationDesiredProperties localBlobStorage = new ()
{
    Name = "LocalBlobStorage",
    DesiredProperties = new Dictionary<string, object>
    {
        ["deviceAutoDeleteProperties"] = new Dictionary<string, object>
        {
            ["deleteOn"] = true,
            ["deleteAfterMinutes"] = 5,
            ["retainWhileUploading"] = true,
        },
        ["deviceToCloudUploadProperties"] = new Dictionary<string, object>
        {
            ["uploadOn"] = true,
            ["uploadOrder"] = "NewestFirst",
            ["deleteAfterUpload"] = true,
            ["cloudStorageConnectionString"] = cloudStorageSasConnectionString,
            ["storageContainersForUpload"] = new Dictionary<string, object>
            {
                [localBlobContainerName] = new Dictionary<string, object>
                {
                    ["target"] = iotHubDeviceId,
                }
            },
        },
    },
};
Вход в полноэкранный режим Выход из полноэкранного режима

SAS маркер

  • Важно преобразовать сгенерированный токен SAS в строку соединения. Эта строка подключения устанавливается как переменная окружения LocalBlobStorage. Для FileUpdater вы можете использовать AzureSasCredential для преобразования SAS-токена в токен, читаемый для BlobClient.
string[] sasContents = weatherFileInfo.BlobSasUrl.Split('?');
AzureSasCredential azureSasCredential = new (sasContents[1]);
Uri blobUri = new (sasContents[0]);
BlobClient blobClient = new (blobUri, azureSasCredential, null);
await blobClient.DownloadToAsync(zipFilePath).ConfigureAwait(false);
Вход в полноэкранный режим Выход из полноэкранного режима

Однако LocalBlobClient по умолчанию требует строку подключения к блобу, а не SAS-токен. Поэтому вам необходимо преобразовать токен SAS в строку соединения с блобом в файле ManfiestGenerator Program.cs

string sasUri = directoryClient.GenerateSasUri(sasBuilder).ToString();
string[] sasContents = sasUri.Split('?');
string sasConnectionString = $"BlobEndpoint=https://{this.dataLakeServiceClient.AccountName}.blob.core.windows.net/{blobContainerName}/{sasDirectory};SharedAccessSignature={sasContents[1]}";
Вход в полноэкранный режим Выход из полноэкранного режима

Оцените статью
devanswers.ru
Добавить комментарий