morpheus-cli 2.12.5 → 3.1.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 +4 -4
- data/Dockerfile +5 -0
- data/lib/morpheus/api/api_client.rb +15 -30
- data/lib/morpheus/api/app_templates_interface.rb +34 -7
- data/lib/morpheus/api/apps_interface.rb +20 -1
- data/lib/morpheus/api/archive_buckets_interface.rb +124 -0
- data/lib/morpheus/api/archive_files_interface.rb +182 -0
- data/lib/morpheus/api/{network_pools_interface.rb → image_builder_boot_scripts_interface.rb} +6 -6
- data/lib/morpheus/api/{policies_interface.rb → image_builder_image_builds_interface.rb} +20 -15
- data/lib/morpheus/api/image_builder_interface.rb +26 -0
- data/lib/morpheus/api/{network_proxies_interface.rb → image_builder_preseed_scripts_interface.rb} +6 -6
- data/lib/morpheus/cli.rb +10 -9
- data/lib/morpheus/cli/alias_command.rb +10 -9
- data/lib/morpheus/cli/app_templates.rb +1566 -457
- data/lib/morpheus/cli/apps.rb +284 -108
- data/lib/morpheus/cli/archives_command.rb +2184 -0
- data/lib/morpheus/cli/boot_scripts_command.rb +382 -0
- data/lib/morpheus/cli/cli_command.rb +9 -35
- data/lib/morpheus/cli/error_handler.rb +2 -0
- data/lib/morpheus/cli/hosts.rb +15 -3
- data/lib/morpheus/cli/image_builder_command.rb +1208 -0
- data/lib/morpheus/cli/instances.rb +118 -47
- data/lib/morpheus/cli/man_command.rb +27 -24
- data/lib/morpheus/cli/mixins/print_helper.rb +19 -5
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +20 -20
- data/lib/morpheus/cli/option_types.rb +45 -14
- data/lib/morpheus/cli/preseed_scripts_command.rb +381 -0
- data/lib/morpheus/cli/remote.rb +1 -0
- data/lib/morpheus/cli/roles.rb +2 -2
- data/lib/morpheus/cli/shell.rb +3 -2
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/ext/hash.rb +22 -0
- data/lib/morpheus/formatters.rb +33 -0
- data/lib/morpheus/terminal.rb +1 -1
- metadata +13 -21
- data/lib/morpheus/api/cloud_policies_interface.rb +0 -47
- data/lib/morpheus/api/group_policies_interface.rb +0 -47
- data/lib/morpheus/api/network_domains_interface.rb +0 -47
- data/lib/morpheus/api/network_groups_interface.rb +0 -47
- data/lib/morpheus/api/network_pool_servers_interface.rb +0 -47
- data/lib/morpheus/api/network_services_interface.rb +0 -47
- data/lib/morpheus/api/networks_interface.rb +0 -54
- data/lib/morpheus/cli/network_domains_command.rb +0 -571
- data/lib/morpheus/cli/network_groups_command.rb +0 -602
- data/lib/morpheus/cli/network_pool_servers_command.rb +0 -430
- data/lib/morpheus/cli/network_pools_command.rb +0 -495
- data/lib/morpheus/cli/network_proxies_command.rb +0 -594
- data/lib/morpheus/cli/network_services_command.rb +0 -148
- data/lib/morpheus/cli/networks_command.rb +0 -855
- data/lib/morpheus/cli/policies_command.rb +0 -847
- data/scripts/generate_morpheus_commands_help.morpheus +0 -1313
data/lib/morpheus/api/{network_pools_interface.rb → image_builder_boot_scripts_interface.rb}
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'morpheus/api/api_client'
|
2
2
|
|
3
|
-
class Morpheus::
|
3
|
+
class Morpheus::ImageBuilderBootScriptsInterface < Morpheus::APIClient
|
4
4
|
def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
|
5
5
|
@access_token = access_token
|
6
6
|
@refresh_token = refresh_token
|
@@ -10,35 +10,35 @@ class Morpheus::NetworkPoolsInterface < Morpheus::APIClient
|
|
10
10
|
|
11
11
|
def get(id, params={})
|
12
12
|
raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
|
13
|
-
url = "#{@base_url}/api/
|
13
|
+
url = "#{@base_url}/api/boot-scripts/#{id}"
|
14
14
|
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
15
15
|
opts = {method: :get, url: url, headers: headers}
|
16
16
|
execute(opts)
|
17
17
|
end
|
18
18
|
|
19
19
|
def list(params={})
|
20
|
-
url = "#{@base_url}/api/
|
20
|
+
url = "#{@base_url}/api/boot-scripts"
|
21
21
|
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
22
22
|
opts = {method: :get, url: url, headers: headers}
|
23
23
|
execute(opts)
|
24
24
|
end
|
25
25
|
|
26
26
|
def create(payload)
|
27
|
-
url = "#{@base_url}/api/
|
27
|
+
url = "#{@base_url}/api/boot-scripts"
|
28
28
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
29
29
|
opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
|
30
30
|
execute(opts)
|
31
31
|
end
|
32
32
|
|
33
33
|
def update(id, payload)
|
34
|
-
url = "#{@base_url}/api/
|
34
|
+
url = "#{@base_url}/api/boot-scripts/#{id}"
|
35
35
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
36
36
|
opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
|
37
37
|
execute(opts)
|
38
38
|
end
|
39
39
|
|
40
40
|
def destroy(id, params={})
|
41
|
-
url = "#{@base_url}/api/
|
41
|
+
url = "#{@base_url}/api/boot-scripts/#{id}"
|
42
42
|
headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
43
43
|
opts = {method: :delete, url: url, timeout: 30, headers: headers}
|
44
44
|
execute(opts)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'morpheus/api/api_client'
|
2
|
-
|
3
|
-
|
4
|
-
class Morpheus::PoliciesInterface < Morpheus::APIClient
|
2
|
+
# this may change to just /api/image-builds
|
3
|
+
class Morpheus::ImageBuilderImageBuildsInterface < Morpheus::APIClient
|
5
4
|
def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
|
6
5
|
@access_token = access_token
|
7
6
|
@refresh_token = refresh_token
|
@@ -11,50 +10,56 @@ class Morpheus::PoliciesInterface < Morpheus::APIClient
|
|
11
10
|
|
12
11
|
def get(id, params={})
|
13
12
|
raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
|
14
|
-
url = "#{@base_url}/api/
|
13
|
+
url = "#{@base_url}/api/image-builds/#{id}"
|
15
14
|
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
16
15
|
opts = {method: :get, url: url, headers: headers}
|
17
16
|
execute(opts)
|
18
17
|
end
|
19
18
|
|
20
19
|
def list(params={})
|
21
|
-
url = "#{@base_url}/api/
|
20
|
+
url = "#{@base_url}/api/image-builds"
|
22
21
|
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
23
22
|
opts = {method: :get, url: url, headers: headers}
|
24
23
|
execute(opts)
|
25
24
|
end
|
26
25
|
|
27
26
|
def create(payload)
|
28
|
-
url = "#{@base_url}/api/
|
27
|
+
url = "#{@base_url}/api/image-builds"
|
29
28
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
30
29
|
opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
|
31
30
|
execute(opts)
|
32
31
|
end
|
33
32
|
|
33
|
+
# def validate_save(payload)
|
34
|
+
# url = "#{@base_url}/api/image-builds/validate-save"
|
35
|
+
# headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
36
|
+
# opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
|
37
|
+
# execute(opts)
|
38
|
+
# end
|
39
|
+
|
34
40
|
def update(id, payload)
|
35
|
-
url = "#{@base_url}/api/
|
41
|
+
url = "#{@base_url}/api/image-builds/#{id}"
|
36
42
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
37
43
|
opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
|
38
44
|
execute(opts)
|
39
45
|
end
|
40
46
|
|
41
47
|
def destroy(id, params={})
|
42
|
-
url = "#{@base_url}/api/
|
48
|
+
url = "#{@base_url}/api/image-builds/#{id}"
|
43
49
|
headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
44
50
|
opts = {method: :delete, url: url, timeout: 30, headers: headers}
|
45
51
|
execute(opts)
|
46
52
|
end
|
47
53
|
|
48
|
-
def
|
49
|
-
url = "#{@base_url}/api/
|
50
|
-
headers = { params
|
51
|
-
opts = {method: :
|
54
|
+
def run(id, params={})
|
55
|
+
url = "#{@base_url}/api/image-builds/#{id}/run"
|
56
|
+
headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
57
|
+
opts = {method: :post, url: url, timeout: 30, headers: headers}
|
52
58
|
execute(opts)
|
53
59
|
end
|
54
60
|
|
55
|
-
def
|
56
|
-
|
57
|
-
url = "#{@base_url}/api/policy-types/#{URI.escape(id.to_s)}"
|
61
|
+
def list_executions(id, params={})
|
62
|
+
url = "#{@base_url}/api/image-builds/#{id}/list-executions"
|
58
63
|
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
59
64
|
opts = {method: :get, url: url, headers: headers}
|
60
65
|
execute(opts)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'morpheus/api/api_client'
|
2
|
+
require 'morpheus/api/image_builder_image_builds_interface'
|
3
|
+
require 'morpheus/api/image_builder_preseed_scripts_interface'
|
4
|
+
require 'morpheus/api/image_builder_boot_scripts_interface'
|
5
|
+
|
6
|
+
class Morpheus::ImageBuilderInterface < Morpheus::APIClient
|
7
|
+
def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
|
8
|
+
@access_token = access_token
|
9
|
+
@refresh_token = refresh_token
|
10
|
+
@base_url = base_url
|
11
|
+
@expires_at = expires_at
|
12
|
+
end
|
13
|
+
|
14
|
+
def image_builds
|
15
|
+
Morpheus::ImageBuilderImageBuildsInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
|
16
|
+
end
|
17
|
+
|
18
|
+
def preseed_scripts
|
19
|
+
Morpheus::ImageBuilderPreseedScriptsInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
|
20
|
+
end
|
21
|
+
|
22
|
+
def boot_scripts
|
23
|
+
Morpheus::ImageBuilderBootScriptsInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
data/lib/morpheus/api/{network_proxies_interface.rb → image_builder_preseed_scripts_interface.rb}
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'morpheus/api/api_client'
|
2
2
|
|
3
|
-
class Morpheus::
|
3
|
+
class Morpheus::ImageBuilderPreseedScriptsInterface < Morpheus::APIClient
|
4
4
|
def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
|
5
5
|
@access_token = access_token
|
6
6
|
@refresh_token = refresh_token
|
@@ -10,35 +10,35 @@ class Morpheus::NetworkProxiesInterface < Morpheus::APIClient
|
|
10
10
|
|
11
11
|
def get(id, params={})
|
12
12
|
raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
|
13
|
-
url = "#{@base_url}/api/
|
13
|
+
url = "#{@base_url}/api/preseed-scripts/#{id}"
|
14
14
|
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
15
15
|
opts = {method: :get, url: url, headers: headers}
|
16
16
|
execute(opts)
|
17
17
|
end
|
18
18
|
|
19
19
|
def list(params={})
|
20
|
-
url = "#{@base_url}/api/
|
20
|
+
url = "#{@base_url}/api/preseed-scripts"
|
21
21
|
headers = { params: params, authorization: "Bearer #{@access_token}" }
|
22
22
|
opts = {method: :get, url: url, headers: headers}
|
23
23
|
execute(opts)
|
24
24
|
end
|
25
25
|
|
26
26
|
def create(payload)
|
27
|
-
url = "#{@base_url}/api/
|
27
|
+
url = "#{@base_url}/api/preseed-scripts"
|
28
28
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
29
29
|
opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
|
30
30
|
execute(opts)
|
31
31
|
end
|
32
32
|
|
33
33
|
def update(id, payload)
|
34
|
-
url = "#{@base_url}/api/
|
34
|
+
url = "#{@base_url}/api/preseed-scripts/#{id}"
|
35
35
|
headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
36
36
|
opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
|
37
37
|
execute(opts)
|
38
38
|
end
|
39
39
|
|
40
40
|
def destroy(id, params={})
|
41
|
-
url = "#{@base_url}/api/
|
41
|
+
url = "#{@base_url}/api/preseed-scripts/#{id}"
|
42
42
|
headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
|
43
43
|
opts = {method: :delete, url: url, timeout: 30, headers: headers}
|
44
44
|
execute(opts)
|
data/lib/morpheus/cli.rb
CHANGED
@@ -29,7 +29,7 @@ module Morpheus
|
|
29
29
|
def self.load!()
|
30
30
|
# load interfaces
|
31
31
|
require 'morpheus/api/api_client.rb'
|
32
|
-
Dir[File.dirname(__FILE__) + "/api
|
32
|
+
Dir[File.dirname(__FILE__) + "/api/**/*.rb"].each {|file| load file }
|
33
33
|
|
34
34
|
# load mixins
|
35
35
|
Dir[File.dirname(__FILE__) + "/cli/mixins/*.rb"].each {|file| load file }
|
@@ -94,14 +94,15 @@ module Morpheus
|
|
94
94
|
load 'morpheus/cli/monitoring_contacts_command.rb'
|
95
95
|
load 'morpheus/cli/monitoring_groups_command.rb'
|
96
96
|
load 'morpheus/cli/monitoring_apps_command.rb'
|
97
|
-
|
98
|
-
|
99
|
-
load 'morpheus/cli/
|
100
|
-
load 'morpheus/cli/
|
101
|
-
load 'morpheus/cli/
|
102
|
-
load 'morpheus/cli/
|
103
|
-
|
104
|
-
load 'morpheus/cli/
|
97
|
+
|
98
|
+
# maybe scope all of these to image-builder or something
|
99
|
+
load 'morpheus/cli/image_builder_command.rb'
|
100
|
+
# load 'morpheus/cli/image_builds_command.rb'
|
101
|
+
load 'morpheus/cli/preseed_scripts_command.rb'
|
102
|
+
load 'morpheus/cli/boot_scripts_command.rb'
|
103
|
+
|
104
|
+
load 'morpheus/cli/archives_command.rb'
|
105
|
+
|
105
106
|
# nice to have commands
|
106
107
|
load 'morpheus/cli/curl_command.rb'
|
107
108
|
load 'morpheus/cli/set_prompt_command.rb'
|
@@ -68,15 +68,14 @@ class Morpheus::Cli::AliasCommand
|
|
68
68
|
exit 1
|
69
69
|
end
|
70
70
|
alias_definition = args[0]
|
71
|
-
alias_name, command_string = Morpheus::Cli::CliRegistry.parse_alias_definition(alias_definition)
|
72
71
|
|
73
|
-
|
74
|
-
print_red_alert "invalid alias syntax: #{alias_definition}"
|
75
|
-
return false
|
76
|
-
else
|
77
|
-
# config[:aliases] ||= []
|
78
|
-
# config[:aliases] << {name: alias_name, command: command_string}
|
72
|
+
|
79
73
|
begin
|
74
|
+
alias_name, command_string = Morpheus::Cli::CliRegistry.parse_alias_definition(alias_definition)
|
75
|
+
if alias_name.empty? || command_string.empty?
|
76
|
+
print_red_alert "invalid alias syntax: #{alias_definition}"
|
77
|
+
return false
|
78
|
+
end
|
80
79
|
Morpheus::Cli::CliRegistry.instance.add_alias(alias_name, command_string)
|
81
80
|
#print "registered alias #{alias_name}", "\n"
|
82
81
|
if do_export
|
@@ -84,12 +83,14 @@ class Morpheus::Cli::AliasCommand
|
|
84
83
|
morpheus_profile = Morpheus::Cli::DotFile.new(Morpheus::Cli::DotFile.morpheus_profile_filename)
|
85
84
|
morpheus_profile.export_aliases({(alias_name) => command_string})
|
86
85
|
end
|
86
|
+
rescue Morpheus::Cli::CliRegistry::BadAlias => err
|
87
|
+
print_red_alert "#{err.message}"
|
88
|
+
return false
|
87
89
|
rescue => err
|
88
|
-
raise err
|
90
|
+
# raise err
|
89
91
|
print_red_alert "#{err.message}"
|
90
92
|
return false
|
91
93
|
end
|
92
|
-
end
|
93
94
|
|
94
95
|
if Morpheus::Cli::Shell.instance
|
95
96
|
Morpheus::Cli::Shell.instance.recalculate_auto_complete_commands()
|
@@ -9,9 +9,21 @@ class Morpheus::Cli::AppTemplates
|
|
9
9
|
include Morpheus::Cli::CliCommand
|
10
10
|
include Morpheus::Cli::ProvisioningHelper
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
#set_command_name :templates # instead of app-templates
|
13
|
+
|
14
|
+
register_subcommands :list, :get, :add, :update, :remove
|
15
|
+
register_subcommands :duplicate
|
16
|
+
register_subcommands :'upload-image' => :upload_image
|
17
|
+
register_subcommands :'available-tiers'
|
18
|
+
register_subcommands :'add-tier', :'update-tier', :'remove-tier', :'connect-tiers', :'disconnect-tiers'
|
19
|
+
register_subcommands :'add-instance'
|
20
|
+
#register_subcommands :'update-instance'
|
21
|
+
register_subcommands :'remove-instance'
|
22
|
+
register_subcommands :'add-instance-config'
|
23
|
+
#register_subcommands :'update-instance-config'
|
24
|
+
register_subcommands :'remove-instance-config'
|
25
|
+
# alias_subcommand :details, :get
|
26
|
+
# set_default_subcommand :list
|
15
27
|
|
16
28
|
def initialize()
|
17
29
|
# @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
@@ -33,9 +45,10 @@ class Morpheus::Cli::AppTemplates
|
|
33
45
|
|
34
46
|
def list(args)
|
35
47
|
options = {}
|
36
|
-
optparse = OptionParser.new do|opts|
|
48
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
37
49
|
opts.banner = subcommand_usage()
|
38
50
|
build_common_options(opts, options, [:list, :json, :dry_run])
|
51
|
+
opts.footer = "List app templates."
|
39
52
|
end
|
40
53
|
optparse.parse!(args)
|
41
54
|
connect(options)
|
@@ -76,12 +89,14 @@ class Morpheus::Cli::AppTemplates
|
|
76
89
|
|
77
90
|
def get(args)
|
78
91
|
options = {}
|
79
|
-
optparse = OptionParser.new do|opts|
|
80
|
-
opts.banner = subcommand_usage("[
|
81
|
-
opts.on( '-c', '--config', "Display
|
82
|
-
options[:
|
92
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
93
|
+
opts.banner = subcommand_usage("[template]")
|
94
|
+
opts.on( '-c', '--config', "Display raw config only. Default is YAML. Combine with -j for JSON instead." ) do
|
95
|
+
options[:show_config] = true
|
83
96
|
end
|
84
|
-
build_common_options(opts, options, [:json, :dry_run])
|
97
|
+
build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
|
98
|
+
opts.footer = "Get details about an app template.\n" +
|
99
|
+
"[template] is required. This is the name or id of an app template."
|
85
100
|
end
|
86
101
|
optparse.parse!(args)
|
87
102
|
if args.count < 1
|
@@ -92,59 +107,59 @@ class Morpheus::Cli::AppTemplates
|
|
92
107
|
begin
|
93
108
|
if options[:dry_run]
|
94
109
|
if args[0].to_s =~ /\A\d{1,}\Z/
|
95
|
-
print_dry_run @
|
110
|
+
print_dry_run @app_templates_interface.dry.get(args[0].to_i)
|
96
111
|
else
|
97
|
-
print_dry_run @
|
112
|
+
print_dry_run @app_templates_interface.dry.list({name:args[0]})
|
98
113
|
end
|
99
114
|
return
|
100
115
|
end
|
101
116
|
app_template = find_app_template_by_name_or_id(args[0])
|
102
117
|
exit 1 if app_template.nil?
|
103
118
|
|
104
|
-
json_response =
|
119
|
+
json_response = {'appTemplate' => app_template} # skip redundant request
|
120
|
+
#json_response = @app_templates_interface.get(app_template['id'])
|
105
121
|
app_template = json_response['appTemplate']
|
106
122
|
|
107
|
-
if options[:
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
"ID" => 'id',
|
115
|
-
"Name" => 'name',
|
116
|
-
"Account" => lambda {|it| it['account'] ? it['account']['name'] : '' }
|
117
|
-
}
|
118
|
-
print_description_list(description_cols, app_template)
|
119
|
-
|
120
|
-
instance_type_names = (app_template['instanceTypes'] || []).collect {|it| it['name'] }
|
121
|
-
#puts "Instance Types: #{instance_type_names.join(', ')}"
|
122
|
-
config = app_template['config']['tierView']
|
123
|
-
tiers = config['nodes'].select {|node| node['data']['type'] == "tier" }
|
124
|
-
if tiers.empty?
|
125
|
-
puts yellow,"0 Tiers",reset
|
123
|
+
if options[:show_config]
|
124
|
+
#print_h2 "RAW"
|
125
|
+
if options[:json]
|
126
|
+
print cyan
|
127
|
+
print "// JSON config for Morpheus App Template: #{app_template['name']}","\n"
|
128
|
+
print reset
|
129
|
+
puts as_json(app_template["config"])
|
126
130
|
else
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
instances.each do |instance|
|
132
|
-
instance_id = instance['data']['id'].to_s.sub('newinstance-', '')
|
133
|
-
instance_name = instance['data']['instance.name'] || ''
|
134
|
-
print green, " #{instance_name} - #{instance['data']['typeName']} (#{instance_id})\n",reset
|
135
|
-
end
|
136
|
-
|
137
|
-
end
|
131
|
+
print cyan
|
132
|
+
print "# YAML config for Morpheus App Template: #{app_template['name']}","\n"
|
133
|
+
print reset
|
134
|
+
puts as_yaml(app_template["config"])
|
138
135
|
end
|
139
|
-
|
136
|
+
return 0
|
137
|
+
end
|
140
138
|
|
141
|
-
|
142
|
-
|
143
|
-
|
139
|
+
if options[:json]
|
140
|
+
if options[:include_fields]
|
141
|
+
json_response = {"appTemplate" => filter_data(json_response["appTemplate"], options[:include_fields]) }
|
144
142
|
end
|
145
|
-
|
146
|
-
|
143
|
+
puts as_json(json_response, options)
|
144
|
+
return 0
|
145
|
+
elsif options[:yaml]
|
146
|
+
if options[:include_fields]
|
147
|
+
json_response = {"appTemplate" => filter_data(json_response["appTemplate"], options[:include_fields]) }
|
148
|
+
end
|
149
|
+
puts as_yaml(json_response, options)
|
150
|
+
return 0
|
151
|
+
end
|
152
|
+
if options[:csv]
|
153
|
+
puts records_as_csv([json_response['appTemplate']], options)
|
154
|
+
return 0
|
147
155
|
end
|
156
|
+
|
157
|
+
print_h1 "App Template Details"
|
158
|
+
|
159
|
+
print_app_template_details(app_template)
|
160
|
+
|
161
|
+
print reset,"\n"
|
162
|
+
|
148
163
|
rescue RestClient::Exception => e
|
149
164
|
print_rest_exception(e, options)
|
150
165
|
exit 1
|
@@ -153,46 +168,58 @@ class Morpheus::Cli::AppTemplates
|
|
153
168
|
|
154
169
|
def add(args)
|
155
170
|
options = {}
|
156
|
-
optparse = OptionParser.new do|opts|
|
157
|
-
opts.banner = subcommand_usage()
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
options[
|
166
|
-
# options[:group] = val
|
171
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
172
|
+
opts.banner = subcommand_usage("[name] [options]")
|
173
|
+
opts.on('--config JSON', String, "App Template Config JSON") do |val|
|
174
|
+
options['config'] = JSON.parse(val.to_s)
|
175
|
+
end
|
176
|
+
opts.on('--config-yaml YAML', String, "App Template Config YAML") do |val|
|
177
|
+
options['config'] = YAML.load(val.to_s)
|
178
|
+
end
|
179
|
+
opts.on('--config-file FILE', String, "App Template Config from a local JSON or YAML file") do |val|
|
180
|
+
options['configFile'] = val.to_s
|
167
181
|
end
|
182
|
+
build_option_type_options(opts, options, add_app_template_option_types(false))
|
168
183
|
build_common_options(opts, options, [:options, :json, :dry_run])
|
184
|
+
opts.footer = "Create a new app template.\n" +
|
185
|
+
"[name] is optional and can be passed as --name or inside the config instead."
|
186
|
+
"[--config] or [--config-file] can be used to define the app template."
|
169
187
|
end
|
170
188
|
optparse.parse!(args)
|
189
|
+
if args.count > 1
|
190
|
+
print_error Morpheus::Terminal.angry_prompt
|
191
|
+
puts_error "#{command_name} add expects 0-1 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
192
|
+
return 1
|
193
|
+
end
|
194
|
+
options[:options] ||= {}
|
195
|
+
if args[0] && !options[:options]['name']
|
196
|
+
options[:options]['name'] = args[0]
|
197
|
+
end
|
171
198
|
connect(options)
|
172
199
|
begin
|
173
|
-
|
174
|
-
|
175
|
-
options[
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
200
|
+
request_payload = nil
|
201
|
+
config_payload = {}
|
202
|
+
if options['config']
|
203
|
+
config_payload = options['config']
|
204
|
+
request_payload = config_payload
|
205
|
+
elsif options['configFile']
|
206
|
+
config_file = File.expand_path(options['configFile'])
|
207
|
+
if !File.exists?(config_file) || !File.file?(config_file)
|
208
|
+
print_red_alert "File not found: #{config_file}"
|
209
|
+
return false
|
210
|
+
end
|
211
|
+
if config_file =~ /\.ya?ml\Z/
|
212
|
+
config_payload = YAML.load_file(config_file)
|
213
|
+
else
|
214
|
+
config_payload = JSON.parse(File.read(config_file))
|
215
|
+
end
|
216
|
+
request_payload = config_payload
|
217
|
+
else
|
218
|
+
params = Morpheus::Cli::OptionTypes.prompt(add_app_template_option_types, options[:options], @api_client, options[:params])
|
219
|
+
app_template_payload = params.select {|k,v| ['name', 'description', 'category'].include?(k) }
|
220
|
+
# expects no namespace, just the config
|
221
|
+
request_payload = app_template_payload
|
189
222
|
end
|
190
|
-
config = {
|
191
|
-
nodes: []
|
192
|
-
}
|
193
|
-
request_payload = {appTemplate: app_template_payload}
|
194
|
-
request_payload['siteId'] = group['id'] if group
|
195
|
-
request_payload['config'] = config
|
196
223
|
|
197
224
|
if options[:dry_run]
|
198
225
|
print_dry_run @app_templates_interface.dry.create(request_payload)
|
@@ -205,9 +232,19 @@ class Morpheus::Cli::AppTemplates
|
|
205
232
|
print JSON.pretty_generate(json_response)
|
206
233
|
print "\n"
|
207
234
|
else
|
208
|
-
|
209
|
-
|
210
|
-
|
235
|
+
app_template = json_response["appTemplate"]
|
236
|
+
print_green_success "Added app template #{app_template['name']}"
|
237
|
+
if !options[:no_prompt]
|
238
|
+
if ::Morpheus::Cli::OptionTypes::confirm("Would you like to add a tier now?", options.merge({default: false}))
|
239
|
+
add_tier([app_template['id']])
|
240
|
+
while ::Morpheus::Cli::OptionTypes::confirm("Add another tier?", options.merge({default: false})) do
|
241
|
+
add_tier([app_template['id']])
|
242
|
+
end
|
243
|
+
else
|
244
|
+
# print details
|
245
|
+
get([app_template['id']])
|
246
|
+
end
|
247
|
+
end
|
211
248
|
end
|
212
249
|
|
213
250
|
rescue RestClient::Exception => e
|
@@ -218,10 +255,23 @@ class Morpheus::Cli::AppTemplates
|
|
218
255
|
|
219
256
|
def update(args)
|
220
257
|
options = {}
|
221
|
-
optparse = OptionParser.new do|opts|
|
222
|
-
opts.banner = subcommand_usage("[
|
258
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
259
|
+
opts.banner = subcommand_usage("[template] [options]")
|
260
|
+
opts.on('--config JSON', String, "App Template Config JSON") do |val|
|
261
|
+
options['config'] = JSON.parse(val.to_s)
|
262
|
+
end
|
263
|
+
opts.on('--config-yaml YAML', String, "App Template Config YAML") do |val|
|
264
|
+
options['config'] = YAML.load(val.to_s)
|
265
|
+
end
|
266
|
+
opts.on('--config-file FILE', String, "App Template Config from a local JSON or YAML file") do |val|
|
267
|
+
options['configFile'] = val.to_s
|
268
|
+
end
|
223
269
|
build_option_type_options(opts, options, update_app_template_option_types(false))
|
224
|
-
build_common_options(opts, options, [:options, :json, :dry_run])
|
270
|
+
build_common_options(opts, options, [:options, :json, :dry_run, :quiet])
|
271
|
+
opts.footer = "Update an app template.\n" +
|
272
|
+
"[template] is required. This is the name or id of an app template.\n" +
|
273
|
+
"[options] Available options include --name and --description. This will update only the specified values.\n" +
|
274
|
+
"[--config] or [--config-file] can be used to replace the entire app template."
|
225
275
|
end
|
226
276
|
optparse.parse!(args)
|
227
277
|
|
@@ -237,291 +287,1106 @@ class Morpheus::Cli::AppTemplates
|
|
237
287
|
app_template = find_app_template_by_name_or_id(args[0])
|
238
288
|
exit 1 if app_template.nil?
|
239
289
|
|
240
|
-
|
241
|
-
|
290
|
+
request_payload = nil
|
291
|
+
config_payload = {}
|
292
|
+
if options['config']
|
293
|
+
config_payload = options['config']
|
294
|
+
request_payload = config_payload
|
295
|
+
elsif options['configFile']
|
296
|
+
config_file = options['configFile']
|
297
|
+
if !File.exists?(config_file)
|
298
|
+
print_red_alert "File not found: #{config_file}"
|
299
|
+
return false
|
300
|
+
end
|
301
|
+
if config_file =~ /\.ya?ml\Z/
|
302
|
+
config_payload = YAML.load_file(config_file)
|
303
|
+
else
|
304
|
+
config_payload = JSON.parse(File.read(config_file))
|
305
|
+
end
|
306
|
+
request_payload = config_payload
|
307
|
+
else
|
308
|
+
# update just name,description,category
|
309
|
+
# preserve all other attributes of the config..
|
242
310
|
|
243
|
-
|
244
|
-
|
245
|
-
puts optparse
|
246
|
-
exit 1
|
247
|
-
end
|
311
|
+
#params = Morpheus::Cli::OptionTypes.prompt(update_app_template_option_types, options[:options], @api_client, options[:params])
|
312
|
+
params = options[:options] || {}
|
248
313
|
|
249
|
-
|
250
|
-
|
251
|
-
|
314
|
+
if params.empty?
|
315
|
+
# print_red_alert "Specify atleast one option to update"
|
316
|
+
print_red_alert "Specify atleast one option to update.\nOr use --config or --config-file to replace the entire config."
|
317
|
+
puts optparse
|
318
|
+
exit 1
|
319
|
+
end
|
252
320
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
321
|
+
#puts "parsed params is : #{params.inspect}"
|
322
|
+
app_template_payload = params.select {|k,v| ['name','description','category'].include?(k) }
|
323
|
+
# expects no namespace, just the config
|
324
|
+
# preserve all other attributes of the config.
|
325
|
+
request_payload = app_template["config"].merge(app_template_payload)
|
326
|
+
# todo maybe: put name, description and category at the front.
|
327
|
+
# request_payload = app_template_payload.merge(app_template["config"].merge(app_template_payload))
|
328
|
+
end
|
329
|
+
|
330
|
+
if options[:dry_run]
|
331
|
+
print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
|
332
|
+
return
|
258
333
|
end
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
334
|
+
|
335
|
+
json_response = @app_templates_interface.update(app_template['id'], request_payload)
|
336
|
+
|
337
|
+
if options[:json]
|
338
|
+
print JSON.pretty_generate(json_response)
|
339
|
+
print "\n"
|
263
340
|
else
|
264
|
-
|
341
|
+
unless options[:quiet]
|
342
|
+
app_template = json_response['appTemplate']
|
343
|
+
print_green_success "Updated app template #{app_template['name']}"
|
344
|
+
details_options = [app_template['id']]
|
345
|
+
get(details_options)
|
346
|
+
end
|
265
347
|
end
|
266
|
-
# request_payload['config'] = config['tierView']
|
267
|
-
request_payload['config'] = config
|
268
348
|
|
349
|
+
rescue RestClient::Exception => e
|
350
|
+
print_rest_exception(e, options)
|
351
|
+
exit 1
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
|
356
|
+
def upload_image(args)
|
357
|
+
image_type_name = nil
|
358
|
+
options = {}
|
359
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
360
|
+
opts.banner = subcommand_usage("[template] [file]")
|
361
|
+
build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
|
362
|
+
opts.footer = "Upload an image file to be used as the icon for an app template.\n" +
|
363
|
+
"[template] is required. This is the name or id of an app template.\n" +
|
364
|
+
"[file] is required. This is the local path of a file to upload [png|jpg|svg]."
|
365
|
+
end
|
366
|
+
optparse.parse!(args)
|
367
|
+
if args.count != 2
|
368
|
+
print_error Morpheus::Terminal.angry_prompt
|
369
|
+
puts_error "#{command_name} upload-image expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
370
|
+
return 1
|
371
|
+
end
|
372
|
+
app_template_name = args[0]
|
373
|
+
filename = File.expand_path(args[1].to_s)
|
374
|
+
image_file = nil
|
375
|
+
if filename && File.file?(filename)
|
376
|
+
# maybe validate it's an image file? [.png|jpg|svg]
|
377
|
+
image_file = File.new(filename, 'rb')
|
378
|
+
else
|
379
|
+
print_red_alert "File not found: #{filename}"
|
380
|
+
# print_error Morpheus::Terminal.angry_prompt
|
381
|
+
# puts_error "bad argument [file] - File not found: #{filename}\n#{optparse}"
|
382
|
+
return 1
|
383
|
+
end
|
384
|
+
connect(options)
|
385
|
+
begin
|
386
|
+
app_template = find_app_template_by_name_or_id(app_template_name)
|
387
|
+
exit 1 if app_template.nil?
|
269
388
|
if options[:dry_run]
|
270
|
-
print_dry_run @app_templates_interface.dry.
|
389
|
+
print_dry_run @app_templates_interface.dry.save_image(app_template['id'], image_file)
|
390
|
+
return 0
|
391
|
+
end
|
392
|
+
unless options[:quiet] || options[:json]
|
393
|
+
print cyan, "Uploading file #{filename} ...", reset, "\n"
|
394
|
+
end
|
395
|
+
json_response = @app_templates_interface.save_image(app_template['id'], image_file)
|
396
|
+
if options[:json]
|
397
|
+
print JSON.pretty_generate(json_response)
|
398
|
+
elsif !options[:quiet]
|
399
|
+
app_template = json_response['appTemplate']
|
400
|
+
new_image_url = app_template['image']
|
401
|
+
print cyan, "Updated app template #{app_template['name']} image.\nNew image url is: #{new_image_url}", reset, "\n\n"
|
402
|
+
get([app_template['id']])
|
403
|
+
end
|
404
|
+
return 0
|
405
|
+
rescue RestClient::Exception => e
|
406
|
+
print_rest_exception(e, options)
|
407
|
+
return 1
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
def duplicate(args)
|
412
|
+
options = {}
|
413
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
414
|
+
opts.banner = subcommand_usage("[template] [new name]")
|
415
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run])
|
416
|
+
opts.footer = "Duplicate an app template." + "\n" +
|
417
|
+
"[template] is required. This is the name or id of an app template." + "\n" +
|
418
|
+
"[new name] is required. This is the name for the clone."
|
419
|
+
end
|
420
|
+
optparse.parse!(args)
|
421
|
+
|
422
|
+
if args.count < 1
|
423
|
+
puts optparse
|
424
|
+
exit 1
|
425
|
+
end
|
426
|
+
|
427
|
+
request_payload = {"appTemplate" => {}}
|
428
|
+
if args[1]
|
429
|
+
request_payload["appTemplate"]["name"] = args[1]
|
430
|
+
end
|
431
|
+
|
432
|
+
connect(options)
|
433
|
+
begin
|
434
|
+
app_template = find_app_template_by_name_or_id(args[0])
|
435
|
+
exit 1 if app_template.nil?
|
436
|
+
# unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to duplicate the app template #{app_template['name']}?")
|
437
|
+
# exit
|
438
|
+
# end
|
439
|
+
if options[:dry_run]
|
440
|
+
print_dry_run @app_templates_interface.dry.duplicate(app_template['id'], request_payload)
|
271
441
|
return
|
272
442
|
end
|
443
|
+
json_response = @app_templates_interface.duplicate(app_template['id'], request_payload)
|
273
444
|
|
274
|
-
|
445
|
+
if options[:json]
|
446
|
+
print JSON.pretty_generate(json_response)
|
447
|
+
print "\n"
|
448
|
+
else
|
449
|
+
new_app_template = json_response["appTemplate"] || {}
|
450
|
+
print_green_success "Created duplicate app template '#{new_app_template['name']}'"
|
451
|
+
#get([new_app_template["id"]])
|
452
|
+
end
|
275
453
|
|
454
|
+
rescue RestClient::Exception => e
|
455
|
+
print_rest_exception(e, options)
|
456
|
+
exit 1
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def remove(args)
|
461
|
+
options = {}
|
462
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
463
|
+
opts.banner = subcommand_usage("[template]")
|
464
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run])
|
465
|
+
opts.footer = "Delete an app template." + "\n" +
|
466
|
+
"[template] is required. This is the name or id of an app template."
|
467
|
+
end
|
468
|
+
optparse.parse!(args)
|
469
|
+
|
470
|
+
if args.count < 1
|
471
|
+
puts optparse
|
472
|
+
exit 1
|
473
|
+
end
|
474
|
+
|
475
|
+
connect(options)
|
476
|
+
begin
|
477
|
+
app_template = find_app_template_by_name_or_id(args[0])
|
478
|
+
exit 1 if app_template.nil?
|
479
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the app template #{app_template['name']}?")
|
480
|
+
exit
|
481
|
+
end
|
482
|
+
if options[:dry_run]
|
483
|
+
print_dry_run @app_templates_interface.dry.destroy(app_template['id'])
|
484
|
+
return
|
485
|
+
end
|
486
|
+
json_response = @app_templates_interface.destroy(app_template['id'])
|
276
487
|
|
277
488
|
if options[:json]
|
278
489
|
print JSON.pretty_generate(json_response)
|
279
490
|
print "\n"
|
280
491
|
else
|
281
|
-
print_green_success "
|
282
|
-
|
283
|
-
|
492
|
+
print_green_success "Removed app template #{app_template['name']}"
|
493
|
+
end
|
494
|
+
|
495
|
+
rescue RestClient::Exception => e
|
496
|
+
print_rest_exception(e, options)
|
497
|
+
exit 1
|
498
|
+
end
|
499
|
+
end
|
500
|
+
|
501
|
+
def add_instance(args)
|
502
|
+
options = {}
|
503
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
504
|
+
opts.banner = subcommand_usage("[template] [tier] [instance-type]")
|
505
|
+
# opts.on( '-g', '--group GROUP', "Group" ) do |val|
|
506
|
+
# options[:group] = val
|
507
|
+
# end
|
508
|
+
# opts.on( '-c', '--cloud CLOUD', "Cloud" ) do |val|
|
509
|
+
# options[:cloud] = val
|
510
|
+
# end
|
511
|
+
opts.on('--name VALUE', String, "Instance Name") do |val|
|
512
|
+
options[:instance_name] = val
|
513
|
+
end
|
514
|
+
build_common_options(opts, options, [:options, :json, :dry_run, :remote])
|
515
|
+
opts.footer = "Update an app template, adding an instance." + "\n" +
|
516
|
+
"[template] is required. This is the name or id of an app template." + "\n" +
|
517
|
+
"[tier] is required and will be prompted for. This is the name of the tier." + "\n" +
|
518
|
+
"[instance-type] is required and will be prompted for. This is the type of instance."
|
519
|
+
end
|
520
|
+
optparse.parse!(args)
|
521
|
+
|
522
|
+
if args.count < 1
|
523
|
+
print_error Morpheus::Terminal.angry_prompt
|
524
|
+
puts_error "#{command_name} add-instance expects 3 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
525
|
+
return 1
|
526
|
+
end
|
527
|
+
|
528
|
+
connect(options)
|
529
|
+
|
530
|
+
begin
|
531
|
+
app_template_name = args[0]
|
532
|
+
tier_name = args[1]
|
533
|
+
instance_type_code = args[2]
|
534
|
+
# we also need consider when there is multiple instances of the same type in
|
535
|
+
# a template/tier.. so maybe split instance_type_code as [type-code]:[index].. or...errr
|
536
|
+
|
537
|
+
app_template = find_app_template_by_name_or_id(app_template_name)
|
538
|
+
return 1 if app_template.nil?
|
539
|
+
|
540
|
+
if !tier_name
|
541
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tierName', 'fieldLabel' => 'Tier Name', 'type' => 'text', 'required' => true, 'description' => 'Enter the name of the tier'}], options[:options])
|
542
|
+
tier_name = v_prompt['tierName']
|
543
|
+
end
|
544
|
+
|
545
|
+
if !instance_type_code
|
546
|
+
instance_type_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'type' => 'select', 'fieldLabel' => 'Type', 'optionSource' => 'instanceTypes', 'required' => true, 'description' => 'Select Instance Type.'}],options[:options],api_client,{})
|
547
|
+
instance_type_code = instance_type_prompt['type']
|
548
|
+
end
|
549
|
+
instance_type = find_instance_type_by_code(instance_type_code)
|
550
|
+
return 1 if instance_type.nil?
|
551
|
+
|
552
|
+
tier_config = nil
|
553
|
+
instance_config = nil
|
554
|
+
|
555
|
+
app_template["config"] ||= {}
|
556
|
+
tiers = app_template["config"]["tiers"]
|
557
|
+
tiers ||= {}
|
558
|
+
# tier identified by name, case sensitive...
|
559
|
+
if !tiers[tier_name]
|
560
|
+
tiers[tier_name] = {}
|
561
|
+
end
|
562
|
+
tier_config = tiers[tier_name]
|
563
|
+
|
564
|
+
tier_config['instances'] ||= []
|
565
|
+
instance_config = tier_config['instances'].find {|it| it["instance"] && it["instance"]["type"] && it["instance"]["type"] == instance_type["code"] }
|
566
|
+
if !instance_config
|
567
|
+
instance_config = {'instance' => {'type' => instance_type['code']} }
|
568
|
+
tier_config['instances'].push(instance_config)
|
569
|
+
end
|
570
|
+
instance_config['instance'] ||= {}
|
571
|
+
|
572
|
+
# just prompts for Instance Name (optional)
|
573
|
+
instance_name = nil
|
574
|
+
if options[:instance_name]
|
575
|
+
instance_name = options[:instance_name]
|
576
|
+
else
|
577
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Instance Name', 'type' => 'text', 'defaultValue' => instance_config['instance']['name']}])
|
578
|
+
instance_name = v_prompt['name'] || ''
|
579
|
+
end
|
580
|
+
|
581
|
+
if instance_name
|
582
|
+
if instance_name.to_s == 'null'
|
583
|
+
instance_config['instance'].delete('name')
|
584
|
+
# instance_config['instance']['name'] = ''
|
585
|
+
else
|
586
|
+
instance_config['instance']['name'] = instance_name
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
# ok, make api request
|
591
|
+
app_template["config"]["tiers"] = tiers
|
592
|
+
request_payload = {appTemplate: app_template}
|
593
|
+
|
594
|
+
if options[:dry_run]
|
595
|
+
print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
|
596
|
+
return 0
|
597
|
+
end
|
598
|
+
|
599
|
+
json_response = @app_templates_interface.update(app_template['id'], request_payload)
|
600
|
+
|
601
|
+
if options[:json]
|
602
|
+
puts JSON.pretty_generate(json_response)
|
603
|
+
elsif !options[:quiet]
|
604
|
+
print_green_success "Instance added to app template #{app_template['name']}"
|
605
|
+
# prompt for new instance
|
606
|
+
if !options[:no_prompt]
|
607
|
+
if ::Morpheus::Cli::OptionTypes::confirm("Would you like to add a config now?", options.merge({default: true}))
|
608
|
+
# todo: this needs to work by index, because you can have multiple instances of the same type
|
609
|
+
add_instance_config([app_template['id'], tier_name, instance_type['code']])
|
610
|
+
while ::Morpheus::Cli::OptionTypes::confirm("Add another config?", options.merge({default: false})) do
|
611
|
+
add_instance_config([app_template['id'], tier_name, instance_type['code']])
|
612
|
+
end
|
613
|
+
else
|
614
|
+
# print details
|
615
|
+
get([app_template['name']])
|
616
|
+
end
|
617
|
+
end
|
618
|
+
end
|
619
|
+
return 0
|
620
|
+
|
621
|
+
rescue RestClient::Exception => e
|
622
|
+
print_rest_exception(e, options)
|
623
|
+
return 1
|
624
|
+
end
|
625
|
+
|
626
|
+
end
|
627
|
+
|
628
|
+
def add_instance_config(args)
|
629
|
+
options = {}
|
630
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
631
|
+
opts.banner = subcommand_usage("[template] [tier] [instance]")
|
632
|
+
opts.on( '-g', '--group GROUP', "Group" ) do |val|
|
633
|
+
options[:group] = val
|
634
|
+
end
|
635
|
+
opts.on( '-c', '--cloud CLOUD', "Cloud" ) do |val|
|
636
|
+
options[:cloud] = val
|
637
|
+
end
|
638
|
+
opts.on( '-e', '--env ENVIRONMENT', "Environment" ) do |val|
|
639
|
+
options[:environment] = val
|
640
|
+
end
|
641
|
+
opts.on('--name VALUE', String, "Instance Name") do |val|
|
642
|
+
options[:instance_name] = val
|
643
|
+
end
|
644
|
+
build_common_options(opts, options, [:options, :json, :dry_run, :remote])
|
645
|
+
opts.footer = "Update an app template, adding an instance config." + "\n" +
|
646
|
+
"[template] is required. This is the name or id of an app template." + "\n" +
|
647
|
+
"[tier] is required. This is the name of the tier." + "\n" +
|
648
|
+
"[instance] is required. This is the type of instance."
|
649
|
+
end
|
650
|
+
optparse.parse!(args)
|
651
|
+
|
652
|
+
if args.count < 3
|
653
|
+
print_error Morpheus::Terminal.angry_prompt
|
654
|
+
puts_error "Wrong number of arguments"
|
655
|
+
puts_error optparse
|
656
|
+
return 1
|
657
|
+
end
|
658
|
+
|
659
|
+
connect(options)
|
660
|
+
|
661
|
+
begin
|
662
|
+
|
663
|
+
app_template_name = args[0]
|
664
|
+
tier_name = args[1]
|
665
|
+
instance_type_code = args[2]
|
666
|
+
# we also need consider when there is multiple instances of the same type in
|
667
|
+
# a template/tier.. so maybe split instance_type_code as [type-code]:[index].. or...errr
|
668
|
+
|
669
|
+
app_template = find_app_template_by_name_or_id(app_template_name)
|
670
|
+
return 1 if app_template.nil?
|
671
|
+
|
672
|
+
instance_type = find_instance_type_by_code(instance_type_code)
|
673
|
+
return 1 if instance_type.nil?
|
674
|
+
|
675
|
+
tier_config = nil
|
676
|
+
instance_config = nil
|
677
|
+
|
678
|
+
app_template["config"] ||= {}
|
679
|
+
tiers = app_template["config"]["tiers"]
|
680
|
+
tiers ||= {}
|
681
|
+
# tier identified by name, case sensitive...
|
682
|
+
if !tiers[tier_name]
|
683
|
+
tiers[tier_name] = {}
|
684
|
+
end
|
685
|
+
tier_config = tiers[tier_name]
|
686
|
+
|
687
|
+
tier_config['instances'] ||= []
|
688
|
+
instance_config = tier_config['instances'].find {|it| it["instance"] && it["instance"]["type"] && it["instance"]["type"] == instance_type["code"] }
|
689
|
+
if !instance_config
|
690
|
+
instance_config = {'instance' => {'type' => instance_type['code']} }
|
691
|
+
tier_config['instances'].push(instance_config)
|
692
|
+
end
|
693
|
+
instance_config['instance'] ||= {}
|
694
|
+
|
695
|
+
# group prompt
|
696
|
+
|
697
|
+
# use active group by default
|
698
|
+
options[:group] ||= @active_group_id
|
699
|
+
|
700
|
+
|
701
|
+
# available_groups = get_available_groups()
|
702
|
+
# group_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'selectOptions' => get_available_groups(), 'required' => true, 'defaultValue' => @active_group_id}],options[:options],@api_client,{})
|
703
|
+
|
704
|
+
# group_id = group_prompt['group']
|
705
|
+
# the_group = find_group_by_name_or_id_for_provisioning(group_id)
|
706
|
+
|
707
|
+
# # cloud prompt
|
708
|
+
# cloud_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'cloud', 'type' => 'select', 'fieldLabel' => 'Cloud', 'optionSource' => 'clouds', 'required' => true, 'description' => 'Select Cloud.'}],options[:options],@api_client,{groupId: group_id})
|
709
|
+
# cloud_id = cloud_prompt['cloud']
|
710
|
+
|
711
|
+
# look for existing config for group + cloud
|
712
|
+
|
713
|
+
options[:name_required] = false
|
714
|
+
options[:instance_type_code] = instance_type["code"]
|
715
|
+
|
716
|
+
#options[:options].deep_merge!(specific_config)
|
717
|
+
# this provisioning helper method handles all (most) of the parsing and prompting
|
718
|
+
instance_config_payload = prompt_new_instance(options)
|
719
|
+
|
720
|
+
# strip all empty string and nil, would be problematic for update()
|
721
|
+
instance_config_payload.deep_compact!
|
722
|
+
|
723
|
+
# puts "INSTANCE CONFIG YAML:"
|
724
|
+
# puts as_yaml(instance_config_payload)
|
725
|
+
|
726
|
+
selected_environment = instance_config_payload.delete('instanceContext') || instance_config_payload.delete('environment')
|
727
|
+
# groom provision instance payload for template purposes
|
728
|
+
selected_cloud_id = instance_config_payload.delete('zoneId')
|
729
|
+
selected_site = instance_config_payload['instance'].delete('site')
|
730
|
+
selected_site_id = selected_site['id']
|
731
|
+
|
732
|
+
selected_group = find_group_by_name_or_id_for_provisioning(selected_site_id)
|
733
|
+
selected_cloud = find_cloud_by_name_or_id_for_provisioning(selected_group['id'], selected_cloud_id)
|
734
|
+
|
735
|
+
# store config in environments => env => groups => groupname => clouds => cloudname =>
|
736
|
+
current_config = instance_config
|
737
|
+
if selected_environment.to_s != ''
|
738
|
+
instance_config['environments'] ||= {}
|
739
|
+
instance_config['environments'][selected_environment] ||= {}
|
740
|
+
current_config = instance_config['environments'][selected_environment]
|
741
|
+
end
|
742
|
+
|
743
|
+
current_config['groups'] ||= {}
|
744
|
+
current_config['groups'][selected_group['name']] ||= {}
|
745
|
+
current_config['groups'][selected_group['name']]['clouds'] ||= {}
|
746
|
+
current_config['groups'][selected_group['name']]['clouds'][selected_cloud['name']] = instance_config_payload
|
747
|
+
|
748
|
+
# ok, make api request
|
749
|
+
app_template["config"]["tiers"] = tiers
|
750
|
+
request_payload = {appTemplate: app_template}
|
751
|
+
|
752
|
+
if options[:dry_run]
|
753
|
+
print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
|
754
|
+
return 0
|
755
|
+
end
|
756
|
+
|
757
|
+
json_response = @app_templates_interface.update(app_template['id'], request_payload)
|
758
|
+
|
759
|
+
if options[:json]
|
760
|
+
puts JSON.pretty_generate(json_response)
|
761
|
+
else
|
762
|
+
print_green_success "Instance added to app template #{app_template['name']}"
|
763
|
+
get([app_template['name']])
|
764
|
+
end
|
765
|
+
return 0
|
766
|
+
|
767
|
+
rescue RestClient::Exception => e
|
768
|
+
print_rest_exception(e, options)
|
769
|
+
return 1
|
770
|
+
end
|
771
|
+
|
772
|
+
end
|
773
|
+
|
774
|
+
def remove_instance_config(args)
|
775
|
+
instance_index = nil
|
776
|
+
options = {}
|
777
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
778
|
+
opts.banner = subcommand_usage("[template] [tier] [instance] -g GROUP -c CLOUD")
|
779
|
+
opts.on( '-g', '--group GROUP', "Group" ) do |val|
|
780
|
+
options[:group] = val
|
781
|
+
end
|
782
|
+
opts.on( '-c', '--cloud CLOUD', "Cloud" ) do |val|
|
783
|
+
options[:cloud] = val
|
784
|
+
end
|
785
|
+
opts.on( '-e', '--env ENV', "Environment" ) do |val|
|
786
|
+
options[:environment] = val
|
787
|
+
end
|
788
|
+
# opts.on( nil, '--index NUMBER', "The index of the instance to remove, starting with 0." ) do |val|
|
789
|
+
# instance_index = val.to_i
|
790
|
+
# end
|
791
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run])
|
792
|
+
opts.footer = "Update an app template, removing a specified instance config." + "\n" +
|
793
|
+
"[template] is required. This is the name or id of an app template." + "\n" +
|
794
|
+
"[tier] is required. This is the name of the tier." + "\n" +
|
795
|
+
"[instance] is required. This is the type of instance." + "\n" +
|
796
|
+
"The config scope is specified with the -g GROUP, -c CLOUD and -e ENV. The -g and -c options are required."
|
797
|
+
end
|
798
|
+
optparse.parse!(args)
|
799
|
+
|
800
|
+
if args.count < 3
|
801
|
+
print_error Morpheus::Terminal.angry_prompt
|
802
|
+
puts_error "Wrong number of arguments"
|
803
|
+
puts_error optparse
|
804
|
+
return 1
|
805
|
+
end
|
806
|
+
if !options[:group]
|
807
|
+
print_error Morpheus::Terminal.angry_prompt
|
808
|
+
puts_error "Missing required argument -g GROUP"
|
809
|
+
puts_error optparse
|
810
|
+
return 1
|
811
|
+
end
|
812
|
+
if !options[:cloud]
|
813
|
+
print_error Morpheus::Terminal.angry_prompt
|
814
|
+
puts_error "Missing required argument -g CLOUD"
|
815
|
+
puts_error optparse
|
816
|
+
return 1
|
817
|
+
end
|
818
|
+
connect(options)
|
819
|
+
|
820
|
+
begin
|
821
|
+
|
822
|
+
app_template_name = args[0]
|
823
|
+
tier_name = args[1]
|
824
|
+
instance_type_code = args[2]
|
825
|
+
# we also need consider when there is multiple instances of the same type in
|
826
|
+
# a template/tier.. so maybe split instance_type_code as [type-code]:[index].. or...errr
|
827
|
+
|
828
|
+
app_template = find_app_template_by_name_or_id(app_template_name)
|
829
|
+
return 1 if app_template.nil?
|
830
|
+
|
831
|
+
instance_type = find_instance_type_by_code(instance_type_code)
|
832
|
+
return 1 if instance_type.nil?
|
833
|
+
|
834
|
+
tier_config = nil
|
835
|
+
# instance_config = nil
|
836
|
+
|
837
|
+
app_template["config"] ||= {}
|
838
|
+
tiers = app_template["config"]["tiers"]
|
839
|
+
tiers ||= {}
|
840
|
+
# tier identified by name, case sensitive...
|
841
|
+
if !tiers[tier_name]
|
842
|
+
print_red_alert "Tier not found by name #{tier_name}"
|
843
|
+
return 1
|
844
|
+
end
|
845
|
+
tier_config = tiers[tier_name]
|
846
|
+
|
847
|
+
if !tier_config
|
848
|
+
print_red_alert "Tier not found by name #{tier1_name}!"
|
849
|
+
return 1
|
850
|
+
elsif tier_config['instances'].nil? || tier_config['instances'].empty?
|
851
|
+
print_red_alert "Tier #{tier_name} is empty!"
|
852
|
+
return 1
|
853
|
+
end
|
854
|
+
|
855
|
+
matching_indices = []
|
856
|
+
if tier_config['instances']
|
857
|
+
if instance_index
|
858
|
+
matching_indices = [instance_index].compact
|
859
|
+
else
|
860
|
+
tier_config['instances'].each_with_index do |instance_config, index|
|
861
|
+
is_match = instance_config['instance'] && instance_config['instance']['type'] == instance_type['code']
|
862
|
+
if is_match
|
863
|
+
matching_indices << index
|
864
|
+
end
|
865
|
+
end
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
if matching_indices.size == 0
|
870
|
+
print_red_alert "Instance not found by tier: #{tier_name}, type: #{instance_type_code}"
|
871
|
+
return 1
|
872
|
+
elsif matching_indices.size > 1
|
873
|
+
#print_error Morpheus::Terminal.angry_prompt
|
874
|
+
print_red_alert "More than one instance found by tier: #{tier_name}, type: #{instance_type_code}"
|
875
|
+
puts_error "Try using the --index option to identify the instance you wish to remove."
|
876
|
+
puts_error optparse
|
877
|
+
return 1
|
878
|
+
end
|
879
|
+
|
880
|
+
# ok, find the specified config
|
881
|
+
instance_config = tier_config['instances'][matching_indices[0]]
|
882
|
+
parent_config = nil
|
883
|
+
current_config = instance_config
|
884
|
+
delete_key = nil
|
885
|
+
|
886
|
+
config_description = "type: #{instance_type['code']}"
|
887
|
+
config_description << " environment: #{options[:environment]}" if options[:environment]
|
888
|
+
config_description << " group: #{options[:group]}" if options[:group]
|
889
|
+
config_description << " cloud: #{options[:cloud]}" if options[:cloud]
|
890
|
+
config_description = config_description.strip
|
891
|
+
|
892
|
+
|
893
|
+
# find config in environments => env => groups => groupname => clouds => cloudname =>
|
894
|
+
if options[:environment]
|
895
|
+
if current_config && current_config['environments'] && current_config['environments'][options[:environment]]
|
896
|
+
parent_config = current_config['environments']
|
897
|
+
delete_key = options[:environment]
|
898
|
+
current_config = parent_config[delete_key]
|
899
|
+
else
|
900
|
+
print_red_alert "Instance config not found for scope #{config_description}"
|
901
|
+
return 1
|
902
|
+
end
|
903
|
+
end
|
904
|
+
if options[:group]
|
905
|
+
if current_config && current_config['groups'] && current_config['groups'][options[:group]]
|
906
|
+
parent_config = current_config['groups']
|
907
|
+
delete_key = options[:group]
|
908
|
+
current_config = parent_config[delete_key]
|
909
|
+
else
|
910
|
+
print_red_alert "Instance config not found for scope #{config_description}"
|
911
|
+
return 1
|
912
|
+
end
|
913
|
+
end
|
914
|
+
if options[:cloud]
|
915
|
+
if current_config && current_config['clouds'] && current_config['clouds'][options[:cloud]]
|
916
|
+
parent_config = current_config['clouds']
|
917
|
+
delete_key = options[:cloud]
|
918
|
+
current_config = parent_config[delete_key]
|
919
|
+
else
|
920
|
+
print_red_alert "Instance config not found for scope #{config_description}"
|
921
|
+
return 1
|
922
|
+
end
|
923
|
+
end
|
924
|
+
|
925
|
+
# remove it
|
926
|
+
parent_config.delete(delete_key)
|
927
|
+
|
928
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete this instance config #{config_description} ?")
|
929
|
+
return 9
|
930
|
+
end
|
931
|
+
|
932
|
+
# ok, make api request
|
933
|
+
app_template["config"]["tiers"] = tiers
|
934
|
+
request_payload = {appTemplate: app_template}
|
935
|
+
|
936
|
+
if options[:dry_run]
|
937
|
+
print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
|
938
|
+
return
|
939
|
+
end
|
940
|
+
json_response = @app_templates_interface.update(app_template['id'], request_payload)
|
941
|
+
|
942
|
+
if options[:json]
|
943
|
+
puts JSON.pretty_generate(json_response)
|
944
|
+
else
|
945
|
+
print_green_success "Removed instance from app template."
|
946
|
+
get([app_template['id']])
|
947
|
+
end
|
948
|
+
return 0
|
949
|
+
|
950
|
+
rescue RestClient::Exception => e
|
951
|
+
print_rest_exception(e, options)
|
952
|
+
exit 1
|
953
|
+
end
|
954
|
+
end
|
955
|
+
|
956
|
+
def update_instance(args)
|
957
|
+
print_red_alert "NOT YET SUPPORTED"
|
958
|
+
return 5
|
959
|
+
end
|
960
|
+
|
961
|
+
def update_instance_config(args)
|
962
|
+
print_red_alert "NOT YET SUPPORTED"
|
963
|
+
return 5
|
964
|
+
end
|
965
|
+
|
966
|
+
def remove_instance(args)
|
967
|
+
instance_index = nil
|
968
|
+
options = {}
|
969
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
970
|
+
opts.banner = subcommand_usage("[template] [tier] [instance]")
|
971
|
+
# opts.on('--index NUMBER', Number, "Identify Instance by index within tier, starting with 0." ) do |val|
|
972
|
+
# instance_index = val.to_i
|
973
|
+
# end
|
974
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run])
|
975
|
+
end
|
976
|
+
optparse.parse!(args)
|
977
|
+
|
978
|
+
if args.count < 3
|
979
|
+
print_error Morpheus::Terminal.angry_prompt
|
980
|
+
puts_error "Wrong number of arguments"
|
981
|
+
puts_error optparse
|
982
|
+
return 1
|
983
|
+
end
|
984
|
+
|
985
|
+
connect(options)
|
986
|
+
|
987
|
+
begin
|
988
|
+
|
989
|
+
app_template_name = args[0]
|
990
|
+
tier_name = args[1]
|
991
|
+
instance_identier = args[2]
|
992
|
+
|
993
|
+
# instance_type_code = args[2]
|
994
|
+
# we also need consider when there is multiple instances of the same type in
|
995
|
+
# a template/tier.. so maybe split instance_type_code as [type-code]:[index].. or...errr
|
996
|
+
|
997
|
+
app_template = find_app_template_by_name_or_id(app_template_name)
|
998
|
+
return 1 if app_template.nil?
|
999
|
+
|
1000
|
+
# instance_type = find_instance_type_by_code(instance_type_code)
|
1001
|
+
# return 1 if instance_type.nil?
|
1002
|
+
|
1003
|
+
tier_config = nil
|
1004
|
+
# instance_config = nil
|
1005
|
+
|
1006
|
+
app_template["config"] ||= {}
|
1007
|
+
tiers = app_template["config"]["tiers"]
|
1008
|
+
tiers ||= {}
|
1009
|
+
# tier identified by name, case sensitive...
|
1010
|
+
if !tiers[tier_name]
|
1011
|
+
print_red_alert "Tier not found by name #{tier_name}"
|
1012
|
+
return 1
|
1013
|
+
end
|
1014
|
+
tier_config = tiers[tier_name]
|
1015
|
+
|
1016
|
+
if tier_config['instances'].nil? || tier_config['instances'].empty?
|
1017
|
+
print_red_alert "Tier #{tier_name} is empty!"
|
1018
|
+
return 1
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
# find instance
|
1022
|
+
matching_indices = []
|
1023
|
+
if tier_config['instances']
|
1024
|
+
if instance_identier.to_s =~ /\A\d{1,}\Z/
|
1025
|
+
matching_indices = [instance_identier.to_i].compact
|
1026
|
+
else
|
1027
|
+
tier_config['instances'].each_with_index do |instance_config, index|
|
1028
|
+
if instance_config['instance'] && instance_config['instance']['type'] == instance_identier
|
1029
|
+
matching_indices << index
|
1030
|
+
elsif instance_config['instance'] && instance_config['instance']['name'] == instance_identier
|
1031
|
+
matching_indices << index
|
1032
|
+
end
|
1033
|
+
end
|
1034
|
+
end
|
1035
|
+
end
|
1036
|
+
if matching_indices.size == 0
|
1037
|
+
print_red_alert "Instance not found by tier: #{tier_name}, instance: #{instance_identier}"
|
1038
|
+
return 1
|
1039
|
+
elsif matching_indices.size > 1
|
1040
|
+
#print_error Morpheus::Terminal.angry_prompt
|
1041
|
+
print_red_alert "More than one instance matched tier: #{tier_name}, instance: #{instance_identier}"
|
1042
|
+
puts_error "Instance can be identified type, name or index within the tier."
|
1043
|
+
puts_error optparse
|
1044
|
+
return 1
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
# remove it
|
1048
|
+
tier_config['instances'].delete_at(matching_indices[0])
|
1049
|
+
|
1050
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete this instance #{instance_type_code} instance from tier: #{tier_name}?")
|
1051
|
+
return 9
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
# ok, make api request
|
1055
|
+
app_template["config"]["tiers"] = tiers
|
1056
|
+
request_payload = {appTemplate: app_template}
|
1057
|
+
|
1058
|
+
if options[:dry_run]
|
1059
|
+
print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
|
1060
|
+
return
|
1061
|
+
end
|
1062
|
+
json_response = @app_templates_interface.update(app_template['id'], request_payload)
|
1063
|
+
|
1064
|
+
if options[:json]
|
1065
|
+
puts JSON.pretty_generate(json_response)
|
1066
|
+
else
|
1067
|
+
print_green_success "Removed instance from app template."
|
1068
|
+
get([app_template['id']])
|
1069
|
+
end
|
1070
|
+
return 0
|
1071
|
+
|
1072
|
+
rescue RestClient::Exception => e
|
1073
|
+
print_rest_exception(e, options)
|
1074
|
+
exit 1
|
1075
|
+
end
|
1076
|
+
end
|
1077
|
+
|
1078
|
+
def add_tier(args)
|
1079
|
+
options = {}
|
1080
|
+
boot_order = nil
|
1081
|
+
linked_tiers = nil
|
1082
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1083
|
+
opts.banner = subcommand_usage("[template] [tier]")
|
1084
|
+
opts.on('--name VALUE', String, "Tier Name") do |val|
|
1085
|
+
options[:name] = val
|
1086
|
+
end
|
1087
|
+
opts.on('--bootOrder NUMBER', String, "Boot Order" ) do |val|
|
1088
|
+
boot_order = val
|
1089
|
+
end
|
1090
|
+
opts.on('--linkedTiers x,y,z', Array, "Connected Tiers.") do |val|
|
1091
|
+
linked_tiers = val
|
1092
|
+
end
|
1093
|
+
build_common_options(opts, options, [:options, :json, :dry_run, :remote])
|
1094
|
+
end
|
1095
|
+
optparse.parse!(args)
|
1096
|
+
|
1097
|
+
if args.count < 1
|
1098
|
+
print_error Morpheus::Terminal.angry_prompt
|
1099
|
+
puts_error "#{command_name} add-tier requires argument: [template]\n#{optparse}"
|
1100
|
+
# puts optparse
|
1101
|
+
return 1
|
1102
|
+
end
|
1103
|
+
app_template_name = args[0]
|
1104
|
+
tier_name = args[1]
|
1105
|
+
|
1106
|
+
connect(options)
|
1107
|
+
|
1108
|
+
begin
|
1109
|
+
app_template = find_app_template_by_name_or_id(app_template_name)
|
1110
|
+
return 1 if app_template.nil?
|
1111
|
+
|
1112
|
+
app_template["config"] ||= {}
|
1113
|
+
app_template["config"]["tiers"] ||= {}
|
1114
|
+
tiers = app_template["config"]["tiers"]
|
1115
|
+
|
1116
|
+
# prompt new tier
|
1117
|
+
# Name
|
1118
|
+
# {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1, 'description' => 'A unique name for the app template.'},
|
1119
|
+
# {'fieldName' => 'bootOrder', 'fieldLabel' => 'Boot Order', 'type' => 'text', 'required' => false, 'displayOrder' => 2, 'description' => 'Boot Order'}
|
1120
|
+
if !tier_name
|
1121
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Tier Name', 'type' => 'text', 'required' => true, 'description' => 'Enter the name of the tier'}], options[:options])
|
1122
|
+
tier_name = v_prompt['name']
|
1123
|
+
end
|
1124
|
+
# case insensitive match
|
1125
|
+
existing_tier_names = tiers.keys
|
1126
|
+
matching_tier_name = existing_tier_names.find {|k| k.downcase == tier_name.downcase }
|
1127
|
+
if matching_tier_name
|
1128
|
+
# print_red_alert "Tier #{tier_name} already exists"
|
1129
|
+
# return 1
|
1130
|
+
print cyan,"Tier #{tier_name} already exists.",reset,"\n"
|
1131
|
+
return 0
|
1132
|
+
end
|
1133
|
+
# idempotent
|
1134
|
+
if !tiers[tier_name]
|
1135
|
+
tiers[tier_name] = {'instances' => []}
|
1136
|
+
end
|
1137
|
+
tier = tiers[tier_name]
|
1138
|
+
|
1139
|
+
# Boot Order
|
1140
|
+
if !boot_order
|
1141
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'bootOrder', 'fieldLabel' => 'Boot Order', 'type' => 'text', 'required' => false, 'description' => 'Sequence order for starting app instances by tier. 0-N', 'defaultValue' => tier['bootOrder']}], options[:options])
|
1142
|
+
boot_order = v_prompt['bootOrder']
|
1143
|
+
end
|
1144
|
+
if boot_order.to_s == 'null'
|
1145
|
+
tier.delete('bootOrder')
|
1146
|
+
elsif boot_order.to_s != ''
|
1147
|
+
tier['bootOrder'] = boot_order.to_i
|
1148
|
+
end
|
1149
|
+
|
1150
|
+
# Connected Tiers
|
1151
|
+
if !linked_tiers
|
1152
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'linkedTiers', 'fieldLabel' => 'Connected Tiers', 'type' => 'text', 'required' => false, 'description' => 'Names of connected tiers, comma separated', 'defaultValue' => (linked_tiers ? linked_tiers.join(',') : nil)}], options[:options])
|
1153
|
+
linked_tiers = v_prompt['linkedTiers'].to_s.split(',').collect {|it| it.strip }.select {|it| it != ''}
|
1154
|
+
end
|
1155
|
+
if linked_tiers && !linked_tiers.empty?
|
1156
|
+
linked_tiers.each do |other_tier_name|
|
1157
|
+
link_result = link_tiers(tiers, [tier_name, other_tier_name])
|
1158
|
+
# could just re-prompt unless options[:no_prompt]
|
1159
|
+
return 1 if !link_result
|
1160
|
+
end
|
1161
|
+
end
|
1162
|
+
|
1163
|
+
# ok, make api request
|
1164
|
+
app_template["config"]["tiers"] = tiers
|
1165
|
+
request_payload = app_template["config"]
|
1166
|
+
# request_payload = {appTemplate: app_template}
|
1167
|
+
|
1168
|
+
if options[:dry_run]
|
1169
|
+
print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
|
1170
|
+
return
|
1171
|
+
end
|
1172
|
+
json_response = @app_templates_interface.update(app_template['id'], request_payload)
|
1173
|
+
|
1174
|
+
if options[:json]
|
1175
|
+
puts JSON.pretty_generate(json_response)
|
1176
|
+
elsif !options[:quiet]
|
1177
|
+
print_green_success "Added tier #{tier_name}"
|
1178
|
+
# prompt for new instance
|
1179
|
+
if !options[:no_prompt]
|
1180
|
+
if ::Morpheus::Cli::OptionTypes::confirm("Would you like to add an instance now?", options.merge({default: true}))
|
1181
|
+
add_instance([app_template['id'], tier_name])
|
1182
|
+
while ::Morpheus::Cli::OptionTypes::confirm("Add another instance now?", options.merge({default: false})) do
|
1183
|
+
add_instance([app_template['id'], tier_name])
|
1184
|
+
end
|
1185
|
+
# if !add_instance_result
|
1186
|
+
# end
|
1187
|
+
end
|
1188
|
+
end
|
1189
|
+
# print details
|
1190
|
+
get([app_template['name']])
|
284
1191
|
end
|
285
|
-
|
1192
|
+
return 0
|
286
1193
|
rescue RestClient::Exception => e
|
287
1194
|
print_rest_exception(e, options)
|
288
1195
|
exit 1
|
289
1196
|
end
|
290
1197
|
end
|
291
1198
|
|
292
|
-
def
|
1199
|
+
def update_tier(args)
|
293
1200
|
options = {}
|
294
|
-
|
295
|
-
|
296
|
-
|
1201
|
+
new_tier_name = nil
|
1202
|
+
boot_order = nil
|
1203
|
+
linked_tiers = nil
|
1204
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1205
|
+
opts.banner = subcommand_usage("[template] [tier]")
|
1206
|
+
opts.on('--name VALUE', String, "Tier Name") do |val|
|
1207
|
+
new_tier_name = val
|
1208
|
+
end
|
1209
|
+
opts.on('--bootOrder NUMBER', String, "Boot Order" ) do |val|
|
1210
|
+
boot_order = val
|
1211
|
+
end
|
1212
|
+
opts.on('--linkedTiers x,y,z', Array, "Connected Tiers") do |val|
|
1213
|
+
linked_tiers = val
|
1214
|
+
end
|
1215
|
+
build_common_options(opts, options, [:options, :json, :dry_run, :remote])
|
297
1216
|
end
|
298
1217
|
optparse.parse!(args)
|
299
1218
|
|
300
|
-
if args.count
|
301
|
-
|
302
|
-
|
1219
|
+
if args.count != 2
|
1220
|
+
print_error Morpheus::Terminal.angry_prompt
|
1221
|
+
puts_error "#{command_name} update-tier expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1222
|
+
return 1
|
303
1223
|
end
|
1224
|
+
app_template_name = args[0]
|
1225
|
+
tier_name = args[1]
|
304
1226
|
|
305
1227
|
connect(options)
|
1228
|
+
|
306
1229
|
begin
|
307
|
-
app_template = find_app_template_by_name_or_id(
|
308
|
-
|
309
|
-
|
310
|
-
|
1230
|
+
app_template = find_app_template_by_name_or_id(app_template_name)
|
1231
|
+
return 1 if app_template.nil?
|
1232
|
+
|
1233
|
+
app_template["config"] ||= {}
|
1234
|
+
app_template["config"]["tiers"] ||= {}
|
1235
|
+
tiers = app_template["config"]["tiers"]
|
1236
|
+
|
1237
|
+
if !tiers[tier_name]
|
1238
|
+
print_red_alert "Tier not found by name #{tier_name}"
|
1239
|
+
return 1
|
1240
|
+
end
|
1241
|
+
tier = tiers[tier_name]
|
1242
|
+
|
1243
|
+
|
1244
|
+
if options[:no_prompt]
|
1245
|
+
if !(new_tier_name || boot_order || linked_tiers)
|
1246
|
+
print_error Morpheus::Terminal.angry_prompt
|
1247
|
+
puts_error "#{command_name} update-tier requires an option to update.\n#{optparse}"
|
1248
|
+
return 1
|
1249
|
+
end
|
1250
|
+
end
|
1251
|
+
|
1252
|
+
# prompt update tier
|
1253
|
+
# Name
|
1254
|
+
if !new_tier_name
|
1255
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Tier Name', 'type' => 'text', 'required' => true, 'description' => 'Rename the tier', 'defaultValue' => tier_name}], options[:options])
|
1256
|
+
new_tier_name = v_prompt['name']
|
1257
|
+
end
|
1258
|
+
if new_tier_name && new_tier_name != tier_name
|
1259
|
+
old_tier_name = tier_name
|
1260
|
+
if tiers[new_tier_name]
|
1261
|
+
print_red_alert "A tier named #{tier_name} already exists."
|
1262
|
+
return 1
|
1263
|
+
end
|
1264
|
+
tier = tiers.delete(tier_name)
|
1265
|
+
tiers[new_tier_name] = tier
|
1266
|
+
# Need to fix all the linkedTiers
|
1267
|
+
tiers.each do |k, v|
|
1268
|
+
if v['linkedTiers'] && v['linkedTiers'].include?(tier_name)
|
1269
|
+
v['linkedTiers'] = v['linkedTiers'].map {|it| it == tier_name ? new_tier_name : it }
|
1270
|
+
end
|
1271
|
+
end
|
1272
|
+
# old_tier_name = tier_name
|
1273
|
+
tier_name = new_tier_name
|
311
1274
|
end
|
1275
|
+
|
1276
|
+
# Boot Order
|
1277
|
+
if !boot_order
|
1278
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'bootOrder', 'fieldLabel' => 'Boot Order', 'type' => 'text', 'required' => false, 'description' => 'Sequence order for starting app instances by tier. 0-N', 'defaultValue' => tier['bootOrder']}], options[:options])
|
1279
|
+
boot_order = v_prompt['bootOrder']
|
1280
|
+
end
|
1281
|
+
if boot_order.to_s == 'null'
|
1282
|
+
tier.delete('bootOrder')
|
1283
|
+
elsif boot_order.to_s != ''
|
1284
|
+
tier['bootOrder'] = boot_order.to_i
|
1285
|
+
end
|
1286
|
+
|
1287
|
+
# Connected Tiers
|
1288
|
+
if !linked_tiers
|
1289
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'linkedTiers', 'fieldLabel' => 'Connected Tiers', 'type' => 'text', 'required' => false, 'description' => 'Names of connected tiers, comma separated', 'defaultValue' => (tier['linkedTiers'] ? tier['linkedTiers'].join(',') : nil)}], options[:options])
|
1290
|
+
linked_tiers = v_prompt['linkedTiers'].to_s.split(',').collect {|it| it.strip }.select {|it| it != ''}
|
1291
|
+
end
|
1292
|
+
current_linked_tiers = tier['linkedTiers'] || []
|
1293
|
+
if linked_tiers && linked_tiers != current_linked_tiers
|
1294
|
+
remove_tiers = current_linked_tiers - linked_tiers
|
1295
|
+
remove_tiers.each do |other_tier_name|
|
1296
|
+
unlink_result = unlink_tiers(tiers, [tier_name, other_tier_name])
|
1297
|
+
# could just re-prompt unless options[:no_prompt]
|
1298
|
+
return 1 if !unlink_result
|
1299
|
+
end
|
1300
|
+
add_tiers = linked_tiers - current_linked_tiers
|
1301
|
+
add_tiers.each do |other_tier_name|
|
1302
|
+
link_result = link_tiers(tiers, [tier_name, other_tier_name])
|
1303
|
+
# could just re-prompt unless options[:no_prompt]
|
1304
|
+
return 1 if !link_result
|
1305
|
+
end
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
# ok, make api request
|
1309
|
+
app_template["config"]["tiers"] = tiers
|
1310
|
+
request_payload = app_template["config"]
|
1311
|
+
# request_payload = {appTemplate: app_template}
|
1312
|
+
|
312
1313
|
if options[:dry_run]
|
313
|
-
print_dry_run @app_templates_interface.dry.
|
1314
|
+
print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
|
314
1315
|
return
|
315
1316
|
end
|
316
|
-
json_response = @app_templates_interface.
|
1317
|
+
json_response = @app_templates_interface.update(app_template['id'], request_payload)
|
317
1318
|
|
318
1319
|
if options[:json]
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
1320
|
+
puts JSON.pretty_generate(json_response)
|
1321
|
+
elsif !options[:quiet]
|
1322
|
+
print_green_success "Updated tier #{tier_name}"
|
1323
|
+
get([app_template['id']])
|
323
1324
|
end
|
324
|
-
|
1325
|
+
return 0
|
325
1326
|
rescue RestClient::Exception => e
|
326
1327
|
print_rest_exception(e, options)
|
327
1328
|
exit 1
|
328
1329
|
end
|
329
1330
|
end
|
330
1331
|
|
331
|
-
def
|
1332
|
+
def remove_tier(args)
|
332
1333
|
options = {}
|
333
|
-
optparse = OptionParser.new do|opts|
|
334
|
-
opts.banner = subcommand_usage("[
|
335
|
-
|
336
|
-
# options[:group] = val
|
337
|
-
# end
|
338
|
-
# opts.on( '-c', '--cloud CLOUD', "Cloud" ) do |val|
|
339
|
-
# options[:cloud] = val
|
340
|
-
# end
|
341
|
-
build_common_options(opts, options, [:json])
|
1334
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1335
|
+
opts.banner = subcommand_usage("[template] [tier]")
|
1336
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run])
|
342
1337
|
end
|
343
1338
|
optparse.parse!(args)
|
344
1339
|
|
345
|
-
if args.count <
|
346
|
-
|
347
|
-
|
1340
|
+
if args.count < 2
|
1341
|
+
print_error Morpheus::Terminal.angry_prompt
|
1342
|
+
puts_error "#{command_name} remove-tier expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1343
|
+
return 1
|
348
1344
|
end
|
349
|
-
|
350
|
-
connect(options)
|
351
|
-
|
352
1345
|
app_template_name = args[0]
|
353
1346
|
tier_name = args[1]
|
354
|
-
instance_type_code = args[2]
|
355
1347
|
|
356
|
-
|
357
|
-
exit 1 if app_template.nil?
|
358
|
-
|
359
|
-
instance_type = find_instance_type_by_code(instance_type_code)
|
360
|
-
if instance_type.nil?
|
361
|
-
exit 1
|
362
|
-
end
|
363
|
-
|
364
|
-
group_id = app_template['config']['siteId']
|
365
|
-
|
366
|
-
if group_id.nil?
|
367
|
-
#puts "Group not found or specified! \n #{optparse}"
|
368
|
-
print_red_alert("Group not found or specified for this template!")
|
369
|
-
puts "#{optparse}"
|
370
|
-
exit 1
|
371
|
-
end
|
372
|
-
|
373
|
-
cloud_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'cloud', 'type' => 'select', 'fieldLabel' => 'Cloud', 'optionSource' => 'clouds', 'required' => true, 'description' => 'Select Cloud.'}],options[:options],@api_client,{groupId: group_id})
|
374
|
-
cloud_id = cloud_prompt['cloud']
|
375
|
-
|
376
|
-
|
377
|
-
instance_option_types = [{'fieldName' => 'name', 'fieldLabel' => 'Instance Name', 'type' => 'text'}]
|
378
|
-
instance_option_values = Morpheus::Cli::OptionTypes.prompt(instance_option_types, options[:options], @api_client, options[:params])
|
379
|
-
instance_name = instance_option_values['name'] || ''
|
380
|
-
|
381
|
-
# copied from instances command, this payload isn't used
|
382
|
-
payload = {
|
383
|
-
:servicePlan => nil,
|
384
|
-
:zoneId => cloud_id,
|
385
|
-
:instance => {
|
386
|
-
:name => instance_name,
|
387
|
-
:site => {
|
388
|
-
:id => group_id
|
389
|
-
},
|
390
|
-
:instanceType => {
|
391
|
-
:code => instance_type_code
|
392
|
-
}
|
393
|
-
}
|
394
|
-
}
|
395
|
-
|
396
|
-
version_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'version', 'type' => 'select', 'fieldLabel' => 'Version', 'optionSource' => 'instanceVersions', 'required' => true, 'skipSingleOption' => true, 'description' => 'Select which version of the instance type to be provisioned.'}],options[:options],@api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id']})
|
397
|
-
layout_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'layout', 'type' => 'select', 'fieldLabel' => 'Layout', 'optionSource' => 'layoutsForCloud', 'required' => true, 'description' => 'Select which configuration of the instance type to be provisioned.'}],options[:options],@api_client,{groupId: group_id, cloudId: cloud_id, instanceTypeId: instance_type['id'], version: version_prompt['version']})
|
398
|
-
layout_id = layout_prompt['layout']
|
399
|
-
layout = instance_type['instanceTypeLayouts'].find{ |lt| lt['id'].to_i == layout_id.to_i}
|
400
|
-
payload[:instance][:layout] = {id: layout['id']}
|
401
|
-
|
402
|
-
# prompt for service plan
|
403
|
-
service_plans_json = @instances_interface.service_plans({zoneId: cloud_id, layoutId: layout_id})
|
404
|
-
service_plans = service_plans_json["plans"]
|
405
|
-
service_plans_dropdown = service_plans.collect {|sp| {'name' => sp["name"], 'value' => sp["id"]} } # already sorted
|
406
|
-
plan_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'servicePlan', 'type' => 'select', 'fieldLabel' => 'Plan', 'selectOptions' => service_plans_dropdown, 'required' => true, 'description' => 'Choose the appropriately sized plan for this instance'}],options[:options])
|
407
|
-
service_plan = service_plans.find {|sp| sp["id"] == plan_prompt['servicePlan'].to_i }
|
408
|
-
payload[:servicePlan] = service_plan["id"]
|
409
|
-
|
410
|
-
|
411
|
-
type_payload = {}
|
412
|
-
if !layout['optionTypes'].nil? && !layout['optionTypes'].empty?
|
413
|
-
type_payload = Morpheus::Cli::OptionTypes.prompt(layout['optionTypes'],options[:options],@api_client,{groupId: group_id, cloudId: cloud_id, zoneId: cloud_id, instanceTypeId: instance_type['id'], version: version_prompt['version']})
|
414
|
-
elsif !instance_type['optionTypes'].nil? && !instance_type['optionTypes'].empty?
|
415
|
-
type_payload = Morpheus::Cli::OptionTypes.prompt(instance_type['optionTypes'],options[:options],@api_client,{groupId: group_id, cloudId: cloud_id, zoneId: cloud_id, instanceTypeId: instance_type['id'], version: version_prompt['version']})
|
416
|
-
end
|
417
|
-
if !type_payload['config'].nil?
|
418
|
-
payload.merge!(type_payload['config'])
|
419
|
-
end
|
420
|
-
|
421
|
-
provision_payload = {}
|
422
|
-
if !layout['provisionType'].nil? && !layout['provisionType']['optionTypes'].nil? && !layout['provisionType']['optionTypes'].empty?
|
423
|
-
puts "Checking for option Types"
|
424
|
-
provision_payload = Morpheus::Cli::OptionTypes.prompt(layout['provisionType']['optionTypes'],options[:options],@api_client,{groupId: group_id, cloudId: cloud_id, zoneId: cloud_id, instanceTypeId: instance_type['id'], version: version_prompt['version']})
|
425
|
-
end
|
426
|
-
|
427
|
-
if !provision_payload.nil? && !provision_payload['config'].nil?
|
428
|
-
payload.merge!(provision_payload['config'])
|
429
|
-
end
|
430
|
-
# if !provision_payload.nil? && !provision_payload['server'].nil?
|
431
|
-
# payload[:server] = provision_payload['server']
|
432
|
-
# end
|
433
|
-
|
434
|
-
|
435
|
-
config = app_template['config']['tierView']
|
436
|
-
|
437
|
-
config['nodes'] ||= []
|
438
|
-
|
439
|
-
tier = config['nodes'].find {|node|
|
440
|
-
node["data"] && node["data"]["type"] == "tier" && node["data"]["id"] == "newtier-#{tier_name}"
|
441
|
-
}
|
442
|
-
if !tier
|
443
|
-
tier = {
|
444
|
-
"classes"=>"tier newtier-#{tier_name}",
|
445
|
-
"data"=>{"id"=>"newtier-#{tier_name}", "name"=> tier_name, "type"=>"tier"},
|
446
|
-
"grabbable"=>true, "group"=>"nodes","locked"=>false,
|
447
|
-
#"position"=>{"x"=>-2.5, "y"=>-45},
|
448
|
-
"removed"=>false, "selectable"=>true, "selected"=>false
|
449
|
-
}
|
450
|
-
config['nodes'] << tier
|
451
|
-
end
|
452
|
-
|
453
|
-
instance_id = generate_id()
|
454
|
-
|
455
|
-
instance_type_node = {
|
456
|
-
"classes"=>"instance newinstance-#{instance_id} #{instance_type['code']}",
|
457
|
-
"data"=>{
|
458
|
-
"controlName" => "instance.layout.id",
|
459
|
-
"id"=>"newinstance-#{instance_id}",
|
460
|
-
"nodeId"=>["newinstance-#{instance_id}"], # not sure what this is for..
|
461
|
-
"index"=>nil,
|
462
|
-
"instance.layout.id"=>layout_id.to_s,
|
463
|
-
"instance.name"=>instance_name,
|
464
|
-
"instanceType"=>instance_type['code'],
|
465
|
-
"isPlaced"=>true,
|
466
|
-
"name"=> instance_name,
|
467
|
-
"parent"=>tier['data']['id'],
|
468
|
-
"type"=>"instance",
|
469
|
-
"typeName"=>instance_type['name'],
|
470
|
-
"servicePlan"=>plan_prompt['servicePlan'].to_s,
|
471
|
-
# "servicePlanOptions.maxCpu": "",
|
472
|
-
# "servicePlanOptions.maxCpuId": nil,
|
473
|
-
# "servicePlanOptions.maxMemory": "",
|
474
|
-
# "servicePlanOptions.maxMemoryId": nil,
|
475
|
-
|
476
|
-
# "volumes.datastoreId": nil,
|
477
|
-
# "volumes.name": "root",
|
478
|
-
# "volumes.rootVolume": "true",
|
479
|
-
# "volumes.size": "5",
|
480
|
-
# "volumes.sizeId": "5",
|
481
|
-
# "volumes.storageType": nil,
|
482
|
-
|
483
|
-
"version"=>version_prompt['version'],
|
484
|
-
"siteId"=>group_id.to_s,
|
485
|
-
"zoneId"=>cloud_id.to_s
|
486
|
-
},
|
487
|
-
"grabbable"=>true, "group"=>"nodes", "locked"=>false,
|
488
|
-
#"position"=>{"x"=>-79.83254449505226, "y"=>458.33806818181824},
|
489
|
-
"removed"=>false, "selectable"=>true, "selected"=>false
|
490
|
-
}
|
491
|
-
|
492
|
-
if !type_payload['config'].nil?
|
493
|
-
instance_type_node['data'].merge!(type_payload['config'])
|
494
|
-
end
|
1348
|
+
connect(options)
|
495
1349
|
|
496
|
-
|
497
|
-
|
498
|
-
|
1350
|
+
begin
|
1351
|
+
app_template = find_app_template_by_name_or_id(app_template_name)
|
1352
|
+
return 1 if app_template.nil?
|
499
1353
|
|
500
|
-
|
1354
|
+
app_template["config"] ||= {}
|
1355
|
+
app_template["config"]["tiers"] ||= {}
|
1356
|
+
tiers = app_template["config"]["tiers"]
|
501
1357
|
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
1358
|
+
if !tiers[tier_name]
|
1359
|
+
# print_red_alert "Tier not found by name #{tier_name}"
|
1360
|
+
# return 1
|
1361
|
+
print cyan,"Tier #{tier_name} does not exist.",reset,"\n"
|
1362
|
+
return 0
|
1363
|
+
end
|
507
1364
|
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
request_payload['config'] = config
|
1365
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the tier #{tier_name}?")
|
1366
|
+
exit
|
1367
|
+
end
|
512
1368
|
|
513
|
-
|
1369
|
+
# remove it
|
1370
|
+
tiers.delete(tier_name)
|
1371
|
+
|
1372
|
+
# ok, make api request
|
1373
|
+
app_template["config"]["tiers"] = tiers
|
1374
|
+
request_payload = app_template["config"]
|
1375
|
+
# request_payload = {appTemplate: app_template}
|
1376
|
+
|
514
1377
|
if options[:dry_run]
|
515
1378
|
print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
|
516
1379
|
return
|
517
1380
|
end
|
1381
|
+
|
518
1382
|
json_response = @app_templates_interface.update(app_template['id'], request_payload)
|
519
1383
|
|
1384
|
+
|
520
1385
|
if options[:json]
|
521
1386
|
print JSON.pretty_generate(json_response)
|
522
1387
|
print "\n"
|
523
1388
|
else
|
524
|
-
print_green_success "
|
1389
|
+
print_green_success "Removed tier #{tier_name}"
|
525
1390
|
get([app_template['name']])
|
526
1391
|
end
|
527
1392
|
|
@@ -529,73 +1394,89 @@ class Morpheus::Cli::AppTemplates
|
|
529
1394
|
print_rest_exception(e, options)
|
530
1395
|
exit 1
|
531
1396
|
end
|
532
|
-
|
533
1397
|
end
|
534
1398
|
|
535
|
-
def
|
1399
|
+
def connect_tiers(args)
|
536
1400
|
options = {}
|
537
|
-
optparse = OptionParser.new do|opts|
|
538
|
-
opts.banner = subcommand_usage("[
|
539
|
-
build_common_options(opts, options, [:
|
1401
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1402
|
+
opts.banner = subcommand_usage("[template] [Tier1] [Tier2]")
|
1403
|
+
build_common_options(opts, options, [:json, :dry_run])
|
540
1404
|
end
|
541
1405
|
optparse.parse!(args)
|
542
1406
|
|
543
|
-
if args.count <
|
544
|
-
|
545
|
-
|
1407
|
+
if args.count < 3
|
1408
|
+
print_error Morpheus::Terminal.angry_prompt
|
1409
|
+
puts_error "#{command_name} connect-tiers expects 3 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1410
|
+
# puts optparse
|
1411
|
+
return 1
|
546
1412
|
end
|
547
1413
|
app_template_name = args[0]
|
548
|
-
|
1414
|
+
tier1_name = args[1]
|
1415
|
+
tier2_name = args[2]
|
549
1416
|
|
550
1417
|
connect(options)
|
551
1418
|
|
552
1419
|
begin
|
553
|
-
|
554
1420
|
app_template = find_app_template_by_name_or_id(app_template_name)
|
555
|
-
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
1421
|
+
return 1 if app_template.nil?
|
1422
|
+
|
1423
|
+
app_template["config"] ||= {}
|
1424
|
+
tiers = app_template["config"]["tiers"]
|
1425
|
+
|
1426
|
+
if !tiers || tiers.keys.size == 0
|
1427
|
+
error_msg = "App Template #{app_template['name']} has no tiers."
|
1428
|
+
# print_red_alert "App Template #{app_template['name']} has no tiers."
|
1429
|
+
# raise_command_error "App Template #{app_template['name']} has no tiers."
|
1430
|
+
print_error Morpheus::Terminal.angry_prompt
|
1431
|
+
puts_error "App Template #{app_template['name']} has no tiers."
|
1432
|
+
return 1
|
1433
|
+
end
|
560
1434
|
|
561
|
-
|
562
|
-
|
563
|
-
|
1435
|
+
connect_tiers = []
|
1436
|
+
tier1 = tiers[tier1_name]
|
1437
|
+
tier2 = tiers[tier2_name]
|
1438
|
+
# uhh support N args
|
564
1439
|
|
565
|
-
if
|
566
|
-
print_red_alert "
|
567
|
-
|
1440
|
+
if tier1.nil?
|
1441
|
+
print_red_alert "Tier not found by name #{tier1_name}!"
|
1442
|
+
return 1
|
568
1443
|
end
|
569
1444
|
|
570
|
-
|
571
|
-
|
1445
|
+
if tier2.nil?
|
1446
|
+
print_red_alert "Tier not found by name #{tier2_name}!"
|
1447
|
+
return 1
|
572
1448
|
end
|
573
1449
|
|
574
|
-
|
575
|
-
|
576
|
-
}
|
1450
|
+
tier1["linkedTiers"] = tier1["linkedTiers"] || []
|
1451
|
+
tier2["linkedTiers"] = tier2["linkedTiers"] || []
|
577
1452
|
|
578
|
-
|
579
|
-
|
580
|
-
|
1453
|
+
found_edge = tier1["linkedTiers"].include?(tier2_name) || tier2["linkedTiers"].include?(tier1_name)
|
1454
|
+
|
1455
|
+
if found_edge
|
1456
|
+
puts cyan,"Tiers #{tier1_name} and #{tier2_name} are already connected.",reset
|
1457
|
+
return 0
|
581
1458
|
end
|
582
1459
|
|
583
|
-
#
|
584
|
-
|
585
|
-
node["data"] && node["data"]["type"] == "instance" && node["data"]["id"] == "newinstance-#{instance_id}"
|
586
|
-
}
|
1460
|
+
# ok to be connect the tiers
|
1461
|
+
# note: the ui doesn't hook up both sides eh?
|
587
1462
|
|
1463
|
+
if !tier1["linkedTiers"].include?(tier2_name)
|
1464
|
+
tier1["linkedTiers"].push(tier2_name)
|
1465
|
+
end
|
588
1466
|
|
589
|
-
|
590
|
-
|
591
|
-
tier_instances.each_with_index do |node, idx|
|
592
|
-
node['data']['index'] = idx
|
1467
|
+
if !tier2["linkedTiers"].include?(tier1_name)
|
1468
|
+
tier2["linkedTiers"].push(tier1_name)
|
593
1469
|
end
|
594
1470
|
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
request_payload
|
1471
|
+
# ok, make api request
|
1472
|
+
app_template["config"]["tiers"] = tiers
|
1473
|
+
request_payload = app_template["config"]
|
1474
|
+
# request_payload = {appTemplate: app_template}
|
1475
|
+
|
1476
|
+
if options[:dry_run]
|
1477
|
+
print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
|
1478
|
+
return
|
1479
|
+
end
|
599
1480
|
json_response = @app_templates_interface.update(app_template['id'], request_payload)
|
600
1481
|
|
601
1482
|
|
@@ -603,7 +1484,7 @@ class Morpheus::Cli::AppTemplates
|
|
603
1484
|
print JSON.pretty_generate(json_response)
|
604
1485
|
print "\n"
|
605
1486
|
else
|
606
|
-
print_green_success "
|
1487
|
+
print_green_success "Connected 2 tiers for app template #{app_template['name']}"
|
607
1488
|
get([app_template['name']])
|
608
1489
|
end
|
609
1490
|
|
@@ -613,17 +1494,19 @@ class Morpheus::Cli::AppTemplates
|
|
613
1494
|
end
|
614
1495
|
end
|
615
1496
|
|
616
|
-
def
|
1497
|
+
def disconnect_tiers(args)
|
617
1498
|
options = {}
|
618
|
-
optparse = OptionParser.new do|opts|
|
619
|
-
opts.banner = subcommand_usage("[
|
1499
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1500
|
+
opts.banner = subcommand_usage("[template] [Tier1] [Tier2]")
|
620
1501
|
build_common_options(opts, options, [:json, :dry_run])
|
621
1502
|
end
|
622
1503
|
optparse.parse!(args)
|
623
1504
|
|
624
1505
|
if args.count < 3
|
625
|
-
|
626
|
-
|
1506
|
+
print_error Morpheus::Terminal.angry_prompt
|
1507
|
+
puts_error "#{command_name} disconnect-tiers expects 3 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1508
|
+
# puts optparse
|
1509
|
+
return 1
|
627
1510
|
end
|
628
1511
|
app_template_name = args[0]
|
629
1512
|
tier1_name = args[1]
|
@@ -632,62 +1515,54 @@ class Morpheus::Cli::AppTemplates
|
|
632
1515
|
connect(options)
|
633
1516
|
|
634
1517
|
begin
|
635
|
-
|
636
1518
|
app_template = find_app_template_by_name_or_id(app_template_name)
|
637
|
-
|
1519
|
+
return 1 if app_template.nil?
|
638
1520
|
|
639
|
-
|
1521
|
+
app_template["config"] ||= {}
|
1522
|
+
tiers = app_template["config"]["tiers"]
|
640
1523
|
|
641
|
-
|
1524
|
+
if !tiers || tiers.keys.size == 0
|
1525
|
+
# print_red_alert "App Template #{app_template['name']} has no tiers."
|
1526
|
+
# raise_command_error "App Template #{app_template['name']} has no tiers."
|
1527
|
+
print_error Morpheus::Terminal.angry_prompt
|
1528
|
+
puts_error "App Template #{app_template['name']} has no tiers."
|
1529
|
+
return 1
|
1530
|
+
end
|
1531
|
+
|
1532
|
+
connect_tiers = []
|
1533
|
+
tier1 = tiers[tier1_name]
|
1534
|
+
tier2 = tiers[tier2_name]
|
1535
|
+
# uhh support N args
|
642
1536
|
|
643
|
-
tier1 = config['nodes'].find {|node|
|
644
|
-
node["data"] && node["data"]["type"] == "tier" && node["data"]["id"] == "newtier-#{tier1_name}"
|
645
|
-
}
|
646
1537
|
if tier1.nil?
|
647
1538
|
print_red_alert "Tier not found by name #{tier1_name}!"
|
648
|
-
|
1539
|
+
return 1
|
649
1540
|
end
|
650
1541
|
|
651
|
-
tier2 = config['nodes'].find {|node|
|
652
|
-
node["data"] && node["data"]["type"] == "tier" && node["data"]["id"] == "newtier-#{tier2_name}"
|
653
|
-
}
|
654
1542
|
if tier2.nil?
|
655
1543
|
print_red_alert "Tier not found by name #{tier2_name}!"
|
656
|
-
|
1544
|
+
return 1
|
657
1545
|
end
|
658
1546
|
|
659
|
-
|
1547
|
+
tier1["linkedTiers"] = tier1["linkedTiers"] || []
|
1548
|
+
tier2["linkedTiers"] = tier2["linkedTiers"] || []
|
660
1549
|
|
661
|
-
found_edge =
|
662
|
-
(edge['data']['source'] == "newtier-#{tier1_name}" && edge['data']['target'] == "newtier-#{tier2_name}") &&
|
663
|
-
(edge['data']['target'] == "newtier-#{tier2_name}" && edge['data']['source'] == "newtier-#{tier1_name}")
|
664
|
-
}
|
1550
|
+
found_edge = tier1["linkedTiers"].include?(tier2_name) || tier2["linkedTiers"].include?(tier1_name)
|
665
1551
|
|
666
1552
|
if found_edge
|
667
|
-
puts
|
668
|
-
|
1553
|
+
puts cyan,"Tiers #{tier1_name} and #{tier2_name} are not connected.",reset
|
1554
|
+
return 0
|
669
1555
|
end
|
670
1556
|
|
671
|
-
#
|
672
|
-
|
673
|
-
|
674
|
-
}
|
675
|
-
new_edge = {
|
676
|
-
"classes"=>"",
|
677
|
-
"data"=>{"id"=>"ele#{new_edge_index}", "source"=>tier1['data']['id'], "target"=>tier2['data']['id']},
|
678
|
-
"grabbable"=>true, "group"=>"edges", "locked"=>false,
|
679
|
-
#"position"=>{},
|
680
|
-
"removed"=>false, "selectable"=>true, "selected"=>false
|
681
|
-
}
|
682
|
-
|
683
|
-
config['edges'].push(new_edge)
|
684
|
-
|
685
|
-
|
686
|
-
request_payload = {appTemplate: {} }
|
687
|
-
request_payload['siteId'] = app_template['config']['siteId']
|
688
|
-
# request_payload['config'] = config['tierView']
|
689
|
-
request_payload['config'] = config
|
1557
|
+
# remove links
|
1558
|
+
tier1["linkedTiers"] = tier1["linkedTiers"].reject {|it| it == tier2_name }
|
1559
|
+
tier2["linkedTiers"] = tier2["linkedTiers"].reject {|it| it == tier1_name }
|
690
1560
|
|
1561
|
+
# ok, make api request
|
1562
|
+
app_template["config"]["tiers"] = tiers
|
1563
|
+
request_payload = app_template["config"]
|
1564
|
+
# request_payload = {appTemplate: app_template}
|
1565
|
+
|
691
1566
|
if options[:dry_run]
|
692
1567
|
print_dry_run @app_templates_interface.dry.update(app_template['id'], request_payload)
|
693
1568
|
return
|
@@ -699,7 +1574,7 @@ class Morpheus::Cli::AppTemplates
|
|
699
1574
|
print JSON.pretty_generate(json_response)
|
700
1575
|
print "\n"
|
701
1576
|
else
|
702
|
-
print_green_success "Connected tiers for app template #{app_template['name']}"
|
1577
|
+
print_green_success "Connected 2 tiers for app template #{app_template['name']}"
|
703
1578
|
get([app_template['name']])
|
704
1579
|
end
|
705
1580
|
|
@@ -711,7 +1586,7 @@ class Morpheus::Cli::AppTemplates
|
|
711
1586
|
|
712
1587
|
def available_tiers(args)
|
713
1588
|
options = {}
|
714
|
-
optparse = OptionParser.new do|opts|
|
1589
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
715
1590
|
opts.banner = subcommand_usage()
|
716
1591
|
build_common_options(opts, options, [:json, :dry_run])
|
717
1592
|
end
|
@@ -725,72 +1600,30 @@ class Morpheus::Cli::AppTemplates
|
|
725
1600
|
return
|
726
1601
|
end
|
727
1602
|
json_response = @app_templates_interface.list_tiers(params)
|
728
|
-
tiers = json_response[
|
1603
|
+
tiers = json_response["tiers"] # just a list of names
|
729
1604
|
if options[:json]
|
730
|
-
|
731
|
-
print "\n"
|
1605
|
+
puts JSON.pretty_generate(json_response)
|
732
1606
|
else
|
733
1607
|
print_h1 "Available Tiers"
|
734
1608
|
if tiers.empty?
|
735
1609
|
print yellow,"No tiers found.",reset,"\n"
|
736
1610
|
else
|
737
|
-
rows = tiers.collect do |tier|
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
end
|
1611
|
+
# rows = tiers.collect do |tier|
|
1612
|
+
# {
|
1613
|
+
# id: tier['id'],
|
1614
|
+
# name: tier['name'],
|
1615
|
+
# }
|
1616
|
+
# end
|
1617
|
+
# print cyan
|
1618
|
+
# tp rows, [:name]
|
743
1619
|
print cyan
|
744
|
-
|
745
|
-
|
746
|
-
end
|
747
|
-
print reset,"\n"
|
748
|
-
end
|
749
|
-
rescue RestClient::Exception => e
|
750
|
-
print_rest_exception(e, options)
|
751
|
-
exit 1
|
752
|
-
end
|
753
|
-
|
754
|
-
end
|
755
|
-
|
756
|
-
def available_types(args)
|
757
|
-
options = {}
|
758
|
-
optparse = OptionParser.new do|opts|
|
759
|
-
opts.banner = subcommand_usage()
|
760
|
-
build_common_options(opts, options, [:json])
|
761
|
-
end
|
762
|
-
optparse.parse!(args)
|
763
|
-
connect(options)
|
764
|
-
params = {}
|
765
|
-
|
766
|
-
begin
|
767
|
-
if options[:dry_run]
|
768
|
-
print_dry_run @app_templates_interface.dry.list_types(params)
|
769
|
-
return
|
770
|
-
end
|
771
|
-
json_response = @app_templates_interface.list_types(params)
|
772
|
-
instance_types = json_response['types']
|
773
|
-
if options[:json]
|
774
|
-
print JSON.pretty_generate(json_response)
|
775
|
-
print "\n"
|
776
|
-
else
|
777
|
-
print_h1 "Available Instance Types"
|
778
|
-
if instance_types.empty?
|
779
|
-
print yellow,"No instance types found.",reset,"\n"
|
780
|
-
else
|
781
|
-
rows = instance_types.collect do |instance_type|
|
782
|
-
{
|
783
|
-
id: instance_type['id'],
|
784
|
-
code: instance_type['code'],
|
785
|
-
name: instance_type['name'],
|
786
|
-
}
|
1620
|
+
tiers.each do |tier_name|
|
1621
|
+
puts tier_name
|
787
1622
|
end
|
788
|
-
print cyan
|
789
|
-
tp rows, [:code, :name]
|
790
|
-
print reset
|
791
1623
|
end
|
792
1624
|
print reset,"\n"
|
793
1625
|
end
|
1626
|
+
return 0
|
794
1627
|
rescue RestClient::Exception => e
|
795
1628
|
print_rest_exception(e, options)
|
796
1629
|
exit 1
|
@@ -804,7 +1637,9 @@ class Morpheus::Cli::AppTemplates
|
|
804
1637
|
def add_app_template_option_types(connected=true)
|
805
1638
|
[
|
806
1639
|
{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 1},
|
807
|
-
{'fieldName' => '
|
1640
|
+
{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
|
1641
|
+
{'fieldName' => 'category', 'fieldLabel' => 'Category', 'type' => 'text', 'required' => false, 'displayOrder' => 3},
|
1642
|
+
#{'fieldName' => 'group', 'fieldLabel' => 'Group', 'type' => 'select', 'selectOptions' => (connected ? get_available_groups() : []), 'required' => true}
|
808
1643
|
]
|
809
1644
|
end
|
810
1645
|
|
@@ -874,25 +1709,38 @@ class Morpheus::Cli::AppTemplates
|
|
874
1709
|
def print_app_templates_table(app_templates, opts={})
|
875
1710
|
table_color = opts[:color] || cyan
|
876
1711
|
rows = app_templates.collect do |app_template|
|
877
|
-
instance_type_names = (app_template['instanceTypes'] || []).collect {|it| it['name'] }.join(', ')
|
1712
|
+
#instance_type_names = (app_template['instanceTypes'] || []).collect {|it| it['name'] }.join(', ')
|
1713
|
+
instance_type_names = []
|
1714
|
+
# if app_template['config'] && app_template['config']["tiers"]
|
1715
|
+
# app_template['config']["tiers"]
|
1716
|
+
# end
|
878
1717
|
{
|
879
1718
|
id: app_template['id'],
|
880
1719
|
name: app_template['name'],
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
#dateCreated: format_local_dt(app_template['dateCreated'])
|
1720
|
+
description: app_template['description'],
|
1721
|
+
category: app_template['category'],
|
1722
|
+
tiers_summary: format_app_template_tiers_summary(app_template)
|
885
1723
|
}
|
886
1724
|
end
|
887
1725
|
|
888
|
-
|
889
|
-
|
1726
|
+
term_width = current_terminal_width()
|
1727
|
+
tiers_col_width = 60
|
1728
|
+
if term_width > 190
|
1729
|
+
tiers_col_width += 130
|
1730
|
+
end
|
1731
|
+
columns = [
|
890
1732
|
:id,
|
891
1733
|
:name,
|
892
|
-
|
893
|
-
:
|
894
|
-
|
1734
|
+
:description,
|
1735
|
+
:category,
|
1736
|
+
{:tiers_summary => {:display_name => "TIERS", :max_width => tiers_col_width} }
|
895
1737
|
]
|
1738
|
+
# # custom pretty table columns ...
|
1739
|
+
# if options[:include_fields]
|
1740
|
+
# columns = options[:include_fields]
|
1741
|
+
# end
|
1742
|
+
print table_color
|
1743
|
+
print as_pretty_table(rows, columns, opts)
|
896
1744
|
print reset
|
897
1745
|
end
|
898
1746
|
|
@@ -902,4 +1750,265 @@ class Morpheus::Cli::AppTemplates
|
|
902
1750
|
id
|
903
1751
|
end
|
904
1752
|
|
1753
|
+
def format_app_template_tiers_summary(app_template)
|
1754
|
+
str = ""
|
1755
|
+
if app_template["config"] && app_template["config"]["tiers"]
|
1756
|
+
tier_descriptions = app_template["config"]["tiers"].collect do |tier_name, tier_config|
|
1757
|
+
# maybe do Tier Name (instance, instance2)
|
1758
|
+
instance_blurbs = []
|
1759
|
+
if tier_config["instances"]
|
1760
|
+
tier_config["instances"].each do |instance_config|
|
1761
|
+
if instance_config["instance"] && instance_config["instance"]["type"]
|
1762
|
+
# only have type: code in the config, rather not name fetch remotely right now..
|
1763
|
+
# instance_blurbs << instance_config["instance"]["type"]
|
1764
|
+
instance_name = instance_config["instance"]["name"] || ""
|
1765
|
+
instance_type_code = instance_config["instance"]["type"]
|
1766
|
+
instances_str = "#{green}#{instance_type_code}#{cyan}"
|
1767
|
+
if instance_name.to_s != ""
|
1768
|
+
instances_str << "#{green} - #{instance_name}#{cyan}"
|
1769
|
+
end
|
1770
|
+
begin
|
1771
|
+
config_list = parse_scoped_instance_configs(instance_config)
|
1772
|
+
if config_list.size == 0
|
1773
|
+
instances_str << " (No configs)"
|
1774
|
+
elsif config_list.size == 1
|
1775
|
+
# configs_str = config_list.collect {|it|
|
1776
|
+
# str = ""
|
1777
|
+
# it[:scope].to_s.inspect
|
1778
|
+
# }.join(", ")
|
1779
|
+
the_config = config_list[0]
|
1780
|
+
scope_str = the_config[:scope].collect {|k,v| v.to_s }.join("/")
|
1781
|
+
instances_str << " (#{scope_str})"
|
1782
|
+
else
|
1783
|
+
instances_str << " (#{config_list.size} configs)"
|
1784
|
+
end
|
1785
|
+
rescue => err
|
1786
|
+
puts_error "Failed to parse instance scoped instance configs: #{err.class} #{err.message}"
|
1787
|
+
raise err
|
1788
|
+
end
|
1789
|
+
instance_blurbs << instances_str
|
1790
|
+
end
|
1791
|
+
end
|
1792
|
+
end
|
1793
|
+
if instance_blurbs.size > 0
|
1794
|
+
tier_name + ": #{instance_blurbs.join(', ')}" + cyan
|
1795
|
+
else
|
1796
|
+
tier_name + ": (empty)" + cyan
|
1797
|
+
end
|
1798
|
+
end
|
1799
|
+
str += tier_descriptions.compact.join(", ")
|
1800
|
+
end
|
1801
|
+
str
|
1802
|
+
end
|
1803
|
+
|
1804
|
+
def print_app_template_details(app_template)
|
1805
|
+
print cyan
|
1806
|
+
description_cols = {
|
1807
|
+
"ID" => 'id',
|
1808
|
+
"Name" => 'name',
|
1809
|
+
"Description" => 'description',
|
1810
|
+
"Category" => 'category',
|
1811
|
+
#"Image" => lambda {|it| it['config'] ? it['config']['image'] : '' },
|
1812
|
+
}
|
1813
|
+
if app_template["config"] && app_template["config"]["image"]
|
1814
|
+
description_cols["Image"] = lambda {|it| app_template["config"]["image"] }
|
1815
|
+
# else
|
1816
|
+
# '/assets/apps/template.png'
|
1817
|
+
end
|
1818
|
+
print_description_list(description_cols, app_template)
|
1819
|
+
# print_h2 "Tiers"
|
1820
|
+
if app_template["config"] && app_template["config"]["tiers"] && app_template["config"]["tiers"].keys.size != 0
|
1821
|
+
print cyan
|
1822
|
+
#puts as_yaml(app_template["config"]["tiers"])
|
1823
|
+
app_template["config"]["tiers"].each do |tier_name, tier_config|
|
1824
|
+
# print_h2 "Tier: #{tier_name}"
|
1825
|
+
print_h2 tier_name
|
1826
|
+
# puts " Instances:"
|
1827
|
+
if tier_config['instances'] && tier_config['instances'].size != 0
|
1828
|
+
# puts as_yaml(tier)
|
1829
|
+
tier_config['instances'].each_with_index do |instance_config, instance_index|
|
1830
|
+
instance_name = instance_config["instance"]["name"] || ""
|
1831
|
+
instance_type_code = ""
|
1832
|
+
if instance_config["instance"]["type"]
|
1833
|
+
instance_type_code = instance_config["instance"]["type"]
|
1834
|
+
end
|
1835
|
+
instance_bullet = ""
|
1836
|
+
# instance_bullet += "#{green} - #{bold}#{instance_type_code}#{reset}"
|
1837
|
+
instance_bullet += " #{green}#{bold}#{instance_type_code}#{reset}"
|
1838
|
+
if instance_name.to_s != ""
|
1839
|
+
instance_bullet += "#{green} - #{instance_name}#{reset}"
|
1840
|
+
end
|
1841
|
+
puts instance_bullet
|
1842
|
+
# print "\n"
|
1843
|
+
begin
|
1844
|
+
config_list = parse_scoped_instance_configs(instance_config)
|
1845
|
+
print cyan
|
1846
|
+
if config_list.size > 0
|
1847
|
+
print "\n"
|
1848
|
+
if config_list.size == 1
|
1849
|
+
puts " Config:"
|
1850
|
+
else
|
1851
|
+
puts " Configs (#{config_list.size}):"
|
1852
|
+
end
|
1853
|
+
config_list.each do |config_obj|
|
1854
|
+
# puts " = #{config_obj[:scope].inspect}"
|
1855
|
+
config_scope = config_obj[:scope]
|
1856
|
+
scoped_instance_config = config_obj[:config]
|
1857
|
+
config_description = ""
|
1858
|
+
config_items = []
|
1859
|
+
if config_scope[:environment]
|
1860
|
+
config_items << {label: "Environment", value: config_scope[:environment]}
|
1861
|
+
end
|
1862
|
+
if config_scope[:group]
|
1863
|
+
config_items << {label: "Group", value: config_scope[:group]}
|
1864
|
+
end
|
1865
|
+
if config_scope[:cloud]
|
1866
|
+
config_items << {label: "Cloud", value: config_scope[:cloud]}
|
1867
|
+
end
|
1868
|
+
# if scoped_instance_config['plan'] && scoped_instance_config['plan']['code']
|
1869
|
+
# config_items << {label: "Plan", value: scoped_instance_config['plan']['code']}
|
1870
|
+
# end
|
1871
|
+
config_description = config_items.collect {|item| "#{item[:label]}: #{item[:value]}"}.join(", ")
|
1872
|
+
puts " * #{config_description}"
|
1873
|
+
end
|
1874
|
+
else
|
1875
|
+
print white," Instance has no configs, see `app-templates add-instance-config \"#{app_template['name']}\" \"#{tier_name}\" \"#{instance_type_code}\"`",reset,"\n"
|
1876
|
+
end
|
1877
|
+
rescue => err
|
1878
|
+
#puts_error "Failed to parse instance scoped instance configs for app template #{app_template['id']} #{app_template['name']} Exception: #{err.class} #{err.message}"
|
1879
|
+
end
|
1880
|
+
print "\n"
|
1881
|
+
#puts as_yaml(instance_config)
|
1882
|
+
# todo: iterate over
|
1883
|
+
# instance_config["groups"][group_name]["clouds"][cloud_name]
|
1884
|
+
end
|
1885
|
+
|
1886
|
+
print cyan
|
1887
|
+
if tier_config['bootOrder']
|
1888
|
+
puts " Boot Order: #{tier_config['bootOrder']}"
|
1889
|
+
end
|
1890
|
+
if tier_config['linkedTiers'] && !tier_config['linkedTiers'].empty?
|
1891
|
+
puts " Connected Tiers: #{tier_config['linkedTiers'].join(', ')}"
|
1892
|
+
end
|
1893
|
+
|
1894
|
+
else
|
1895
|
+
print white," Tier is empty, see `app-templates add-instance \"#{app_template['name']}\" \"#{tier_name}\"`",reset,"\n"
|
1896
|
+
end
|
1897
|
+
# print "\n"
|
1898
|
+
|
1899
|
+
end
|
1900
|
+
# print "\n"
|
1901
|
+
|
1902
|
+
else
|
1903
|
+
print white,"\nTemplate is empty, see `app-templates add-tier \"#{app_template['name']}\"`",reset,"\n"
|
1904
|
+
end
|
1905
|
+
end
|
1906
|
+
|
1907
|
+
# this parses the environments => groups => clouds tree structure
|
1908
|
+
# and returns a list of objects like {scope: {group:'thegroup'}, config: Map}
|
1909
|
+
# this would be be better as a recursive function, brute forced for now.
|
1910
|
+
def parse_scoped_instance_configs(instance_config)
|
1911
|
+
config_list = []
|
1912
|
+
if instance_config['environments'] && instance_config['environments'].keys.size > 0
|
1913
|
+
instance_config['environments'].each do |env_name, env_config|
|
1914
|
+
if env_config['groups']
|
1915
|
+
env_config['groups'].each do |group_name, group_config|
|
1916
|
+
if group_config['clouds'] && !group_config['clouds'].empty?
|
1917
|
+
group_config['clouds'].each do |cloud_name, cloud_config|
|
1918
|
+
config_list << {config: cloud_config, scope: {environment: env_name, group: group_name, cloud: cloud_name}}
|
1919
|
+
end
|
1920
|
+
end
|
1921
|
+
if (!group_config['clouds'] || group_config['clouds'].empty?)
|
1922
|
+
config_list << {config: group_config, scope: {environment: env_name, group: group_name}}
|
1923
|
+
end
|
1924
|
+
end
|
1925
|
+
end
|
1926
|
+
if env_config['clouds'] && !env_config['clouds'].empty?
|
1927
|
+
env_config['clouds'].each do |cloud_name, cloud_config|
|
1928
|
+
config_list << {config: cloud_config, scope: {environment: env_name, cloud: cloud_name}}
|
1929
|
+
end
|
1930
|
+
end
|
1931
|
+
if (!env_config['groups'] || env_config['groups'].empty?) && (!env_config['clouds'] || env_config['clouds'].empty?)
|
1932
|
+
config_list << {config: env_config, scope: {environment: env_name}}
|
1933
|
+
end
|
1934
|
+
end
|
1935
|
+
end
|
1936
|
+
if instance_config['groups']
|
1937
|
+
instance_config['groups'].each do |group_name, group_config|
|
1938
|
+
if group_config['clouds'] && !group_config['clouds'].empty?
|
1939
|
+
group_config['clouds'].each do |cloud_name, cloud_config|
|
1940
|
+
config_list << {config: cloud_config, scope: {group: group_name, cloud: cloud_name}}
|
1941
|
+
end
|
1942
|
+
end
|
1943
|
+
if (!group_config['clouds'] || group_config['clouds'].empty?)
|
1944
|
+
config_list << {config: group_config, scope: {group: group_name}}
|
1945
|
+
end
|
1946
|
+
end
|
1947
|
+
end
|
1948
|
+
if instance_config['clouds']
|
1949
|
+
instance_config['clouds'].each do |cloud_name, cloud_config|
|
1950
|
+
config_list << {config: cloud_config, scope: {cloud: cloud_name}}
|
1951
|
+
end
|
1952
|
+
end
|
1953
|
+
return config_list
|
1954
|
+
end
|
1955
|
+
|
1956
|
+
def link_tiers(tiers, tier_names)
|
1957
|
+
# tiers = app_template["config"]["tiers"]
|
1958
|
+
tier_names = [tier_names].flatten.collect {|it| it }.compact.uniq
|
1959
|
+
if !tiers
|
1960
|
+
print_red_alert "No tiers found for template"
|
1961
|
+
return false
|
1962
|
+
end
|
1963
|
+
|
1964
|
+
existing_tier_names = tiers.keys
|
1965
|
+
matching_tier_names = tier_names.map {|tier_name|
|
1966
|
+
existing_tier_names.find {|k| k.downcase == tier_name.downcase }
|
1967
|
+
}.compact
|
1968
|
+
if matching_tier_names.size != tier_names.size
|
1969
|
+
print_red_alert "Template does not contain tiers: '#{tier_names}'"
|
1970
|
+
return false
|
1971
|
+
end
|
1972
|
+
matching_tier_names.each do |tier_name|
|
1973
|
+
tier = tiers[tier_name]
|
1974
|
+
tier['linkedTiers'] ||= []
|
1975
|
+
other_tier_names = matching_tier_names.select {|it| tier_name != it}
|
1976
|
+
other_tier_names.each do |other_tier_name|
|
1977
|
+
if !tier['linkedTiers'].include?(other_tier_name)
|
1978
|
+
tier['linkedTiers'].push(other_tier_name)
|
1979
|
+
end
|
1980
|
+
end
|
1981
|
+
end
|
1982
|
+
return true
|
1983
|
+
end
|
1984
|
+
|
1985
|
+
def unlink_tiers(tiers, tier_names)
|
1986
|
+
# tiers = app_template["config"]["tiers"]
|
1987
|
+
tier_names = [tier_names].flatten.collect {|it| it }.compact.uniq
|
1988
|
+
if !tiers
|
1989
|
+
print_red_alert "No tiers found for template"
|
1990
|
+
return false
|
1991
|
+
end
|
1992
|
+
|
1993
|
+
existing_tier_names = tiers.keys
|
1994
|
+
matching_tier_names = tier_names.map {|tier_name|
|
1995
|
+
existing_tier_names.find {|k| k.downcase == tier_name.downcase }
|
1996
|
+
}.compact
|
1997
|
+
if matching_tier_names.size != tier_names.size
|
1998
|
+
print_red_alert "Template does not contain tiers: '#{tier_names}'"
|
1999
|
+
return false
|
2000
|
+
end
|
2001
|
+
matching_tier_names.each do |tier_name|
|
2002
|
+
tier = tiers[tier_name]
|
2003
|
+
tier['linkedTiers'] ||= []
|
2004
|
+
other_tier_names = matching_tier_names.select {|it| tier_name != it}
|
2005
|
+
other_tier_names.each do |other_tier_name|
|
2006
|
+
if tier['linkedTiers'].include?(other_tier_name)
|
2007
|
+
tier['linkedTiers'] = tier['linkedTiers'].reject {|it| it == other_tier_name }
|
2008
|
+
end
|
2009
|
+
end
|
2010
|
+
end
|
2011
|
+
return true
|
2012
|
+
end
|
2013
|
+
|
905
2014
|
end
|