morpheus-cli 2.12.5 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Dockerfile +5 -0
- data/lib/morpheus/api/api_client.rb +15 -30
- data/lib/morpheus/api/app_templates_interface.rb +34 -7
- data/lib/morpheus/api/apps_interface.rb +20 -1
- data/lib/morpheus/api/archive_buckets_interface.rb +124 -0
- data/lib/morpheus/api/archive_files_interface.rb +182 -0
- data/lib/morpheus/api/{network_pools_interface.rb → image_builder_boot_scripts_interface.rb} +6 -6
- data/lib/morpheus/api/{policies_interface.rb → image_builder_image_builds_interface.rb} +20 -15
- data/lib/morpheus/api/image_builder_interface.rb +26 -0
- data/lib/morpheus/api/{network_proxies_interface.rb → image_builder_preseed_scripts_interface.rb} +6 -6
- data/lib/morpheus/cli.rb +10 -9
- data/lib/morpheus/cli/alias_command.rb +10 -9
- data/lib/morpheus/cli/app_templates.rb +1566 -457
- data/lib/morpheus/cli/apps.rb +284 -108
- data/lib/morpheus/cli/archives_command.rb +2184 -0
- data/lib/morpheus/cli/boot_scripts_command.rb +382 -0
- data/lib/morpheus/cli/cli_command.rb +9 -35
- data/lib/morpheus/cli/error_handler.rb +2 -0
- data/lib/morpheus/cli/hosts.rb +15 -3
- data/lib/morpheus/cli/image_builder_command.rb +1208 -0
- data/lib/morpheus/cli/instances.rb +118 -47
- data/lib/morpheus/cli/man_command.rb +27 -24
- data/lib/morpheus/cli/mixins/print_helper.rb +19 -5
- data/lib/morpheus/cli/mixins/provisioning_helper.rb +20 -20
- data/lib/morpheus/cli/option_types.rb +45 -14
- data/lib/morpheus/cli/preseed_scripts_command.rb +381 -0
- data/lib/morpheus/cli/remote.rb +1 -0
- data/lib/morpheus/cli/roles.rb +2 -2
- data/lib/morpheus/cli/shell.rb +3 -2
- data/lib/morpheus/cli/version.rb +1 -1
- data/lib/morpheus/ext/hash.rb +22 -0
- data/lib/morpheus/formatters.rb +33 -0
- data/lib/morpheus/terminal.rb +1 -1
- metadata +13 -21
- data/lib/morpheus/api/cloud_policies_interface.rb +0 -47
- data/lib/morpheus/api/group_policies_interface.rb +0 -47
- data/lib/morpheus/api/network_domains_interface.rb +0 -47
- data/lib/morpheus/api/network_groups_interface.rb +0 -47
- data/lib/morpheus/api/network_pool_servers_interface.rb +0 -47
- data/lib/morpheus/api/network_services_interface.rb +0 -47
- data/lib/morpheus/api/networks_interface.rb +0 -54
- data/lib/morpheus/cli/network_domains_command.rb +0 -571
- data/lib/morpheus/cli/network_groups_command.rb +0 -602
- data/lib/morpheus/cli/network_pool_servers_command.rb +0 -430
- data/lib/morpheus/cli/network_pools_command.rb +0 -495
- data/lib/morpheus/cli/network_proxies_command.rb +0 -594
- data/lib/morpheus/cli/network_services_command.rb +0 -148
- data/lib/morpheus/cli/networks_command.rb +0 -855
- data/lib/morpheus/cli/policies_command.rb +0 -847
- data/scripts/generate_morpheus_commands_help.morpheus +0 -1313
@@ -0,0 +1,2184 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'json'
|
3
|
+
require 'yaml'
|
4
|
+
require 'rest_client'
|
5
|
+
require 'optparse'
|
6
|
+
require 'filesize'
|
7
|
+
require 'table_print'
|
8
|
+
require 'morpheus/cli/cli_command'
|
9
|
+
|
10
|
+
class Morpheus::Cli::ArchivesCommand
|
11
|
+
include Morpheus::Cli::CliCommand
|
12
|
+
include Morpheus::Cli::ProvisioningHelper
|
13
|
+
|
14
|
+
set_command_name :archives
|
15
|
+
|
16
|
+
# bucket commands
|
17
|
+
# register_subcommands :list_buckets, :get_bucket, :add_bucket, :update_bucket, :remove_bucket
|
18
|
+
register_subcommands :'list' => :list_buckets
|
19
|
+
register_subcommands :'get' => :get_bucket
|
20
|
+
register_subcommands :'add' => :add_bucket
|
21
|
+
register_subcommands :'update' => :update_bucket
|
22
|
+
register_subcommands :'remove' => :remove_bucket
|
23
|
+
register_subcommands :'download-bucket' => :download_bucket_zip
|
24
|
+
# file commands
|
25
|
+
register_subcommands :'list-files' => :list_files
|
26
|
+
register_subcommands :'ls' => :ls
|
27
|
+
register_subcommands :'file' => :get_file
|
28
|
+
register_subcommands :'file-history' => :file_history
|
29
|
+
register_subcommands :'file-links' => :file_links
|
30
|
+
# register_subcommands :'history' => :file_history
|
31
|
+
register_subcommands :'upload' => :upload_file
|
32
|
+
register_subcommands :'download' => :download_file
|
33
|
+
register_subcommands :'read' => :read_file
|
34
|
+
register_subcommands :'remove-file' => :remove_file
|
35
|
+
register_subcommands :'rm' => :remove_file
|
36
|
+
|
37
|
+
# file link commands
|
38
|
+
register_subcommands :'add-file-link' => :add_file_link
|
39
|
+
# register_subcommands :'get-file-link' => :get_file_link
|
40
|
+
register_subcommands :'remove-file-link' => :remove_file_link
|
41
|
+
register_subcommands :'download-link' => :download_file_link
|
42
|
+
|
43
|
+
|
44
|
+
# set_default_subcommand :list
|
45
|
+
|
46
|
+
def initialize()
|
47
|
+
# @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
|
48
|
+
end
|
49
|
+
|
50
|
+
def connect(opts)
|
51
|
+
@api_client = establish_remote_appliance_connection(opts)
|
52
|
+
@archive_buckets_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).archive_buckets
|
53
|
+
@archive_files_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).archive_files
|
54
|
+
@options_interface = Morpheus::APIClient.new(@access_token,nil,nil, @appliance_url).options
|
55
|
+
# @active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
|
56
|
+
end
|
57
|
+
|
58
|
+
def handle(args)
|
59
|
+
handle_subcommand(args)
|
60
|
+
end
|
61
|
+
|
62
|
+
def list_buckets(args)
|
63
|
+
options = {}
|
64
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
65
|
+
opts.banner = subcommand_usage()
|
66
|
+
build_common_options(opts, options, [:list, :json, :dry_run])
|
67
|
+
opts.footer = "List archive buckets."
|
68
|
+
end
|
69
|
+
optparse.parse!(args)
|
70
|
+
if args.count != 0
|
71
|
+
raise_command_error "#{command_name} list expects 0 arguments\n#{optparse}"
|
72
|
+
end
|
73
|
+
connect(options)
|
74
|
+
begin
|
75
|
+
params = {}
|
76
|
+
[:phrase, :offset, :max, :sort, :direction].each do |k|
|
77
|
+
params[k] = options[k] unless options[k].nil?
|
78
|
+
end
|
79
|
+
|
80
|
+
if options[:dry_run]
|
81
|
+
print_dry_run @archive_buckets_interface.dry.list(params)
|
82
|
+
return
|
83
|
+
end
|
84
|
+
|
85
|
+
json_response = @archive_buckets_interface.list(params)
|
86
|
+
if options[:json]
|
87
|
+
print JSON.pretty_generate(json_response)
|
88
|
+
print "\n"
|
89
|
+
return
|
90
|
+
end
|
91
|
+
archive_buckets = json_response['archiveBuckets']
|
92
|
+
title = "Morpheus Archive Buckets"
|
93
|
+
subtitles = []
|
94
|
+
# if group
|
95
|
+
# subtitles << "Group: #{group['name']}".strip
|
96
|
+
# end
|
97
|
+
# if cloud
|
98
|
+
# subtitles << "Cloud: #{cloud['name']}".strip
|
99
|
+
# end
|
100
|
+
if params[:phrase]
|
101
|
+
subtitles << "Search: #{params[:phrase]}".strip
|
102
|
+
end
|
103
|
+
print_h1 title, subtitles
|
104
|
+
if archive_buckets.empty?
|
105
|
+
print cyan,"No archive buckets found.",reset,"\n"
|
106
|
+
else
|
107
|
+
rows = archive_buckets.collect {|archive_bucket|
|
108
|
+
row = {
|
109
|
+
id: archive_bucket['id'],
|
110
|
+
name: archive_bucket['name'],
|
111
|
+
description: archive_bucket['description'],
|
112
|
+
storageProvider: archive_bucket['storageProvider'] ? archive_bucket['storageProvider']['name'] : 'N/A',
|
113
|
+
fileCount: archive_bucket['fileCount'],
|
114
|
+
# createdBy: archive_bucket['createdBy'] ? archive_bucket['createdBy']['username'] : '',
|
115
|
+
size: format_bytes(archive_bucket['rawSize']),
|
116
|
+
owner: archive_bucket['owner'] ? archive_bucket['owner']['name'] : '',
|
117
|
+
tenants: archive_bucket['accounts'] ? archive_bucket['accounts'].collect {|it| it['name'] }.join(', ') : '',
|
118
|
+
visibility: archive_bucket['visibility'] ? archive_bucket['visibility'].capitalize() : '',
|
119
|
+
isPublic: archive_bucket['isPublic'] ? 'Yes' : 'No'
|
120
|
+
}
|
121
|
+
row
|
122
|
+
}
|
123
|
+
columns = [
|
124
|
+
:id,
|
125
|
+
:name,
|
126
|
+
{:storageProvider => {label: 'Storage'.upcase}},
|
127
|
+
{:fileCount => {label: '# Files'.upcase}},
|
128
|
+
:size,
|
129
|
+
:owner,
|
130
|
+
:tenants,
|
131
|
+
:visibility,
|
132
|
+
{:isPublic => {label: 'Public URL'.upcase}}
|
133
|
+
]
|
134
|
+
term_width = current_terminal_width()
|
135
|
+
# if term_width > 170
|
136
|
+
# columns += [:cpu, :memory, :storage]
|
137
|
+
# end
|
138
|
+
# custom pretty table columns ...
|
139
|
+
if options[:include_fields]
|
140
|
+
columns = options[:include_fields]
|
141
|
+
end
|
142
|
+
print cyan
|
143
|
+
print as_pretty_table(rows, columns, options)
|
144
|
+
print reset
|
145
|
+
print_results_pagination(json_response, {:label => "bucket", :n_label => "buckets"})
|
146
|
+
print reset,"\n"
|
147
|
+
end
|
148
|
+
return 0
|
149
|
+
rescue RestClient::Exception => e
|
150
|
+
print_rest_exception(e, options)
|
151
|
+
exit 1
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def get_bucket(args)
|
156
|
+
options = {}
|
157
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
158
|
+
opts.banner = subcommand_usage("[bucket:/path]")
|
159
|
+
build_common_options(opts, options, [:json, :dry_run])
|
160
|
+
opts.footer = "Display archive bucket details and files. " +
|
161
|
+
"\nThe [bucket] component of the argument is the name or id of an archive bucket." +
|
162
|
+
"\nThe [:/path] component is optional and can be used to display files under a sub-directory."
|
163
|
+
end
|
164
|
+
optparse.parse!(args)
|
165
|
+
if args.count != 1
|
166
|
+
print_error Morpheus::Terminal.angry_prompt
|
167
|
+
puts_error "#{command_name} get expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
168
|
+
return 1
|
169
|
+
end
|
170
|
+
bucket_id, search_file_path = parse_bucket_id_and_file_path(args[0])
|
171
|
+
connect(options)
|
172
|
+
begin
|
173
|
+
if options[:dry_run]
|
174
|
+
if args[0].to_s =~ /\A\d{1,}\Z/
|
175
|
+
print_dry_run @archive_buckets_interface.dry.get(bucket_id.to_i)
|
176
|
+
else
|
177
|
+
print_dry_run @archive_buckets_interface.dry.list({name:bucket_id})
|
178
|
+
end
|
179
|
+
return
|
180
|
+
end
|
181
|
+
archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
|
182
|
+
return 1 if archive_bucket.nil?
|
183
|
+
json_response = {'archiveBucket' => archive_bucket} # skip redundant request
|
184
|
+
# json_response = @archive_buckets_interface.get(archive_bucket['id'])
|
185
|
+
archive_bucket = json_response['archiveBucket']
|
186
|
+
if options[:json]
|
187
|
+
print JSON.pretty_generate(json_response)
|
188
|
+
return
|
189
|
+
end
|
190
|
+
subtitles = []
|
191
|
+
if search_file_path != "/"
|
192
|
+
subtitles << "Path: #{search_file_path}"
|
193
|
+
end
|
194
|
+
print_h1 "Archive Bucket Details", subtitles
|
195
|
+
print cyan
|
196
|
+
|
197
|
+
description_cols = {
|
198
|
+
"ID" => 'id',
|
199
|
+
"Name" => 'name',
|
200
|
+
"Description" => 'description',
|
201
|
+
# "Created By" => lambda {|it| it['createdBy'] ? it['createdBy']['username'] : '' },
|
202
|
+
"Owner" => lambda {|it| it['owner'] ? it['owner']['name'] : '' },
|
203
|
+
"Tenants" => lambda {|it| it['accounts'] ? it['accounts'].collect {|acnt| acnt['name']}.join(', ') : '' },
|
204
|
+
"Visibility" => lambda {|it| it['visibility'] ? it['visibility'].capitalize() : '' },
|
205
|
+
"Public URL" => lambda {|it| it['isPublic'] ? 'Yes' : 'No' },
|
206
|
+
"Storage" => lambda {|it| it['storageProvider'] ? it['storageProvider']['name'] : '' },
|
207
|
+
"# Files" => lambda {|it| it['fileCount'] },
|
208
|
+
"Size" => lambda {|it| format_bytes(it['rawSize']) },
|
209
|
+
"Date Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
210
|
+
"Last Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
|
211
|
+
}
|
212
|
+
print_description_list(description_cols, archive_bucket)
|
213
|
+
|
214
|
+
# show files
|
215
|
+
# search_file_path = "/"
|
216
|
+
# if args[1]
|
217
|
+
# search_file_path = args[1]
|
218
|
+
# end
|
219
|
+
# if search_file_path[0].chr != "/"
|
220
|
+
# search_file_path = "/" + search_file_path
|
221
|
+
# end
|
222
|
+
# print_h2 "Path: #{search_file_path}"
|
223
|
+
print "\n"
|
224
|
+
archive_files_json_response = @archive_buckets_interface.list_files(archive_bucket['name'], search_file_path)
|
225
|
+
archive_files = archive_files_json_response['archiveFiles']
|
226
|
+
if archive_files && archive_files.size > 0
|
227
|
+
# archive_files.each do |archive_file|
|
228
|
+
# puts " = #{archive_file['name']}"
|
229
|
+
# end
|
230
|
+
print_archive_files_table(archive_files)
|
231
|
+
else
|
232
|
+
if search_file_path.empty? || search_file_path == "/"
|
233
|
+
puts "This archive bucket has no files."
|
234
|
+
else
|
235
|
+
puts "No files found for path #{search_file_path}"
|
236
|
+
end
|
237
|
+
end
|
238
|
+
print cyan
|
239
|
+
|
240
|
+
print reset,"\n"
|
241
|
+
|
242
|
+
rescue RestClient::Exception => e
|
243
|
+
print_rest_exception(e, options)
|
244
|
+
return 1
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def add_bucket(args)
|
249
|
+
options = {}
|
250
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
251
|
+
opts.banner = subcommand_usage("[options]")
|
252
|
+
opts.on('--name VALUE', String, "Name") do |val|
|
253
|
+
options['name'] = val
|
254
|
+
end
|
255
|
+
opts.on('--description VALUE', String, "Description") do |val|
|
256
|
+
options['description'] = val
|
257
|
+
end
|
258
|
+
opts.on('--storageProvider VALUE', String, "Storage Provider ID") do |val|
|
259
|
+
options['storageProvider'] = val.to_s
|
260
|
+
end
|
261
|
+
opts.on('--payload JSON', String, "JSON Payload") do |val|
|
262
|
+
options['payload'] = JSON.parse(val.to_s)
|
263
|
+
end
|
264
|
+
opts.on('--payload-file FILE', String, "JSON Payload from a local file") do |val|
|
265
|
+
payload_file = val.to_s
|
266
|
+
options['payload'] = JSON.parse(File.read(payload_file))
|
267
|
+
end
|
268
|
+
|
269
|
+
opts.on('--visibility [private|public]', String, "Visibility determines if read access is restricted to the specified Tenants (Private) or all tenants (Public).") do |val|
|
270
|
+
options['visibility'] = val.to_s
|
271
|
+
end
|
272
|
+
opts.on('--accounts LIST', String, "Tenant Accounts (comma separated ids)") do |val|
|
273
|
+
# uh don't put commas or leading/trailing spaces in script names pl
|
274
|
+
options['accounts'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
|
275
|
+
end
|
276
|
+
opts.on('--isPublic [on|off]', String, "Enabling Public URL allows files to be downloaded without any authentication.") do |val|
|
277
|
+
options['isPublic'] = (val.to_s == 'on' || val.to_s == 'true')
|
278
|
+
end
|
279
|
+
build_common_options(opts, options, [:options, :json, :dry_run, :quiet])
|
280
|
+
opts.footer = "Create a new archive bucket."
|
281
|
+
end
|
282
|
+
optparse.parse!(args)
|
283
|
+
connect(options)
|
284
|
+
begin
|
285
|
+
options.merge!(options[:options]) if options[:options] # so -O var= works..
|
286
|
+
|
287
|
+
# use the -g GROUP or active group by default
|
288
|
+
# options['group'] ||= @active_group_id
|
289
|
+
|
290
|
+
# support first arg as name instead of --name
|
291
|
+
if args[0] && !options['name']
|
292
|
+
options['name'] = args[0]
|
293
|
+
end
|
294
|
+
|
295
|
+
archive_bucket_payload = prompt_new_archive_bucket(options)
|
296
|
+
return 1 if !archive_bucket_payload
|
297
|
+
payload = {'archiveBucket' => archive_bucket_payload}
|
298
|
+
|
299
|
+
if options[:dry_run]
|
300
|
+
print_dry_run @archive_buckets_interface.dry.create(payload)
|
301
|
+
return
|
302
|
+
end
|
303
|
+
json_response = @archive_buckets_interface.create(payload)
|
304
|
+
if options[:json]
|
305
|
+
print JSON.pretty_generate(json_response)
|
306
|
+
print "\n"
|
307
|
+
elsif !options[:quiet]
|
308
|
+
new_archive_bucket = json_response['archiveBucket']
|
309
|
+
print_green_success "Added archive bucket #{new_archive_bucket['name']}"
|
310
|
+
get_bucket([new_archive_bucket['id']])
|
311
|
+
# list([])
|
312
|
+
end
|
313
|
+
|
314
|
+
rescue RestClient::Exception => e
|
315
|
+
print_rest_exception(e, options)
|
316
|
+
return 1
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def update_bucket(args)
|
321
|
+
options = {}
|
322
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
323
|
+
opts.banner = subcommand_usage("[bucket] [options]")
|
324
|
+
opts.on('--name VALUE', String, "Name") do |val|
|
325
|
+
options['name'] = val
|
326
|
+
end
|
327
|
+
opts.on('--description VALUE', String, "Description") do |val|
|
328
|
+
options['description'] = val
|
329
|
+
end
|
330
|
+
# storage provider cannot be changed
|
331
|
+
# opts.on('--storageProvider VALUE', String, "Storage Provider ID") do |val|
|
332
|
+
# options['storageProvider'] = val.to_s
|
333
|
+
# end
|
334
|
+
opts.on('--payload JSON', String, "JSON Payload") do |val|
|
335
|
+
options['payload'] = JSON.parse(val.to_s)
|
336
|
+
end
|
337
|
+
opts.on('--payload-file FILE', String, "JSON Payload from a local file") do |val|
|
338
|
+
payload_file = val.to_s
|
339
|
+
options['payload'] = JSON.parse(File.read(payload_file))
|
340
|
+
end
|
341
|
+
|
342
|
+
opts.on('--visibility [private|public]', String, "Visibility determines if read access is restricted to the specified Tenants (Private) or all tenants (Public).") do |val|
|
343
|
+
options['visibility'] = val.to_s
|
344
|
+
end
|
345
|
+
opts.on('--accounts LIST', String, "Tenant Accounts (comma separated ids)") do |val|
|
346
|
+
# uh don't put commas or leading/trailing spaces in script names pl
|
347
|
+
options['accounts'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
|
348
|
+
end
|
349
|
+
opts.on('--isPublic [on|off]', String, "Enabling Public URL allows files to be downloaded without any authentication.") do |val|
|
350
|
+
options['isPublic'] = (val.to_s == 'on' || val.to_s == 'true')
|
351
|
+
end
|
352
|
+
build_common_options(opts, options, [:options, :json, :dry_run, :quiet])
|
353
|
+
opts.footer = "Update an existing archive bucket."
|
354
|
+
end
|
355
|
+
optparse.parse!(args)
|
356
|
+
if args.count < 1
|
357
|
+
puts optparse
|
358
|
+
return 1
|
359
|
+
end
|
360
|
+
connect(options)
|
361
|
+
|
362
|
+
begin
|
363
|
+
archive_bucket = find_archive_bucket_by_name_or_id(args[0])
|
364
|
+
|
365
|
+
archive_bucket_payload = prompt_edit_archive_bucket(archive_bucket, options)
|
366
|
+
return 1 if !archive_bucket_payload
|
367
|
+
payload = {'archiveBucket' => archive_bucket_payload}
|
368
|
+
|
369
|
+
if options[:dry_run]
|
370
|
+
print_dry_run @archive_buckets_interface.dry.update(archive_bucket["id"], payload)
|
371
|
+
return
|
372
|
+
end
|
373
|
+
|
374
|
+
json_response = @archive_buckets_interface.update(archive_bucket["id"], payload)
|
375
|
+
if options[:json]
|
376
|
+
print JSON.pretty_generate(json_response)
|
377
|
+
print "\n"
|
378
|
+
elsif !options[:quiet]
|
379
|
+
print_green_success "Updated archive bucket #{archive_bucket['name']}"
|
380
|
+
get([archive_bucket['id']])
|
381
|
+
end
|
382
|
+
return 0
|
383
|
+
rescue RestClient::Exception => e
|
384
|
+
print_rest_exception(e, options)
|
385
|
+
return 1
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
def remove_bucket(args)
|
390
|
+
full_command_string = "#{command_name} remove #{args.join(' ')}".strip
|
391
|
+
options = {}
|
392
|
+
query_params = {}
|
393
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
394
|
+
opts.banner = subcommand_usage("[bucket]")
|
395
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run])
|
396
|
+
end
|
397
|
+
optparse.parse!(args)
|
398
|
+
|
399
|
+
if args.count < 1
|
400
|
+
print_error Morpheus::Terminal.angry_prompt
|
401
|
+
puts_error "#{command_name} remove expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
402
|
+
return 1
|
403
|
+
end
|
404
|
+
bucket_id = args[0]
|
405
|
+
connect(options)
|
406
|
+
begin
|
407
|
+
# archive_bucket = find_archive_bucket_by_name_or_id(args[0])
|
408
|
+
json_response = @archive_buckets_interface.get(bucket_id, {})
|
409
|
+
archive_bucket = json_response['archiveBucket']
|
410
|
+
is_owner = json_response['isOwner']
|
411
|
+
return 1 if archive_bucket.nil?
|
412
|
+
if is_owner == false
|
413
|
+
print_red_alert "You must be the owner of archive bucket to remove it."
|
414
|
+
return 3
|
415
|
+
end
|
416
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the archive bucket: #{archive_bucket['name']}?")
|
417
|
+
return 9, "aborted command"
|
418
|
+
end
|
419
|
+
if options[:dry_run]
|
420
|
+
print_dry_run @archive_buckets_interface.dry.destroy(archive_bucket['id'], query_params), full_command_string
|
421
|
+
return 0
|
422
|
+
end
|
423
|
+
json_response = @archive_buckets_interface.destroy(archive_bucket['id'], query_params)
|
424
|
+
if options[:json]
|
425
|
+
print JSON.pretty_generate(json_response)
|
426
|
+
print "\n"
|
427
|
+
else
|
428
|
+
print_green_success "Removed archive bucket #{archive_bucket['name']}"
|
429
|
+
# list([])
|
430
|
+
end
|
431
|
+
return 0
|
432
|
+
rescue RestClient::Exception => e
|
433
|
+
print_rest_exception(e, options)
|
434
|
+
return 1
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
def upload_file(args)
|
439
|
+
options = {}
|
440
|
+
query_params = {}
|
441
|
+
do_recursive = false
|
442
|
+
ignore_regexp = nil
|
443
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
444
|
+
opts.banner = subcommand_usage("[local-file] [bucket:/path]")
|
445
|
+
# opts.on('--filename FILEPATH', String, "Remote file path for the file or folder being uploaded, this is an alternative to [remote-file-path]." ) do |val|
|
446
|
+
# options['type'] = val
|
447
|
+
# end
|
448
|
+
opts.on( '-R', '--recursive', "Upload a directory and all of its files. This must be passed if [local-file] is a directory." ) do
|
449
|
+
do_recursive = true
|
450
|
+
end
|
451
|
+
opts.on('--ignore-files PATTERN', String, "Pattern of files to be ignored when uploading a directory." ) do |val|
|
452
|
+
ignore_regexp = /#{Regexp.escape(val)}/
|
453
|
+
end
|
454
|
+
opts.footer = "Upload a local file or folder to an archive bucket. " +
|
455
|
+
"\nThe first argument [local-file] should be the path of a local file or directory." +
|
456
|
+
"\nThe second argument [bucket:/path] should contain the bucket name." +
|
457
|
+
"\nThe [:/path] component is optional and can be used to specify the destination of the uploaded file or folder." +
|
458
|
+
"\nThe default destination is the same name as the [local-file], under the root bucket directory '/'. " +
|
459
|
+
"\nThis will overwrite any existing remote files that match the destination /path."
|
460
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run])
|
461
|
+
end
|
462
|
+
optparse.parse!(args)
|
463
|
+
|
464
|
+
if args.count != 2
|
465
|
+
print_error Morpheus::Terminal.angry_prompt
|
466
|
+
puts_error "#{command_name} upload expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
467
|
+
return 1
|
468
|
+
end
|
469
|
+
# validate local file path
|
470
|
+
local_file_path = File.expand_path(args[0].squeeze('/'))
|
471
|
+
if local_file_path == "" || local_file_path == "/" || local_file_path == "."
|
472
|
+
print_error Morpheus::Terminal.angry_prompt
|
473
|
+
puts_error "#{command_name} missing argument: [local-file]\n#{optparse}"
|
474
|
+
return 1
|
475
|
+
end
|
476
|
+
if !File.exists?(local_file_path)
|
477
|
+
print_error Morpheus::Terminal.angry_prompt
|
478
|
+
puts_error "#{command_name} bad argument: [local-file]\nFile '#{local_file_path}' was not found.\n#{optparse}"
|
479
|
+
return 1
|
480
|
+
end
|
481
|
+
|
482
|
+
# validate bucket:/path
|
483
|
+
bucket_id, remote_file_path = parse_bucket_id_and_file_path(args[1])
|
484
|
+
|
485
|
+
# if local_file_path.include?('../') # || options[:yes]
|
486
|
+
# raise_command_error "Sorry, you may not use relative paths in your local filepath."
|
487
|
+
# end
|
488
|
+
|
489
|
+
# validate bucket name (or id)
|
490
|
+
if !bucket_id
|
491
|
+
print_error Morpheus::Terminal.angry_prompt
|
492
|
+
puts_error "#{command_name} missing argument: [bucket]\n#{optparse}"
|
493
|
+
return 1
|
494
|
+
end
|
495
|
+
|
496
|
+
# strip leading slash of remote name
|
497
|
+
# if remote_file_path[0].chr == "/"
|
498
|
+
# remote_file_path = remote_file_path[1..-1]
|
499
|
+
# end
|
500
|
+
|
501
|
+
if remote_file_path.include?('./') # || options[:yes]
|
502
|
+
raise_command_error "Sorry, you may not use relative paths in your remote filepath."
|
503
|
+
end
|
504
|
+
|
505
|
+
# if !options[:yes]
|
506
|
+
scary_local_paths = ["/", "/root", "C:\\"]
|
507
|
+
if scary_local_paths.include?(local_file_path)
|
508
|
+
unless Morpheus::Cli::OptionTypes.confirm("Are you sure you want to upload all the files in local directory '#{local_file_path}' !?")
|
509
|
+
return 9, "aborted command"
|
510
|
+
end
|
511
|
+
end
|
512
|
+
# end
|
513
|
+
|
514
|
+
connect(options)
|
515
|
+
begin
|
516
|
+
archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
|
517
|
+
return 1 if archive_bucket.nil?
|
518
|
+
|
519
|
+
# how many files we dealing with?
|
520
|
+
files_to_upload = []
|
521
|
+
if File.directory?(local_file_path)
|
522
|
+
# upload directory
|
523
|
+
if !do_recursive
|
524
|
+
print_error Morpheus::Terminal.angry_prompt
|
525
|
+
puts_error "bad argument: '#{local_file_path}' is a directory. Use -R or --recursive to upload a directory.\n#{optparse}"
|
526
|
+
return 1
|
527
|
+
end
|
528
|
+
found_files = Dir.glob("#{local_file_path}/**/*")
|
529
|
+
# note: api call for directories is not needed
|
530
|
+
found_files = found_files.select {|file| File.file?(file) }
|
531
|
+
if ignore_regexp
|
532
|
+
found_files = found_files.reject {|it| it =~ ignore_regexp}
|
533
|
+
end
|
534
|
+
files_to_upload = found_files
|
535
|
+
|
536
|
+
if files_to_upload.size == 0
|
537
|
+
print_error Morpheus::Terminal.angry_prompt
|
538
|
+
puts_error "bad argument: Local directory '#{local_file_path}' contains 0 files."
|
539
|
+
return 1
|
540
|
+
end
|
541
|
+
|
542
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to upload directory #{local_file_path} (#{files_to_upload.size} files) to #{archive_bucket['name']}:#{remote_file_path}?")
|
543
|
+
return 9, "aborted command"
|
544
|
+
end
|
545
|
+
|
546
|
+
if !options[:yes]
|
547
|
+
if files_to_upload.size > 100
|
548
|
+
unless Morpheus::Cli::OptionTypes.confirm("Are you REALLY sure you want to upload #{files_to_upload.size} files ?")
|
549
|
+
return 9, "aborted command"
|
550
|
+
end
|
551
|
+
end
|
552
|
+
end
|
553
|
+
|
554
|
+
# local_dirname = File.dirname(local_file_path)
|
555
|
+
# local_basename = File.basename(local_file_path)
|
556
|
+
upload_file_list = []
|
557
|
+
files_to_upload.each do |file|
|
558
|
+
destination = file.sub(local_file_path, (remote_file_path || "")).squeeze('/')
|
559
|
+
upload_file_list << {file: file, destination: destination}
|
560
|
+
end
|
561
|
+
|
562
|
+
if options[:dry_run]
|
563
|
+
print_h1 "DRY RUN"
|
564
|
+
print "\n",cyan, bold, "Uploading #{upload_file_list.size} Files...", reset, "\n"
|
565
|
+
upload_file_list.each do |obj|
|
566
|
+
file, destination = obj[:file], obj[:destination]
|
567
|
+
#print_dry_run @archive_buckets_interface.dry.upload_file(bucket_id, file, destination)
|
568
|
+
print cyan,bold, " - Uploading #{file} to #{bucket_id}:#{destination}", reset, "\n"
|
569
|
+
end
|
570
|
+
return 0
|
571
|
+
end
|
572
|
+
|
573
|
+
print "\n",cyan, bold, "Uploading #{upload_file_list.size} Files...", reset, "\n"
|
574
|
+
bad_upload_responses = []
|
575
|
+
upload_file_list.each do |obj|
|
576
|
+
file, destination = obj[:file], obj[:destination]
|
577
|
+
print cyan,bold, " - Uploading #{file} to #{bucket_id}:#{destination}", reset
|
578
|
+
upload_response = @archive_buckets_interface.upload_file(bucket_id, file, destination)
|
579
|
+
if upload_response['success']
|
580
|
+
print bold," #{green}SUCCESS#{reset}"
|
581
|
+
else
|
582
|
+
print bold," #{red}ERROR#{reset}"
|
583
|
+
if upload_response['msg']
|
584
|
+
bad_upload_responses << upload_response
|
585
|
+
print " #{upload_response['msg']}#{reset}"
|
586
|
+
end
|
587
|
+
end
|
588
|
+
print "\n"
|
589
|
+
end
|
590
|
+
if bad_upload_responses.size > 0
|
591
|
+
print cyan, bold, "Completed Upload of #{upload_file_list.size} Files. #{red}#{bad_upload_responses.size} Errors!", reset, "\n"
|
592
|
+
else
|
593
|
+
print cyan, bold, "Completed Upload of #{upload_file_list.size} Files!", reset, "\n"
|
594
|
+
end
|
595
|
+
|
596
|
+
else
|
597
|
+
|
598
|
+
# upload file
|
599
|
+
if !File.exists?(local_file_path) && !File.file?(local_file_path)
|
600
|
+
print_error Morpheus::Terminal.angry_prompt
|
601
|
+
puts_error "#{command_name} bad argument: [local-file]\nFile '#{local_file_path}' was not found.\n#{optparse}"
|
602
|
+
return 1
|
603
|
+
end
|
604
|
+
|
605
|
+
# local_dirname = File.dirname(local_file_path)
|
606
|
+
# local_basename = File.basename(local_file_path)
|
607
|
+
|
608
|
+
file = local_file_path
|
609
|
+
destination = File.basename(file)
|
610
|
+
if remote_file_path[-1].chr == "/"
|
611
|
+
# work like `cp`, and place into the directory
|
612
|
+
destination = remote_file_path + File.basename(file)
|
613
|
+
elsif remote_file_path
|
614
|
+
# renaming file
|
615
|
+
destination = remote_file_path
|
616
|
+
end
|
617
|
+
destination = destination.squeeze('/')
|
618
|
+
|
619
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to upload #{local_file_path} to #{archive_bucket['name']}:#{destination}?")
|
620
|
+
return 9, "aborted command"
|
621
|
+
end
|
622
|
+
|
623
|
+
if options[:dry_run]
|
624
|
+
print_h1 "DRY RUN"
|
625
|
+
#print_dry_run @archive_buckets_interface.dry.upload_file(bucket_id, file, destination)
|
626
|
+
print cyan,bold, " - Uploading #{file} to #{bucket_id}:#{destination} SKIPPED", reset, "\n"
|
627
|
+
return 0
|
628
|
+
end
|
629
|
+
|
630
|
+
print cyan,bold, " - Uploading #{file} to #{bucket_id}:#{destination}", reset
|
631
|
+
upload_response = @archive_buckets_interface.upload_file(bucket_id, file, destination)
|
632
|
+
if upload_response['success']
|
633
|
+
print bold," #{green}Success#{reset}"
|
634
|
+
else
|
635
|
+
print bold," #{red}Error#{reset}"
|
636
|
+
if upload_response['msg']
|
637
|
+
print " #{upload_response['msg']}#{reset}"
|
638
|
+
end
|
639
|
+
end
|
640
|
+
print "\n"
|
641
|
+
|
642
|
+
end
|
643
|
+
#print cyan, bold, "Upload Complete!", reset, "\n"
|
644
|
+
|
645
|
+
return 0
|
646
|
+
rescue RestClient::Exception => e
|
647
|
+
print_rest_exception(e, options)
|
648
|
+
return 1
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
def list_files(args)
|
653
|
+
options = {}
|
654
|
+
params = {}
|
655
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
656
|
+
opts.banner = subcommand_usage("[bucket:/path]")
|
657
|
+
opts.on('-a', '--all', "Show all files, including subdirectories under the /path.") do
|
658
|
+
params[:fullTree] = true
|
659
|
+
end
|
660
|
+
build_common_options(opts, options, [:list, :json, :dry_run])
|
661
|
+
opts.footer = "List files in an archive bucket. \nInclude [/path] to show files under a directory."
|
662
|
+
end
|
663
|
+
optparse.parse!(args)
|
664
|
+
if args.count != 1
|
665
|
+
print_error Morpheus::Terminal.angry_prompt
|
666
|
+
puts_error "#{command_name} list-files expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
667
|
+
return 1
|
668
|
+
end
|
669
|
+
bucket_id, search_file_path = parse_bucket_id_and_file_path(args[0])
|
670
|
+
connect(options)
|
671
|
+
begin
|
672
|
+
archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
|
673
|
+
return 1 if archive_bucket.nil?
|
674
|
+
[:phrase, :offset, :max, :sort, :direction, :fullTree].each do |k|
|
675
|
+
params[k] = options[k] unless options[k].nil?
|
676
|
+
end
|
677
|
+
if params[:phrase]
|
678
|
+
params[:fullTree] = true # these are not exclusively supported by api yet
|
679
|
+
end
|
680
|
+
if options[:dry_run]
|
681
|
+
print_dry_run @archive_buckets_interface.dry.list_files(bucket_id, search_file_path, params)
|
682
|
+
return
|
683
|
+
end
|
684
|
+
json_response = @archive_buckets_interface.list_files(bucket_id, search_file_path, params)
|
685
|
+
archive_files = json_response['archiveFiles']
|
686
|
+
# archive_bucket = json_response['archiveBucket']
|
687
|
+
if options[:json]
|
688
|
+
print JSON.pretty_generate(json_response)
|
689
|
+
return
|
690
|
+
end
|
691
|
+
# print_h1 "Archive Files"
|
692
|
+
# print_h1 "Archive Files", ["Bucket: [#{archive_bucket['id']}] #{archive_bucket['name']}", "Path: #{search_file_path}"]
|
693
|
+
print_h1 "Archive Files", ["#{archive_bucket['name']}:#{search_file_path}"]
|
694
|
+
print cyan
|
695
|
+
description_cols = {
|
696
|
+
"Bucket ID" => 'id',
|
697
|
+
"Bucket Name" => 'name',
|
698
|
+
#"Path" => lambda {|it| search_file_path }
|
699
|
+
}
|
700
|
+
#print_description_list(description_cols, archive_bucket)
|
701
|
+
#print "\n"
|
702
|
+
#print_h2 "Path: #{search_file_path}"
|
703
|
+
# print "Directory: #{search_file_path}"
|
704
|
+
if archive_files && archive_files.size > 0
|
705
|
+
print_archive_files_table(archive_files, {fullTree: params[:fullTree]})
|
706
|
+
print_results_pagination(json_response, {:label => "file", :n_label => "files"})
|
707
|
+
else
|
708
|
+
# puts "No files found for path #{search_file_path}"
|
709
|
+
if search_file_path.empty? || search_file_path == "/"
|
710
|
+
puts "This archive bucket has no files."
|
711
|
+
else
|
712
|
+
puts "No files found for path #{search_file_path}"
|
713
|
+
return 1
|
714
|
+
end
|
715
|
+
end
|
716
|
+
print reset,"\n"
|
717
|
+
return 0
|
718
|
+
rescue RestClient::Exception => e
|
719
|
+
print_rest_exception(e, options)
|
720
|
+
return 1
|
721
|
+
end
|
722
|
+
end
|
723
|
+
|
724
|
+
def ls(args)
|
725
|
+
options = {}
|
726
|
+
params = {}
|
727
|
+
do_one_file_per_line = false
|
728
|
+
do_long_format = false
|
729
|
+
do_human_bytes = false
|
730
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
731
|
+
opts.banner = subcommand_usage("[bucket/path]")
|
732
|
+
opts.on('-a', '--all', "Show all files, including subdirectories under the /path.") do
|
733
|
+
params[:fullTree] = true
|
734
|
+
do_one_file_per_line = true
|
735
|
+
end
|
736
|
+
opts.on('-l', '--long', "Lists files in the long format, which contains lots of useful information, e.g. the exact size of the file, the file type, and when it was last modified.") do
|
737
|
+
do_long_format = true
|
738
|
+
do_one_file_per_line
|
739
|
+
end
|
740
|
+
opts.on('-H', '--human', "Humanized file sizes. The default is just the number of bytes.") do
|
741
|
+
do_human_bytes = true
|
742
|
+
end
|
743
|
+
opts.on('-1', '--oneline', "One file per line. The default delimiter is a single space.") do
|
744
|
+
do_one_file_per_line = true
|
745
|
+
end
|
746
|
+
build_common_options(opts, options, [:list, :json, :dry_run])
|
747
|
+
opts.footer = "Print filenames for a given archive location.\nPass archive location in the format bucket/path."
|
748
|
+
end
|
749
|
+
optparse.parse!(args)
|
750
|
+
if args.count != 1
|
751
|
+
print_error Morpheus::Terminal.angry_prompt
|
752
|
+
puts_error "#{command_name} ls expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
753
|
+
return 1
|
754
|
+
end
|
755
|
+
bucket_id, search_file_path = parse_bucket_id_and_file_path(args[0])
|
756
|
+
connect(options)
|
757
|
+
begin
|
758
|
+
# archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
|
759
|
+
# return 1 if archive_bucket.nil?
|
760
|
+
[:phrase, :offset, :max, :sort, :direction, :fullTree].each do |k|
|
761
|
+
params[k] = options[k] unless options[k].nil?
|
762
|
+
end
|
763
|
+
if options[:dry_run]
|
764
|
+
print_dry_run @archive_buckets_interface.dry.list_files(bucket_id, search_file_path, params)
|
765
|
+
return 0
|
766
|
+
end
|
767
|
+
json_response = @archive_buckets_interface.list_files(bucket_id, search_file_path, params)
|
768
|
+
if options[:json]
|
769
|
+
puts as_json(json_response, options)
|
770
|
+
# no files is an error condition for this command
|
771
|
+
if !json_response['archiveFiles'] || json_response['archiveFiles'].size == 0
|
772
|
+
return 1
|
773
|
+
end
|
774
|
+
return 0
|
775
|
+
end
|
776
|
+
archive_bucket = json_response['archiveBucket'] # yep, this is returned too
|
777
|
+
archive_files = json_response['archiveFiles']
|
778
|
+
# archive_bucket = json_response['archiveBucket']
|
779
|
+
# print_h2 "Directory: #{search_file_path}"
|
780
|
+
# print "Directory: #{search_file_path}"
|
781
|
+
if archive_files && archive_files.size > 0
|
782
|
+
if do_long_format
|
783
|
+
# ls long format
|
784
|
+
# owner groups filesize type filename
|
785
|
+
now = Time.now
|
786
|
+
archive_files.each do |archive_file|
|
787
|
+
# -rw-r--r-- 1 jdickson staff 1361 Oct 23 08:00 voltron_2.10.log
|
788
|
+
file_color = cyan # reset
|
789
|
+
if archive_file['isDirectory']
|
790
|
+
file_color = blue
|
791
|
+
end
|
792
|
+
file_info = []
|
793
|
+
# Number of links
|
794
|
+
# file_info << file["linkCount"].to_i + 1
|
795
|
+
# Owner
|
796
|
+
owner_str = ""
|
797
|
+
if archive_file['owner']
|
798
|
+
owner_str = archive_file['owner']['name']
|
799
|
+
elsif archive_bucket['owner']
|
800
|
+
owner_str = archive_bucket['owner']['name']
|
801
|
+
else
|
802
|
+
owner_str = "noone"
|
803
|
+
end
|
804
|
+
file_info << truncate_string(owner_str, 15).ljust(15, " ")
|
805
|
+
# Group (Tenants)
|
806
|
+
groups_str = ""
|
807
|
+
if archive_file['visibility'] == 'public'
|
808
|
+
# this is confusing because of Public URL (isPublic) setting
|
809
|
+
groups_str = "public"
|
810
|
+
else
|
811
|
+
if archive_file['accounts'].instance_of?(Array) && archive_file['accounts'].size > 0
|
812
|
+
# groups_str = archive_file['accounts'].collect {|it| it['name'] }.join(',')
|
813
|
+
groups_str = (archive_file['accounts'].size == 1) ? "#{archive_file['accounts'][0]['name']}" : "#{archive_file['accounts'].size} tenants"
|
814
|
+
elsif archive_bucket['accounts'].instance_of?(Array) && archive_bucket['accounts'].size > 0
|
815
|
+
# groups_str = archive_bucket['accounts'].collect {|it| it['name'] }.join(',')
|
816
|
+
groups_str = (archive_bucket['accounts'].size == 1) ? "#{archive_bucket['accounts'][0]['name']}" : "#{archive_bucket['accounts'].size} tenants"
|
817
|
+
else
|
818
|
+
groups_str = owner_str
|
819
|
+
end
|
820
|
+
end
|
821
|
+
groups_str =
|
822
|
+
file_info << truncate_string(groups_str, 15).ljust(15, " ")
|
823
|
+
# File Type
|
824
|
+
content_type = archive_file['contentType'].to_s
|
825
|
+
if archive_file['isDirectory']
|
826
|
+
content_type = "directory"
|
827
|
+
else
|
828
|
+
content_type = archive_file['contentType'].to_s
|
829
|
+
end
|
830
|
+
file_info << content_type.ljust(25, " ")
|
831
|
+
filesize_str = ""
|
832
|
+
if do_human_bytes
|
833
|
+
# filesize_str = format_bytes(archive_file['rawSize'])
|
834
|
+
filesize_str = format_bytes_short(archive_file['rawSize'])
|
835
|
+
else
|
836
|
+
filesize_str = archive_file['rawSize'].to_i.to_s
|
837
|
+
end
|
838
|
+
# file_info << filesize_str.ljust(12, " ")
|
839
|
+
file_info << filesize_str.ljust(7, " ")
|
840
|
+
mtime = ""
|
841
|
+
last_updated = parse_time(archive_file['lastUpdated'])
|
842
|
+
if last_updated
|
843
|
+
if last_updated.year == now.year
|
844
|
+
mtime = format_local_dt(last_updated, {format: "%b %e %H:%M"})
|
845
|
+
else
|
846
|
+
mtime = format_local_dt(last_updated, {format: "%b %e %Y"})
|
847
|
+
end
|
848
|
+
end
|
849
|
+
file_info << mtime # .ljust(21, " ")
|
850
|
+
if params[:fullTree]
|
851
|
+
file_info << file_color + archive_file["filePath"].to_s + cyan
|
852
|
+
else
|
853
|
+
file_info << file_color + archive_file["name"].to_s + cyan
|
854
|
+
end
|
855
|
+
print cyan, file_info.join(" "), reset, "\n"
|
856
|
+
end
|
857
|
+
else
|
858
|
+
file_names = archive_files.collect do |archive_file|
|
859
|
+
file_color = cyan # reset
|
860
|
+
if archive_file['isDirectory']
|
861
|
+
file_color = blue
|
862
|
+
end
|
863
|
+
if params[:fullTree]
|
864
|
+
file_color + archive_file["filePath"].to_s + reset
|
865
|
+
else
|
866
|
+
file_color + archive_file["name"].to_s + reset
|
867
|
+
end
|
868
|
+
end
|
869
|
+
if do_one_file_per_line
|
870
|
+
print file_names.join("\n")
|
871
|
+
else
|
872
|
+
print file_names.join(" ")
|
873
|
+
end
|
874
|
+
print "\n"
|
875
|
+
end
|
876
|
+
else
|
877
|
+
print_error yellow, "No files found for path: #{search_file_path}", reset, "\n"
|
878
|
+
return 1
|
879
|
+
end
|
880
|
+
|
881
|
+
return 0
|
882
|
+
rescue RestClient::Exception => e
|
883
|
+
print_rest_exception(e, options)
|
884
|
+
return 1
|
885
|
+
end
|
886
|
+
end
|
887
|
+
|
888
|
+
def get_file(args)
|
889
|
+
full_command_string = "#{command_name} get-file #{args.join(' ')}".strip
|
890
|
+
options = {}
|
891
|
+
max_links = 10
|
892
|
+
max_history = 10
|
893
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
894
|
+
opts.banner = subcommand_usage("[bucket:/path]")
|
895
|
+
opts.on('-L', '--all-links', "Display all links instead of only 10." ) do
|
896
|
+
max_links = 10000
|
897
|
+
end
|
898
|
+
opts.on('-H', '--all-history', "Display all history instead of only 10." ) do
|
899
|
+
max_history = 10000
|
900
|
+
end
|
901
|
+
build_common_options(opts, options, [:json, :dry_run])
|
902
|
+
opts.footer = "Get details about an archive file.\n" +
|
903
|
+
"[bucket:/path] is required. This is the name of the bucket and /path the file or folder to be fetched." + "\n" +
|
904
|
+
"[id] can be passed instead of [bucket:/path]. This is the numeric File ID."
|
905
|
+
end
|
906
|
+
optparse.parse!(args)
|
907
|
+
# consider only allowing args.count == 1 here in the format [bucket:/path]
|
908
|
+
if args.count != 1
|
909
|
+
print_error Morpheus::Terminal.angry_prompt
|
910
|
+
puts_error "#{command_name} get-file expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
911
|
+
return 1
|
912
|
+
end
|
913
|
+
file_id = nil
|
914
|
+
bucket_id = nil
|
915
|
+
file_path = nil
|
916
|
+
# allow id in place of bucket:path
|
917
|
+
if args[0].to_s =~ /\A\d{1,}\Z/
|
918
|
+
file_id = args[0]
|
919
|
+
else
|
920
|
+
bucket_id, file_path = parse_bucket_id_and_file_path(args[0])
|
921
|
+
end
|
922
|
+
|
923
|
+
connect(options)
|
924
|
+
begin
|
925
|
+
# archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
|
926
|
+
# return 1 if archive_bucket.nil?
|
927
|
+
params = {}
|
928
|
+
if options[:dry_run]
|
929
|
+
if file_id
|
930
|
+
print_dry_run @archive_files_interface.dry.get(file_id, params), full_command_string
|
931
|
+
else
|
932
|
+
print_dry_run @archive_buckets_interface.dry.list_files(bucket_id, file_path, params), full_command_string
|
933
|
+
end
|
934
|
+
return 0
|
935
|
+
end
|
936
|
+
archive_file = nil
|
937
|
+
json_response = nil
|
938
|
+
if !file_id
|
939
|
+
archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
|
940
|
+
return 1 if archive_file.nil?
|
941
|
+
file_id = archive_file['id']
|
942
|
+
end
|
943
|
+
# archive_file = find_archive_file_by_id(file_id)
|
944
|
+
json_response = @archive_files_interface.get(file_id, params)
|
945
|
+
if options[:json]
|
946
|
+
puts as_json(json_response, options)
|
947
|
+
return 0
|
948
|
+
end
|
949
|
+
archive_file = json_response['archiveFile']
|
950
|
+
archive_logs = json_response['archiveLogs']
|
951
|
+
is_owner = json_response['isOwner']
|
952
|
+
if !bucket_id && archive_file["archiveBucket"]
|
953
|
+
bucket_id = archive_file["archiveBucket"]["name"]
|
954
|
+
end
|
955
|
+
|
956
|
+
print_h1 "Archive File Details"
|
957
|
+
print cyan
|
958
|
+
description_cols = {
|
959
|
+
"File ID" => 'id',
|
960
|
+
"Bucket" => lambda {|it| bucket_id },
|
961
|
+
"File Path" => lambda {|it| it['filePath'] },
|
962
|
+
"Type" => lambda {|it| it['isDirectory'] ? 'directory' : (it['contentType']) },
|
963
|
+
"Size" => lambda {|it| format_bytes(it['rawSize']) },
|
964
|
+
"Downloads" => lambda {|it| it['downloadCount'] },
|
965
|
+
"Created" => lambda {|it| format_local_dt(it['dateCreated']) },
|
966
|
+
"Last Modified" => lambda {|it| format_local_dt(it['lastUpdated']) }
|
967
|
+
}
|
968
|
+
print_description_list(description_cols, archive_file)
|
969
|
+
|
970
|
+
# print "\n"
|
971
|
+
|
972
|
+
|
973
|
+
print_h2 "Download URLs"
|
974
|
+
private_download_url = "#{@appliance_url}/api/archives/download/#{URI.escape(bucket_id)}" + "/#{URI.escape(archive_file['filePath'])}".squeeze('/')
|
975
|
+
public_download_url = nil
|
976
|
+
if archive_file['archiveBucket'] && archive_file['archiveBucket']['isPublic']
|
977
|
+
public_download_url = "#{@appliance_url}/public-archives/download/#{URI.escape(bucket_id)}" + "/#{URI.escape(archive_file['filePath'])}".squeeze('/')
|
978
|
+
end
|
979
|
+
print cyan
|
980
|
+
puts "Private URL: #{private_download_url}"
|
981
|
+
if public_download_url
|
982
|
+
puts " Public URL: #{public_download_url}"
|
983
|
+
end
|
984
|
+
|
985
|
+
do_show_links = is_owner
|
986
|
+
if do_show_links
|
987
|
+
links_json_response = @archive_files_interface.list_links(archive_file['id'], {max: max_links})
|
988
|
+
archive_file_links = links_json_response['archiveFileLinks']
|
989
|
+
if archive_file_links && archive_file_links.size > 0
|
990
|
+
print_h2 "Links"
|
991
|
+
print_archive_file_links_table(archive_file_links)
|
992
|
+
print_results_pagination(links_json_response, {:label => "link", :n_label => "links"})
|
993
|
+
else
|
994
|
+
print_h2 "File Links"
|
995
|
+
puts "No links found"
|
996
|
+
end
|
997
|
+
end
|
998
|
+
# print "\n"
|
999
|
+
do_show_history = is_owner
|
1000
|
+
if do_show_history
|
1001
|
+
history_json_response = @archive_files_interface.history(archive_file['id'], {max: max_history})
|
1002
|
+
archive_logs = history_json_response['archiveLogs']
|
1003
|
+
print_h2 "History"
|
1004
|
+
if archive_logs && archive_logs.size > 0
|
1005
|
+
print_archive_logs_table(archive_logs, {exclude:[:bucket]})
|
1006
|
+
print_results_pagination(history_json_response, {:label => "history record", :n_label => "history records"})
|
1007
|
+
else
|
1008
|
+
puts "No history found"
|
1009
|
+
end
|
1010
|
+
end
|
1011
|
+
print reset,"\n"
|
1012
|
+
return 0
|
1013
|
+
rescue RestClient::Exception => e
|
1014
|
+
print_rest_exception(e, options)
|
1015
|
+
return 1
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
# Use upload file bucket:/path
|
1020
|
+
# def add_file(args)
|
1021
|
+
# raise "not yet implemented"
|
1022
|
+
# end
|
1023
|
+
|
1024
|
+
def remove_file(args)
|
1025
|
+
options = {}
|
1026
|
+
query_params = {}
|
1027
|
+
do_recursive = false
|
1028
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1029
|
+
opts.banner = subcommand_usage("[bucket:/path]")
|
1030
|
+
opts.on( '-R', '--recursive', "Delete a directory and all of its files. This must be passed if specifying a directory." ) do
|
1031
|
+
do_recursive = true
|
1032
|
+
end
|
1033
|
+
build_common_options(opts, options, [:auto_confirm, :json, :dry_run])
|
1034
|
+
opts.footer = "Delete an archive file or directory."
|
1035
|
+
end
|
1036
|
+
optparse.parse!(args)
|
1037
|
+
# consider only allowing args.count == 1 here in the format [bucket:/path]
|
1038
|
+
if args.count != 1
|
1039
|
+
print_error Morpheus::Terminal.angry_prompt
|
1040
|
+
puts_error "#{command_name} remove-file expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1041
|
+
return 1
|
1042
|
+
end
|
1043
|
+
bucket_id, file_path = parse_bucket_id_and_file_path(args[0])
|
1044
|
+
connect(options)
|
1045
|
+
begin
|
1046
|
+
|
1047
|
+
archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
|
1048
|
+
return 1 if archive_file.nil?
|
1049
|
+
if archive_file['isDirectory']
|
1050
|
+
if !do_recursive
|
1051
|
+
print_error Morpheus::Terminal.angry_prompt
|
1052
|
+
puts_error "bad argument: '#{file_path}' is a directory. Use -R or --recursive to delete a directory.\n#{optparse}"
|
1053
|
+
return 1
|
1054
|
+
end
|
1055
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the archive directory: #{args[0]}?")
|
1056
|
+
return 9, "aborted command"
|
1057
|
+
end
|
1058
|
+
else
|
1059
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the archive file: #{args[0]}?")
|
1060
|
+
return 9, "aborted command"
|
1061
|
+
end
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
if options[:dry_run]
|
1065
|
+
print_dry_run @archive_files_interface.dry.destroy(archive_file['id'], query_params)
|
1066
|
+
return 0
|
1067
|
+
end
|
1068
|
+
json_response = @archive_files_interface.destroy(archive_file['id'], query_params)
|
1069
|
+
if options[:json]
|
1070
|
+
print JSON.pretty_generate(json_response)
|
1071
|
+
print "\n"
|
1072
|
+
else
|
1073
|
+
print_green_success "Removed archive file #{args[0]}"
|
1074
|
+
end
|
1075
|
+
return 0
|
1076
|
+
|
1077
|
+
rescue RestClient::Exception => e
|
1078
|
+
print_rest_exception(e, options)
|
1079
|
+
return 1
|
1080
|
+
end
|
1081
|
+
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
def file_history(args)
|
1085
|
+
options = {}
|
1086
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1087
|
+
opts.banner = subcommand_usage("[bucket:/path]")
|
1088
|
+
build_common_options(opts, options, [:list, :json, :dry_run])
|
1089
|
+
opts.footer = "List history log events for an archive file."
|
1090
|
+
end
|
1091
|
+
optparse.parse!(args)
|
1092
|
+
if args.count != 1
|
1093
|
+
print_error Morpheus::Terminal.angry_prompt
|
1094
|
+
puts_error "#{command_name} file-history expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1095
|
+
return 1
|
1096
|
+
end
|
1097
|
+
bucket_id, file_path = parse_bucket_id_and_file_path(args[0])
|
1098
|
+
connect(options)
|
1099
|
+
begin
|
1100
|
+
# todo: only 1 api call needed here.
|
1101
|
+
# archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
|
1102
|
+
# return 1 if archive_bucket.nil?
|
1103
|
+
archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
|
1104
|
+
return 1 if archive_file.nil?
|
1105
|
+
# ok, load history
|
1106
|
+
params = {}
|
1107
|
+
[:phrase, :offset, :max, :sort, :direction].each do |k|
|
1108
|
+
params[k] = options[k] unless options[k].nil?
|
1109
|
+
end
|
1110
|
+
if options[:dry_run]
|
1111
|
+
print_dry_run @archive_files_interface.dry.history(archive_file['id'], params)
|
1112
|
+
return
|
1113
|
+
end
|
1114
|
+
json_response = @archive_files_interface.history(archive_file['id'], params)
|
1115
|
+
archive_logs = json_response['archiveLogs']
|
1116
|
+
|
1117
|
+
if options[:json]
|
1118
|
+
print JSON.pretty_generate(json_response)
|
1119
|
+
return
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
print_h1 "Archive File History", ["#{bucket_id}:#{file_path}"]
|
1123
|
+
# print cyan
|
1124
|
+
# description_cols = {
|
1125
|
+
# "File ID" => 'id',
|
1126
|
+
# "Bucket" => lambda {|it| bucket_id },
|
1127
|
+
# "File Path" => lambda {|it| file_path }
|
1128
|
+
# }
|
1129
|
+
# print_description_list(description_cols, archive_file)
|
1130
|
+
# print "\n"
|
1131
|
+
# print_h2 "History"
|
1132
|
+
if archive_logs && archive_logs.size > 0
|
1133
|
+
print_archive_logs_table(archive_logs)
|
1134
|
+
print_results_pagination(json_response, {:label => "history record", :n_label => "history records"})
|
1135
|
+
else
|
1136
|
+
puts "No history found"
|
1137
|
+
end
|
1138
|
+
print reset,"\n"
|
1139
|
+
return 0
|
1140
|
+
rescue RestClient::Exception => e
|
1141
|
+
print_rest_exception(e, options)
|
1142
|
+
return 1
|
1143
|
+
end
|
1144
|
+
end
|
1145
|
+
|
1146
|
+
def file_links(args)
|
1147
|
+
options = {}
|
1148
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1149
|
+
opts.banner = subcommand_usage("[bucket:/path]")
|
1150
|
+
build_common_options(opts, options, [:list, :json, :dry_run])
|
1151
|
+
opts.footer = "List links for an archive file."
|
1152
|
+
end
|
1153
|
+
optparse.parse!(args)
|
1154
|
+
if args.count != 1
|
1155
|
+
print_error Morpheus::Terminal.angry_prompt
|
1156
|
+
puts_error "#{command_name} file-history expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1157
|
+
return 1
|
1158
|
+
end
|
1159
|
+
bucket_id, file_path = parse_bucket_id_and_file_path(args[0])
|
1160
|
+
connect(options)
|
1161
|
+
begin
|
1162
|
+
# todo: only 1 api call needed here.
|
1163
|
+
# archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
|
1164
|
+
# return 1 if archive_bucket.nil?
|
1165
|
+
archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
|
1166
|
+
return 1 if archive_file.nil?
|
1167
|
+
# ok, load links
|
1168
|
+
params = {}
|
1169
|
+
[:phrase, :offset, :max, :sort, :direction].each do |k|
|
1170
|
+
params[k] = options[k] unless options[k].nil?
|
1171
|
+
end
|
1172
|
+
if options[:dry_run]
|
1173
|
+
print_dry_run @archive_files_interface.dry.list_links(archive_file['id'], params)
|
1174
|
+
return
|
1175
|
+
end
|
1176
|
+
json_response = @archive_files_interface.list_links(archive_file['id'], params)
|
1177
|
+
archive_file_links = json_response['archiveFileLinks']
|
1178
|
+
|
1179
|
+
if options[:json]
|
1180
|
+
print JSON.pretty_generate(json_response)
|
1181
|
+
return
|
1182
|
+
end
|
1183
|
+
|
1184
|
+
print_h1 "Archive File Links", ["#{bucket_id}:#{file_path}"]
|
1185
|
+
# print_h1 "Archive File"
|
1186
|
+
# print cyan
|
1187
|
+
# description_cols = {
|
1188
|
+
# "File ID" => 'id',
|
1189
|
+
# "Bucket" => lambda {|it| bucket_id },
|
1190
|
+
# "File Path" => lambda {|it| file_path }
|
1191
|
+
# }
|
1192
|
+
# print_description_list(description_cols, archive_file)
|
1193
|
+
# print "\n"
|
1194
|
+
# print_h2 "Links"
|
1195
|
+
if archive_file_links && archive_file_links.size > 0
|
1196
|
+
print_archive_file_links_table(archive_file_links)
|
1197
|
+
print_results_pagination(json_response, {:label => "link", :n_label => "links"})
|
1198
|
+
else
|
1199
|
+
puts "No history found"
|
1200
|
+
end
|
1201
|
+
print reset,"\n"
|
1202
|
+
return 0
|
1203
|
+
rescue RestClient::Exception => e
|
1204
|
+
print_rest_exception(e, options)
|
1205
|
+
return 1
|
1206
|
+
end
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
def download_file(args)
|
1210
|
+
full_command_string = "#{command_name} download #{args.join(' ')}".strip
|
1211
|
+
options = {}
|
1212
|
+
outfile = nil
|
1213
|
+
do_overwrite = false
|
1214
|
+
do_mkdir = false
|
1215
|
+
use_public_url = false
|
1216
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1217
|
+
opts.banner = subcommand_usage("[bucket:/path] [local-file]")
|
1218
|
+
opts.on( '-f', '--force', "Overwrite existing [local-file] if it exists." ) do
|
1219
|
+
do_overwrite = true
|
1220
|
+
# do_mkdir = true
|
1221
|
+
end
|
1222
|
+
opts.on( '-p', '--mkdir', "Create missing directories for [local-file] if they do not exist." ) do
|
1223
|
+
do_mkdir = true
|
1224
|
+
end
|
1225
|
+
opts.on( '-p', '--public', "Use Public Download URL instead of Private. The file must be in a public archives." ) do
|
1226
|
+
use_public_url = true
|
1227
|
+
# do_mkdir = true
|
1228
|
+
end
|
1229
|
+
build_common_options(opts, options, [:dry_run, :quiet])
|
1230
|
+
opts.footer = "Download an archive file or directory.\n" +
|
1231
|
+
"[bucket:/path] is required. This is the name of the bucket and /path the file or folder to be downloaded.\n" +
|
1232
|
+
"[local-file] is required. This is the full local filepath for the downloaded file.\n" +
|
1233
|
+
"Directories will be downloaded as a .zip file, so you'll want to specify a [local-file] with a .zip extension."
|
1234
|
+
end
|
1235
|
+
optparse.parse!(args)
|
1236
|
+
if args.count != 2
|
1237
|
+
print_error Morpheus::Terminal.angry_prompt
|
1238
|
+
puts_error "#{command_name} download expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1239
|
+
return 1
|
1240
|
+
end
|
1241
|
+
connect(options)
|
1242
|
+
begin
|
1243
|
+
bucket_id, file_path = parse_bucket_id_and_file_path(args[0])
|
1244
|
+
# just make 1 api call for now
|
1245
|
+
# archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
|
1246
|
+
# return 1 if archive_file.nil?
|
1247
|
+
full_file_path = "#{bucket_id}/#{file_path}".squeeze('/')
|
1248
|
+
# full_file_path = args[0]
|
1249
|
+
# end download destination with a slash to use the local file basename
|
1250
|
+
outfile = args[1]
|
1251
|
+
# if outfile[-1] == "/" || outfile[-1] == "\\"
|
1252
|
+
# outfile = File.join(outfile, File.basename(full_file_path))
|
1253
|
+
# end
|
1254
|
+
outfile = File.expand_path(outfile)
|
1255
|
+
if Dir.exists?(outfile)
|
1256
|
+
outfile = File.join(outfile, File.basename(full_file_path))
|
1257
|
+
end
|
1258
|
+
if Dir.exists?(outfile)
|
1259
|
+
print_red_alert "[local-file] is invalid. It is the name of an existing directory: #{outfile}"
|
1260
|
+
return 1
|
1261
|
+
end
|
1262
|
+
destination_dir = File.dirname(outfile)
|
1263
|
+
if !Dir.exists?(destination_dir)
|
1264
|
+
if do_mkdir
|
1265
|
+
print cyan,"Creating local directory #{destination_dir}",reset,"\n"
|
1266
|
+
FileUtils.mkdir_p(destination_dir)
|
1267
|
+
else
|
1268
|
+
print_red_alert "[local-file] is invalid. Directory not found: #{destination_dir}"
|
1269
|
+
return 1
|
1270
|
+
end
|
1271
|
+
end
|
1272
|
+
if File.exists?(outfile)
|
1273
|
+
if do_overwrite
|
1274
|
+
# uhh need to be careful wih the passed filepath here..
|
1275
|
+
# don't delete, just overwrite.
|
1276
|
+
# File.delete(outfile)
|
1277
|
+
else
|
1278
|
+
print_error Morpheus::Terminal.angry_prompt
|
1279
|
+
puts_error "[local-file] is invalid. File already exists: #{outfile}", "Use -f to overwrite the existing file."
|
1280
|
+
# puts_error optparse
|
1281
|
+
return 1
|
1282
|
+
end
|
1283
|
+
end
|
1284
|
+
begin
|
1285
|
+
if options[:dry_run]
|
1286
|
+
# print_dry_run @archive_files_interface.dry.download_file_by_path(full_file_path), full_command_string
|
1287
|
+
if use_public_url
|
1288
|
+
print_dry_run @archive_files_interface.dry.download_file_by_path_chunked(full_file_path, outfile), full_command_string
|
1289
|
+
else
|
1290
|
+
print_dry_run @archive_files_interface.dry.download_public_file_by_path_chunked(full_file_path, outfile), full_command_string
|
1291
|
+
end
|
1292
|
+
return 1
|
1293
|
+
end
|
1294
|
+
if !options[:quiet]
|
1295
|
+
print cyan + "Downloading archive file #{bucket_id}:#{file_path} to #{outfile} ... "
|
1296
|
+
end
|
1297
|
+
# file_response = @archive_files_interface.download_file_by_path(full_file_path)
|
1298
|
+
# File.write(outfile, file_response.body)
|
1299
|
+
# err, maybe write to a random tmp file, then mv to outfile
|
1300
|
+
# currently, whatever the response is, it's written to the outfile. eg. 404 html
|
1301
|
+
http_response = nil
|
1302
|
+
if use_public_url
|
1303
|
+
http_response = @archive_files_interface.download_public_file_by_path_chunked(full_file_path, outfile)
|
1304
|
+
else
|
1305
|
+
http_response = @archive_files_interface.download_file_by_path_chunked(full_file_path, outfile)
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
# FileUtils.chmod(0600, outfile)
|
1309
|
+
success = http_response.code.to_i == 200
|
1310
|
+
if success
|
1311
|
+
if !options[:quiet]
|
1312
|
+
print green + "SUCCESS" + reset + "\n"
|
1313
|
+
end
|
1314
|
+
return 0
|
1315
|
+
else
|
1316
|
+
if !options[:quiet]
|
1317
|
+
print red + "ERROR" + reset + " HTTP #{http_response.code}" + "\n"
|
1318
|
+
end
|
1319
|
+
# F it, just remove a bad result
|
1320
|
+
if File.exists?(outfile) && File.file?(outfile)
|
1321
|
+
Morpheus::Logging::DarkPrinter.puts "Deleting bad file download: #{outfile}" if Morpheus::Logging.debug?
|
1322
|
+
File.delete(outfile)
|
1323
|
+
end
|
1324
|
+
if options[:debug]
|
1325
|
+
puts_error http_response.inspect
|
1326
|
+
end
|
1327
|
+
return 1
|
1328
|
+
end
|
1329
|
+
rescue RestClient::Exception => e
|
1330
|
+
# this is not reached
|
1331
|
+
if e.response && e.response.code == 404
|
1332
|
+
print_red_alert "Archive file not found by path #{full_file_path}"
|
1333
|
+
return nil
|
1334
|
+
else
|
1335
|
+
raise e
|
1336
|
+
end
|
1337
|
+
end
|
1338
|
+
rescue RestClient::Exception => e
|
1339
|
+
print_rest_exception(e, options)
|
1340
|
+
return 1
|
1341
|
+
end
|
1342
|
+
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
def read_file(args)
|
1346
|
+
full_command_string = "archives read #{args.join(' ')}".strip
|
1347
|
+
options = {}
|
1348
|
+
outfile = nil
|
1349
|
+
do_overwrite = false
|
1350
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1351
|
+
opts.banner = subcommand_usage("[bucket:/path]")
|
1352
|
+
build_common_options(opts, options, [:auto_confirm, :dry_run])
|
1353
|
+
opts.footer = "Print the contents of an archive file.\n" +
|
1354
|
+
"[bucket:/path] is required. This is the name of the bucket and /path the file or folder to be downloaded.\n" +
|
1355
|
+
"Confirmation is needed if the specified file is more than 1KB.\n" +
|
1356
|
+
"This confirmation can be skipped with the -y option."
|
1357
|
+
end
|
1358
|
+
optparse.parse!(args)
|
1359
|
+
if args.count != 1
|
1360
|
+
print_error Morpheus::Terminal.angry_prompt
|
1361
|
+
puts_error "#{command_name} read expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1362
|
+
return 1
|
1363
|
+
end
|
1364
|
+
connect(options)
|
1365
|
+
begin
|
1366
|
+
bucket_id, file_path = parse_bucket_id_and_file_path(args[0])
|
1367
|
+
archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
|
1368
|
+
return 1 if archive_file.nil?
|
1369
|
+
full_file_path = "#{bucket_id}/#{file_path}".squeeze('/')
|
1370
|
+
if options[:dry_run]
|
1371
|
+
print_dry_run @archive_files_interface.dry.download_file_by_path(full_file_path), full_command_string
|
1372
|
+
return 1
|
1373
|
+
end
|
1374
|
+
if archive_file['rawSize'].to_i > 1024
|
1375
|
+
pretty_size = format_bytes(archive_file['rawSize'])
|
1376
|
+
unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to print the contents of this file (#{pretty_size}) ?")
|
1377
|
+
return 9, "aborted command"
|
1378
|
+
end
|
1379
|
+
end
|
1380
|
+
file_response = @archive_files_interface.download_file_by_path(full_file_path)
|
1381
|
+
puts file_response.body.to_s
|
1382
|
+
return 0
|
1383
|
+
rescue RestClient::Exception => e
|
1384
|
+
print_rest_exception(e, options)
|
1385
|
+
return 1
|
1386
|
+
end
|
1387
|
+
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
def add_file_link(args)
|
1391
|
+
options = {}
|
1392
|
+
expiration_seconds = 20*60 # default expiration is 20 minutes
|
1393
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1394
|
+
opts.banner = subcommand_usage("[bucket:/path]")
|
1395
|
+
opts.on('-e', '--expire SECONDS', "The time to live for this link. The default is 1200 (20 minutes). A value less than 1 means never expire.") do |val|
|
1396
|
+
expiration_seconds = val.to_i
|
1397
|
+
end
|
1398
|
+
build_common_options(opts, options, [:json, :dry_run, :quiet])
|
1399
|
+
opts.footer = "Create a public link to a file.\n" +
|
1400
|
+
"[bucket:/path] is required. This is the name of the bucket and /path the file or folder to be fetched."
|
1401
|
+
end
|
1402
|
+
optparse.parse!(args)
|
1403
|
+
# consider only allowing args.count == 1 here in the format [bucket:/path]
|
1404
|
+
if args.count != 1
|
1405
|
+
print_error Morpheus::Terminal.angry_prompt
|
1406
|
+
puts_error "#{command_name} add-file-link expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1407
|
+
return 1
|
1408
|
+
end
|
1409
|
+
bucket_id, file_path = parse_bucket_id_and_file_path(args[0])
|
1410
|
+
connect(options)
|
1411
|
+
begin
|
1412
|
+
archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
|
1413
|
+
return 1 if archive_file.nil?
|
1414
|
+
|
1415
|
+
params = {}
|
1416
|
+
if expiration_seconds.to_i > 0
|
1417
|
+
params['expireSeconds'] = expiration_seconds.to_i
|
1418
|
+
end
|
1419
|
+
if options[:dry_run]
|
1420
|
+
print_dry_run @archive_files_interface.dry.create_file_link(archive_file['id'], params)
|
1421
|
+
return
|
1422
|
+
end
|
1423
|
+
json_response = @archive_files_interface.create_file_link(archive_file['id'], params)
|
1424
|
+
|
1425
|
+
if options[:json]
|
1426
|
+
print JSON.pretty_generate(json_response)
|
1427
|
+
return 0
|
1428
|
+
elsif !options[:quiet]
|
1429
|
+
print_green_success "Created archive file link #{bucket_id}:/#{archive_file['filePath']} token: #{json_response['secretAccessKey']}"
|
1430
|
+
end
|
1431
|
+
return 0
|
1432
|
+
rescue RestClient::Exception => e
|
1433
|
+
print_rest_exception(e, options)
|
1434
|
+
return 1
|
1435
|
+
end
|
1436
|
+
end
|
1437
|
+
|
1438
|
+
def remove_file_link(args)
|
1439
|
+
options = {}
|
1440
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1441
|
+
opts.banner = subcommand_usage("[bucket:/path] [token]")
|
1442
|
+
build_common_options(opts, options, [:auto_confirm, :dry_run, :quiet])
|
1443
|
+
opts.footer = "Delete a public link to a file.\n" +
|
1444
|
+
"[bucket:/path] is required. This is the name of the bucket and /path the file or folder to be fetched." +
|
1445
|
+
"[token] is required. This is the secret access key that identifies the link."
|
1446
|
+
end
|
1447
|
+
optparse.parse!(args)
|
1448
|
+
# consider only allowing args.count == 1 here in the format [bucket:/path]
|
1449
|
+
if args.count != 2
|
1450
|
+
print_error Morpheus::Terminal.angry_prompt
|
1451
|
+
puts_error "#{command_name} remove-file-link expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1452
|
+
return 1
|
1453
|
+
end
|
1454
|
+
bucket_id, file_path = parse_bucket_id_and_file_path(args[0])
|
1455
|
+
connect(options)
|
1456
|
+
begin
|
1457
|
+
archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
|
1458
|
+
return 1 if archive_file.nil?
|
1459
|
+
link_id = nil
|
1460
|
+
secret_access_key = args[1]
|
1461
|
+
secret_access_key = secret_access_key.sub('/public-archives/link?s=', '')
|
1462
|
+
# find the int id via token...
|
1463
|
+
links_json_response = @archive_files_interface.list_links(archive_file['id'], {s: secret_access_key})
|
1464
|
+
if links_json_response['archiveFileLinks'] && links_json_response['archiveFileLinks'][0]
|
1465
|
+
link_id = links_json_response['archiveFileLinks'][0]['id']
|
1466
|
+
end
|
1467
|
+
if !link_id
|
1468
|
+
print_red_alert "Archive file link not found for #{bucket_id}:/#{archive_file['filePath']} token: #{secret_access_key}"
|
1469
|
+
return 1
|
1470
|
+
end
|
1471
|
+
params = {}
|
1472
|
+
if options[:dry_run]
|
1473
|
+
print_dry_run @archive_files_interface.dry.destroy_file_link(archive_file['id'], link_id, params)
|
1474
|
+
return
|
1475
|
+
end
|
1476
|
+
json_response = @archive_files_interface.destroy_file_link(archive_file['id'], link_id, params)
|
1477
|
+
|
1478
|
+
if options[:json]
|
1479
|
+
print JSON.pretty_generate(json_response)
|
1480
|
+
return 0
|
1481
|
+
elsif !options[:quiet]
|
1482
|
+
print_green_success "Deleted archive file link #{bucket_id}:/#{archive_file['filePath']} token: #{secret_access_key}"
|
1483
|
+
end
|
1484
|
+
return 0
|
1485
|
+
rescue RestClient::Exception => e
|
1486
|
+
print_rest_exception(e, options)
|
1487
|
+
return 1
|
1488
|
+
end
|
1489
|
+
end
|
1490
|
+
|
1491
|
+
def download_file_link(args)
|
1492
|
+
full_command_string = "archives download-link #{args.join(' ')}".strip
|
1493
|
+
options = {}
|
1494
|
+
outfile = nil
|
1495
|
+
do_overwrite = false
|
1496
|
+
dor_mkdir = false
|
1497
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1498
|
+
opts.banner = subcommand_usage("[link-key] [local-file]")
|
1499
|
+
opts.on( '-f', '--force', "Overwrite existing [local-file] if it exists." ) do
|
1500
|
+
do_overwrite = true
|
1501
|
+
# do_mkdir = true
|
1502
|
+
end
|
1503
|
+
opts.on( '-p', '--mkdir', "Create missing directories for [local-file] if they do not exist." ) do
|
1504
|
+
do_mkdir = true
|
1505
|
+
end
|
1506
|
+
build_common_options(opts, options, [:dry_run, :quiet])
|
1507
|
+
opts.footer = "Download an archive file link.\n" +
|
1508
|
+
"[link-key] is required. This is the secret access key for the archive file link.\n" +
|
1509
|
+
"[local-file] is required. This is the full local filepath for the downloaded file."
|
1510
|
+
end
|
1511
|
+
optparse.parse!(args)
|
1512
|
+
if args.count != 2
|
1513
|
+
print_error Morpheus::Terminal.angry_prompt
|
1514
|
+
puts_error "#{command_name} download-link expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1515
|
+
return 1
|
1516
|
+
end
|
1517
|
+
connect(options)
|
1518
|
+
begin
|
1519
|
+
link_key = args[0]
|
1520
|
+
# archive_file_link = find_archive_file_link_by_key(link_key)
|
1521
|
+
# just make 1 api call for now
|
1522
|
+
# archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
|
1523
|
+
# return 1 if archive_file.nil?
|
1524
|
+
full_file_path = "#{bucket_id}/#{file_path}".squeeze('/')
|
1525
|
+
# full_file_path = args[0]
|
1526
|
+
outfile = File.expand_path(args[1])
|
1527
|
+
# [local-file] must include the full file name when downloading a link
|
1528
|
+
# if Dir.exists?(outfile)
|
1529
|
+
# outfile = File.join(outfile, File.basename(archive_file['name']))
|
1530
|
+
# end
|
1531
|
+
if Dir.exists?(outfile)
|
1532
|
+
print_red_alert "[local-file] is invalid. It is the name of an existing directory: #{outfile}"
|
1533
|
+
return 1
|
1534
|
+
end
|
1535
|
+
destination_dir = File.dirname(outfile)
|
1536
|
+
if !Dir.exists?(destination_dir)
|
1537
|
+
if do_mkdir
|
1538
|
+
print cyan,"Creating local directory #{destination_dir}",reset,"\n"
|
1539
|
+
FileUtils.mkdir_p(destination_dir)
|
1540
|
+
else
|
1541
|
+
print_red_alert "[local-file] is invalid. Directory not found: #{destination_dir}"
|
1542
|
+
return 1
|
1543
|
+
end
|
1544
|
+
end
|
1545
|
+
if File.exists?(outfile)
|
1546
|
+
if do_overwrite
|
1547
|
+
# uhh need to be careful wih the passed filepath here..
|
1548
|
+
# don't delete, just overwrite.
|
1549
|
+
# File.delete(outfile)
|
1550
|
+
else
|
1551
|
+
print_red_alert "[local-file] is invalid. File already exists: #{outfile}"
|
1552
|
+
# print_error Morpheus::Terminal.angry_prompt
|
1553
|
+
# puts_error "[local-file] is invalid. File already exists: #{outfile}\n#{optparse}"
|
1554
|
+
puts_error "Use -f to overwrite the existing file."
|
1555
|
+
# puts_error optparse
|
1556
|
+
return 1
|
1557
|
+
end
|
1558
|
+
end
|
1559
|
+
|
1560
|
+
if options[:dry_run]
|
1561
|
+
# print_dry_run @archive_files_interface.dry.download_file_by_path(full_file_path), full_command_string
|
1562
|
+
print_dry_run @archive_files_interface.dry.download_file_by_link_chunked(link_key, outfile), full_command_string
|
1563
|
+
return 1
|
1564
|
+
end
|
1565
|
+
if !options[:quiet]
|
1566
|
+
print cyan + "Downloading archive file link #{link_key} to #{outfile} ... "
|
1567
|
+
end
|
1568
|
+
# file_response = @archive_files_interface.download_file_by_path(full_file_path)
|
1569
|
+
# File.write(outfile, file_response.body)
|
1570
|
+
# err, maybe write to a random tmp file, then mv to outfile
|
1571
|
+
# currently, whatever the response is, it's written to the outfile. eg. 404 html
|
1572
|
+
http_response = @archive_files_interface.download_file_by_link_chunked(link_key, outfile)
|
1573
|
+
|
1574
|
+
# FileUtils.chmod(0600, outfile)
|
1575
|
+
success = http_response.code.to_i == 200
|
1576
|
+
if success
|
1577
|
+
if !options[:quiet]
|
1578
|
+
print green + "SUCCESS" + reset + "\n"
|
1579
|
+
end
|
1580
|
+
return 0
|
1581
|
+
else
|
1582
|
+
if !options[:quiet]
|
1583
|
+
print red + "ERROR" + reset + " HTTP #{http_response.code}" + "\n"
|
1584
|
+
end
|
1585
|
+
# F it, just remove a bad result
|
1586
|
+
if File.exists?(outfile) && File.file?(outfile)
|
1587
|
+
Morpheus::Logging::DarkPrinter.puts "Deleting bad file download: #{outfile}" if Morpheus::Logging.debug?
|
1588
|
+
File.delete(outfile)
|
1589
|
+
end
|
1590
|
+
if options[:debug]
|
1591
|
+
puts_error http_response.inspect
|
1592
|
+
end
|
1593
|
+
return 1
|
1594
|
+
end
|
1595
|
+
rescue RestClient::Exception => e
|
1596
|
+
print_rest_exception(e, options)
|
1597
|
+
return 1
|
1598
|
+
end
|
1599
|
+
|
1600
|
+
end
|
1601
|
+
|
1602
|
+
def download_bucket_zip(args)
|
1603
|
+
full_command_string = "#{command_name} download-bucket #{args.join(' ')}".strip
|
1604
|
+
options = {}
|
1605
|
+
outfile = nil
|
1606
|
+
do_overwrite = false
|
1607
|
+
do_mkdir = false
|
1608
|
+
use_public_url = false
|
1609
|
+
optparse = Morpheus::Cli::OptionParser.new do |opts|
|
1610
|
+
opts.banner = subcommand_usage("[bucket] [local-file]")
|
1611
|
+
opts.on( '-f', '--force', "Overwrite existing [local-file] if it exists." ) do
|
1612
|
+
do_overwrite = true
|
1613
|
+
# do_mkdir = true
|
1614
|
+
end
|
1615
|
+
opts.on( '-p', '--mkdir', "Create missing directories for [local-file] if they do not exist." ) do
|
1616
|
+
do_mkdir = true
|
1617
|
+
end
|
1618
|
+
# api endpoint needed still for public bucket.zip
|
1619
|
+
# opts.on( '-p', '--public', "Use Public Download URL instead of Private. The bucket must be have Public URL enabled." ) do
|
1620
|
+
# use_public_url = true
|
1621
|
+
# # do_mkdir = true
|
1622
|
+
# end
|
1623
|
+
build_common_options(opts, options, [:dry_run, :quiet])
|
1624
|
+
opts.footer = "Download an entire archive bucket as a .zip file.\n" +
|
1625
|
+
"[bucket] is required. This is the name of the bucket.\n" +
|
1626
|
+
"[local-file] is required. This is the full local filepath for the downloaded file.\n" +
|
1627
|
+
"Buckets are be downloaded as a .zip file, so you'll want to specify a [local-file] with a .zip extension."
|
1628
|
+
end
|
1629
|
+
optparse.parse!(args)
|
1630
|
+
if args.count != 2
|
1631
|
+
print_error Morpheus::Terminal.angry_prompt
|
1632
|
+
puts_error "#{command_name} download-bucket expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
|
1633
|
+
return 1
|
1634
|
+
end
|
1635
|
+
connect(options)
|
1636
|
+
begin
|
1637
|
+
bucket_id = args[0].to_s
|
1638
|
+
archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
|
1639
|
+
return 1 if archive_bucket.nil?
|
1640
|
+
|
1641
|
+
outfile = args[1]
|
1642
|
+
# if outfile[-1] == "/" || outfile[-1] == "\\"
|
1643
|
+
# outfile = File.join(outfile, archive_bucket['name'].to_s) + ".zip"
|
1644
|
+
# end
|
1645
|
+
outfile = File.expand_path(outfile)
|
1646
|
+
if Dir.exists?(outfile)
|
1647
|
+
outfile = File.join(outfile, archive_bucket['name'].to_s) + ".zip"
|
1648
|
+
end
|
1649
|
+
if Dir.exists?(outfile)
|
1650
|
+
print_red_alert "[local-file] is invalid. It is the name of an existing directory: #{outfile}"
|
1651
|
+
return 1
|
1652
|
+
end
|
1653
|
+
# always a .zip
|
1654
|
+
if outfile[-4..-1] != ".zip"
|
1655
|
+
outfile << ".zip"
|
1656
|
+
end
|
1657
|
+
destination_dir = File.dirname(outfile)
|
1658
|
+
if !Dir.exists?(destination_dir)
|
1659
|
+
if do_mkdir
|
1660
|
+
print cyan,"Creating local directory #{destination_dir}",reset,"\n"
|
1661
|
+
FileUtils.mkdir_p(destination_dir)
|
1662
|
+
else
|
1663
|
+
print_red_alert "[local-file] is invalid. Directory not found: #{destination_dir}"
|
1664
|
+
return 1
|
1665
|
+
end
|
1666
|
+
end
|
1667
|
+
if File.exists?(outfile)
|
1668
|
+
if do_overwrite
|
1669
|
+
# uhh need to be careful wih the passed filepath here..
|
1670
|
+
# don't delete, just overwrite.
|
1671
|
+
# File.delete(outfile)
|
1672
|
+
else
|
1673
|
+
print_error Morpheus::Terminal.angry_prompt
|
1674
|
+
puts_error "[local-file] is invalid. File already exists: #{outfile}", "Use -f to overwrite the existing file."
|
1675
|
+
# puts_error optparse
|
1676
|
+
return 1
|
1677
|
+
end
|
1678
|
+
end
|
1679
|
+
|
1680
|
+
if options[:dry_run]
|
1681
|
+
print_dry_run @archive_buckets_interface.dry.download_bucket_zip_chunked(bucket_id, outfile), full_command_string
|
1682
|
+
return 1
|
1683
|
+
end
|
1684
|
+
if !options[:quiet]
|
1685
|
+
print cyan + "Downloading archive bucket #{bucket_id} to #{outfile} ... "
|
1686
|
+
end
|
1687
|
+
|
1688
|
+
http_response = @archive_buckets_interface.download_bucket_zip_chunked(bucket_id, outfile)
|
1689
|
+
|
1690
|
+
# FileUtils.chmod(0600, outfile)
|
1691
|
+
success = http_response.code.to_i == 200
|
1692
|
+
if success
|
1693
|
+
if !options[:quiet]
|
1694
|
+
print green + "SUCCESS" + reset + "\n"
|
1695
|
+
end
|
1696
|
+
return 0
|
1697
|
+
else
|
1698
|
+
if !options[:quiet]
|
1699
|
+
print red + "ERROR" + reset + " HTTP #{http_response.code}" + "\n"
|
1700
|
+
end
|
1701
|
+
# F it, just remove a bad result
|
1702
|
+
if File.exists?(outfile) && File.file?(outfile)
|
1703
|
+
Morpheus::Logging::DarkPrinter.puts "Deleting bad file download: #{outfile}" if Morpheus::Logging.debug?
|
1704
|
+
File.delete(outfile)
|
1705
|
+
end
|
1706
|
+
if options[:debug]
|
1707
|
+
puts_error http_response.inspect
|
1708
|
+
end
|
1709
|
+
return 1
|
1710
|
+
end
|
1711
|
+
|
1712
|
+
rescue RestClient::Exception => e
|
1713
|
+
print_rest_exception(e, options)
|
1714
|
+
return 1
|
1715
|
+
end
|
1716
|
+
end
|
1717
|
+
|
1718
|
+
|
1719
|
+
private
|
1720
|
+
|
1721
|
+
def find_archive_bucket_by_name_or_id(val)
|
1722
|
+
return find_archive_bucket_by_id(val)
|
1723
|
+
# if val.to_s =~ /\A\d{1,}\Z/
|
1724
|
+
# return find_archive_bucket_by_id(val)
|
1725
|
+
# else
|
1726
|
+
# return find_archive_bucket_by_name(val)
|
1727
|
+
# end
|
1728
|
+
end
|
1729
|
+
|
1730
|
+
def find_archive_bucket_by_id(id)
|
1731
|
+
begin
|
1732
|
+
# this is typically passed as name, the api supports either name or id
|
1733
|
+
json_response = @archive_buckets_interface.get(id.to_s)
|
1734
|
+
archive_bucket = json_response['archiveBucket']
|
1735
|
+
archive_bucket['isOwner'] = !!json_response['isOwner']
|
1736
|
+
return archive_bucket
|
1737
|
+
rescue RestClient::Exception => e
|
1738
|
+
if e.response && e.response.code == 404
|
1739
|
+
print_red_alert "Archive bucket not found by id #{id}"
|
1740
|
+
return nil
|
1741
|
+
else
|
1742
|
+
raise e
|
1743
|
+
end
|
1744
|
+
end
|
1745
|
+
end
|
1746
|
+
|
1747
|
+
def find_archive_bucket_by_name(name)
|
1748
|
+
archive_buckets = @archive_buckets_interface.list({name: name.to_s})['archiveBuckets']
|
1749
|
+
if archive_buckets.empty?
|
1750
|
+
print_red_alert "Archive bucket not found by name #{name}"
|
1751
|
+
return nil
|
1752
|
+
elsif archive_buckets.size > 1
|
1753
|
+
print_red_alert "#{archive_buckets.size} archive buckets found by name #{name}"
|
1754
|
+
# print_archive_buckets_table(archive_buckets, {color: red})
|
1755
|
+
rows = archive_buckets.collect do |it|
|
1756
|
+
{id: it['id'], name: it['name']}
|
1757
|
+
end
|
1758
|
+
print red
|
1759
|
+
tp rows, [:id, :name]
|
1760
|
+
print reset,"\n"
|
1761
|
+
return nil
|
1762
|
+
else
|
1763
|
+
return archive_buckets[0]
|
1764
|
+
end
|
1765
|
+
end
|
1766
|
+
|
1767
|
+
def find_archive_file_by_id(id)
|
1768
|
+
begin
|
1769
|
+
json_response = @archive_files_interface.get(id.to_s)
|
1770
|
+
return json_response['archiveFile']
|
1771
|
+
rescue RestClient::Exception => e
|
1772
|
+
if e.response && e.response.code == 404
|
1773
|
+
print_red_alert "Archive file not found by id #{id}"
|
1774
|
+
return nil
|
1775
|
+
else
|
1776
|
+
raise e
|
1777
|
+
end
|
1778
|
+
end
|
1779
|
+
end
|
1780
|
+
|
1781
|
+
def find_archive_file_by_bucket_and_path(bucket_id, file_path)
|
1782
|
+
if file_path.to_s.empty? || file_path.to_s.strip == "/"
|
1783
|
+
print_red_alert "Archive file not found for bucket: '#{bucket_id}' file: (blank)"
|
1784
|
+
return nil
|
1785
|
+
end
|
1786
|
+
# chomp leading and trailing slashes, the api isn't doin this right now.
|
1787
|
+
if file_path.size > 1 && file_path[-1] == "/"
|
1788
|
+
file_path = file_path[0..-2]
|
1789
|
+
end
|
1790
|
+
if file_path[0] && file_path[0].chr == "/"
|
1791
|
+
file_path = file_path[1..-1]
|
1792
|
+
end
|
1793
|
+
# ok, find the file id by searching /archives/buckets/$bucketId/files/$filePath
|
1794
|
+
json_response = @archive_buckets_interface.list_files(bucket_id, file_path)
|
1795
|
+
# json_response = @archive_buckets_interface.list_files(bucket_id, "/", {phrase: file_path})
|
1796
|
+
# json_response = @archive_buckets_interface.list_files(bucket_id, "/", {absoluteFilePath: file_path})
|
1797
|
+
# puts "find_archive_file() json_response is: ", JSON.pretty_generate(json_response)
|
1798
|
+
archive_file = nil
|
1799
|
+
archive_files = json_response['archiveFiles']
|
1800
|
+
# silly hack, not needed while using ?absoluteFilePath=
|
1801
|
+
if json_response['parentDirectory'] && json_response['parentDirectory']['filePath'] == file_path
|
1802
|
+
archive_file = json_response['parentDirectory']
|
1803
|
+
else
|
1804
|
+
archive_file = archive_files[0]
|
1805
|
+
end
|
1806
|
+
if archive_file.nil?
|
1807
|
+
print_red_alert "Archive file not found for bucket: '#{bucket_id}' file: '#{file_path}'"
|
1808
|
+
return nil
|
1809
|
+
end
|
1810
|
+
return archive_file
|
1811
|
+
end
|
1812
|
+
|
1813
|
+
# def find_group_by_name(name)
|
1814
|
+
# group_results = @groups_interface.get(name)
|
1815
|
+
# if group_results['groups'].empty?
|
1816
|
+
# print_red_alert "Group not found by name #{name}"
|
1817
|
+
# return nil
|
1818
|
+
# end
|
1819
|
+
# return group_results['groups'][0]
|
1820
|
+
# end
|
1821
|
+
|
1822
|
+
# def find_cloud_by_name(group_id, name)
|
1823
|
+
# option_results = @options_interface.options_for_source('clouds',{groupId: group_id})
|
1824
|
+
# match = option_results['data'].find { |grp| grp['value'].to_s == name.to_s || grp['name'].downcase == name.downcase}
|
1825
|
+
# if match.nil?
|
1826
|
+
# print_red_alert "Cloud not found by name #{name}"
|
1827
|
+
# return nil
|
1828
|
+
# else
|
1829
|
+
# return match['value']
|
1830
|
+
# end
|
1831
|
+
# end
|
1832
|
+
|
1833
|
+
|
1834
|
+
def format_archive_bucket_full_status(archive_bucket, return_color=cyan)
|
1835
|
+
out = ""
|
1836
|
+
if archive_bucket['lastResult']
|
1837
|
+
out << format_archive_bucket_execution_status(archive_bucket['lastResult'])
|
1838
|
+
else
|
1839
|
+
out << ""
|
1840
|
+
end
|
1841
|
+
out
|
1842
|
+
end
|
1843
|
+
|
1844
|
+
def format_archive_bucket_status(archive_bucket, return_color=cyan)
|
1845
|
+
out = ""
|
1846
|
+
if archive_bucket['lastResult']
|
1847
|
+
out << format_archive_bucket_execution_status(archive_bucket['lastResult'])
|
1848
|
+
else
|
1849
|
+
out << ""
|
1850
|
+
end
|
1851
|
+
out
|
1852
|
+
end
|
1853
|
+
|
1854
|
+
def format_archive_bucket_execution_status(archive_bucket_execution, return_color=cyan)
|
1855
|
+
out = ""
|
1856
|
+
status_string = archive_bucket_execution['status']
|
1857
|
+
if status_string == 'running'
|
1858
|
+
out << "#{green}#{status_string.upcase}#{return_color}"
|
1859
|
+
elsif status_string == 'success'
|
1860
|
+
out << "#{green}#{status_string.upcase}#{return_color}"
|
1861
|
+
elsif status_string == 'failed'
|
1862
|
+
out << "#{red}#{status_string.upcase}#{return_color}"
|
1863
|
+
elsif status_string == 'pending'
|
1864
|
+
out << "#{white}#{status_string.upcase}#{return_color}"
|
1865
|
+
elsif status_string
|
1866
|
+
out << "#{yellow}#{status_string.upcase}#{return_color}"
|
1867
|
+
else
|
1868
|
+
out << ""
|
1869
|
+
end
|
1870
|
+
out
|
1871
|
+
end
|
1872
|
+
|
1873
|
+
def format_archive_bucket_execution_result(archive_bucket_execution, return_color=cyan)
|
1874
|
+
out = ""
|
1875
|
+
status_string = archive_bucket_execution['status']
|
1876
|
+
if status_string == 'running' # || status_string == 'pending'
|
1877
|
+
out << generate_usage_bar(archive_bucket_execution['statusPercent'], 100, {max_bars: 10})
|
1878
|
+
out << return_color if return_color
|
1879
|
+
out << " - #{archive_bucket_execution['statusMessage']}"
|
1880
|
+
elsif archive_bucket_execution['statusMessage']
|
1881
|
+
out << "#{archive_bucket_execution['statusMessage']}"
|
1882
|
+
end
|
1883
|
+
if archive_bucket_execution['errorMessage']
|
1884
|
+
out << " - #{red}#{archive_bucket_execution['errorMessage']}#{return_color}"
|
1885
|
+
end
|
1886
|
+
out
|
1887
|
+
end
|
1888
|
+
|
1889
|
+
# def get_available_boot_scripts()
|
1890
|
+
# boot_scripts_dropdown = []
|
1891
|
+
# scripts = @boot_scripts_interface.list({max:1000})['bootScripts']
|
1892
|
+
# scripts.each do |it|
|
1893
|
+
# boot_scripts_dropdown << {'name'=>it['fileName'],'value'=>it['id']}
|
1894
|
+
# end
|
1895
|
+
# boot_scripts_dropdown << {'name'=>'Custom','value'=> 'custom'}
|
1896
|
+
# return boot_scripts_dropdown
|
1897
|
+
# end
|
1898
|
+
|
1899
|
+
def get_available_boot_scripts(refresh=false)
|
1900
|
+
if !@available_boot_scripts || refresh
|
1901
|
+
# option_results = options_interface.options_for_source('bootScripts',{})['data']
|
1902
|
+
boot_scripts_dropdown = []
|
1903
|
+
scripts = @boot_scripts_interface.list({max:1000})['bootScripts']
|
1904
|
+
scripts.each do |it|
|
1905
|
+
boot_scripts_dropdown << {'name'=>it['fileName'],'value'=>it['id'],'id'=>it['id']}
|
1906
|
+
end
|
1907
|
+
boot_scripts_dropdown << {'name'=>'Custom','value'=> 'custom','id'=> 'custom'}
|
1908
|
+
@available_boot_scripts = boot_scripts_dropdown
|
1909
|
+
end
|
1910
|
+
#puts "available_boot_scripts() rtn: #{@available_boot_scripts.inspect}"
|
1911
|
+
return @available_boot_scripts
|
1912
|
+
end
|
1913
|
+
|
1914
|
+
def find_boot_script(val)
|
1915
|
+
if val.nil? || val.to_s.empty?
|
1916
|
+
return nil
|
1917
|
+
else
|
1918
|
+
return get_available_boot_scripts().find { |it|
|
1919
|
+
(it['id'].to_s.downcase == val.to_s.downcase) ||
|
1920
|
+
(it['name'].to_s.downcase == val.to_s.downcase)
|
1921
|
+
}
|
1922
|
+
end
|
1923
|
+
end
|
1924
|
+
|
1925
|
+
def get_available_preseed_scripts(refresh=false)
|
1926
|
+
if !@available_preseed_scripts || refresh
|
1927
|
+
# option_results = options_interface.options_for_source('preseedScripts',{})['data']
|
1928
|
+
preseed_scripts_dropdown = []
|
1929
|
+
scripts = @preseed_scripts_interface.list({max:1000})['preseedScripts']
|
1930
|
+
scripts.each do |it|
|
1931
|
+
preseed_scripts_dropdown << {'name'=>it['fileName'],'value'=>it['id'],'id'=>it['id']}
|
1932
|
+
end
|
1933
|
+
# preseed_scripts_dropdown << {'name'=>'Custom','value'=> 'custom','value'=> 'custom'}
|
1934
|
+
@available_preseed_scripts = preseed_scripts_dropdown
|
1935
|
+
end
|
1936
|
+
#puts "available_preseed_scripts() rtn: #{@available_preseed_scripts.inspect}"
|
1937
|
+
return @available_preseed_scripts
|
1938
|
+
end
|
1939
|
+
|
1940
|
+
def find_preseed_script(val)
|
1941
|
+
if val.nil? || val.to_s.empty?
|
1942
|
+
return nil
|
1943
|
+
else
|
1944
|
+
return get_available_preseed_scripts().find { |it|
|
1945
|
+
(it['id'].to_s.downcase == val.to_s.downcase) ||
|
1946
|
+
(it['name'].to_s.downcase == val.to_s.downcase)
|
1947
|
+
}
|
1948
|
+
end
|
1949
|
+
end
|
1950
|
+
|
1951
|
+
def prompt_edit_archive_bucket(archive_bucket, options={})
|
1952
|
+
# populate default prompt values with the existing archive bucket
|
1953
|
+
default_values = archive_bucket.dup # lazy, but works as long as GET matches POST api structure
|
1954
|
+
# storage provider (cannot be edited anyhow..)
|
1955
|
+
if archive_bucket['storageProvider'].kind_of?(Hash)
|
1956
|
+
default_values['storageProvider'] = archive_bucket['storageProvider']['id']
|
1957
|
+
end
|
1958
|
+
# tenants
|
1959
|
+
if archive_bucket['accounts'].kind_of?(Array) && archive_bucket['accounts'].size > 0
|
1960
|
+
default_values['accounts'] = archive_bucket['accounts'].collect {|it| it['name'] }.join(", ")
|
1961
|
+
end
|
1962
|
+
# any other mismatches? preseedScript, bootScript?
|
1963
|
+
options[:is_edit] = true
|
1964
|
+
return prompt_new_archive_bucket(options, default_values)
|
1965
|
+
end
|
1966
|
+
|
1967
|
+
def prompt_new_archive_bucket(options={}, default_values={})
|
1968
|
+
payload = {}
|
1969
|
+
|
1970
|
+
# Name
|
1971
|
+
if options['name']
|
1972
|
+
payload['name'] = options['name']
|
1973
|
+
else
|
1974
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Enter a name for this archive bucket.', 'defaultValue' => default_values['name']}], options, @api_client)
|
1975
|
+
payload['name'] = v_prompt['name']
|
1976
|
+
end
|
1977
|
+
|
1978
|
+
# Description
|
1979
|
+
if options['description']
|
1980
|
+
payload['description'] = options['description']
|
1981
|
+
else
|
1982
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'defaultValue' => default_values['description']}], options, @api_client)
|
1983
|
+
payload['description'] = v_prompt['description']
|
1984
|
+
end
|
1985
|
+
|
1986
|
+
# Storage Provider
|
1987
|
+
unless options[:is_edit]
|
1988
|
+
if options['storageProvider']
|
1989
|
+
# payload['storageProvider'] = options['storageProvider']
|
1990
|
+
# prompt is skipped when options['fieldName'] is passed in, this will return an id from a name
|
1991
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'storageProvider', 'fieldLabel' => 'Storage Provider', 'type' => 'select', 'optionSource' => 'storageProviders', 'description' => 'Storage Provider', 'defaultValue' => options['storageProvider'], 'required' => true}], options, @api_client, {})
|
1992
|
+
payload['storageProvider'] = {id: v_prompt['storageProvider']}
|
1993
|
+
else
|
1994
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'storageProvider', 'fieldLabel' => 'Storage Provider', 'type' => 'select', 'optionSource' => 'storageProviders', 'description' => 'Storage Provider', 'defaultValue' => default_values['storageProvider'], 'required' => true}], options, @api_client, {})
|
1995
|
+
payload['storageProvider'] = {id: v_prompt['storageProvider']}
|
1996
|
+
end
|
1997
|
+
end
|
1998
|
+
|
1999
|
+
# Tenants
|
2000
|
+
# TODO: a nice select component for adding/removing from this array
|
2001
|
+
if options['accounts']
|
2002
|
+
payload['accounts'] = options['accounts'] #.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
|
2003
|
+
else
|
2004
|
+
tenants_default_value = default_values['accounts']
|
2005
|
+
if tenants_default_value.kind_of?(Array)
|
2006
|
+
tenants_default_value = tenants_default_value.collect {|it| it["id"] }.join(", ")
|
2007
|
+
end
|
2008
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'accounts', 'fieldLabel' => 'Tenants', 'type' => 'text', 'description' => 'Tenant Accounts (comma separated ids)', 'defaultValue' => tenants_default_value}], options, @api_client)
|
2009
|
+
payload['accounts'] = v_prompt['accounts'].to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
|
2010
|
+
end
|
2011
|
+
|
2012
|
+
# Visibility
|
2013
|
+
if options['visibility']
|
2014
|
+
payload['visibility'] = options['visibility']
|
2015
|
+
else
|
2016
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'visibility', 'fieldLabel' => 'Visibility', 'type' => 'select', 'selectOptions' => [{'name' => 'Private', 'value' => 'private'}, {'name' => 'Public', 'value' => 'public'}], 'defaultValue' => (default_values['visibility'] || 'private'), 'required' => true}], options, @api_client, {})
|
2017
|
+
payload['visibility'] = v_prompt['visibility']
|
2018
|
+
end
|
2019
|
+
|
2020
|
+
# Public URL
|
2021
|
+
if options['isPublic']
|
2022
|
+
payload['isPublic'] = options['isPublic']
|
2023
|
+
else
|
2024
|
+
v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'isPublic', 'fieldLabel' => 'Public URL', 'type' => 'checkbox', 'description' => 'Enabling Public URL allows files to be downloaded without any authentication.', 'defaultValue' => (default_values['isPublic'].nil? ? false : default_values['isPublic']), 'required' => true}], options, @api_client, {})
|
2025
|
+
payload['isPublic'] = v_prompt['isPublic']
|
2026
|
+
end
|
2027
|
+
|
2028
|
+
return payload
|
2029
|
+
end
|
2030
|
+
|
2031
|
+
def print_archive_files_table(archive_files, options={})
|
2032
|
+
table_color = options[:color] || cyan
|
2033
|
+
rows = archive_files.collect do |archive_file|
|
2034
|
+
{
|
2035
|
+
id: archive_file['id'],
|
2036
|
+
name: options[:fullTree] ? archive_file['filePath'] : archive_file['name'],
|
2037
|
+
type: archive_file['isDirectory'] ? 'directory' : (archive_file['contentType']),
|
2038
|
+
size: format_bytes(archive_file['rawSize']),
|
2039
|
+
dateCreated: format_local_dt(archive_file['dateCreated']),
|
2040
|
+
lastUpdated: format_local_dt(archive_file['lastUpdated'])
|
2041
|
+
}
|
2042
|
+
end
|
2043
|
+
columns = [
|
2044
|
+
# :id,
|
2045
|
+
{:name => {:display_name => "File".upcase} },
|
2046
|
+
:type,
|
2047
|
+
:size,
|
2048
|
+
# {:dateCreated => {:display_name => "Date Created"} },
|
2049
|
+
{:lastUpdated => {:display_name => "Last Modified".upcase} }
|
2050
|
+
]
|
2051
|
+
print table_color
|
2052
|
+
print as_pretty_table(rows, columns, options)
|
2053
|
+
print reset
|
2054
|
+
end
|
2055
|
+
|
2056
|
+
def print_archive_logs_table(archive_logs, opts={})
|
2057
|
+
table_color = opts[:color] || cyan
|
2058
|
+
rows = archive_logs.collect do |archive_log|
|
2059
|
+
response_str = ""
|
2060
|
+
if archive_log['responseCode']
|
2061
|
+
if archive_log['responseCode'].to_i == 200
|
2062
|
+
response_str = "#{green}#{archive_log['responseCode']} #{archive_log['responseMessage']}".strip + table_color
|
2063
|
+
else
|
2064
|
+
response_str = "#{red}#{archive_log['responseCode']}#{reset} #{archive_log['responseMessage']}".strip + table_color
|
2065
|
+
end
|
2066
|
+
end
|
2067
|
+
if archive_log['fileSize'] && archive_log['fileSize'].to_i > 0
|
2068
|
+
response_str << " (#{format_bytes(archive_log['fileSize'])})"
|
2069
|
+
end
|
2070
|
+
request_str = "#{archive_log['requestUrl']}".strip
|
2071
|
+
# if archive_log['archiveFileLink']
|
2072
|
+
# request_str << " [Link #{archive_log['archiveFileLink']['shortKey']}]"
|
2073
|
+
# end
|
2074
|
+
{
|
2075
|
+
id: archive_log['id'],
|
2076
|
+
eventType: archive_log['eventType'] ? archive_log['eventType'] : '',
|
2077
|
+
bucket: archive_log['archiveBucket'] ? archive_log['archiveBucket']['name'] : '',
|
2078
|
+
file: archive_log['archiveFile'] ? archive_log['archiveFile']['filePath'] : '',
|
2079
|
+
link: archive_log['archiveFileLink'] ? archive_log['archiveFileLink']['shortKey'] : '',
|
2080
|
+
description: archive_log['description'] ? archive_log['description'] : '',
|
2081
|
+
fileSize: format_bytes(archive_log['fileSize']),
|
2082
|
+
dateCreated: format_local_dt(archive_log['dateCreated']),
|
2083
|
+
user: archive_log['user'] ? archive_log['user']['username'] : '',
|
2084
|
+
webInterface: archive_log['webInterface'],
|
2085
|
+
#request: request_str,
|
2086
|
+
response: response_str
|
2087
|
+
}
|
2088
|
+
end
|
2089
|
+
columns = [
|
2090
|
+
{:dateCreated => {:display_name => "Date".upcase} },
|
2091
|
+
{:eventType => {:display_name => "Event".upcase} },
|
2092
|
+
:bucket,
|
2093
|
+
:file,
|
2094
|
+
:link,
|
2095
|
+
# :fileSize,
|
2096
|
+
:description,
|
2097
|
+
:user,
|
2098
|
+
{:webInterface => {:display_name => "Interface".upcase} },
|
2099
|
+
#:request,
|
2100
|
+
:response
|
2101
|
+
]
|
2102
|
+
if opts[:exclude]
|
2103
|
+
columns = columns.reject {|c|
|
2104
|
+
c.is_a?(Hash) ? opts[:exclude].include?(c.keys[0]) : opts[:exclude].include?(c)
|
2105
|
+
}
|
2106
|
+
end
|
2107
|
+
print table_color
|
2108
|
+
print as_pretty_table(rows, columns, opts)
|
2109
|
+
print reset
|
2110
|
+
end
|
2111
|
+
|
2112
|
+
def print_archive_file_links_table(archive_file_links, opts={})
|
2113
|
+
table_color = opts[:color] || cyan
|
2114
|
+
rows = archive_file_links.collect do |archive_file_link|
|
2115
|
+
status_str = ""
|
2116
|
+
begin
|
2117
|
+
if archive_file_link['expirationDate'] && Time.now.utc > parse_time(archive_file_link['expirationDate'])
|
2118
|
+
status_str = red + "EXPIRED" + table_color
|
2119
|
+
else
|
2120
|
+
status_str = green + "ACTIVE" + table_color
|
2121
|
+
end
|
2122
|
+
rescue => ex
|
2123
|
+
Morpheus::Logging::DarkPrinter.puts "trouble parsing expiration date: #{ex.inspect}" if Morpheus::Logging.debug?
|
2124
|
+
end
|
2125
|
+
link_url = "/public-archives/link?s=" + archive_file_link['secretAccessKey'].to_s
|
2126
|
+
{
|
2127
|
+
url: link_url,
|
2128
|
+
created: format_local_dt(archive_file_link['dateCreated']),
|
2129
|
+
expires: archive_file_link['expirationDate'] ? format_local_dt(archive_file_link['expirationDate']) : 'Never',
|
2130
|
+
downloads: archive_file_link['downloadCount'],
|
2131
|
+
status: status_str
|
2132
|
+
}
|
2133
|
+
end
|
2134
|
+
columns = [
|
2135
|
+
{:url => {:display_name => "Link URL".upcase} },
|
2136
|
+
:created,
|
2137
|
+
:expires,
|
2138
|
+
:downloads,
|
2139
|
+
:status
|
2140
|
+
]
|
2141
|
+
print table_color
|
2142
|
+
print as_pretty_table(rows, columns, opts)
|
2143
|
+
print reset
|
2144
|
+
end
|
2145
|
+
|
2146
|
+
# parse_bucket_id_and_file_path() provides flexible argument formats for bucket and path
|
2147
|
+
# it looks for [bucket:/path] or [bucket] [path]
|
2148
|
+
# @param delim [String] Default is a comma and any surrounding white space.
|
2149
|
+
# @return [Array] 2 elements, bucket name (or id) and the file path.
|
2150
|
+
# The default file path is "/".
|
2151
|
+
# Examples:
|
2152
|
+
# parse_bucket_id_and_file_path("test") == ["test", "/"]
|
2153
|
+
# parse_bucket_id_and_file_path("test:/global.cfg") == ["test", "/global.cfg"]
|
2154
|
+
# parse_bucket_id_and_file_path("test:/node1/node.cfg") == ["test", "/node1/node.cfg"]
|
2155
|
+
# parse_bucket_id_and_file_path("test/node1/node.cfg") == ["test", "/node1/node.cfg"]
|
2156
|
+
# parse_bucket_id_and_file_path("test", "node1/node.cfg") == ["test", "/node1/node.cfg"]
|
2157
|
+
#
|
2158
|
+
def parse_bucket_id_and_file_path(*args)
|
2159
|
+
if args.size < 1 || args.size > 2
|
2160
|
+
return nil, nil
|
2161
|
+
end
|
2162
|
+
if !args[0]
|
2163
|
+
return nil, nil
|
2164
|
+
end
|
2165
|
+
full_path = args[0].to_s
|
2166
|
+
if args[1]
|
2167
|
+
if full_path.include?(":")
|
2168
|
+
full_path = "#{full_path}/#{args[1]}"
|
2169
|
+
else
|
2170
|
+
full_path = "#{full_path}:#{args[1]}"
|
2171
|
+
end
|
2172
|
+
end
|
2173
|
+
# ok fine, allow just bucketId/filePath, without a colon.
|
2174
|
+
if !full_path.include?(":") && full_path.include?("/")
|
2175
|
+
path_elements = full_path.split("/")
|
2176
|
+
full_path = path_elements[0] + ":" + path_elements[1..-1].join("/")
|
2177
|
+
end
|
2178
|
+
uri_elements = full_path.split(":")
|
2179
|
+
bucket_id = uri_elements[0]
|
2180
|
+
file_path = uri_elements[1..-1].join("/") # [1]
|
2181
|
+
file_path = "/#{file_path}".squeeze("/")
|
2182
|
+
return bucket_id, file_path
|
2183
|
+
end
|
2184
|
+
end
|