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.
Files changed (356) hide show
  1. s/.devcontainer/Dockerfile +75 -0
  2. s/.devcontainer/devcontainer.json +40 -0
  3. s/.devcontainer/library-scripts/docker-in-docker-debian.sh +236 -0
  4. s/.env.tmp +108 -0
  5. s/.git/FETCH_HEAD +61 -0
  6. s/.git/HEAD +1 -0
  7. s/.git/config +12 -0
  8. s/.git/description +1 -0
  9. s/.git/hooks/applypatch-msg.sample +15 -0
  10. s/.git/hooks/commit-msg.sample +24 -0
  11. s/.git/hooks/fsmonitor-watchman.sample +173 -0
  12. s/.git/hooks/post-update.sample +8 -0
  13. s/.git/hooks/pre-applypatch.sample +14 -0
  14. s/.git/hooks/pre-commit.sample +49 -0
  15. s/.git/hooks/pre-merge-commit.sample +13 -0
  16. s/.git/hooks/pre-push.sample +53 -0
  17. s/.git/hooks/pre-rebase.sample +169 -0
  18. s/.git/hooks/pre-receive.sample +24 -0
  19. s/.git/hooks/prepare-commit-msg.sample +42 -0
  20. s/.git/hooks/push-to-checkout.sample +78 -0
  21. s/.git/hooks/update.sample +128 -0
  22. s/.git/index +0 -0
  23. s/.git/info/exclude +6 -0
  24. s/.git/logs/HEAD +1 -0
  25. s/.git/logs/refs/remotes/origin/EO +1 -0
  26. s/.git/logs/refs/remotes/origin/avagraw/security_fix +1 -0
  27. s/.git/logs/refs/remotes/origin/cherry-pick-rename +1 -0
  28. s/.git/logs/refs/remotes/origin/chyuan/fix-validation-not-fail-test +1 -0
  29. s/.git/logs/refs/remotes/origin/chyuan/update-linux-agent +1 -0
  30. s/.git/logs/refs/remotes/origin/dependabot/pip/pyyaml-5.4 +1 -0
  31. s/.git/logs/refs/remotes/origin/eliises/add-device-tagging-docs +1 -0
  32. s/.git/logs/refs/remotes/origin/main +1 -0
  33. s/.git/logs/refs/remotes/origin/ray-deps +1 -0
  34. s/.git/logs/refs/remotes/origin/ray-dev-setup +1 -0
  35. s/.git/logs/refs/remotes/origin/ray-tox +1 -0
  36. s/.git/logs/refs/remotes/origin/release-pipeline-automation +1 -0
  37. s/.git/logs/refs/remotes/origin/spmcdono/fix-pipeline-test-install +1 -0
  38. s/.git/logs/refs/remotes/origin/spmcdono/layered-deployments +1 -0
  39. s/.git/logs/refs/remotes/origin/spmcdono/pypi-package-test-pipeline +1 -0
  40. s/.git/logs/refs/remotes/origin/spmcdono/remove-py2 +1 -0
  41. s/.git/logs/refs/remotes/origin/spmcdono/remove-py2-dev +1 -0
  42. s/.git/logs/refs/remotes/origin/spmcdono/test-native-runtime-install +1 -0
  43. s/.git/logs/refs/remotes/origin/spmcdono/update-pipeline +1 -0
  44. s/.git/logs/refs/remotes/origin/user/marine/EliiseS/devcontainer +1 -0
  45. s/.git/logs/refs/remotes/origin/user/marine/fixsimver +1 -0
  46. s/.git/logs/refs/remotes/origin/user/marine/layereddeploymentsbuildtest +1 -0
  47. s/.git/logs/refs/remotes/origin/user/marine/master-to-main +1 -0
  48. s/.git/logs/refs/remotes/origin/user/marine/pipeline +1 -0
  49. s/.git/logs/refs/remotes/origin/user/vikas +1 -0
  50. s/.git/logs/refs/remotes/origin/user/vikas0212/verify-tests +1 -0
  51. s/.git/logs/refs/remotes/origin/user/vikas2 +1 -0
  52. s/.git/logs/refs/remotes/origin/user/vikas_pypi +1 -0
  53. s/.git/logs/refs/remotes/origin/user/vikas_pypi-1 +1 -0
  54. s/.git/logs/refs/remotes/origin/user/vikas_testing +1 -0
  55. s/.git/logs/refs/remotes/origin/user/xinzed/dps_conn_str +1 -0
  56. s/.git/logs/refs/remotes/origin/user/xinzed/update-python-docker +1 -0
  57. s/.git/logs/refs/remotes/origin/vikas/azure +1 -0
  58. s/.git/logs/refs/remotes/origin/vikas/dependebot +1 -0
  59. s/.git/logs/refs/remotes/origin/xinzed/3_1_0rc1 +1 -0
  60. s/.git/logs/refs/remotes/origin/xinzed/update-runtime +1 -0
  61. s/.git/objects/pack/pack-a8179feb7da51d11a42c37c5b3c8593cffe17075.idx +0 -0
  62. s/.git/objects/pack/pack-a8179feb7da51d11a42c37c5b3c8593cffe17075.pack +0 -0
  63. s/.git/refs/remotes/origin/EO +1 -0
  64. s/.git/refs/remotes/origin/avagraw/security_fix +1 -0
  65. s/.git/refs/remotes/origin/cherry-pick-rename +1 -0
  66. s/.git/refs/remotes/origin/chyuan/fix-validation-not-fail-test +1 -0
  67. s/.git/refs/remotes/origin/chyuan/update-linux-agent +1 -0
  68. s/.git/refs/remotes/origin/dependabot/pip/pyyaml-5.4 +1 -0
  69. s/.git/refs/remotes/origin/eliises/add-device-tagging-docs +1 -0
  70. s/.git/refs/remotes/origin/main +1 -0
  71. s/.git/refs/remotes/origin/ray-deps +1 -0
  72. s/.git/refs/remotes/origin/ray-dev-setup +1 -0
  73. s/.git/refs/remotes/origin/ray-tox +1 -0
  74. s/.git/refs/remotes/origin/release-pipeline-automation +1 -0
  75. s/.git/refs/remotes/origin/spmcdono/fix-pipeline-test-install +1 -0
  76. s/.git/refs/remotes/origin/spmcdono/layered-deployments +1 -0
  77. s/.git/refs/remotes/origin/spmcdono/pypi-package-test-pipeline +1 -0
  78. s/.git/refs/remotes/origin/spmcdono/remove-py2 +1 -0
  79. s/.git/refs/remotes/origin/spmcdono/remove-py2-dev +1 -0
  80. s/.git/refs/remotes/origin/spmcdono/test-native-runtime-install +1 -0
  81. s/.git/refs/remotes/origin/spmcdono/update-pipeline +1 -0
  82. s/.git/refs/remotes/origin/user/marine/EliiseS/devcontainer +1 -0
  83. s/.git/refs/remotes/origin/user/marine/fixsimver +1 -0
  84. s/.git/refs/remotes/origin/user/marine/layereddeploymentsbuildtest +1 -0
  85. s/.git/refs/remotes/origin/user/marine/master-to-main +1 -0
  86. s/.git/refs/remotes/origin/user/marine/pipeline +1 -0
  87. s/.git/refs/remotes/origin/user/vikas +1 -0
  88. s/.git/refs/remotes/origin/user/vikas0212/verify-tests +1 -0
  89. s/.git/refs/remotes/origin/user/vikas2 +1 -0
  90. s/.git/refs/remotes/origin/user/vikas_pypi +1 -0
  91. s/.git/refs/remotes/origin/user/vikas_pypi-1 +1 -0
  92. s/.git/refs/remotes/origin/user/vikas_testing +1 -0
  93. s/.git/refs/remotes/origin/user/xinzed/dps_conn_str +1 -0
  94. s/.git/refs/remotes/origin/user/xinzed/update-python-docker +1 -0
  95. s/.git/refs/remotes/origin/vikas/azure +1 -0
  96. s/.git/refs/remotes/origin/vikas/dependebot +1 -0
  97. s/.git/refs/remotes/origin/xinzed/3_1_0rc1 +1 -0
  98. s/.git/refs/remotes/origin/xinzed/update-runtime +1 -0
  99. s/.git/refs/tags/v0.49.0 +1 -0
  100. s/.git/refs/tags/v0.50.0 +1 -0
  101. s/.git/refs/tags/v0.51.0 +1 -0
  102. s/.git/refs/tags/v0.52.0 +1 -0
  103. s/.git/refs/tags/v0.53.0 +1 -0
  104. s/.git/refs/tags/v0.54.0 +1 -0
  105. s/.git/refs/tags/v0.55.0 +1 -0
  106. s/.git/refs/tags/v0.56.0 +1 -0
  107. s/.git/refs/tags/v0.57.0 +1 -0
  108. s/.git/refs/tags/v0.58.0 +1 -0
  109. s/.git/refs/tags/v0.59.0 +1 -0
  110. s/.git/refs/tags/v0.60.0 +1 -0
  111. s/.git/refs/tags/v0.61.0 +1 -0
  112. s/.git/refs/tags/v0.62.0 +1 -0
  113. s/.git/refs/tags/v0.63.0 +1 -0
  114. s/.git/refs/tags/v0.64.0 +1 -0
  115. s/.git/refs/tags/v0.65.0 +1 -0
  116. s/.git/refs/tags/v0.66.0 +1 -0
  117. s/.git/refs/tags/v0.67.0 +1 -0
  118. s/.git/refs/tags/v0.68.0 +1 -0
  119. s/.git/refs/tags/v0.69.0 +1 -0
  120. s/.git/refs/tags/v0.70.0 +1 -0
  121. s/.git/refs/tags/v0.71.0 +1 -0
  122. s/.git/refs/tags/v0.72.0 +1 -0
  123. s/.git/refs/tags/v0.73.0 +1 -0
  124. s/.git/refs/tags/v0.74.0 +1 -0
  125. s/.git/refs/tags/v0.75.0 +1 -0
  126. s/.git/refs/tags/v0.76.0 +1 -0
  127. s/.git/refs/tags/v0.77.0 +1 -0
  128. s/.git/refs/tags/v0.78.0 +1 -0
  129. s/.git/refs/tags/v0.79.0 +1 -0
  130. s/.git/refs/tags/v0.82.0 +1 -0
  131. s/.git/refs/tags/v0.82.0-container +1 -0
  132. s/.git/refs/tags/v1.0.0 +1 -0
  133. s/.git/refs/tags/v1.1.0 +1 -0
  134. s/.git/refs/tags/v1.2.0 +1 -0
  135. s/.git/refs/tags/v1.3.0 +1 -0
  136. s/.git/refs/tags/v2.0.0 +1 -0
  137. s/.git/refs/tags/v2.0.1 +1 -0
  138. s/.git/refs/tags/v2.0.2 +1 -0
  139. s/.git/refs/tags/v2.1.0 +1 -0
  140. s/.git/refs/tags/v2.1.1 +1 -0
  141. s/.git/refs/tags/v2.1.2 +1 -0
  142. s/.git/refs/tags/v2.1.3 +1 -0
  143. s/.git/refs/tags/v2.1.4 +1 -0
  144. s/.git/refs/tags/v2.1.6 +1 -0
  145. s/.git/refs/tags/v2.2.0 +1 -0
  146. s/.git/refs/tags/v3.0.0 +1 -0
  147. s/.git/refs/tags/v3.0.0-rc +1 -0
  148. s/.git/refs/tags/v3.1.0 +1 -0
  149. s/.git/refs/tags/v3.1.1 +1 -0
  150. s/.git/refs/tags/v3.1.2 +1 -0
  151. s/.git/refs/tags/v3.2.0 +1 -0
  152. s/.git/refs/tags/v3.2.0rc0 +1 -0
  153. s/.git/refs/tags/v3.3.0 +1 -0
  154. s/.git/refs/tags/v3.3.1 +1 -0
  155. s/.git/refs/tags/v3.3.2 +1 -0
  156. s/.git/refs/tags/v3.3.3 +1 -0
  157. s/.git/refs/tags/v3.3.3-rc0 +1 -0
  158. s/.git/refs/tags/v3.3.4 +1 -0
  159. s/.github/ISSUE_TEMPLATE/bug_report.md +16 -0
  160. s/.github/ISSUE_TEMPLATE/feature_request.md +7 -0
  161. s/.github/workflows/wiki.yml +54 -0
  162. s/.gitignore +103 -0
  163. s/.vscode/launch.json +26 -0
  164. s/.vscode/settings.json +30 -0
  165. s/CHANGELOG.md +156 -0
  166. s/CODE_OF_CONDUCT.md +1 -0
  167. s/CONTRIBUTING.md +122 -0
  168. s/LICENSE +14 -0
  169. s/MANIFEST.in +7 -0
  170. s/Makefile +87 -0
  171. s/README.md +204 -0
  172. s/assets/armdeployment.png +0 -0
  173. s/assets/armoutputs.png +0 -0
  174. s/assets/deploy/ARMDeployment/ARMDeployment.deployproj +36 -0
  175. s/assets/deploy/ARMDeployment/Deploy-AzureResourceGroup.ps1 +118 -0
  176. s/assets/deploy/ARMDeployment/azuredeploy.json +145 -0
  177. s/assets/deploy/ARMDeployment/azuredeploy.parameters.json +6 -0
  178. s/assets/deploy/ARMDeployment.sln +25 -0
  179. s/assets/edgedevtool2mins.png +0 -0
  180. s/assets/edgedevtoolintro.png +0 -0
  181. s/assets/edgedevtoolquickstartlarge.png +0 -0
  182. s/assets/edgedevtoolquickstartsmall.png +0 -0
  183. s/assets/edgedevtoolwsl.png +0 -0
  184. s/azure-pipelines.yml +144 -0
  185. s/build/lib/iotedgedev/__init__.py +6 -0
  186. s/build/lib/iotedgedev/args.py +18 -0
  187. s/build/lib/iotedgedev/azurecli.py +542 -0
  188. s/build/lib/iotedgedev/buildoptionsparser.py +151 -0
  189. s/build/lib/iotedgedev/buildprofile.py +5 -0
  190. s/build/lib/iotedgedev/cli.py +830 -0
  191. s/build/lib/iotedgedev/connectionstring.py +58 -0
  192. s/build/lib/iotedgedev/constants.py +12 -0
  193. s/build/lib/iotedgedev/containerregistry.py +5 -0
  194. s/build/lib/iotedgedev/decorators.py +116 -0
  195. s/build/lib/iotedgedev/deploymentmanifest.py +313 -0
  196. s/build/lib/iotedgedev/dockercls.py +255 -0
  197. s/build/lib/iotedgedev/dotnet.py +31 -0
  198. s/build/lib/iotedgedev/edge.py +33 -0
  199. s/build/lib/iotedgedev/envvars.py +240 -0
  200. s/build/lib/iotedgedev/iothub.py +65 -0
  201. s/build/lib/iotedgedev/module.py +62 -0
  202. s/build/lib/iotedgedev/modules.py +332 -0
  203. s/build/lib/iotedgedev/organizedgroup.py +48 -0
  204. s/build/lib/iotedgedev/output.py +57 -0
  205. s/build/lib/iotedgedev/simulator.py +58 -0
  206. s/build/lib/iotedgedev/solution.py +48 -0
  207. s/build/lib/iotedgedev/telemetry.py +155 -0
  208. s/build/lib/iotedgedev/telemetryconfig.py +85 -0
  209. s/build/lib/iotedgedev/telemetryuploader.py +53 -0
  210. s/build/lib/iotedgedev/template/.env.tmp +87 -0
  211. s/build/lib/iotedgedev/template/.gitignore +10 -0
  212. s/build/lib/iotedgedev/template/deployment.template.json +79 -0
  213. s/build/lib/iotedgedev/template/launch_c.json +50 -0
  214. s/build/lib/iotedgedev/template/launch_csharp.json +43 -0
  215. s/build/lib/iotedgedev/template/launch_java.json +27 -0
  216. s/build/lib/iotedgedev/template/launch_node.json +36 -0
  217. s/build/lib/iotedgedev/template/launch_python.json +22 -0
  218. s/build/lib/iotedgedev/utility.py +179 -0
  219. s/build/lib/iotedgedev/version.py +4 -0
  220. s/build.sh +150 -0
  221. s/cleanup.sh +7 -0
  222. s/dist/iotedgedev-3.3.4-py3-none-any.whl +0 -0
  223. s/docker/tool/build-docker.sh +168 -0
  224. s/docker/tool/deps.txt +7 -0
  225. s/docker/tool/linux/Dockerfile +7 -0
  226. s/docker/tool/linux/Dockerfile.base +61 -0
  227. s/docker/tool/linux/install-dev.sh +8 -0
  228. s/docker/tool/linux/run.ps1 +5 -0
  229. s/docker/tool/linux/run.sh +5 -0
  230. s/docker/tool/push-docker.sh +35 -0
  231. s/docker/tool/windows/Dockerfile +4 -0
  232. s/docker/tool/windows/Dockerfile.base +63 -0
  233. s/docker/tool/windows/install-dev.bat +4 -0
  234. s/docker/tool/windows/run.ps1 +30 -0
  235. s/docs/Azure-Setup.md +102 -0
  236. s/docs/Command-List.md +564 -0
  237. s/docs/Command-Tips.md +51 -0
  238. s/docs/Edge-Device-Setup.md +23 -0
  239. s/docs/Home.md +10 -0
  240. s/docs/Manual-Dev-Machine-Setup.md +77 -0
  241. s/docs/Migration-Guides.md +131 -0
  242. s/docs/Overview.md +66 -0
  243. s/docs/Python-Virtual-Environment-Setup.md +35 -0
  244. s/docs/Quickstart-with-IoT-Edge-Dev-Container.md +145 -0
  245. s/docs/Test-Coverage.md +7 -0
  246. s/docs/Troubleshooting.md +21 -0
  247. s/docs/Usage.md +123 -0
  248. s/docs/_Sidebar.md +13 -0
  249. s/iotedgedev/__init__.py +6 -0
  250. s/iotedgedev/args.py +18 -0
  251. s/iotedgedev/azurecli.py +542 -0
  252. s/iotedgedev/buildoptionsparser.py +151 -0
  253. s/iotedgedev/buildprofile.py +5 -0
  254. s/iotedgedev/cli.py +830 -0
  255. s/iotedgedev/connectionstring.py +58 -0
  256. s/iotedgedev/constants.py +12 -0
  257. s/iotedgedev/containerregistry.py +5 -0
  258. s/iotedgedev/decorators.py +116 -0
  259. s/iotedgedev/deploymentmanifest.py +313 -0
  260. s/iotedgedev/dockercls.py +255 -0
  261. s/iotedgedev/dotnet.py +31 -0
  262. s/iotedgedev/edge.py +33 -0
  263. s/iotedgedev/envvars.py +240 -0
  264. s/iotedgedev/iothub.py +65 -0
  265. s/iotedgedev/module.py +62 -0
  266. s/iotedgedev/modules.py +332 -0
  267. s/iotedgedev/organizedgroup.py +48 -0
  268. s/iotedgedev/output.py +57 -0
  269. s/iotedgedev/simulator.py +58 -0
  270. s/iotedgedev/solution.py +48 -0
  271. s/iotedgedev/telemetry.py +155 -0
  272. s/iotedgedev/telemetryconfig.py +85 -0
  273. s/iotedgedev/telemetryuploader.py +53 -0
  274. s/iotedgedev/template/.env.tmp +87 -0
  275. s/iotedgedev/template/.gitignore +10 -0
  276. s/iotedgedev/template/deployment.template.json +79 -0
  277. s/iotedgedev/template/launch_c.json +50 -0
  278. s/iotedgedev/template/launch_csharp.json +43 -0
  279. s/iotedgedev/template/launch_java.json +27 -0
  280. s/iotedgedev/template/launch_node.json +36 -0
  281. s/iotedgedev/template/launch_python.json +22 -0
  282. s/iotedgedev/utility.py +179 -0
  283. s/iotedgedev/version.py +4 -0
  284. s/iotedgedev.egg-info/PKG-INFO +21 -0
  285. s/iotedgedev.egg-info/SOURCES.txt +100 -0
  286. s/iotedgedev.egg-info/dependency_links.txt +1 -0
  287. s/iotedgedev.egg-info/entry_points.txt +3 -0
  288. s/iotedgedev.egg-info/not-zip-safe +1 -0
  289. s/iotedgedev.egg-info/requires.txt +18 -0
  290. s/iotedgedev.egg-info/top_level.txt +1 -0
  291. s/pull_request_template.md +9 -0
  292. s/pytest.ini +4 -0
  293. s/requirements.txt +12 -0
  294. s/requirements_dev.txt +17 -0
  295. s/scripts/gen-help-markdown.bat +177 -0
  296. s/scripts/install-pip.sh +1 -0
  297. s/scripts/setup-wsl.sh +7 -0
  298. s/setup.cfg +18 -0
  299. s/setup.py +66 -0
  300. s/tests/__init__.py +5 -0
  301. s/tests/assets/deployment.manifest_invalid.json +106 -0
  302. s/tests/assets/deployment.manifest_invalid_createoptions.json +102 -0
  303. s/tests/assets/deployment.manifest_invalid_schema.json +69 -0
  304. s/tests/assets/deployment.template.non_str_placeholder.json +79 -0
  305. s/tests/assets/deployment.template_1.json +163 -0
  306. s/tests/assets/deployment.template_2.json +163 -0
  307. s/tests/assets/deployment.template_3.json +174 -0
  308. s/tests/assets/deployment.template_4.json +79 -0
  309. s/tests/assets/deployment.template_invalidresult.json +183 -0
  310. s/tests/assets/deployment.template_without_schema_template.json +99 -0
  311. s/tests/assets/launch.json +225 -0
  312. s/tests/assets/launch_without_nodejs.json +194 -0
  313. s/tests/assets/test_solution_shared_lib/.gitignore +2 -0
  314. s/tests/assets/test_solution_shared_lib/.vscode/launch.json +43 -0
  315. s/tests/assets/test_solution_shared_lib/deployment.debug.template.json +100 -0
  316. s/tests/assets/test_solution_shared_lib/deployment.escapedpath.template.json +90 -0
  317. s/tests/assets/test_solution_shared_lib/deployment.template.json +106 -0
  318. s/tests/assets/test_solution_shared_lib/layered_deployment.flattened_props.template.json +21 -0
  319. s/tests/assets/test_solution_shared_lib/layered_deployment.no_modules.template.json +12 -0
  320. s/tests/assets/test_solution_shared_lib/libs/sharedlib/Class1.cs +12 -0
  321. s/tests/assets/test_solution_shared_lib/libs/sharedlib/sharedlib.csproj +7 -0
  322. s/tests/assets/test_solution_shared_lib/modules/non_module_project/placeholder.txt +0 -0
  323. s/tests/assets/test_solution_shared_lib/modules/sample_module/.gitignore +34 -0
  324. s/tests/assets/test_solution_shared_lib/modules/sample_module/Dockerfile.amd64 +18 -0
  325. s/tests/assets/test_solution_shared_lib/modules/sample_module/Dockerfile.amd64.debug +25 -0
  326. s/tests/assets/test_solution_shared_lib/modules/sample_module/Dockerfile.windows-amd64 +15 -0
  327. s/tests/assets/test_solution_shared_lib/modules/sample_module/Program.cs +91 -0
  328. s/tests/assets/test_solution_shared_lib/modules/sample_module/module.json +18 -0
  329. s/tests/assets/test_solution_shared_lib/modules/sample_module/sample_module.csproj +26 -0
  330. s/tests/assets/test_solution_shared_lib/sample_module_2/.gitignore +34 -0
  331. s/tests/assets/test_solution_shared_lib/sample_module_2/Dockerfile.amd64 +18 -0
  332. s/tests/assets/test_solution_shared_lib/sample_module_2/Dockerfile.amd64.debug +25 -0
  333. s/tests/assets/test_solution_shared_lib/sample_module_2/Dockerfile.windows-amd64 +15 -0
  334. s/tests/assets/test_solution_shared_lib/sample_module_2/Program.cs +91 -0
  335. s/tests/assets/test_solution_shared_lib/sample_module_2/module.json +18 -0
  336. s/tests/assets/test_solution_shared_lib/sample_module_2/sample_module_2.csproj +26 -0
  337. s/tests/test_azurecli.py +33 -0
  338. s/tests/test_buildoptionsparser.py +155 -0
  339. s/tests/test_cli.py +55 -0
  340. s/tests/test_config.py +28 -0
  341. s/tests/test_connectionstring.py +87 -0
  342. s/tests/test_decorators.py +44 -0
  343. s/tests/test_deploymentmanifest.py +106 -0
  344. s/tests/test_envvars.py +169 -0
  345. s/tests/test_iotedgedev_iothub_deploy.py +199 -0
  346. s/tests/test_iotedgedev_simulator.py +133 -0
  347. s/tests/test_iotedgedev_solution.py +457 -0
  348. s/tests/test_iotedgedev_solution_build.py +246 -0
  349. s/tests/test_iotedgedev_solution_tag.py +99 -0
  350. s/tests/test_utility.py +132 -0
  351. s/tests/utility.py +132 -0
  352. s/tox.ini +29 -0
  353. s/vsts_ci/.vsts-ci.yml +93 -0
  354. s/vsts_ci/darwin/continuous-build-darwin.yml +54 -0
  355. s/vsts_ci/linux/continuous-build-linux.yml +28 -0
  356. s/vsts_ci/win32/continuous-build-win32.yml +26 -0
