iotedgedev 3.3.4__zip
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- s/.devcontainer/Dockerfile +75 -0
- s/.devcontainer/devcontainer.json +40 -0
- s/.devcontainer/library-scripts/docker-in-docker-debian.sh +236 -0
- s/.env.tmp +108 -0
- s/.git/FETCH_HEAD +61 -0
- s/.git/HEAD +1 -0
- s/.git/config +12 -0
- s/.git/description +1 -0
- s/.git/hooks/applypatch-msg.sample +15 -0
- s/.git/hooks/commit-msg.sample +24 -0
- s/.git/hooks/fsmonitor-watchman.sample +173 -0
- s/.git/hooks/post-update.sample +8 -0
- s/.git/hooks/pre-applypatch.sample +14 -0
- s/.git/hooks/pre-commit.sample +49 -0
- s/.git/hooks/pre-merge-commit.sample +13 -0
- s/.git/hooks/pre-push.sample +53 -0
- s/.git/hooks/pre-rebase.sample +169 -0
- s/.git/hooks/pre-receive.sample +24 -0
- s/.git/hooks/prepare-commit-msg.sample +42 -0
- s/.git/hooks/push-to-checkout.sample +78 -0
- s/.git/hooks/update.sample +128 -0
- s/.git/index +0 -0
- s/.git/info/exclude +6 -0
- s/.git/logs/HEAD +1 -0
- s/.git/logs/refs/remotes/origin/EO +1 -0
- s/.git/logs/refs/remotes/origin/avagraw/security_fix +1 -0
- s/.git/logs/refs/remotes/origin/cherry-pick-rename +1 -0
- s/.git/logs/refs/remotes/origin/chyuan/fix-validation-not-fail-test +1 -0
- s/.git/logs/refs/remotes/origin/chyuan/update-linux-agent +1 -0
- s/.git/logs/refs/remotes/origin/dependabot/pip/pyyaml-5.4 +1 -0
- s/.git/logs/refs/remotes/origin/eliises/add-device-tagging-docs +1 -0
- s/.git/logs/refs/remotes/origin/main +1 -0
- s/.git/logs/refs/remotes/origin/ray-deps +1 -0
- s/.git/logs/refs/remotes/origin/ray-dev-setup +1 -0
- s/.git/logs/refs/remotes/origin/ray-tox +1 -0
- s/.git/logs/refs/remotes/origin/release-pipeline-automation +1 -0
- s/.git/logs/refs/remotes/origin/spmcdono/fix-pipeline-test-install +1 -0
- s/.git/logs/refs/remotes/origin/spmcdono/layered-deployments +1 -0
- s/.git/logs/refs/remotes/origin/spmcdono/pypi-package-test-pipeline +1 -0
- s/.git/logs/refs/remotes/origin/spmcdono/remove-py2 +1 -0
- s/.git/logs/refs/remotes/origin/spmcdono/remove-py2-dev +1 -0
- s/.git/logs/refs/remotes/origin/spmcdono/test-native-runtime-install +1 -0
- s/.git/logs/refs/remotes/origin/spmcdono/update-pipeline +1 -0
- s/.git/logs/refs/remotes/origin/user/marine/EliiseS/devcontainer +1 -0
- s/.git/logs/refs/remotes/origin/user/marine/fixsimver +1 -0
- s/.git/logs/refs/remotes/origin/user/marine/layereddeploymentsbuildtest +1 -0
- s/.git/logs/refs/remotes/origin/user/marine/master-to-main +1 -0
- s/.git/logs/refs/remotes/origin/user/marine/pipeline +1 -0
- s/.git/logs/refs/remotes/origin/user/vikas +1 -0
- s/.git/logs/refs/remotes/origin/user/vikas0212/verify-tests +1 -0
- s/.git/logs/refs/remotes/origin/user/vikas2 +1 -0
- s/.git/logs/refs/remotes/origin/user/vikas_pypi +1 -0
- s/.git/logs/refs/remotes/origin/user/vikas_pypi-1 +1 -0
- s/.git/logs/refs/remotes/origin/user/vikas_testing +1 -0
- s/.git/logs/refs/remotes/origin/user/xinzed/dps_conn_str +1 -0
- s/.git/logs/refs/remotes/origin/user/xinzed/update-python-docker +1 -0
- s/.git/logs/refs/remotes/origin/vikas/azure +1 -0
- s/.git/logs/refs/remotes/origin/vikas/dependebot +1 -0
- s/.git/logs/refs/remotes/origin/xinzed/3_1_0rc1 +1 -0
- s/.git/logs/refs/remotes/origin/xinzed/update-runtime +1 -0
- s/.git/objects/pack/pack-a8179feb7da51d11a42c37c5b3c8593cffe17075.idx +0 -0
- s/.git/objects/pack/pack-a8179feb7da51d11a42c37c5b3c8593cffe17075.pack +0 -0
- s/.git/refs/remotes/origin/EO +1 -0
- s/.git/refs/remotes/origin/avagraw/security_fix +1 -0
- s/.git/refs/remotes/origin/cherry-pick-rename +1 -0
- s/.git/refs/remotes/origin/chyuan/fix-validation-not-fail-test +1 -0
- s/.git/refs/remotes/origin/chyuan/update-linux-agent +1 -0
- s/.git/refs/remotes/origin/dependabot/pip/pyyaml-5.4 +1 -0
- s/.git/refs/remotes/origin/eliises/add-device-tagging-docs +1 -0
- s/.git/refs/remotes/origin/main +1 -0
- s/.git/refs/remotes/origin/ray-deps +1 -0
- s/.git/refs/remotes/origin/ray-dev-setup +1 -0
- s/.git/refs/remotes/origin/ray-tox +1 -0
- s/.git/refs/remotes/origin/release-pipeline-automation +1 -0
- s/.git/refs/remotes/origin/spmcdono/fix-pipeline-test-install +1 -0
- s/.git/refs/remotes/origin/spmcdono/layered-deployments +1 -0
- s/.git/refs/remotes/origin/spmcdono/pypi-package-test-pipeline +1 -0
- s/.git/refs/remotes/origin/spmcdono/remove-py2 +1 -0
- s/.git/refs/remotes/origin/spmcdono/remove-py2-dev +1 -0
- s/.git/refs/remotes/origin/spmcdono/test-native-runtime-install +1 -0
- s/.git/refs/remotes/origin/spmcdono/update-pipeline +1 -0
- s/.git/refs/remotes/origin/user/marine/EliiseS/devcontainer +1 -0
- s/.git/refs/remotes/origin/user/marine/fixsimver +1 -0
- s/.git/refs/remotes/origin/user/marine/layereddeploymentsbuildtest +1 -0
- s/.git/refs/remotes/origin/user/marine/master-to-main +1 -0
- s/.git/refs/remotes/origin/user/marine/pipeline +1 -0
- s/.git/refs/remotes/origin/user/vikas +1 -0
- s/.git/refs/remotes/origin/user/vikas0212/verify-tests +1 -0
- s/.git/refs/remotes/origin/user/vikas2 +1 -0
- s/.git/refs/remotes/origin/user/vikas_pypi +1 -0
- s/.git/refs/remotes/origin/user/vikas_pypi-1 +1 -0
- s/.git/refs/remotes/origin/user/vikas_testing +1 -0
- s/.git/refs/remotes/origin/user/xinzed/dps_conn_str +1 -0
- s/.git/refs/remotes/origin/user/xinzed/update-python-docker +1 -0
- s/.git/refs/remotes/origin/vikas/azure +1 -0
- s/.git/refs/remotes/origin/vikas/dependebot +1 -0
- s/.git/refs/remotes/origin/xinzed/3_1_0rc1 +1 -0
- s/.git/refs/remotes/origin/xinzed/update-runtime +1 -0
- s/.git/refs/tags/v0.49.0 +1 -0
- s/.git/refs/tags/v0.50.0 +1 -0
- s/.git/refs/tags/v0.51.0 +1 -0
- s/.git/refs/tags/v0.52.0 +1 -0
- s/.git/refs/tags/v0.53.0 +1 -0
- s/.git/refs/tags/v0.54.0 +1 -0
- s/.git/refs/tags/v0.55.0 +1 -0
- s/.git/refs/tags/v0.56.0 +1 -0
- s/.git/refs/tags/v0.57.0 +1 -0
- s/.git/refs/tags/v0.58.0 +1 -0
- s/.git/refs/tags/v0.59.0 +1 -0
- s/.git/refs/tags/v0.60.0 +1 -0
- s/.git/refs/tags/v0.61.0 +1 -0
- s/.git/refs/tags/v0.62.0 +1 -0
- s/.git/refs/tags/v0.63.0 +1 -0
- s/.git/refs/tags/v0.64.0 +1 -0
- s/.git/refs/tags/v0.65.0 +1 -0
- s/.git/refs/tags/v0.66.0 +1 -0
- s/.git/refs/tags/v0.67.0 +1 -0
- s/.git/refs/tags/v0.68.0 +1 -0
- s/.git/refs/tags/v0.69.0 +1 -0
- s/.git/refs/tags/v0.70.0 +1 -0
- s/.git/refs/tags/v0.71.0 +1 -0
- s/.git/refs/tags/v0.72.0 +1 -0
- s/.git/refs/tags/v0.73.0 +1 -0
- s/.git/refs/tags/v0.74.0 +1 -0
- s/.git/refs/tags/v0.75.0 +1 -0
- s/.git/refs/tags/v0.76.0 +1 -0
- s/.git/refs/tags/v0.77.0 +1 -0
- s/.git/refs/tags/v0.78.0 +1 -0
- s/.git/refs/tags/v0.79.0 +1 -0
- s/.git/refs/tags/v0.82.0 +1 -0
- s/.git/refs/tags/v0.82.0-container +1 -0
- s/.git/refs/tags/v1.0.0 +1 -0
- s/.git/refs/tags/v1.1.0 +1 -0
- s/.git/refs/tags/v1.2.0 +1 -0
- s/.git/refs/tags/v1.3.0 +1 -0
- s/.git/refs/tags/v2.0.0 +1 -0
- s/.git/refs/tags/v2.0.1 +1 -0
- s/.git/refs/tags/v2.0.2 +1 -0
- s/.git/refs/tags/v2.1.0 +1 -0
- s/.git/refs/tags/v2.1.1 +1 -0
- s/.git/refs/tags/v2.1.2 +1 -0
- s/.git/refs/tags/v2.1.3 +1 -0
- s/.git/refs/tags/v2.1.4 +1 -0
- s/.git/refs/tags/v2.1.6 +1 -0
- s/.git/refs/tags/v2.2.0 +1 -0
- s/.git/refs/tags/v3.0.0 +1 -0
- s/.git/refs/tags/v3.0.0-rc +1 -0
- s/.git/refs/tags/v3.1.0 +1 -0
- s/.git/refs/tags/v3.1.1 +1 -0
- s/.git/refs/tags/v3.1.2 +1 -0
- s/.git/refs/tags/v3.2.0 +1 -0
- s/.git/refs/tags/v3.2.0rc0 +1 -0
- s/.git/refs/tags/v3.3.0 +1 -0
- s/.git/refs/tags/v3.3.1 +1 -0
- s/.git/refs/tags/v3.3.2 +1 -0
- s/.git/refs/tags/v3.3.3 +1 -0
- s/.git/refs/tags/v3.3.3-rc0 +1 -0
- s/.git/refs/tags/v3.3.4 +1 -0
- s/.github/ISSUE_TEMPLATE/bug_report.md +16 -0
- s/.github/ISSUE_TEMPLATE/feature_request.md +7 -0
- s/.github/workflows/wiki.yml +54 -0
- s/.gitignore +103 -0
- s/.vscode/launch.json +26 -0
- s/.vscode/settings.json +30 -0
- s/CHANGELOG.md +156 -0
- s/CODE_OF_CONDUCT.md +1 -0
- s/CONTRIBUTING.md +122 -0
- s/LICENSE +14 -0
- s/MANIFEST.in +7 -0
- s/Makefile +87 -0
- s/README.md +204 -0
- s/assets/armdeployment.png +0 -0
- s/assets/armoutputs.png +0 -0
- s/assets/deploy/ARMDeployment/ARMDeployment.deployproj +36 -0
- s/assets/deploy/ARMDeployment/Deploy-AzureResourceGroup.ps1 +118 -0
- s/assets/deploy/ARMDeployment/azuredeploy.json +145 -0
- s/assets/deploy/ARMDeployment/azuredeploy.parameters.json +6 -0
- s/assets/deploy/ARMDeployment.sln +25 -0
- s/assets/edgedevtool2mins.png +0 -0
- s/assets/edgedevtoolintro.png +0 -0
- s/assets/edgedevtoolquickstartlarge.png +0 -0
- s/assets/edgedevtoolquickstartsmall.png +0 -0
- s/assets/edgedevtoolwsl.png +0 -0
- s/azure-pipelines.yml +144 -0
- s/build/lib/iotedgedev/__init__.py +6 -0
- s/build/lib/iotedgedev/args.py +18 -0
- s/build/lib/iotedgedev/azurecli.py +542 -0
- s/build/lib/iotedgedev/buildoptionsparser.py +151 -0
- s/build/lib/iotedgedev/buildprofile.py +5 -0
- s/build/lib/iotedgedev/cli.py +830 -0
- s/build/lib/iotedgedev/connectionstring.py +58 -0
- s/build/lib/iotedgedev/constants.py +12 -0
- s/build/lib/iotedgedev/containerregistry.py +5 -0
- s/build/lib/iotedgedev/decorators.py +116 -0
- s/build/lib/iotedgedev/deploymentmanifest.py +313 -0
- s/build/lib/iotedgedev/dockercls.py +255 -0
- s/build/lib/iotedgedev/dotnet.py +31 -0
- s/build/lib/iotedgedev/edge.py +33 -0
- s/build/lib/iotedgedev/envvars.py +240 -0
- s/build/lib/iotedgedev/iothub.py +65 -0
- s/build/lib/iotedgedev/module.py +62 -0
- s/build/lib/iotedgedev/modules.py +332 -0
- s/build/lib/iotedgedev/organizedgroup.py +48 -0
- s/build/lib/iotedgedev/output.py +57 -0
- s/build/lib/iotedgedev/simulator.py +58 -0
- s/build/lib/iotedgedev/solution.py +48 -0
- s/build/lib/iotedgedev/telemetry.py +155 -0
- s/build/lib/iotedgedev/telemetryconfig.py +85 -0
- s/build/lib/iotedgedev/telemetryuploader.py +53 -0
- s/build/lib/iotedgedev/template/.env.tmp +87 -0
- s/build/lib/iotedgedev/template/.gitignore +10 -0
- s/build/lib/iotedgedev/template/deployment.template.json +79 -0
- s/build/lib/iotedgedev/template/launch_c.json +50 -0
- s/build/lib/iotedgedev/template/launch_csharp.json +43 -0
- s/build/lib/iotedgedev/template/launch_java.json +27 -0
- s/build/lib/iotedgedev/template/launch_node.json +36 -0
- s/build/lib/iotedgedev/template/launch_python.json +22 -0
- s/build/lib/iotedgedev/utility.py +179 -0
- s/build/lib/iotedgedev/version.py +4 -0
- s/build.sh +150 -0
- s/cleanup.sh +7 -0
- s/dist/iotedgedev-3.3.4-py3-none-any.whl +0 -0
- s/docker/tool/build-docker.sh +168 -0
- s/docker/tool/deps.txt +7 -0
- s/docker/tool/linux/Dockerfile +7 -0
- s/docker/tool/linux/Dockerfile.base +61 -0
- s/docker/tool/linux/install-dev.sh +8 -0
- s/docker/tool/linux/run.ps1 +5 -0
- s/docker/tool/linux/run.sh +5 -0
- s/docker/tool/push-docker.sh +35 -0
- s/docker/tool/windows/Dockerfile +4 -0
- s/docker/tool/windows/Dockerfile.base +63 -0
- s/docker/tool/windows/install-dev.bat +4 -0
- s/docker/tool/windows/run.ps1 +30 -0
- s/docs/Azure-Setup.md +102 -0
- s/docs/Command-List.md +564 -0
- s/docs/Command-Tips.md +51 -0
- s/docs/Edge-Device-Setup.md +23 -0
- s/docs/Home.md +10 -0
- s/docs/Manual-Dev-Machine-Setup.md +77 -0
- s/docs/Migration-Guides.md +131 -0
- s/docs/Overview.md +66 -0
- s/docs/Python-Virtual-Environment-Setup.md +35 -0
- s/docs/Quickstart-with-IoT-Edge-Dev-Container.md +145 -0
- s/docs/Test-Coverage.md +7 -0
- s/docs/Troubleshooting.md +21 -0
- s/docs/Usage.md +123 -0
- s/docs/_Sidebar.md +13 -0
- s/iotedgedev/__init__.py +6 -0
- s/iotedgedev/args.py +18 -0
- s/iotedgedev/azurecli.py +542 -0
- s/iotedgedev/buildoptionsparser.py +151 -0
- s/iotedgedev/buildprofile.py +5 -0
- s/iotedgedev/cli.py +830 -0
- s/iotedgedev/connectionstring.py +58 -0
- s/iotedgedev/constants.py +12 -0
- s/iotedgedev/containerregistry.py +5 -0
- s/iotedgedev/decorators.py +116 -0
- s/iotedgedev/deploymentmanifest.py +313 -0
- s/iotedgedev/dockercls.py +255 -0
- s/iotedgedev/dotnet.py +31 -0
- s/iotedgedev/edge.py +33 -0
- s/iotedgedev/envvars.py +240 -0
- s/iotedgedev/iothub.py +65 -0
- s/iotedgedev/module.py +62 -0
- s/iotedgedev/modules.py +332 -0
- s/iotedgedev/organizedgroup.py +48 -0
- s/iotedgedev/output.py +57 -0
- s/iotedgedev/simulator.py +58 -0
- s/iotedgedev/solution.py +48 -0
- s/iotedgedev/telemetry.py +155 -0
- s/iotedgedev/telemetryconfig.py +85 -0
- s/iotedgedev/telemetryuploader.py +53 -0
- s/iotedgedev/template/.env.tmp +87 -0
- s/iotedgedev/template/.gitignore +10 -0
- s/iotedgedev/template/deployment.template.json +79 -0
- s/iotedgedev/template/launch_c.json +50 -0
- s/iotedgedev/template/launch_csharp.json +43 -0
- s/iotedgedev/template/launch_java.json +27 -0
- s/iotedgedev/template/launch_node.json +36 -0
- s/iotedgedev/template/launch_python.json +22 -0
- s/iotedgedev/utility.py +179 -0
- s/iotedgedev/version.py +4 -0
- s/iotedgedev.egg-info/PKG-INFO +21 -0
- s/iotedgedev.egg-info/SOURCES.txt +100 -0
- s/iotedgedev.egg-info/dependency_links.txt +1 -0
- s/iotedgedev.egg-info/entry_points.txt +3 -0
- s/iotedgedev.egg-info/not-zip-safe +1 -0
- s/iotedgedev.egg-info/requires.txt +18 -0
- s/iotedgedev.egg-info/top_level.txt +1 -0
- s/pull_request_template.md +9 -0
- s/pytest.ini +4 -0
- s/requirements.txt +12 -0
- s/requirements_dev.txt +17 -0
- s/scripts/gen-help-markdown.bat +177 -0
- s/scripts/install-pip.sh +1 -0
- s/scripts/setup-wsl.sh +7 -0
- s/setup.cfg +18 -0
- s/setup.py +66 -0
- s/tests/__init__.py +5 -0
- s/tests/assets/deployment.manifest_invalid.json +106 -0
- s/tests/assets/deployment.manifest_invalid_createoptions.json +102 -0
- s/tests/assets/deployment.manifest_invalid_schema.json +69 -0
- s/tests/assets/deployment.template.non_str_placeholder.json +79 -0
- s/tests/assets/deployment.template_1.json +163 -0
- s/tests/assets/deployment.template_2.json +163 -0
- s/tests/assets/deployment.template_3.json +174 -0
- s/tests/assets/deployment.template_4.json +79 -0
- s/tests/assets/deployment.template_invalidresult.json +183 -0
- s/tests/assets/deployment.template_without_schema_template.json +99 -0
- s/tests/assets/launch.json +225 -0
- s/tests/assets/launch_without_nodejs.json +194 -0
- s/tests/assets/test_solution_shared_lib/.gitignore +2 -0
- s/tests/assets/test_solution_shared_lib/.vscode/launch.json +43 -0
- s/tests/assets/test_solution_shared_lib/deployment.debug.template.json +100 -0
- s/tests/assets/test_solution_shared_lib/deployment.escapedpath.template.json +90 -0
- s/tests/assets/test_solution_shared_lib/deployment.template.json +106 -0
- s/tests/assets/test_solution_shared_lib/layered_deployment.flattened_props.template.json +21 -0
- s/tests/assets/test_solution_shared_lib/layered_deployment.no_modules.template.json +12 -0
- s/tests/assets/test_solution_shared_lib/libs/sharedlib/Class1.cs +12 -0
- s/tests/assets/test_solution_shared_lib/libs/sharedlib/sharedlib.csproj +7 -0
- s/tests/assets/test_solution_shared_lib/modules/non_module_project/placeholder.txt +0 -0
- s/tests/assets/test_solution_shared_lib/modules/sample_module/.gitignore +34 -0
- s/tests/assets/test_solution_shared_lib/modules/sample_module/Dockerfile.amd64 +18 -0
- s/tests/assets/test_solution_shared_lib/modules/sample_module/Dockerfile.amd64.debug +25 -0
- s/tests/assets/test_solution_shared_lib/modules/sample_module/Dockerfile.windows-amd64 +15 -0
- s/tests/assets/test_solution_shared_lib/modules/sample_module/Program.cs +91 -0
- s/tests/assets/test_solution_shared_lib/modules/sample_module/module.json +18 -0
- s/tests/assets/test_solution_shared_lib/modules/sample_module/sample_module.csproj +26 -0
- s/tests/assets/test_solution_shared_lib/sample_module_2/.gitignore +34 -0
- s/tests/assets/test_solution_shared_lib/sample_module_2/Dockerfile.amd64 +18 -0
- s/tests/assets/test_solution_shared_lib/sample_module_2/Dockerfile.amd64.debug +25 -0
- s/tests/assets/test_solution_shared_lib/sample_module_2/Dockerfile.windows-amd64 +15 -0
- s/tests/assets/test_solution_shared_lib/sample_module_2/Program.cs +91 -0
- s/tests/assets/test_solution_shared_lib/sample_module_2/module.json +18 -0
- s/tests/assets/test_solution_shared_lib/sample_module_2/sample_module_2.csproj +26 -0
- s/tests/test_azurecli.py +33 -0
- s/tests/test_buildoptionsparser.py +155 -0
- s/tests/test_cli.py +55 -0
- s/tests/test_config.py +28 -0
- s/tests/test_connectionstring.py +87 -0
- s/tests/test_decorators.py +44 -0
- s/tests/test_deploymentmanifest.py +106 -0
- s/tests/test_envvars.py +169 -0
- s/tests/test_iotedgedev_iothub_deploy.py +199 -0
- s/tests/test_iotedgedev_simulator.py +133 -0
- s/tests/test_iotedgedev_solution.py +457 -0
- s/tests/test_iotedgedev_solution_build.py +246 -0
- s/tests/test_iotedgedev_solution_tag.py +99 -0
- s/tests/test_utility.py +132 -0
- s/tests/utility.py +132 -0
- s/tox.ini +29 -0
- s/vsts_ci/.vsts-ci.yml +93 -0
- s/vsts_ci/darwin/continuous-build-darwin.yml +54 -0
- s/vsts_ci/linux/continuous-build-linux.yml +28 -0
- s/vsts_ci/win32/continuous-build-win32.yml +26 -0
s/iotedgedev/module.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from .utility import Utility
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Module(object):
|
|
8
|
+
def __init__(self, envvars, utility, module_dir):
|
|
9
|
+
self.utility = utility
|
|
10
|
+
self.module_dir = module_dir
|
|
11
|
+
self.module_json_file = os.path.join(self.module_dir, "module.json")
|
|
12
|
+
self.file_json_content = None
|
|
13
|
+
self.load_module_json()
|
|
14
|
+
|
|
15
|
+
def load_module_json(self):
|
|
16
|
+
if os.path.exists(self.module_json_file):
|
|
17
|
+
try:
|
|
18
|
+
self.file_json_content = json.loads(Utility.get_file_contents(self.module_json_file, expandvars=True))
|
|
19
|
+
except KeyError as e:
|
|
20
|
+
raise KeyError("Error parsing {0} from module.json file : {1}".format(str(e), self.module_json_file))
|
|
21
|
+
except IOError:
|
|
22
|
+
raise IOError("Error loading module.json file : {0}".format(self.module_json_file))
|
|
23
|
+
else:
|
|
24
|
+
raise FileNotFoundError("No module.json file found. module.json file is required in the root of your module folder")
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def platforms(self):
|
|
28
|
+
return self.file_json_content.get("image", {}).get("tag", {}).get("platforms", "")
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def tag_version(self):
|
|
32
|
+
tag = self.file_json_content.get("image", {}).get("tag", {}).get("version", "0.0.0")
|
|
33
|
+
|
|
34
|
+
return tag
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def repository(self):
|
|
38
|
+
return self.file_json_content.get("image", {}).get("repository", "")
|
|
39
|
+
|
|
40
|
+
@repository.setter
|
|
41
|
+
def repository(self, repo):
|
|
42
|
+
self.utility.nested_set(self.file_json_content, ["image", "repository"], repo)
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def build_options(self):
|
|
46
|
+
return self.file_json_content.get("image", {}).get("buildOptions", [])
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def context_path(self):
|
|
50
|
+
context_path = self.file_json_content.get("image", {}).get("contextPath", ".")
|
|
51
|
+
return os.path.abspath(os.path.join(self.module_dir, context_path))
|
|
52
|
+
|
|
53
|
+
def get_dockerfile_by_platform(self, platform):
|
|
54
|
+
platforms = self.file_json_content.get("image", {}).get("tag", {}).get("platforms", {})
|
|
55
|
+
if platform not in platforms:
|
|
56
|
+
raise KeyError("Dockerfile for {0} is not defined in {1}", platform, self.module_json_file)
|
|
57
|
+
|
|
58
|
+
return os.path.abspath(os.path.join(self.module_dir, platforms.get(platform)))
|
|
59
|
+
|
|
60
|
+
def dump(self):
|
|
61
|
+
with open(self.module_json_file, "w") as f:
|
|
62
|
+
json.dump(self.file_json_content, f, indent=2)
|
s/iotedgedev/modules.py
ADDED
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import re
|
|
3
|
+
import shutil
|
|
4
|
+
import sys
|
|
5
|
+
from io import BytesIO
|
|
6
|
+
from urllib.request import urlopen
|
|
7
|
+
from zipfile import ZipFile
|
|
8
|
+
|
|
9
|
+
import commentjson
|
|
10
|
+
|
|
11
|
+
from iotedgedev.envvars import EnvVars
|
|
12
|
+
from iotedgedev.output import Output
|
|
13
|
+
|
|
14
|
+
from . import telemetry
|
|
15
|
+
from .buildoptionsparser import BuildOptionsParser
|
|
16
|
+
from .buildprofile import BuildProfile
|
|
17
|
+
from .constants import Constants
|
|
18
|
+
from .deploymentmanifest import DeploymentManifest
|
|
19
|
+
from .dockercls import Docker
|
|
20
|
+
from .dotnet import DotNet
|
|
21
|
+
from .module import Module
|
|
22
|
+
from .utility import Utility
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Modules:
|
|
26
|
+
def __init__(self, envvars: EnvVars, output: Output):
|
|
27
|
+
self.envvars = envvars
|
|
28
|
+
self.output = output
|
|
29
|
+
self.utility = Utility(self.envvars, self.output)
|
|
30
|
+
|
|
31
|
+
def add(self, name, template, group_id):
|
|
32
|
+
self.output.header("ADDING MODULE {0}".format(name))
|
|
33
|
+
|
|
34
|
+
deployment_manifest = DeploymentManifest(self.envvars, self.output, self.utility, self.envvars.DEPLOYMENT_CONFIG_TEMPLATE_FILE, True)
|
|
35
|
+
|
|
36
|
+
cwd = self.envvars.MODULES_PATH
|
|
37
|
+
self.utility.ensure_dir(cwd)
|
|
38
|
+
|
|
39
|
+
if name.startswith("_") or name.endswith("_"):
|
|
40
|
+
raise ValueError("Module name cannot start or end with the symbol _")
|
|
41
|
+
elif not re.match("^[a-zA-Z0-9_]+$", name):
|
|
42
|
+
raise ValueError("Module name can only contain alphanumeric characters and the symbol _")
|
|
43
|
+
elif os.path.exists(os.path.join(cwd, name)):
|
|
44
|
+
raise ValueError("Module \"{0}\" already exists under {1}".format(name, os.path.abspath(self.envvars.MODULES_PATH)))
|
|
45
|
+
|
|
46
|
+
telemetry.add_extra_props({"template": template})
|
|
47
|
+
|
|
48
|
+
repo = "{0}/{1}".format("${CONTAINER_REGISTRY_SERVER}", name.lower())
|
|
49
|
+
if template == "c":
|
|
50
|
+
github_prefix = "https://github.com/Azure"
|
|
51
|
+
git_repo = "azure-iot-edge-c-module"
|
|
52
|
+
branch = "master"
|
|
53
|
+
url = "{0}/{1}/archive/{2}.zip".format(github_prefix, git_repo, branch)
|
|
54
|
+
response = urlopen(url)
|
|
55
|
+
|
|
56
|
+
temp_dir = os.path.join(os.path.expanduser("~"), '.iotedgedev')
|
|
57
|
+
self.utility.ensure_dir(temp_dir)
|
|
58
|
+
zip_file_prefix = "{0}-{1}".format(git_repo, branch)
|
|
59
|
+
temp_template_dir = os.path.join(temp_dir, zip_file_prefix)
|
|
60
|
+
if os.path.exists(temp_template_dir):
|
|
61
|
+
shutil.rmtree(temp_template_dir)
|
|
62
|
+
|
|
63
|
+
with ZipFile(BytesIO(response.read())) as zip_f:
|
|
64
|
+
zip_f.extractall(temp_dir)
|
|
65
|
+
|
|
66
|
+
shutil.move(temp_template_dir, os.path.join(cwd, name))
|
|
67
|
+
|
|
68
|
+
module = Module(self.envvars, self.utility, os.path.join(cwd, name))
|
|
69
|
+
module.repository = repo
|
|
70
|
+
module.dump()
|
|
71
|
+
elif template == "csharp":
|
|
72
|
+
dotnet = DotNet(self.output, self.utility)
|
|
73
|
+
dotnet.install_module_template()
|
|
74
|
+
dotnet.create_custom_module(name, repo, cwd)
|
|
75
|
+
elif template == "java":
|
|
76
|
+
self.utility.check_dependency("mvn --help".split(), "To add new Java modules, the Maven tool")
|
|
77
|
+
cmd = ["mvn",
|
|
78
|
+
"archetype:generate",
|
|
79
|
+
"-DarchetypeGroupId=com.microsoft.azure",
|
|
80
|
+
"-DarchetypeArtifactId=azure-iot-edge-archetype",
|
|
81
|
+
"-DgroupId={0}".format(group_id),
|
|
82
|
+
"-DartifactId={0}".format(name),
|
|
83
|
+
"-Dversion=1.0.0-SNAPSHOT",
|
|
84
|
+
"-Dpackage={0}".format(group_id),
|
|
85
|
+
"-Drepository={0}".format(repo),
|
|
86
|
+
"-B"]
|
|
87
|
+
|
|
88
|
+
self.output.header(" ".join(cmd))
|
|
89
|
+
self.utility.exe_proc(cmd, shell=not self.envvars.is_posix(), cwd=cwd)
|
|
90
|
+
elif template == "nodejs":
|
|
91
|
+
self.utility.check_dependency("yo azure-iot-edge-module --help".split(), "To add new Node.js modules, the Yeoman tool and Azure IoT Edge Node.js module generator")
|
|
92
|
+
cmd = "yo azure-iot-edge-module -n {0} -r {1}".format(name, repo)
|
|
93
|
+
self.output.header(cmd)
|
|
94
|
+
self.utility.exe_proc(cmd.split(), shell=not self.envvars.is_posix(), cwd=cwd)
|
|
95
|
+
elif template == "python":
|
|
96
|
+
self.utility.check_dependency("cookiecutter --help".split(), "To add new Python modules, the Cookiecutter tool")
|
|
97
|
+
github_source = "https://github.com/Azure/cookiecutter-azure-iot-edge-module"
|
|
98
|
+
branch = "master"
|
|
99
|
+
cmd = "cookiecutter --no-input {0} module_name={1} image_repository={2} --checkout {3}".format(github_source, name, repo, branch)
|
|
100
|
+
self.output.header(cmd)
|
|
101
|
+
self.utility.exe_proc(cmd.split(), cwd=cwd)
|
|
102
|
+
elif template == "csharpfunction":
|
|
103
|
+
dotnet = DotNet(self.output, self.utility)
|
|
104
|
+
dotnet.install_function_template()
|
|
105
|
+
dotnet.create_function_module(name, repo, cwd)
|
|
106
|
+
|
|
107
|
+
deployment_manifest.add_module_template(name)
|
|
108
|
+
deployment_manifest.dump()
|
|
109
|
+
|
|
110
|
+
debug_create_options = self._get_debug_create_options(template)
|
|
111
|
+
if os.path.exists(self.envvars.DEPLOYMENT_CONFIG_DEBUG_TEMPLATE_FILE):
|
|
112
|
+
deployment_manifest_debug = DeploymentManifest(self.envvars, self.output, self.utility, self.envvars.DEPLOYMENT_CONFIG_DEBUG_TEMPLATE_FILE, True)
|
|
113
|
+
deployment_manifest_debug.add_module_template(name, debug_create_options, True)
|
|
114
|
+
deployment_manifest_debug.dump()
|
|
115
|
+
|
|
116
|
+
self._update_launch_json(name, template, group_id)
|
|
117
|
+
|
|
118
|
+
self.output.footer("ADD COMPLETE")
|
|
119
|
+
|
|
120
|
+
def build(self, template_file, platform):
|
|
121
|
+
return self.build_push(template_file, platform, no_push=True)
|
|
122
|
+
|
|
123
|
+
def push(self, template_file, platform, no_build=False):
|
|
124
|
+
return self.build_push(template_file, platform, no_build=no_build)
|
|
125
|
+
|
|
126
|
+
def build_push(self, template_file, default_platform, no_build=False, no_push=False, fail_on_validation_error=False):
|
|
127
|
+
self.output.header("BUILDING MODULES", suppress=no_build)
|
|
128
|
+
|
|
129
|
+
template_file_folder = os.path.dirname(template_file)
|
|
130
|
+
bypass_modules = self.utility.get_bypass_modules()
|
|
131
|
+
validation_success = True
|
|
132
|
+
|
|
133
|
+
# map image placeholder to tag.
|
|
134
|
+
# sample: ('${MODULES.filtermodule.amd64}', 'localhost:5000/filtermodule:0.0.1-amd64')
|
|
135
|
+
placeholder_tag_map = {}
|
|
136
|
+
# map image tag to BuildProfile object
|
|
137
|
+
tag_build_profile_map = {}
|
|
138
|
+
# image tags to build
|
|
139
|
+
# sample: 'localhost:5000/filtermodule:0.0.1-amd64'
|
|
140
|
+
tags_to_build = set()
|
|
141
|
+
|
|
142
|
+
deployment_manifest = DeploymentManifest(self.envvars, self.output, self.utility, template_file, True, True)
|
|
143
|
+
|
|
144
|
+
# get image tags for ${MODULES.modulename.xxx} placeholder
|
|
145
|
+
modules_path = os.path.join(template_file_folder, self.envvars.MODULES_PATH)
|
|
146
|
+
if os.path.isdir(modules_path):
|
|
147
|
+
for folder_name in os.listdir(modules_path):
|
|
148
|
+
project_folder = os.path.join(modules_path, folder_name)
|
|
149
|
+
if os.path.exists(os.path.join(project_folder, "module.json")):
|
|
150
|
+
module = Module(self.envvars, self.utility, project_folder)
|
|
151
|
+
self._update_module_maps("MODULES.{0}".format(folder_name), module, placeholder_tag_map, tag_build_profile_map, default_platform)
|
|
152
|
+
|
|
153
|
+
# get image tags for ${MODULEDIR<relative path>.xxx} placeholder
|
|
154
|
+
user_modules = deployment_manifest.get_user_modules()
|
|
155
|
+
for module_name, module_info in user_modules.items():
|
|
156
|
+
image = module_info["settings"]["image"]
|
|
157
|
+
match_result = re.search(Constants.moduledir_placeholder_pattern, image)
|
|
158
|
+
if (match_result is not None):
|
|
159
|
+
module_dir = match_result.group(1)
|
|
160
|
+
module = Module(self.envvars, self.utility, os.path.join(template_file_folder, module_dir))
|
|
161
|
+
self._update_module_maps("MODULEDIR<{0}>".format(module_dir), module, placeholder_tag_map, tag_build_profile_map, default_platform)
|
|
162
|
+
|
|
163
|
+
replacements = {}
|
|
164
|
+
for module_name, module_info in user_modules.items():
|
|
165
|
+
image = module_info["settings"]["image"]
|
|
166
|
+
if image in placeholder_tag_map:
|
|
167
|
+
tag = placeholder_tag_map[image]
|
|
168
|
+
replacements[module_name] = tag
|
|
169
|
+
if not self.utility.in_asterisk_list(module_name, bypass_modules):
|
|
170
|
+
tags_to_build.add(tag)
|
|
171
|
+
|
|
172
|
+
if not no_build or not no_push:
|
|
173
|
+
docker = Docker(self.envvars, self.utility, self.output)
|
|
174
|
+
for tag in tags_to_build:
|
|
175
|
+
if tag in tag_build_profile_map:
|
|
176
|
+
# BUILD DOCKER IMAGE
|
|
177
|
+
if not no_build:
|
|
178
|
+
build_profile = tag_build_profile_map.get(tag)
|
|
179
|
+
|
|
180
|
+
dockerfile = build_profile.dockerfile
|
|
181
|
+
self.output.info("PROCESSING DOCKERFILE: {0}".format(dockerfile))
|
|
182
|
+
self.output.info("BUILDING DOCKER IMAGE: {0}".format(tag))
|
|
183
|
+
|
|
184
|
+
build_options = build_profile.extra_options
|
|
185
|
+
build_options_parser = BuildOptionsParser(build_options)
|
|
186
|
+
sdk_options = build_options_parser.parse_build_options()
|
|
187
|
+
|
|
188
|
+
context_path = build_profile.context_path
|
|
189
|
+
|
|
190
|
+
# A hack to work around Python Docker SDK's bug with Linux container mode on Windows
|
|
191
|
+
# https://github.com/docker/docker-py/issues/2127
|
|
192
|
+
dockerfile_relative = os.path.relpath(dockerfile, context_path)
|
|
193
|
+
if docker.get_os_type() == "linux" and sys.platform == "win32":
|
|
194
|
+
dockerfile_relative = dockerfile_relative.replace("\\", "/")
|
|
195
|
+
|
|
196
|
+
build_args = {"tag": tag, "path": context_path, "dockerfile": dockerfile_relative}
|
|
197
|
+
build_args.update(sdk_options)
|
|
198
|
+
|
|
199
|
+
response = docker.docker_api.build(**build_args)
|
|
200
|
+
docker.process_api_response(response)
|
|
201
|
+
self.output.footer("BUILD COMPLETE")
|
|
202
|
+
if not no_push:
|
|
203
|
+
docker.init_registry()
|
|
204
|
+
|
|
205
|
+
# PUSH TO CONTAINER REGISTRY
|
|
206
|
+
self.output.info("PUSHING DOCKER IMAGE: " + tag)
|
|
207
|
+
registry_key = None
|
|
208
|
+
for key, registry in self.envvars.CONTAINER_REGISTRY_MAP.items():
|
|
209
|
+
# Split the repository tag in the module.json (ex: Localhost:5000/filtermodule)
|
|
210
|
+
if registry.server.lower() == tag.split('/')[0].lower():
|
|
211
|
+
registry_key = key
|
|
212
|
+
break
|
|
213
|
+
if registry_key is None:
|
|
214
|
+
self.output.info("Could not find registry credentials with name {0} in environment variable. Pushing anonymously.".format(tag.split('/')[0].lower()))
|
|
215
|
+
response = docker.docker_client.images.push(repository=tag, stream=True)
|
|
216
|
+
else:
|
|
217
|
+
response = docker.docker_client.images.push(repository=tag, stream=True, auth_config={
|
|
218
|
+
"username": self.envvars.CONTAINER_REGISTRY_MAP[registry_key].username,
|
|
219
|
+
"password": self.envvars.CONTAINER_REGISTRY_MAP[registry_key].password})
|
|
220
|
+
docker.process_api_response(response)
|
|
221
|
+
self.output.footer("PUSH COMPLETE")
|
|
222
|
+
|
|
223
|
+
self.output.info("Expanding image placeholders")
|
|
224
|
+
deployment_manifest.expand_image_placeholders(replacements)
|
|
225
|
+
self.output.info("Converting createOptions")
|
|
226
|
+
deployment_manifest.convert_create_options()
|
|
227
|
+
self.output.info("Deleting template schema version")
|
|
228
|
+
template_schema_ver = deployment_manifest.get_template_schema_ver()
|
|
229
|
+
deployment_manifest.del_key(["$schema-template"])
|
|
230
|
+
|
|
231
|
+
self.utility.ensure_dir(self.envvars.CONFIG_OUTPUT_DIR)
|
|
232
|
+
gen_deployment_manifest_name = Utility.get_deployment_manifest_name(template_file, template_schema_ver, default_platform)
|
|
233
|
+
gen_deployment_manifest_path = os.path.join(self.envvars.CONFIG_OUTPUT_DIR, gen_deployment_manifest_name)
|
|
234
|
+
|
|
235
|
+
self.output.info("Expanding '{0}' to '{1}'".format(os.path.basename(template_file), gen_deployment_manifest_path))
|
|
236
|
+
deployment_manifest.dump(gen_deployment_manifest_path)
|
|
237
|
+
|
|
238
|
+
self.output.info("Validating generated deployment manifest %s" % gen_deployment_manifest_path)
|
|
239
|
+
validation_success = deployment_manifest.validate_deployment_manifest()
|
|
240
|
+
|
|
241
|
+
if fail_on_validation_error and not validation_success:
|
|
242
|
+
raise Exception("Deployment manifest validation failed. Please see previous logs for more details.")
|
|
243
|
+
|
|
244
|
+
return gen_deployment_manifest_path
|
|
245
|
+
|
|
246
|
+
def _update_module_maps(self, placeholder_base, module, placeholder_tag_map, tag_build_profile_map, default_platform):
|
|
247
|
+
try:
|
|
248
|
+
for platform in module.platforms:
|
|
249
|
+
# get the Dockerfile from module.json
|
|
250
|
+
dockerfile = module.get_dockerfile_by_platform(platform)
|
|
251
|
+
container_tag = "" if self.envvars.CONTAINER_TAG == "" else "-" + self.envvars.CONTAINER_TAG
|
|
252
|
+
tag = "{0}:{1}{2}-{3}".format(module.repository.lower(), module.tag_version, container_tag, platform)
|
|
253
|
+
placeholder_tag_map["${{{0}.{1}}}".format(placeholder_base, platform)] = tag
|
|
254
|
+
placeholder_tag_map[tag] = tag
|
|
255
|
+
|
|
256
|
+
if platform == default_platform:
|
|
257
|
+
placeholder_tag_map["${{{0}}}".format(placeholder_base)] = tag
|
|
258
|
+
elif platform == default_platform + ".debug":
|
|
259
|
+
placeholder_tag_map["${{{0}.{1}}}".format(placeholder_base, "debug")] = tag
|
|
260
|
+
|
|
261
|
+
tag_build_profile_map[tag] = BuildProfile(dockerfile, module.context_path, module.build_options)
|
|
262
|
+
except FileNotFoundError:
|
|
263
|
+
pass
|
|
264
|
+
|
|
265
|
+
def _get_debug_create_options(self, template):
|
|
266
|
+
if template == "c":
|
|
267
|
+
return {"HostConfig": {"Privileged": True}}
|
|
268
|
+
elif template == "java":
|
|
269
|
+
return {"HostConfig": {"PortBindings": {"5005/tcp": [{"HostPort": "5005"}]}}}
|
|
270
|
+
elif template == "nodejs":
|
|
271
|
+
return {"ExposedPorts": {"9229/tcp": {}},
|
|
272
|
+
"HostConfig": {"PortBindings": {"9229/tcp": [{"HostPort": "9229"}]}}}
|
|
273
|
+
elif template == "python":
|
|
274
|
+
return {"ExposedPorts": {"5678/tcp": {}},
|
|
275
|
+
"HostConfig": {"PortBindings": {"5678/tcp": [{"HostPort": "5678"}]}}}
|
|
276
|
+
else:
|
|
277
|
+
return {}
|
|
278
|
+
|
|
279
|
+
def _update_launch_json(self, name, template, group_id):
|
|
280
|
+
new_launch_json = self._get_launch_json(name, template, group_id)
|
|
281
|
+
if new_launch_json is not None:
|
|
282
|
+
self._merge_launch_json(new_launch_json)
|
|
283
|
+
|
|
284
|
+
def _get_launch_json(self, name, template, group_id):
|
|
285
|
+
replacements = {}
|
|
286
|
+
replacements["%MODULE%"] = name
|
|
287
|
+
replacements["%MODULE_FOLDER%"] = name
|
|
288
|
+
|
|
289
|
+
launch_json_file = None
|
|
290
|
+
is_function = False
|
|
291
|
+
if template == "c":
|
|
292
|
+
launch_json_file = "launch_c.json"
|
|
293
|
+
replacements["%APP_FOLDER%"] = "/app"
|
|
294
|
+
elif template == "csharp":
|
|
295
|
+
launch_json_file = "launch_csharp.json"
|
|
296
|
+
replacements["%APP_FOLDER%"] = "/app"
|
|
297
|
+
elif template == "java":
|
|
298
|
+
launch_json_file = "launch_java.json"
|
|
299
|
+
replacements["%GROUP_ID%"] = group_id
|
|
300
|
+
elif template == "nodejs":
|
|
301
|
+
launch_json_file = "launch_node.json"
|
|
302
|
+
elif template == "csharpfunction":
|
|
303
|
+
launch_json_file = "launch_csharp.json"
|
|
304
|
+
replacements["%APP_FOLDER%"] = "/app"
|
|
305
|
+
is_function = True
|
|
306
|
+
elif template == "python":
|
|
307
|
+
launch_json_file = "launch_python.json"
|
|
308
|
+
replacements["%APP_FOLDER%"] = "/app"
|
|
309
|
+
|
|
310
|
+
if launch_json_file is not None:
|
|
311
|
+
launch_json_file = os.path.join(os.path.split(__file__)[0], "template", launch_json_file)
|
|
312
|
+
launch_json_content = Utility.get_file_contents(launch_json_file)
|
|
313
|
+
for key, value in replacements.items():
|
|
314
|
+
launch_json_content = launch_json_content.replace(key, value)
|
|
315
|
+
launch_json = commentjson.loads(launch_json_content)
|
|
316
|
+
if is_function and launch_json is not None and "configurations" in launch_json:
|
|
317
|
+
# for Function modules, there shouldn't be launch config for local debug
|
|
318
|
+
launch_json["configurations"] = list(filter(lambda x: x["request"] != "launch", launch_json["configurations"]))
|
|
319
|
+
return launch_json
|
|
320
|
+
|
|
321
|
+
def _merge_launch_json(self, new_launch_json):
|
|
322
|
+
vscode_dir = os.path.join(os.getcwd(), ".vscode")
|
|
323
|
+
self.utility.ensure_dir(vscode_dir)
|
|
324
|
+
launch_json_file = os.path.join(vscode_dir, "launch.json")
|
|
325
|
+
if os.path.exists(launch_json_file):
|
|
326
|
+
launch_json = commentjson.loads(Utility.get_file_contents(launch_json_file))
|
|
327
|
+
launch_json['configurations'].extend(new_launch_json['configurations'])
|
|
328
|
+
with open(launch_json_file, "w") as f:
|
|
329
|
+
commentjson.dump(launch_json, f, indent=2)
|
|
330
|
+
else:
|
|
331
|
+
with open(launch_json_file, "w") as f:
|
|
332
|
+
commentjson.dump(new_launch_json, f, indent=2)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class OrganizedGroup(click.Group):
|
|
5
|
+
"""A subclass of click.Group which allows specifying an `order` parameter (0 by default) to sort the commands and groups"""
|
|
6
|
+
|
|
7
|
+
def __init__(self, *args, **kwargs):
|
|
8
|
+
self.orders = {}
|
|
9
|
+
super(OrganizedGroup, self).__init__(*args, **kwargs)
|
|
10
|
+
|
|
11
|
+
def get_help(self, ctx):
|
|
12
|
+
self.list_commands = self.list_commands_for_help
|
|
13
|
+
return super(OrganizedGroup, self).get_help(ctx)
|
|
14
|
+
|
|
15
|
+
def list_commands_for_help(self, ctx):
|
|
16
|
+
"""reorder the list of commands when listing the help"""
|
|
17
|
+
commands = super(OrganizedGroup, self).list_commands(ctx)
|
|
18
|
+
return (c[1] for c in sorted(
|
|
19
|
+
(self.orders.get(command, 0), command)
|
|
20
|
+
for command in commands))
|
|
21
|
+
|
|
22
|
+
def command(self, *args, **kwargs):
|
|
23
|
+
"""Behaves the same as `click.Group.command()` except capture
|
|
24
|
+
a priority for listing command names in help.
|
|
25
|
+
"""
|
|
26
|
+
order = kwargs.pop('order', 0)
|
|
27
|
+
orders = self.orders
|
|
28
|
+
|
|
29
|
+
def decorator(f):
|
|
30
|
+
cmd = super(OrganizedGroup, self).command(*args, **kwargs)(f)
|
|
31
|
+
orders[cmd.name] = order
|
|
32
|
+
return cmd
|
|
33
|
+
|
|
34
|
+
return decorator
|
|
35
|
+
|
|
36
|
+
def group(self, *args, **kwargs):
|
|
37
|
+
"""Behaves the same as `click.Group.group()` except capture
|
|
38
|
+
a priority for listing command names in help.
|
|
39
|
+
"""
|
|
40
|
+
order = kwargs.pop('order', 0)
|
|
41
|
+
orders = self.orders
|
|
42
|
+
|
|
43
|
+
def decorator(f):
|
|
44
|
+
cmd = super(OrganizedGroup, self).group(*args, **kwargs)(f)
|
|
45
|
+
orders[cmd.name] = order
|
|
46
|
+
return cmd
|
|
47
|
+
|
|
48
|
+
return decorator
|
s/iotedgedev/output.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Output:
|
|
5
|
+
|
|
6
|
+
def info(self, text, suppress=False):
|
|
7
|
+
if not suppress:
|
|
8
|
+
self.echo(text, color='yellow')
|
|
9
|
+
|
|
10
|
+
def warning(self, text):
|
|
11
|
+
self.echo("Warning: %s" % text, color='yellow')
|
|
12
|
+
|
|
13
|
+
def status(self, text):
|
|
14
|
+
self.info(text)
|
|
15
|
+
self.line()
|
|
16
|
+
|
|
17
|
+
def prompt(self, text):
|
|
18
|
+
self.echo(text, color='white')
|
|
19
|
+
|
|
20
|
+
def error(self, text):
|
|
21
|
+
self.echo("ERROR: " + text, color='red', err=True)
|
|
22
|
+
|
|
23
|
+
def header(self, text, suppress=False):
|
|
24
|
+
|
|
25
|
+
if not suppress:
|
|
26
|
+
self.line()
|
|
27
|
+
s = "======== {0} ========".format(text).upper()
|
|
28
|
+
m = "="*len(s)
|
|
29
|
+
self.echo(m, color='white')
|
|
30
|
+
self.echo(s, color='white')
|
|
31
|
+
self.echo(m, color='white')
|
|
32
|
+
self.line()
|
|
33
|
+
|
|
34
|
+
def param(self, text, value, status, suppress):
|
|
35
|
+
if value and not suppress:
|
|
36
|
+
self.header("SETTING " + text)
|
|
37
|
+
self.status(status)
|
|
38
|
+
|
|
39
|
+
def footer(self, text, suppress=False):
|
|
40
|
+
if not suppress:
|
|
41
|
+
self.info(text.upper())
|
|
42
|
+
self.line()
|
|
43
|
+
|
|
44
|
+
def procout(self, text, nl=True):
|
|
45
|
+
self.echo(text, dim=True, nl=nl)
|
|
46
|
+
|
|
47
|
+
def line(self):
|
|
48
|
+
self.echo(text="")
|
|
49
|
+
|
|
50
|
+
def echo(self, text, color="", dim=False, nl=True, err=False):
|
|
51
|
+
try:
|
|
52
|
+
click.secho(text, fg=color, dim=dim, nl=nl, err=err)
|
|
53
|
+
except Exception:
|
|
54
|
+
print(text)
|
|
55
|
+
|
|
56
|
+
def confirm(self, text, default=False, abort=True):
|
|
57
|
+
return click.confirm(text, default=default, abort=abort)
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from .modules import Modules
|
|
4
|
+
from .utility import Utility
|
|
5
|
+
|
|
6
|
+
class Simulator:
|
|
7
|
+
def __init__(self, envvars, output):
|
|
8
|
+
self.envvars = envvars
|
|
9
|
+
self.output = output
|
|
10
|
+
self.utility = Utility(self.envvars, self.output)
|
|
11
|
+
|
|
12
|
+
def setup(self, gateway_host, iothub_connection_string=""):
|
|
13
|
+
self.output.header("Setting Up IoT Edge Simulator")
|
|
14
|
+
self.envvars.verify_envvar_has_val("DEVICE_CONNECTION_STRING", self.envvars.DEVICE_CONNECTION_STRING)
|
|
15
|
+
|
|
16
|
+
cmd = ["iotedgehubdev", "setup", "-c", self.envvars.DEVICE_CONNECTION_STRING]
|
|
17
|
+
if gateway_host:
|
|
18
|
+
cmd.extend(["-g", gateway_host])
|
|
19
|
+
if iothub_connection_string:
|
|
20
|
+
cmd.extend(["-i", iothub_connection_string])
|
|
21
|
+
self.utility.exe_proc(cmd)
|
|
22
|
+
|
|
23
|
+
def start_single(self, inputs, port):
|
|
24
|
+
self.output.header("Starting IoT Edge Simulator in Single Mode")
|
|
25
|
+
|
|
26
|
+
cmd = ["iotedgehubdev", "start", "-i", inputs]
|
|
27
|
+
if port:
|
|
28
|
+
cmd.extend(["-p", str(port)])
|
|
29
|
+
self.utility.exe_proc(cmd)
|
|
30
|
+
|
|
31
|
+
def start_solution(self, manifest_file, default_platform, verbose=True, build=False):
|
|
32
|
+
if build:
|
|
33
|
+
mod = Modules(self.envvars, self.output)
|
|
34
|
+
manifest_file = mod.build(manifest_file, default_platform)
|
|
35
|
+
|
|
36
|
+
if not os.path.exists(manifest_file):
|
|
37
|
+
raise FileNotFoundError("Deployment manifest {0} not found. Please build the solution before starting IoT Edge simulator.".format(self.envvars.DEPLOYMENT_CONFIG_FILE_PATH))
|
|
38
|
+
|
|
39
|
+
self.output.header("Starting IoT Edge Simulator in Solution Mode")
|
|
40
|
+
|
|
41
|
+
cmd = ["iotedgehubdev", "start", "-d", manifest_file]
|
|
42
|
+
if verbose:
|
|
43
|
+
cmd.append("-v")
|
|
44
|
+
self.utility.call_proc(cmd)
|
|
45
|
+
|
|
46
|
+
def stop(self):
|
|
47
|
+
self.output.header("Stopping IoT Edge Simulator")
|
|
48
|
+
self.utility.call_proc(["iotedgehubdev", "stop"])
|
|
49
|
+
|
|
50
|
+
def modulecred(self, local, output_file):
|
|
51
|
+
self.output.header("Getting Target Module Credentials")
|
|
52
|
+
|
|
53
|
+
cmd = ["iotedgehubdev", "modulecred"]
|
|
54
|
+
if local:
|
|
55
|
+
cmd.append("-l")
|
|
56
|
+
if output_file:
|
|
57
|
+
cmd.extend(["-o", output_file])
|
|
58
|
+
self.utility.exe_proc(cmd)
|
s/iotedgedev/solution.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from .constants import Constants
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Solution:
|
|
7
|
+
def __init__(self, output, utility):
|
|
8
|
+
self.output = output
|
|
9
|
+
self.utility = utility
|
|
10
|
+
|
|
11
|
+
def create(self, name, module, template, runtime_tag, group_id):
|
|
12
|
+
if name == ".":
|
|
13
|
+
dir_path = os.getcwd()
|
|
14
|
+
else:
|
|
15
|
+
dir_path = os.path.join(os.getcwd(), name)
|
|
16
|
+
|
|
17
|
+
if not self.utility.is_dir_empty(dir_path):
|
|
18
|
+
raise ValueError("Directory is not empty. Run `iotedgedev iothub setup` to retrieve or create required Azure resources or clean the directory.")
|
|
19
|
+
|
|
20
|
+
self.output.header("CREATING AZURE IOT EDGE SOLUTION: {0}".format(name))
|
|
21
|
+
|
|
22
|
+
self.utility.ensure_dir(dir_path)
|
|
23
|
+
|
|
24
|
+
self.utility.copy_from_template_dir(Constants.default_deployment_template_file, dir_path, replacements={"%MODULE%": module})
|
|
25
|
+
self.utility.copy_from_template_dir(Constants.default_deployment_template_file, dir_path,
|
|
26
|
+
dest_file=Constants.default_deployment_debug_template_file, replacements={"%MODULE%": module})
|
|
27
|
+
self.utility.copy_from_template_dir(".gitignore", dir_path)
|
|
28
|
+
edgeagent_schema_version = runtime_tag
|
|
29
|
+
edgehub_schema_version = runtime_tag
|
|
30
|
+
# exception for runtime version 1.2
|
|
31
|
+
if(runtime_tag == "1.2"):
|
|
32
|
+
edgeagent_schema_version = "1.1"
|
|
33
|
+
edgehub_schema_version = "1.2"
|
|
34
|
+
|
|
35
|
+
self.utility.copy_from_template_dir(".env.tmp", dir_path, dest_file=".env", replacements={
|
|
36
|
+
"%EDGE_RUNTIME_VERSION%": runtime_tag, "%EDGEAGENT_SCHEMA_VERSION%": edgeagent_schema_version, "%EDGEHUB_SCHEMA_VERSION%": edgehub_schema_version})
|
|
37
|
+
|
|
38
|
+
if template == "java":
|
|
39
|
+
mod_cmd = "iotedgedev solution add {0} --template {1} --group-id {2}".format(module, template, group_id)
|
|
40
|
+
else:
|
|
41
|
+
mod_cmd = "iotedgedev solution add {0} --template {1}".format(module, template)
|
|
42
|
+
|
|
43
|
+
self.output.header(mod_cmd)
|
|
44
|
+
self.utility.call_proc(mod_cmd.split(), cwd=name)
|
|
45
|
+
|
|
46
|
+
self.output.footer("Azure IoT Edge Solution Created")
|
|
47
|
+
if name != ".":
|
|
48
|
+
self.output.info("Execute 'cd {0}' to navigate to your new solution.".format(name))
|