morpheus-cli 3.4.1.10 → 3.5.1

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
- SHA1:
3
- metadata.gz: 546db01c07f0ed59b7d0c6a78f5864e6dc123b00
4
- data.tar.gz: e4965c3651a6396c17a45481e03988cdf0edbb4b
2
+ SHA256:
3
+ metadata.gz: dd3b5e29881772dd9ef4e80eb5226176fa814c4661464f68ffe9249e0ccda99c
4
+ data.tar.gz: cdbca169540925cfa847c48c12aad367bd4626a733e2844323d6078f5d351a44
5
5
  SHA512:
6
- metadata.gz: 83cce232452557026284d972f1f123d7fd0e72e188ae9518913ab746e04964d144bd903b58dc28c4ee175e3d309e491cbedb1efb9c48089785ca90bcf2d1aa86
7
- data.tar.gz: c784eb91e5e4ff851d173c7682de04a7326329e1fc59d6fa8a9caf81abde6758af5915020368a54e3612176300192b94dcf48af653e0f8f4d74691ecb6d45201
6
+ metadata.gz: 1f094793c1f8e11ffc84c9dea26e2e094881efb22d5b0d71b65ed5d206df40b16e6fc80ddbdf6e028c12184749f485113aba555c2d6fd595c2643e1d6f8547cb
7
+ data.tar.gz: 4715322bcc111e6e82fa8fcd98e60b0123ebc50afd6b22a7719cc108cf36e2c02c095456e0fa3dad2800e0fb4b65724c9bbad668eed46e966d2db2389a5dec1d
@@ -295,4 +295,8 @@ class Morpheus::APIClient
295
295
  Morpheus::PackagesInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
296
296
  end
297
297
 
298
+ def cypher
299
+ Morpheus::CypherInterface.new(@access_token, @refresh_token, @expires_at, @base_url)
300
+ end
301
+
298
302
  end