@@ -0,0 +1,542 @@
1
+ from iotedgedev.connectionstring import IoTHubConnectionString
2
+ import json
3
+ import os
4
+ import signal
5
+ import subprocess
6
+ import sys
7
+ from io import StringIO
8
+ from threading import Thread, Timer
9
+
10
+ from azure.cli.core import get_default_cli
11
+ from fstrings import f
12
+ from queue import Empty, Queue
13
+
14
+ from . import telemetry
15
+
16
+ output_io_cls = StringIO
17
+
18
+
19
+ def get_query_argument_for_id_and_name(token):
20
+ return "[?starts_with(@.id,'{0}') || contains(@.name,'{1}')]".format(token.lower(), token)
21
+
22
+
23
+ class AzureCli:
24
+ def __init__(self, output, envvars, cli=get_default_cli()):
25
+ self.output = output
26
+ self.envvars = envvars
27
+ self.az_cli = cli
28
+ self.process = None
29
+ self._proc_terminated = False
30
+
31
+ def decode(self, val):
32
+ return val.decode("utf-8").strip()
33
+
34
+ def is_posix(self):
35
+ return self.envvars.is_posix()
36
+
37
+ def prepare_az_cli_args(self, args, suppress_output=False):
38
+ if suppress_output:
39
+ args.extend(["--query", "\"[?n]|[0]\""])
40
+
41
+ az_args = ["az"] + args
42
+ return az_args
43
+
44
+ def invoke_az_cli_outproc(self, args, error_message=None, stdout_io=None, stderr_io=None, suppress_output=False, timeout=None):
45
+ try:
46
+ if timeout:
47
+ timeout = int(timeout)
48
+
49
+ monitor_events = False
50
+ if 'monitor-events' in args:
51
+ monitor_events = True
52
+ self._proc_terminated = False
53
+
54
+ # Consider using functools
55
+ if monitor_events:
56
+ process = subprocess.Popen(self.prepare_az_cli_args(args, suppress_output),
57
+ shell=not self.is_posix(),
58
+ stdout=subprocess.PIPE,
59
+ stderr=subprocess.PIPE,
60
+ preexec_fn=os.setsid if self.is_posix() else None,
61
+ creationflags=subprocess.CREATE_NEW_PROCESS_GROUP if not self.is_posix() else 0)
62
+ elif stdout_io or stderr_io:
63
+ process = subprocess.Popen(self.prepare_az_cli_args(args, suppress_output),
64
+ stdout=subprocess.PIPE,
65
+ stderr=subprocess.PIPE,
66
+ shell=not self.is_posix())
67
+ else:
68
+ process = subprocess.Popen(self.prepare_az_cli_args(args, suppress_output),
69
+ shell=not self.is_posix())
70
+
71
+ self.process = process
72
+
73
+ timer = None
74
+ if timeout:
75
+ # This Timer will attempt to be accurate but its not always the case in practice
76
+ timer = Timer(float(timeout),
77
+ self._terminate_process_tree,
78
+ args=['Timeout set to {0} seconds, which expired as expected.'.format(timeout)])
79
+ try:
80
+ if timer:
81
+ timer.start()
82
+
83
+ if not monitor_events:
84
+ stdout_data, stderr_data = process.communicate()
85
+ else:
86
+ return self._handle_monitor_event_process(process)
87
+
88
+ finally:
89
+ if timer:
90
+ timer.cancel()
91
+
92
+ if stderr_data and b"invalid_grant" in stderr_data:
93
+ self.output.error(self.decode(stderr_data))
94
+ self.output.info(
95
+ "Your Azure CLI session has expired. Please re-run `iotedgedev iothub setup` to refresh your credentials.")
96
+ self.logout()
97
+ sys.exit()
98
+
99
+ if stdout_io and stdout_data != "":
100
+ stdout_io.writelines(self.decode(stdout_data))
101
+
102
+ if stderr_io and stderr_data != "":
103
+ stderr_io.writelines(self.decode(stderr_data))
104
+
105
+ if process.returncode != 0:
106
+ if error_message:
107
+ self.output.error(error_message)
108
+ self.output.line()
109
+ return False
110
+
111
+ if not stdout_io and not stderr_io:
112
+ self.output.line()
113
+
114
+ except Exception as e:
115
+ if error_message:
116
+ self.output.error(error_message)
117
+ self.output.error(str(e))
118
+ self.output.line()
119
+ return False
120
+
121
+ return True
122
+
123
+ def _enqueue_stream(self, stream, queue):
124
+ try:
125
+ while not self._proc_terminated:
126
+ queue.put(stream.readline().decode('utf8').rstrip())
127
+ finally:
128
+ stream.close()
129
+
130
+ def _handle_monitor_event_process(self, process, error_message=None):
131
+ stdout_queue = Queue()
132
+ stderr_queue = Queue()
133
+
134
+ stream_thread_map = {
135
+ 'stdout': Thread(target=self._enqueue_stream, args=(process.stdout, stdout_queue), daemon=True),
136
+ 'stderr': Thread(target=self._enqueue_stream, args=(process.stderr, stderr_queue), daemon=True)
137
+ }
138
+
139
+ stream_thread_map['stdout'].start()
140
+ stream_thread_map['stderr'].start()
141
+
142
+ try:
143
+ while not self._proc_terminated:
144
+ if not process.poll():
145
+ try:
146
+ self.output.echo(stdout_queue.get_nowait())
147
+ except Empty:
148
+ pass
149
+ else:
150
+ err = None
151
+ try:
152
+ err = stderr_queue.get_nowait()
153
+ except Empty:
154
+ pass
155
+ # Avoid empty sys.excepthook errors from underlying future
156
+ # There is already a uAMQP issue in work for this
157
+ # https://github.com/Azure/azure-uamqp-python/issues/30
158
+ if err and "sys.excepthook" not in err:
159
+ err = err.lstrip()
160
+ err = err.lstrip('ERROR:')
161
+ if error_message:
162
+ err = "{}: {}".format(error_message, err)
163
+ self.output.error(err)
164
+ return False
165
+ except KeyboardInterrupt:
166
+ self.output.info('Terminating process...')
167
+ self._terminate_process_tree()
168
+
169
+ return True
170
+
171
+ def _terminate_process_tree(self, msg=None):
172
+ try:
173
+ if self.process:
174
+ if self.is_posix():
175
+ os.killpg(os.getpgid(self.process.pid), signal.SIGTERM)
176
+ else:
177
+ self.process.send_signal(signal.CTRL_BREAK_EVENT)
178
+ self.process.kill()
179
+ self._proc_terminated = True
180
+ if msg:
181
+ self.output.info(msg)
182
+ self.output.line()
183
+ return True
184
+ except Exception:
185
+ return False
186
+
187
+ def invoke_az_cli(self, args, error_message=None, stdout_io=None):
188
+ try:
189
+ exit_code = self.az_cli.invoke(args, out_file=stdout_io)
190
+ if exit_code and exit_code != 0:
191
+ if error_message:
192
+ self.output.error(error_message)
193
+ return False
194
+ except Exception as e:
195
+ if error_message:
196
+ self.output.error(error_message)
197
+ self.output.error(str(e))
198
+ return False
199
+
200
+ self.output.line()
201
+
202
+ return True
203
+
204
+ def add_extension(self, name):
205
+ return self.invoke_az_cli_outproc(["extension", "add", "--name", name,
206
+ "--yes"],
207
+ f("Error while adding extension {name}."), suppress_output=True)
208
+
209
+ def add_extension_with_source(self, source_url):
210
+ return self.invoke_az_cli_outproc(["extension", "add", "--source", source_url,
211
+ "--yes"],
212
+ f("Error while add extension from source {source_url}."),
213
+ suppress_output=True)
214
+
215
+ def extension_exists(self, name):
216
+ return self.invoke_az_cli_outproc(["extension", "show", "--name", name, "--output", "table"],
217
+ f("Error while checking for extension {name}."), suppress_output=True)
218
+
219
+ def user_has_logged_in(self):
220
+
221
+ self.output.header("AUTHENTICATION")
222
+
223
+ self.output.status(f("Retrieving Azure CLI credentials from cache..."))
224
+
225
+ with output_io_cls() as io:
226
+ result = self.invoke_az_cli_outproc(
227
+ ["account", "show"], stdout_io=io)
228
+
229
+ if result:
230
+ try:
231
+ self.output.prompt("Azure CLI credentials found.")
232
+ out_string = io.getvalue()
233
+ data = json.loads(out_string)
234
+ return data["id"]
235
+ except Exception:
236
+ pass
237
+ self.output.prompt(
238
+ "Azure CLI credentials not found. Please follow instructions below to login to the Azure CLI.")
239
+ return None
240
+
241
+ def login_account(self, username, password):
242
+
243
+ return self.invoke_az_cli_outproc(["login", "-u", username,
244
+ "-p", password],
245
+ "Error while trying to login to Azure. Make sure your account credentials are correct", suppress_output=True)
246
+
247
+ def login_sp(self, username, password, tenant):
248
+
249
+ return self.invoke_az_cli_outproc(["login", "--service-principal", "-u", username,
250
+ "-p", password, "--tenant", tenant],
251
+ "Error while trying to login to Azure. Make sure your service principal credentials are correct.", suppress_output=True)
252
+
253
+ def login_interactive(self):
254
+ return self.invoke_az_cli_outproc(["login"],
255
+ "Error while trying to login to Azure.", suppress_output=True)
256
+
257
+ def logout(self):
258
+ return self.invoke_az_cli_outproc(["account", "clear"])
259
+
260
+ def list_subscriptions(self):
261
+ self.output.status("Retrieving Azure Subscriptions...")
262
+ return self.invoke_az_cli_outproc(["account", "list", "--all", "--query", "[].{\"Subscription Name\":name, Id:id}", "--out", "table"],
263
+ "Error while trying to list Azure subscriptions.")
264
+
265
+ def get_default_subscription(self):
266
+
267
+ with output_io_cls() as io:
268
+ result = self.invoke_az_cli_outproc(["account", "show"],
269
+ "Error while trying to get the default Azure subscription id.", io)
270
+ if result:
271
+ out_string = io.getvalue()
272
+ data = json.loads(out_string)
273
+ return data["id"]
274
+ return ''
275
+
276
+ def get_subscription_id_starts_with(self, token):
277
+ with output_io_cls() as io:
278
+ query = get_query_argument_for_id_and_name(token)
279
+ result = self.invoke_az_cli_outproc(["account", "list", "--query", query],
280
+ "Could not find a subscription for which the id starts with or name contains '{0}'".format(token), io)
281
+
282
+ if result:
283
+ out_string = io.getvalue()
284
+ if out_string:
285
+
286
+ data = json.loads(out_string)
287
+ if len(data) == 1:
288
+ return data[0]["id"]
289
+ elif len(data) > 1:
290
+ self.output.error(
291
+ "Found multiple subscriptions for which the ids start with or names contain '{0}'. Please enter more characters to further refine your selection.".format(token))
292
+ return token
293
+ else:
294
+ self.output.error("Could not find a subscription for which the id starts with or name contains '{0}'.".format(token))
295
+
296
+ return ''
297
+
298
+ def set_subscription(self, subscription):
299
+
300
+ if len(subscription) < 36:
301
+ subscription = self.get_subscription_id_starts_with(subscription)
302
+ if len(subscription) < 36:
303
+ return subscription
304
+
305
+ if len(subscription) == 36:
306
+ self.output.status(f("Setting Subscription to '{subscription}'..."))
307
+
308
+ result = self.invoke_az_cli_outproc(["account", "set", "--subscription", subscription],
309
+ "Error while trying to set Azure subscription.")
310
+ if result:
311
+ return subscription
312
+
313
+ return None
314
+
315
+ def resource_group_exists(self, name):
316
+ self.output.status(f("Checking if Resource Group '{name}' exists..."))
317
+
318
+ with output_io_cls() as io:
319
+ result = self.invoke_az_cli_outproc(["group", "exists", "-n", name],
320
+ f("Resource Group {name} does not exist."), io)
321
+ if result:
322
+ out_string = io.getvalue()
323
+ if out_string == "true":
324
+ return True
325
+
326
+ self.output.prompt(f("Resource Group {name} does not exist."))
327
+
328
+ return False
329
+
330
+ def get_resource_group_location(self, name):
331
+
332
+ self.output.status(f("Retrieving Resource Group '{name}' location..."))
333
+
334
+ with output_io_cls() as io:
335
+ result = self.invoke_az_cli_outproc(["group", "show", "-n", name, "--query", "location", "--output", "tsv"],
336
+ f("Could not retrieve Resource Group {name}'s location."), io)
337
+ if result:
338
+ return io.getvalue()
339
+ else:
340
+ return ''
341
+
342
+ def create_resource_group(self, name, location):
343
+ self.output.status(
344
+ f("Creating Resource Group '{name}' at '{location}'..."))
345
+
346
+ with output_io_cls() as io:
347
+
348
+ result = self.invoke_az_cli_outproc(["group", "create", "--name", name, "--location", location],
349
+ f("Could not create the new Resource Group {name} at location:{location}."), io)
350
+ return result
351
+
352
+ def list_resource_groups(self):
353
+ self.output.header("RESOURCE GROUP")
354
+ self.output.status("Retrieving Resource Groups...")
355
+
356
+ with output_io_cls() as io:
357
+
358
+ result = self.invoke_az_cli_outproc(["group", "list", "--query", "[].{\"Resource Group\":name, Location:location}", "--out", "table"], "Could not list the Resource Groups.", stdout_io=io)
359
+
360
+ self.output.prompt(io.getvalue())
361
+ self.output.line()
362
+
363
+ return result
364
+
365
+ def set_modules(self, config: str, device_id: str, connection_string: IoTHubConnectionString):
366
+ self.output.status(f"Deploying '{config}' to '{device_id}'...")
367
+
368
+ config = os.path.join(os.getcwd(), config)
369
+
370
+ if not os.path.exists(config):
371
+ raise FileNotFoundError(f"Deployment manifest file '{config}' not found. Please run `iotedgedev build` first")
372
+
373
+ telemetry.add_extra_props({'iothubhostname': connection_string.iothub_host.name_hash, 'iothubhostnamesuffix': connection_string.iothub_host.name_suffix})
374
+
375
+ return self.invoke_az_cli_outproc(["iot", "edge", "set-modules", "-d", device_id, "-n", connection_string.iothub_host.hub_name, "-k", config, "-l", connection_string.connection_string],
376
+ error_message=f("Failed to deploy '{config}' to '{device_id}'..."), suppress_output=True)
377
+
378
+ def set_device_tag(self, connection_string: IoTHubConnectionString, device_id: str, tags: str):
379
+ self.output.status(f"Adding tag '{tags}' to '{device_id}'...")
380
+
381
+ telemetry.add_extra_props({'iothubhostname': connection_string.iothub_host.name_hash, 'iothubhostnamesuffix': connection_string.iothub_host.name_suffix})
382
+
383
+ return self.invoke_az_cli_outproc(["iot", "hub", "device-twin", "update", "-d", device_id, "-l", connection_string.connection_string, "--tags", tags],
384
+ error_message=f"Failed to add tag: '{tags}' to device '{device_id}' ...", suppress_output=True)
385
+
386
+ def create_deployment(self,
387
+ config: str,
388
+ connection_string: IoTHubConnectionString,
389
+ deployment_name: str,
390
+ target_condition: str,
391
+ priority: str) -> bool:
392
+ self.output.status(f"Deploying '{config}' to '{connection_string.iothub_host.hub_name}'...")
393
+
394
+ config = os.path.join(os.getcwd(), config)
395
+
396
+ if not os.path.exists(config):
397
+ raise FileNotFoundError(f"Deployment manifest file '{config}' not found. Please run `iotedgedev build` first")
398
+
399
+ telemetry.add_extra_props({'iothubhostname': connection_string.iothub_host.name_hash, 'iothubhostnamesuffix': connection_string.iothub_host.name_suffix})
400
+ with output_io_cls() as io:
401
+ result = self.invoke_az_cli_outproc(["iot", "edge", "deployment", "create", "-d", deployment_name, "-l", connection_string.connection_string, "--content", config,
402
+ "--target-condition", target_condition, "--priority", priority],
403
+ error_message=f"Failed to deploy '{config}' to '{connection_string.iothub_host.hub_name}'...", stderr_io=io)
404
+ if io.getvalue():
405
+ self.output.error(io.getvalue())
406
+ self.output.line()
407
+
408
+ return result
409
+
410
+ def monitor_events(self, device_id, connection_string, hub_name, timeout=300):
411
+ return self.invoke_az_cli_outproc(["iot", "hub", "monitor-events", "-d", device_id, "-n", hub_name, "-l", connection_string, '-t', str(timeout), '-y'],
412
+ error_message=f("Failed to start monitoring events."), suppress_output=False, timeout=timeout)
413
+
414
+ def get_free_iothub(self):
415
+ with output_io_cls() as io:
416
+
417
+ result = self.invoke_az_cli_outproc(["iot", "hub", "list"], f("Could not list IoT Hubs in subscription."), stdout_io=io)
418
+ if result:
419
+ out_string = io.getvalue()
420
+ data = json.loads(out_string)
421
+ for iot in data:
422
+ if iot["sku"]["name"] == "F1":
423
+ return (iot["name"], iot["resourcegroup"])
424
+ return (None, None)
425
+
426
+ def get_first_iothub(self, resource_group):
427
+
428
+ with output_io_cls() as io:
429
+ result = self.invoke_az_cli_outproc(
430
+ ["iot", "hub", "list", "--resource-group", resource_group, "--query", "[0]"], f("Could not get first IoT Hub."), io)
431
+
432
+ if result:
433
+ out_string = io.getvalue()
434
+ if out_string:
435
+ data = json.loads(out_string)
436
+ return data["name"]
437
+ return ''
438
+
439
+ def list_iot_hubs(self, resource_group):
440
+ self.output.header("IOT HUB")
441
+ self.output.status(f("Retrieving IoT Hubs in '{resource_group}'..."))
442
+
443
+ return self.invoke_az_cli_outproc(["iot", "hub", "list", "--resource-group", resource_group, "--query", "[].{\"IoT Hub\":name}", "--out", "table"],
444
+ f("Could not list the IoT Hubs in {resource_group}."))
445
+
446
+ def iothub_exists(self, value, resource_group):
447
+ self.output.status(
448
+ f("Checking if '{value}' IoT Hub exists..."))
449
+
450
+ with output_io_cls() as io:
451
+
452
+ result = self.invoke_az_cli_outproc(["iot", "hub", "show", "--name", value, "--resource-group",
453
+ resource_group, "--out", "table"], stderr_io=io)
454
+ if not result:
455
+ self.output.prompt(
456
+ f("Could not locate the {value} in {resource_group}."))
457
+ return result
458
+
459
+ def create_iothub(self, value, resource_group, sku):
460
+ self.output.status(
461
+ f("Creating '{value}' in '{resource_group}' with '{sku}' sku..."))
462
+
463
+ with output_io_cls() as io:
464
+ with output_io_cls() as error_io:
465
+ self.output.prompt(
466
+ "Creating IoT Hub. Please wait as this could take a few minutes to complete...")
467
+
468
+ result = self.invoke_az_cli_outproc(["iot", "hub", "create", "--name", value, "--resource-group",
469
+ resource_group, "--sku", sku, "--query", "[].{\"IoT Hub\":name}", "--out", "table"],
470
+ f("Could not create the IoT Hub {value} in {resource_group} with sku {sku}."), stdout_io=io, stderr_io=error_io)
471
+ if not result and error_io.getvalue():
472
+ self.output.error(error_io.getvalue())
473
+ self.output.line()
474
+ elif io.getvalue():
475
+ self.output.prompt(io.getvalue())
476
+ self.output.line()
477
+ return result
478
+
479
+ def get_iothub_connection_string(self, value, resource_group):
480
+ self.output.status(
481
+ f("Retrieving '{value}' connection string..."))
482
+
483
+ with output_io_cls() as io:
484
+ result = self.invoke_az_cli_outproc(["iot", "hub", "connection-string", "show", "--hub-name", value,
485
+ "--resource-group", resource_group],
486
+ f("Could not create the IoT Hub {value} in {resource_group}."), stdout_io=io)
487
+ if result:
488
+ out_string = io.getvalue()
489
+ data = json.loads(out_string)
490
+ if "cs" in data:
491
+ return data["cs"]
492
+ else:
493
+ return data["connectionString"]
494
+
495
+ return ''
496
+
497
+ def edge_device_exists(self, value, iothub, resource_group):
498
+ self.output.status(
499
+ f("Checking if '{value}' device exists in '{iothub}'..."))
500
+
501
+ with output_io_cls() as io:
502
+ result = self.invoke_az_cli_outproc(["iot", "hub", "device-identity", "show", "--device-id", value, "--hub-name", iothub,
503
+ "--resource-group", resource_group, "--out", "table"], stderr_io=io)
504
+ if not result:
505
+ self.output.prompt(
506
+ f("Could not locate the {value} device in {iothub} IoT Hub in {resource_group}."))
507
+ return result
508
+
509
+ def list_edge_devices(self, iothub):
510
+ self.output.header("EDGE DEVICE")
511
+ self.output.status(
512
+ f("Retrieving edge devices in '{iothub}'..."))
513
+
514
+ return self.invoke_az_cli_outproc(["iot", "hub", "device-identity", "list", "--hub-name", iothub,
515
+ "--edge-enabled", "--query", "[].{\"Device Id\":deviceId}", "--output", "table"],
516
+ f("Could not list the edge devices in {iothub} IoT Hub."))
517
+
518
+ def create_edge_device(self, value, iothub, resource_group):
519
+ self.output.status(
520
+ f("Creating '{value}' edge device in '{iothub}'..."))
521
+
522
+ return self.invoke_az_cli_outproc(["iot", "hub", "device-identity", "create", "--device-id", value, "--hub-name", iothub,
523
+ "--resource-group", resource_group, "--edge-enabled", "--query", "[].{\"Device Id\":deviceId}", "--output", "table"],
524
+ f("Could not locate the {value} device in {iothub} IoT Hub in {resource_group}."))
525
+
526
+ def get_device_connection_string(self, value, iothub, resource_group):
527
+ self.output.status(
528
+ f("Retrieving '{value}' connection string..."))
529
+
530
+ with output_io_cls() as io:
531
+ result = self.invoke_az_cli_outproc(["iot", "hub", "device-identity", "connection-string", "show", "--device-id", value, "--hub-name", iothub,
532
+ "--resource-group", resource_group],
533
+ f("Could not locate the {value} device in {iothub} IoT Hub in {resource_group}."), stdout_io=io)
534
+ if result:
535
+ out_string = io.getvalue()
536
+ data = json.loads(out_string)
537
+ if "cs" in data:
538
+ return data["cs"]
539
+ else:
540
+ return data["connectionString"]
541
+
542
+ return ''