From 6d33ebaba3f1398c504b18f84834ee4532a9ea2c Mon Sep 17 00:00:00 2001 From: Mariana Mihova <10135329+marianan@users.noreply.github.com> Date: Mon, 7 Jun 2021 11:33:15 -0700 Subject: [PATCH] VSCode Edge extension install a custom version of simulator (#581) * npm vulnerabilities + environment variable IOTEDGEHUBDEV_VERSION * releases is a structure, not array * Add log for invalid release * Fix lint issues --- CHANGELOG.md | 554 +++++++++++++------------- package-lock.json | 20 +- package.json | 2 +- src/common/constants.ts | 2 +- src/edge/edgeManager.ts | 4 +- src/edge/simulator.ts | 854 ++++++++++++++++++++-------------------- 6 files changed, 728 insertions(+), 708 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba70ceb6..c3b575b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,275 +1,279 @@ -# Change Log -## 1.24.0 - 2021-3-26 -### Changed -* Allow user to select Edge Runtime version between 1.0 and 1.1 - -## 1.23.0 - 2020-9-23 -### Fixed -* Guide user to setup device connection string when missing during 'Set module credentials' operation - -## 1.22.0 - 2020-5-27 -### Changed -* Allow user to select deployment template when add module -* Adopt VS Code's 'asWebviewUri' API - -## 1.21.0 - 2020-4-07 -### Added -* Add check when input repository URL -* Add retry logic when download standalone simulator -* Add single module debug support for Python -* Add arm64v8 platform support - -### Changed -* Use pip package URL instead of GitHub API to avoid rate limitation - -### Fixed -* Fixed image placeholder and docker path verification issue: [#159](https://github.com/microsoft/vscode-azure-iot-edge/issues/159) - -## 1.20.0 - 2019-12-31 -### Changed -* Update scroll bar css to compatible with dark theme for Sample Gallery -* Update subtitle in the description - -## 1.19.0 - 2019-11-15 -### Added -* Add docker status detection -* Support adding Azure Event Grid module - -### Changed -* Use standalone simulator (iotedgehubdev) - -## 1.18.0 - 2019-10-30 -### Added -* Support multiple plans for marketplace - -### Changed -* Optimize ASA retry logic - -## 1.17.0 - 2019-08-29 -### Added -* Add CodeLens to help user update ASA job information - -### Fixed -* Fix broken link in README.md - -## 1.16.0 - 2019-08-13 -### Added -* Guide user to setup connection string from UI when failed to start simulator - -## 1.15.0 - 2019-07-19 -### Changed -* ASA error message improvement -* Rename 'tempSensor' to 'SimulatedTemperatureSensor' -* Read connection string from API of Azure IoT Hub Toolkit - -## 1.14.0 - 2019-06-25 -### Added -* Support create an Azure IoT Edge Solution without any module. - -### Changed -* Only add tempSensor module when adding custom module. -* Fixed the issue command broken on VSCode 1.35.0+ [#459](https://github.com/microsoft/vscode-azure-iot-edge/issues/459). - -## 1.13.0 - 2019-05-28 -### Added -* Support relative path schema for the reference of external custom module. The schema is like ${MODULEDIR\} - -## 1.12.0 - 2019-04-26 -### Added -* Integrate the new setup option from [iotedgehubdev v0.8.0](https://github.com/Azure/iotedgehubdev/releases/tag/v0.8.0) to setup Azure IoT Hub connection string. So now module twin will be updated automatically when running deployment in simulator. - -### Changed -* Only one sample gallery page will be opened in VS Code [#414](https://github.com/Microsoft/vscode-azure-iot-edge/issues/414) - -## 1.11.1 - 2019-03-30 -### Changed -* Marketplace integration hotfix - -## 1.11.0 - 2019-03-22 -### Added -* Add IoT Edge Marketplace page. User can view and create IoT Edge modules from Azure Marketplace. -* Add json schema validation for deployment.*.template.json file. - -## 1.10.0 - 2019-02-19 -### Added -* Add sample gallery page. User can view and create Azure IoT Edge solution based on samples. -* Add issue template - -## 1.9.0 - 2019-01-30 -### Added -* Add configuration "azure-iot-edge.executor.env" which can be configured to inject environment variables into terminals created by VS Code Azure IoT Edge extension. - -### Changed -* Change the extension activation condition. It will not be activated by a debug session. - -## 1.8.0 - 2019-01-07 -### Added -* Autodetect/install/update iotedgehubdev -* Install Azure IoT Edge Node.js Module Generator automatically before new Node.js module - -### Changed -* Use Webpack to improve extension performance ⚡ -* Use git download instead of cookiecutter to add Python module - -## 1.7.0 - 2018-12-06 -### Added -* Support adding Azure Machine Learning modules. -* Support setting the template versions to be used by "New IoT Edge Solution" or "Add IoT Edge Module" commands. - * [Released CSharp module template versions](https://github.com/Azure/dotnet-template-azure-iot-edge-module/blob/master/CHANGELOG.md) - * [Released CSharp function module template versions](https://github.com/Azure/dotnet-template-azure-iot-edge-function/blob/master/CHANGELOG.md) - * [Released Java module template versions](https://mvnrepository.com/artifact/com.microsoft.azure/azure-iot-edge-archetype) - * [Released Python module template versions](https://github.com/Azure/cookiecutter-azure-iot-edge-module/releases) - * [Released C module template versions](https://github.com/Azure/azure-iot-edge-c-module/releases) - -## 1.6.0 - 2018-11-23 -### Added -* Add **deployment.debug.template.json** when creating new solution. The template refer to the debug flavour image of the modules and has debug createOptions populated automatically. -* Enable switch between different platforms for Azure IoT Edge Solution. User could switch the platform through status bar. By default, we provide "arm32v7", "amd64" and "windows-amd64" as the platform set since these are Azure IoT Edge supporting platforms today. Besides, user could customize new platform via user settings (azure-iot-edge.platforms). Now the image reference parameter in deployment template could be platform neutral. And the platform configured will be used when build the solution. For example, to reference the module "SampleModule" in deployment template, the parameter could be **"${MODULES.SampleModule}"** which does not have the platfrom suffix like ".amd64". -* Add third party module template support. User can define custom module scaffolding command in the user setting. And when add new module, the command could be triggered in the workflow. - -### Changed -* Change default type of "createOptions" in deloyment.template.json/deployment.debug.template.json to json object. -* Support build/generate/run template files which has **.template.json** suffix through command palette - -## 1.5.1 - 2018-11-02 -### Changed -* Support createOptions in deployment.template.json configuration up to 4K -* Fix some bugs - -## 1.5.0 - 2018-10-15 -### Added -* Support Java module with Windows container - -## 1.4.0 - 2018-09-20 -### Changed -* Update CSharp module debug configuration to support netcoreapp2.1 target framework -* Update Python module debug configuration to support released python debugger -* Fix some bugs - -## 1.3.0 - 2018-08-30 -### Added -* Support Java module type in add module -* Support debug Python module (amd64) - -## 1.2.0 - 2018-08-09 -### Changed -* `Azure IoT Edge: Build IoT Edge Solution` does not push images anymore - -### Added -* Integerate with [iotedgehubdev](https://pypi.org/project/iotedgehubdev/) tool -* Azure IoT Edge: Build and Push IoT Edge Solution -* Azure IoT Edge: Setup IoT Edge Simulator -* Azure IoT Edge: Build and Run IoT Edge Solution in Simulator -* Azure IoT Edge: Run IoT Edge Solution in Simulator -* Azure IoT Edge: Start IoT Edge Hub Simulator for Single Module -* Azure IoT Edge: Stop IoT Edge Simulator -* Azure IoT Edge: Set Module Credentials to User Settings -* Support ASA module type in add module -* Debugging configuration for "Launch IoT Edge Module (Node.js)" -* Debugging configuration for "Launch IoT Edge Module (.Net Core)" - -### Known Issues -* Cannot run C and Python module in IoT Edge Simulator -* IoT Edge Simulator does not work on Windows Container -* [ASA module may fail sending message](https://github.com/Microsoft/vscode-azure-iot-edge/issues/213) - -## 1.1.1 - 2018-08-01 -### Changed -* Update vscode-extension-telemetry npm to latest version (0.0.18) - -## 1.1.0 - 2018-07-30 -### Added -* Add support for C module - -## 1.0.0 - 2018-06-27 -### Added -* Add "Add IoT Edge Module" item to the context menu of "modules" folder -* Add support for Node.js module - -### Changed -* Default route is added into deployment.template.json when adding module -* Docker registry credential is now managed in deployment.template.json - -### Removed -* Azure IoT Edge: Setup Edge -* Azure IoT Edge: Start Edge -* Azure IoT Edge: Setup Edge using configuration file -* Azure IoT Edge: Generate Edge setup configuration file -* Azure IoT Edge: Stop Edge -* Azure IoT Edge: Restart Edge -* Azure IoT Edge: Uninstall IoT Edge -* Azure IoT Edge: Log in to container registry -* Azure IoT Edge: Convert to IoT Edge Module - -## 0.4.0 - 2018-05-24 -### Added -* Import existing modules from Azure Container Registry when adding new modules to the solution -* Respect .env file in the root of the solution folder -* Automatically start local registry when it is used by a module - -### Changed -* Update files generated by the "Azure IoT Edge: Convert to IoT Edge Module" command to align with Azure IoT Edge .NET templates' recent releases - -### Known Issues -* [#161](https://github.com/Microsoft/vscode-azure-iot-edge/issues/161) You may encounter "Entry not found in cache" error when importing ACR modules randomly, especially after idling VS Code for several hours. To work around this issue, please open command palette and run "Reload Window" or restart VS Code. We are investigating the issue and will post an update once the issue is resolved. - -## 0.3.0 - 2018-05-02 -### Added -* Import existing module from container registry when adding new module to solution -* Support adding extra Docker build options in the `buildOptions` array of module.json -* New IntelliSense features (watch the screencasts [here](https://github.com/Microsoft/vscode-azure-iot-edge/issues/115)) - * Dockerfile path validation in module.json - * Image placeholder validation in deployment.template.json - * Dockerfile content hover preview in deployment.template.json - * Go-to-Dockerfile in deployment.template.json -* "Azure" branding to command palette category - -### Changed -* Always check out the `master` branch of [Cookiecutter template](https://github.com/Azure/cookiecutter-azure-iot-edge-module/) when adding Python module - -## 0.2.0 - 2018-03-27 -### Added -* Introduce IoT Edge Solution which includes multiple modules and a deployment manifest template (deployment.template.json) -* Command "New IoT Edge Solution". -* Command "Build IoT Edge Module Image". -* Command "Build and Push IoT Edge Module Image". -* Command "Build IoT Edge Solution". -* Command "Generate IoT Edge Deployment Manifest". -* Command "Add IoT Edge Module". -* Command "Convert to IoT Edge Module". The command helps the migration from legacy modules. Please refer to the [migration steps](MIGRATION_STEPS.md) for detail. -* Add IntelliSense support in deployment.template.json file of IoT Edge Solution. -* Support of the Python IoT Edge Module. - -### Removed -* Command "Build IoT Edge module" has been removed. Use command "Build IoT Edge Module Image" to build the module image in this new version. -* Command "Build IoT Edge module Docker image" has been removed. Use command "Build IoT Edge Module Image" to build the module image in this new version. -* Command "Push IoT Edge module Docker image" has been removed. Use command "Build and Push IoT Edge Module Image" to build and push the module image in this new version. - -### Changed -* IoT Edge Module folder structure has been changed. - * module.json file is added to the project root to manage the version and platform. - * Dockerfiles for different platforms are moved to the project root. - * For C# IoT Edge module, the build binary steps are now put into Dockerfile. So module could be built without building C# project first. - -## 0.1.3 - 2017-12-25 -### Added -* Add support for F# - -## 0.1.2 - 2017-12-06 -### Added -* Add Command Palette integration for build module command -* Cache last used image name - -## 0.1.1 - 2017-11-17 -### Changed -* Show 'Build IoT Edge module' command only on csproj file - -## 0.1.0 - 2017-11-13 -### Added -* Support for developing and debugging C# module and C# Function. -* Context menu for `*.csproj` and `Dockerfile` for C# module and Function development. -* Support for creating Edge deployment with Azure IoT Hub for single Edge device. -* Integration of the basic functionalities of `azure-iot-edge-runtime-ctl`. -* Context menu in Device List to manage IoT Edge runtime and IoT Edge devices. -* Telemetry to understand which commands developers find useful. This will help us refine which commands we add in the future. -> Please note, you can turn off telemetry reporting for VS Code and all extensions through the ["telemetry.enableTelemetry": false setting](https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting). +# Change Log +## 1.24.1 - 2021-6-4 +### Changed +* Allow user to specify the version of iotedgehubdev through IOTEDGEHUBDEV_VERSION environment variable + +## 1.24.0 - 2021-3-26 +### Changed +* Allow user to select Edge Runtime version between 1.0 and 1.1 + +## 1.23.0 - 2020-9-23 +### Fixed +* Guide user to setup device connection string when missing during 'Set module credentials' operation + +## 1.22.0 - 2020-5-27 +### Changed +* Allow user to select deployment template when add module +* Adopt VS Code's 'asWebviewUri' API + +## 1.21.0 - 2020-4-07 +### Added +* Add check when input repository URL +* Add retry logic when download standalone simulator +* Add single module debug support for Python +* Add arm64v8 platform support + +### Changed +* Use pip package URL instead of GitHub API to avoid rate limitation + +### Fixed +* Fixed image placeholder and docker path verification issue: [#159](https://github.com/microsoft/vscode-azure-iot-edge/issues/159) + +## 1.20.0 - 2019-12-31 +### Changed +* Update scroll bar css to compatible with dark theme for Sample Gallery +* Update subtitle in the description + +## 1.19.0 - 2019-11-15 +### Added +* Add docker status detection +* Support adding Azure Event Grid module + +### Changed +* Use standalone simulator (iotedgehubdev) + +## 1.18.0 - 2019-10-30 +### Added +* Support multiple plans for marketplace + +### Changed +* Optimize ASA retry logic + +## 1.17.0 - 2019-08-29 +### Added +* Add CodeLens to help user update ASA job information + +### Fixed +* Fix broken link in README.md + +## 1.16.0 - 2019-08-13 +### Added +* Guide user to setup connection string from UI when failed to start simulator + +## 1.15.0 - 2019-07-19 +### Changed +* ASA error message improvement +* Rename 'tempSensor' to 'SimulatedTemperatureSensor' +* Read connection string from API of Azure IoT Hub Toolkit + +## 1.14.0 - 2019-06-25 +### Added +* Support create an Azure IoT Edge Solution without any module. + +### Changed +* Only add tempSensor module when adding custom module. +* Fixed the issue command broken on VSCode 1.35.0+ [#459](https://github.com/microsoft/vscode-azure-iot-edge/issues/459). + +## 1.13.0 - 2019-05-28 +### Added +* Support relative path schema for the reference of external custom module. The schema is like ${MODULEDIR\} + +## 1.12.0 - 2019-04-26 +### Added +* Integrate the new setup option from [iotedgehubdev v0.8.0](https://github.com/Azure/iotedgehubdev/releases/tag/v0.8.0) to setup Azure IoT Hub connection string. So now module twin will be updated automatically when running deployment in simulator. + +### Changed +* Only one sample gallery page will be opened in VS Code [#414](https://github.com/Microsoft/vscode-azure-iot-edge/issues/414) + +## 1.11.1 - 2019-03-30 +### Changed +* Marketplace integration hotfix + +## 1.11.0 - 2019-03-22 +### Added +* Add IoT Edge Marketplace page. User can view and create IoT Edge modules from Azure Marketplace. +* Add json schema validation for deployment.*.template.json file. + +## 1.10.0 - 2019-02-19 +### Added +* Add sample gallery page. User can view and create Azure IoT Edge solution based on samples. +* Add issue template + +## 1.9.0 - 2019-01-30 +### Added +* Add configuration "azure-iot-edge.executor.env" which can be configured to inject environment variables into terminals created by VS Code Azure IoT Edge extension. + +### Changed +* Change the extension activation condition. It will not be activated by a debug session. + +## 1.8.0 - 2019-01-07 +### Added +* Autodetect/install/update iotedgehubdev +* Install Azure IoT Edge Node.js Module Generator automatically before new Node.js module + +### Changed +* Use Webpack to improve extension performance ⚡ +* Use git download instead of cookiecutter to add Python module + +## 1.7.0 - 2018-12-06 +### Added +* Support adding Azure Machine Learning modules. +* Support setting the template versions to be used by "New IoT Edge Solution" or "Add IoT Edge Module" commands. + * [Released CSharp module template versions](https://github.com/Azure/dotnet-template-azure-iot-edge-module/blob/master/CHANGELOG.md) + * [Released CSharp function module template versions](https://github.com/Azure/dotnet-template-azure-iot-edge-function/blob/master/CHANGELOG.md) + * [Released Java module template versions](https://mvnrepository.com/artifact/com.microsoft.azure/azure-iot-edge-archetype) + * [Released Python module template versions](https://github.com/Azure/cookiecutter-azure-iot-edge-module/releases) + * [Released C module template versions](https://github.com/Azure/azure-iot-edge-c-module/releases) + +## 1.6.0 - 2018-11-23 +### Added +* Add **deployment.debug.template.json** when creating new solution. The template refer to the debug flavour image of the modules and has debug createOptions populated automatically. +* Enable switch between different platforms for Azure IoT Edge Solution. User could switch the platform through status bar. By default, we provide "arm32v7", "amd64" and "windows-amd64" as the platform set since these are Azure IoT Edge supporting platforms today. Besides, user could customize new platform via user settings (azure-iot-edge.platforms). Now the image reference parameter in deployment template could be platform neutral. And the platform configured will be used when build the solution. For example, to reference the module "SampleModule" in deployment template, the parameter could be **"${MODULES.SampleModule}"** which does not have the platfrom suffix like ".amd64". +* Add third party module template support. User can define custom module scaffolding command in the user setting. And when add new module, the command could be triggered in the workflow. + +### Changed +* Change default type of "createOptions" in deloyment.template.json/deployment.debug.template.json to json object. +* Support build/generate/run template files which has **.template.json** suffix through command palette + +## 1.5.1 - 2018-11-02 +### Changed +* Support createOptions in deployment.template.json configuration up to 4K +* Fix some bugs + +## 1.5.0 - 2018-10-15 +### Added +* Support Java module with Windows container + +## 1.4.0 - 2018-09-20 +### Changed +* Update CSharp module debug configuration to support netcoreapp2.1 target framework +* Update Python module debug configuration to support released python debugger +* Fix some bugs + +## 1.3.0 - 2018-08-30 +### Added +* Support Java module type in add module +* Support debug Python module (amd64) + +## 1.2.0 - 2018-08-09 +### Changed +* `Azure IoT Edge: Build IoT Edge Solution` does not push images anymore + +### Added +* Integerate with [iotedgehubdev](https://pypi.org/project/iotedgehubdev/) tool +* Azure IoT Edge: Build and Push IoT Edge Solution +* Azure IoT Edge: Setup IoT Edge Simulator +* Azure IoT Edge: Build and Run IoT Edge Solution in Simulator +* Azure IoT Edge: Run IoT Edge Solution in Simulator +* Azure IoT Edge: Start IoT Edge Hub Simulator for Single Module +* Azure IoT Edge: Stop IoT Edge Simulator +* Azure IoT Edge: Set Module Credentials to User Settings +* Support ASA module type in add module +* Debugging configuration for "Launch IoT Edge Module (Node.js)" +* Debugging configuration for "Launch IoT Edge Module (.Net Core)" + +### Known Issues +* Cannot run C and Python module in IoT Edge Simulator +* IoT Edge Simulator does not work on Windows Container +* [ASA module may fail sending message](https://github.com/Microsoft/vscode-azure-iot-edge/issues/213) + +## 1.1.1 - 2018-08-01 +### Changed +* Update vscode-extension-telemetry npm to latest version (0.0.18) + +## 1.1.0 - 2018-07-30 +### Added +* Add support for C module + +## 1.0.0 - 2018-06-27 +### Added +* Add "Add IoT Edge Module" item to the context menu of "modules" folder +* Add support for Node.js module + +### Changed +* Default route is added into deployment.template.json when adding module +* Docker registry credential is now managed in deployment.template.json + +### Removed +* Azure IoT Edge: Setup Edge +* Azure IoT Edge: Start Edge +* Azure IoT Edge: Setup Edge using configuration file +* Azure IoT Edge: Generate Edge setup configuration file +* Azure IoT Edge: Stop Edge +* Azure IoT Edge: Restart Edge +* Azure IoT Edge: Uninstall IoT Edge +* Azure IoT Edge: Log in to container registry +* Azure IoT Edge: Convert to IoT Edge Module + +## 0.4.0 - 2018-05-24 +### Added +* Import existing modules from Azure Container Registry when adding new modules to the solution +* Respect .env file in the root of the solution folder +* Automatically start local registry when it is used by a module + +### Changed +* Update files generated by the "Azure IoT Edge: Convert to IoT Edge Module" command to align with Azure IoT Edge .NET templates' recent releases + +### Known Issues +* [#161](https://github.com/Microsoft/vscode-azure-iot-edge/issues/161) You may encounter "Entry not found in cache" error when importing ACR modules randomly, especially after idling VS Code for several hours. To work around this issue, please open command palette and run "Reload Window" or restart VS Code. We are investigating the issue and will post an update once the issue is resolved. + +## 0.3.0 - 2018-05-02 +### Added +* Import existing module from container registry when adding new module to solution +* Support adding extra Docker build options in the `buildOptions` array of module.json +* New IntelliSense features (watch the screencasts [here](https://github.com/Microsoft/vscode-azure-iot-edge/issues/115)) + * Dockerfile path validation in module.json + * Image placeholder validation in deployment.template.json + * Dockerfile content hover preview in deployment.template.json + * Go-to-Dockerfile in deployment.template.json +* "Azure" branding to command palette category + +### Changed +* Always check out the `master` branch of [Cookiecutter template](https://github.com/Azure/cookiecutter-azure-iot-edge-module/) when adding Python module + +## 0.2.0 - 2018-03-27 +### Added +* Introduce IoT Edge Solution which includes multiple modules and a deployment manifest template (deployment.template.json) +* Command "New IoT Edge Solution". +* Command "Build IoT Edge Module Image". +* Command "Build and Push IoT Edge Module Image". +* Command "Build IoT Edge Solution". +* Command "Generate IoT Edge Deployment Manifest". +* Command "Add IoT Edge Module". +* Command "Convert to IoT Edge Module". The command helps the migration from legacy modules. Please refer to the [migration steps](MIGRATION_STEPS.md) for detail. +* Add IntelliSense support in deployment.template.json file of IoT Edge Solution. +* Support of the Python IoT Edge Module. + +### Removed +* Command "Build IoT Edge module" has been removed. Use command "Build IoT Edge Module Image" to build the module image in this new version. +* Command "Build IoT Edge module Docker image" has been removed. Use command "Build IoT Edge Module Image" to build the module image in this new version. +* Command "Push IoT Edge module Docker image" has been removed. Use command "Build and Push IoT Edge Module Image" to build and push the module image in this new version. + +### Changed +* IoT Edge Module folder structure has been changed. + * module.json file is added to the project root to manage the version and platform. + * Dockerfiles for different platforms are moved to the project root. + * For C# IoT Edge module, the build binary steps are now put into Dockerfile. So module could be built without building C# project first. + +## 0.1.3 - 2017-12-25 +### Added +* Add support for F# + +## 0.1.2 - 2017-12-06 +### Added +* Add Command Palette integration for build module command +* Cache last used image name + +## 0.1.1 - 2017-11-17 +### Changed +* Show 'Build IoT Edge module' command only on csproj file + +## 0.1.0 - 2017-11-13 +### Added +* Support for developing and debugging C# module and C# Function. +* Context menu for `*.csproj` and `Dockerfile` for C# module and Function development. +* Support for creating Edge deployment with Azure IoT Hub for single Edge device. +* Integration of the basic functionalities of `azure-iot-edge-runtime-ctl`. +* Context menu in Device List to manage IoT Edge runtime and IoT Edge devices. +* Telemetry to understand which commands developers find useful. This will help us refine which commands we add in the future. +> Please note, you can turn off telemetry reporting for VS Code and all extensions through the ["telemetry.enableTelemetry": false setting](https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting). diff --git a/package-lock.json b/package-lock.json index 413062f9..ce9f3742 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "azure-iot-edge", - "version": "1.24.0", + "version": "1.24.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3450,9 +3450,9 @@ } }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "log-symbols": { "version": "4.0.0", @@ -5243,9 +5243,9 @@ } }, "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "dev": true, "requires": { "figgy-pudding": "^3.5.1" @@ -5729,9 +5729,9 @@ } }, "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz", + "integrity": "sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g==" }, "union-value": { "version": "1.0.1", diff --git a/package.json b/package.json index cc0956db..09394742 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "azure-iot-edge", "displayName": "Azure IoT Edge", "description": "This extension is now a part of Azure IoT Tools extension pack. We highly recommend installing Azure IoT Tools to get full capabilities for Azure IoT development. Develop, deploy, debug, and manage your IoT Edge solution.", - "version": "1.24.0", + "version": "1.24.1", "publisher": "vsciot-vscode", "aiKey": "95b20d64-f54f-4de3-8ad5-165a75a6c6fe", "icon": "logo.png", diff --git a/src/common/constants.ts b/src/common/constants.ts index df0f65de..a799e392 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -187,7 +187,7 @@ export class Constants { public static failedInstallSimulator = "Failed to install 'iotedgehubdev' tool because of error:"; public static outputNoSimulatorMsg = "Cannot execute command since 'iotedgehubdev' is not installed. Please install it first for IoT Edge Simulator."; public static outputSimulatorIsInstallingMsg = "'iotedgehubdev' is being installed now, and please wait for the installation."; - public static downloadingAndInstallingStandaloneSimulatorMsg = "Downloading and installing Azure IoT EdgeHub Dev Tool (iotedgehubdev)..."; + public static downloadingAndInstallingStandaloneSimulatorMsg = "Downloading and installing Azure IoT EdgeHub Dev Tool (iotedgehubdev) version "; public static installStandaloneSimulatorFailedMsg = "Failed to install 'iotedgehubdev' tool, please check the output channel (Azure IoT Edge) for detailed error message."; public static unexpectedErrorWhenValidateSimulatorUpdate = "Unexpected errors occur when install / update 'iotedgehubdev': "; public static installManuallyMsg = "Please install 'iotedgehubdev' tool first for IoT Edge Simulator."; diff --git a/src/edge/edgeManager.ts b/src/edge/edgeManager.ts index d6f0806b..df26ffc1 100644 --- a/src/edge/edgeManager.ts +++ b/src/edge/edgeManager.ts @@ -531,7 +531,7 @@ export class EdgeManager { } case Constants.LANGUAGE_PYTHON: try { - await new Promise((resolve, reject) => { + await new Promise((resolve, reject) => { tmp.dir({ unsafeCleanup: true }, (err, tmpDir, cleanupCallback) => { if (err) { reject(err); @@ -584,7 +584,7 @@ export class EdgeManager { } case Constants.LANGUAGE_C: try { - await new Promise((resolve, reject) => { + await new Promise((resolve, reject) => { download(`github:Azure/azure-iot-edge-c-module#${Versions.cTemplateVersion()}`, path.join(parent, name), (err) => { if (err) { reject(err); diff --git a/src/edge/simulator.ts b/src/edge/simulator.ts index 2b338901..f593d275 100644 --- a/src/edge/simulator.ts +++ b/src/edge/simulator.ts @@ -1,419 +1,435 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -"use strict"; -import * as dotenv from "dotenv"; -import * as fse from "fs-extra"; -import * as os from "os"; -import * as path from "path"; -import * as request from "request-promise"; -import * as semver from "semver"; -import * as unzipper from "unzipper"; -import * as vscode from "vscode"; -import { ConfigNotSetError } from "../common/ConfigNotSetError"; -import { Configuration } from "../common/configuration"; -import { Constants } from "../common/constants"; -import { Executor } from "../common/executor"; -import { LearnMoreError } from "../common/LearnMoreError"; -import { RetryPolicy } from "../common/retryPolicy"; -import { SimulatorInfo } from "../common/SimulatorInfo"; -import { TelemetryClient } from "../common/telemetryClient"; -import { UserCancelledError } from "../common/UserCancelledError"; -import { Utility } from "../common/utility"; -import { Versions } from "../common/version"; -import { IDeviceItem } from "../typings/IDeviceItem"; -import { InstallResult, InstallReturn } from "./InstallResult"; - -enum SimulatorType { - Pip = 0, - Standalone = 1, - NotInstalled = 2, -} - -export class Simulator { - private static iotedgehubdevVersionUrl: string = "https://pypi.org/pypi/iotedgehubdev/json"; - private static learnMoreUrl: string = "https://aka.ms/AA3nuw8"; - private static simulatorVersionKey: string = "SimulatorVersion"; - private static simulatorExecutableName = "iotedgehubdev"; - - private static currentPlatform = os.platform(); - private static WindowsStandaloneSimulatorFolder = path.join(vscode.extensions.getExtension(Constants.ExtensionId).extensionPath, Simulator.simulatorExecutableName); - - private static maxRetryTimes: number = 3; - private static retryInterval: number = 5000; - - private static extractVersion(output: string): string | null { - if (!output) { - return null; - } - const pattern: RegExp = new RegExp(/version\s+([^\s]+)/g); - const matchRes = output.match(pattern); - if (matchRes !== null) { - return matchRes[0].replace(/version\s+/g, ""); - } - return null; - } - - private static async checkCmdExist(cmd: string): Promise { - try { - if (cmd) { - await Executor.executeCMD(null, cmd, { shell: true }, "--version"); - return true; - } - } catch (error) { } - return false; - } - - private static adjustTerminalCommand(command: string): string { - return (Simulator.currentPlatform === "linux" || Simulator.currentPlatform === "darwin") ? `sudo ${command}` : command; - } - - private isInstalling: boolean = false; - private latestSimulatorInfo: SimulatorInfo; - private simulatorExecutablePath: string; - - constructor(private context: vscode.ExtensionContext) { - if (Simulator.currentPlatform === "win32") { - const installedVersion: string = this.context.globalState.get(Simulator.simulatorVersionKey); - if (installedVersion) { - this.simulatorExecutablePath = path.join(Simulator.WindowsStandaloneSimulatorFolder, installedVersion , Simulator.simulatorExecutableName); - } - } else { - this.simulatorExecutablePath = Simulator.simulatorExecutableName; - } - } - - public async validateSimulatorUpdated(outputChannel: vscode.OutputChannel = null): Promise { - let message: string; - let type: string = ""; - const telemetryName = "simulatorUpdated"; - try { - const simulatorType = await this.simulatorInstalled(); - if (simulatorType === SimulatorType.NotInstalled) { - message = Constants.needSimulatorInstalledMsg; - type = "install"; - } else { - const version: string | null = await this.getCurrentSimulatorVersion(); - if (version && semver.valid(version)) { - const latestVersion: string | undefined = await this.getLatestSimulatorVersion(outputChannel); - if (latestVersion && semver.gt(latestVersion, version)) { - message = `${Constants.updateSimulatorMsg} (${version} to ${latestVersion})`; - } else { - return; - } - } else { - message = Constants.updateSimulatorMsg; - } - - if (simulatorType === SimulatorType.Pip) { - type = "upgradePipPackage"; - } else { - type = "upgradeStandalone"; - } - } - - TelemetryClient.sendEvent(`${telemetryName}.${type}`); - - const installResult = await this.autoInstallSimulator(outputChannel); - - if (installResult.errMsg) { - TelemetryClient.sendErrorEvent(`${telemetryName}.${type}.${InstallReturn[installResult.resultType]}`, { [Constants.errorProperties.error]: installResult.errMsg }); - } else { - TelemetryClient.sendEvent(`${telemetryName}.${type}.${InstallReturn[installResult.resultType]}`); - } - - if (InstallReturn.NotSupported === installResult.resultType) { - const learnMore: vscode.MessageItem = { title: Constants.learnMore }; - if (await vscode.window.showWarningMessage(message, ...[learnMore]) === learnMore) { - await vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(Simulator.learnMoreUrl)); - } - } else if (InstallReturn.Failed === installResult.resultType) { - await vscode.window.showErrorMessage(Constants.installStandaloneSimulatorFailedMsg); - } - } catch (err) { - type = "unexpectedError"; - TelemetryClient.sendEvent(`${telemetryName}.${type}`); - outputChannel.appendLine(Constants.unexpectedErrorWhenValidateSimulatorUpdate + err.message); - } - } - - public async setupIotedgehubdev(deviceItem: IDeviceItem, outputChannel: vscode.OutputChannel) { - return await this.callWithInstallationCheck(outputChannel, async () => { - deviceItem = await Utility.getInputDevice(deviceItem, outputChannel); - if (deviceItem) { - let commandStr = this.getAdjustedSimulatorExecutorPath() + ` setup -c "${deviceItem.connectionString}"`; - if (await this.isModuleTwinSupported()) { - const iotHubConnectionStr = await Configuration.getIotHubConnectionString(); - if (iotHubConnectionStr) { - commandStr = `${commandStr} -i "${iotHubConnectionStr}"`; - } - } - Executor.runInTerminal(Simulator.adjustTerminalCommand(commandStr)); - } - }); - } - - public async startEdgeHubSingleModule(outputChannel: vscode.OutputChannel): Promise { - return await this.callWithInstallationCheck(outputChannel, async () => { - await this.checkIoTedgehubdevConnectionString(outputChannel); - const inputs = await this.inputInputNames(); - const imgVersion = Versions.edgeHubVersion(); - await this.setModuleCred(outputChannel); - await Executor.runInTerminal(Simulator.adjustTerminalCommand(this.getAdjustedSimulatorExecutorPath() + ` start -er "${imgVersion}" -i "${inputs}"`)); - }); - } - - public async setModuleCred(outputChannel: vscode.OutputChannel): Promise { - return await this.callWithInstallationCheck(outputChannel, async () => { - await this.checkIoTedgehubdevConnectionString(outputChannel); - let storagePath = this.context.storagePath; - if (!storagePath) { - storagePath = path.resolve(os.tmpdir(), "vscodeedge"); - } - await fse.ensureDir(storagePath); - const outputFile = path.join(storagePath, "module.env"); - await Executor.executeCMD(outputChannel, this.getAdjustedSimulatorExecutorPath(true), { shell: true }, `modulecred -l -o "${outputFile}"`); - - const moduleConfig = dotenv.parse(await fse.readFile(outputFile)); - await Configuration.setGlobalConfigurationProperty("EdgeHubConnectionString", moduleConfig.EdgeHubConnectionString); - await Configuration.setGlobalConfigurationProperty("EdgeModuleCACertificateFile", moduleConfig.EdgeModuleCACertificateFile); - }); - } - - public async stopSolution(outputChannel: vscode.OutputChannel): Promise { - return await this.callWithInstallationCheck(outputChannel, async () => { - Executor.runInTerminal(Simulator.adjustTerminalCommand(this.getAdjustedSimulatorExecutorPath() + ` stop`)); - return; - }); - } - - public async runSolution(outputChannel: vscode.OutputChannel, deployFileUri?: vscode.Uri, commands: string[] = []): Promise { - return await this.callWithInstallationCheck(outputChannel, async () => { - await this.checkIoTedgehubdevConnectionString(outputChannel); - const pattern = "{**/deployment.*.json,**/deployment.json,**/deployment.*.debug.json,**/config/*.json}"; - const excludePattern = `{${Constants.tsonPattern}}`; - const deployFile: string = await Utility.getInputFilePath(deployFileUri, - pattern, - Constants.deploymentFileDesc, - `${Constants.runSolutionEvent}.selectDeploymentFile`, - excludePattern); - if (!deployFile) { - return; - } - - commands.push(this.constructRunCmd(deployFile)); - Executor.runInTerminal(Utility.combineCommands(commands), this.getRunCmdTerminalTitle()); - return; - }); - } - - private async getLastestSimulatorInfo(outputChannel: vscode.OutputChannel) { - if (!this.latestSimulatorInfo) { - await RetryPolicy.retry(Simulator.maxRetryTimes, Simulator.retryInterval, outputChannel, async () => { - const pipResponse = await request.get(Simulator.iotedgehubdevVersionUrl); - const version = JSON.parse(pipResponse).info.version; - const standaloneDownloadUrl = `https://github.com/Azure/iotedgehubdev/releases/download/v${version}/iotedgehubdev-v${version}-win32-ia32.zip`; - this.latestSimulatorInfo = new SimulatorInfo(version, standaloneDownloadUrl); - }); - - return this.latestSimulatorInfo; - } else { - return this.latestSimulatorInfo; - } - } - - private async getLatestSimulatorVersion(outputChannel: vscode.OutputChannel): Promise { - try { - const info: SimulatorInfo = await this.getLastestSimulatorInfo(outputChannel); - return info.version; - } catch (error) { - return undefined; - } - } - - private async getCurrentSimulatorVersion(): Promise { - const output: string = await Executor.executeCMD(undefined, this.getAdjustedSimulatorExecutorPath(true), { shell: true }, "--version"); - const version: string | null = Simulator.extractVersion(output); - return version; - } - - private async simulatorInstalled(): Promise { - const exist = await Simulator.checkCmdExist(this.getAdjustedSimulatorExecutorPath(true)); - if (exist) { - return Simulator.currentPlatform === "win32" ? SimulatorType.Standalone : SimulatorType.Pip; - } - - return SimulatorType.NotInstalled; - } - - private getAdjustedSimulatorExecutorPath(forceUseCmd: boolean = false): string { - let executorPath: string; - - if (!forceUseCmd) { - executorPath = `"${Utility.adjustFilePath(this.simulatorExecutablePath)}"`; - if (Utility.isUsingPowershell()) { - executorPath = `& ${executorPath}`; - } - } else { - executorPath = `"${this.simulatorExecutablePath}"`; - } - - return executorPath; - } - - private async downloadStandaloneSimulatorWithProgress(outputChannel: vscode.OutputChannel) { - await vscode.window.withProgress({ - location: vscode.ProgressLocation.Notification, - title: Constants.downloadingAndInstallingStandaloneSimulatorMsg, - }, async () => { - await this.downloadStandaloneSimulator(outputChannel); - }); - } - - private async downloadStandaloneSimulator(outputChannel: vscode.OutputChannel) { - const info: SimulatorInfo = await this.getLastestSimulatorInfo(outputChannel); - const binariesZipUrl: string = info.standaloneDownloadUrl; - const version: string = info.version; - - await RetryPolicy.retry(Simulator.maxRetryTimes, Simulator.retryInterval, outputChannel, async () => { - await new Promise((resolve, reject) => { - const req = request(binariesZipUrl); - req.on("response", (res) => { - if (res.statusCode === 200) { - req.pipe(unzipper.Extract({ path: Simulator.WindowsStandaloneSimulatorFolder })) - .on("close", () => resolve()).on("error", (e) => reject(new Error("Cannot extract simulator binaries from zip file: " + e.message))); - } else { - reject(new Error("Cannot download simulator with status code: " + res.statusCode)); - } - }); - - req.on("error", (err) => { - reject(new Error("Cannot download simulator, please check your network connection: " + err.message)); - }); - }); - - try { - if (this.simulatorExecutablePath) { - await fse.remove(path.dirname(this.simulatorExecutablePath)); - } - } catch (err) { - // ignore - } - - await fse.move(path.join(Simulator.WindowsStandaloneSimulatorFolder, Simulator.simulatorExecutableName), path.join(Simulator.WindowsStandaloneSimulatorFolder, version)); - this.context.globalState.update(Simulator.simulatorVersionKey, version); - this.simulatorExecutablePath = path.join(Simulator.WindowsStandaloneSimulatorFolder, version , Simulator.simulatorExecutableName); - }); - } - - private async autoInstallSimulator(outputChannel: vscode.OutputChannel = null): Promise { - // auto install only supported on windows. For linux/macOS ask user install manually. - if (Simulator.currentPlatform !== "win32") { - return new InstallResult(InstallReturn.NotSupported); - } - - if (!this.isInstalling) { - this.isInstalling = true; - let ret: InstallReturn = InstallReturn.Success; - let errMsg: string; - try { - await this.downloadStandaloneSimulatorWithProgress(outputChannel); - } catch (error) { - if (outputChannel) { - outputChannel.appendLine(`${Constants.failedInstallSimulator} ${error.message}`); - } - ret = InstallReturn.Failed; - errMsg = error.message; - } - - this.isInstalling = false; - return new InstallResult(ret, errMsg); - } else { - return new InstallResult(InstallReturn.IsInstalling); - } - } - - private async checkIoTedgehubdevConnectionString(outputChannel: vscode.OutputChannel) { - if (await this.isValidateConfigSupported()) { - try { - await Executor.executeCMD(null, this.getAdjustedSimulatorExecutorPath(true), { shell: true }, "validateconfig"); - } catch (error) { - let errorMsg = error.message; - if (error.errorCode === 2 && Simulator.currentPlatform === "win32") { - errorMsg = Constants.connectionStringNotSetErrorMsgOnWindows; - } - throw new ConfigNotSetError(errorMsg); - } - } - } - - private async isModuleTwinSupported(): Promise { - return this.isSupported("0.8.0"); - } - - private async isValidateConfigSupported(): Promise { - return this.isSupported("0.10.0"); - } - - private async isSupported(supportedVersion: string): Promise { - let isSupported = false; - try { - const output = await Executor.executeCMD(undefined, this.getAdjustedSimulatorExecutorPath(true), { shell: true }, "--version"); - const version: string | null = Simulator.extractVersion(output); - if (version && semver.valid(version)) { - isSupported = semver.gte(version, supportedVersion); - } - } catch (err) {} - return isSupported; - } - - private constructRunCmd(deployFile: string): string { - return Simulator.adjustTerminalCommand(this.getAdjustedSimulatorExecutorPath() + ` start -d "${deployFile}" -v`); - } - - private getRunCmdTerminalTitle(): string { - return Constants.edgeDisplayName + " Solution Status"; - } - - private async inputInputNames(): Promise { - return await Utility.showInputBox( - Constants.inputNamePattern, - Constants.inputNamePrompt, null, "input1,input2"); - } - - private async validateSimulatorInstalled(outputChannel: vscode.OutputChannel = null): Promise { - const telemetryName = "simulatorInstalled"; - if (await this.simulatorInstalled() === SimulatorType.NotInstalled) { - TelemetryClient.sendEvent(`${telemetryName}.install`); - const installResult = await this.autoInstallSimulator(outputChannel); - - if (installResult.errMsg) { - TelemetryClient.sendErrorEvent(`${telemetryName}.install.${InstallReturn[installResult.resultType]}`, { [Constants.errorProperties.error]: installResult.errMsg }); - } else { - TelemetryClient.sendEvent(`${telemetryName}.install.${InstallReturn[installResult.resultType]}`); - } - - return installResult.resultType; - } else { - return InstallReturn.Success; - } - } - - private async callWithInstallationCheck(outputChannel: vscode.OutputChannel, callback: () => Promise): Promise { - const installReturn = await this.validateSimulatorInstalled(outputChannel); - - switch (installReturn) { - case InstallReturn.Success: - return await callback(); - case InstallReturn.Failed: - await vscode.window.showErrorMessage(Constants.installStandaloneSimulatorFailedMsg); - case InstallReturn.NotSupported: - outputChannel.appendLine(Constants.outputNoSimulatorMsg); - throw new LearnMoreError(Constants.installManuallyMsg, Simulator.learnMoreUrl); - case InstallReturn.IsInstalling: - outputChannel.appendLine(Constants.outputSimulatorIsInstallingMsg); - default: - throw new UserCancelledError(); - } - } -} +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +"use strict"; +import * as dotenv from "dotenv"; +import * as fse from "fs-extra"; +import * as os from "os"; +import * as path from "path"; +import * as request from "request-promise"; +import * as semver from "semver"; +import * as unzipper from "unzipper"; +import * as vscode from "vscode"; +import { ConfigNotSetError } from "../common/ConfigNotSetError"; +import { Configuration } from "../common/configuration"; +import { Constants } from "../common/constants"; +import { Executor } from "../common/executor"; +import { LearnMoreError } from "../common/LearnMoreError"; +import { RetryPolicy } from "../common/retryPolicy"; +import { SimulatorInfo } from "../common/SimulatorInfo"; +import { TelemetryClient } from "../common/telemetryClient"; +import { UserCancelledError } from "../common/UserCancelledError"; +import { Utility } from "../common/utility"; +import { Versions } from "../common/version"; +import { IDeviceItem } from "../typings/IDeviceItem"; +import { InstallResult, InstallReturn } from "./InstallResult"; + +enum SimulatorType { + Pip = 0, + Standalone = 1, + NotInstalled = 2, +} + +export class Simulator { + private static iotedgehubdevVersionUrl: string = "https://pypi.org/pypi/iotedgehubdev/json"; + private static iotedgehubdevLockVersionKey = "IOTEDGEHUBDEV_VERSION"; + private static iotedgehubdevDefaultVersion = "0.14.8"; + private static learnMoreUrl: string = "https://aka.ms/AA3nuw8"; + private static simulatorVersionKey: string = "SimulatorVersion"; + private static simulatorExecutableName = "iotedgehubdev"; + + private static currentPlatform = os.platform(); + private static WindowsStandaloneSimulatorFolder = path.join(vscode.extensions.getExtension(Constants.ExtensionId).extensionPath, Simulator.simulatorExecutableName); + + private static maxRetryTimes: number = 3; + private static retryInterval: number = 5000; + + private static extractVersion(output: string): string | null { + if (!output) { + return null; + } + const pattern: RegExp = new RegExp(/version\s+([^\s]+)/g); + const matchRes = output.match(pattern); + if (matchRes !== null) { + return matchRes[0].replace(/version\s+/g, ""); + } + return null; + } + + private static async checkCmdExist(cmd: string): Promise { + try { + if (cmd) { + await Executor.executeCMD(null, cmd, { shell: true }, "--version"); + return true; + } + } catch (error) { } + return false; + } + + private static adjustTerminalCommand(command: string): string { + return (Simulator.currentPlatform === "linux" || Simulator.currentPlatform === "darwin") ? `sudo ${command}` : command; + } + + private isInstalling: boolean = false; + private desiredSimulatorInfo: SimulatorInfo; + private simulatorExecutablePath: string; + + constructor(private context: vscode.ExtensionContext) { + if (Simulator.currentPlatform === "win32") { + const installedVersion: string = this.context.globalState.get(Simulator.simulatorVersionKey); + if (installedVersion) { + this.simulatorExecutablePath = path.join(Simulator.WindowsStandaloneSimulatorFolder, installedVersion , Simulator.simulatorExecutableName); + } + } else { + this.simulatorExecutablePath = Simulator.simulatorExecutableName; + } + } + + public async validateSimulatorUpdated(outputChannel: vscode.OutputChannel = null): Promise { + let message: string; + let type: string = ""; + const telemetryName = "simulatorUpdated"; + try { + const simulatorType = await this.simulatorInstalled(); + if (simulatorType === SimulatorType.NotInstalled) { + message = Constants.needSimulatorInstalledMsg; + type = "install"; + } else { + const version: string | null = await this.getCurrentSimulatorVersion(); + if (version && semver.valid(version)) { + const desiredVersion: string | undefined = await this.getDesiredSimulatorVersion(outputChannel); + if (desiredVersion && semver.neq(desiredVersion, version)) { + message = `${Constants.updateSimulatorMsg} (${version} to ${desiredVersion})`; + } else { + return; + } + } else { + message = Constants.updateSimulatorMsg; + } + + if (simulatorType === SimulatorType.Pip) { + type = "upgradePipPackage"; + } else { + type = "upgradeStandalone"; + } + } + + TelemetryClient.sendEvent(`${telemetryName}.${type}`); + + const installResult = await this.autoInstallSimulator(outputChannel); + + if (installResult.errMsg) { + TelemetryClient.sendErrorEvent(`${telemetryName}.${type}.${InstallReturn[installResult.resultType]}`, { [Constants.errorProperties.error]: installResult.errMsg }); + } else { + TelemetryClient.sendEvent(`${telemetryName}.${type}.${InstallReturn[installResult.resultType]}`); + } + + if (InstallReturn.NotSupported === installResult.resultType) { + const learnMore: vscode.MessageItem = { title: Constants.learnMore }; + if (await vscode.window.showWarningMessage(message, ...[learnMore]) === learnMore) { + await vscode.commands.executeCommand("vscode.open", vscode.Uri.parse(Simulator.learnMoreUrl)); + } + } else if (InstallReturn.Failed === installResult.resultType) { + await vscode.window.showErrorMessage(Constants.installStandaloneSimulatorFailedMsg); + } + } catch (err) { + type = "unexpectedError"; + TelemetryClient.sendEvent(`${telemetryName}.${type}`); + outputChannel.appendLine(Constants.unexpectedErrorWhenValidateSimulatorUpdate + err.message); + } + } + + public async setupIotedgehubdev(deviceItem: IDeviceItem, outputChannel: vscode.OutputChannel) { + return await this.callWithInstallationCheck(outputChannel, async () => { + deviceItem = await Utility.getInputDevice(deviceItem, outputChannel); + if (deviceItem) { + let commandStr = this.getAdjustedSimulatorExecutorPath() + ` setup -c "${deviceItem.connectionString}"`; + if (await this.isModuleTwinSupported()) { + const iotHubConnectionStr = await Configuration.getIotHubConnectionString(); + if (iotHubConnectionStr) { + commandStr = `${commandStr} -i "${iotHubConnectionStr}"`; + } + } + Executor.runInTerminal(Simulator.adjustTerminalCommand(commandStr)); + } + }); + } + + public async startEdgeHubSingleModule(outputChannel: vscode.OutputChannel): Promise { + return await this.callWithInstallationCheck(outputChannel, async () => { + await this.checkIoTedgehubdevConnectionString(outputChannel); + const inputs = await this.inputInputNames(); + const imgVersion = Versions.edgeHubVersion(); + await this.setModuleCred(outputChannel); + await Executor.runInTerminal(Simulator.adjustTerminalCommand(this.getAdjustedSimulatorExecutorPath() + ` start -er "${imgVersion}" -i "${inputs}"`)); + }); + } + + public async setModuleCred(outputChannel: vscode.OutputChannel): Promise { + return await this.callWithInstallationCheck(outputChannel, async () => { + await this.checkIoTedgehubdevConnectionString(outputChannel); + let storagePath = this.context.storagePath; + if (!storagePath) { + storagePath = path.resolve(os.tmpdir(), "vscodeedge"); + } + await fse.ensureDir(storagePath); + const outputFile = path.join(storagePath, "module.env"); + await Executor.executeCMD(outputChannel, this.getAdjustedSimulatorExecutorPath(true), { shell: true }, `modulecred -l -o "${outputFile}"`); + + const moduleConfig = dotenv.parse(await fse.readFile(outputFile)); + await Configuration.setGlobalConfigurationProperty("EdgeHubConnectionString", moduleConfig.EdgeHubConnectionString); + await Configuration.setGlobalConfigurationProperty("EdgeModuleCACertificateFile", moduleConfig.EdgeModuleCACertificateFile); + }); + } + + public async stopSolution(outputChannel: vscode.OutputChannel): Promise { + return await this.callWithInstallationCheck(outputChannel, async () => { + Executor.runInTerminal(Simulator.adjustTerminalCommand(this.getAdjustedSimulatorExecutorPath() + ` stop`)); + return; + }); + } + + public async runSolution(outputChannel: vscode.OutputChannel, deployFileUri?: vscode.Uri, commands: string[] = []): Promise { + return await this.callWithInstallationCheck(outputChannel, async () => { + await this.checkIoTedgehubdevConnectionString(outputChannel); + const pattern = "{**/deployment.*.json,**/deployment.json,**/deployment.*.debug.json,**/config/*.json}"; + const excludePattern = `{${Constants.tsonPattern}}`; + const deployFile: string = await Utility.getInputFilePath(deployFileUri, + pattern, + Constants.deploymentFileDesc, + `${Constants.runSolutionEvent}.selectDeploymentFile`, + excludePattern); + if (!deployFile) { + return; + } + + commands.push(this.constructRunCmd(deployFile)); + Executor.runInTerminal(Utility.combineCommands(commands), this.getRunCmdTerminalTitle()); + return; + }); + } + + private async getDesiredSimulatorInfo(outputChannel: vscode.OutputChannel) { + if (!this.desiredSimulatorInfo) { + await RetryPolicy.retry(Simulator.maxRetryTimes, Simulator.retryInterval, outputChannel, async () => { + let version = Simulator.iotedgehubdevDefaultVersion; + const pipResponse = await request.get(Simulator.iotedgehubdevVersionUrl); + const releases = JSON.parse(pipResponse).releases; + const lockVersion = process.env[Simulator.iotedgehubdevLockVersionKey]; + if (lockVersion !== undefined && lockVersion.trim() !== "") { + // Make sure the custom version is an existing release + if (releases.hasOwnProperty(lockVersion)) { + version = lockVersion; + } else { + outputChannel.appendLine(`The specified iotedgehubdev version ${version} is not a valid release`); + } + } + outputChannel.appendLine(`The specified iotedgehubdev version is: ${version}`); + const standaloneDownloadUrl = `https://github.com/Azure/iotedgehubdev/releases/download/v${version}/iotedgehubdev-v${version}-win32-ia32.zip`; + this.desiredSimulatorInfo = new SimulatorInfo(version, standaloneDownloadUrl); + }); + + return this.desiredSimulatorInfo; + } else { + return this.desiredSimulatorInfo; + } + } + + private async getDesiredSimulatorVersion(outputChannel: vscode.OutputChannel): Promise { + try { + const info: SimulatorInfo = await this.getDesiredSimulatorInfo(outputChannel); + return info.version; + } catch (error) { + return undefined; + } + } + + private async getCurrentSimulatorVersion(): Promise { + const output: string = await Executor.executeCMD(undefined, this.getAdjustedSimulatorExecutorPath(true), { shell: true }, "--version"); + const version: string | null = Simulator.extractVersion(output); + return version; + } + + private async simulatorInstalled(): Promise { + const exist = await Simulator.checkCmdExist(this.getAdjustedSimulatorExecutorPath(true)); + if (exist) { + return Simulator.currentPlatform === "win32" ? SimulatorType.Standalone : SimulatorType.Pip; + } + + return SimulatorType.NotInstalled; + } + + private getAdjustedSimulatorExecutorPath(forceUseCmd: boolean = false): string { + let executorPath: string; + + if (!forceUseCmd) { + executorPath = `"${Utility.adjustFilePath(this.simulatorExecutablePath)}"`; + if (Utility.isUsingPowershell()) { + executorPath = `& ${executorPath}`; + } + } else { + executorPath = `"${this.simulatorExecutablePath}"`; + } + + return executorPath; + } + + private async downloadStandaloneSimulatorWithProgress(outputChannel: vscode.OutputChannel) { + const info: SimulatorInfo = await this.getDesiredSimulatorInfo(outputChannel); + const version: string = info.version; + + await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + title: Constants.downloadingAndInstallingStandaloneSimulatorMsg + version, + }, async () => { + await this.downloadStandaloneSimulator(outputChannel); + }); + } + + private async downloadStandaloneSimulator(outputChannel: vscode.OutputChannel) { + const info: SimulatorInfo = await this.getDesiredSimulatorInfo(outputChannel); + const binariesZipUrl: string = info.standaloneDownloadUrl; + const version: string = info.version; + + await RetryPolicy.retry(Simulator.maxRetryTimes, Simulator.retryInterval, outputChannel, async () => { + await new Promise((resolve, reject) => { + const req = request(binariesZipUrl); + req.on("response", (res) => { + if (res.statusCode === 200) { + req.pipe(unzipper.Extract({ path: Simulator.WindowsStandaloneSimulatorFolder })) + .on("close", () => resolve()).on("error", (e) => reject(new Error("Cannot extract simulator binaries from zip file: " + e.message))); + } else { + reject(new Error("Cannot download simulator with status code: " + res.statusCode)); + } + }); + + req.on("error", (err) => { + reject(new Error("Cannot download simulator, please check your network connection: " + err.message)); + }); + }); + + try { + if (this.simulatorExecutablePath) { + await fse.remove(path.dirname(this.simulatorExecutablePath)); + } + } catch (err) { + // ignore + } + + await fse.move(path.join(Simulator.WindowsStandaloneSimulatorFolder, Simulator.simulatorExecutableName), path.join(Simulator.WindowsStandaloneSimulatorFolder, version)); + this.context.globalState.update(Simulator.simulatorVersionKey, version); + this.simulatorExecutablePath = path.join(Simulator.WindowsStandaloneSimulatorFolder, version , Simulator.simulatorExecutableName); + }); + } + + private async autoInstallSimulator(outputChannel: vscode.OutputChannel = null): Promise { + // auto install only supported on windows. For linux/macOS ask user install manually. + if (Simulator.currentPlatform !== "win32") { + return new InstallResult(InstallReturn.NotSupported); + } + + if (!this.isInstalling) { + this.isInstalling = true; + let ret: InstallReturn = InstallReturn.Success; + let errMsg: string; + try { + await this.downloadStandaloneSimulatorWithProgress(outputChannel); + } catch (error) { + if (outputChannel) { + outputChannel.appendLine(`${Constants.failedInstallSimulator} ${error.message}`); + } + ret = InstallReturn.Failed; + errMsg = error.message; + } + + this.isInstalling = false; + return new InstallResult(ret, errMsg); + } else { + return new InstallResult(InstallReturn.IsInstalling); + } + } + + private async checkIoTedgehubdevConnectionString(outputChannel: vscode.OutputChannel) { + if (await this.isValidateConfigSupported()) { + try { + await Executor.executeCMD(null, this.getAdjustedSimulatorExecutorPath(true), { shell: true }, "validateconfig"); + } catch (error) { + let errorMsg = error.message; + if (error.errorCode === 2 && Simulator.currentPlatform === "win32") { + errorMsg = Constants.connectionStringNotSetErrorMsgOnWindows; + } + throw new ConfigNotSetError(errorMsg); + } + } + } + + private async isModuleTwinSupported(): Promise { + return this.isSupported("0.8.0"); + } + + private async isValidateConfigSupported(): Promise { + return this.isSupported("0.10.0"); + } + + private async isSupported(supportedVersion: string): Promise { + let isSupported = false; + try { + const output = await Executor.executeCMD(undefined, this.getAdjustedSimulatorExecutorPath(true), { shell: true }, "--version"); + const version: string | null = Simulator.extractVersion(output); + if (version && semver.valid(version)) { + isSupported = semver.gte(version, supportedVersion); + } + } catch (err) {} + return isSupported; + } + + private constructRunCmd(deployFile: string): string { + return Simulator.adjustTerminalCommand(this.getAdjustedSimulatorExecutorPath() + ` start -d "${deployFile}" -v`); + } + + private getRunCmdTerminalTitle(): string { + return Constants.edgeDisplayName + " Solution Status"; + } + + private async inputInputNames(): Promise { + return await Utility.showInputBox( + Constants.inputNamePattern, + Constants.inputNamePrompt, null, "input1,input2"); + } + + private async validateSimulatorInstalled(outputChannel: vscode.OutputChannel = null): Promise { + const telemetryName = "simulatorInstalled"; + if (await this.simulatorInstalled() === SimulatorType.NotInstalled) { + TelemetryClient.sendEvent(`${telemetryName}.install`); + const installResult = await this.autoInstallSimulator(outputChannel); + + if (installResult.errMsg) { + TelemetryClient.sendErrorEvent(`${telemetryName}.install.${InstallReturn[installResult.resultType]}`, { [Constants.errorProperties.error]: installResult.errMsg }); + } else { + TelemetryClient.sendEvent(`${telemetryName}.install.${InstallReturn[installResult.resultType]}`); + } + + return installResult.resultType; + } else { + return InstallReturn.Success; + } + } + + private async callWithInstallationCheck(outputChannel: vscode.OutputChannel, callback: () => Promise): Promise { + const installReturn = await this.validateSimulatorInstalled(outputChannel); + + switch (installReturn) { + case InstallReturn.Success: + return await callback(); + case InstallReturn.Failed: + await vscode.window.showErrorMessage(Constants.installStandaloneSimulatorFailedMsg); + case InstallReturn.NotSupported: + outputChannel.appendLine(Constants.outputNoSimulatorMsg); + throw new LearnMoreError(Constants.installManuallyMsg, Simulator.learnMoreUrl); + case InstallReturn.IsInstalling: + outputChannel.appendLine(Constants.outputSimulatorIsInstallingMsg); + default: + throw new UserCancelledError(); + } + } +}