@@ -0,0 +1,55 @@
1
+ require 'morpheus/api/api_client'
2
+ require 'uri'
3
+
4
+ class Morpheus::CypherInterface < Morpheus::APIClient
5
+ def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
6
+ @access_token = access_token
7
+ @refresh_token = refresh_token
8
+ @base_url = base_url
9
+ @expires_at = expires_at
10
+ end
11
+
12
+ def get(id, params={})
13
+ raise "#{self.class}.get() passed a blank id!" if id.to_s == ''
14
+ url = "#{@base_url}/api/cypher/#{id}"
15
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
16
+ opts = {method: :get, url: url, headers: headers}
17
+ execute(opts)
18
+ end
19
+
20
+ def list(params={})
21
+ url = "#{@base_url}/api/cypher"
22
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
23
+ opts = {method: :get, url: url, headers: headers}
24
+ execute(opts)
25
+ end
26
+
27
+ def create(payload)
28
+ url = "#{@base_url}/api/cypher"
29
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
30
+ opts = {method: :post, url: url, headers: headers, payload: payload.to_json}
31
+ execute(opts)
32
+ end
33
+
34
+ def update(id, payload)
35
+ url = "#{@base_url}/api/cypher/#{id}"
36
+ headers = { :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
37
+ opts = {method: :put, url: url, headers: headers, payload: payload.to_json}
38
+ execute(opts)
39
+ end
40
+
41
+ def destroy(id, params={})
42
+ url = "#{@base_url}/api/cypher/#{id}"
43
+ headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/json' }
44
+ opts = {method: :delete, url: url, timeout: 30, headers: headers}
45
+ execute(opts)
46
+ end
47
+
48
+ def decrypt(id, params={})
49
+ url = "#{@base_url}/api/cypher/#{id}/decrypt"
50
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
51
+ opts = {method: :get, url: url, headers: headers}
52
+ execute(opts)
53
+ end
54
+
55
+ end
@@ -1,4 +1,5 @@
1
1
  require 'morpheus/api/api_client'
2
+ require 'uri'
2
3
 
3
4
  class Morpheus::StorageProvidersInterface < Morpheus::APIClient
4
5
  def initialize(access_token, refresh_token,expires_at = nil, base_url=nil)
@@ -44,4 +45,116 @@ class Morpheus::StorageProvidersInterface < Morpheus::APIClient
44
45
  execute(opts)
45
46
  end
46
47
 
48
+ def list_files(id, file_path, params={})
49
+ if file_path.to_s.strip == "/"
50
+ file_path = ""
51
+ end
52
+ url = "#{@base_url}/api/storage/providers/#{URI.escape(id.to_s)}" + "/files/#{URI.escape(file_path)}".squeeze('/')
53
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
54
+ opts = {method: :get, url: url, headers: headers}
55
+ execute(opts)
56
+ end
57
+
58
+ # upload a file without multipart
59
+ def upload_file(id, local_file, destination, params={})
60
+ # puts "upload_file #{local_file} to destination #{destination}"
61
+ # destination should be the full filePath, but the api looks like directory?filename=
62
+ path = destination.to_s.squeeze("/")
63
+ if !path || path == "" || path == "/" || path == "."
64
+ raise "#{self.class}.upload_file() passed a bad destination: '#{destination}'"
65
+ end
66
+ if path[0].chr == "/"
67
+ path = path[1..-1]
68
+ end
69
+ path_chunks = path.split("/")
70
+ filename = path_chunks.pop
71
+ safe_dirname = path_chunks.collect {|it| URI.escape(it) }.join("/")
72
+ # filename = File.basename(destination)
73
+ # dirname = File.dirname(destination)
74
+ # if filename == "" || filename == "/"
75
+ # filename = File.basename(local_file)
76
+ # end
77
+ url = "#{@base_url}/api/storage/providers/#{URI.escape(id.to_s)}" + "/files/#{safe_dirname}".squeeze('/')
78
+ headers = { :params => params, :authorization => "Bearer #{@access_token}", 'Content-Type' => 'application/octet-stream'}
79
+ headers[:params][:filename] = filename # File.basename(destination)
80
+ if !local_file.kind_of?(File)
81
+ local_file = File.new(local_file, 'rb')
82
+ end
83
+ payload = local_file
84
+ headers['Content-Length'] = local_file.size # File.size(local_file)
85
+ execute(method: :post, url: url, headers: headers, payload: payload)
86
+ end
87
+
88
+ def download_file(id, file_path, params={})
89
+ raise "#{self.class}.download_file() passed a blank id!" if id.to_s == ''
90
+ raise "#{self.class}.download_file() passed a blank file path!" if file_path.to_s == ''
91
+ escaped_file_path = file_path.split("/").collect {|it| URI.escape(it) }.join("/")
92
+ url = "#{@base_url}/api/storage/providers/#{URI.escape(id.to_s)}" + "/download-file/#{escaped_file_path}".squeeze('/')
93
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
94
+ opts = {method: :get, url: url, headers: headers}
95
+ execute(opts, false)
96
+ end
97
+
98
+ def download_file_chunked(id, file_path, outfile, params={})
99
+ raise "#{self.class}.download_file() passed a blank id!" if id.to_s == ''
100
+ raise "#{self.class}.download_file() passed a blank file path!" if file_path.to_s == ''
101
+ escaped_file_path = file_path.split("/").collect {|it| URI.escape(it) }.join("/")
102
+ url = "#{@base_url}/api/storage/providers/#{URI.escape(id.to_s)}" + "/download-file/#{escaped_file_path}".squeeze('/')
103
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
104
+ opts = {method: :get, url: url, headers: headers}
105
+ # execute(opts, false)
106
+ if Dir.exists?(outfile)
107
+ raise "outfile is invalid. It is the name of an existing directory: #{outfile}"
108
+ end
109
+ # if @verify_ssl == false
110
+ # opts[:verify_ssl] = OpenSSL::SSL::VERIFY_NONE
111
+ # end
112
+ if @dry_run
113
+ return opts
114
+ end
115
+ http_response = nil
116
+ File.open(outfile, 'w') {|f|
117
+ block = proc { |response|
118
+ response.read_body do |chunk|
119
+ # writing to #{outfile} ..."
120
+ f.write chunk
121
+ end
122
+ }
123
+ opts[:block_response] = block
124
+ http_response = RestClient::Request.new(opts).execute
125
+ # RestClient::Request.execute(opts)
126
+ }
127
+ return http_response
128
+ end
129
+
130
+ def download_zip_chunked(id, outfile, params={})
131
+ #url = "#{@base_url}/api/storage/providers/#{URI.escape(id.to_s)}" + ".zip"
132
+ url = "#{@base_url}/api/storage/providers/#{URI.escape(id.to_s)}/download-zip"
133
+ headers = { params: params, authorization: "Bearer #{@access_token}" }
134
+ opts = {method: :get, url: url, headers: headers}
135
+ # execute(opts, false)
136
+ if Dir.exists?(outfile)
137
+ raise "outfile is invalid. It is the name of an existing directory: #{outfile}"
138
+ end
139
+ # if @verify_ssl == false
140
+ # opts[:verify_ssl] = OpenSSL::SSL::VERIFY_NONE
141
+ # end
142
+ if @dry_run
143
+ return opts
144
+ end
145
+ http_response = nil
146
+ File.open(outfile, 'w') {|f|
147
+ block = proc { |response|
148
+ response.read_body do |chunk|
149
+ # writing to #{outfile} ..."
150
+ f.write chunk
151
+ end
152
+ }
153
+ opts[:block_response] = block
154
+ http_response = RestClient::Request.new(opts).execute
155
+ # RestClient::Request.execute(opts)
156
+ }
157
+ return http_response
158
+ end
159
+
47
160
  end
@@ -138,6 +138,7 @@ module Morpheus
138
138
  load 'morpheus/cli/network_pool_servers_command.rb'
139
139
  load 'morpheus/cli/network_domains_command.rb'
140
140
  load 'morpheus/cli/network_proxies_command.rb'
141
+ load 'morpheus/cli/cypher_command.rb'
141
142
  load 'morpheus/cli/image_builder_command.rb'
142
143
  load 'morpheus/cli/preseed_scripts_command.rb'
143
144
  load 'morpheus/cli/boot_scripts_command.rb'
@@ -820,7 +820,6 @@ class Morpheus::Cli::ArchivesCommand
820
820
  groups_str = owner_str
821
821
  end
822
822
  end
823
- groups_str =
824
823
  file_info << truncate_string(groups_str, 15).ljust(15, " ")
825
824
  # File Type
826
825
  content_type = archive_file['contentType'].to_s
@@ -848,7 +847,7 @@ class Morpheus::Cli::ArchivesCommand
848
847
  mtime = format_local_dt(last_updated, {format: "%b %e %Y"})
849
848
  end
850
849
  end
851
- file_info << mtime # .ljust(21, " ")
850
+ file_info << mtime.ljust(12, " ")
852
851
  if params[:fullTree]
853
852
  file_info << file_color + archive_file["filePath"].to_s + cyan
854
853
  else
@@ -871,7 +870,7 @@ class Morpheus::Cli::ArchivesCommand
871
870
  if do_one_file_per_line
872
871
  print file_names.join("\n")
873
872
  else
874
- print file_names.join(" ")
873
+ print file_names.join("\t")
875
874
  end
876
875
  print "\n"
877
876
  end
@@ -1287,11 +1286,11 @@ class Morpheus::Cli::ArchivesCommand
1287
1286
  if options[:dry_run]
1288
1287
  # print_dry_run @archive_files_interface.dry.download_file_by_path(full_file_path), full_command_string
1289
1288
  if use_public_url
1290
- print_dry_run @archive_files_interface.dry.download_file_by_path_chunked(full_file_path, outfile), full_command_string
1291
- else
1292
1289
  print_dry_run @archive_files_interface.dry.download_public_file_by_path_chunked(full_file_path, outfile), full_command_string
1290
+ else
1291
+ print_dry_run @archive_files_interface.dry.download_file_by_path_chunked(full_file_path, outfile), full_command_string
1293
1292
  end
1294
- return 1
1293
+ return 0
1295
1294
  end
1296
1295
  if !options[:quiet]
1297
1296
  print cyan + "Downloading archive file #{bucket_id}:#{file_path} to #{outfile} ... "
@@ -318,7 +318,16 @@ module Morpheus
318
318
  k, v = k.split(":")
319
319
  end
320
320
  if (!k.to_s.empty?)
321
- options[:query_filters][k.to_s.strip] = v.to_s.strip
321
+ if options[:query_filters].key?(k.to_s.strip)
322
+ cur_val = options[:query_filters][k.to_s.strip]
323
+ if cur_val.instance_of?(Array)
324
+ options[:query_filters][k.to_s.strip] << v.to_s.strip
325
+ else
326
+ options[:query_filters][k.to_s.strip] = [cur_val, v.to_s.strip]
327
+ end
328
+ else
329
+ options[:query_filters][k.to_s.strip] = v.to_s.strip
330
+ end
322
331
  end
323
332
  end
324
333
  end
@@ -198,6 +198,7 @@ class Morpheus::Cli::Clouds
198
198
  "Location" => 'location',
199
199
  "Visibility" => lambda {|it| it['visibility'].to_s.capitalize },
200
200
  "Groups" => lambda {|it| it['groups'].collect {|g| g.instance_of?(Hash) ? g['name'] : g.to_s }.join(', ') },
201
+ "Enabled" => lambda {|it| format_boolean(it['enabled']) },
201
202
  "Status" => lambda {|it| format_cloud_status(it) }
202
203
  }
