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,58 @@
1
+ from .utility import Utility
2
+
3
+
4
+ class ConnectionString:
5
+ def __init__(self, value):
6
+ self.connection_string = value
7
+ self.data = dict()
8
+
9
+ if self.connection_string:
10
+ parts = self.connection_string.split(';')
11
+ if len(parts) > 0:
12
+ for part in parts:
13
+ subpart = part.split('=', 1)
14
+ if len(subpart) == 2:
15
+ self.data[subpart[0].lower()] = subpart[1].strip()
16
+
17
+ if self.data:
18
+ self.iothub_host = IoTHubHost(self["hostname"])
19
+ self.shared_access_key = None
20
+ if("sharedaccesskey" in self.data):
21
+ self.shared_access_key = self["sharedaccesskey"]
22
+ else:
23
+ # unexpected input of connection. Set to None and throw error
24
+ self.connection_string = None
25
+
26
+ def __getitem__(self, key):
27
+ return self.data[key]
28
+
29
+
30
+ class IoTHubConnectionString(ConnectionString):
31
+ def __init__(self, value):
32
+ ConnectionString.__init__(self, value)
33
+
34
+ if self.connection_string:
35
+ self.shared_access_key_name = self["sharedaccesskeyname"]
36
+
37
+
38
+ class DeviceConnectionString(ConnectionString):
39
+ def __init__(self, value):
40
+ ConnectionString.__init__(self, value)
41
+
42
+ if self.connection_string:
43
+ self.device_id = self["deviceid"]
44
+
45
+
46
+ class IoTHubHost:
47
+ def __init__(self, hostname):
48
+ self.name = hostname
49
+ if self.name and "." in self.name:
50
+ self.hub_name = self.name.split('.')[0]
51
+ # get connection string hostname hash to count distint IoT Hub number
52
+ self.name_hash = Utility.get_sha256_hash(self.name)
53
+ # get hostname suffix (e.g., azure-devices.net) to distinguish national clouds
54
+ self.name_suffix = self.name[self.name.index(".")+1:]
55
+ else:
56
+ self.hub_name = ""
57
+ self.name_hash = ""
58
+ self.name_suffix = ""
@@ -0,0 +1,12 @@
1
+ class Constants:
2
+ default_config_folder = "config"
3
+ default_modules_folder = "modules"
4
+ default_deployment_template_file = "deployment.template.json"
5
+ default_deployment_debug_template_file = "deployment.debug.template.json"
6
+ default_platform = "amd64"
7
+ deployment_template_suffix = ".template.json"
8
+ deployment_template_schema_version = "4.0.0"
9
+ moduledir_placeholder_pattern = r'\${MODULEDIR<(.+)>(\..+)?}'
10
+ deployment_template_schema_url = "http://json.schemastore.org/azure-iot-edge-deployment-template-4.0"
11
+ deployment_manifest_schema_url = "http://json.schemastore.org/azure-iot-edge-deployment-2.0"
12
+ azure_cli_iot_ext_source_url = "https://github.com/Azure/azure-iot-cli-extension/releases/download/v0.10.11/azure_iot-0.10.11-py3-none-any.whl"
@@ -0,0 +1,5 @@
1
+ class ContainerRegistry:
2
+ def __init__(self, server, username, password):
3
+ self.server = server
4
+ self.username = username
5
+ self.password = password
@@ -0,0 +1,116 @@
1
+ import sys
2
+ from functools import wraps
3
+
4
+ import click
5
+
6
+ PARAMS_WITH_VALUES = {'edge_runtime_version'}
7
+
8
+ def with_telemetry(func):
9
+ @wraps(func)
10
+ def _wrapped_func(*args, **kwargs):
11
+ from . import telemetry
12
+ from .telemetryconfig import TelemetryConfig
13
+
14
+ config = TelemetryConfig()
15
+ config.check_firsttime()
16
+ params = parse_params(*args, **kwargs)
17
+ telemetry.start(func.__name__, params)
18
+ try:
19
+ value = func(*args, **kwargs)
20
+ telemetry.success()
21
+ telemetry.flush()
22
+ return value
23
+ except Exception as e:
24
+ from .output import Output
25
+ Output().error(str(e))
26
+ telemetry.fail(str(e), 'Command failed')
27
+ telemetry.flush()
28
+ sys.exit(1)
29
+
30
+ return _wrapped_func
31
+
32
+
33
+ def suppress_all_exceptions(fallback_return=None):
34
+ """We need to suppress exceptions for some internal functions such as those related to telemetry.
35
+ They should not be visible to users.
36
+ """
37
+ def _decorator(func):
38
+ @wraps(func)
39
+ def _wrapped_func(*args, **kwargs):
40
+ try:
41
+ return func(*args, **kwargs)
42
+ except Exception:
43
+ if fallback_return:
44
+ return fallback_return
45
+ else:
46
+ pass
47
+
48
+ return _wrapped_func
49
+
50
+ return _decorator
51
+
52
+
53
+ @suppress_all_exceptions()
54
+ def parse_params(*args, **kwargs):
55
+ """Record the parameter keys and whether the values are None"""
56
+ params = []
57
+ for key, value in kwargs.items():
58
+ if (value is None) or (key in PARAMS_WITH_VALUES):
59
+ params.append('{0}={1}'.format(key, value))
60
+ else:
61
+ params.append('{0}!=None'.format(key))
62
+ return params
63
+
64
+
65
+ def hash256_result(func):
66
+ """Secure the return string of the annotated function with SHA256 algorithm. If the annotated
67
+ function doesn't return string or return None, raise ValueError."""
68
+ @wraps(func)
69
+ def _wrapped_func(*args, **kwargs):
70
+ val = func(*args, **kwargs)
71
+ if not val:
72
+ raise ValueError('Return value is None')
73
+ elif not isinstance(val, str):
74
+ raise ValueError('Return value is not string')
75
+
76
+ from .utility import Utility
77
+ return Utility.get_sha256_hash(val)
78
+
79
+ return _wrapped_func
80
+
81
+
82
+ def module_template_options(func):
83
+ """Merge the module template option decorators into a single one."""
84
+ template_dec = click.option("--template",
85
+ "-t",
86
+ default="csharp",
87
+ show_default=True,
88
+ required=False,
89
+ type=click.Choice(["c", "csharp", "java", "nodejs", "python", "csharpfunction"]),
90
+ help="Specify the template used to create the default module")
91
+ group_id_dec = click.option("--group-id",
92
+ "-g",
93
+ default="com.edgemodule",
94
+ show_default=True,
95
+ help="(Java modules only) Specify the groupId")
96
+ return template_dec(group_id_dec(func))
97
+
98
+
99
+ def add_module_options(envvars, init=False):
100
+ """Decorate commands which involve adding modules to the solution."""
101
+ """`init` specifies whether the command initializes a new solution."""
102
+ if init:
103
+ module_name_dec = click.option("--module",
104
+ "-m",
105
+ required=False,
106
+ default=envvars.get_envvar("DEFAULT_MODULE_NAME", default="filtermodule"),
107
+ show_default=True,
108
+ help="Specify the name of the default module")
109
+ else:
110
+ module_name_dec = click.argument("name",
111
+ required=True)
112
+
113
+ def _decorator(func):
114
+ return module_name_dec(module_template_options(func))
115
+
116
+ return _decorator
@@ -0,0 +1,313 @@
1
+ """
2
+ This module provides interfaces to manipulate IoT Edge deployment manifest (deployment.json)
3
+ and deployment manifest template (deployment.template.json)
4
+ """
5
+
6
+ import json
7
+ import os
8
+ import re
9
+ import shutil
10
+ import functools
11
+
12
+ import jsonschema
13
+ from urllib.request import urlopen
14
+
15
+ from .utility import Utility
16
+ from .constants import Constants
17
+
18
+
19
+ TWIN_VALUE_MAX_SIZE = 512
20
+ TWIN_VALUE_MAX_CHUNKS = 8
21
+
22
+
23
+ class DeploymentManifest:
24
+ def __init__(self, envvars, output, utility, path, is_template, expand_vars=True):
25
+ self.envvars = envvars
26
+ self.utility = utility
27
+ self.output = output
28
+ try:
29
+ self.path = path
30
+ self.is_template = is_template
31
+ self.json = json.loads(Utility.get_file_contents(path, expandvars=expand_vars))
32
+ except FileNotFoundError:
33
+ if is_template:
34
+ deployment_manifest_path = self.envvars.DEPLOYMENT_CONFIG_FILE_PATH
35
+ if os.path.exists(deployment_manifest_path):
36
+ self.output.error('Deployment manifest template file "{0}" not found'.format(path))
37
+ if output.confirm('Would you like to make a copy of the deployment manifest file "{0}" as the deployment template file?'.format(deployment_manifest_path), default=True):
38
+ shutil.copyfile(deployment_manifest_path, path)
39
+ self.json = json.load(open(self.envvars.DEPLOYMENT_CONFIG_FILE_PATH))
40
+ self.envvars.save_envvar("DEPLOYMENT_CONFIG_TEMPLATE_FILE", path)
41
+ else:
42
+ raise FileNotFoundError('Deployment manifest file "{0}" not found'.format(path))
43
+ else:
44
+ raise FileNotFoundError('Deployment manifest file "{0}" not found'.format(path))
45
+
46
+ def add_module_template(self, module_name, create_options={}, is_debug=False):
47
+ """Add a module template to the deployment manifest"""
48
+ new_module = {
49
+ "version": "1.0",
50
+ "type": "docker",
51
+ "status": "running",
52
+ "restartPolicy": "always",
53
+ "settings": {
54
+ "image": DeploymentManifest.get_image_placeholder(module_name, is_debug),
55
+ "createOptions": create_options
56
+ }
57
+ }
58
+
59
+ try:
60
+ self.utility.nested_set(self._get_module_content(), ["$edgeAgent", "properties.desired", "modules", module_name], new_module)
61
+ except KeyError as err:
62
+ raise KeyError("Missing key {0} in file {1}".format(err, self.path))
63
+
64
+ self.add_default_route(module_name)
65
+
66
+ def add_default_route(self, module_name):
67
+ """Add a default route to send messages to IoT Hub"""
68
+ new_route_name = "{0}ToIoTHub".format(module_name)
69
+ new_route = "FROM /messages/modules/{0}/outputs/* INTO $upstream".format(module_name)
70
+
71
+ try:
72
+ self.utility.nested_set(self._get_module_content(), ["$edgeHub", "properties.desired", "routes", new_route_name], new_route)
73
+ except KeyError as err:
74
+ raise KeyError("Missing key {0} in file {1}".format(err, self.path))
75
+
76
+ def get_user_modules(self) -> dict:
77
+ """Get user modules from deployment manifest"""
78
+ try:
79
+ if not self.has_user_modules() and self.is_layered_deployment_schema():
80
+ return {}
81
+ return self.get_desired_property("$edgeAgent", "modules")
82
+ except KeyError as err:
83
+ raise KeyError("Missing key {0} in file {1}".format(err, self.path))
84
+
85
+ def get_system_modules(self):
86
+ """Get system modules from deployment manifest"""
87
+ try:
88
+ if not self.has_system_modules() and self.is_layered_deployment_schema():
89
+ return {}
90
+ return self.get_desired_property("$edgeAgent", "systemModules")
91
+ except KeyError as err:
92
+ raise KeyError("Missing key {0} in file {1}".format(err, self.path))
93
+
94
+ def get_all_modules(self):
95
+ all_modules = {}
96
+ all_modules.update(self.get_user_modules())
97
+ all_modules.update(self.get_system_modules())
98
+
99
+ return all_modules
100
+
101
+ def get_desired_property(self, module, prop):
102
+ return self._get_module_content_split()[module]["properties"]["desired"][prop]
103
+
104
+ def get_template_schema_ver(self):
105
+ return self.json.get("$schema-template", "")
106
+
107
+ def has_system_modules(self):
108
+ """Check if system modules exist in the deployment manifest"""
109
+ return self.has_desired_property("$edgeAgent", "systemModules")
110
+
111
+ def has_user_modules(self):
112
+ """Check if user modules exist in the deployment manifest"""
113
+ return self.has_desired_property("$edgeAgent", "modules")
114
+
115
+ def has_desired_property(self, module, prop) -> bool:
116
+ return prop in self._get_module_content_split().get(module, {}).get('properties', {}).get('desired', {})
117
+
118
+ def convert_create_options(self):
119
+ modules = self.get_all_modules()
120
+ for module_name, module_info in modules.items():
121
+ if "settings" in module_info and "createOptions" in module_info["settings"]:
122
+ create_options = module_info["settings"]["createOptions"]
123
+ if not isinstance(create_options, str):
124
+ # Stringify and minify the createOptions from dict format
125
+ create_options = json.dumps(create_options, separators=(',', ':'))
126
+
127
+ options = [m for m in re.finditer("(.|[\r\n]){{1,{0}}}".format(TWIN_VALUE_MAX_SIZE), create_options)]
128
+ if len(options) > TWIN_VALUE_MAX_CHUNKS:
129
+ raise ValueError("Size of createOptions of {0} is too big. The maximum size of createOptions is 4K".format(module_name))
130
+
131
+ for i, option in enumerate(options):
132
+ if i == 0:
133
+ module_info["settings"]["createOptions"] = option.group()
134
+ else:
135
+ module_info["settings"]["createOptions{0:0=2d}".format(i)] = option.group()
136
+
137
+ def expand_image_placeholders(self, replacements):
138
+ modules = self.get_all_modules()
139
+ for module_name, module_info in modules.items():
140
+ if module_name in replacements:
141
+ self.utility.nested_set(module_info, ["settings", "image"], replacements[module_name])
142
+
143
+ def del_key(self, keys):
144
+ self.utility.del_key(self.json, keys)
145
+
146
+ def dump(self, path=None):
147
+ """Dump the JSON to the disk"""
148
+ if path is None:
149
+ path = self.path
150
+
151
+ with open(path, "w") as deployment_manifest:
152
+ json.dump(self.json, deployment_manifest, indent=2)
153
+
154
+ def validate_deployment_template(self):
155
+ validation_success = True
156
+ try:
157
+ template_schema = json.loads(urlopen(Constants.deployment_template_schema_url).read().decode("utf-8"))
158
+ validation_success = self._validate_json_schema(template_schema, self.json, "Deployment template")
159
+ except Exception as ex: # Ignore other non shcema validation errors
160
+ self.output.info("Unexpected error during deployment template schema validation, skip schema validation. Error:%s" % ex)
161
+
162
+ return validation_success
163
+
164
+ def validate_deployment_manifest(self):
165
+ validation_success = True
166
+ try:
167
+ if not self.is_layered_deployment_schema():
168
+ validation_success = self._validate_deployment_manifest_schema()
169
+ validation_success &= self._validate_create_options()
170
+ except Exception as err:
171
+ self.output.info("Unexpected error during deployment manifest validation, skip the validation. Error:%s" % err)
172
+
173
+ return validation_success
174
+
175
+ def is_layered_deployment_schema(self):
176
+ return "content" in self.json
177
+
178
+ @staticmethod
179
+ def get_image_placeholder(module_name, is_debug=False):
180
+ return "${{MODULES.{0}}}".format(module_name + ".debug" if is_debug else module_name)
181
+
182
+ def _get_module_content_split(self):
183
+ return DeploymentManifest.dot_to_json(self._get_module_content())
184
+
185
+ def _get_module_content(self):
186
+ if "modulesContent" in self.json:
187
+ return self.json["modulesContent"]
188
+ elif "moduleContent" in self.json:
189
+ return self.json["moduleContent"]
190
+ elif self.is_layered_deployment_schema() and "modulesContent" in self.json["content"]:
191
+ return self.json["content"]["modulesContent"]
192
+ else:
193
+ raise KeyError("modulesContent")
194
+
195
+ @staticmethod
196
+ def dot_to_json(a):
197
+ output = {}
198
+ for key, value in a.items():
199
+ if '.' not in key:
200
+ output[key] = DeploymentManifest.dot_to_json(value)
201
+ continue
202
+ path = key.split('.')
203
+ target = functools.reduce(lambda d, k: d.setdefault(k, {}), path[:-1], output)
204
+ target[path[-1]] = value
205
+ return output
206
+
207
+ # Carefully check upper/lower case of the output when using this function
208
+ def _validate_json_schema(self, schema_object, json_object, schema_type):
209
+ validation_success = True
210
+ try:
211
+ self.output.info("Validating schema of %s." % schema_type.lower())
212
+ validator_class = jsonschema.validators.validator_for(schema_object)
213
+ validator = validator_class(schema_object)
214
+ validation_errors = validator.iter_errors(self.json)
215
+ for error in validation_errors:
216
+ validation_success = False
217
+ self.output.warning("%s schema error: %s. Property path:%s" % (schema_type, error.message, "->".join(error.path)))
218
+ if validation_success:
219
+ self.output.info("%s schema validation passed." % schema_type)
220
+ else:
221
+ self.output.warning("%s schema validation failed. Please see previous logs for more details" % schema_type)
222
+
223
+ except jsonschema.exceptions.SchemaError as schemaErr:
224
+ self.output.info("Errors found in %s schema, skip schema validation. Error:%s" % (schema_type, schemaErr.message))
225
+ except Exception as ex: # Ignore other non schema validation errors
226
+ self.output.info("Unexpected error during %s schema validation, skip schema validation. Error:%s" % (schema_type, ex))
227
+
228
+ return validation_success
229
+
230
+ def _validate_deployment_manifest_schema(self):
231
+ validation_success = True
232
+ try:
233
+ deployment_schema = json.loads(urlopen(Constants.deployment_manifest_schema_url).read())
234
+ validation_success = self._validate_json_schema(deployment_schema, self.json, "Deployment manifest")
235
+ except Exception as ex: # Ignore other non schema validation errors
236
+ self.output.info("Unexpected error during deployment manifest schema validation, skip schema validation. Error:%s" % ex)
237
+
238
+ return validation_success
239
+
240
+ # Call _validate_deployment_manifest_schema first. This function assumes createOptions are strings.
241
+ def _validate_create_options(self):
242
+ self.output.info("Start validating createOptions for all modules.")
243
+ modules = self.get_all_modules()
244
+ validation_success = True
245
+ for module_name, module_info in modules.items():
246
+ current_module_validation_success = True
247
+ try:
248
+ self.output.info("Validating createOptions for module %s" % module_name)
249
+ if "settings" in module_info and "createOptions" in module_info["settings"]:
250
+ current_module_validation_success = self._validate_create_options_for_module(module_name, module_info)
251
+ if current_module_validation_success:
252
+ self.output.info("createOptions of module %s validation passed" % module_name)
253
+ else:
254
+ self.output.info("No settings or createOptions property found in module %s. Skip createOptions validation." % module_name)
255
+ except Exception as ex:
256
+ self.output.info("Unexpected error occurs when validating createOptions for module %s: %s" % (module_name, ex))
257
+ finally:
258
+ validation_success &= current_module_validation_success
259
+ if (validation_success):
260
+ self.output.info("Validation for all createOptions passed.")
261
+ else:
262
+ self.output.warning("Errors found during createOptions validation. Please check the logs for details.")
263
+ return validation_success
264
+
265
+ def _validate_create_options_for_module(self, module_name, module_info):
266
+ validation_success = True
267
+ validation_success &= self._validate_create_options_lengh(module_name, module_info)
268
+ validation_success &= self._validate_create_options_format(module_name, module_info)
269
+ return validation_success
270
+
271
+ def _validate_create_options_lengh(self, module_name, module_info):
272
+ validation_success = True
273
+ create_options_value = module_info["settings"]["createOptions"]
274
+ if len(str(create_options_value)) > TWIN_VALUE_MAX_SIZE:
275
+ validation_success = False
276
+ self.output.warning("Length of createOptions in module %s exceeds %d" % (module_name, TWIN_VALUE_MAX_SIZE))
277
+ # Merge additional create options
278
+ for i in range(1, TWIN_VALUE_MAX_CHUNKS):
279
+ property_name = "createOptions0%d" % i
280
+ if property_name in module_info["settings"]:
281
+ create_options_value = module_info["settings"][property_name]
282
+ if len(str(create_options_value)) > TWIN_VALUE_MAX_SIZE:
283
+ validation_success = False
284
+ self.output.warning("Length of %s in module %s exceeds %d" % (property_name, module_name, TWIN_VALUE_MAX_SIZE))
285
+ else:
286
+ break
287
+ return validation_success
288
+
289
+ def _validate_create_options_format(self, module_name, module_info):
290
+ validation_success = True
291
+ create_options_string = self._merge_create_options(module_name, module_info)
292
+ if not create_options_string.startswith('{'):
293
+ validation_success = False
294
+ self.output.warning("createOptions of module %s should be an object" % module_name)
295
+ else:
296
+ try:
297
+ json.loads(create_options_string)
298
+ except ValueError as err:
299
+ validation_success = False
300
+ self.output.warning("createOptions of module %s is not a valid JSON string. Error: %s" % (module_name, err))
301
+ return validation_success
302
+
303
+ def _merge_create_options(self, module_name, module_info):
304
+ create_options = []
305
+ create_options.append(str(module_info["settings"]["createOptions"]))
306
+ # Merge additional create options
307
+ for i in range(1, TWIN_VALUE_MAX_CHUNKS):
308
+ property_name = "createOptions0%d" % i
309
+ if property_name in module_info["settings"]:
310
+ create_options.append(str(module_info["settings"][property_name]))
311
+ else:
312
+ break
313
+ return "".join(create_options).strip()