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.
- checksums.yaml +7 -0
- data/.dockerignore +20 -0
- data/.github/pull_request_template.md +14 -0
- data/.github/workflows/ci.yml +56 -0
- data/.github/workflows/release.yml +46 -0
- data/.gitignore +25 -0
- data/CLAUDE.md +24 -0
- data/CONTRIBUTING.md +91 -0
- data/Dockerfile +19 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +23 -0
- data/README.md +187 -0
- data/Rakefile +26 -0
- data/bin/mcs +3 -0
- data/bin/mtncloud +27 -0
- data/lib/mtncloud/api/account_groups_interface.rb +48 -0
- data/lib/mtncloud/api/account_users_interface.rb +68 -0
- data/lib/mtncloud/api/accounts_interface.rb +9 -0
- data/lib/mtncloud/api/activity_interface.rb +9 -0
- data/lib/mtncloud/api/affinity_groups_interface.rb +46 -0
- data/lib/mtncloud/api/api_client.rb +1091 -0
- data/lib/mtncloud/api/appliance_settings_interface.rb +49 -0
- data/lib/mtncloud/api/approvals_interface.rb +32 -0
- data/lib/mtncloud/api/apps_interface.rb +180 -0
- data/lib/mtncloud/api/archive_buckets_interface.rb +116 -0
- data/lib/mtncloud/api/archive_files_interface.rb +173 -0
- data/lib/mtncloud/api/audit_interface.rb +9 -0
- data/lib/mtncloud/api/auth_interface.rb +58 -0
- data/lib/mtncloud/api/backup_jobs_interface.rb +13 -0
- data/lib/mtncloud/api/backup_restores_interface.rb +27 -0
- data/lib/mtncloud/api/backup_results_interface.rb +32 -0
- data/lib/mtncloud/api/backup_service_types_interface.rb +9 -0
- data/lib/mtncloud/api/backup_services_interface.rb +9 -0
- data/lib/mtncloud/api/backup_settings_interface.rb +20 -0
- data/lib/mtncloud/api/backup_types_interface.rb +9 -0
- data/lib/mtncloud/api/backups_interface.rb +21 -0
- data/lib/mtncloud/api/billing_interface.rb +34 -0
- data/lib/mtncloud/api/blueprints_interface.rb +70 -0
- data/lib/mtncloud/api/body_io.rb +22 -0
- data/lib/mtncloud/api/budgets_interface.rb +48 -0
- data/lib/mtncloud/api/catalog_item_types_interface.rb +29 -0
- data/lib/mtncloud/api/certificate_types_interface.rb +14 -0
- data/lib/mtncloud/api/certificates_interface.rb +9 -0
- data/lib/mtncloud/api/clients_interface.rb +41 -0
- data/lib/mtncloud/api/cloud_datastores_interface.rb +48 -0
- data/lib/mtncloud/api/cloud_folders_interface.rb +41 -0
- data/lib/mtncloud/api/cloud_policies_interface.rb +41 -0
- data/lib/mtncloud/api/cloud_resource_pools_interface.rb +66 -0
- data/lib/mtncloud/api/clouds_interface.rb +217 -0
- data/lib/mtncloud/api/clusters_interface.rb +471 -0
- data/lib/mtncloud/api/containers_interface.rb +145 -0
- data/lib/mtncloud/api/credential_types_interface.rb +9 -0
- data/lib/mtncloud/api/credentials_interface.rb +9 -0
- data/lib/mtncloud/api/custom_instance_types_interface.rb +99 -0
- data/lib/mtncloud/api/cypher_interface.rb +51 -0
- data/lib/mtncloud/api/dashboard_interface.rb +14 -0
- data/lib/mtncloud/api/datastores_interface.rb +21 -0
- data/lib/mtncloud/api/deploy_interface.rb +44 -0
- data/lib/mtncloud/api/deployments_interface.rb +79 -0
- data/lib/mtncloud/api/distributed_workers_interface.rb +41 -0
- data/lib/mtncloud/api/email_templates_interface.rb +53 -0
- data/lib/mtncloud/api/environments_interface.rb +48 -0
- data/lib/mtncloud/api/execute_schedules_interface.rb +71 -0
- data/lib/mtncloud/api/execution_request_interface.rb +27 -0
- data/lib/mtncloud/api/file_copy_request_interface.rb +68 -0
- data/lib/mtncloud/api/forgot_password_interface.rb +17 -0
- data/lib/mtncloud/api/group_policies_interface.rb +41 -0
- data/lib/mtncloud/api/groups_interface.rb +73 -0
- data/lib/mtncloud/api/guidance_interface.rb +44 -0
- data/lib/mtncloud/api/guidance_settings_interface.rb +17 -0
- data/lib/mtncloud/api/health_interface.rb +100 -0
- data/lib/mtncloud/api/host_vm_groups_interface.rb +37 -0
- data/lib/mtncloud/api/hub_interface.rb +25 -0
- data/lib/mtncloud/api/image_builder_boot_scripts_interface.rb +41 -0
- data/lib/mtncloud/api/image_builder_image_builds_interface.rb +73 -0
- data/lib/mtncloud/api/image_builder_interface.rb +20 -0
- data/lib/mtncloud/api/image_builder_preseed_scripts_interface.rb +41 -0
- data/lib/mtncloud/api/instance_types_interface.rb +20 -0
- data/lib/mtncloud/api/instances_interface.rb +453 -0
- data/lib/mtncloud/api/integration_types_interface.rb +14 -0
- data/lib/mtncloud/api/integrations_interface.rb +74 -0
- data/lib/mtncloud/api/invoice_line_items_interface.rb +9 -0
- data/lib/mtncloud/api/invoices_interface.rb +28 -0
- data/lib/mtncloud/api/jobs_interface.rb +83 -0
- data/lib/mtncloud/api/key_pairs_interface.rb +57 -0
- data/lib/mtncloud/api/library_cluster_layouts_interface.rb +47 -0
- data/lib/mtncloud/api/library_cluster_packages_interface.rb +60 -0
- data/lib/mtncloud/api/library_container_scripts_interface.rb +43 -0
- data/lib/mtncloud/api/library_container_templates_interface.rb +43 -0
- data/lib/mtncloud/api/library_container_types_interface.rb +59 -0
- data/lib/mtncloud/api/library_container_upgrades_interface.rb +60 -0
- data/lib/mtncloud/api/library_instance_types_interface.rb +74 -0
- data/lib/mtncloud/api/library_layouts_interface.rb +69 -0
- data/lib/mtncloud/api/library_operating_systems_interface.rb +63 -0
- data/lib/mtncloud/api/library_spec_template_types_interface.rb +43 -0
- data/lib/mtncloud/api/library_spec_templates_interface.rb +43 -0
- data/lib/mtncloud/api/license_interface.rb +30 -0
- data/lib/mtncloud/api/load_balancer_monitors_interface.rb +9 -0
- data/lib/mtncloud/api/load_balancer_pool_nodes_interface.rb +8 -0
- data/lib/mtncloud/api/load_balancer_pools_interface.rb +9 -0
- data/lib/mtncloud/api/load_balancer_pools_secondary_interface.rb +9 -0
- data/lib/mtncloud/api/load_balancer_profiles_interface.rb +9 -0
- data/lib/mtncloud/api/load_balancer_types_interface.rb +9 -0
- data/lib/mtncloud/api/load_balancer_virtual_servers_interface.rb +18 -0
- data/lib/mtncloud/api/load_balancers_interface.rb +14 -0
- data/lib/mtncloud/api/log_settings_interface.rb +44 -0
- data/lib/mtncloud/api/logs_interface.rb +37 -0
- data/lib/mtncloud/api/migrations_interface.rb +49 -0
- data/lib/mtncloud/api/monitoring_alerts_interface.rb +39 -0
- data/lib/mtncloud/api/monitoring_apps_interface.rb +69 -0
- data/lib/mtncloud/api/monitoring_checks_interface.rb +103 -0
- data/lib/mtncloud/api/monitoring_contacts_interface.rb +41 -0
- data/lib/mtncloud/api/monitoring_groups_interface.rb +77 -0
- data/lib/mtncloud/api/monitoring_incidents_interface.rb +107 -0
- data/lib/mtncloud/api/monitoring_interface.rb +35 -0
- data/lib/mtncloud/api/monitoring_settings_interface.rb +21 -0
- data/lib/mtncloud/api/network_dhcp_relays_interface.rb +36 -0
- data/lib/mtncloud/api/network_dhcp_servers_interface.rb +36 -0
- data/lib/mtncloud/api/network_domain_records_interface.rb +41 -0
- data/lib/mtncloud/api/network_domains_interface.rb +41 -0
- data/lib/mtncloud/api/network_edge_clusters_interface.rb +26 -0
- data/lib/mtncloud/api/network_floating_ips_interface.rb +40 -0
- data/lib/mtncloud/api/network_groups_interface.rb +41 -0
- data/lib/mtncloud/api/network_pool_ips_interface.rb +41 -0
- data/lib/mtncloud/api/network_pool_server_types_interface.rb +9 -0
- data/lib/mtncloud/api/network_pool_servers_interface.rb +48 -0
- data/lib/mtncloud/api/network_pools_interface.rb +41 -0
- data/lib/mtncloud/api/network_proxies_interface.rb +41 -0
- data/lib/mtncloud/api/network_routers_interface.rb +181 -0
- data/lib/mtncloud/api/network_security_server_types_interface.rb +9 -0
- data/lib/mtncloud/api/network_security_servers_interface.rb +27 -0
- data/lib/mtncloud/api/network_server_groups_interface.rb +7 -0
- data/lib/mtncloud/api/network_server_types_interface.rb +9 -0
- data/lib/mtncloud/api/network_servers_interface.rb +102 -0
- data/lib/mtncloud/api/network_services_interface.rb +63 -0
- data/lib/mtncloud/api/network_static_routes_interface.rb +36 -0
- data/lib/mtncloud/api/network_subnets_interface.rb +42 -0
- data/lib/mtncloud/api/network_types_interface.rb +20 -0
- data/lib/mtncloud/api/networks_interface.rb +48 -0
- data/lib/mtncloud/api/option_type_forms_interface.rb +9 -0
- data/lib/mtncloud/api/option_type_lists_interface.rb +53 -0
- data/lib/mtncloud/api/option_types_interface.rb +41 -0
- data/lib/mtncloud/api/options_interface.rb +17 -0
- data/lib/mtncloud/api/packages_interface.rb +112 -0
- data/lib/mtncloud/api/ping_interface.rb +22 -0
- data/lib/mtncloud/api/plugins_interface.rb +22 -0
- data/lib/mtncloud/api/policies_interface.rb +56 -0
- data/lib/mtncloud/api/power_schedules_interface.rb +71 -0
- data/lib/mtncloud/api/price_sets_interface.rb +39 -0
- data/lib/mtncloud/api/prices_interface.rb +71 -0
- data/lib/mtncloud/api/processes_interface.rb +40 -0
- data/lib/mtncloud/api/projects_interface.rb +33 -0
- data/lib/mtncloud/api/provision_types_interface.rb +24 -0
- data/lib/mtncloud/api/provisioning_license_types_interface.rb +31 -0
- data/lib/mtncloud/api/provisioning_licenses_interface.rb +60 -0
- data/lib/mtncloud/api/provisioning_settings_interface.rb +26 -0
- data/lib/mtncloud/api/read_interface.rb +24 -0
- data/lib/mtncloud/api/reports_interface.rb +70 -0
- data/lib/mtncloud/api/resource_pool_groups_interface.rb +51 -0
- data/lib/mtncloud/api/rest_interface.rb +37 -0
- data/lib/mtncloud/api/roles_interface.rb +151 -0
- data/lib/mtncloud/api/scale_thresholds_interface.rb +9 -0
- data/lib/mtncloud/api/search_interface.rb +13 -0
- data/lib/mtncloud/api/secondary_read_interface.rb +25 -0
- data/lib/mtncloud/api/secondary_rest_interface.rb +42 -0
- data/lib/mtncloud/api/security_group_rules_interface.rb +35 -0
- data/lib/mtncloud/api/security_groups_interface.rb +56 -0
- data/lib/mtncloud/api/security_package_types_interface.rb +9 -0
- data/lib/mtncloud/api/security_packages_interface.rb +9 -0
- data/lib/mtncloud/api/security_scans_interface.rb +9 -0
- data/lib/mtncloud/api/server_devices_interface.rb +30 -0
- data/lib/mtncloud/api/server_types_interface.rb +20 -0
- data/lib/mtncloud/api/servers_interface.rb +230 -0
- data/lib/mtncloud/api/service_catalog_interface.rb +89 -0
- data/lib/mtncloud/api/service_plans_interface.rb +62 -0
- data/lib/mtncloud/api/setup_interface.rb +46 -0
- data/lib/mtncloud/api/snapshots_interface.rb +19 -0
- data/lib/mtncloud/api/storage_datastores_interface.rb +44 -0
- data/lib/mtncloud/api/storage_providers_interface.rb +151 -0
- data/lib/mtncloud/api/storage_server_types_interface.rb +14 -0
- data/lib/mtncloud/api/storage_servers_interface.rb +9 -0
- data/lib/mtncloud/api/storage_volume_types_interface.rb +9 -0
- data/lib/mtncloud/api/storage_volumes_interface.rb +24 -0
- data/lib/mtncloud/api/subnet_types_interface.rb +20 -0
- data/lib/mtncloud/api/subnets_interface.rb +41 -0
- data/lib/mtncloud/api/support_bundles_interface.rb +46 -0
- data/lib/mtncloud/api/system_types_interface.rb +13 -0
- data/lib/mtncloud/api/systems_interface.rb +55 -0
- data/lib/mtncloud/api/task_sets_interface.rb +52 -0
- data/lib/mtncloud/api/tasks_interface.rb +66 -0
- data/lib/mtncloud/api/tokens_interface.rb +39 -0
- data/lib/mtncloud/api/usage_interface.rb +18 -0
- data/lib/mtncloud/api/user_groups_interface.rb +59 -0
- data/lib/mtncloud/api/user_settings_interface.rb +90 -0
- data/lib/mtncloud/api/user_sources_interface.rb +99 -0
- data/lib/mtncloud/api/users_interface.rb +33 -0
- data/lib/mtncloud/api/vdi_allocations_interface.rb +9 -0
- data/lib/mtncloud/api/vdi_apps_interface.rb +9 -0
- data/lib/mtncloud/api/vdi_gateways_interface.rb +9 -0
- data/lib/mtncloud/api/vdi_interface.rb +28 -0
- data/lib/mtncloud/api/vdi_pools_interface.rb +19 -0
- data/lib/mtncloud/api/virtual_images_interface.rb +210 -0
- data/lib/mtncloud/api/virtual_servers_interface.rb +9 -0
- data/lib/mtncloud/api/virtual_switches_interface.rb +9 -0
- data/lib/mtncloud/api/whitelabel_settings_interface.rb +57 -0
- data/lib/mtncloud/api/whoami_interface.rb +13 -0
- data/lib/mtncloud/api/wiki_interface.rb +48 -0
- data/lib/mtncloud/api.rb +12 -0
- data/lib/mtncloud/benchmarking.rb +259 -0
- data/lib/mtncloud/cli/cli_command.rb +2281 -0
- data/lib/mtncloud/cli/cli_registry.rb +337 -0
- data/lib/mtncloud/cli/commands/access_token_command.rb +201 -0
- data/lib/mtncloud/cli/commands/account_groups_command.rb +530 -0
- data/lib/mtncloud/cli/commands/activity_command.rb +149 -0
- data/lib/mtncloud/cli/commands/affinity_groups_command.rb +554 -0
- data/lib/mtncloud/cli/commands/alias_command.rb +278 -0
- data/lib/mtncloud/cli/commands/appliance_settings_command.rb +382 -0
- data/lib/mtncloud/cli/commands/approvals_command.rb +236 -0
- data/lib/mtncloud/cli/commands/apps.rb +2549 -0
- data/lib/mtncloud/cli/commands/archives_command.rb +2161 -0
- data/lib/mtncloud/cli/commands/audit.rb +188 -0
- data/lib/mtncloud/cli/commands/backup_jobs_command.rb +366 -0
- data/lib/mtncloud/cli/commands/backup_restores_command.rb +144 -0
- data/lib/mtncloud/cli/commands/backup_results_command.rb +149 -0
- data/lib/mtncloud/cli/commands/backup_services_command.rb +44 -0
- data/lib/mtncloud/cli/commands/backup_settings_command.rb +184 -0
- data/lib/mtncloud/cli/commands/backup_types_command.rb +53 -0
- data/lib/mtncloud/cli/commands/backups_command.rb +674 -0
- data/lib/mtncloud/cli/commands/benchmark_command.rb +458 -0
- data/lib/mtncloud/cli/commands/blueprints_command.rb +2297 -0
- data/lib/mtncloud/cli/commands/boot_scripts_command.rb +379 -0
- data/lib/mtncloud/cli/commands/budgets_command.rb +754 -0
- data/lib/mtncloud/cli/commands/cat_command.rb +45 -0
- data/lib/mtncloud/cli/commands/catalog_item_types_command.rb +858 -0
- data/lib/mtncloud/cli/commands/certificates_command.rb +575 -0
- data/lib/mtncloud/cli/commands/change_password_command.rb +132 -0
- data/lib/mtncloud/cli/commands/clients_command.rb +331 -0
- data/lib/mtncloud/cli/commands/cloud_datastores_command.rb +543 -0
- data/lib/mtncloud/cli/commands/cloud_folders_command.rb +512 -0
- data/lib/mtncloud/cli/commands/cloud_resource_pools_command.rb +870 -0
- data/lib/mtncloud/cli/commands/clouds.rb +2037 -0
- data/lib/mtncloud/cli/commands/clouds_types.rb +140 -0
- data/lib/mtncloud/cli/commands/cluster_types.rb +179 -0
- data/lib/mtncloud/cli/commands/clusters.rb +5855 -0
- data/lib/mtncloud/cli/commands/coloring_command.rb +82 -0
- data/lib/mtncloud/cli/commands/containers_command.rb +967 -0
- data/lib/mtncloud/cli/commands/credential_types_command.rb +36 -0
- data/lib/mtncloud/cli/commands/credentials_command.rb +124 -0
- data/lib/mtncloud/cli/commands/curl_command.rb +168 -0
- data/lib/mtncloud/cli/commands/cypher_command.rb +587 -0
- data/lib/mtncloud/cli/commands/dashboard_command.rb +300 -0
- data/lib/mtncloud/cli/commands/debug_command.rb +75 -0
- data/lib/mtncloud/cli/commands/deploy.rb +485 -0
- data/lib/mtncloud/cli/commands/deployments.rb +923 -0
- data/lib/mtncloud/cli/commands/deploys.rb +485 -0
- data/lib/mtncloud/cli/commands/distributed_workers_command.rb +282 -0
- data/lib/mtncloud/cli/commands/echo_command.rb +61 -0
- data/lib/mtncloud/cli/commands/edit_profile_command.rb +57 -0
- data/lib/mtncloud/cli/commands/edit_rc_command.rb +77 -0
- data/lib/mtncloud/cli/commands/email_templates_command.rb +373 -0
- data/lib/mtncloud/cli/commands/environments_command.rb +401 -0
- data/lib/mtncloud/cli/commands/execute_schedules_command.rb +735 -0
- data/lib/mtncloud/cli/commands/execution_request_command.rb +333 -0
- data/lib/mtncloud/cli/commands/exit_command.rb +34 -0
- data/lib/mtncloud/cli/commands/file_copy_request_command.rb +352 -0
- data/lib/mtncloud/cli/commands/forgot_password.rb +133 -0
- data/lib/mtncloud/cli/commands/get_prompt_command.rb +36 -0
- data/lib/mtncloud/cli/commands/groups.rb +818 -0
- data/lib/mtncloud/cli/commands/guidance_command.rb +529 -0
- data/lib/mtncloud/cli/commands/guidance_settings.rb +148 -0
- data/lib/mtncloud/cli/commands/health_command.rb +1046 -0
- data/lib/mtncloud/cli/commands/history_command.rb +60 -0
- data/lib/mtncloud/cli/commands/host_vm_groups_command.rb +423 -0
- data/lib/mtncloud/cli/commands/hosts.rb +2986 -0
- data/lib/mtncloud/cli/commands/hub.rb +215 -0
- data/lib/mtncloud/cli/commands/image_builder_command.rb +1197 -0
- data/lib/mtncloud/cli/commands/instance_types.rb +163 -0
- data/lib/mtncloud/cli/commands/instances.rb +5813 -0
- data/lib/mtncloud/cli/commands/integrations_command.rb +1412 -0
- data/lib/mtncloud/cli/commands/invoices_command.rb +1151 -0
- data/lib/mtncloud/cli/commands/jobs_command.rb +998 -0
- data/lib/mtncloud/cli/commands/key_pairs.rb +470 -0
- data/lib/mtncloud/cli/commands/library_cluster_layouts_command.rb +901 -0
- data/lib/mtncloud/cli/commands/library_cluster_packages_command.rb +552 -0
- data/lib/mtncloud/cli/commands/library_container_scripts_command.rb +446 -0
- data/lib/mtncloud/cli/commands/library_container_templates_command.rb +526 -0
- data/lib/mtncloud/cli/commands/library_container_types_command.rb +698 -0
- data/lib/mtncloud/cli/commands/library_forms_command.rb +625 -0
- data/lib/mtncloud/cli/commands/library_instance_types_command.rb +668 -0
- data/lib/mtncloud/cli/commands/library_layouts_command.rb +849 -0
- data/lib/mtncloud/cli/commands/library_operating_systems_command.rb +671 -0
- data/lib/mtncloud/cli/commands/library_option_lists_command.rb +503 -0
- data/lib/mtncloud/cli/commands/library_option_types_command.rb +329 -0
- data/lib/mtncloud/cli/commands/library_spec_templates_command.rb +547 -0
- data/lib/mtncloud/cli/commands/library_upgrades_command.rb +596 -0
- data/lib/mtncloud/cli/commands/license.rb +475 -0
- data/lib/mtncloud/cli/commands/load_balancer_monitors.rb +70 -0
- data/lib/mtncloud/cli/commands/load_balancer_pool_nodes.rb +87 -0
- data/lib/mtncloud/cli/commands/load_balancer_pools.rb +129 -0
- data/lib/mtncloud/cli/commands/load_balancer_profiles.rb +64 -0
- data/lib/mtncloud/cli/commands/load_balancer_types.rb +38 -0
- data/lib/mtncloud/cli/commands/load_balancer_virtual_servers.rb +147 -0
- data/lib/mtncloud/cli/commands/load_balancers.rb +189 -0
- data/lib/mtncloud/cli/commands/log_level_command.rb +54 -0
- data/lib/mtncloud/cli/commands/log_settings_command.rb +443 -0
- data/lib/mtncloud/cli/commands/login.rb +148 -0
- data/lib/mtncloud/cli/commands/logout.rb +63 -0
- data/lib/mtncloud/cli/commands/logs_command.rb +140 -0
- data/lib/mtncloud/cli/commands/man_command.rb +409 -0
- data/lib/mtncloud/cli/commands/migrations.rb +453 -0
- data/lib/mtncloud/cli/commands/monitoring_alerts_command.rb +791 -0
- data/lib/mtncloud/cli/commands/monitoring_apps_command.rb +716 -0
- data/lib/mtncloud/cli/commands/monitoring_checks_command.rb +766 -0
- data/lib/mtncloud/cli/commands/monitoring_contacts_command.rb +339 -0
- data/lib/mtncloud/cli/commands/monitoring_groups_command.rb +674 -0
- data/lib/mtncloud/cli/commands/monitoring_incidents_command.rb +835 -0
- data/lib/mtncloud/cli/commands/monitoring_settings.rb +212 -0
- data/lib/mtncloud/cli/commands/network_dhcp_relays_command.rb +375 -0
- data/lib/mtncloud/cli/commands/network_dhcp_servers_command.rb +366 -0
- data/lib/mtncloud/cli/commands/network_domains_command.rb +896 -0
- data/lib/mtncloud/cli/commands/network_edge_clusters_command.rb +288 -0
- data/lib/mtncloud/cli/commands/network_firewalls_command.rb +783 -0
- data/lib/mtncloud/cli/commands/network_floating_ips.rb +147 -0
- data/lib/mtncloud/cli/commands/network_groups_command.rb +654 -0
- data/lib/mtncloud/cli/commands/network_pool_server_types.rb +20 -0
- data/lib/mtncloud/cli/commands/network_pool_servers_command.rb +318 -0
- data/lib/mtncloud/cli/commands/network_pools_command.rb +903 -0
- data/lib/mtncloud/cli/commands/network_proxies_command.rb +578 -0
- data/lib/mtncloud/cli/commands/network_routers_command.rb +2628 -0
- data/lib/mtncloud/cli/commands/network_security_server_types.rb +20 -0
- data/lib/mtncloud/cli/commands/network_server_groups_command.rb +222 -0
- data/lib/mtncloud/cli/commands/network_server_types_command.rb +20 -0
- data/lib/mtncloud/cli/commands/network_servers_command.rb +398 -0
- data/lib/mtncloud/cli/commands/network_services_command.rb +198 -0
- data/lib/mtncloud/cli/commands/network_static_routes_command.rb +451 -0
- data/lib/mtncloud/cli/commands/network_transport_zones_command.rb +412 -0
- data/lib/mtncloud/cli/commands/networks_command.rb +1692 -0
- data/lib/mtncloud/cli/commands/open_command.rb +30 -0
- data/lib/mtncloud/cli/commands/options.rb +231 -0
- data/lib/mtncloud/cli/commands/packages_command.rb +822 -0
- data/lib/mtncloud/cli/commands/ping.rb +242 -0
- data/lib/mtncloud/cli/commands/plugins.rb +214 -0
- data/lib/mtncloud/cli/commands/policies_command.rb +990 -0
- data/lib/mtncloud/cli/commands/power_schedules_command.rb +731 -0
- data/lib/mtncloud/cli/commands/preseed_scripts_command.rb +378 -0
- data/lib/mtncloud/cli/commands/price_sets_command.rb +557 -0
- data/lib/mtncloud/cli/commands/prices_command.rb +692 -0
- data/lib/mtncloud/cli/commands/processes_command.rb +469 -0
- data/lib/mtncloud/cli/commands/projects_command.rb +537 -0
- data/lib/mtncloud/cli/commands/provisioning_licenses_command.rb +509 -0
- data/lib/mtncloud/cli/commands/provisioning_settings_command.rb +238 -0
- data/lib/mtncloud/cli/commands/recent_activity_command.rb +171 -0
- data/lib/mtncloud/cli/commands/remote.rb +1818 -0
- data/lib/mtncloud/cli/commands/reports_command.rb +724 -0
- data/lib/mtncloud/cli/commands/resource_pool_groups_command.rb +586 -0
- data/lib/mtncloud/cli/commands/rm_command.rb +14 -0
- data/lib/mtncloud/cli/commands/roles.rb +3120 -0
- data/lib/mtncloud/cli/commands/scale_thresholds.rb +103 -0
- data/lib/mtncloud/cli/commands/search_command.rb +184 -0
- data/lib/mtncloud/cli/commands/security_group_rules.rb +230 -0
- data/lib/mtncloud/cli/commands/security_groups.rb +1294 -0
- data/lib/mtncloud/cli/commands/security_package_types.rb +32 -0
- data/lib/mtncloud/cli/commands/security_packages.rb +84 -0
- data/lib/mtncloud/cli/commands/security_scans.rb +107 -0
- data/lib/mtncloud/cli/commands/self_service_command.rb +17 -0
- data/lib/mtncloud/cli/commands/server_devices_command.rb +207 -0
- data/lib/mtncloud/cli/commands/service_catalog_command.rb +1528 -0
- data/lib/mtncloud/cli/commands/service_plans_command.rb +895 -0
- data/lib/mtncloud/cli/commands/set_prompt_command.rb +51 -0
- data/lib/mtncloud/cli/commands/setup.rb +423 -0
- data/lib/mtncloud/cli/commands/shell.rb +717 -0
- data/lib/mtncloud/cli/commands/sleep_command.rb +43 -0
- data/lib/mtncloud/cli/commands/snapshots.rb +156 -0
- data/lib/mtncloud/cli/commands/source_command.rb +30 -0
- data/lib/mtncloud/cli/commands/ssl_verification_command.rb +47 -0
- data/lib/mtncloud/cli/commands/storage_datastores.rb +457 -0
- data/lib/mtncloud/cli/commands/storage_providers_command.rb +1401 -0
- data/lib/mtncloud/cli/commands/storage_server_types.rb +45 -0
- data/lib/mtncloud/cli/commands/storage_servers.rb +116 -0
- data/lib/mtncloud/cli/commands/storage_volume_types.rb +45 -0
- data/lib/mtncloud/cli/commands/storage_volumes.rb +236 -0
- data/lib/mtncloud/cli/commands/subnets_command.rb +819 -0
- data/lib/mtncloud/cli/commands/support_bundles_command.rb +606 -0
- data/lib/mtncloud/cli/commands/systems.rb +1203 -0
- data/lib/mtncloud/cli/commands/tasks.rb +1391 -0
- data/lib/mtncloud/cli/commands/tee_command.rb +14 -0
- data/lib/mtncloud/cli/commands/tenants_command.rb +315 -0
- data/lib/mtncloud/cli/commands/tokens_command.rb +391 -0
- data/lib/mtncloud/cli/commands/update_command.rb +46 -0
- data/lib/mtncloud/cli/commands/usage_command.rb +203 -0
- data/lib/mtncloud/cli/commands/user_groups_command.rb +512 -0
- data/lib/mtncloud/cli/commands/user_settings_command.rb +849 -0
- data/lib/mtncloud/cli/commands/user_sources_command.rb +1065 -0
- data/lib/mtncloud/cli/commands/users.rb +897 -0
- data/lib/mtncloud/cli/commands/vdi_allocations_command.rb +159 -0
- data/lib/mtncloud/cli/commands/vdi_apps_command.rb +317 -0
- data/lib/mtncloud/cli/commands/vdi_command.rb +359 -0
- data/lib/mtncloud/cli/commands/vdi_gateways_command.rb +290 -0
- data/lib/mtncloud/cli/commands/vdi_pools_command.rb +574 -0
- data/lib/mtncloud/cli/commands/version_command.rb +38 -0
- data/lib/mtncloud/cli/commands/view.rb +107 -0
- data/lib/mtncloud/cli/commands/virtual_images.rb +1362 -0
- data/lib/mtncloud/cli/commands/virtual_switches_command.rb +336 -0
- data/lib/mtncloud/cli/commands/whitelabel_settings_command.rb +653 -0
- data/lib/mtncloud/cli/commands/whoami.rb +323 -0
- data/lib/mtncloud/cli/commands/wiki_command.rb +448 -0
- data/lib/mtncloud/cli/commands/workflows.rb +1022 -0
- data/lib/mtncloud/cli/credentials.rb +482 -0
- data/lib/mtncloud/cli/dot_file.rb +180 -0
- data/lib/mtncloud/cli/error_handler.rb +307 -0
- data/lib/mtncloud/cli/errors.rb +53 -0
- data/lib/mtncloud/cli/expression_parser.rb +153 -0
- data/lib/mtncloud/cli/mixins/accounts_helper.rb +484 -0
- data/lib/mtncloud/cli/mixins/affinity_helper.rb +118 -0
- data/lib/mtncloud/cli/mixins/backups_helper.rb +200 -0
- data/lib/mtncloud/cli/mixins/deployments_helper.rb +163 -0
- data/lib/mtncloud/cli/mixins/execution_request_helper.rb +50 -0
- data/lib/mtncloud/cli/mixins/infrastructure_helper.rb +966 -0
- data/lib/mtncloud/cli/mixins/jobs_helper.rb +202 -0
- data/lib/mtncloud/cli/mixins/library_helper.rb +539 -0
- data/lib/mtncloud/cli/mixins/load_balancers_helper.rb +176 -0
- data/lib/mtncloud/cli/mixins/logs_helper.rb +131 -0
- data/lib/mtncloud/cli/mixins/monitoring_helper.rb +876 -0
- data/lib/mtncloud/cli/mixins/networks_helper.rb +62 -0
- data/lib/mtncloud/cli/mixins/operations_helper.rb +41 -0
- data/lib/mtncloud/cli/mixins/option_source_helper.rb +254 -0
- data/lib/mtncloud/cli/mixins/print_helper.rb +1631 -0
- data/lib/mtncloud/cli/mixins/processes_helper.rb +286 -0
- data/lib/mtncloud/cli/mixins/prompt_helper.rb +136 -0
- data/lib/mtncloud/cli/mixins/provisioning_helper.rb +2963 -0
- data/lib/mtncloud/cli/mixins/remote_helper.rb +139 -0
- data/lib/mtncloud/cli/mixins/rest_command.rb +994 -0
- data/lib/mtncloud/cli/mixins/secondary_rest_command.rb +700 -0
- data/lib/mtncloud/cli/mixins/storage_servers_helper.rb +93 -0
- data/lib/mtncloud/cli/mixins/storage_volumes_helper.rb +76 -0
- data/lib/mtncloud/cli/mixins/vdi_helper.rb +246 -0
- data/lib/mtncloud/cli/mixins/whoami_helper.rb +73 -0
- data/lib/mtncloud/cli/option_parser.rb +127 -0
- data/lib/mtncloud/cli/option_types.rb +1622 -0
- data/lib/mtncloud/cli/version.rb +6 -0
- data/lib/mtncloud/cli.rb +105 -0
- data/lib/mtncloud/currency.rb +184 -0
- data/lib/mtncloud/ext/hash.rb +109 -0
- data/lib/mtncloud/ext/nil_class.rb +5 -0
- data/lib/mtncloud/ext/rest_client.rb +37 -0
- data/lib/mtncloud/ext/string.rb +66 -0
- data/lib/mtncloud/formatters.rb +511 -0
- data/lib/mtncloud/logging.rb +185 -0
- data/lib/mtncloud/morpkg.rb +119 -0
- data/lib/mtncloud/rest_client.rb +104 -0
- data/lib/mtncloud/routes.rb +304 -0
- data/lib/mtncloud/terminal.rb +528 -0
- data/lib/mtncloud/util.rb +29 -0
- data/lib/mtncloud.rb +5 -0
- data/mtncloud-cli.gemspec +38 -0
- data/test/api/affinity_groups_interface_test.rb +43 -0
- data/test/api/clients_interface_test.rb +23 -0
- data/test/api/clouds_interface_test.rb +67 -0
- data/test/api/containers_interface_test.rb +68 -0
- data/test/api/host_vm_groups_interface_test.rb +35 -0
- data/test/api/instances_interface_test.rb +22 -0
- data/test/api/network_routers_interface_test.rb +22 -0
- data/test/api/systems_interface_test.rb +26 -0
- data/test/api/whoami_interface_test.rb +14 -0
- data/test/branding_test.rb +160 -0
- data/test/cli/access_token_test.rb +36 -0
- data/test/cli/affinity_groups_cloud_cluster_scope_test.rb +270 -0
- data/test/cli/affinity_groups_cluster_scope_test.rb +171 -0
- data/test/cli/affinity_groups_test.rb +57 -0
- data/test/cli/affinity_groups_violations_scope_test.rb +141 -0
- data/test/cli/affinity_helper_test.rb +367 -0
- data/test/cli/auth_test.rb +88 -0
- data/test/cli/backups_test.rb +212 -0
- data/test/cli/cli_test.rb +48 -0
- data/test/cli/clients_test.rb +45 -0
- data/test/cli/clusters_uuid_test.rb +146 -0
- data/test/cli/containers_test.rb +92 -0
- data/test/cli/distributed_workers_test.rb +68 -0
- data/test/cli/help_test.rb +25 -0
- data/test/cli/host_vm_groups_cluster_scope_test.rb +187 -0
- data/test/cli/host_vm_groups_test.rb +63 -0
- data/test/cli/infrastructure_helper_cluster_resolver_test.rb +250 -0
- data/test/cli/instances_test.rb +37 -0
- data/test/cli/man_test.rb +14 -0
- data/test/cli/migrations_test.rb +39 -0
- data/test/cli/network_routers_test.rb +65 -0
- data/test/cli/remote_test.rb +89 -0
- data/test/cli/roles_test.rb +47 -0
- data/test/cli/shell_test.rb +81 -0
- data/test/cli/storage_volumes_test.rb +79 -0
- data/test/cli/systems_test.rb +206 -0
- data/test/cli/version_test.rb +23 -0
- data/test/cli/view_test.rb +55 -0
- data/test/cli/whoami_test.rb +17 -0
- data/test/mtncloud_test.rb +16 -0
- data/test/test_case.rb +342 -0
- data/test/test_config.rb +137 -0
- data/test/test_data_helper.rb +97 -0
- 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
|