mtncloud-cli 1.0.0

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 (499) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +20 -0
  3. data/.github/pull_request_template.md +14 -0
  4. data/.github/workflows/ci.yml +56 -0
  5. data/.github/workflows/release.yml +46 -0
  6. data/.gitignore +25 -0
  7. data/CLAUDE.md +24 -0
  8. data/CONTRIBUTING.md +91 -0
  9. data/Dockerfile +19 -0
  10. data/Gemfile +4 -0
  11. data/LICENSE.txt +23 -0
  12. data/README.md +187 -0
  13. data/Rakefile +26 -0
  14. data/bin/mcs +3 -0
  15. data/bin/mtncloud +27 -0
  16. data/lib/mtncloud/api/account_groups_interface.rb +48 -0
  17. data/lib/mtncloud/api/account_users_interface.rb +68 -0
  18. data/lib/mtncloud/api/accounts_interface.rb +9 -0
  19. data/lib/mtncloud/api/activity_interface.rb +9 -0
  20. data/lib/mtncloud/api/affinity_groups_interface.rb +46 -0
  21. data/lib/mtncloud/api/api_client.rb +1091 -0
  22. data/lib/mtncloud/api/appliance_settings_interface.rb +49 -0
  23. data/lib/mtncloud/api/approvals_interface.rb +32 -0
  24. data/lib/mtncloud/api/apps_interface.rb +180 -0
  25. data/lib/mtncloud/api/archive_buckets_interface.rb +116 -0
  26. data/lib/mtncloud/api/archive_files_interface.rb +173 -0
  27. data/lib/mtncloud/api/audit_interface.rb +9 -0
  28. data/lib/mtncloud/api/auth_interface.rb +58 -0
  29. data/lib/mtncloud/api/backup_jobs_interface.rb +13 -0
  30. data/lib/mtncloud/api/backup_restores_interface.rb +27 -0
  31. data/lib/mtncloud/api/backup_results_interface.rb +32 -0
  32. data/lib/mtncloud/api/backup_service_types_interface.rb +9 -0
  33. data/lib/mtncloud/api/backup_services_interface.rb +9 -0
  34. data/lib/mtncloud/api/backup_settings_interface.rb +20 -0
  35. data/lib/mtncloud/api/backup_types_interface.rb +9 -0
  36. data/lib/mtncloud/api/backups_interface.rb +21 -0
  37. data/lib/mtncloud/api/billing_interface.rb +34 -0
  38. data/lib/mtncloud/api/blueprints_interface.rb +70 -0
  39. data/lib/mtncloud/api/body_io.rb +22 -0
  40. data/lib/mtncloud/api/budgets_interface.rb +48 -0
  41. data/lib/mtncloud/api/catalog_item_types_interface.rb +29 -0
  42. data/lib/mtncloud/api/certificate_types_interface.rb +14 -0
  43. data/lib/mtncloud/api/certificates_interface.rb +9 -0
  44. data/lib/mtncloud/api/clients_interface.rb +41 -0
  45. data/lib/mtncloud/api/cloud_datastores_interface.rb +48 -0
  46. data/lib/mtncloud/api/cloud_folders_interface.rb +41 -0
  47. data/lib/mtncloud/api/cloud_policies_interface.rb +41 -0
  48. data/lib/mtncloud/api/cloud_resource_pools_interface.rb +66 -0
  49. data/lib/mtncloud/api/clouds_interface.rb +217 -0
  50. data/lib/mtncloud/api/clusters_interface.rb +471 -0
  51. data/lib/mtncloud/api/containers_interface.rb +145 -0
  52. data/lib/mtncloud/api/credential_types_interface.rb +9 -0
  53. data/lib/mtncloud/api/credentials_interface.rb +9 -0
  54. data/lib/mtncloud/api/custom_instance_types_interface.rb +99 -0
  55. data/lib/mtncloud/api/cypher_interface.rb +51 -0
  56. data/lib/mtncloud/api/dashboard_interface.rb +14 -0
  57. data/lib/mtncloud/api/datastores_interface.rb +21 -0
  58. data/lib/mtncloud/api/deploy_interface.rb +44 -0
  59. data/lib/mtncloud/api/deployments_interface.rb +79 -0
  60. data/lib/mtncloud/api/distributed_workers_interface.rb +41 -0
  61. data/lib/mtncloud/api/email_templates_interface.rb +53 -0
  62. data/lib/mtncloud/api/environments_interface.rb +48 -0
  63. data/lib/mtncloud/api/execute_schedules_interface.rb +71 -0
  64. data/lib/mtncloud/api/execution_request_interface.rb +27 -0
  65. data/lib/mtncloud/api/file_copy_request_interface.rb +68 -0
  66. data/lib/mtncloud/api/forgot_password_interface.rb +17 -0
  67. data/lib/mtncloud/api/group_policies_interface.rb +41 -0
  68. data/lib/mtncloud/api/groups_interface.rb +73 -0
  69. data/lib/mtncloud/api/guidance_interface.rb +44 -0
  70. data/lib/mtncloud/api/guidance_settings_interface.rb +17 -0
  71. data/lib/mtncloud/api/health_interface.rb +100 -0
  72. data/lib/mtncloud/api/host_vm_groups_interface.rb +37 -0
  73. data/lib/mtncloud/api/hub_interface.rb +25 -0
  74. data/lib/mtncloud/api/image_builder_boot_scripts_interface.rb +41 -0
  75. data/lib/mtncloud/api/image_builder_image_builds_interface.rb +73 -0
  76. data/lib/mtncloud/api/image_builder_interface.rb +20 -0
  77. data/lib/mtncloud/api/image_builder_preseed_scripts_interface.rb +41 -0
  78. data/lib/mtncloud/api/instance_types_interface.rb +20 -0
  79. data/lib/mtncloud/api/instances_interface.rb +453 -0
  80. data/lib/mtncloud/api/integration_types_interface.rb +14 -0
  81. data/lib/mtncloud/api/integrations_interface.rb +74 -0
  82. data/lib/mtncloud/api/invoice_line_items_interface.rb +9 -0
  83. data/lib/mtncloud/api/invoices_interface.rb +28 -0
  84. data/lib/mtncloud/api/jobs_interface.rb +83 -0
  85. data/lib/mtncloud/api/key_pairs_interface.rb +57 -0
  86. data/lib/mtncloud/api/library_cluster_layouts_interface.rb +47 -0
  87. data/lib/mtncloud/api/library_cluster_packages_interface.rb +60 -0
  88. data/lib/mtncloud/api/library_container_scripts_interface.rb +43 -0
  89. data/lib/mtncloud/api/library_container_templates_interface.rb +43 -0
  90. data/lib/mtncloud/api/library_container_types_interface.rb +59 -0
  91. data/lib/mtncloud/api/library_container_upgrades_interface.rb +60 -0
  92. data/lib/mtncloud/api/library_instance_types_interface.rb +74 -0
  93. data/lib/mtncloud/api/library_layouts_interface.rb +69 -0
  94. data/lib/mtncloud/api/library_operating_systems_interface.rb +63 -0
  95. data/lib/mtncloud/api/library_spec_template_types_interface.rb +43 -0
  96. data/lib/mtncloud/api/library_spec_templates_interface.rb +43 -0
  97. data/lib/mtncloud/api/license_interface.rb +30 -0
  98. data/lib/mtncloud/api/load_balancer_monitors_interface.rb +9 -0
  99. data/lib/mtncloud/api/load_balancer_pool_nodes_interface.rb +8 -0
  100. data/lib/mtncloud/api/load_balancer_pools_interface.rb +9 -0
  101. data/lib/mtncloud/api/load_balancer_pools_secondary_interface.rb +9 -0
  102. data/lib/mtncloud/api/load_balancer_profiles_interface.rb +9 -0
  103. data/lib/mtncloud/api/load_balancer_types_interface.rb +9 -0
  104. data/lib/mtncloud/api/load_balancer_virtual_servers_interface.rb +18 -0
  105. data/lib/mtncloud/api/load_balancers_interface.rb +14 -0
  106. data/lib/mtncloud/api/log_settings_interface.rb +44 -0
  107. data/lib/mtncloud/api/logs_interface.rb +37 -0
  108. data/lib/mtncloud/api/migrations_interface.rb +49 -0
  109. data/lib/mtncloud/api/monitoring_alerts_interface.rb +39 -0
  110. data/lib/mtncloud/api/monitoring_apps_interface.rb +69 -0
  111. data/lib/mtncloud/api/monitoring_checks_interface.rb +103 -0
  112. data/lib/mtncloud/api/monitoring_contacts_interface.rb +41 -0
  113. data/lib/mtncloud/api/monitoring_groups_interface.rb +77 -0
  114. data/lib/mtncloud/api/monitoring_incidents_interface.rb +107 -0
  115. data/lib/mtncloud/api/monitoring_interface.rb +35 -0
  116. data/lib/mtncloud/api/monitoring_settings_interface.rb +21 -0
  117. data/lib/mtncloud/api/network_dhcp_relays_interface.rb +36 -0
  118. data/lib/mtncloud/api/network_dhcp_servers_interface.rb +36 -0
  119. data/lib/mtncloud/api/network_domain_records_interface.rb +41 -0
  120. data/lib/mtncloud/api/network_domains_interface.rb +41 -0
  121. data/lib/mtncloud/api/network_edge_clusters_interface.rb +26 -0
  122. data/lib/mtncloud/api/network_floating_ips_interface.rb +40 -0
  123. data/lib/mtncloud/api/network_groups_interface.rb +41 -0
  124. data/lib/mtncloud/api/network_pool_ips_interface.rb +41 -0
  125. data/lib/mtncloud/api/network_pool_server_types_interface.rb +9 -0
  126. data/lib/mtncloud/api/network_pool_servers_interface.rb +48 -0
  127. data/lib/mtncloud/api/network_pools_interface.rb +41 -0
  128. data/lib/mtncloud/api/network_proxies_interface.rb +41 -0
  129. data/lib/mtncloud/api/network_routers_interface.rb +181 -0
  130. data/lib/mtncloud/api/network_security_server_types_interface.rb +9 -0
  131. data/lib/mtncloud/api/network_security_servers_interface.rb +27 -0
  132. data/lib/mtncloud/api/network_server_groups_interface.rb +7 -0
  133. data/lib/mtncloud/api/network_server_types_interface.rb +9 -0
  134. data/lib/mtncloud/api/network_servers_interface.rb +102 -0
  135. data/lib/mtncloud/api/network_services_interface.rb +63 -0
  136. data/lib/mtncloud/api/network_static_routes_interface.rb +36 -0
  137. data/lib/mtncloud/api/network_subnets_interface.rb +42 -0
  138. data/lib/mtncloud/api/network_types_interface.rb +20 -0
  139. data/lib/mtncloud/api/networks_interface.rb +48 -0
  140. data/lib/mtncloud/api/option_type_forms_interface.rb +9 -0
  141. data/lib/mtncloud/api/option_type_lists_interface.rb +53 -0
  142. data/lib/mtncloud/api/option_types_interface.rb +41 -0
  143. data/lib/mtncloud/api/options_interface.rb +17 -0
  144. data/lib/mtncloud/api/packages_interface.rb +112 -0
  145. data/lib/mtncloud/api/ping_interface.rb +22 -0
  146. data/lib/mtncloud/api/plugins_interface.rb +22 -0
  147. data/lib/mtncloud/api/policies_interface.rb +56 -0
  148. data/lib/mtncloud/api/power_schedules_interface.rb +71 -0
  149. data/lib/mtncloud/api/price_sets_interface.rb +39 -0
  150. data/lib/mtncloud/api/prices_interface.rb +71 -0
  151. data/lib/mtncloud/api/processes_interface.rb +40 -0
  152. data/lib/mtncloud/api/projects_interface.rb +33 -0
  153. data/lib/mtncloud/api/provision_types_interface.rb +24 -0
  154. data/lib/mtncloud/api/provisioning_license_types_interface.rb +31 -0
  155. data/lib/mtncloud/api/provisioning_licenses_interface.rb +60 -0
  156. data/lib/mtncloud/api/provisioning_settings_interface.rb +26 -0
  157. data/lib/mtncloud/api/read_interface.rb +24 -0
  158. data/lib/mtncloud/api/reports_interface.rb +70 -0
  159. data/lib/mtncloud/api/resource_pool_groups_interface.rb +51 -0
  160. data/lib/mtncloud/api/rest_interface.rb +37 -0
  161. data/lib/mtncloud/api/roles_interface.rb +151 -0
  162. data/lib/mtncloud/api/scale_thresholds_interface.rb +9 -0
  163. data/lib/mtncloud/api/search_interface.rb +13 -0
  164. data/lib/mtncloud/api/secondary_read_interface.rb +25 -0
  165. data/lib/mtncloud/api/secondary_rest_interface.rb +42 -0
  166. data/lib/mtncloud/api/security_group_rules_interface.rb +35 -0
  167. data/lib/mtncloud/api/security_groups_interface.rb +56 -0
  168. data/lib/mtncloud/api/security_package_types_interface.rb +9 -0
  169. data/lib/mtncloud/api/security_packages_interface.rb +9 -0
  170. data/lib/mtncloud/api/security_scans_interface.rb +9 -0
  171. data/lib/mtncloud/api/server_devices_interface.rb +30 -0
  172. data/lib/mtncloud/api/server_types_interface.rb +20 -0
  173. data/lib/mtncloud/api/servers_interface.rb +230 -0
  174. data/lib/mtncloud/api/service_catalog_interface.rb +89 -0
  175. data/lib/mtncloud/api/service_plans_interface.rb +62 -0
  176. data/lib/mtncloud/api/setup_interface.rb +46 -0
  177. data/lib/mtncloud/api/snapshots_interface.rb +19 -0
  178. data/lib/mtncloud/api/storage_datastores_interface.rb +44 -0
  179. data/lib/mtncloud/api/storage_providers_interface.rb +151 -0
  180. data/lib/mtncloud/api/storage_server_types_interface.rb +14 -0
  181. data/lib/mtncloud/api/storage_servers_interface.rb +9 -0
  182. data/lib/mtncloud/api/storage_volume_types_interface.rb +9 -0
  183. data/lib/mtncloud/api/storage_volumes_interface.rb +24 -0
  184. data/lib/mtncloud/api/subnet_types_interface.rb +20 -0
  185. data/lib/mtncloud/api/subnets_interface.rb +41 -0
  186. data/lib/mtncloud/api/support_bundles_interface.rb +46 -0
  187. data/lib/mtncloud/api/system_types_interface.rb +13 -0
  188. data/lib/mtncloud/api/systems_interface.rb +55 -0
  189. data/lib/mtncloud/api/task_sets_interface.rb +52 -0
  190. data/lib/mtncloud/api/tasks_interface.rb +66 -0
  191. data/lib/mtncloud/api/tokens_interface.rb +39 -0
  192. data/lib/mtncloud/api/usage_interface.rb +18 -0
  193. data/lib/mtncloud/api/user_groups_interface.rb +59 -0
  194. data/lib/mtncloud/api/user_settings_interface.rb +90 -0
  195. data/lib/mtncloud/api/user_sources_interface.rb +99 -0
  196. data/lib/mtncloud/api/users_interface.rb +33 -0
  197. data/lib/mtncloud/api/vdi_allocations_interface.rb +9 -0
  198. data/lib/mtncloud/api/vdi_apps_interface.rb +9 -0
  199. data/lib/mtncloud/api/vdi_gateways_interface.rb +9 -0
  200. data/lib/mtncloud/api/vdi_interface.rb +28 -0
  201. data/lib/mtncloud/api/vdi_pools_interface.rb +19 -0
  202. data/lib/mtncloud/api/virtual_images_interface.rb +210 -0
  203. data/lib/mtncloud/api/virtual_servers_interface.rb +9 -0
  204. data/lib/mtncloud/api/virtual_switches_interface.rb +9 -0
  205. data/lib/mtncloud/api/whitelabel_settings_interface.rb +57 -0
  206. data/lib/mtncloud/api/whoami_interface.rb +13 -0
  207. data/lib/mtncloud/api/wiki_interface.rb +48 -0
  208. data/lib/mtncloud/api.rb +12 -0
  209. data/lib/mtncloud/benchmarking.rb +259 -0
  210. data/lib/mtncloud/cli/cli_command.rb +2281 -0
  211. data/lib/mtncloud/cli/cli_registry.rb +337 -0
  212. data/lib/mtncloud/cli/commands/access_token_command.rb +201 -0
  213. data/lib/mtncloud/cli/commands/account_groups_command.rb +530 -0
  214. data/lib/mtncloud/cli/commands/activity_command.rb +149 -0
  215. data/lib/mtncloud/cli/commands/affinity_groups_command.rb +554 -0
  216. data/lib/mtncloud/cli/commands/alias_command.rb +278 -0
  217. data/lib/mtncloud/cli/commands/appliance_settings_command.rb +382 -0
  218. data/lib/mtncloud/cli/commands/approvals_command.rb +236 -0
  219. data/lib/mtncloud/cli/commands/apps.rb +2549 -0
  220. data/lib/mtncloud/cli/commands/archives_command.rb +2161 -0
  221. data/lib/mtncloud/cli/commands/audit.rb +188 -0
  222. data/lib/mtncloud/cli/commands/backup_jobs_command.rb +366 -0
  223. data/lib/mtncloud/cli/commands/backup_restores_command.rb +144 -0
  224. data/lib/mtncloud/cli/commands/backup_results_command.rb +149 -0
  225. data/lib/mtncloud/cli/commands/backup_services_command.rb +44 -0
  226. data/lib/mtncloud/cli/commands/backup_settings_command.rb +184 -0
  227. data/lib/mtncloud/cli/commands/backup_types_command.rb +53 -0
  228. data/lib/mtncloud/cli/commands/backups_command.rb +674 -0
  229. data/lib/mtncloud/cli/commands/benchmark_command.rb +458 -0
  230. data/lib/mtncloud/cli/commands/blueprints_command.rb +2297 -0
  231. data/lib/mtncloud/cli/commands/boot_scripts_command.rb +379 -0
  232. data/lib/mtncloud/cli/commands/budgets_command.rb +754 -0
  233. data/lib/mtncloud/cli/commands/cat_command.rb +45 -0
  234. data/lib/mtncloud/cli/commands/catalog_item_types_command.rb +858 -0
  235. data/lib/mtncloud/cli/commands/certificates_command.rb +575 -0
  236. data/lib/mtncloud/cli/commands/change_password_command.rb +132 -0
  237. data/lib/mtncloud/cli/commands/clients_command.rb +331 -0
  238. data/lib/mtncloud/cli/commands/cloud_datastores_command.rb +543 -0
  239. data/lib/mtncloud/cli/commands/cloud_folders_command.rb +512 -0
  240. data/lib/mtncloud/cli/commands/cloud_resource_pools_command.rb +870 -0
  241. data/lib/mtncloud/cli/commands/clouds.rb +2037 -0
  242. data/lib/mtncloud/cli/commands/clouds_types.rb +140 -0
  243. data/lib/mtncloud/cli/commands/cluster_types.rb +179 -0
  244. data/lib/mtncloud/cli/commands/clusters.rb +5855 -0
  245. data/lib/mtncloud/cli/commands/coloring_command.rb +82 -0
  246. data/lib/mtncloud/cli/commands/containers_command.rb +967 -0
  247. data/lib/mtncloud/cli/commands/credential_types_command.rb +36 -0
  248. data/lib/mtncloud/cli/commands/credentials_command.rb +124 -0
  249. data/lib/mtncloud/cli/commands/curl_command.rb +168 -0
  250. data/lib/mtncloud/cli/commands/cypher_command.rb +587 -0
  251. data/lib/mtncloud/cli/commands/dashboard_command.rb +300 -0
  252. data/lib/mtncloud/cli/commands/debug_command.rb +75 -0
  253. data/lib/mtncloud/cli/commands/deploy.rb +485 -0
  254. data/lib/mtncloud/cli/commands/deployments.rb +923 -0
  255. data/lib/mtncloud/cli/commands/deploys.rb +485 -0
  256. data/lib/mtncloud/cli/commands/distributed_workers_command.rb +282 -0
  257. data/lib/mtncloud/cli/commands/echo_command.rb +61 -0
  258. data/lib/mtncloud/cli/commands/edit_profile_command.rb +57 -0
  259. data/lib/mtncloud/cli/commands/edit_rc_command.rb +77 -0
  260. data/lib/mtncloud/cli/commands/email_templates_command.rb +373 -0
  261. data/lib/mtncloud/cli/commands/environments_command.rb +401 -0
  262. data/lib/mtncloud/cli/commands/execute_schedules_command.rb +735 -0
  263. data/lib/mtncloud/cli/commands/execution_request_command.rb +333 -0
  264. data/lib/mtncloud/cli/commands/exit_command.rb +34 -0
  265. data/lib/mtncloud/cli/commands/file_copy_request_command.rb +352 -0
  266. data/lib/mtncloud/cli/commands/forgot_password.rb +133 -0
  267. data/lib/mtncloud/cli/commands/get_prompt_command.rb +36 -0
  268. data/lib/mtncloud/cli/commands/groups.rb +818 -0
  269. data/lib/mtncloud/cli/commands/guidance_command.rb +529 -0
  270. data/lib/mtncloud/cli/commands/guidance_settings.rb +148 -0
  271. data/lib/mtncloud/cli/commands/health_command.rb +1046 -0
  272. data/lib/mtncloud/cli/commands/history_command.rb +60 -0
  273. data/lib/mtncloud/cli/commands/host_vm_groups_command.rb +423 -0
  274. data/lib/mtncloud/cli/commands/hosts.rb +2986 -0
  275. data/lib/mtncloud/cli/commands/hub.rb +215 -0
  276. data/lib/mtncloud/cli/commands/image_builder_command.rb +1197 -0
  277. data/lib/mtncloud/cli/commands/instance_types.rb +163 -0
  278. data/lib/mtncloud/cli/commands/instances.rb +5813 -0
  279. data/lib/mtncloud/cli/commands/integrations_command.rb +1412 -0
  280. data/lib/mtncloud/cli/commands/invoices_command.rb +1151 -0
  281. data/lib/mtncloud/cli/commands/jobs_command.rb +998 -0
  282. data/lib/mtncloud/cli/commands/key_pairs.rb +470 -0
  283. data/lib/mtncloud/cli/commands/library_cluster_layouts_command.rb +901 -0
  284. data/lib/mtncloud/cli/commands/library_cluster_packages_command.rb +552 -0
  285. data/lib/mtncloud/cli/commands/library_container_scripts_command.rb +446 -0
  286. data/lib/mtncloud/cli/commands/library_container_templates_command.rb +526 -0
  287. data/lib/mtncloud/cli/commands/library_container_types_command.rb +698 -0
  288. data/lib/mtncloud/cli/commands/library_forms_command.rb +625 -0
  289. data/lib/mtncloud/cli/commands/library_instance_types_command.rb +668 -0
  290. data/lib/mtncloud/cli/commands/library_layouts_command.rb +849 -0
  291. data/lib/mtncloud/cli/commands/library_operating_systems_command.rb +671 -0
  292. data/lib/mtncloud/cli/commands/library_option_lists_command.rb +503 -0
  293. data/lib/mtncloud/cli/commands/library_option_types_command.rb +329 -0
  294. data/lib/mtncloud/cli/commands/library_spec_templates_command.rb +547 -0
  295. data/lib/mtncloud/cli/commands/library_upgrades_command.rb +596 -0
  296. data/lib/mtncloud/cli/commands/license.rb +475 -0
  297. data/lib/mtncloud/cli/commands/load_balancer_monitors.rb +70 -0
  298. data/lib/mtncloud/cli/commands/load_balancer_pool_nodes.rb +87 -0
  299. data/lib/mtncloud/cli/commands/load_balancer_pools.rb +129 -0
  300. data/lib/mtncloud/cli/commands/load_balancer_profiles.rb +64 -0
  301. data/lib/mtncloud/cli/commands/load_balancer_types.rb +38 -0
  302. data/lib/mtncloud/cli/commands/load_balancer_virtual_servers.rb +147 -0
  303. data/lib/mtncloud/cli/commands/load_balancers.rb +189 -0
  304. data/lib/mtncloud/cli/commands/log_level_command.rb +54 -0
  305. data/lib/mtncloud/cli/commands/log_settings_command.rb +443 -0
  306. data/lib/mtncloud/cli/commands/login.rb +148 -0
  307. data/lib/mtncloud/cli/commands/logout.rb +63 -0
  308. data/lib/mtncloud/cli/commands/logs_command.rb +140 -0
  309. data/lib/mtncloud/cli/commands/man_command.rb +409 -0
  310. data/lib/mtncloud/cli/commands/migrations.rb +453 -0
  311. data/lib/mtncloud/cli/commands/monitoring_alerts_command.rb +791 -0
  312. data/lib/mtncloud/cli/commands/monitoring_apps_command.rb +716 -0
  313. data/lib/mtncloud/cli/commands/monitoring_checks_command.rb +766 -0
  314. data/lib/mtncloud/cli/commands/monitoring_contacts_command.rb +339 -0
  315. data/lib/mtncloud/cli/commands/monitoring_groups_command.rb +674 -0
  316. data/lib/mtncloud/cli/commands/monitoring_incidents_command.rb +835 -0
  317. data/lib/mtncloud/cli/commands/monitoring_settings.rb +212 -0
  318. data/lib/mtncloud/cli/commands/network_dhcp_relays_command.rb +375 -0
  319. data/lib/mtncloud/cli/commands/network_dhcp_servers_command.rb +366 -0
  320. data/lib/mtncloud/cli/commands/network_domains_command.rb +896 -0
  321. data/lib/mtncloud/cli/commands/network_edge_clusters_command.rb +288 -0
  322. data/lib/mtncloud/cli/commands/network_firewalls_command.rb +783 -0
  323. data/lib/mtncloud/cli/commands/network_floating_ips.rb +147 -0
  324. data/lib/mtncloud/cli/commands/network_groups_command.rb +654 -0
  325. data/lib/mtncloud/cli/commands/network_pool_server_types.rb +20 -0
  326. data/lib/mtncloud/cli/commands/network_pool_servers_command.rb +318 -0
  327. data/lib/mtncloud/cli/commands/network_pools_command.rb +903 -0
  328. data/lib/mtncloud/cli/commands/network_proxies_command.rb +578 -0
  329. data/lib/mtncloud/cli/commands/network_routers_command.rb +2628 -0
  330. data/lib/mtncloud/cli/commands/network_security_server_types.rb +20 -0
  331. data/lib/mtncloud/cli/commands/network_server_groups_command.rb +222 -0
  332. data/lib/mtncloud/cli/commands/network_server_types_command.rb +20 -0
  333. data/lib/mtncloud/cli/commands/network_servers_command.rb +398 -0
  334. data/lib/mtncloud/cli/commands/network_services_command.rb +198 -0
  335. data/lib/mtncloud/cli/commands/network_static_routes_command.rb +451 -0
  336. data/lib/mtncloud/cli/commands/network_transport_zones_command.rb +412 -0
  337. data/lib/mtncloud/cli/commands/networks_command.rb +1692 -0
  338. data/lib/mtncloud/cli/commands/open_command.rb +30 -0
  339. data/lib/mtncloud/cli/commands/options.rb +231 -0
  340. data/lib/mtncloud/cli/commands/packages_command.rb +822 -0
  341. data/lib/mtncloud/cli/commands/ping.rb +242 -0
  342. data/lib/mtncloud/cli/commands/plugins.rb +214 -0
  343. data/lib/mtncloud/cli/commands/policies_command.rb +990 -0
  344. data/lib/mtncloud/cli/commands/power_schedules_command.rb +731 -0
  345. data/lib/mtncloud/cli/commands/preseed_scripts_command.rb +378 -0
  346. data/lib/mtncloud/cli/commands/price_sets_command.rb +557 -0
  347. data/lib/mtncloud/cli/commands/prices_command.rb +692 -0
  348. data/lib/mtncloud/cli/commands/processes_command.rb +469 -0
  349. data/lib/mtncloud/cli/commands/projects_command.rb +537 -0
  350. data/lib/mtncloud/cli/commands/provisioning_licenses_command.rb +509 -0
  351. data/lib/mtncloud/cli/commands/provisioning_settings_command.rb +238 -0
  352. data/lib/mtncloud/cli/commands/recent_activity_command.rb +171 -0
  353. data/lib/mtncloud/cli/commands/remote.rb +1818 -0
  354. data/lib/mtncloud/cli/commands/reports_command.rb +724 -0
  355. data/lib/mtncloud/cli/commands/resource_pool_groups_command.rb +586 -0
  356. data/lib/mtncloud/cli/commands/rm_command.rb +14 -0
  357. data/lib/mtncloud/cli/commands/roles.rb +3120 -0
  358. data/lib/mtncloud/cli/commands/scale_thresholds.rb +103 -0
  359. data/lib/mtncloud/cli/commands/search_command.rb +184 -0
  360. data/lib/mtncloud/cli/commands/security_group_rules.rb +230 -0
  361. data/lib/mtncloud/cli/commands/security_groups.rb +1294 -0
  362. data/lib/mtncloud/cli/commands/security_package_types.rb +32 -0
  363. data/lib/mtncloud/cli/commands/security_packages.rb +84 -0
  364. data/lib/mtncloud/cli/commands/security_scans.rb +107 -0
  365. data/lib/mtncloud/cli/commands/self_service_command.rb +17 -0
  366. data/lib/mtncloud/cli/commands/server_devices_command.rb +207 -0
  367. data/lib/mtncloud/cli/commands/service_catalog_command.rb +1528 -0
  368. data/lib/mtncloud/cli/commands/service_plans_command.rb +895 -0
  369. data/lib/mtncloud/cli/commands/set_prompt_command.rb +51 -0
  370. data/lib/mtncloud/cli/commands/setup.rb +423 -0
  371. data/lib/mtncloud/cli/commands/shell.rb +717 -0
  372. data/lib/mtncloud/cli/commands/sleep_command.rb +43 -0
  373. data/lib/mtncloud/cli/commands/snapshots.rb +156 -0
  374. data/lib/mtncloud/cli/commands/source_command.rb +30 -0
  375. data/lib/mtncloud/cli/commands/ssl_verification_command.rb +47 -0
  376. data/lib/mtncloud/cli/commands/storage_datastores.rb +457 -0
  377. data/lib/mtncloud/cli/commands/storage_providers_command.rb +1401 -0
  378. data/lib/mtncloud/cli/commands/storage_server_types.rb +45 -0
  379. data/lib/mtncloud/cli/commands/storage_servers.rb +116 -0
  380. data/lib/mtncloud/cli/commands/storage_volume_types.rb +45 -0
  381. data/lib/mtncloud/cli/commands/storage_volumes.rb +236 -0
  382. data/lib/mtncloud/cli/commands/subnets_command.rb +819 -0
  383. data/lib/mtncloud/cli/commands/support_bundles_command.rb +606 -0
  384. data/lib/mtncloud/cli/commands/systems.rb +1203 -0
  385. data/lib/mtncloud/cli/commands/tasks.rb +1391 -0
  386. data/lib/mtncloud/cli/commands/tee_command.rb +14 -0
  387. data/lib/mtncloud/cli/commands/tenants_command.rb +315 -0
  388. data/lib/mtncloud/cli/commands/tokens_command.rb +391 -0
  389. data/lib/mtncloud/cli/commands/update_command.rb +46 -0
  390. data/lib/mtncloud/cli/commands/usage_command.rb +203 -0
  391. data/lib/mtncloud/cli/commands/user_groups_command.rb +512 -0
  392. data/lib/mtncloud/cli/commands/user_settings_command.rb +849 -0
  393. data/lib/mtncloud/cli/commands/user_sources_command.rb +1065 -0
  394. data/lib/mtncloud/cli/commands/users.rb +897 -0
  395. data/lib/mtncloud/cli/commands/vdi_allocations_command.rb +159 -0
  396. data/lib/mtncloud/cli/commands/vdi_apps_command.rb +317 -0
  397. data/lib/mtncloud/cli/commands/vdi_command.rb +359 -0
  398. data/lib/mtncloud/cli/commands/vdi_gateways_command.rb +290 -0
  399. data/lib/mtncloud/cli/commands/vdi_pools_command.rb +574 -0
  400. data/lib/mtncloud/cli/commands/version_command.rb +38 -0
  401. data/lib/mtncloud/cli/commands/view.rb +107 -0
  402. data/lib/mtncloud/cli/commands/virtual_images.rb +1362 -0
  403. data/lib/mtncloud/cli/commands/virtual_switches_command.rb +336 -0
  404. data/lib/mtncloud/cli/commands/whitelabel_settings_command.rb +653 -0
  405. data/lib/mtncloud/cli/commands/whoami.rb +323 -0
  406. data/lib/mtncloud/cli/commands/wiki_command.rb +448 -0
  407. data/lib/mtncloud/cli/commands/workflows.rb +1022 -0
  408. data/lib/mtncloud/cli/credentials.rb +482 -0
  409. data/lib/mtncloud/cli/dot_file.rb +180 -0
  410. data/lib/mtncloud/cli/error_handler.rb +307 -0
  411. data/lib/mtncloud/cli/errors.rb +53 -0
  412. data/lib/mtncloud/cli/expression_parser.rb +153 -0
  413. data/lib/mtncloud/cli/mixins/accounts_helper.rb +484 -0
  414. data/lib/mtncloud/cli/mixins/affinity_helper.rb +118 -0
  415. data/lib/mtncloud/cli/mixins/backups_helper.rb +200 -0
  416. data/lib/mtncloud/cli/mixins/deployments_helper.rb +163 -0
  417. data/lib/mtncloud/cli/mixins/execution_request_helper.rb +50 -0
  418. data/lib/mtncloud/cli/mixins/infrastructure_helper.rb +966 -0
  419. data/lib/mtncloud/cli/mixins/jobs_helper.rb +202 -0
  420. data/lib/mtncloud/cli/mixins/library_helper.rb +539 -0
  421. data/lib/mtncloud/cli/mixins/load_balancers_helper.rb +176 -0
  422. data/lib/mtncloud/cli/mixins/logs_helper.rb +131 -0
  423. data/lib/mtncloud/cli/mixins/monitoring_helper.rb +876 -0
  424. data/lib/mtncloud/cli/mixins/networks_helper.rb +62 -0
  425. data/lib/mtncloud/cli/mixins/operations_helper.rb +41 -0
  426. data/lib/mtncloud/cli/mixins/option_source_helper.rb +254 -0
  427. data/lib/mtncloud/cli/mixins/print_helper.rb +1631 -0
  428. data/lib/mtncloud/cli/mixins/processes_helper.rb +286 -0
  429. data/lib/mtncloud/cli/mixins/prompt_helper.rb +136 -0
  430. data/lib/mtncloud/cli/mixins/provisioning_helper.rb +2963 -0
  431. data/lib/mtncloud/cli/mixins/remote_helper.rb +139 -0
  432. data/lib/mtncloud/cli/mixins/rest_command.rb +994 -0
  433. data/lib/mtncloud/cli/mixins/secondary_rest_command.rb +700 -0
  434. data/lib/mtncloud/cli/mixins/storage_servers_helper.rb +93 -0
  435. data/lib/mtncloud/cli/mixins/storage_volumes_helper.rb +76 -0
  436. data/lib/mtncloud/cli/mixins/vdi_helper.rb +246 -0
  437. data/lib/mtncloud/cli/mixins/whoami_helper.rb +73 -0
  438. data/lib/mtncloud/cli/option_parser.rb +127 -0
  439. data/lib/mtncloud/cli/option_types.rb +1622 -0
  440. data/lib/mtncloud/cli/version.rb +6 -0
  441. data/lib/mtncloud/cli.rb +105 -0
  442. data/lib/mtncloud/currency.rb +184 -0
  443. data/lib/mtncloud/ext/hash.rb +109 -0
  444. data/lib/mtncloud/ext/nil_class.rb +5 -0
  445. data/lib/mtncloud/ext/rest_client.rb +37 -0
  446. data/lib/mtncloud/ext/string.rb +66 -0
  447. data/lib/mtncloud/formatters.rb +511 -0
  448. data/lib/mtncloud/logging.rb +185 -0
  449. data/lib/mtncloud/morpkg.rb +119 -0
  450. data/lib/mtncloud/rest_client.rb +104 -0
  451. data/lib/mtncloud/routes.rb +304 -0
  452. data/lib/mtncloud/terminal.rb +528 -0
  453. data/lib/mtncloud/util.rb +29 -0
  454. data/lib/mtncloud.rb +5 -0
  455. data/mtncloud-cli.gemspec +38 -0
  456. data/test/api/affinity_groups_interface_test.rb +43 -0
  457. data/test/api/clients_interface_test.rb +23 -0
  458. data/test/api/clouds_interface_test.rb +67 -0
  459. data/test/api/containers_interface_test.rb +68 -0
  460. data/test/api/host_vm_groups_interface_test.rb +35 -0
  461. data/test/api/instances_interface_test.rb +22 -0
  462. data/test/api/network_routers_interface_test.rb +22 -0
  463. data/test/api/systems_interface_test.rb +26 -0
  464. data/test/api/whoami_interface_test.rb +14 -0
  465. data/test/branding_test.rb +160 -0
  466. data/test/cli/access_token_test.rb +36 -0
  467. data/test/cli/affinity_groups_cloud_cluster_scope_test.rb +270 -0
  468. data/test/cli/affinity_groups_cluster_scope_test.rb +171 -0
  469. data/test/cli/affinity_groups_test.rb +57 -0
  470. data/test/cli/affinity_groups_violations_scope_test.rb +141 -0
  471. data/test/cli/affinity_helper_test.rb +367 -0
  472. data/test/cli/auth_test.rb +88 -0
  473. data/test/cli/backups_test.rb +212 -0
  474. data/test/cli/cli_test.rb +48 -0
  475. data/test/cli/clients_test.rb +45 -0
  476. data/test/cli/clusters_uuid_test.rb +146 -0
  477. data/test/cli/containers_test.rb +92 -0
  478. data/test/cli/distributed_workers_test.rb +68 -0
  479. data/test/cli/help_test.rb +25 -0
  480. data/test/cli/host_vm_groups_cluster_scope_test.rb +187 -0
  481. data/test/cli/host_vm_groups_test.rb +63 -0
  482. data/test/cli/infrastructure_helper_cluster_resolver_test.rb +250 -0
  483. data/test/cli/instances_test.rb +37 -0
  484. data/test/cli/man_test.rb +14 -0
  485. data/test/cli/migrations_test.rb +39 -0
  486. data/test/cli/network_routers_test.rb +65 -0
  487. data/test/cli/remote_test.rb +89 -0
  488. data/test/cli/roles_test.rb +47 -0
  489. data/test/cli/shell_test.rb +81 -0
  490. data/test/cli/storage_volumes_test.rb +79 -0
  491. data/test/cli/systems_test.rb +206 -0
  492. data/test/cli/version_test.rb +23 -0
  493. data/test/cli/view_test.rb +55 -0
  494. data/test/cli/whoami_test.rb +17 -0
  495. data/test/mtncloud_test.rb +16 -0
  496. data/test/test_case.rb +342 -0
  497. data/test/test_config.rb +137 -0
  498. data/test/test_data_helper.rb +97 -0
  499. metadata +807 -0
