morpheus-cli 4.1.12 → 4.1.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6d784e08791add2fae739cdbcdf4f671c4a5f513d5e696ba4b24782e93422743
4
- data.tar.gz: b0f6fc1c33a414d1684e5cd9e2f0fa8113286b4b287cad136b3291fd48b67922
3
+ metadata.gz: ac0df1975e8da11d8f660ff39e84e186395b54b32f5022d444a182afc63d3df5
4
+ data.tar.gz: b3171f8c621d1090962322eb95478653e51740d1d08c9aafb1db9b20d60f619e
5
5
  SHA512:
6
- metadata.gz: b73e441079d35cd77752382978e603817d743cb58b6da4934bb34a2979397a080c6515105154c122e7ee24e3a5b4192d186db6a97f2b57f5192ce287a6376182
7
- data.tar.gz: 6e6cba06dbf2cf04bbc553e63600a7c070db58e038afd02b06b9c4e03594ed6ece102cbdd40db22f7c02a59190459b90ff85109ef10d35b38131c4560cd62020
6
+ metadata.gz: d554ef4a1d27f7763e41eebeca34952ee24ce70072148999615bb50cea6863ebb13e9527751ab0701c6ebd5af300986cf12703c4e88059462ee02a05f1d95902
7
+ data.tar.gz: a223e0e79f20bf202743e2592186c249da94cb81accd63cef4d1f38c44571dce8e761dd8282cc355fdd4e7121f6117c731ed0c081fa4a62cb1d940b0eda344fb
data/Dockerfile CHANGED
@@ -1,5 +1,5 @@
1
1
  FROM ruby:2.5.1
2
2
 
3
- RUN gem install morpheus-cli -v 4.1.11
3
+ RUN gem install morpheus-cli -v 4.1.13
4
4
 
5
5
  ENTRYPOINT ["morpheus"]
@@ -602,8 +602,12 @@ class Morpheus::APIClient
602
602
  Morpheus::LibraryClusterLayoutsInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
603
603
  end
604
604
 
605
- def library_resource_specs
606
- Morpheus::LibraryResourceSpecsInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
605
+ def library_spec_templates
606
+ Morpheus::LibrarySpecTemplatesInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
607
+ end
608
+
609
+ def library_spec_template_types
610
+ Morpheus::LibrarySpecTemplateTypesInterface.new(@access_token, @refresh_token, @expires_at, @base_url).setopts(@options)
607
611
  end
608
612
 
609
613
  def packages
@@ -0,0 +1,49 @@
1
+ require 'morpheus/api/api_client'
2
+
3
+ class Morpheus::LibrarySpecTemplateTypesInterface < Morpheus::APIClient
4
+ def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
5
+ @access_token = access_token
6
+ @refresh_token = refresh_token
7
+ @base_url = base_url
8
+ @expires_at = expires_at
9
+ end
10
+
11
+ def get(id, params={})
12
+ raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
13
+ url = "#{@base_url}/api/library/spec-template-types/#{id}"
14
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
15
+ opts = {method: :get, url: url, headers: headers}
16
+ execute(opts)
17
+ end
18
+
19
+ def list(params={})
20
+ url = "#{@base_url}/api/library/spec-template-types"
21
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
22
+ opts = {method: :get, url: url, headers: headers}
23
+ execute(opts)
24
+ end
25
+
26
+ # def create(options)
27
+ # url = "#{@base_url}/api/library/spec-template-types"
28
+ # headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
29
+ # payload = options
30
+ # opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
31
+ # execute(opts)
32
+ # end
33
+
34
+ # def update(id, options)
35
+ # url = "#{@base_url}/api/library/spec-template-types/#{id}"
36
+ # headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
37
+ # payload = options
38
+ # opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
39
+ # execute(opts)
40
+ # end
41
+
42
+ # def destroy(id, payload={})
43
+ # url = "#{@base_url}/api/library/spec-template-types/#{id}"
44
+ # headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
45
+ # opts = {method: :delete, url: url, headers: headers, payload: payload.to_json}
46
+ # execute(opts)
47
+ # end
48
+
49
+ end
@@ -1,6 +1,6 @@
1
1
  require 'morpheus/api/api_client'
2
2
 
3
- class Morpheus::LibraryResourceSpecsInterface < Morpheus::APIClient
3
+ class Morpheus::LibrarySpecTemplatesInterface < 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
@@ -8,10 +8,10 @@ class Morpheus::UsersInterface < Morpheus::APIClient
8
8
  @expires_at = expires_at
9
9
  end
10
10
 