203
204
  print_description_list(description_cols, cloud)
@@ -0,0 +1,412 @@
1
+ require 'json'
2
+ require 'yaml'
3
+ require 'table_print'
4
+ require 'morpheus/cli/cli_command'
5
+
6
+ class Morpheus::Cli::CypherCommand
7
+ include Morpheus::Cli::CliCommand
8
+
9
+ set_command_name :cypher
10
+
11
+ register_subcommands :list, :get, :add, :remove, :decrypt
12
+
13
+ def initialize()
14
+ # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
15
+ end
16
+
17
+ def connect(opts)
18
+ @api_client = establish_remote_appliance_connection(opts)
19
+ @cypher_interface = @api_client.cypher
20
+ end
21
+
22
+ def handle(args)
23
+ handle_subcommand(args)
24
+ end
25
+
26
+ def list(args)
27
+ options = {}
28
+ params = {}
29
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
30
+ opts.banner = subcommand_usage()
31
+ build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :json, :dry_run, :remote])
32
+ opts.footer = "List cypher items."
33
+ end
34
+ optparse.parse!(args)
35
+ connect(options)
36
+ begin
37
+ params.merge!(parse_list_options(options))
38
+ if options[:dry_run]
39
+ print_dry_run @cypher_interface.dry.list(params)
40
+ return 0
41
+ end
42
+ json_response = @cypher_interface.list(params)
43
+ cypher_items = json_response["cyphers"]
44
+ if options[:json]
45
+ puts as_json(json_response, options, "cyphers")
46
+ return 0
47
+ elsif options[:yaml]
48
+ puts as_yaml(json_response, options, "cyphers")
49
+ return 0
50
+ elsif options[:csv]
51
+ puts records_as_csv(cypher_items, options)
52
+ return 0
53
+ end
54
+ title = "Morpheus Cypher List"
55
+ subtitles = []
56
+ subtitles += parse_list_subtitles(options)
57
+ print_h1 title, subtitles
58
+ if cypher_items.empty?
59
+ print cyan,"No cypher items found.",reset,"\n"
60
+ else
61
+ cypher_columns = {
62
+ "ID" => 'id',
63
+ "KEY" => lambda {|it| it["itemKey"] || it["key"] },
64
+ "LEASE REMAINING" => lambda {|it| it['expireDate'] ? format_local_dt(it['expireDate']) : "" },
65
+ "DATED CREATED" => lambda {|it| format_local_dt(it["dateCreated"]) },
66
+ "LAST ACCESSED" => lambda {|it| format_local_dt(it["lastAccessed"]) }
67
+ }
68
+ if options[:include_fields]
69
+ cypher_columns = options[:include_fields]
70
+ end
71
+ print cyan
72
+ print as_pretty_table(cypher_items, cypher_columns, options)
73
+ print reset
74
+ print_results_pagination(json_response)
75
+ end
76
+ print reset,"\n"
77
+ return 0
78
+ rescue RestClient::Exception => e
79
+ print_rest_exception(e, options)
80
+ exit 1
81
+ end
82
+ end
83
+
84
+ def get(args)
85
+ options = {}
86
+ do_decrypt = false
87
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
88
+ opts.banner = subcommand_usage("[id]")
89
+ opts.on(nil, '--decrypt', 'Display the decrypted value') do
90
+ do_decrypt = true
91
+ end
92
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
93
+ opts.footer = "Get details about a cypher." + "\n" +
94
+ "[id] is required. This is the id or key of a cypher."
95
+ end
96
+ optparse.parse!(args)
97
+ if args.count != 1
98
+ print_error Morpheus::Terminal.angry_prompt
99
+ puts_error "wrong number of arguments, expected 1 and got #{args.count}\n#{optparse}"
100
+ return 1
101
+ end
102
+ connect(options)
103
+ begin
104
+ if options[:dry_run]
105
+ if args[0].to_s =~ /\A\d{1,}\Z/
106
+ print_dry_run @cypher_interface.dry.get(args[0].to_i)
107
+ else
108
+ print_dry_run @cypher_interface.dry.list({name:args[0]})
109
+ end
110
+ return
111
+ end
112
+ cypher_item = find_cypher_by_name_or_id(args[0])
113
+ return 1 if cypher_item.nil?
114
+ json_response = {'cypher' => cypher_item} # skip redundant request
115
+ decrypt_json_response = nil
116
+ if do_decrypt
117
+ decrypt_json_response = @cypher_interface.decrypt(cypher_item["id"])
118
+ end
119
+ # json_response = @cypher_interface.get(cypher_item['id'])
120
+ cypher_item = json_response['cypher']
121
+ if options[:json]
122
+ puts as_json(json_response, options, "cypher")
123
+ return 0
124
+ elsif options[:yaml]
125
+ puts as_yaml(json_response, options, "cypher")
126
+ return 0
127
+ elsif options[:csv]
128
+ puts records_as_csv([cypher_item], options)
129
+ return 0
130
+ end
131
+ print_h1 "Cypher Details"
132
+ print cyan
133
+ description_cols = {
134
+ "ID" => 'id',
135
+ # what here
136
+ "Key" => lambda {|it| it["itemKey"] },
137
+ #"Value" => lambda {|it| it["value"] || "************" },
138
+ "Lease Remaining" => lambda {|it| format_local_dt(it["expireDate"]) },
139
+ "Date Created" => lambda {|it| format_local_dt(it["dateCreated"]) },
140
+ "Last Accessed" => lambda {|it| format_local_dt(it["lastAccessed"]) }
141
+ }
142
+ print_description_list(description_cols, cypher_item)
143
+ if decrypt_json_response
144
+ print_h2 "Decrypted Value"
145
+ print cyan
146
+ puts decrypt_json_response["cypher"] ? decrypt_json_response["cypher"]["itemValue"] : ""
147
+ end
148
+ print reset, "\n"
149
+
150
+ return 0
151
+ rescue RestClient::Exception => e
152
+ print_rest_exception(e, options)
153
+ return 1
154
+ end
155
+ end
156
+
157
+ def decrypt(args)
158
+ params = {}
159
+ options = {}
160
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
161
+ opts.banner = subcommand_usage("[id]")
162
+ build_common_options(opts, options, [:json, :yaml, :csv, :fields, :dry_run, :remote])
163
+ opts.footer = "Decrypt the value of a cypher." + "\n" +
164
+ "[id] is required. This is the id or key of a cypher."
165
+ end
166
+ optparse.parse!(args)
167
+ if args.count != 1
168
+ print_error Morpheus::Terminal.angry_prompt
169
+ puts_error "wrong number of arguments, expected 1 and got #{args.count}\n#{optparse}"
170
+ return 1
171
+ end
172
+ connect(options)
173
+ begin
174
+ cypher_item = find_cypher_by_name_or_id(args[0])
175
+ return 1 if cypher_item.nil?
176
+ if options[:dry_run]
177
+ print_dry_run @cypher_interface.dry.decrypt(cypher_item["id"], params)
178
+ return
179
+ end
180
+
181
+ cypher_item = find_cypher_by_name_or_id(args[0])
182
+ return 1 if cypher_item.nil?
183
+
184
+ json_response = @cypher_interface.decrypt(cypher_item["id"], params)
185
+ if options[:json]
186
+ puts as_json(json_response, options, "cypher")
187
+ return 0
188
+ elsif options[:yaml]
189
+ puts as_yaml(json_response, options, "cypher")
190
+ return 0
191
+ elsif options[:csv]
192
+ puts records_as_csv([json_response["crypt"]], options)
193
+ return 0
194
+ end
195
+ print_h1 "Cypher Decrypt"
196
+ print cyan
197
+ print_description_list({
198
+ "ID" => 'id',
199
+ "Key" => lambda {|it| it["itemKey"] },
200
+ "Value" => lambda {|it| it["itemValue"] }
201
+ }, json_response["cypher"])
202
+ print reset, "\n"
203
+ return 0
204
+ rescue RestClient::Exception => e
205
+ print_rest_exception(e, options)
206
+ return 1
207
+ end
208
+ end
209
+
210
+ def add(args)
211
+ options = {}
212
+ params = {}
213
+
214
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
215
+ opts.banner = subcommand_usage()
216
+ opts.on('--key VALUE', String, "Key for this cypher") do |val|
217
+ params['itemKey'] = val
218
+ end
219
+ build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
220
+ opts.footer = "Create a new cypher."
221
+ end
222
+ optparse.parse!(args)
223
+ if args.count > 1
224
+ print_error Morpheus::Terminal.angry_prompt
225
+ puts_error "wrong number of arguments, expected 0-1 and got #{args.count}\n#{optparse}"
226
+ return 1
227
+ end
228
+ connect(options)
229
+ begin
230
+ payload = nil
231
+ if options[:payload]
232
+ payload = options[:payload]
233
+ else
234
+ # merge -O options into normally parsed options
235
+ params.deep_merge!(options[:options].reject {|k,v| k.is_a?(Symbol) }) if options[:options] && options[:options].keys.size > 0
236
+
237
+ # support [key] as first argument
238
+ if args[0]
239
+ params['itemKey'] = args[0]
240
+ end
241
+ # Key
242
+ if !params['itemKey']
243
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'itemKey', 'fieldLabel' => 'Key', 'type' => 'text', 'required' => true, 'description' => cypher_key_help}], options)
244
+ params['itemKey'] = v_prompt['itemKey']
245
+ end
246
+
247
+ # Value
248
+ value_is_required = false
249
+ cypher_mount_type = params['itemKey'].split("/").first
250
+ if cypher_mount_type == ["secret", "password"].include?(cypher_mount_type)
251
+ value_is_required = true
252
+ end
253
+
254
+ if !params['itemValue']
255
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'itemValue', 'fieldLabel' => 'Value', 'type' => 'text', 'required' => value_is_required, 'description' => "Value for this cypher"}], options)
256
+ params['itemValue'] = v_prompt['itemValue']
257
+ end
258
+
259
+ # Lease
260
+ if !params['leaseTimeout']
261
+ v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'leaseTimeout', 'fieldLabel' => 'Lease', 'type' => 'text', 'required' => false, 'description' => cypher_lease_help}], options)
262
+ params['leaseTimeout'] = v_prompt['leaseTimeout']
263
+ end
264
+ if !params['leaseTimeout'].to_s.empty?
265
+ params['leaseTimeout'] = params['leaseTimeout'].to_i
266
+ end
267
+
268
+ # construct payload
269
+ payload = {
270
+ 'cypher' => params
271
+ }
272
+ end
273
+
274
+ if options[:dry_run]
275
+ print_dry_run @cypher_interface.dry.create(payload)
276
+ return
277
+ end
278
+ json_response = @cypher_interface.create(payload)
279
+ if options[:json]
280
+ print JSON.pretty_generate(json_response)
281
+ print "\n"
282
+ elsif !options[:quiet]
283
+ print_green_success "Added cypher"
284
+ # list([])
285
+ cypher_item = json_response['cypher']
286
+ get([cypher_item['id']])
287
+ end
288
+ return 0
289
+ rescue RestClient::Exception => e
290
+ print_rest_exception(e, options)
291
+ exit 1
292
+ end
293
+ end
294
+
295
+ # def update(args)
296
+ # end
297
+
298
+ # def decrypt(args)
299
+ # end
300
+
301
+ def remove(args)
302
+ options = {}
303
+ optparse = Morpheus::Cli::OptionParser.new do |opts|
304
+ opts.banner = subcommand_usage("[id]")
305
+ build_common_options(opts, options, [:account, :auto_confirm, :json, :dry_run, :quiet, :remote])
306
+ opts.footer = "Delete a cypher." + "\n" +
307
+ "[id] is required. This is the id or key of a cypher."
308
+ end
309
+ optparse.parse!(args)
310
+
311
+ if args.count != 1
312
+ print_error Morpheus::Terminal.angry_prompt
313
+ puts_error "wrong number of arguments, expected 1 and got #{args.count}\n#{optparse}"
314
+ return 1
315
+ end
316
+
317
+ connect(options)
318
+ begin
319
+ cypher_item = find_cypher_by_name_or_id(args[0])
320
+ return 1 if cypher_item.nil?
321
+ unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the cypher #{cypher_item['itemKey']}?")
322
+ return 9, "aborted command"
323
+ end
324
+ if options[:dry_run]
325
+ print_dry_run @cypher_interface.dry.destroy(cypher_item["id"])
326
+ return
327
+ end
328
+ json_response = @cypher_interface.destroy(cypher_item["id"])
329
+ if options[:json]
330
+ print JSON.pretty_generate(json_response)
331
+ print "\n"
332
+ elsif !options[:quiet]
333
+ print_green_success "Deleted cypher #{cypher_item['itemKey']}"
334
+ # list([])
335
+ end
336
+ return 0
337
+ rescue RestClient::Exception => e
338
+ print_rest_exception(e, options)
339
+ return 1
340
+ end
341
+ end
342
+
343
+ private
344
+
345
+ def find_cypher_by_name_or_id(val)
346
+ if val.to_s =~ /\A\d{1,}\Z/
347
+ return find_cypher_by_id(val)
348
+ else
349
+ return find_cypher_by_name(val)
350
+ end
351
+ end
352
+
353
+ def find_cypher_by_id(id)
354
+ begin
355
+
356
+ json_response = @cypher_interface.get(id.to_i)
357
+ return json_response['cypher']
358
+ rescue RestClient::Exception => e
359
+ if e.response && e.response.code == 404
360
+ print_red_alert "Cypher not found by id #{id}"
361
+ return nil
362
+ else
363
+ raise e
364
+ end
365
+ end
366
+ end
367
+
368
+ def find_cypher_by_name(name)
369
+ # api supports name as alias for itemKey
370
+ json_response = @cypher_interface.list({name: name.to_s})
371
+
372
+ cypher_items = json_response['cyphers']
373
+ if cypher_items.empty?
374
+ print_red_alert "Cypher not found by name #{name}"
375
+ return nil
376
+ elsif cypher_items.size > 1
377
+ print_red_alert "#{cypher_items.size} cyphers found by name #{name}"
378
+ rows = cypher_items.collect do |cypher_item|
379
+ {id: cypher_item['id'], name: cypher_item['name']}
380
+ end
381
+ print red
382
+ print as_pretty_table(rows, [:id, :name])
383
+ print reset,"\n"
384
+ return nil
385
+ else
386
+ return cypher_items[0]
387
+ end
388
+ end
389
+
390
+ def cypher_key_help
391
+ """
392
+ Keys can have different behaviors depending on the specified mountpoint.
393
+ Available Mountpoints:
394
+ password - Generates a secure password of specified character length in the key pattern (or 15) with symbols, numbers, upper case, and lower case letters (i.e. password/15/mypass generates a 15 character password).
395
+ tfvars - This is a module to store a tfvars file for terraform.
396
+ secret - This is the standard secret module that stores a key/value in encrypted form.
397
+ uuid - Returns a new UUID by key name when requested and stores the generated UUID by key name for a given lease timeout period.
398
+ key - Generates a Base 64 encoded AES Key of specified bit length in the key pattern (i.e. key/128/mykey generates a 128-bit key)"""
399
+ end
400
+
401
+ def cypher_lease_help
402
+ """
403
+ Lease time in MS (defaults to 32 days)
404
+ Quick MS Time Reference:
405
+ Day: 86400000
406
+ Week: 604800000
407
+ Month (30 days): 2592000000
408
+ Year: 31536000000"""
409
+ end
410
+
411
+ end
412
+