@@ -0,0 +1,2549 @@
1
+ require 'mtncloud/cli/cli_command'
2
+
3
+ class MtnCloud::Cli::Apps
4
+ include MtnCloud::Cli::CliCommand
5
+ include MtnCloud::Cli::AccountsHelper # needed? replace with OptionSourceHelper
6
+ include MtnCloud::Cli::OptionSourceHelper
7
+ include MtnCloud::Cli::ProvisioningHelper
8
+ include MtnCloud::Cli::ProcessesHelper
9
+ include MtnCloud::Cli::LogsHelper
10
+ include MtnCloud::Cli::ExecutionRequestHelper
11
+
12
+ set_command_name :apps
13
+ set_command_description "View and manage apps."
14
+ register_subcommands :list, :count, :get, :view, :add, :update, :remove, :cancel_removal, :add_instance, :remove_instance, :logs, :security_groups, :apply_security_groups, :history
15
+ register_subcommands :refresh, :prepare_apply, :apply, :state #:plan << this command isn't created yet
16
+ register_subcommands :stop, :start, :restart
17
+ register_subcommands :wiki, :update_wiki
18
+ #register_subcommands :firewall_disable, :firewall_enable
19
+ #register_subcommands :validate # add --validate instead
20
+ alias_subcommand :details, :get
21
+ set_default_subcommand :list
22
+
23
+ # hide these for now
24
+ set_subcommands_hidden :prepare_apply
25
+
26
+ def initialize()
27
+ # @appliance_name, @appliance_url = MtnCloud::Cli::Remote.active_appliance
28
+ end
29
+
30
+ def connect(opts)
31
+ @api_client = establish_remote_appliance_connection(opts)
32
+ @accounts_interface = @api_client.accounts
33
+ @account_users_interface = @api_client.account_users
34
+ @apps_interface = @api_client.apps
35
+ @blueprints_interface = @api_client.blueprints
36
+ @instance_types_interface = @api_client.instance_types
37
+ @library_layouts_interface = @api_client.library_layouts
38
+ @instances_interface = @api_client.instances
39
+ @options_interface = @api_client.options
40
+ @groups_interface = @api_client.groups
41
+ @clouds_interface = @api_client.clouds
42
+ @logs_interface = @api_client.logs
43
+ @processes_interface = @api_client.processes
44
+ @active_group_id = MtnCloud::Cli::Groups.active_groups[@appliance_name]
45
+ end
46
+
47
+ def handle(args)
48
+ handle_subcommand(args)
49
+ end
50
+
51
+ def list(args)
52
+ params = {}
53
+ options = {}
54
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
55
+ opts.banner = subcommand_usage()
56
+ opts.on( '-t', '--type TYPE', "Filter by type" ) do |val|
57
+ options[:type] = val
58
+ end
59
+ opts.on( '--blueprint BLUEPRINT', "Blueprint Name or ID" ) do |val|
60
+ options[:blueprint] = val
61
+ end
62
+ opts.on( '--owner USER', "Owner Username or ID" ) do |val|
63
+ options[:owner] = val
64
+ end
65
+ opts.on( '--created-by USER', "[DEPRECATED] Alias for --owner" ) do |val|
66
+ options[:owner] = val
67
+ end
68
+ opts.add_hidden_option('--created-by')
69
+ opts.on('--pending-removal', "Include apps pending removal.") do
70
+ options[:showDeleted] = true
71
+ end
72
+ opts.on('--pending-removal-only', "Only apps pending removal.") do
73
+ options[:deleted] = true
74
+ end
75
+ opts.on('--environment ENV', "Filter by environment code (appContext)") do |val|
76
+ # environment means appContext
77
+ params['environment'] = (params['environment'] || []) + val.to_s.split(',').collect {|s| s.strip }.select {|s| s != "" }
78
+ end
79
+ opts.on('--status STATUS', "Filter by status.") do |val|
80
+ params['status'] = (params['status'] || []) + val.to_s.split(',').collect {|s| s.strip }.select {|s| s != "" }
81
+ end
82
+ opts.on('-l', '--labels LABEL', String, "Filter by labels, can match any of the values") do |val|
83
+ add_query_parameter(params, 'labels', parse_labels(val))
84
+ end
85
+ opts.on('--all-labels LABEL', String, "Filter by labels, must match all of the values") do |val|
86
+ add_query_parameter(params, 'allLabels', parse_labels(val))
87
+ end
88
+ opts.on('-a', '--details', "Display all details: memory and storage usage used / max values." ) do
89
+ options[:details] = true
90
+ end
91
+ build_standard_list_options(opts, options)
92
+ opts.footer = "List apps."
93
+ end
94
+ optparse.parse!(args)
95
+ # verify_args!(args:args, optparse:optparse, count:0)
96
+ if args.count > 0
97
+ options[:phrase] = args.join(" ")
98
+ end
99
+ connect(options)
100
+
101
+ if options[:type]
102
+ params['type'] = [options[:type]].flatten.collect {|it| it.to_s.strip.split(",") }.flatten.collect {|it| it.to_s.strip }
103
+ end
104
+ if options[:blueprint]
105
+ blueprint_ids = [options[:blueprint]].flatten.collect {|it| it.to_s.strip.split(",") }.flatten.collect {|it| it.to_s.strip }
106
+ params['blueprintId'] = blueprint_ids.collect do |blueprint_id|
107
+ if blueprint_id.to_s =~ /\A\d{1,}\Z/
108
+ return blueprint_id
109
+ else
110
+ blueprint = find_blueprint_by_name_or_id(blueprint_id)
111
+ return 1 if blueprint.nil?
112
+ blueprint['id']
113
+ end
114
+ end
115
+ end
116
+ if options[:owner]
117
+ owner_ids = [options[:owner]].flatten.collect {|it| it.to_s.strip.split(",") }.flatten.collect {|it| it.to_s.strip }
118
+ params['ownerId'] = owner_ids.collect do |owner_id|
119
+ if owner_id.to_s =~ /\A\d{1,}\Z/
120
+ return owner_id
121
+ else
122
+ user = find_available_user_option(owner_id)
123
+ return 1 if user.nil?
124
+ user['id']
125
+ end
126
+ end
127
+ end
128
+ params.merge!(parse_list_options(options))
129
+ account = nil
130
+ if options[:owner]
131
+ created_by_ids = find_all_user_ids(account ? account['id'] : nil, options[:owner])
132
+ return if created_by_ids.nil?
133
+ params['createdBy'] = created_by_ids
134
+ # params['ownerId'] = created_by_ids # 4.2.1+
135
+ end
136
+
137
+ params['showDeleted'] = options[:showDeleted] if options.key?(:showDeleted)
138
+ params['deleted'] = options[:deleted] if options.key?(:deleted)
139
+
140
+ @apps_interface.setopts(options)
141
+ if options[:dry_run]
142
+ print_dry_run @apps_interface.dry.list(params)
143
+ return
144
+ end
145
+ json_response = @apps_interface.list(params)
146
+ render_response(json_response, options, "apps") do
147
+ apps = json_response['apps']
148
+ title = "Apps"
149
+ subtitles = []
150
+ subtitles += parse_list_subtitles(options)
151
+ print_h1 title, subtitles, options
152
+ if apps.empty?
153
+ print cyan,"No apps found.",reset,"\n"
154
+ else
155
+ print_apps_table(apps, options)
156
+ print_results_pagination(json_response)
157
+ end
158
+ print reset,"\n"
159
+ end
160
+ return 0, nil
161
+ end
162
+
163
+ def count(args)
164
+ options = {}
165
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
166
+ opts.banner = subcommand_usage("[options]")
167
+ opts.on( '--owner USER', "Owner Username or ID" ) do |val|
168
+ options[:owner] = val
169
+ end
170
+ opts.on( '--created-by USER', "Alias for --owner" ) do |val|
171
+ options[:owner] = val
172
+ end
173
+ opts.add_hidden_option('--created-by')
174
+ opts.on( '-s', '--search PHRASE', "Search Phrase" ) do |phrase|
175
+ options[:phrase] = phrase
176
+ end
177
+ build_common_options(opts, options, [:query, :remote, :dry_run])
178
+ opts.footer = "Get the number of apps."
179
+ end
180
+ optparse.parse!(args)
181
+ connect(options)
182
+ begin
183
+ params = {}
184
+ params.merge!(parse_list_options(options))
185
+ account = nil
186
+ if options[:owner]
187
+ created_by_ids = find_all_user_ids(account ? account['id'] : nil, options[:owner])
188
+ return if created_by_ids.nil?
189
+ params['createdBy'] = created_by_ids
190
+ # params['ownerId'] = created_by_ids # 4.2.1+
191
+ end
192
+ @apps_interface.setopts(options)
193
+ if options[:dry_run]
194
+ print_dry_run @apps_interface.dry.list(params)
195
+ return
196
+ end
197
+ json_response = @apps_interface.list(params)
198
+ # print number only
199
+ if json_response['meta'] && json_response['meta']['total']
200
+ print cyan, json_response['meta']['total'], reset, "\n"
201
+ else
202
+ print yellow, "unknown", reset, "\n"
203
+ end
204
+ return 0
205
+ rescue RestClient::Exception => e
206
+ print_rest_exception(e, options)
207
+ exit 1
208
+ end
209
+ end
210
+
211
+ def add(args)
212
+ options = {}
213
+ params = {}
214
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
215
+ opts.banner = subcommand_usage("[name] [options]")
216
+ #build_option_type_options(opts, options, add_app_option_types(false))
217
+ # these come from build_options_types
218
+ opts.on( '-b', '--blueprint BLUEPRINT', "Blueprint Name or ID. The default value is 'existing' which means no blueprint, for creating a blank app and adding existing instances." ) do |val|
219
+ options[:blueprint] = val
220
+ end
221
+ opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
222
+ options[:group] = val
223
+ end
224
+ opts.on( '-c', '--cloud CLOUD', "Default Cloud Name or ID." ) do |val|
225
+ options[:cloud] = val
226
+ end
227
+ opts.on( '--name VALUE', String, "Name" ) do |val|
228
+ options[:name] = val
229
+ end
230
+ opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
231
+ options[:options]['labels'] = parse_labels(val)
232
+ end
233
+ opts.on( '--description VALUE', String, "Description" ) do |val|
234
+ options[:description] = val
235
+ end
236
+ opts.on( '-e', '--environment VALUE', "Environment Name" ) do |val|
237
+ options[:environment] = val.to_s == 'null' ? nil : val
238
+ end
239
+ # config is being deprecated in favor of the standard --payload options
240
+ # opts.add_hidden_option(['config', 'config-dir', 'config-file', 'config-yaml'])
241
+ opts.on('--validate','--validate', "Validate Only. Validates the configuration and skips creating it.") do
242
+ options[:validate_only] = true
243
+ end
244
+ opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
245
+ options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
246
+ end
247
+ build_common_options(opts, options, [:options, :payload, :json, :yaml, :dry_run, :quiet])
248
+ opts.footer = "Create a new app.\n" +
249
+ "[name] is required. This is the name of the new app. It may also be passed as --name or inside your config."
250
+ end
251
+ optparse.parse!(args)
252
+ if args.count > 1
253
+ print_error MtnCloud::Terminal.angry_prompt
254
+ puts_error "#{command_name} add expects 0-1 arguments and received #{args.count}: #{args}\n#{optparse}"
255
+ return 1
256
+ end
257
+ # allow name as first argument
258
+ if args[0] # && !options[:name]
259
+ options[:name] = args[0]
260
+ end
261
+ connect(options)
262
+ begin
263
+ options[:options] ||= {}
264
+ passed_options = (options[:options] || {}).reject {|k,v| k.is_a?(Symbol) }
265
+ payload = {}
266
+ if options[:payload]
267
+ # payload is from parsed json|yaml files or arguments.
268
+ payload = options[:payload]
269
+ # merge -O options
270
+ payload.deep_merge!(passed_options) unless passed_options.empty?
271
+ # support some options on top of --payload
272
+ [:name, :description, :environment].each do |k|
273
+ if options.key?(k)
274
+ payload[k.to_s] = options[k]
275
+ end
276
+ end
277
+ else
278
+ # prompt for payload
279
+ payload = {}
280
+ # merge -O options
281
+ payload.deep_merge!(passed_options) unless passed_options.empty?
282
+
283
+ # this could have some special -O context, like -O tier.Web.0.instance.name
284
+ # tier_config_options = payload.delete('tier')
285
+
286
+ # Blueprint
287
+ blueprint_id = 'existing'
288
+ blueprint = nil
289
+ if options[:blueprint]
290
+ blueprint_id = options[:blueprint]
291
+ options[:options]['blueprint'] = options[:blueprint]
292
+ end
293
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'blueprint', 'fieldLabel' => 'Blueprint', 'type' => 'select', 'selectOptions' => get_available_blueprints(), 'required' => true, 'defaultValue' => 'existing', 'description' => "The blueprint to use. The default value is 'existing' which means no template, for creating a blank app and adding existing instances."}], options[:options])
294
+ blueprint_id = v_prompt['blueprint']
295
+
296
+ if blueprint_id.to_s.empty? || blueprint_id == 'existing'
297
+ blueprint = {"id" => "existing", "name" => "Existing Instances", "value" => "existing", "type" => "morpheus"}
298
+ else
299
+ blueprint = find_blueprint_by_name_or_id(blueprint_id)
300
+ if blueprint.nil?
301
+ #print_red_alert "Blueprint not found by name or id '#{blueprint_id}'"
302
+ return 1
303
+ end
304
+ end
305
+
306
+ payload['templateId'] = blueprint['id'] # for pre-3.6 api
307
+ payload['blueprintId'] = blueprint['id']
308
+ payload['blueprintName'] = blueprint['name'] #for future api plz
309
+
310
+ # Name
311
+ options[:options]['name'] = options[:name] if options.key?(:name)
312
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Enter a name for this app'}], options[:options])
313
+ payload['name'] = v_prompt['name']
314
+
315
+ # Description
316
+ options[:options]['description'] = options[:description] if options.key?(:description)
317
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false}], options[:options])
318
+ payload['description'] = v_prompt['description']
319
+
320
+
321
+ # Group
322
+ group_id = nil
323
+ options[:options]['group'] = options[:group] if options.key?(:group)
324
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'selectOptions' => get_available_groups(), 'required' => true, 'defaultValue' => @active_group_id}], options[:options])
325
+ group_id = v_prompt['group']
326
+
327
+ group = find_group_by_name_or_id_for_provisioning(group_id)
328
+ return 1 if group.nil?
329
+ payload['group'] = {'id' => group['id'], 'name' => group['name']}
330
+
331
+ # Default Cloud
332
+ cloud_id = nil
333
+ cloud = nil
334
+ scoped_available_clouds = get_available_clouds(group['id'])
335
+ if options[:cloud]
336
+ cloud_id = options[:cloud]
337
+ else
338
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'cloud', 'fieldLabel' => 'Default Cloud', 'type' => 'select', 'selectOptions' => scoped_available_clouds}], options[:options])
339
+ cloud_id = v_prompt['cloud'] unless v_prompt['cloud'].to_s.empty?
340
+ end
341
+ if cloud_id
342
+ cloud = find_cloud_by_name_or_id_for_provisioning(group['id'], cloud_id)
343
+ payload['defaultCloud'] = {'id' => cloud ? cloud['id'] : cloud_id}
344
+ end
345
+
346
+ # Environment
347
+ selected_environment = nil
348
+ available_environments = get_available_environments()
349
+ if options[:environment]
350
+ payload['environment'] = options[:environment]
351
+ else
352
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'environment', 'fieldLabel' => 'Environment', 'type' => 'select', 'selectOptions' => available_environments}], options[:options], @api_client)
353
+ payload['environment'] = v_prompt['environment'] unless v_prompt['environment'].to_s.empty?
354
+ end
355
+ selected_environment = nil
356
+ if payload['environment']
357
+ selected_environment = available_environments.find {|it| it['code'] == payload['environment'] || it['name'] == payload['environment'] }
358
+ if selected_environment.nil?
359
+ print_red_alert "Environment not found by name or code '#{payload['environment']}'"
360
+ return 1
361
+ end
362
+ end
363
+ # payload['appContext'] = payload['environment'] if payload['environment']
364
+
365
+ # Labels
366
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'labels', 'fieldLabel' => 'Labels', 'type' => 'text'}], options[:options])
367
+ payload['labels'] = parse_labels(v_prompt['labels']) unless v_prompt['labels'].to_s.empty?
368
+
369
+ # Configure (Tiers and their instances)
370
+ if !payload['tiers']
371
+ if payload['blueprintId'] != 'existing'
372
+
373
+ # fetch the app template
374
+ blueprint = find_blueprint_by_name_or_id(payload['blueprintId'])
375
+ return 1 if blueprint.nil?
376
+
377
+ unless options[:quiet]
378
+ print cyan, "Configuring app with blueprint id: #{blueprint['id']}, name: #{blueprint['name']}, type: #{blueprint['type']}\n"
379
+ end
380
+
381
+ blueprint_type = blueprint['type'] || 'morpheus'
382
+ if blueprint_type == 'morpheus'
383
+ # configure each tier and instance in the blueprint
384
+ # tiers are a map, heh, sort them by tierIndex
385
+ tiers = blueprint["config"]["tiers"] ? blueprint["config"]["tiers"] : (blueprint["tiers"] || {})
386
+ sorted_tiers = tiers.collect {|k,v| [k,v] }.sort {|a,b| a[1]['tierIndex'] <=> b[1]['tierIndex'] }
387
+ payload.delete('defaultCloud')
388
+ sorted_tiers.each do |tier_obj|
389
+ tier_name = tier_obj[0]
390
+ tier_config = tier_obj[1]
391
+ payload['tiers'] ||= {}
392
+ payload['tiers'][tier_name] ||= tier_config.clone
393
+ # remove instances, they will be iterated over and merged back in
394
+ tier_instances = payload['tiers'][tier_name].delete("instances")
395
+ # remove other blank stuff
396
+ if payload['tiers'][tier_name]['linkedTiers'] && payload['tiers'][tier_name]['linkedTiers'].empty?
397
+ payload['tiers'][tier_name].delete('linkedTiers')
398
+ end
399
+ # remove extra instance options at tierName.index, probabl need a namespace here like tier.TierName.index
400
+ tier_extra_options = {}
401
+ if payload[tier_name]
402
+ tier_extra_options = payload.delete(tier_name)
403
+ end
404
+ tier_instance_types = tier_instances ? tier_instances.collect {|it| (it['instance'] && it['instance']['type']) ? it['instance']['type'].to_s : 'unknown'}.compact : []
405
+ unless options[:quiet]
406
+ # print cyan, "Configuring Tier: #{tier_name} (#{tier_instance_types.empty? ? 'empty' : tier_instance_types.join(', ')})", "\n"
407
+ print cyan, "Configuring tier #{tier_name}", reset, "\n"
408
+ end
409
+ # todo: also prompt for tier settings here, like linkedTiers: []
410
+ if tier_instances
411
+ tier_instances = tier_config['instances'] || []
412
+ tier_instances.each_with_index do |instance_config, instance_index|
413
+ instance_type_code = instance_config['type']
414
+ if instance_config['instance'] && instance_config['instance']['type']
415
+ instance_type_code = instance_config['instance']['type']
416
+ end
417
+ if instance_type_code.nil?
418
+ print_red_alert "Unable to determine instance type for tier: #{tier_name} index: #{instance_index}"
419
+ return 1
420
+ else
421
+ unless options[:quiet]
422
+ print cyan, "Configuring #{instance_type_code} instance #{tier_name}.#{instance_index}", reset, "\n"
423
+ end
424
+
425
+ # Cloud
426
+ # cloud_id = nil
427
+ # v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'cloud', 'fieldLabel' => 'Cloud', 'type' => 'select', 'selectOptions' => scoped_available_clouds, 'defaultValue' => cloud ? cloud['name'] : nil}], options[:options])
428
+ # cloud_id = v_prompt['cloud'] unless v_prompt['cloud'].to_s.empty?
429
+ # if cloud_id
430
+ # # cloud = find_cloud_by_name_or_id_for_provisioning(group['id'], cloud_id)
431
+ # cloud = scoped_available_clouds.find {|it| it['name'] == cloud_id.to_s } || scoped_available_clouds.find {|it| it['id'].to_s == cloud_id.to_s }
432
+ # return 1 if cloud.nil?
433
+ # else
434
+ # # prompt still happens inside get_scoped_instance_config
435
+ # end
436
+
437
+
438
+ # prompt for the cloud for this instance
439
+ # the cloud is part of finding the scoped config in the blueprint
440
+ scoped_instance_config = get_scoped_instance_config(instance_config.clone, selected_environment ? selected_environment['name'] : nil, group ? group['name'] : nil, cloud ? cloud['name'] : nil)
441
+
442
+ # now configure an instance like normal, use the config as default options with :always_prompt
443
+ instance_prompt_options = {}
444
+ instance_prompt_options[:group] = group ? group['id'] : nil
445
+ #instance_prompt_options[:cloud] = cloud ? cloud['name'] : nil
446
+ instance_prompt_options[:default_cloud] = cloud ? cloud['name'] : nil
447
+ instance_prompt_options[:environment] = selected_environment ? selected_environment['code'] : nil
448
+ instance_prompt_options[:default_security_groups] = scoped_instance_config['securityGroups'] ? scoped_instance_config['securityGroups'] : nil
449
+
450
+ instance_prompt_options[:no_prompt] = options[:no_prompt]
451
+ #instance_prompt_options[:always_prompt] = options[:no_prompt] != true # options[:always_prompt]
452
+ instance_prompt_options[:options] = scoped_instance_config # meh, actually need to make these default values instead..
453
+ #instance_prompt_options[:options][:always_prompt] = instance_prompt_options[:no_prompt] != true
454
+ instance_prompt_options[:options][:no_prompt] = instance_prompt_options[:no_prompt]
455
+ # also allow arbritrary options passed as tierName.instanceIndex like Web.0.instance.layout.id=75
456
+ instance_extra_options = {}
457
+ if tier_extra_options && tier_extra_options[instance_index.to_s]
458
+ instance_extra_options = tier_extra_options[instance_index.to_s]
459
+ end
460
+ instance_prompt_options[:options].deep_merge!(instance_extra_options)
461
+
462
+ #instance_prompt_options[:name_required] = true
463
+ instance_prompt_options[:instance_type_code] = instance_type_code
464
+ # todo: an effort to render more useful help eg. -O Web.0.instance.name
465
+ help_field_prefix = "#{tier_name}.#{instance_index}"
466
+ instance_prompt_options[:help_field_prefix] = help_field_prefix
467
+ instance_prompt_options[:options][:help_field_prefix] = help_field_prefix
468
+ instance_prompt_options[:locked_fields] = scoped_instance_config['lockedFields']
469
+ instance_prompt_options[:for_app] = true
470
+ instance_prompt_options[:skip_labels_prompt] = true
471
+ # or could do this: instance_prompt_options[:labels] = default_labels
472
+ # this provisioning helper method handles all (most) of the parsing and prompting
473
+ scoped_instance_config = Marshal.load( Marshal.dump(scoped_instance_config) )
474
+ instance_config_payload = prompt_new_instance(instance_prompt_options)
475
+
476
+ # strip all empty string and nil
477
+ instance_config_payload.deep_compact!
478
+ # use the blueprint config as the base
479
+ final_config = scoped_instance_config.clone
480
+ # merge the prompted values
481
+ final_config.deep_merge!(instance_config_payload)
482
+ final_config.delete('environments')
483
+ final_config.delete('groups')
484
+ final_config.delete('clouds')
485
+ final_config.delete('lockedFields')
486
+ final_config.delete('userRemovedFields')
487
+ # should not need this...
488
+ final_config.delete(:no_prompt)
489
+ final_config.delete(:help_field_prefix)
490
+ # add config to payload
491
+ payload['tiers'][tier_name]['instances'] ||= []
492
+ payload['tiers'][tier_name]['instances'] << final_config
493
+ end
494
+ end
495
+ else
496
+ puts yellow, "Tier '#{tier_name}' is empty", reset
497
+ end
498
+ end
499
+ elsif blueprint_type == 'terraform'
500
+ # prompt for Terraform config
501
+ # todo
502
+ elsif blueprint_type == 'arm'
503
+ # prompt for ARM config
504
+ # todo
505
+ elsif blueprint_type == 'cloudFormation'
506
+ # prompt for cloudFormation config
507
+ # todo
508
+ else
509
+ print yellow, "Unknown template type: #{blueprint_type})", "\n"
510
+ end
511
+ end
512
+ end
513
+ end
514
+
515
+ @apps_interface.setopts(options)
516
+
517
+ # Validate Only
518
+ if options[:validate_only] == true
519
+ # Validate Only Dry run
520
+ if options[:dry_run]
521
+ if options[:json]
522
+ puts as_json(payload, options)
523
+ elsif options[:yaml]
524
+ puts as_yaml(payload, options)
525
+ else
526
+ print_dry_run @apps_interface.dry.validate(payload)
527
+ end
528
+ return 0
529
+ end
530
+ json_response = @apps_interface.validate(payload)
531
+
532
+ if options[:json]
533
+ puts as_json(json_response, options)
534
+ else
535
+ if !options[:quiet]
536
+ if json_response['success'] == true
537
+ print_green_success "New app '#{payload['name']}' validation passed. #{json_response['msg']}".strip
538
+ else
539
+ print_red_alert "New app '#{payload['name']}' validation failed. #{json_response['msg']}".strip
540
+ # looks for special error format like instances.instanceErrors
541
+ if json_response['errors'] && json_response['errors']['instances']
542
+ json_response['errors']['instances'].each do |error_obj|
543
+ tier_name = error_obj['tier']
544
+ instance_index = error_obj['index']
545
+ instance_errors = error_obj['instanceErrors']
546
+ print_error red, "#{tier_name} : #{instance_index}", reset, "\n"
547
+ if instance_errors
548
+ instance_errors.each do |err_key, err_msg|
549
+ print_error red, " * #{err_key} : #{err_msg}", reset, "\n"
550
+ end
551
+ end
552
+ end
553
+ else
554
+ # a default way to print errors
555
+ (json_response['errors'] || {}).each do |error_key, error_msg|
556
+ if error_key != 'instances'
557
+ print_error red, " * #{error_key} : #{error_msg}", reset, "\n"
558
+ end
559
+ end
560
+ end
561
+ end
562
+ end
563
+ end
564
+ if json_response['success'] == true
565
+ return 0
566
+ else
567
+ return 1
568
+ end
569
+ end
570
+
571
+ @apps_interface.setopts(options)
572
+ if options[:dry_run]
573
+ print_dry_run @apps_interface.dry.create(payload)
574
+ return 0
575
+ end
576
+
577
+ json_response = @apps_interface.create(payload)
578
+
579
+ if options[:json]
580
+ puts as_json(json_response, options)
581
+ print "\n"
582
+ elsif !options[:quiet]
583
+ app = json_response["app"]
584
+ print_green_success "Added app #{app['name']}"
585
+ # add existing instances to blank app now?
586
+ if !options[:no_prompt] && !payload['tiers'] && payload['id'] == 'existing'
587
+ if ::MtnCloud::Cli::OptionTypes::confirm("Would you like to add an instance now?", options.merge({default: false}))
588
+ add_instance([app['id']])
589
+ while ::MtnCloud::Cli::OptionTypes::confirm("Add another instance?", options.merge({default: false})) do
590
+ add_instance([app['id']])
591
+ end
592
+ end
593
+ end
594
+ # print details
595
+ get_args = [app['id']] + (options[:remote] ? ["-r",options[:remote]] : []) + (options[:refresh_interval] ? ['--refresh', options[:refresh_interval].to_s] : [])
596
+ get(get_args)
597
+ end
598
+ return 0
599
+ rescue RestClient::Exception => e
600
+ #print_rest_exception(e, options)
601
+ json_response = nil
602
+ begin
603
+ json_response = JSON.parse(e.response.to_s)
604
+ rescue TypeError, JSON::ParserError => ex
605
+ print_error red, "Failed to parse JSON response: #{ex}", reset, "\n"
606
+ end
607
+ if json_response && (json_response['errors'].nil? || json_response['errors'].empty?)
608
+ # The default way to print error msg
609
+ print_rest_exception(e, options)
610
+ else
611
+ # print errors and look for special errors.instances
612
+ # todo: just handle sub lists of errors default error handler (print_rest_exception)
613
+ (json_response['errors'] || {}).each do |error_key, error_msg|
614
+ if error_key != 'instances'
615
+ print_error red, " * #{error_key} : #{error_msg}", reset, "\n"
616
+ end
617
+ end
618
+ # looks for special error format like instances.instanceErrors
619
+ if json_response['errors'] && json_response['errors']['instances']
620
+ json_response['errors']['instances'].each do |error_obj|
621
+ tier_name = error_obj['tier']
622
+ instance_index = error_obj['index']
623
+ instance_errors = error_obj['instanceErrors']
624
+ print_error red, "#{tier_name} : #{instance_index}", reset, "\n"
625
+ if instance_errors
626
+ instance_errors.each do |err_key, err_msg|
627
+ print_error red, " * #{err_key} : #{err_msg}", reset, "\n"
628
+ end
629
+ end
630
+ end
631
+ end
632
+ end
633
+ exit 1
634
+ end
635
+ end
636
+
637
+ def get(args)
638
+ options = {}
639
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
640
+ opts.banner = subcommand_usage("[app]")
641
+ opts.on('--refresh [SECONDS]', String, "Refresh until status is running,failed. Default interval is #{default_refresh_interval} seconds.") do |val|
642
+ options[:refresh_until_status] ||= "running,failed"
643
+ if !val.to_s.empty?
644
+ options[:refresh_interval] = val.to_f
645
+ end
646
+ end
647
+ opts.on('--refresh-until STATUS', String, "Refresh until a specified status is reached.") do |val|
648
+ options[:refresh_until_status] = val.to_s.downcase
649
+ end
650
+ build_standard_get_options(opts, options)
651
+ opts.footer = "Get details about an app.\n" +
652
+ "[app] is required. This is the name or id of an app. Supports 1-N [app] arguments."
653
+ end
654
+ optparse.parse!(args)
655
+ if args.count < 1
656
+ print_error MtnCloud::Terminal.angry_prompt
657
+ puts_error "#{command_name} get expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
658
+ return 1
659
+ end
660
+
661
+ connect(options)
662
+
663
+ id_list = parse_id_list(args)
664
+ return run_command_for_each_arg(id_list) do |arg|
665
+ _get(arg, options)
666
+ end
667
+
668
+ end
669
+
670
+ def _get(id, options={})
671
+ app = nil
672
+ if id.to_s !~ /\A\d{1,}\Z/
673
+ app = find_app_by_name_or_id(id)
674
+ id = app['id']
675
+ end
676
+ @apps_interface.setopts(options)
677
+ if options[:dry_run]
678
+ print_dry_run @apps_interface.dry.get(id)
679
+ return
680
+ end
681
+ json_response = @apps_interface.get(id.to_i)
682
+ render_response(json_response, options, 'app') do
683
+ app = json_response['app']
684
+ # API used to only return apps.appTiers including instances with lots of detail
685
+ # now returns simplified instance list as "instances" for terraform, etc with only id,name
686
+ # so load the instance details if needed
687
+ app_tiers = []
688
+ instances = []
689
+ if app['appTiers'] && !app['appTiers'].empty?
690
+ # appTiers contains instances with lots of detail
691
+ app_tiers = app['appTiers']
692
+ app_tiers.each do |app_tier|
693
+ instances += (app_tier['appInstances'] || []).collect {|it| it['instance']}.flatten().compact
694
+ end
695
+ elsif app['instances'] && !app['instances'].empty?
696
+ # need to load instance details which are not returned in this simple list
697
+ instance_ids = app['instances'].collect {|it| it['id'] }
698
+ instances = @instances_interface.list({id: instance_ids})['instances']
699
+ end
700
+ print_h1 "App Details", [], options
701
+ print cyan
702
+ description_cols = {
703
+ "ID" => 'id',
704
+ "Name" => 'name',
705
+ "Labels" => lambda {|it| format_list(it['labels']) rescue '' },
706
+ "Description" => 'description',
707
+ "Type" => lambda {|it|
708
+ if it['type']
709
+ format_blueprint_type(it['type'])
710
+ else
711
+ format_blueprint_type(it['blueprint'] ? it['blueprint']['type'] : nil)
712
+ end
713
+ },
714
+ "Blueprint" => lambda {|it| it['blueprint'] ? it['blueprint']['name'] : '' },
715
+ "Group" => lambda {|it| it['group'] ? it['group']['name'] : it['siteId'] },
716
+ "Environment" => lambda {|it| it['appContext'] },
717
+ "Owner" => lambda {|it| it['owner'] ? it['owner']['username'] : '' },
718
+ #"Tenant" => lambda {|it| it['account'] ? it['account']['name'] : '' },
719
+ "Tiers" => lambda {|it|
720
+ # it['instanceCount']
721
+ tiers = []
722
+ app_tiers.each do |app_tier|
723
+ tiers << app_tier['tier']
724
+ end
725
+ "(#{(tiers || []).size()}) #{tiers.collect {|it| it.is_a?(Hash) ? it['name'] : it }.join(',')}"
726
+ },
727
+ "Instances" => lambda {|it|
728
+ # it['instanceCount']
729
+ "(#{instances.count}) #{instances.collect {|it| it['name'] }.join(',')}"
730
+ },
731
+ "Containers" => lambda {|it|
732
+ #it['containerCount']
733
+ containers = []
734
+ instances.each do |instance|
735
+ containers += (instance['containers'] || [])
736
+ end
737
+ #"(#{containers.count})"
738
+ "(#{containers.count}) #{containers.collect {|it| it }.join(',')}"
739
+ },
740
+ "Status" => lambda {|it| format_app_status(it) }
741
+ }
742
+
743
+ description_cols["Removal Date"] = lambda {|it| format_local_dt(it['removalDate'])} if app['status'] == 'pendingRemoval'
744
+
745
+ # if app['blueprint'].nil?
746
+ # description_cols.delete("Blueprint")
747
+ # end
748
+ # if app['description'].nil?
749
+ # description_cols.delete("Description")
750
+ # end
751
+ print_description_list(description_cols, app)
752
+
753
+ stats = app['stats']
754
+ if app['instanceCount'].to_i > 0
755
+ print_h2 "App Usage", options
756
+ print_stats_usage(stats, {include: [:memory, :storage]})
757
+ end
758
+
759
+ if app_tiers.empty?
760
+ print reset,"\n"
761
+ if instances.empty?
762
+ print cyan, "This app is empty", reset, "\n\n"
763
+ else
764
+ print_h2 "Instances", options
765
+ instances_rows = instances.collect do |instance|
766
+ connection_string = ''
767
+ if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
768
+ connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
769
+ end
770
+ {id: instance['id'], name: instance['name'], connection: connection_string, environment: instance['instanceContext'], nodes: (instance['containers'] || []).count, status: format_instance_status(instance), type: instance['instanceType']['name'], group: !instance['group'].nil? ? instance['group']['name'] : nil, cloud: !instance['cloud'].nil? ? instance['cloud']['name'] : nil}
771
+ end
772
+ instances_rows = instances_rows.sort {|x,y| x[:id] <=> y[:id] } #oldest to newest..
773
+ print cyan
774
+ print as_pretty_table(instances_rows, [:id, :name, :cloud, :type, :environment, :nodes, :connection, :status], {border_style: options[:border_style]})
775
+ print reset
776
+ print "\n"
777
+ end
778
+ else
779
+ app_tiers.each do |app_tier|
780
+ # print_h2 "Tier: #{app_tier['tier']['name']}", options
781
+ print_h2 "#{app_tier['tier']['name']}", options
782
+ print cyan
783
+ tier_instances = (app_tier['appInstances'] || []).collect {|it| it['instance']}
784
+ instance_list = tier_instances.collect { |tier_instance| instances.find { |i| i['id'] == tier_instance['id'] } }
785
+ if instance_list.empty?
786
+ puts yellow, "This tier is empty", reset
787
+ else
788
+ instances_rows = instance_list.collect do |instance|
789
+ connection_string = ''
790
+ if !instance['connectionInfo'].nil? && instance['connectionInfo'].empty? == false
791
+ connection_string = "#{instance['connectionInfo'][0]['ip']}:#{instance['connectionInfo'][0]['port']}"
792
+ end
793
+ {id: instance['id'], name: instance['displayName'] ? instance['displayName'] : instance['name'], connection: connection_string, environment: instance['instanceContext'], nodes: (instance['containers'] || []).count, status: format_instance_status(instance), type: instance['instanceType']['name'], group: !instance['group'].nil? ? instance['group']['name'] : nil, cloud: !instance['cloud'].nil? ? instance['cloud']['name'] : nil}
794
+ end
795
+ instances_rows = instances_rows.sort {|x,y| x[:id] <=> y[:id] } #oldest to newest..
796
+ print cyan
797
+ print as_pretty_table(instances_rows, [:id, :name, :cloud, :type, :environment, :nodes, :connection, :status], {border_style: options[:border_style]})
798
+ print reset
799
+ print "\n"
800
+ end
801
+ end
802
+ end
803
+ print cyan
804
+
805
+
806
+ # refresh until a status is reached
807
+ if options[:refresh_until_status]
808
+ if options[:refresh_interval].nil? || options[:refresh_interval].to_f < 0
809
+ options[:refresh_interval] = default_refresh_interval
810
+ end
811
+ statuses = options[:refresh_until_status].to_s.downcase.split(",").collect {|s| s.strip }.select {|s| !s.to_s.empty? }
812
+ if !statuses.include?(app['status'])
813
+ print cyan, "Refreshing in #{options[:refresh_interval] > 1 ? options[:refresh_interval].to_i : options[:refresh_interval]} seconds"
814
+ sleep_with_dots(options[:refresh_interval])
815
+ print "\n"
816
+ _get(app['id'], options)
817
+ end
818
+ end
819
+ end
820
+ return 0, nil
821
+ end
822
+
823
+ def update(args)
824
+ params, payload, options = {}, {}, {}
825
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
826
+ opts.banner = subcommand_usage("[app] [options]")
827
+ #build_option_type_options(opts, options, update_app_option_types(false))
828
+ opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
829
+ options[:group] = val
830
+ end
831
+ opts.on( '--name VALUE', String, "Name" ) do |val|
832
+ options[:name] = val
833
+ end
834
+ opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
835
+ options[:options]['labels'] = parse_labels(val)
836
+ end
837
+ opts.on( '--description VALUE', String, "Description" ) do |val|
838
+ options[:description] = val
839
+ end
840
+ opts.on( '--environment VALUE', String, "Environment" ) do |val|
841
+ options[:environment] = val
842
+ end
843
+ opts.on( '--owner USER', "Owner Username or ID" ) do |val|
844
+ options[:owner] = val == 'null' ? nil : val
845
+ end
846
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run])
847
+ opts.footer = "Update an app.\n" +
848
+ "[app] is required. This is the name or id of an app."
849
+ end
850
+ optparse.parse!(args)
851
+ if args.count != 1
852
+ print_error MtnCloud::Terminal.angry_prompt
853
+ puts_error "#{command_name} update expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
854
+ return 1
855
+ end
856
+ connect(options)
857
+
858
+ begin
859
+ app = find_app_by_name_or_id(args[0])
860
+ return 1 if app.nil?
861
+ if options[:payload]
862
+ payload = options[:payload]
863
+ end
864
+ payload['app'] ||= {}
865
+ payload.deep_merge!({'app' => parse_passed_options(options)})
866
+ if options[:name]
867
+ payload['app']['name'] = options[:name]
868
+ end
869
+ if options[:description]
870
+ payload['app']['description'] = options[:description]
871
+ end
872
+ if options[:environment]
873
+ # payload['app']['environment'] = options[:environment]
874
+ payload['app']['appContext'] = options[:environment]
875
+ end
876
+ if options[:group]
877
+ group = find_group_by_name_or_id_for_provisioning(options[:group])
878
+ return 1 if group.nil?
879
+ payload['app']['group'] = {'id' => group['id'], 'name' => group['name']}
880
+ end
881
+ if options.key?(:owner)
882
+ owner_id = options[:owner]
883
+ if owner_id.to_s.empty?
884
+ # allow clearing
885
+ owner_id = nil
886
+ elsif options[:owner]
887
+ if owner_id.to_s =~ /\A\d{1,}\Z/
888
+ # allow id without lookup
889
+ else
890
+ user = find_available_user_option(owner_id)
891
+ return 1 if user.nil?
892
+ owner_id = user['id']
893
+ end
894
+ end
895
+ payload['app']['ownerId'] = owner_id
896
+ end
897
+ if payload['app'] && payload['app'].empty?
898
+ payload.delete('app')
899
+ end
900
+ if payload.empty?
901
+ raise_command_error "Specify at least one option to update.\n#{optparse}" if payload.empty?
902
+ end
903
+
904
+ @apps_interface.setopts(options)
905
+ if options[:dry_run]
906
+ print_dry_run @apps_interface.dry.update(app["id"], payload)
907
+ return
908
+ end
909
+ json_response = @apps_interface.update(app["id"], payload)
910
+ render_result = render_with_format(json_response, options)
911
+ return 0 if render_result
912
+ print_green_success "Updated app #{app['name']}"
913
+ get([app['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
914
+ return 0
915
+ rescue RestClient::Exception => e
916
+ print_rest_exception(e, options)
917
+ exit 1
918
+ end
919
+ end
920
+
921
+ def refresh(args)
922
+ params, payload, options = {}, {}, {}
923
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
924
+ opts.banner = subcommand_usage("[app] [options]")
925
+ build_standard_update_options(opts, options, [:auto_confirm])
926
+ opts.footer = <<-EOT
927
+ Refresh an app.
928
+ [app] is required. This is the name or id of an app.
929
+ This is only supported by certain types of apps.
930
+ EOT
931
+ end
932
+ optparse.parse!(args)
933
+ verify_args!(args:args, optparse:optparse, count:1)
934
+ connect(options)
935
+
936
+ begin
937
+ app = find_app_by_name_or_id(args[0])
938
+ return 1 if app.nil?
939
+ # construct request
940
+ params.merge!(parse_query_options(options))
941
+ payload = {}
942
+ if options[:payload]
943
+ payload = options[:payload]
944
+ payload.deep_merge!(parse_passed_options(options))
945
+ else
946
+ payload.deep_merge!(parse_passed_options(options))
947
+ # raise_command_error "Specify at least one option to update.\n#{optparse}" if payload.empty?
948
+ end
949
+ unless options[:yes] || MtnCloud::Cli::OptionTypes.confirm("Are you sure you want to refresh this app: #{app['name']}?")
950
+ return 9, "aborted command"
951
+ end
952
+ @apps_interface.setopts(options)
953
+ if options[:dry_run]
954
+ print_dry_run @apps_interface.dry.refresh(app["id"], params, payload)
955
+ return
956
+ end
957
+ json_response = @apps_interface.refresh(app["id"], params, payload)
958
+ render_response(json_response, options) do
959
+ print_green_success "Refreshing app #{app['name']}"
960
+ # return _get(app['id'], options)
961
+ end
962
+ return 0, nil
963
+ rescue RestClient::Exception => e
964
+ print_rest_exception(e, options)
965
+ exit 1
966
+ end
967
+ end
968
+
969
+ def prepare_apply(args)
970
+ params, payload, options = {}, {}, {}
971
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
972
+ opts.banner = subcommand_usage("[app] [options]")
973
+ build_standard_update_options(opts, options, [:auto_confirm])
974
+ opts.footer = <<-EOT
975
+ Prepare to apply an app.
976
+ [app] is required. This is the name or id of an app.
977
+ Displays the current configuration data used by the apply command.
978
+ This is only supported by certain types of apps such as terraform.
979
+ EOT
980
+ end
981
+ optparse.parse!(args)
982
+ if args.count != 1
983
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(', ')}\n#{optparse}"
984
+ end
985
+ connect(options)
986
+
987
+ begin
988
+ app = find_app_by_name_or_id(args[0])
989
+ return 1 if app.nil?
990
+ # construct request
991
+ params.merge!(parse_query_options(options))
992
+ payload = {}
993
+ if options[:payload]
994
+ payload = options[:payload]
995
+ payload.deep_merge!(parse_passed_options(options))
996
+ else
997
+ payload.deep_merge!(parse_passed_options(options))
998
+ # raise_command_error "Specify at least one option to update.\n#{optparse}" if payload.empty?
999
+ end
1000
+ @apps_interface.setopts(options)
1001
+ if options[:dry_run]
1002
+ print_dry_run @apps_interface.dry.prepare_apply(app["id"], params)
1003
+ return
1004
+ end
1005
+ json_response = @apps_interface.prepare_apply(app["id"], params)
1006
+ render_result = render_with_format(json_response, options)
1007
+ return 0 if render_result
1008
+ # print_green_success "Prepared to apply app: #{app['name']}"
1009
+ print_h1 "Prepared App: #{app['name']}"
1010
+ app_config = json_response['data']
1011
+ # app_config = json_response if app_config.nil?
1012
+ puts as_yaml(app_config, options)
1013
+ #return get([app['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
1014
+ print "\n", reset
1015
+ return 0
1016
+ rescue RestClient::Exception => e
1017
+ print_rest_exception(e, options)
1018
+ exit 1
1019
+ end
1020
+ end
1021
+
1022
+ def apply(args)
1023
+ default_refresh_interval = 5
1024
+ params, payload, options = {}, {}, {}
1025
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1026
+ opts.banner = subcommand_usage("[app] [options]")
1027
+ opts.on( '-p', '--parameter NAME=VALUE', "Template parameter name and value" ) do |val|
1028
+ k, v = val.split("=")
1029
+ options[:options]['templateParameter'] ||= {}
1030
+ options[:options]['templateParameter'][k] = v
1031
+ end
1032
+ opts.on('--refresh [SECONDS]', String, "Refresh until execution is complete. Default interval is #{default_refresh_interval} seconds.") do |val|
1033
+ options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
1034
+ end
1035
+ opts.on(nil, '--no-refresh', "Do not refresh" ) do
1036
+ options[:no_refresh] = true
1037
+ end
1038
+ opts.on(nil, '--no-validate', "Do not validate planned changes before apply" ) do
1039
+ options[:no_validate] = true
1040
+ end
1041
+ opts.on(nil, '--validate-only', "Only validate planned changes, do not execute the apply command." ) do
1042
+ options[:validate_only] = true
1043
+ end
1044
+ build_standard_update_options(opts, options, [:auto_confirm])
1045
+ opts.footer = <<-EOT
1046
+ Apply an app.
1047
+ [app] is required. This is the name or id of an app.
1048
+ This is only supported by certain types of apps such as terraform.
1049
+ By default this executes two requests to validate and then apply the changes.
1050
+ The first request corresponds to the terraform plan command only.
1051
+ Use --no-validate to skip this step apply changes in one step.
1052
+ EOT
1053
+ end
1054
+ optparse.parse!(args)
1055
+ verify_args!(args:args, optparse:optparse, count:1)
1056
+ connect(options)
1057
+
1058
+ app = find_app_by_name_or_id(args[0])
1059
+ return 1 if app.nil?
1060
+ # construct request
1061
+ params.merge!(parse_query_options(options))
1062
+ payload = {}
1063
+ if options[:payload]
1064
+ payload = options[:payload]
1065
+ payload.deep_merge!(parse_passed_options(options))
1066
+ else
1067
+ payload.deep_merge!(parse_passed_options(options))
1068
+ # attempt to load prepare-apply to get templateParameter values and prompt for them
1069
+ # ok, actually use options/layoutParameters to get the list of parameters
1070
+ begin
1071
+ prepare_apply_json_response = @apps_interface.prepare_apply(app["id"])
1072
+ config = prepare_apply_json_response['data']
1073
+ variable_map = config['templateParameter']
1074
+ # need to load the instance details to get the app cloud...ugh
1075
+ first_instance = app['instances'][0]
1076
+ instance = first_instance ? find_instance_by_name_or_id(first_instance['id']) : nil
1077
+ zone_id = instance ? instance['cloud']['id'] : nil
1078
+ api_params = {templateId: app['blueprint']['id'], appId: app['id'], zoneId: zone_id, siteId: app['group']['id']}
1079
+ layout_parameters = @options_interface.options_for_source('templateParameters',api_params)['data']
1080
+
1081
+ if layout_parameters && !layout_parameters.empty?
1082
+ variable_option_types = []
1083
+ i = 0
1084
+ layout_parameters.each do |layout_parameter|
1085
+ var_label = layout_parameter['displayName'] || layout_parameter['name']
1086
+ var_name = layout_parameter['name']
1087
+ var_value = variable_map ? variable_map[var_name] : layout_parameter['defaultValue']
1088
+ if var_value.nil? && layout_parameter['defaultValue']
1089
+ var_value = layout_parameter['defaultValue']
1090
+ end
1091
+ var_type = (layout_parameter['passwordType'] || layout_parameter['sensitive']) ? 'password' : 'text'
1092
+ option_type = {'fieldContext' => 'templateParameter', 'fieldName' => var_name, 'fieldLabel' => var_label, 'type' => var_type, 'required' => true, 'defaultValue' => (var_value.to_s.empty? ? nil : var_value.to_s), 'displayOrder' => (i+1) }
1093
+ variable_option_types << option_type
1094
+ i+=1
1095
+ end
1096
+ blueprint_type_display = format_blueprint_type(config['type'])
1097
+ if blueprint_type_display == "terraform"
1098
+ blueprint_type_display = "Terraform"
1099
+ end
1100
+ print_h2 "#{blueprint_type_display} Variables"
1101
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt(variable_option_types, options[:options], @api_client)
1102
+ v_prompt.deep_compact!
1103
+ payload.deep_merge!(v_prompt)
1104
+ end
1105
+ rescue RestClient::Exception => ex
1106
+ # if e.response && e.response.code == 404
1107
+ MtnCloud::Logging::DarkPrinter.puts "Unable to load config for app apply, skipping parameter prompting" if MtnCloud::Logging.debug?
1108
+ # print_rest_exception(ex, options)
1109
+ # end
1110
+ end
1111
+ end
1112
+
1113
+ @apps_interface.setopts(options)
1114
+ if options[:validate_only]
1115
+ # validate only
1116
+ if options[:dry_run]
1117
+ print_dry_run @apps_interface.dry.validate_apply(app["id"], params, payload)
1118
+ return
1119
+ end
1120
+ json_response = @apps_interface.validate_apply(app["id"], params, payload)
1121
+ print_green_success "Validating app #{app['name']}"
1122
+ execution_id = json_response['executionId']
1123
+ if !options[:no_refresh]
1124
+ #MtnCloud::Cli::ExecutionRequestCommand.new.handle(["get", execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
1125
+ validate_execution_request = wait_for_execution_request(execution_id, options)
1126
+ end
1127
+ elsif options[:no_validate]
1128
+ # skip validate, apply only
1129
+ if options[:dry_run]
1130
+ print_dry_run @apps_interface.dry.apply(app["id"], params, payload)
1131
+ return
1132
+ end
1133
+ json_response = @apps_interface.apply(app["id"], params, payload)
1134
+ render_response(json_response, options) do
1135
+ print_green_success "Applying app #{app['name']}"
1136
+ execution_id = json_response['executionId']
1137
+ if !options[:no_refresh]
1138
+ #MtnCloud::Cli::ExecutionRequestCommand.new.handle(["get", execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
1139
+ apply_execution_request = wait_for_execution_request(execution_id, options)
1140
+ end
1141
+ end
1142
+ else
1143
+ # validate and then apply
1144
+ if options[:dry_run]
1145
+ print_dry_run @apps_interface.dry.validate_apply(app["id"], params, payload)
1146
+ print_dry_run @apps_interface.dry.apply(app["id"], params, payload)
1147
+ return
1148
+ end
1149
+ json_response = @apps_interface.validate_apply(app["id"], params, payload)
1150
+ print_green_success "Validating app #{app['name']}"
1151
+ execution_id = json_response['executionId']
1152
+ validate_execution_request = wait_for_execution_request(execution_id, options)
1153
+ if validate_execution_request['status'] != 'complete'
1154
+ print_red_alert "Validation failed. Changes will not be applied."
1155
+ return 1, "Validation failed. Changes will not be applied."
1156
+ else
1157
+ unless options[:yes] || MtnCloud::Cli::OptionTypes.confirm("Are you sure you want to apply these changes?")
1158
+ return 9, "aborted command"
1159
+ end
1160
+ json_response = @apps_interface.apply(app["id"], params, payload)
1161
+ render_response(json_response, options) do
1162
+ print_green_success "Applying app #{app['name']}"
1163
+ execution_id = json_response['executionId']
1164
+ if !options[:no_refresh]
1165
+ #MtnCloud::Cli::ExecutionRequestCommand.new.handle(["get", execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
1166
+ apply_execution_request = wait_for_execution_request(execution_id, options)
1167
+ end
1168
+ end
1169
+ end
1170
+ end
1171
+ return 0, nil
1172
+ end
1173
+
1174
+ def state(args)
1175
+ params, payload, options = {}, {}, {}
1176
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1177
+ opts.banner = subcommand_usage("[app] [options]")
1178
+ opts.on('--data', "Display State Data") do
1179
+ options[:include_state_data] = true
1180
+ end
1181
+ opts.on('--specs', "Display Spec Templates") do
1182
+ options[:include_spec_templates] = true
1183
+ end
1184
+ opts.on('--plan', "Display Plan Data") do
1185
+ options[:include_plan_data] = true
1186
+ end
1187
+ opts.on('--input', "Display Input") do
1188
+ options[:include_input] = true
1189
+ end
1190
+ opts.on('--output', "Display Output") do
1191
+ options[:include_output] = true
1192
+ end
1193
+ opts.on('-a','--all', "Display All Details") do
1194
+ options[:include_state_data] = true
1195
+ options[:include_spec_templates] = true
1196
+ options[:include_plan_data] = true
1197
+ options[:include_input] = true
1198
+ options[:include_output] = true
1199
+ options[:details] = true
1200
+ end
1201
+ build_standard_get_options(opts, options)
1202
+ opts.footer = <<-EOT
1203
+ View state of an app.
1204
+ [app] is required. This is the name or id of an app.
1205
+ This is only supported by certain types of apps such as terraform.
1206
+ EOT
1207
+ end
1208
+ optparse.parse!(args)
1209
+ verify_args!(args:args, optparse:optparse, count:1)
1210
+ connect(options)
1211
+ app = find_app_by_name_or_id(args[0])
1212
+ return 1 if app.nil?
1213
+ # construct request
1214
+ params.merge!(parse_query_options(options))
1215
+ @apps_interface.setopts(options)
1216
+ if options[:dry_run]
1217
+ print_dry_run @apps_interface.dry.state(app["id"], params)
1218
+ return
1219
+ end
1220
+ json_response = @apps_interface.state(app["id"], params)
1221
+ render_result = render_with_format(json_response, options)
1222
+ return 0 if render_result
1223
+ print_h1 "App State: #{app['name']}", options
1224
+ # print_h2 "Workloads", options
1225
+ if json_response['workloads'] && !json_response['workloads'].empty?
1226
+ workload_columns = {
1227
+ "Name" => lambda {|it| it['subRefName'].to_s.empty? ? "#{it['refName']}" : "#{it['refName']} - #{it['subRefName']}" },
1228
+ "Last Check" => lambda {|it| format_local_dt(it['stateDate']) },
1229
+ "Status" => lambda {|it| format_ok_status(it['status'] || 'ok') },
1230
+ "Drift Status" => lambda {|it| it['iacDrift'] ? "Drift" : "No Drift" }
1231
+ }
1232
+ print as_pretty_table(json_response['workloads'], workload_columns.upcase_keys!, options)
1233
+ else
1234
+ print cyan,"No workloads found.",reset,"\n"
1235
+ end
1236
+ if options[:include_state_data]
1237
+ print_h2 "State Data", options
1238
+ puts json_response['stateData']
1239
+ end
1240
+ if options[:include_spec_templates]
1241
+ print_h2 "Spec Templates", options
1242
+ spec_templates_columns = {
1243
+ "Resource Spec" => lambda {|it| it['name'] || (it['template'] ? it['template']['name'] : nil) },
1244
+ "Attached to Source Template" => lambda {|it| format_boolean(!it['isolated']) },
1245
+ "Source Spec Template" => lambda {|it| (it['template'] ? it['template']['name'] : nil) || it['name'] }
1246
+ }
1247
+ print as_pretty_table(json_response['specs'], spec_templates_columns.upcase_keys!, options)
1248
+ # print "\n", reset
1249
+ end
1250
+ if options[:include_plan_data]
1251
+ # print_h2 "Plan Data", options
1252
+ if app['type'] == 'terraform'
1253
+ print_h2 "Terraform Plan", options
1254
+ else
1255
+ print_h2 "Plan Data", options
1256
+ end
1257
+ puts json_response['planData']
1258
+ # print "\n", reset
1259
+ end
1260
+ if options[:include_input]
1261
+ # print_h2 "Input", options
1262
+ if json_response['input'] && json_response['input']['variables']
1263
+ print_h2 "VARIABLES", options
1264
+ input_variable_columns = {
1265
+ "Name" => lambda {|it| it['name'] },
1266
+ "Value" => lambda {|it| it['value'] }
1267
+ }
1268
+ print as_pretty_table(json_response['input']['variables'], input_variable_columns.upcase_keys!, options)
1269
+ end
1270
+ if json_response['input'] && json_response['input']['providers']
1271
+ print_h2 "PROVIDERS", options
1272
+ input_provider_columns = {
1273
+ "Name" => lambda {|it| it['name'] }
1274
+ }
1275
+ print as_pretty_table(json_response['input']['providers'], input_provider_columns.upcase_keys!, options)
1276
+ end
1277
+ if json_response['input'] && json_response['input']['data']
1278
+ print_h2 "DATA", options
1279
+ input_data_columns = {
1280
+ "Type" => lambda {|it| it['type'] },
1281
+ "Key" => lambda {|it| it['key'] },
1282
+ "Name" => lambda {|it| it['name'] }
1283
+ }
1284
+ print as_pretty_table(json_response['input']['data'], input_data_columns.upcase_keys!, options)
1285
+ end
1286
+ # print "\n", reset
1287
+ end
1288
+ if options[:include_output]
1289
+ # print_h2 "Output", options
1290
+ if json_response['output'] && json_response['output']['outputs']
1291
+ print_h2 "OUTPUTS", options
1292
+ input_variable_columns = {
1293
+ "Name" => lambda {|it| it['name'] },
1294
+ "Value" => lambda {|it| it['value'] }
1295
+ }
1296
+ print as_pretty_table(json_response['output']['outputs'], input_variable_columns.upcase_keys!, options)
1297
+ end
1298
+ # print "\n", reset
1299
+ end
1300
+ print "\n", reset
1301
+ return 0
1302
+ end
1303
+
1304
+ def add_instance(args)
1305
+ options = {}
1306
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1307
+ opts.banner = subcommand_usage("[app] [instance] [tier]")
1308
+ build_common_options(opts, options, [:options, :json, :dry_run])
1309
+ opts.footer = "Add an existing instance to an app.\n" +
1310
+ "[app] is required. This is the name or id of an app." + "\n" +
1311
+ "[instance] is required. This is the name or id of an instance." + "\n" +
1312
+ "[tier] is required. This is the name of the tier."
1313
+ end
1314
+ optparse.parse!(args)
1315
+ if args.count < 1 || args.count > 3
1316
+ print_error MtnCloud::Terminal.angry_prompt
1317
+ puts_error "#{command_name} add-instance expects 1-3 arguments and received #{args.count}: #{args}\n#{optparse}"
1318
+ return 1
1319
+ end
1320
+ # optional [tier] and [instance] arguments
1321
+ if args[1] && args[1] !~ /\A\-/
1322
+ options[:instance_name] = args[1]
1323
+ if args[2] && args[2] !~ /\A\-/
1324
+ options[:tier_name] = args[2]
1325
+ end
1326
+ end
1327
+ connect(options)
1328
+ begin
1329
+ app = find_app_by_name_or_id(args[0])
1330
+
1331
+ # Only supports adding an existing instance right now..
1332
+
1333
+ payload = {}
1334
+
1335
+ if options[:instance_name]
1336
+ instance = find_instance_by_name_or_id(options[:instance_name])
1337
+ else
1338
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'instance', 'fieldLabel' => 'Instance', 'type' => 'text', 'required' => true, 'description' => 'Enter the instance name or id'}], options[:options])
1339
+ instance = find_instance_by_name_or_id(v_prompt['instance'])
1340
+ end
1341
+ payload[:instanceId] = instance['id']
1342
+
1343
+ if options[:tier_name]
1344
+ payload[:tierName] = options[:tier_name]
1345
+ else
1346
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'tier', 'fieldLabel' => 'Tier', 'type' => 'text', 'required' => true, 'description' => 'Enter the name of the tier'}], options[:options])
1347
+ payload[:tierName] = v_prompt['tier']
1348
+ end
1349
+ @apps_interface.setopts(options)
1350
+ if options[:dry_run]
1351
+ print_dry_run @apps_interface.dry.add_instance(app['id'], payload)
1352
+ return
1353
+ end
1354
+ json_response = @apps_interface.add_instance(app['id'], payload)
1355
+ if options[:json]
1356
+ print JSON.pretty_generate(json_response)
1357
+ print "\n"
1358
+ else
1359
+ print_green_success "Added instance #{instance['name']} to app #{app['name']}"
1360
+ #get(app['id'])
1361
+ end
1362
+ return 0
1363
+ rescue RestClient::Exception => e
1364
+ print_rest_exception(e, options)
1365
+ exit 1
1366
+ end
1367
+ end
1368
+
1369
+ def remove(args)
1370
+ options = {}
1371
+ query_params = {}
1372
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1373
+ opts.banner = subcommand_usage("[app]")
1374
+ #JD: UI defaults to on, but perhaps better to be explicate for now.
1375
+ opts.on('--remove-instances [on|off]', ['on','off'], "Remove instances. Default is off.") do |val|
1376
+ query_params[:removeInstances] = val.nil? ? 'on' : val
1377
+ end
1378
+ opts.on('--preserve-volumes [on|off]', ['on','off'], "Preserve Volumes. Default is off. Applies to certain types only.") do |val|
1379
+ query_params[:preserveVolumes] = val.nil? ? 'on' : val
1380
+ end
1381
+ opts.on( '--keep-backups', '--keep-backups', "Preserve copy of backups" ) do
1382
+ query_params[:keepBackups] = 'on'
1383
+ end
1384
+ opts.on('--release-ips [on|off]', ['on','off'], "Release Floating IPs. Default is on. Applies to certain types only. Only applies when used with --remove-instances") do |val|
1385
+ query_params[:releaseFloatingIps] = val.nil? ? 'on' : val
1386
+ query_params[:releaseEIPs] = query_params[:releaseFloatingIps] # old parameter before 6.0
1387
+ end
1388
+ opts.on('--releaseEIPs [on|off]', ['on','off'], "Alias for Release Floating IPs") do |val|
1389
+ query_params[:releaseFloatingIps] = val.nil? ? 'on' : val
1390
+ query_params[:releaseEIPs] = query_params[:releaseFloatingIps] # old parameter before 6.0
1391
+ end
1392
+ opts.add_hidden_option('--releaseEIPs')
1393
+ opts.on( '-f', '--force', "Force Delete" ) do
1394
+ query_params[:force] = 'on'
1395
+ end
1396
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :auto_confirm])
1397
+ opts.footer = "Delete an app.\n" +
1398
+ "[app] is required. This is the name or id of an app."
1399
+ end
1400
+ optparse.parse!(args)
1401
+ if args.count != 1
1402
+ print_error MtnCloud::Terminal.angry_prompt
1403
+ puts_error "#{command_name} remove expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
1404
+ return 1
1405
+ end
1406
+ connect(options)
1407
+
1408
+ begin
1409
+ app = find_app_by_name_or_id(args[0])
1410
+ unless options[:yes] || ::MtnCloud::Cli::OptionTypes::confirm("Are you sure you would like to remove the app '#{app['name']}'?", options)
1411
+ return 9
1412
+ end
1413
+ # JD: removeVolumes to maintain the old behavior with pre-3.5.2 appliances, remove me later
1414
+ if query_params[:preserveVolumes].nil?
1415
+ query_params[:removeVolumes] = 'on'
1416
+ end
1417
+ @apps_interface.setopts(options)
1418
+ if options[:dry_run]
1419
+ print_dry_run @apps_interface.dry.destroy(app['id'], query_params)
1420
+ return
1421
+ end
1422
+ json_response = @apps_interface.destroy(app['id'], query_params)
1423
+ if options[:json]
1424
+ print JSON.pretty_generate(json_response)
1425
+ print "\n"
1426
+ elsif !options[:quiet]
1427
+ print_green_success "Removed app #{app['name']}"
1428
+ #list([])
1429
+ end
1430
+ rescue RestClient::Exception => e
1431
+ print_rest_exception(e, options)
1432
+ exit 1
1433
+ end
1434
+ end
1435
+
1436
+ def cancel_removal(args)
1437
+ options = {}
1438
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1439
+ opts.banner = subcommand_usage("[app]")
1440
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
1441
+ end
1442
+ optparse.parse!(args)
1443
+ if args.count < 1
1444
+ puts optparse
1445
+ exit 1
1446
+ end
1447
+ connect(options)
1448
+ begin
1449
+ app = find_app_by_name_or_id(args[0])
1450
+ @apps_interface.setopts(options)
1451
+ if options[:dry_run]
1452
+ print_dry_run @apps_interface.dry.cancel_removal(app['id'])
1453
+ return
1454
+ end
1455
+ json_response = @apps_interface.cancel_removal(app['id'])
1456
+ if options[:json]
1457
+ print as_json(json_response, options), "\n"
1458
+ return
1459
+ elsif !options[:quiet]
1460
+ get([app['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
1461
+ end
1462
+ rescue RestClient::Exception => e
1463
+ print_rest_exception(e, options)
1464
+ exit 1
1465
+ end
1466
+ end
1467
+
1468
+ def remove_instance(args)
1469
+ options = {}
1470
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1471
+ opts.banner = subcommand_usage("[app] [instance]")
1472
+ build_common_options(opts, options, [:options, :json, :dry_run])
1473
+ opts.footer = "Remove an instance from an app.\n" +
1474
+ "[app] is required. This is the name or id of an app." + "\n" +
1475
+ "[instance] is required. This is the name or id of an instance."
1476
+ end
1477
+ optparse.parse!(args)
1478
+ if args.count < 1 || args.count > 2
1479
+ print_error MtnCloud::Terminal.angry_prompt
1480
+ puts_error "#{command_name} remove-instance expects 1-2 arguments and received #{args.count}: #{args}\n#{optparse}"
1481
+ return 1
1482
+ end
1483
+ # optional [tier] and [instance] arguments
1484
+ if args[1] && args[1] !~ /\A\-/
1485
+ options[:instance_name] = args[1]
1486
+ end
1487
+ connect(options)
1488
+ begin
1489
+ app = find_app_by_name_or_id(args[0])
1490
+
1491
+ payload = {}
1492
+
1493
+ if options[:instance_name]
1494
+ instance = find_instance_by_name_or_id(options[:instance_name])
1495
+ else
1496
+ v_prompt = MtnCloud::Cli::OptionTypes.prompt([{'fieldName' => 'instance', 'fieldLabel' => 'Instance', 'type' => 'text', 'required' => true, 'description' => 'Enter the instance name or id'}], options[:options])
1497
+ instance = find_instance_by_name_or_id(v_prompt['instance'])
1498
+ end
1499
+ payload[:instanceId] = instance['id']
1500
+ @apps_interface.setopts(options)
1501
+ if options[:dry_run]
1502
+ print_dry_run @apps_interface.dry.remove_instance(app['id'], payload)
1503
+ return
1504
+ end
1505
+
1506
+ json_response = @apps_interface.remove_instance(app['id'], payload)
1507
+
1508
+ if options[:json]
1509
+ print JSON.pretty_generate(json_response)
1510
+ print "\n"
1511
+ else
1512
+ print_green_success "Removed instance #{instance['name']} from app #{app['name']}"
1513
+ #list([])
1514
+ # details_options = [app['name']]
1515
+ # details(details_options)
1516
+ end
1517
+
1518
+ rescue RestClient::Exception => e
1519
+ print_rest_exception(e, options)
1520
+ exit 1
1521
+ end
1522
+ end
1523
+
1524
+ def logs(args)
1525
+ options = {}
1526
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1527
+ opts.banner = subcommand_usage("[app]")
1528
+ # opts.on('--hosts HOSTS', String, "Filter logs to specific Host ID(s)") do |val|
1529
+ # params['servers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
1530
+ # end
1531
+ # opts.on('--servers HOSTS', String, "alias for --hosts") do |val|
1532
+ # params['servers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
1533
+ # end
1534
+ # opts.on('--vms HOSTS', String, "alias for --hosts") do |val|
1535
+ # params['servers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
1536
+ # end
1537
+ # opts.on('--container CONTAINER', String, "Filter logs to specific Container ID(s)") do |val|
1538
+ # params['containers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
1539
+ # end
1540
+ opts.on( '-n', '--node NODE_ID', "Scope logs to specific Container or VM" ) do |node_id|
1541
+ options[:node_id] = node_id.to_i
1542
+ end
1543
+ # opts.on('--nodes HOST', String, "alias for --containers") do |val|
1544
+ # params['containers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
1545
+ # end
1546
+ opts.on('--start TIMESTAMP','--start TIMESTAMP', "Start timestamp. Default is 30 days ago.") do |val|
1547
+ options[:start] = parse_time(val) #.utc.iso8601
1548
+ end
1549
+ opts.on('--end TIMESTAMP','--end TIMESTAMP', "End timestamp. Default is now.") do |val|
1550
+ options[:end] = parse_time(val) #.utc.iso8601
1551
+ end
1552
+ # opts.on('--interval TIME','--interval TIME', "Interval of time to include, in seconds. Default is 30 days ago.") do |val|
1553
+ # options[:interval] = parse_time(val).utc.iso8601
1554
+ # end
1555
+ opts.on('--level VALUE', String, "Log Level. DEBUG,INFO,WARN,ERROR") do |val|
1556
+ params['level'] = params['level'] ? [params['level'], val].flatten : [val]
1557
+ end
1558
+ opts.on('--table', '--table', "Format ouput as a table.") do
1559
+ options[:table] = true
1560
+ end
1561
+ opts.on('-a', '--all', "Display all details: entire message." ) do
1562
+ options[:details] = true
1563
+ end
1564
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
1565
+ opts.footer = "List logs for an app.\n" +
1566
+ "[app] is required. This is the name or id of an app."
1567
+ end
1568
+ optparse.parse!(args)
1569
+ if args.count !=1
1570
+ print_error MtnCloud::Terminal.angry_prompt
1571
+ puts_error "#{command_name} logs expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
1572
+ return 1
1573
+ end
1574
+ connect(options)
1575
+ begin
1576
+ app = find_app_by_name_or_id(args[0])
1577
+ container_ids = []
1578
+ # API used to only return apps.appTiers
1579
+ # now returns detailed instance list as 'instances'
1580
+ app_tiers = app['appTiers'] || []
1581
+ instances = app['instances']
1582
+ if instances.nil?
1583
+ instances = []
1584
+ app_tiers.each do |app_tier|
1585
+ instances += (app_tier['appInstances'] || []).collect {|it| it['instance']}.flatten().compact
1586
+ end
1587
+ end
1588
+ instances.each do |instance|
1589
+ container_ids += instance['containers']
1590
+ end
1591
+ if container_ids.empty?
1592
+ print cyan,"app is empty",reset,"\n"
1593
+ return 0
1594
+ # print_error yellow,"app is empty",reset,"\n"
1595
+ # return 1
1596
+ end
1597
+
1598
+ if options[:node_id]
1599
+ if container_ids.include?(options[:node_id])
1600
+ container_ids = [options[:node_id]]
1601
+ else
1602
+ print_red_alert "App does not include node #{options[:node_id]}"
1603
+ return 1
1604
+ end
1605
+ end
1606
+ params = {}
1607
+ params['level'] = params['level'].collect {|it| it.to_s.upcase }.join('|') if params['level'] # api works with INFO|WARN
1608
+ params.merge!(parse_list_options(options))
1609
+ params['query'] = params.delete('phrase') if params['phrase']
1610
+ params['order'] = params['direction'] unless params['direction'].nil? # old api version expects order instead of direction
1611
+ params['startMs'] = (options[:start].to_i * 1000) if options[:start]
1612
+ params['endMs'] = (options[:end].to_i * 1000) if options[:end]
1613
+ params['interval'] = options[:interval].to_s if options[:interval]
1614
+ @logs_interface.setopts(options)
1615
+ if options[:dry_run]
1616
+ print_dry_run @logs_interface.dry.container_logs(container_ids, params)
1617
+ return
1618
+ end
1619
+ json_response = @logs_interface.container_logs(container_ids, params)
1620
+ render_result = json_response['logs'] ? render_with_format(json_response, options, 'logs') : render_with_format(json_response, options, 'data')
1621
+ return 0 if render_result
1622
+
1623
+ title = "App Logs: #{app['name']}"
1624
+ subtitles = parse_list_subtitles(options)
1625
+ if options[:start]
1626
+ subtitles << "Start: #{options[:start]}".strip
1627
+ end
1628
+ if options[:end]
1629
+ subtitles << "End: #{options[:end]}".strip
1630
+ end
1631
+ if params[:query]
1632
+ subtitles << "Search: #{params[:query]}".strip
1633
+ end
1634
+ if params['servers']
1635
+ subtitles << "Servers: #{params['servers']}".strip
1636
+ end
1637
+ if params['containers']
1638
+ subtitles << "Containers: #{params['containers']}".strip
1639
+ end
1640
+ if params[:query]
1641
+ subtitles << "Search: #{params[:query]}".strip
1642
+ end
1643
+ if params['level']
1644
+ subtitles << "Level: #{params['level']}"
1645
+ end
1646
+ logs = json_response['data'] || json_response['logs']
1647
+ print_h1 title, subtitles, options
1648
+ if logs.empty?
1649
+ print "#{cyan}No logs found.#{reset}\n"
1650
+ else
1651
+ print format_log_records(logs, options)
1652
+ print_results_pagination({'meta'=>{'total'=>(json_response['total']['value'] rescue json_response['total']),'size'=>logs.size,'max'=>(json_response['max'] || options[:max]),'offset'=>(json_response['offset'] || options[:offset] || 0)}})
1653
+ end
1654
+ print reset,"\n"
1655
+ return 0
1656
+ rescue RestClient::Exception => e
1657
+ print_rest_exception(e, options)
1658
+ exit 1
1659
+ end
1660
+ end
1661
+
1662
+
1663
+ def stop(args)
1664
+ options = {}
1665
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1666
+ opts.banner = subcommand_usage("[app]")
1667
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
1668
+ opts.footer = "Stop an app.\n" +
1669
+ "[app] is required. This is the name or id of an app. Supports 1-N [app] arguments."
1670
+ end
1671
+ optparse.parse!(args)
1672
+ if args.count < 1
1673
+ puts_error "[id] argument is required"
1674
+ puts_error optparse
1675
+ return 1
1676
+ end
1677
+ connect(options)
1678
+ id_list = parse_id_list(args)
1679
+ unless options[:yes] || ::MtnCloud::Cli::OptionTypes::confirm("Are you sure you would like to stop #{id_list.size == 1 ? 'app' : 'apps'} #{anded_list(id_list)}?", options)
1680
+ return 9, "aborted command"
1681
+ end
1682
+ return run_command_for_each_arg(id_list) do |arg|
1683
+ _stop(arg, options)
1684
+ end
1685
+ end
1686
+
1687
+ def _stop(app_id, options)
1688
+ app = find_app_by_name_or_id(app_id)
1689
+ return 1 if app.nil?
1690
+ tier_records = extract_app_tiers(app)
1691
+ if options[:dry_run]
1692
+ print_h1 "Dry Run", [], options
1693
+ end
1694
+ tier_records.each do |tier_record|
1695
+ tier_record[:instances].each do |instance|
1696
+ stop_cmd = "instances stop #{instance['id']} -y"
1697
+ if options[:dry_run]
1698
+ puts stop_cmd
1699
+ else
1700
+ my_terminal.execute(stop_cmd)
1701
+ end
1702
+ end
1703
+ end
1704
+ return 0
1705
+ end
1706
+
1707
+ def start(args)
1708
+ options = {}
1709
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1710
+ opts.banner = subcommand_usage("[app]")
1711
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
1712
+ opts.footer = "Start an app.\n" +
1713
+ "[app] is required. This is the name or id of an app. Supports 1-N [app] arguments."
1714
+ end
1715
+ optparse.parse!(args)
1716
+ if args.count < 1
1717
+ puts_error "[id] argument is required"
1718
+ puts_error optparse
1719
+ return 1
1720
+ end
1721
+ connect(options)
1722
+ id_list = parse_id_list(args)
1723
+ unless options[:yes] || ::MtnCloud::Cli::OptionTypes::confirm("Are you sure you would like to start #{id_list.size == 1 ? 'app' : 'apps'} #{anded_list(id_list)}?", options)
1724
+ return 9, "aborted command"
1725
+ end
1726
+ return run_command_for_each_arg(id_list) do |arg|
1727
+ _start(arg, options)
1728
+ end
1729
+ end
1730
+
1731
+ def _start(app_id, options)
1732
+ app = find_app_by_name_or_id(app_id)
1733
+ return 1 if app.nil?
1734
+ tier_records = extract_app_tiers(app)
1735
+ if options[:dry_run]
1736
+ print_h1 "Dry Run", [], options
1737
+ end
1738
+ tier_records.each do |tier_record|
1739
+ tier_record[:instances].each do |instance|
1740
+ start_cmd = "instances start #{instance['id']} -y"
1741
+ if options[:dry_run]
1742
+ puts start_cmd
1743
+ else
1744
+ my_terminal.execute(start_cmd)
1745
+ end
1746
+ end
1747
+ end
1748
+ return 0
1749
+ end
1750
+
1751
+ def restart(args)
1752
+ options = {}
1753
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1754
+ opts.banner = subcommand_usage("[app]")
1755
+ build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
1756
+ opts.footer = "Restart an app.\n" +
1757
+ "[app] is required. This is the name or id of an app. Supports 1-N [app] arguments."
1758
+ end
1759
+ optparse.parse!(args)
1760
+ if args.count < 1
1761
+ puts_error "[id] argument is required"
1762
+ puts_error optparse
1763
+ return 1
1764
+ end
1765
+ connect(options)
1766
+ id_list = parse_id_list(args)
1767
+ unless options[:yes] || ::MtnCloud::Cli::OptionTypes::confirm("Are you sure you would like to restart #{id_list.size == 1 ? 'app' : 'apps'} #{anded_list(id_list)}?", options)
1768
+ return 9, "aborted command"
1769
+ end
1770
+ return run_command_for_each_arg(id_list) do |arg|
1771
+ _restart(arg, options)
1772
+ end
1773
+ end
1774
+
1775
+ def _restart(app_id, options)
1776
+ app = find_app_by_name_or_id(app_id)
1777
+ return 1 if app.nil?
1778
+ tier_records = extract_app_tiers(app)
1779
+ if options[:dry_run]
1780
+ print_h1 "Dry Run", [], options
1781
+ end
1782
+ tier_records.each do |tier_record|
1783
+ tier_record[:instances].each do |instance|
1784
+ restart_cmd = "instances restart #{instance['id']} -y"
1785
+ if options[:dry_run]
1786
+ puts restart_cmd
1787
+ else
1788
+ my_terminal.execute(restart_cmd)
1789
+ end
1790
+ end
1791
+ end
1792
+ return 0
1793
+ end
1794
+
1795
+ def firewall_disable(args)
1796
+ options = {}
1797
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1798
+ opts.banner = subcommand_usage("[app]")
1799
+ build_common_options(opts, options, [:json, :dry_run])
1800
+ end
1801
+ optparse.parse!(args)
1802
+ if args.count != 1
1803
+ print_error MtnCloud::Terminal.angry_prompt
1804
+ puts_error "#{command_name} firewall-disable expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
1805
+ return 1
1806
+ end
1807
+ connect(options)
1808
+
1809
+ begin
1810
+ app = find_app_by_name_or_id(args[0])
1811
+ @apps_interface.setopts(options)
1812
+ if options[:dry_run]
1813
+ print_dry_run @apps_interface.dry.firewall_disable(app['id'])
1814
+ return
1815
+ end
1816
+ @apps_interface.firewall_disable(app['id'])
1817
+ security_groups([args[0]])
1818
+ rescue RestClient::Exception => e
1819
+ print_rest_exception(e, options)
1820
+ exit 1
1821
+ end
1822
+ end
1823
+
1824
+ def firewall_enable(args)
1825
+ options = {}
1826
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1827
+ opts.banner = subcommand_usage("[app]")
1828
+ build_common_options(opts, options, [:json, :dry_run])
1829
+ end
1830
+ optparse.parse!(args)
1831
+ if args.count != 1
1832
+ print_error MtnCloud::Terminal.angry_prompt
1833
+ puts_error "#{command_name} firewall-enable expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
1834
+ return 1
1835
+ end
1836
+ connect(options)
1837
+
1838
+ begin
1839
+ app = find_app_by_name_or_id(args[0])
1840
+ @apps_interface.setopts(options)
1841
+ if options[:dry_run]
1842
+ print_dry_run @apps_interface.dry.firewall_enable(app['id'])
1843
+ return
1844
+ end
1845
+ @apps_interface.firewall_enable(app['id'])
1846
+ security_groups([args[0]])
1847
+ rescue RestClient::Exception => e
1848
+ print_rest_exception(e, options)
1849
+ exit 1
1850
+ end
1851
+ end
1852
+
1853
+ def security_groups(args)
1854
+ options = {}
1855
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1856
+ opts.banner = subcommand_usage("[app]")
1857
+ build_common_options(opts, options, [:json, :dry_run])
1858
+ end
1859
+ optparse.parse!(args)
1860
+ if args.count != 1
1861
+ print_error MtnCloud::Terminal.angry_prompt
1862
+ puts_error "#{command_name} security-groups expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
1863
+ return 1
1864
+ end
1865
+ connect(options)
1866
+
1867
+ begin
1868
+ app = find_app_by_name_or_id(args[0])
1869
+ @apps_interface.setopts(options)
1870
+ if options[:dry_run]
1871
+ print_dry_run @apps_interface.dry.security_groups(app['id'])
1872
+ return
1873
+ end
1874
+ json_response = @apps_interface.security_groups(app['id'])
1875
+ securityGroups = json_response['securityGroups']
1876
+ print_h1 "Security Groups for App: #{app['name']}", options
1877
+ print cyan
1878
+ print_description_list({"Firewall Enabled" => lambda {|it| format_boolean it['firewallEnabled'] } }, json_response)
1879
+ if securityGroups.empty?
1880
+ print cyan,"\n","No security groups currently applied.",reset,"\n"
1881
+ else
1882
+ print "\n"
1883
+ securityGroups.each do |securityGroup|
1884
+ print cyan, "= #{securityGroup['id']} (#{securityGroup['name']}) - (#{securityGroup['description']})\n"
1885
+ end
1886
+ end
1887
+ print reset,"\n"
1888
+
1889
+ rescue RestClient::Exception => e
1890
+ print_rest_exception(e, options)
1891
+ exit 1
1892
+ end
1893
+ end
1894
+
1895
+ def apply_security_groups(args)
1896
+ options = {}
1897
+ params = {}
1898
+ clear_or_secgroups_specified = false
1899
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1900
+ opts.banner = subcommand_usage("[app] [--clear] [-s]")
1901
+ opts.on( '-c', '--clear', "Clear all security groups" ) do
1902
+ params[:securityGroupIds] = []
1903
+ clear_or_secgroups_specified = true
1904
+ end
1905
+ opts.on( '-s', '--secgroups SECGROUPS', "Apply the specified comma separated security group ids" ) do |secgroups|
1906
+ params[:securityGroupIds] = secgroups.split(",")
1907
+ clear_or_secgroups_specified = true
1908
+ end
1909
+ opts.on( '-h', '--help', "Print this help" ) do
1910
+ puts opts
1911
+ exit
1912
+ end
1913
+ build_common_options(opts, options, [:json, :dry_run])
1914
+ end
1915
+ optparse.parse!(args)
1916
+ if args.count != 1
1917
+ print_error MtnCloud::Terminal.angry_prompt
1918
+ puts_error "#{command_name} apply-security-groups expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
1919
+ return 1
1920
+ end
1921
+ if !clear_or_secgroups_specified
1922
+ print_error MtnCloud::Terminal.angry_prompt
1923
+ puts_error "#{command_name} apply-security-groups requires either --clear or --secgroups\n#{optparse}"
1924
+ return 1
1925
+ end
1926
+
1927
+ connect(options)
1928
+
1929
+ begin
1930
+ app = find_app_by_name_or_id(args[0])
1931
+ payload = params
1932
+ @apps_interface.setopts(options)
1933
+ if options[:dry_run]
1934
+ print_dry_run @apps_interface.dry.apply_security_groups(app['id'], payload)
1935
+ return
1936
+ end
1937
+ @apps_interface.apply_security_groups(app['id'], payload)
1938
+ security_groups([args[0]])
1939
+ rescue RestClient::Exception => e
1940
+ print_rest_exception(e, options)
1941
+ exit 1
1942
+ end
1943
+ end
1944
+
1945
+ def history(args)
1946
+ raw_args = args.dup
1947
+ options = {}
1948
+ #options[:show_output] = true
1949
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
1950
+ opts.banner = subcommand_usage("[app]")
1951
+ # opts.on( '-n', '--node NODE_ID', "Scope history to specific Container or VM" ) do |node_id|
1952
+ # options[:node_id] = node_id.to_i
1953
+ # end
1954
+ opts.on( nil, '--events', "Display sub processes (events)." ) do
1955
+ options[:show_events] = true
1956
+ end
1957
+ opts.on( nil, '--output', "Display process output." ) do
1958
+ options[:show_output] = true
1959
+ end
1960
+ opts.on(nil, '--details', "Display more details. Shows everything, untruncated." ) do
1961
+ options[:show_events] = true
1962
+ options[:show_output] = true
1963
+ options[:details] = true
1964
+ end
1965
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
1966
+ opts.footer = "List historical processes for a specific app.\n" +
1967
+ "[app] is required. This is the name or id of an app."
1968
+ end
1969
+ optparse.parse!(args)
1970
+
1971
+ if args.count != 1
1972
+ puts optparse
1973
+ return 1
1974
+ end
1975
+ connect(options)
1976
+ begin
1977
+ app = find_app_by_name_or_id(args[0])
1978
+
1979
+ instance_ids = []
1980
+ # API used to only return apps.appTiers
1981
+ # now returns detailed instance list as "instances"
1982
+ app_tiers = app['appTiers'] || []
1983
+ instances = app['instances']
1984
+ if instances.nil?
1985
+ instances = []
1986
+ app_tiers.each do |app_tier|
1987
+ instances += (app_tier['appInstances'] || []).collect {|it| it['instance']}.flatten().compact
1988
+ end
1989
+ end
1990
+ instances.each do |instance|
1991
+ instance_ids << instance['id']
1992
+ end
1993
+
1994
+ # container_ids = instance['containers']
1995
+ # if options[:node_id] && container_ids.include?(options[:node_id])
1996
+ # container_ids = [options[:node_id]]
1997
+ # end
1998
+ params = {}
1999
+ params['instanceIds'] = instance_ids
2000
+ params.merge!(parse_list_options(options))
2001
+ # params['query'] = params.delete('phrase') if params['phrase']
2002
+ @processes_interface.setopts(options)
2003
+ if options[:dry_run]
2004
+ print_dry_run @processes_interface.dry.list(params)
2005
+ return
2006
+ end
2007
+ json_response = @processes_interface.list(params)
2008
+ if options[:json]
2009
+ puts as_json(json_response, options, "processes")
2010
+ return 0
2011
+ elsif options[:yaml]
2012
+ puts as_yaml(json_response, options, "processes")
2013
+ return 0
2014
+ elsif options[:csv]
2015
+ puts records_as_csv(json_response['processes'], options)
2016
+ return 0
2017
+ else
2018
+
2019
+ title = "App History: #{app['name']}"
2020
+ subtitles = []
2021
+ if params[:query]
2022
+ subtitles << "Search: #{params[:query]}".strip
2023
+ end
2024
+ subtitles += parse_list_subtitles(options)
2025
+ print_h1 title, subtitles, options
2026
+ if json_response['processes'].empty?
2027
+ print "#{cyan}No process history found.#{reset}\n\n"
2028
+ else
2029
+ history_records = []
2030
+ json_response["processes"].each do |process|
2031
+ row = {
2032
+ id: process['id'],
2033
+ eventId: nil,
2034
+ uniqueId: process['uniqueId'],
2035
+ name: process['displayName'],
2036
+ description: process['description'],
2037
+ processType: process['processType'] ? (process['processType']['name'] || process['processType']['code']) : process['processTypeName'],
2038
+ createdBy: process['createdBy'] ? (process['createdBy']['displayName'] || process['createdBy']['username']) : '',
2039
+ startDate: format_local_dt(process['startDate']),
2040
+ duration: format_process_duration(process),
2041
+ status: format_process_status(process),
2042
+ error: format_process_error(process),
2043
+ output: format_process_output(process)
2044
+ }
2045
+ history_records << row
2046
+ process_events = process['events'] || process['processEvents']
2047
+ if options[:show_events]
2048
+ if process_events
2049
+ process_events.each do |process_event|
2050
+ event_row = {
2051
+ id: process['id'],
2052
+ eventId: process_event['id'],
2053
+ uniqueId: process_event['uniqueId'],
2054
+ name: process_event['displayName'], # blank like the UI
2055
+ description: process_event['description'],
2056
+ processType: process_event['processType'] ? (process_event['processType']['name'] || process_event['processType']['code']) : process['processTypeName'],
2057
+ createdBy: process_event['createdBy'] ? (process_event['createdBy']['displayName'] || process_event['createdBy']['username']) : '',
2058
+ startDate: format_local_dt(process_event['startDate']),
2059
+ duration: format_process_duration(process_event),
2060
+ status: format_process_status(process_event),
2061
+ error: format_process_error(process_event, options[:details] ? nil : 20),
2062
+ output: format_process_output(process_event, options[:details] ? nil : 20)
2063
+ }
2064
+ history_records << event_row
2065
+ end
2066
+ else
2067
+
2068
+ end
2069
+ end
2070
+ end
2071
+ columns = [
2072
+ {:id => {:display_name => "PROCESS ID"} },
2073
+ :name,
2074
+ :description,
2075
+ {:processType => {:display_name => "PROCESS TYPE"} },
2076
+ {:createdBy => {:display_name => "CREATED BY"} },
2077
+ {:startDate => {:display_name => "START DATE"} },
2078
+ {:duration => {:display_name => "ETA/DURATION"} },
2079
+ :status,
2080
+ :error
2081
+ ]
2082
+ if options[:show_events]
2083
+ columns.insert(1, {:eventId => {:display_name => "EVENT ID"} })
2084
+ end
2085
+ if options[:show_output]
2086
+ columns << :output
2087
+ end
2088
+ # custom pretty table columns ...
2089
+ if options[:include_fields]
2090
+ columns = options[:include_fields]
2091
+ end
2092
+ print cyan
2093
+ print as_pretty_table(history_records, columns, options)
2094
+ #print_results_pagination(json_response)
2095
+ print_results_pagination(json_response, {:label => "process", :n_label => "processes"})
2096
+ print reset, "\n"
2097
+ return 0
2098
+ end
2099
+ end
2100
+ rescue RestClient::Exception => e
2101
+ print_rest_exception(e, options)
2102
+ exit 1
2103
+ end
2104
+ end
2105
+
2106
+ def view(args)
2107
+ options = {}
2108
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
2109
+ opts.banner = subcommand_usage("[app]")
2110
+ opts.on('-w','--wiki', "Open the wiki tab for this app") do
2111
+ options[:link_tab] = "wiki"
2112
+ end
2113
+ opts.on('--tab VALUE', String, "Open a specific tab") do |val|
2114
+ options[:link_tab] = val.to_s
2115
+ end
2116
+ build_common_options(opts, options, [:dry_run, :remote])
2117
+ opts.footer = "View an app in a web browser" + "\n" +
2118
+ "[app] is required. This is the name or id of an app. Supports 1-N [app] arguments."
2119
+ end
2120
+ optparse.parse!(args)
2121
+ if args.count < 1
2122
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
2123
+ end
2124
+ connect(options)
2125
+ id_list = parse_id_list(args)
2126
+ return run_command_for_each_arg(id_list) do |arg|
2127
+ _view(arg, options)
2128
+ end
2129
+ end
2130
+
2131
+ def _view(arg, options={})
2132
+ begin
2133
+ app = find_app_by_name_or_id(arg)
2134
+ return 1 if app.nil?
2135
+
2136
+ link = "#{@appliance_url}/login/oauth-redirect?access_token=#{@access_token}\\&redirectUri=/provisioning/apps/#{app['id']}"
2137
+ if options[:link_tab]
2138
+ link << "#!#{options[:link_tab]}"
2139
+ end
2140
+
2141
+ if options[:dry_run]
2142
+ puts MtnCloud::Util.open_url_command(link)
2143
+ return 0
2144
+ end
2145
+ return MtnCloud::Util.open_url(link)
2146
+ rescue RestClient::Exception => e
2147
+ print_rest_exception(e, options)
2148
+ exit 1
2149
+ end
2150
+ end
2151
+
2152
+ def wiki(args)
2153
+ options = {}
2154
+ params = {}
2155
+ open_wiki_link = false
2156
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
2157
+ opts.banner = subcommand_usage("[app]")
2158
+ opts.on('--view', '--view', "View wiki page in web browser.") do
2159
+ open_wiki_link = true
2160
+ end
2161
+ build_common_options(opts, options, [:json, :dry_run, :remote])
2162
+ opts.footer = "View wiki page details for an app." + "\n" +
2163
+ "[app] is required. This is the name or id of an app."
2164
+ end
2165
+ optparse.parse!(args)
2166
+ if args.count != 1
2167
+ puts_error "#{MtnCloud::Terminal.angry_prompt}wrong number of arguments. Expected 1 and received #{args.count} #{args.inspect}\n#{optparse}"
2168
+ return 1
2169
+ end
2170
+ connect(options)
2171
+
2172
+ begin
2173
+ app = find_app_by_name_or_id(args[0])
2174
+ return 1 if app.nil?
2175
+
2176
+
2177
+ @apps_interface.setopts(options)
2178
+ if options[:dry_run]
2179
+ print_dry_run @apps_interface.dry.wiki(app["id"], params)
2180
+ return
2181
+ end
2182
+ json_response = @apps_interface.wiki(app["id"], params)
2183
+ page = json_response['page']
2184
+
2185
+ render_result = render_with_format(json_response, options, 'page')
2186
+ return 0 if render_result
2187
+
2188
+ if page
2189
+
2190
+ # my_terminal.exec("wiki get #{page['id']}")
2191
+
2192
+ print_h1 "App Wiki Page: #{app['name']}"
2193
+ # print_h1 "Wiki Page Details"
2194
+ print cyan
2195
+
2196
+ print_description_list({
2197
+ "Page ID" => 'id',
2198
+ "Name" => 'name',
2199
+ #"Category" => 'category',
2200
+ #"Ref Type" => 'refType',
2201
+ #"Ref ID" => 'refId',
2202
+ #"Owner" => lambda {|it| it['account'] ? it['account']['name'] : '' },
2203
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
2204
+ "Created By" => lambda {|it| it['createdBy'] ? it['createdBy']['username'] : '' },
2205
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
2206
+ "Updated By" => lambda {|it| it['updatedBy'] ? it['updatedBy']['username'] : '' }
2207
+ }, page)
2208
+ print reset,"\n"
2209
+
2210
+ print_h2 "Page Content"
2211
+ print cyan, page['content'], reset, "\n"
2212
+
2213
+ else
2214
+ print "\n"
2215
+ print cyan, "No wiki page found.", reset, "\n"
2216
+ end
2217
+ print reset,"\n"
2218
+
2219
+ if open_wiki_link
2220
+ return view_wiki([args[0]])
2221
+ end
2222
+
2223
+ return 0
2224
+ rescue RestClient::Exception => e
2225
+ print_rest_exception(e, options)
2226
+ exit 1
2227
+ end
2228
+ end
2229
+
2230
+ def view_wiki(args)
2231
+ params = {}
2232
+ options = {}
2233
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
2234
+ opts.banner = subcommand_usage("[id]")
2235
+ build_common_options(opts, options, [:dry_run, :remote])
2236
+ opts.footer = "View app wiki page in a web browser" + "\n" +
2237
+ "[app] is required. This is the name or id of an app."
2238
+ end
2239
+ optparse.parse!(args)
2240
+ if args.count != 1
2241
+ raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
2242
+ end
2243
+ connect(options)
2244
+ begin
2245
+ app = find_app_by_name_or_id(args[0])
2246
+ return 1 if app.nil?
2247
+
2248
+ link = "#{@appliance_url}/login/oauth-redirect?access_token=#{@access_token}\\&redirectUri=/provisioning/apps/#{app['id']}#!wiki"
2249
+
2250
+ if options[:dry_run]
2251
+ puts MtnCloud::Util.open_url_command(link)
2252
+ return 0
2253
+ end
2254
+ return MtnCloud::Util.open_url(link)
2255
+ rescue RestClient::Exception => e
2256
+ print_rest_exception(e, options)
2257
+ exit 1
2258
+ end
2259
+ end
2260
+
2261
+ def update_wiki(args)
2262
+ options = {}
2263
+ params = {}
2264
+ optparse = MtnCloud::Cli::OptionParser.new do |opts|
2265
+ opts.banner = subcommand_usage("[app] [options]")
2266
+ build_option_type_options(opts, options, update_wiki_page_option_types)
2267
+ opts.on('--file FILE', "File containing the wiki content. This can be used instead of --content") do |filename|
2268
+ full_filename = File.expand_path(filename)
2269
+ if File.exist?(full_filename)
2270
+ params['content'] = File.read(full_filename)
2271
+ else
2272
+ print_red_alert "File not found: #{full_filename}"
2273
+ return 1
2274
+ end
2275
+ # use the filename as the name by default.
2276
+ if !params['name']
2277
+ params['name'] = File.basename(full_filename)
2278
+ end
2279
+ end
2280
+ opts.on(nil, '--clear', "Clear current page content") do |val|
2281
+ params['content'] = ""
2282
+ end
2283
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
2284
+ end
2285
+ optparse.parse!(args)
2286
+ if args.count != 1
2287
+ puts_error "#{MtnCloud::Terminal.angry_prompt}wrong number of arguments. Expected 1 and received #{args.count} #{args.inspect}\n#{optparse}"
2288
+ return 1
2289
+ end
2290
+ connect(options)
2291
+
2292
+ begin
2293
+ app = find_app_by_name_or_id(args[0])
2294
+ return 1 if app.nil?
2295
+ # construct payload
2296
+ passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
2297
+ payload = nil
2298
+ if options[:payload]
2299
+ payload = options[:payload]
2300
+ payload.deep_merge!({'page' => passed_options}) unless passed_options.empty?
2301
+ else
2302
+ payload = {
2303
+ 'page' => {
2304
+ }
2305
+ }
2306
+ # allow arbitrary -O options
2307
+ payload.deep_merge!({'page' => passed_options}) unless passed_options.empty?
2308
+ # prompt for options
2309
+ #params = MtnCloud::Cli::OptionTypes.prompt(update_wiki_page_option_types, options[:options], @api_client, options[:params])
2310
+ #params = passed_options
2311
+ params.deep_merge!(passed_options)
2312
+
2313
+ if params.empty?
2314
+ raise_command_error "Specify at least one option to update.\n#{optparse}"
2315
+ end
2316
+
2317
+ payload.deep_merge!({'page' => params}) unless params.empty?
2318
+ end
2319
+ @apps_interface.setopts(options)
2320
+ if options[:dry_run]
2321
+ print_dry_run @apps_interface.dry.update_wiki(app["id"], payload)
2322
+ return
2323
+ end
2324
+ json_response = @apps_interface.update_wiki(app["id"], payload)
2325
+
2326
+ if options[:json]
2327
+ puts as_json(json_response, options)
2328
+ else
2329
+ print_green_success "Updated wiki page for app #{app['name']}"
2330
+ wiki([app['id']])
2331
+ end
2332
+ return 0
2333
+ rescue RestClient::Exception => e
2334
+ print_rest_exception(e, options)
2335
+ exit 1
2336
+ end
2337
+ end
2338
+
2339
+ private
2340
+
2341
+ def extract_app_tiers(app)
2342
+ tier_rows = []
2343
+ begin
2344
+ app_tiers = app['appTiers'] || []
2345
+ sorted_app_tiers = app_tiers.sort {|a,b| a['bootSequence'] <=> b['bootSequence'] }
2346
+ sorted_app_tiers.each do |app_tier|
2347
+ tier_name = app_tier['tier']['name']
2348
+ boot_sequence = app_tier['bootSequence'] || 0
2349
+ instances = (app_tier['appInstances'] || []).collect {|it| it['instance']}
2350
+ row = {tier_name: tier_name, boot_sequence: boot_sequence, instances: instances}
2351
+ tier_rows << row
2352
+ end
2353
+ rescue => ex
2354
+ MtnCloud::Logging::DarkPrinter.puts "Error extracting app instances: #{ex}" if MtnCloud::Logging.debug?
2355
+ end
2356
+ return tier_rows
2357
+ end
2358
+
2359
+ # def add_app_option_types(connected=true)
2360
+ # [
2361
+ # {'fieldName' => 'blueprint', 'fieldLabel' => 'Blueprint', 'type' => 'select', 'selectOptions' => (connected ? get_available_blueprints() : []), 'required' => true, 'defaultValue' => 'existing', 'description' => "The blueprint to use. The default value is 'existing' which means no template, for creating a blank app and adding existing instances."},
2362
+ # {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Enter a name for this app'},
2363
+ # {'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false},
2364
+ # {'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'selectOptions' => (connected ? get_available_groups() : []), 'required' => false},
2365
+ # {'fieldName' => 'cloud', 'fieldLabel' => 'Default Cloud', 'type' => 'select', 'selectOptions' => [], 'required' => true},
2366
+ # {'fieldName' => 'environment', 'fieldLabel' => 'Environment', 'type' => 'text', 'required' => false},
2367
+ # ]
2368
+ # end
2369
+
2370
+ # def update_app_option_types(connected=true)
2371
+ # list = add_app_option_types(connected)
2372
+ # list = list.reject {|it| ["blueprint", "group"].include? it['fieldName'] }
2373
+ # list.each {|it| it['required'] = false }
2374
+ # list
2375
+ # end
2376
+
2377
+ def print_apps_table(apps, options={})
2378
+
2379
+ table_color = options[:color] || cyan
2380
+ rows = apps.collect do |app|
2381
+ tiers_str = format_app_tiers(app)
2382
+ instances_str = (app['instanceCount'].to_i == 1) ? "1" : "#{app['instanceCount']}"
2383
+ containers_str = (app['containerCount'].to_i == 1) ? "1" : "#{app['containerCount']}"
2384
+ stats = app['stats']
2385
+ # app_stats = app['appStats']
2386
+ cpu_usage_str = !stats ? "" : generate_usage_bar((stats['cpuUsage'] || stats['cpuUsagePeak']).to_f, 100, {max_bars: 10})
2387
+ memory_usage_str = !stats ? "" : generate_usage_bar(stats['usedMemory'], stats['maxMemory'], {max_bars: 10})
2388
+ storage_usage_str = !stats ? "" : generate_usage_bar(stats['usedStorage'], stats['maxStorage'], {max_bars: 10})
2389
+ if options[:details]
2390
+ if stats['maxMemory'] && stats['maxMemory'].to_i != 0
2391
+ memory_usage_str = memory_usage_str + cyan + format_bytes_short(stats['usedMemory']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxMemory']).strip
2392
+ end
2393
+ if stats['maxStorage'] && stats['maxStorage'].to_i != 0
2394
+ storage_usage_str = storage_usage_str + cyan + format_bytes_short(stats['usedStorage']).strip.rjust(8, ' ') + " / " + format_bytes_short(stats['maxStorage']).strip
2395
+ end
2396
+ end
2397
+ {
2398
+ id: app['id'],
2399
+ name: app['name'],
2400
+ labels: format_list(app['labels'], '', 3),
2401
+ description: app['description'],
2402
+ blueprint: app['blueprint'] ? app['blueprint']['name'] : '',
2403
+ type: app['type'] ? format_blueprint_type(app['type']) : (format_blueprint_type(app['blueprint'] ? app['blueprint']['type'] : nil)),
2404
+ group: app['group'] ? app['group']['name'] : app['siteId'],
2405
+ environment: app['appContext'],
2406
+ tiers: tiers_str,
2407
+ instances: instances_str,
2408
+ containers: containers_str,
2409
+ owner: app['owner'] ? app['owner']['username'] : '',
2410
+ tenant: app['account'] ? app['account']['name'] : nil,
2411
+ status: format_app_status(app, table_color),
2412
+ cpu: cpu_usage_str + cyan,
2413
+ memory: memory_usage_str + table_color,
2414
+ storage: storage_usage_str + table_color,
2415
+ created: format_local_dt(app['dateCreated']),
2416
+ updated: format_local_dt(app['lastUpdated'])
2417
+ }
2418
+ end
2419
+
2420
+ columns = [
2421
+ :id,
2422
+ :name,
2423
+ :labels,
2424
+ # :description,
2425
+ :type,
2426
+ :blueprint,
2427
+ :group,
2428
+ :environment,
2429
+ :status,
2430
+ :tiers,
2431
+ :instances,
2432
+ :containers,
2433
+ {:cpu => {:display_name => "MAX CPU"} },
2434
+ :memory,
2435
+ :storage,
2436
+ :owner,
2437
+ #:tenant,
2438
+ :created,
2439
+ :updated
2440
+ ]
2441
+
2442
+ # custom pretty table columns ...
2443
+ # if options[:include_fields]
2444
+ # columns = options[:include_fields]
2445
+ # end
2446
+ # print cyan
2447
+ print as_pretty_table(rows, columns, options) #{color: table_color}
2448
+ print reset
2449
+ end
2450
+
2451
+ def format_app_tiers(app)
2452
+ out = ""
2453
+ begin
2454
+ app_tiers = app['appTiers']
2455
+ if app_tiers
2456
+ app_tier_names = app_tiers.collect { |app_tier| app_tier['tier']['name'] }
2457
+ out << app_tier_names.join(", ")
2458
+ end
2459
+ if out.empty?
2460
+ #out = "(Empty)"
2461
+ end
2462
+ rescue => ex
2463
+ MtnCloud::Logging::DarkPrinter.puts "A formatting exception occured: #{ex}" if MtnCloud::Logging.debug?
2464
+ end
2465
+ out
2466
+ end
2467
+
2468
+ def get_available_blueprints(refresh=false)
2469
+ if !@available_blueprints || refresh
2470
+ #results = @options_interface.options_for_source('appTemplates',{}) # still exists
2471
+ results = @options_interface.options_for_source('blueprints',{})
2472
+ @available_blueprints = results['data'].collect {|it|
2473
+ {"id" => it["value"], "name" => it["name"], "value" => it["value"]}
2474
+ }
2475
+ default_option = {"id" => "existing", "name" => "Existing Instances", "value" => "existing", "type" => "morpheus"}
2476
+ @available_blueprints.unshift(default_option)
2477
+ end
2478
+ #puts "get_available_blueprints() rtn: #{@available_blueprints.inspect}"
2479
+ return @available_blueprints
2480
+ end
2481
+
2482
+ def find_blueprint_by_name_or_id(val)
2483
+ if val.to_s =~ /\A\d{1,}\Z/
2484
+ return find_blueprint_by_id(val)
2485
+ else
2486
+ return find_blueprint_by_name(val)
2487
+ end
2488
+ end
2489
+
2490
+ def find_blueprint_by_id(id)
2491
+ begin
2492
+ json_response = @blueprints_interface.get(id.to_i)
2493
+ return json_response['blueprint']
2494
+ rescue RestClient::Exception => e
2495
+ if e.response && e.response.code == 404
2496
+ print_red_alert "Blueprint not found by id #{id}"
2497
+ else
2498
+ raise e
2499
+ end
2500
+ end
2501
+ end
2502
+
2503
+ def find_blueprint_by_name(name)
2504
+ blueprints = @blueprints_interface.list({name: name.to_s})['blueprints']
2505
+ if blueprints.empty?
2506
+ print_red_alert "Blueprint not found by name #{name}"
2507
+ return nil
2508
+ elsif blueprints.size > 1
2509
+ print_red_alert "#{blueprints.size} blueprints found by name #{name}"
2510
+ # print_blueprints_table(blueprints, {color: red})
2511
+ rows = blueprints.collect { |it| {id: it['id'], name: it['name']} }
2512
+ print red
2513
+ print as_pretty_table(rows, [:id, :name], {color:red})
2514
+ print reset,"\n"
2515
+ return nil
2516
+ else
2517
+ return blueprints[0]
2518
+ end
2519
+ end
2520
+
2521
+ # lookup scoped instance config in a blueprint
2522
+ # this only finds one right now
2523
+ def get_scoped_instance_config(instance_config, env_name, group_name, cloud_name)
2524
+ config = instance_config.clone
2525
+ if env_name.to_s != '' && config['environments'] && config['environments'][env_name]
2526
+ config = config['environments'][env_name].clone
2527
+ end
2528
+ if group_name.to_s != '' && config['groups'] && config['groups'][group_name]
2529
+ config = config['groups'][group_name].clone
2530
+ end
2531
+ if cloud_name.to_s != '' && config['clouds'] && config['clouds'][cloud_name]
2532
+ config = config['clouds'][cloud_name].clone
2533
+ end
2534
+ config.delete('environments')
2535
+ config.delete('groups')
2536
+ config.delete('clouds')
2537
+ # puts "get_scoped_instance_config(instance_config, #{env_name}, #{group_name}, #{cloud_name})"
2538
+ # puts "returned config: #{config}"
2539
+ return config
2540
+ end
2541
+
2542
+ def update_wiki_page_option_types
2543
+ [
2544
+ {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => false, 'displayOrder' => 1, 'description' => 'The name of the wiki page for this instance. Default is the instance name.'},
2545
+ #{'fieldName' => 'category', 'fieldLabel' => 'Category', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
2546
+ {'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'textarea', 'required' => false, 'displayOrder' => 3, 'description' => 'The content (markdown) of the wiki page.'}
2547
+ ]
2548
+ end
2549
+ end