11
- def get(account_id, id)
11
+ def get(account_id, id, params={})
12
12
  raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
13
13
  url = build_url(account_id, id)
14
- headers = { params: {}, authorization: "Bearer #{@access_token}" }
14
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
15
15
  opts = {method: :get, url: url, timeout: 10, headers: headers}
16
16
  execute(opts)
17
17
  end
data/lib/morpheus/cli.rb CHANGED
@@ -122,7 +122,7 @@ module Morpheus
122
122
  load 'morpheus/cli/library_container_templates_command.rb'
123
123
  load 'morpheus/cli/library_option_types_command.rb'
124
124
  load 'morpheus/cli/library_option_lists_command.rb'
125
- load 'morpheus/cli/library_resource_specs_command.rb'
125
+ load 'morpheus/cli/library_spec_templates_command.rb'
126
126
  load 'morpheus/cli/monitoring_incidents_command.rb'
127
127
  load 'morpheus/cli/monitoring_checks_command.rb'
128
128
  load 'morpheus/cli/monitoring_contacts_command.rb'
@@ -1191,6 +1191,9 @@ class Morpheus::Cli::Hosts
1191
1191
  opts.on('--install-agent [on|off]', String, "Install Agent? Pass false to manually install agent. Default is true.") do |val|
1192
1192
  options['installAgent'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
1193
1193
  end
1194
+ opts.on('-g', '--group GROUP', String, "Group to assign to new instance.") do |val|
1195
+ options[:group] = val
1196
+ end
1194
1197
  # opts.on('--instance-type-id ID', String, "Instance Type ID for the new instance.") do |val|
1195
1198
  # options['instanceTypeId'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
1196
1199
  # end
@@ -1222,6 +1225,11 @@ class Morpheus::Cli::Hosts
1222
1225
  if account_id
1223
1226
  payload['server']['account'] = {id: account}
1224
1227
  end
1228
+ if options[:group]
1229
+ group = options[:group] ? find_group_by_name_or_id_for_provisioning(options[:group]) : nil
1230
+ return 1 if group.nil?
1231
+ params['provisionSiteId'] = group['id']
1232
+ end
1225
1233
  payload['server'].merge!(params)
1226
1234
  ['installAgent','instanceTypeId'].each do |k|
1227
1235
  if options[k] != nil
@@ -34,6 +34,9 @@ class Morpheus::Cli::JobsCommand
34
34
  opts.on("--source [all|user|discovered]", String, "Filters job based upon specified source. Default is all") do |val|
35
35
  options[:source] = val.to_s
36
36
  end
37
+ opts.on("--internal [true|false]", String, "Filters job based on internal flag. Internal jobs are excluded by default.") do |val|
38
+ params["internalOnly"] = (val.to_s != "false")
39
+ end
37
40
  build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
38
41
  opts.footer = "List jobs."
39
42
  end
@@ -68,6 +71,9 @@ class Morpheus::Cli::JobsCommand
68
71
  title = "Morpheus Jobs"
69
72
  subtitles = []
70
73
  subtitles += parse_list_subtitles(options)
74
+ if params["internalOnly"]
75
+ subtitles << "internalOnly: #{params['internalOnly']}"
76
+ end
71
77
  print_h1 title, subtitles
72
78
 
73
79
  jobs = json_response['jobs']
@@ -178,6 +184,7 @@ class Morpheus::Cli::JobsCommand
178
184
 
179
185
  print cyan
180
186
  description_cols = {
187
+ "ID" => lambda {|it| it['id'] },
181
188
  "Name" => lambda {|it| it['name']},
182
189
  "Job Type" => lambda {|it| it['type']['name']},
183
190
  "Enabled" => lambda {|it| format_boolean(it['enabled'])},
@@ -739,6 +746,7 @@ class Morpheus::Cli::JobsCommand
739
746
  process = exec['process']
740
747
  print cyan
741
748
  description_cols = {
749
+ "ID" => lambda {|it| it['id'] },
742
750
  "Job" => lambda {|it| it['job'] ? it['job']['name'] : ''},
743
751
  "Job Type" => lambda {|it| it['job'] && it['job']['type'] ? (it['job']['type']['code'] == 'morpheus.workflow' ? 'Workflow' : 'Task') : ''},
744
752
  # "Description" => lambda {|it| it['description'] || (it['job'] ? it['job']['description'] : '') },
@@ -0,0 +1,644 @@
1
+ require 'morpheus/cli/cli_command'
2
+
3
+ class Morpheus::Cli::LibrarySpecTemplatesCommand
4
+ include Morpheus::Cli::CliCommand
5
+ set_command_name :'library-spec-templates'
6
+ set_command_hidden
7
+ register_subcommands :list, :get, :add, :update, :remove, :list_types
8
+
9
+ def connect(opts)
10
+ @api_client = establish_remote_appliance_connection(opts)
11
+ @spec_templates_interface = @api_client.library_spec_templates
12
+ @spec_template_types_interface = @api_client.library_spec_template_types
13
+ end
14
+
15
+ def handle(args)
16
+ handle_subcommand(args)
17
+ end
18
+
19
+ def list(args)
20
+ options = {}
21
+ params = {}
22
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
23
+ opts.banner = subcommand_usage()
24
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
25
+ opts.footer = "List spec templates."
26
+ end
27
+ optparse.parse!(args)
28
+ connect(options)
29
+ if args.count > 0
30
+ print_error Morpheus::Terminal.angry_prompt
31
+ puts_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.inspect}\n#{optparse}"
32
+ return 1
33
+ end
34
+ begin
35
+ # construct payload
36
+ params.merge!(parse_list_options(options))
37
+ @spec_templates_interface.setopts(options)
38
+ if options[:dry_run]
39
+ print_dry_run @spec_templates_interface.dry.list(params)
40
+ return
41
+ end
42
+ # do it
43
+ json_response = @spec_templates_interface.list(params)
44
+ render_result = render_with_format(json_response, options, 'specTemplates')
45
+ return 0 if render_result
46
+ resource_specs = json_response['specTemplates']
47
+ title = "Morpheus Library - Spec Templates"
48
+ subtitles = []
49
+ subtitles += parse_list_subtitles(options)
50
+ print_h1 title, subtitles
51
+ if resource_specs.empty?
52
+ print cyan,"No spec templates found.",reset,"\n"
53
+ else
54
+ # print_resource_specs_table(resource_specs, options)
55
+ columns = [
56
+ {"ID" => lambda {|resource_spec| resource_spec['id'] } },
57
+ {"NAME" => lambda {|resource_spec| resource_spec['name'] } },
58
+ {"TYPE" => lambda {|resource_spec| resource_spec['type']['name'] rescue '' } },
59
+ {"SOURCE" => lambda {|resource_spec| resource_spec['file']['sourceType'] rescue '' } },
60
+ {"CREATED" => lambda {|resource_spec| format_local_dt(resource_spec['dateCreated']) } },
61
+ ]
62
+ print as_pretty_table(resource_specs, columns, options)
63
+ print_results_pagination(json_response)
64
+ end
65
+ print reset,"\n"
66
+ rescue RestClient::Exception => e
67
+ print_rest_exception(e, options)
68
+ return 1
69
+ end
70
+ end
71
+
72
+ def get(args)
73
+ options = {}
74
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
75
+ opts.banner = subcommand_usage("[name]")
76
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
77
+ end
78
+ optparse.parse!(args)
79
+ if args.count < 1
80
+ puts optparse
81
+ return 1
82
+ end
83
+ connect(options)
84
+ id_list = parse_id_list(args)
85
+ return run_command_for_each_arg(id_list) do |arg|
86
+ _get(arg, options)
87
+ end
88
+ end
89
+
90
+ def _get(id, options)
91
+
92
+ begin
93
+ resource_spec = find_resource_spec_by_name_or_id(id)
94
+ if resource_spec.nil?
95
+ return 1
96
+ end
97
+ @spec_templates_interface.setopts(options)
98
+ if options[:dry_run]
99
+ print_dry_run @spec_templates_interface.dry.get(resource_spec['id'])
100
+ return
101
+ end
102
+ json_response = @spec_templates_interface.get(resource_spec['id'])
103
+ resource_spec = json_response['specTemplate']
104
+ instances = json_response['instances'] || []
105
+ servers = json_response['servers'] || []
106
+ if options[:json]
107
+ puts as_json(json_response, options, 'specTemplate')
108
+ return 0
109
+ elsif options[:yaml]
110
+ puts as_yaml(json_response, options, 'specTemplate')
111
+ return 0
112
+ elsif options[:csv]
113
+ puts records_as_csv([json_response['specTemplate']], options)
114
+ return 0
115
+ end
116
+
117
+ print_h1 "Spec Template Details"
118
+ print cyan
119
+ description_cols = {
120
+ "ID" => lambda {|it| it['id'] },
121
+ "Name" => lambda {|it| it['name'] },
122
+ "Type" => lambda {|it| it['type']['name'] rescue '' },
123
+ "Source" => lambda {|it| it['file']['sourceType'] rescue '' },
124
+ #"Owner" => lambda {|it| it['account'] ? it['account']['name'] : '' },
125
+ "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
126
+ "Created By" => lambda {|it| it['createdBy'] },
127
+ "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
128
+ "Updated By" => lambda {|it| it['updatedBy'] },
129
+ # "Created" => lambda {|it| format_local_dt(it['dateCreated']) + " by #{it['createdBy']}" },
130
+ # "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) + " by #{it['updatedBy'] || it['createdBy']}" },
131
+ }
132
+ print_description_list(description_cols, resource_spec)
133
+
134
+ file_content = resource_spec['file']
135
+ print_h2 "Content"
136
+ if file_content
137
+ if file_content['sourceType'] == 'local'
138
+ puts file_content['content']
139
+ elsif file_content['sourceType'] == 'url'
140
+ puts "URL: #{file_content['contentPath']}"
141
+ elsif file_content['sourceType'] == 'repository'
142
+ puts "Repository: #{file_content['repository']['name'] rescue 'n/a'}"
143
+ puts "Path: #{file_content['contentPath']}"
144
+ if file_content['contentRef']
145
+ puts "Ref: #{file_content['contentRef']}"
146
+ end
147
+ else
148
+ puts "Source: #{file_content['sourceType']}"
149
+ puts "Path: #{file_content['contentPath']}"
150
+ end
151
+ else
152
+ print yellow,"No file content.",reset,"\n"
153
+ end
154
+
155
+ print reset,"\n"
156
+ return 0
157
+ rescue RestClient::Exception => e
158
+ print_rest_exception(e, options)
159
+ return 1
160
+ end
161
+ end
162
+
163
+ def add(args)
164
+ options = {}
165
+ params = {}
166
+ file_params = {}
167
+ template_type = nil
168
+ source_type = nil
169
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
170
+ opts.banner = subcommand_usage("[name]")
171
+ opts.on('--name VALUE', String, "Name") do |val|
172
+ params['name'] = val
173
+ end
174
+ opts.on('--type VALUE', String, "Spec Template Type. kubernetes, helm, terraform") do |val|
175
+ template_type = val.to_s
176
+ end
177
+ opts.on('--source VALUE', String, "Source Type. local, repository, url") do |val|
178
+ source_type = val.to_s
179
+ end
180
+ opts.on('--content TEXT', String, "Contents of the template. This implies source is local.") do |val|
181
+ source_type = 'local' if source_type.nil?
182
+ file_params['content'] = val
183
+ end
184
+ opts.on('--file FILE', "File containing the template. This can be used instead of --content" ) do |filename|
185
+ source_type = 'local' if source_type.nil?
186
+ full_filename = File.expand_path(filename)
187
+ if File.exists?(full_filename)
188
+ file_params['content'] = File.read(full_filename)
189
+ else
190
+ print_red_alert "File not found: #{full_filename}"
191
+ exit 1
192
+ end
193
+ # use the filename as the name by default.
194
+ if !params['name']
195
+ params['name'] = File.basename(full_filename)
196
+ end
197
+ end
198
+ opts.on('--url VALUE', String, "URL, for use when source is url") do |val|
199
+ file_params['contentPath'] = val
200
+ end
201
+ opts.on('--content-path VALUE', String, "Content Path, for use when source is repository or url") do |val|
202
+ file_params['contentPath'] = val
203
+ end
204
+ opts.on('--content-ref VALUE', String, "Content Ref (Version Ref), for use when source is repository") do |val|
205
+ file_params['contentRef'] = val
206
+ end
207
+ # opts.on('--enabled [on|off]', String, "Can be used to disable it") do |val|
208
+ # options['enabled'] = !(val.to_s == 'off' || val.to_s == 'false')
209
+ # end
210
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
211
+ opts.footer = "Create a new spec template."
212
+ end
213
+ optparse.parse!(args)
214
+ # support [name] as first argument
215
+ if args[0]
216
+ params['name'] = args[0]
217
+ end
218
+ connect(options)
219
+ begin
220
+ # construct payload
221
+ payload = nil
222
+ if options[:payload]
223
+ payload = options[:payload]
224
+ else
225
+ # merge -O options into normally parsed options
226
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
227
+ # prompt
228
+ if params['name'].nil?
229
+ params['name'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true}], options[:options], @api_client,{})['name']
230
+ end
231
+ if template_type.nil?
232
+ # should use code instead probably...
233
+ #template_type = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'optionSource' => 'resourceSpecType', 'required' => true}], options[:options], @api_client,{})['type']
234
+ #params['type'] = {'id' => template_type}
235
+ spec_type_dropdown = get_all_spec_template_types.collect { |it| {'value' => it['code'], 'name' => it['name']} }
236
+ template_type = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => spec_type_dropdown, 'required' => true}], options[:options], @api_client,{})['type']
237
+ params['type'] = {'code' => template_type}
238
+ else
239
+ # gotta look up id
240
+ template_type_obj = find_spec_template_type_by_name_or_code_id(template_type)
241
+ return 1 if template_type_obj.nil?
242
+ template_type = template_type_obj['code']
243
+ params['type'] = {'code' => template_type}
244
+ end
245
+ if source_type.nil?
246
+ source_type = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'source', 'fieldLabel' => 'Source', 'type' => 'select', 'optionSource' => 'fileContentSource', 'required' => true, 'defaultValue' => 'local'}], options[:options], @api_client,{})['source']
247
+ file_params['sourceType'] = source_type
248
+ end
249
+ if source_type == "local"
250
+ # prompt for content
251
+ if file_params['content'].nil?
252
+ file_params['content'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'content', 'type' => 'code-editor', 'fieldLabel' => 'Content', 'required' => true, 'description' => 'The file content'}], options[:options])['content']
253
+ end
254
+ elsif source_type == "url"
255
+ if file_params['contentPath'].nil?
256
+ file_params['contentPath'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'url', 'fieldLabel' => 'URL', 'type' => 'text', 'required' => true}], options[:options], @api_client,{})['url']
257
+ end
258
+ elsif source_type == "repository"
259
+ if file_params['repository'].nil?
260
+ repository_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'repositoryId', 'fieldLabel' => 'Repository', 'type' => 'select', 'optionSource' => 'codeRepositories', 'required' => true}], options[:options], @api_client,{})['repositoryId']
261
+ file_params['repository'] = {'id' => repository_id}
262
+ end
263
+ if file_params['contentPath'].nil?
264
+ file_params['contentPath'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'path', 'fieldLabel' => 'File Path', 'type' => 'text', 'required' => true}], options[:options], @api_client,{})['path']
265
+ end
266
+ if file_params['contentRef'].nil?
267
+ file_params['contentRef'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'ref', 'fieldLabel' => 'Version Ref', 'type' => 'text'}], options[:options], @api_client,{})['ref']
268
+ end
269
+ end
270
+ if template_type == "cloudFormation" # this right code?
271
+ # JD: the field names the UI uses are strange, we should make these consistent...
272
+ cloud_formation_option_types = [
273
+ {'fieldContext' => 'config', 'fieldName' => 'cloudformation.IAM', 'fieldLabel' => 'CAPABILITY_IAM', 'type' => 'checkbox'},
274
+ {'fieldContext' => 'config', 'fieldName' => 'cloudformation.CAPABILITY_NAMED_IAM', 'fieldLabel' => 'CAPABILITY_NAMED_IAM', 'type' => 'checkbox'},
275
+ {'fieldContext' => 'config', 'fieldName' => 'cloudformation.CAPABILITY_AUTO_EXPAND', 'fieldLabel' => 'CAPABILITY_AUTO_EXPAND', 'type' => 'checkbox'}
276
+ ]
277
+ v_prompt = Morpheus::Cli::OptionTypes.prompt(cloud_formation_option_types, options[:options], @api_client,{})
278
+ params.deep_merge!(v_prompt)
279
+ end
280
+ params['file'] = file_params
281
+ payload = {'specTemplate' => params}
282
+ end
283
+ @spec_templates_interface.setopts(options)
284
+ if options[:dry_run]
285
+ print_dry_run @spec_templates_interface.dry.create(payload)
286
+ return
287
+ end
288
+ json_response = @spec_templates_interface.create(payload)
289
+ if options[:json]
290
+ puts as_json(json_response, options)
291
+ elsif !options[:quiet]
292
+ resource_spec = json_response['specTemplate']
293
+ print_green_success "Added spec template #{resource_spec['name']}"
294
+ _get(resource_spec['id'], {})
295
+ end
296
+ return 0
297
+ rescue RestClient::Exception => e
298
+ print_rest_exception(e, options)
299
+ return 1
300
+ end
301
+ end
302
+
303
+ def update(args)
304
+ options = {}
305
+ params = {}
306
+ file_params = {}
307
+ template_type = nil
308
+ source_type = nil
309
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
310
+ opts.banner = subcommand_usage("[name]")
311
+ opts.on('--name VALUE', String, "Name") do |val|
312
+ params['name'] = val
313
+ end
314
+ opts.on('--type VALUE', String, "Spec Template Type. kubernetes, helm, terraform") do |val|
315
+ template_type = val.to_s
316
+ end
317
+ opts.on('--source VALUE', String, "Source Type. local, repository, url") do |val|
318
+ source_type = val.to_s
319
+ end
320
+ opts.on('--content TEXT', String, "Contents of the template. This implies source is local.") do |val|
321
+ source_type = 'local' if source_type.nil?
322
+ file_params['content'] = val
323
+ end
324
+ opts.on('--file FILE', "File containing the template. This can be used instead of --content" ) do |filename|
325
+ source_type = 'local' if source_type.nil?
326
+ full_filename = File.expand_path(filename)
327
+ if File.exists?(full_filename)
328
+ file_params['content'] = File.read(full_filename)
329
+ else
330
+ print_red_alert "File not found: #{full_filename}"
331
+ exit 1
332
+ end
333
+ end
334
+ opts.on('--url VALUE', String, "File URL, for use when source is url") do |val|
335
+ file_params['contentPath'] = val
336
+ end
337
+ opts.on('--content-path VALUE', String, "Content Path, for use when source is repository or url") do |val|
338
+ file_params['contentPath'] = val
339
+ end
340
+ opts.on('--content-ref VALUE', String, "Content Ref (Version Ref), for use when source is repository") do |val|
341
+ file_params['contentRef'] = val
342
+ end
343
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote, :quiet])
344
+ opts.footer = "Update a spec template." + "\n" +
345
+ "[name] is required. This is the name or id of a spec template."
346
+ end
347
+ optparse.parse!(args)
348
+ if args.count != 1
349
+ print_error Morpheus::Terminal.angry_prompt
350
+ puts_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.inspect}\n#{optparse}"
351
+ return 1
352
+ end
353
+ connect(options)
354
+ begin
355
+ resource_spec = find_resource_spec_by_name_or_id(args[0])
356
+ if resource_spec.nil?
357
+ return 1
358
+ end
359
+ # construct payload
360
+ payload = nil
361
+ if options[:payload]
362
+ payload = options[:payload]
363
+ else
364
+ # merge -O options into normally parsed options
365
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options]
366
+ if params.empty? && file_params.empty?
367
+ print_red_alert "Specify at least one option to update"
368
+ puts optparse
369
+ return 1
370
+ end
371
+ # massage special params
372
+ if !template_type.nil?
373
+ # gotta look up id
374
+ template_type_obj = find_spec_template_type_by_name_or_code_id(template_type)
375
+ return 1 if template_type_obj.nil?
376
+ template_type = template_type_obj['code']
377
+ params['type'] = {'code' => template_type}
378
+ end
379
+ if !source_type.nil?
380
+ file_params['sourceType'] = source_type
381
+ end
382
+ # if source_type == "local"
383
+ # # prompt for content
384
+ # if file_params['content'].nil?
385
+ # file_params['content'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'content', 'type' => 'code-editor', 'fieldLabel' => 'Content', 'required' => true, 'description' => 'The file content'}], options[:options])['content']
386
+ # end
387
+ # elsif source_type == "url"
388
+ # if file_params['contentPath'].nil?
389
+ # file_params['contentPath'] = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'url', 'fieldLabel' => 'URL', 'type' => 'text', 'required' => true}], options[:options], @api_client,{})['url']
390
+ # end
391
+ # elsif source_type == "repository"
392
+ # if file_params['repository'].nil?
393
+ # repository_id = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'repositoryId', 'fieldLabel' => 'Repository', 'type' => 'select', 'optionSource' => 'codeRepositories', 'required' => true, 'defaultValue' => 'local'}], options[:options], @api_client,{})['repositoryId']
394
+ # file_params['repository'] = {'id' => repository_id}
395
+ # end
396
+ # end
397
+ # if template_type == "cloudFormation" # this right code?
398
+ # # JD: the field names the UI uses are strange, we should make these consistent...
399
+ # cloud_formation_option_types = [
400
+ # {'fieldContext' => 'config', 'fieldName' => 'cloudformation.IAM', 'fieldLabel' => 'CAPABILITY_IAM', 'type' => 'checkbox'},
401
+ # {'fieldContext' => 'config', 'fieldName' => 'cloudformation.CAPABILITY_NAMED_IAM', 'fieldLabel' => 'CAPABILITY_NAMED_IAM', 'type' => 'checkbox'},
402
+ # {'fieldContext' => 'config', 'fieldName' => 'cloudformation.CAPABILITY_AUTO_EXPAND', 'fieldLabel' => 'CAPABILITY_AUTO_EXPAND', 'type' => 'checkbox'}
403
+ # ]
404
+ # v_prompt = Morpheus::Cli::OptionTypes.prompt(cloud_formation_option_types, options[:options], @api_client,{})
405
+ # params.deep_merge!(v_prompt)
406
+ # end
407
+ if !file_params.empty?
408
+ params['file'] = file_params
409
+ end
410
+ payload = {'specTemplate' => params}
411
+ end
412
+ @spec_templates_interface.setopts(options)
413
+ if options[:dry_run]
414
+ print_dry_run @spec_templates_interface.dry.update(resource_spec["id"], payload)
415
+ return
416
+ end
417
+ json_response = @spec_templates_interface.update(resource_spec["id"], payload)
418
+ if options[:json]
419
+ puts as_json(json_response, options)
420
+ elsif !options[:quiet]
421
+ print_green_success "Updated spec template #{resource_spec['name']}"
422
+ _get(resource_spec['id'], {})
423
+ end
424
+ return 0
425
+ rescue RestClient::Exception => e
426
+ print_rest_exception(e, options)
427
+ return 1
428
+ end
429
+ end
430
+
431
+ def remove(args)
432
+ options = {}
433
+ params = {}
434
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
435
+ opts.banner = subcommand_usage("[name]")
436
+ build_common_options(opts, options, [:json, :dry_run, :quiet, :auto_confirm])
437
+ end
438
+ optparse.parse!(args)
439
+ if args.count < 1
440
+ puts optparse
441
+ return 127
442
+ end
443
+ connect(options)
444
+
445
+ begin
446
+ resource_spec = find_resource_spec_by_name_or_id(args[0])
447
+ if resource_spec.nil?
448
+ return 1
449
+ end
450
+
451
+ unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to delete spec template '#{resource_spec['name']}'?", options)
452
+ return false
453
+ end
454
+
455
+ # payload = {
456
+ # 'specTemplate' => {id: resource_spec["id"]}
457
+ # }
458
+ # payload['specTemplate'].merge!(resource_spec)
459
+ payload = params
460
+ @spec_templates_interface.setopts(options)
461
+ if options[:dry_run]
462
+ print_dry_run @spec_templates_interface.dry.destroy(resource_spec["id"])
463
+ return
464
+ end
465
+
466
+ json_response = @spec_templates_interface.destroy(resource_spec["id"])
467
+ if options[:json]
468
+ puts as_json(json_response, options)
469
+ elsif !options[:quiet]
470
+ print_green_success "Deleted spec template #{resource_spec['name']}"
471
+ end
472
+ return 0, nil
473
+ rescue RestClient::Exception => e
474
+ print_rest_exception(e, options)
475
+ return 1
476
+ end
477
+ end
478
+
479
+ def list_types(args)
480
+ options = {}
481
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
482
+ opts.banner = subcommand_usage()
483
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
484
+ opts.footer = "List spec template types."
485
+ end
486
+ optparse.parse!(args)
487
+ connect(options)
488
+ begin
489
+ params = {}
490
+ params.merge!(parse_list_options(options))
491
+ @spec_template_types_interface.setopts(options)
492
+ if options[:dry_run]
493
+ print_dry_run @spec_template_types_interface.dry.list(params)
494
+ return
495
+ end
496
+ json_response = @spec_template_types_interface.list(params)
497
+
498
+ render_result = render_with_format(json_response, options, 'specTemplateTypes')
499
+ return 0 if render_result
500
+
501
+ spec_template_types = json_response['specTemplateTypes']
502
+
503
+ title = "Morpheus Spec Template Types"
504
+ subtitles = []
505
+ subtitles += parse_list_subtitles(options)
506
+ print_h1 title, subtitles
507
+ if spec_template_types.empty?
508
+ print cyan,"No spec template types found.",reset,"\n"
509
+ else
510
+ rows = spec_template_types.collect do |spec_template_type|
511
+ {
512
+ id: spec_template_type['id'],
513
+ code: spec_template_type['code'],
514
+ name: spec_template_type['name']
515
+ }
516
+ end
517
+ columns = [:id, :name, :code]
518
+ print cyan
519
+ print as_pretty_table(rows, columns, options)
520
+ print reset
521
+ print_results_pagination(json_response)
522
+ end
523
+ print reset,"\n"
524
+ return 0
525
+
526
+ rescue RestClient::Exception => e
527
+ print_rest_exception(e, options)
528
+ return 1
529
+ end
530
+ end
531
+
532
+ private
533
+
534
+ def find_resource_spec_by_name_or_id(val)
535
+ if val.to_s =~ /\A\d{1,}\Z/
536
+ return find_resource_spec_by_id(val)
537
+ else
538
+ return find_resource_spec_by_name(val)
539
+ end
540
+ end
541
+
542
+ def find_resource_spec_by_id(id)
543
+ begin
544
+ json_response = @spec_templates_interface.get(id.to_i)
545
+ return json_response['specTemplate']
546
+ rescue RestClient::Exception => e
547
+ if e.response && e.response.code == 404
548
+ print_red_alert "Spec Template not found by id #{id}"
549
+ else
550
+ raise e
551
+ end
552
+ end
553
+ end
554
+
555
+ def find_resource_spec_by_name(name)
556
+ resource_specs = @spec_templates_interface.list({name: name.to_s})['specTemplates']
557
+ if resource_specs.empty?
558
+ print_red_alert "Spec Template not found by name #{name}"
559
+ return nil
560
+ elsif resource_specs.size > 1
561
+ print_red_alert "#{resource_specs.size} spec templates found by name #{name}"
562
+ print_resource_specs_table(resource_specs, {color: red})
563
+ print_red_alert "Try using ID instead"
564
+ print reset,"\n"
565
+ return nil
566
+ else
567
+ return resource_specs[0]
568
+ end
569
+ end
570
+
571
+ def print_resource_specs_table(resource_specs, opts={})
572
+ columns = [
573
+ {"ID" => lambda {|resource_spec| resource_spec['id'] } },
574
+ {"NAME" => lambda {|resource_spec| resource_spec['name'] } },
575
+ #{"OWNER" => lambda {|resource_spec| resource_spec['account'] ? resource_spec['account']['name'] : '' } },
576
+ ]
577
+ if opts[:include_fields]
578
+ columns = opts[:include_fields]
579
+ end
580
+ print as_pretty_table(resource_specs, columns, opts)
581
+ end
582
+
583
+ def find_spec_template_type_by_id(id)
584
+ begin
585
+ json_response = @spec_template_types_interface.get(id.to_i)
586
+ return json_response['networkType']
587
+ rescue RestClient::Exception => e
588
+ if e.response && e.response.code == 404
589
+ print_red_alert "Network Type not found by id #{id}"
590
+ return nil
591
+ else
592
+ raise e
593
+ end
594
+ end
595
+ end
596
+
597
+ # def find_spec_template_type_by_name(name)
598
+ # json_response = @spec_template_types_interface.list({name: name.to_s})
599
+ # spec_template_types = json_response['networkTypes']
600
+ # if spec_template_types.empty?
601
+ # print_red_alert "Network Type not found by name #{name}"
602
+ # return spec_template_types
603
+ # elsif spec_template_types.size > 1
604
+ # print_red_alert "#{spec_template_types.size} network types found by name #{name}"
605
+ # rows = spec_template_types.collect do |it|
606
+ # {id: it['id'], name: it['name']}
607
+ # end
608
+ # puts as_pretty_table(rows, [:id, :name], {color:red})
609
+ # return nil
610
+ # else
611
+ # return spec_template_types[0]
612
+ # end
613
+ # end
614
+
615
+ def find_spec_template_type_by_name_or_code(name)
616
+ spec_template_types = get_all_spec_template_types().select { |it| name && it['code'] == name || it['name'] == name }
617
+ if spec_template_types.empty?
618
+ print_red_alert "Spec Template Type not found by code #{name}"
619
+ return nil
620
+ elsif spec_template_types.size > 1
621
+ print_red_alert "#{spec_template_types.size} spec template types found by code #{name}"
622
+ rows = spec_template_types.collect do |it|
623
+ {id: it['id'], code: it['code'], name: it['name']}
624
+ end
625
+ print "\n"
626
+ puts as_pretty_table(rows, [:id, :code, :name], {color:red})
627
+ return nil
628
+ else
629
+ return spec_template_types[0]
630
+ end
631
+ end
632
+
633
+ def find_spec_template_type_by_name_or_code_id(val)
634
+ if val.to_s =~ /\A\d{1,}\Z/
635
+ return find_subnet_by_id(val)
636
+ else
637
+ return find_spec_template_type_by_name_or_code(val)
638
+ end
639
+ end
640
+
641
+ def get_all_spec_template_types
642
+ @all_spec_template_types ||= @spec_template_types_interface.list({max: 1000})['specTemplateTypes'] || []
643
+ end
644
+ end