morpheus-cli 4.1.12 → 4.1.13

Sign up to get free protection for your applications and to get access to all the features.
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