class Morpheus::Cli::ArchivesCommand

Public Class Methods

new() click to toggle source

set_default_subcommand :list

# File lib/morpheus/cli/commands/archives_command.rb, line 39
def initialize()
  # @appliance_name, @appliance_url = Morpheus::Cli::Remote.active_appliance
end

Public Instance Methods

add_bucket(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 238
def add_bucket(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[options]")
    opts.on('--name VALUE', String, "Name") do |val|
      options['name'] = val
    end
    opts.on('--description VALUE', String, "Description") do |val|
      options['description'] = val
    end
    opts.on('--storageProvider VALUE', String, "Storage Provider ID") do |val|
      options['storageProvider'] = val.to_s
    end
    opts.on('--visibility [private|public]', String, "Visibility determines if read access is restricted to the specified Tenants (Private) or all tenants (Public).") do |val|
      options['visibility'] = val.to_s
    end
    opts.on('--accounts LIST', String, "Tenant Accounts (comma separated ids)") do |val|
      # uh don't put commas or leading/trailing spaces in script names pl
      options['accounts'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
    end
    opts.on('--isPublic [on|off]', String, "Enabling Public URL allows files to be downloaded without any authentication.") do |val|
      options['isPublic'] = (val.to_s == 'on' || val.to_s == 'true')
    end
    build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
    opts.footer = "Create a new archive bucket."
  end
  optparse.parse!(args)
  connect(options)
  begin
    options.merge!(options[:options]) if options[:options] # so -O var= works..
    option_params = options.reject {|k,v| k.is_a?(Symbol) }
    # use the -g GROUP or active group by default
    # options['group'] ||=  @active_group_id
    
    # support first arg as name instead of --name
    if args[0] && !options['name']
      options['name'] = args[0]
    end
    payload = nil
    if options[:payload]
      payload = options[:payload]
      payload.deep_merge!({'archiveBucket' => option_params}) if !option_params.empty?
    else
      archive_bucket_payload = prompt_new_archive_bucket(options)
      return 1 if !archive_bucket_payload
      payload = {'archiveBucket' => archive_bucket_payload}
    end
    @archive_buckets_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @archive_buckets_interface.dry.create(payload)
      return
    end
    json_response = @archive_buckets_interface.create(payload)
    if options[:json]
      print JSON.pretty_generate(json_response)
      print "\n"
    elsif !options[:quiet]
      new_archive_bucket = json_response['archiveBucket']
      print_green_success "Added archive bucket #{new_archive_bucket['name']}"
      get_bucket([new_archive_bucket['id']])
      # list([])
    end

  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
end
connect(opts) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 43
def connect(opts)
  @api_client = establish_remote_appliance_connection(opts)
  @archive_buckets_interface = @api_client.archive_buckets
  @archive_files_interface = @api_client.archive_files
  @options_interface = @api_client.options
  # @active_group_id = Morpheus::Cli::Groups.active_groups[@appliance_name]
end
download_bucket_zip(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1603
def download_bucket_zip(args)
  options = {}
  outfile = nil
  do_overwrite = false
  do_mkdir = false
  use_public_url = false
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[bucket] [local-file]")
    opts.on( '-f', '--force', "Overwrite existing [local-file] if it exists." ) do
      do_overwrite = true
      # do_mkdir = true
    end
    opts.on( '-p', '--mkdir', "Create missing directories for [local-file] if they do not exist." ) do
      do_mkdir = true
    end
    # api endpoint needed still for public bucket.zip
    # opts.on( '-p', '--public', "Use Public Download URL instead of Private. The bucket must be have Public URL enabled." ) do
    #   use_public_url = true
    #   # do_mkdir = true
    # end
    build_common_options(opts, options, [:dry_run, :quiet, :remote])
    opts.footer = "Download an entire archive bucket as a .zip file.\n" + 
                  "[bucket] is required. This is the name of the bucket.\n" +
                  "[local-file] is required. This is the full local filepath for the downloaded file.\n" +
                  "Buckets are be downloaded as a .zip file, so you'll want to specify a [local-file] with a .zip extension."
  end
  optparse.parse!(args)
  if args.count != 2
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} download-bucket expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
    return 1
  end
  connect(options)
  begin
    bucket_id = args[0].to_s
    archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
    return 1 if archive_bucket.nil?
    
    outfile = args[1]
    # if outfile[-1] == "/" || outfile[-1] == "\\"
    #   outfile = File.join(outfile, archive_bucket['name'].to_s) + ".zip"
    # end
    outfile = File.expand_path(outfile)
    if Dir.exist?(outfile)
      outfile = File.join(outfile, archive_bucket['name'].to_s) + ".zip"
    end
    if Dir.exist?(outfile)
      print_red_alert "[local-file] is invalid. It is the name of an existing directory: #{outfile}"
      return 1
    end
    # always a .zip
    if outfile[-4..-1] != ".zip"
      outfile << ".zip"
    end
    destination_dir = File.dirname(outfile)
    if !Dir.exist?(destination_dir)
      if do_mkdir
        print cyan,"Creating local directory #{destination_dir}",reset,"\n"
        FileUtils.mkdir_p(destination_dir)
      else
        print_red_alert "[local-file] is invalid. Directory not found: #{destination_dir}"
        return 1
      end
    end
    if File.exist?(outfile)
      if do_overwrite
        # uhh need to be careful wih the passed filepath here..
        # don't delete, just overwrite.
        # File.delete(outfile)
      else
        print_error Morpheus::Terminal.angry_prompt
        puts_error "[local-file] is invalid. File already exists: #{outfile}", "Use -f to overwrite the existing file."
        # puts_error optparse
        return 1
      end
    end
    @archive_buckets_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @archive_buckets_interface.dry.download_bucket_zip_chunked(bucket_id, outfile)
      return 1
    end
    if !options[:quiet]
      print cyan + "Downloading archive bucket #{bucket_id} to #{outfile} ... "
    end

    http_response = @archive_buckets_interface.download_bucket_zip_chunked(bucket_id, outfile)

    # FileUtils.chmod(0600, outfile)
    success = http_response.code.to_i == 200
    if success
      if !options[:quiet]
        print green + "SUCCESS" + reset + "\n"
      end
      return 0
    else
      if !options[:quiet]
        print red + "ERROR" + reset + " HTTP #{http_response.code}" + "\n"
      end
      # F it, just remove a bad result
      if File.exist?(outfile) && File.file?(outfile)
        Morpheus::Logging::DarkPrinter.puts "Deleting bad file download: #{outfile}" if Morpheus::Logging.debug?
        File.delete(outfile)
      end
      if options[:debug]
        puts_error http_response.inspect
      end
      return 1
    end
    
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
end
download_file(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1211
def download_file(args)
  options = {}
  outfile = nil
  do_overwrite = false
  do_mkdir = false
  use_public_url = false
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[bucket:/path] [local-file]")
    opts.on( '-f', '--force', "Overwrite existing [local-file] if it exists." ) do
      do_overwrite = true
      # do_mkdir = true
    end
    opts.on( '-p', '--mkdir', "Create missing directories for [local-file] if they do not exist." ) do
      do_mkdir = true
    end
    opts.on( '-p', '--public', "Use Public Download URL instead of Private. The file must be in a public archives." ) do
      use_public_url = true
      # do_mkdir = true
    end
    build_common_options(opts, options, [:dry_run, :quiet, :remote])
    opts.footer = "Download an archive file or directory.\n" + 
                  "[bucket:/path] is required. This is the name of the bucket and /path the file or folder to be downloaded.\n" +
                  "[local-file] is required. This is the full local filepath for the downloaded file.\n" +
                  "Directories will be downloaded as a .zip file, so you'll want to specify a [local-file] with a .zip extension."
  end
  optparse.parse!(args)
  if args.count != 2
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} download expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
    return 1
  end
  connect(options)
  begin
    bucket_id, file_path  = parse_bucket_id_and_file_path(args[0])
    # just make 1 api call for now
    # archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
    # return 1 if archive_file.nil?
    full_file_path = "#{bucket_id}/#{file_path}".squeeze('/')
    # full_file_path = args[0]
    # end download destination with a slash to use the local file basename
    outfile = args[1]
    # if outfile[-1] == "/" || outfile[-1] == "\\"
    #   outfile = File.join(outfile, File.basename(full_file_path))
    # end
    outfile = File.expand_path(outfile)
    if Dir.exist?(outfile)
      outfile = File.join(outfile, File.basename(full_file_path))
    end
    if Dir.exist?(outfile)
      print_red_alert "[local-file] is invalid. It is the name of an existing directory: #{outfile}"
      return 1
    end
    destination_dir = File.dirname(outfile)
    if !Dir.exist?(destination_dir)
      if do_mkdir
        print cyan,"Creating local directory #{destination_dir}",reset,"\n"
        FileUtils.mkdir_p(destination_dir)
      else
        print_red_alert "[local-file] is invalid. Directory not found: #{destination_dir}"
        return 1
      end
    end
    if File.exist?(outfile)
      if do_overwrite
        # uhh need to be careful wih the passed filepath here..
        # don't delete, just overwrite.
        # File.delete(outfile)
      else
        print_error Morpheus::Terminal.angry_prompt
        puts_error "[local-file] is invalid. File already exists: #{outfile}", "Use -f to overwrite the existing file."
        # puts_error optparse
        return 1
      end
    end
    begin
      @archive_files_interface.setopts(options)
      if options[:dry_run]
        if use_public_url
          print_dry_run @archive_files_interface.dry.download_public_file_by_path_chunked(full_file_path, outfile)
        else
          print_dry_run @archive_files_interface.dry.download_file_by_path_chunked(full_file_path, outfile)
        end
        return 0
      end
      if !options[:quiet]
        print cyan + "Downloading archive file #{bucket_id}:#{file_path} to #{outfile} ... "
      end
      # file_response = @archive_files_interface.download_file_by_path(full_file_path)
      # File.write(outfile, file_response.body)
      # err, maybe write to a random tmp file, then mv to outfile
      # currently, whatever the response is, it's written to the outfile. eg. 404 html
      http_response = nil
      if use_public_url
        http_response = @archive_files_interface.download_public_file_by_path_chunked(full_file_path, outfile)
      else
        http_response = @archive_files_interface.download_file_by_path_chunked(full_file_path, outfile)
      end

      # FileUtils.chmod(0600, outfile)
      success = http_response.code.to_i == 200
      if success
        if !options[:quiet]
          print green + "SUCCESS" + reset + "\n"
        end
        return 0
      else
        if !options[:quiet]
          print red + "ERROR" + reset + " HTTP #{http_response.code}" + "\n"
        end
        # F it, just remove a bad result
        if File.exist?(outfile) && File.file?(outfile)
          Morpheus::Logging::DarkPrinter.puts "Deleting bad file download: #{outfile}" if Morpheus::Logging.debug?
          File.delete(outfile)
        end
        if options[:debug]
          puts_error http_response.inspect
        end
        return 1
      end
    rescue RestClient::Exception => e
      # this is not reached
      if e.response && e.response.code == 404
        print_red_alert "Archive file not found by path #{full_file_path}"
        return nil
      else
        raise e
      end
    end
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
  
end
file_history(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1084
def file_history(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[bucket:/path]")
    build_common_options(opts, options, [:list, :json, :dry_run, :remote])
    opts.footer = "List history log events for an archive file."
  end
  optparse.parse!(args)
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} file-history expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
    return 1
  end
  bucket_id, file_path  = parse_bucket_id_and_file_path(args[0])
  connect(options)
  begin
    # todo: only 1 api call needed here.
    # archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
    # return 1 if archive_bucket.nil?
    archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
    return 1 if archive_file.nil?
    # ok, load history
    params = {}
    [:phrase, :offset, :max, :sort, :direction].each do |k|
      params[k] = options[k] unless options[k].nil?
    end
    @archive_files_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @archive_files_interface.dry.history(archive_file['id'], params)
      return
    end
    json_response = @archive_files_interface.history(archive_file['id'], params)
    archive_logs = json_response['archiveLogs']

    if options[:json]
      print JSON.pretty_generate(json_response)
      return
    end

    print_h1 "Archive File History", ["#{bucket_id}:#{file_path}"]
    # print cyan
    # description_cols = {
    #   "File ID" => 'id',
    #   "Bucket" => lambda {|it| bucket_id },
    #   "File Path" => lambda {|it| file_path }
    # }
    # print_description_list(description_cols, archive_file)
    # print "\n"
    # print_h2 "History"
    if archive_logs && archive_logs.size > 0
      print_archive_logs_table(archive_logs)
      print_results_pagination(json_response, {:label => "history record", :n_label => "history records"})
    else
      puts "No history found"
    end
    print reset,"\n"
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
end
get_bucket(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 144
def get_bucket(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[bucket:/path]")
    build_common_options(opts, options, [:json, :dry_run, :remote])
    opts.footer = "Display archive bucket details and files. " +
                  "\nThe [bucket] component of the argument is the name or id of an archive bucket." +
                  "\nThe [:/path] component is optional and can be used to display files under a sub-directory."
  end
  optparse.parse!(args)
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} get expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
    return 1
  end
  bucket_id, search_file_path  = parse_bucket_id_and_file_path(args[0])
  connect(options)
  begin
    @archive_buckets_interface.setopts(options)
    if options[:dry_run]
      if args[0].to_s =~ /\A\d{1,}\Z/
        print_dry_run @archive_buckets_interface.dry.get(bucket_id.to_i)
      else
        print_dry_run @archive_buckets_interface.dry.list({name:bucket_id})
      end
      return
    end
    archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
    return 1 if archive_bucket.nil?
    json_response = {'archiveBucket' => archive_bucket}  # skip redundant request
    # json_response = @archive_buckets_interface.get(archive_bucket['id'])
    archive_bucket = json_response['archiveBucket']
    if options[:json]
      print JSON.pretty_generate(json_response)
      return
    end
    subtitles = []
    if search_file_path != "/"
      subtitles << "Path: #{search_file_path}"
    end
    print_h1 "Archive Bucket Details", subtitles
    print cyan

    description_cols = {
      "ID" => 'id',
      "Name" => 'name',
      "Description" => 'description',
      # "Created By" => lambda {|it| it['createdBy'] ? it['createdBy']['username'] : '' },
      "Owner" => lambda {|it| it['owner'] ? it['owner']['name'] : '' },
      "Tenants" => lambda {|it| it['accounts'] ? it['accounts'].collect {|acnt| acnt['name']}.join(', ') : '' },
      "Visibility" => lambda {|it| it['visibility'] ? it['visibility'].capitalize() : '' },
      "Public URL" => lambda {|it| it['isPublic'] ? 'Yes' : 'No' },
      "Storage" => lambda {|it| it['storageProvider'] ? it['storageProvider']['name'] : '' },
      "# Files" => lambda {|it| it['fileCount'] },
      "Size" => lambda {|it| format_bytes(it['rawSize']) },
      "Date Created" => lambda {|it| format_local_dt(it['dateCreated']) },
      "Last Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
    }
    print_description_list(description_cols, archive_bucket)

    # show files
    # search_file_path = "/"
    # if args[1]
    #   search_file_path = args[1]
    # end
    # if search_file_path[0].chr != "/"
    #   search_file_path = "/" + search_file_path
    # end
    # print_h2 "Path: #{search_file_path}"
    print "\n"
    archive_files_json_response = @archive_buckets_interface.list_files(archive_bucket['name'], search_file_path)
    archive_files = archive_files_json_response['archiveFiles']
    if archive_files && archive_files.size > 0
      # archive_files.each do |archive_file|
      #   puts " = #{archive_file['name']}"
      # end
      print_archive_files_table(archive_files)
    else
      if search_file_path.empty? || search_file_path == "/"
        puts "This archive bucket has no files."
      else
        puts "No files found for path #{search_file_path}"
      end
    end
    print cyan

    print reset,"\n"

  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
end
get_file(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 887
def get_file(args)
  options = {}
  max_links = 10
  max_history = 10
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[bucket:/path]")
    opts.on('-L', '--all-links', "Display all links instead of only 10." ) do
      max_links = 10000
    end
    opts.on('-H', '--all-history', "Display all history instead of only 10." ) do
      max_history = 10000
    end
    build_common_options(opts, options, [:json, :dry_run, :remote])
    opts.footer = "Get details about an archive file.\n" + 
                  "[bucket:/path] is required. This is the name of the bucket and /path the file or folder to be fetched." + "\n" +
                  "[id] can be passed instead of [bucket:/path]. This is the numeric File ID."
  end
  optparse.parse!(args)
  # consider only allowing args.count == 1 here in the format [bucket:/path]
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} get-file expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
    return 1
  end
  file_id = nil
  bucket_id = nil
  file_path = nil
  # allow id in place of bucket:path
  if args[0].to_s =~ /\A\d{1,}\Z/
    file_id = args[0]
  else
    bucket_id, file_path  = parse_bucket_id_and_file_path(args[0])
  end

  connect(options)
  begin
    # archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
    # return 1 if archive_bucket.nil?
    params = {}
    @archive_buckets_interface.setopts(options)
    @archive_files_interface.setopts(options)
    if options[:dry_run]
      if file_id
        print_dry_run @archive_files_interface.dry.get(file_id, params)
      else
        print_dry_run @archive_buckets_interface.dry.list_files(bucket_id, file_path, params)
      end
      return 0
    end
    archive_file = nil
    json_response = nil
    if !file_id
      archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
      return 1 if archive_file.nil?
      file_id = archive_file['id']
    end
    # archive_file = find_archive_file_by_id(file_id)
    json_response = @archive_files_interface.get(file_id, params)
    if options[:json]
      puts as_json(json_response, options)
      return 0
    end
    archive_file = json_response['archiveFile']
    archive_logs = json_response['archiveLogs']
    is_owner = json_response['isOwner']
    if !bucket_id && archive_file["archiveBucket"]
      bucket_id = archive_file["archiveBucket"]["name"]
    end

    print_h1 "Archive File Details"
    print cyan
    description_cols = {
      "File ID" => 'id',
      "Bucket" => lambda {|it| bucket_id },
      "File Path" => lambda {|it| it['filePath'] },
      "Type" => lambda {|it| it['isDirectory'] ? 'directory' : (it['contentType']) },
      "Size" => lambda {|it| format_bytes(it['rawSize']) },
      "Downloads" => lambda {|it| it['downloadCount'] },
      "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
      "Last Modified" => lambda {|it| format_local_dt(it['lastUpdated']) }
    }
    print_description_list(description_cols, archive_file)
    
    # print "\n"
    
    
    print_h2 "Download URLs"
    private_download_url = "#{@appliance_url}/api/archives/download/#{CGI::escape(bucket_id)}" + "/#{escape_filepath(archive_file['filePath'])}".squeeze('/')
    public_download_url = nil
    if archive_file['archiveBucket'] && archive_file['archiveBucket']['isPublic']
      public_download_url = "#{@appliance_url}/public-archives/download/#{CGI::escape(bucket_id)}" + "/#{escape_filepath(archive_file['filePath'])}".squeeze('/')
    end
    print cyan
    puts "Private URL: #{private_download_url}"
    if public_download_url
      puts " Public URL: #{public_download_url}"
    end

    do_show_links = is_owner
    if do_show_links
      links_json_response = @archive_files_interface.list_links(archive_file['id'], {max: max_links})
      archive_file_links = links_json_response['archiveFileLinks']
      if archive_file_links && archive_file_links.size > 0
        print_h2 "Links"
        print_archive_file_links_table(archive_file_links)
        print_results_pagination(links_json_response, {:label => "link", :n_label => "links"})
      else
        print_h2 "File Links"
        puts "No links found"
      end
    end
    # print "\n"
    do_show_history = is_owner
    if do_show_history
      history_json_response = @archive_files_interface.history(archive_file['id'], {max: max_history})
      archive_logs =         history_json_response['archiveLogs']
      print_h2 "History"
      if archive_logs && archive_logs.size > 0
        print_archive_logs_table(archive_logs, {exclude:[:bucket]})
        print_results_pagination(history_json_response, {:label => "history record", :n_label => "history records"})
      else
        puts "No history found"
      end
    end
    print reset,"\n"
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
end
handle(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 51
def handle(args)
  handle_subcommand(args)
end
list_buckets(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 55
def list_buckets(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage()
    build_common_options(opts, options, [:list, :json, :dry_run, :remote])
    opts.footer = "List archive buckets."
  end
  optparse.parse!(args)
  if args.count != 0
    raise_command_error "#{command_name} list expects 0 arguments\n#{optparse}"
  end
  connect(options)
  begin
    params = {}
    [:phrase, :offset, :max, :sort, :direction].each do |k|
      params[k] = options[k] unless options[k].nil?
    end
    @archive_buckets_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @archive_buckets_interface.dry.list(params)
      return
    end

    json_response = @archive_buckets_interface.list(params)
    if options[:json]
      print JSON.pretty_generate(json_response)
      print "\n"
      return
    end
    archive_buckets = json_response['archiveBuckets']
    title = "Morpheus Archive Buckets"
    subtitles = []
    # if group
    #   subtitles << "Group: #{group['name']}".strip
    # end
    # if cloud
    #   subtitles << "Cloud: #{cloud['name']}".strip
    # end
    if params[:phrase]
      subtitles << "Search: #{params[:phrase]}".strip
    end
    print_h1 title, subtitles
    if archive_buckets.empty?
      print cyan,"No archive buckets found.",reset,"\n"
    else
      rows = archive_buckets.collect {|archive_bucket| 
          row = {
            id: archive_bucket['id'],
            name: archive_bucket['name'],
            description: archive_bucket['description'],
            storageProvider: archive_bucket['storageProvider'] ? archive_bucket['storageProvider']['name'] : 'N/A',
            fileCount: archive_bucket['fileCount'],
            # createdBy: archive_bucket['createdBy'] ? archive_bucket['createdBy']['username'] : '',
            size: format_bytes(archive_bucket['rawSize']),
            owner: archive_bucket['owner'] ? archive_bucket['owner']['name'] : '',
            tenants: archive_bucket['accounts'] ? archive_bucket['accounts'].collect {|it| it['name'] }.join(', ') : '',
            visibility: archive_bucket['visibility'] ? archive_bucket['visibility'].capitalize() : '',
            isPublic: archive_bucket['isPublic'] ? 'Yes' : 'No'
          }
          row
        }
        columns = [
          :id, 
          :name, 
          {:storageProvider => {label: 'Storage'.upcase}}, 
          {:fileCount => {label: '# Files'.upcase}}, 
          :size,
          :owner,
          :tenants,
          :visibility,
          {:isPublic => {label: 'Public URL'.upcase}}
        ]
        
        if options[:include_fields]
          columns = options[:include_fields]
        end
        print cyan
        print as_pretty_table(rows, columns, options)
        print reset
        print_results_pagination(json_response, {:label => "bucket", :n_label => "buckets"})
        print reset,"\n"
    end
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
list_files(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 652
def list_files(args)
  options = {}
  params = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[bucket:/path]")
    opts.on('-a', '--all', "Show all files, including subdirectories under the /path.") do
      params[:fullTree] = true
    end
    build_common_options(opts, options, [:list, :json, :dry_run, :remote])
    opts.footer = "List files in an archive bucket. \nInclude [/path] to show files under a directory."
  end
  optparse.parse!(args)
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} list-files expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
    return 1
  end
  bucket_id, search_file_path  = parse_bucket_id_and_file_path(args[0])
  connect(options)
  begin
    archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
    return 1 if archive_bucket.nil?
    [:phrase, :offset, :max, :sort, :direction, :fullTree].each do |k|
      params[k] = options[k] unless options[k].nil?
    end
    if params[:phrase]
      params[:fullTree] = true # these are not exclusively supported by api yet
    end
    @archive_buckets_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @archive_buckets_interface.dry.list_files(bucket_id, search_file_path, params)
      return
    end
    json_response = @archive_buckets_interface.list_files(bucket_id, search_file_path, params)
    archive_files = json_response['archiveFiles']
    # archive_bucket = json_response['archiveBucket']
    if options[:json]
      print JSON.pretty_generate(json_response)
      return
    end
    print_h1 "Archive Files", ["#{archive_bucket['name']}:#{search_file_path}"]
    print cyan
    description_cols = {
      "Bucket ID" => 'id',
      "Bucket Name" => 'name',
      #"Path" => lambda {|it| search_file_path }
    }
    #print_description_list(description_cols, archive_bucket)
    #print "\n"
    #print_h2 "Path: #{search_file_path}"
    # print "Directory: #{search_file_path}"
    if archive_files && archive_files.size > 0
      print_archive_files_table(archive_files, {fullTree: params[:fullTree]})
      print_results_pagination(json_response, {:label => "file", :n_label => "files"})
    else
      # puts "No files found for path #{search_file_path}"
      if search_file_path.empty? || search_file_path == "/"
        puts "This archive bucket has no files."
      else
        puts "No files found for path #{search_file_path}"
        return 1
      end
    end
    print reset,"\n"
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
end
ls(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 723
def ls(args)
  options = {}
  params = {}
  do_one_file_per_line = false
  do_long_format = false
  do_human_bytes = false
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[bucket/path]")
    opts.on('-a', '--all', "Show all files, including subdirectories under the /path.") do
      params[:fullTree] = true
      do_one_file_per_line = true
    end
    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
      do_long_format = true
      do_one_file_per_line
    end
    opts.on('-H', '--human', "Humanized file sizes. The default is just the number of bytes.") do
      do_human_bytes = true
    end
    opts.on('-1', '--oneline', "One file per line. The default delimiter is a single space.") do
      do_one_file_per_line = true
    end
    build_common_options(opts, options, [:list, :json, :dry_run, :remote])
    opts.footer = "Print filenames for a given archive location.\nPass archive location in the format bucket/path."
  end
  optparse.parse!(args)
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} ls expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
    return 1
  end
  bucket_id, search_file_path  = parse_bucket_id_and_file_path(args[0])
  connect(options)
  begin
    # archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
    # return 1 if archive_bucket.nil?
    [:phrase, :offset, :max, :sort, :direction, :fullTree].each do |k|
      params[k] = options[k] unless options[k].nil?
    end
    @archive_buckets_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @archive_buckets_interface.dry.list_files(bucket_id, search_file_path, params)
      return 0
    end
    json_response = @archive_buckets_interface.list_files(bucket_id, search_file_path, params)
    if options[:json]
      puts as_json(json_response, options)
      # no files is an error condition for this command
      if !json_response['archiveFiles'] || json_response['archiveFiles'].size == 0
        return 1
      end
      return 0
    end
    archive_bucket = json_response['archiveBucket'] # yep, this is returned too
    archive_files = json_response['archiveFiles']
    # archive_bucket = json_response['archiveBucket']
    # print_h2 "Directory: #{search_file_path}"
    # print "Directory: #{search_file_path}"
    if archive_files && archive_files.size > 0
      if do_long_format
        # ls long format
        # owner groups filesize type filename
        now = Time.now
        archive_files.each do |archive_file|
          # -rw-r--r--    1 jdickson  staff   1361 Oct 23 08:00 voltron_2.10.log
          file_color = cyan # reset
          if archive_file['isDirectory']
            file_color = blue
          end
          file_info = []
          # Number of links
          # file_info << file["linkCount"].to_i + 1
          # Owner
          owner_str = ""
          if archive_file['owner']
            owner_str = archive_file['owner']['name']
          elsif archive_bucket['owner']
            owner_str = archive_bucket['owner']['name']
          else
            owner_str = "noone"
          end
          file_info << truncate_string(owner_str, 15).ljust(15, " ")
          # Group (Tenants)
          groups_str = ""
          if archive_file['visibility'] == 'public'
            # this is confusing because of Public URL (isPublic) setting
            groups_str = "public"
          else
            if archive_file['accounts'].instance_of?(Array) && archive_file['accounts'].size > 0
              # groups_str = archive_file['accounts'].collect {|it| it['name'] }.join(',')
              groups_str = (archive_file['accounts'].size == 1) ? "#{archive_file['accounts'][0]['name']}" : "#{archive_file['accounts'].size} tenants"
            elsif archive_bucket['accounts'].instance_of?(Array) && archive_bucket['accounts'].size > 0
              # groups_str = archive_bucket['accounts'].collect {|it| it['name'] }.join(',')
              groups_str = (archive_bucket['accounts'].size == 1) ? "#{archive_bucket['accounts'][0]['name']}" : "#{archive_bucket['accounts'].size} tenants"
            else
              groups_str = owner_str
            end
          end
          file_info << truncate_string(groups_str, 15).ljust(15, " ")
          # File Type
          content_type = archive_file['contentType'].to_s
          if archive_file['isDirectory']
            content_type = "directory"
          else
            content_type = archive_file['contentType'].to_s
          end
          file_info << content_type.ljust(25, " ")
          filesize_str = ""
          if do_human_bytes
            # filesize_str = format_bytes(archive_file['rawSize'])
            filesize_str = format_bytes_short(archive_file['rawSize'])
          else
            filesize_str = archive_file['rawSize'].to_i.to_s
          end
          # file_info << filesize_str.ljust(12, " ")
          file_info << filesize_str.ljust(7, " ")
          mtime = ""
          last_updated = parse_time(archive_file['lastUpdated'])
          if last_updated
            if last_updated.year == now.year
              mtime = format_local_dt(last_updated, {format: "%b %e %H:%M"})
            else
              mtime = format_local_dt(last_updated, {format: "%b %e %Y"})
            end
          end
          file_info << mtime.ljust(12, " ")
          if params[:fullTree]
            file_info << file_color + archive_file["filePath"].to_s + cyan
          else
            file_info << file_color + archive_file["name"].to_s + cyan
          end
          print cyan, file_info.join("  "), reset, "\n"
        end
      else
        file_names = archive_files.collect do |archive_file|
          file_color = cyan # reset
          if archive_file['isDirectory']
            file_color = blue
          end
          if params[:fullTree]
            file_color + archive_file["filePath"].to_s + reset
          else
            file_color + archive_file["name"].to_s + reset
          end
        end
        if do_one_file_per_line
          print file_names.join("\n")
        else
          print file_names.join("\t")
        end
        print "\n"
      end
    else
      print_error yellow, "No files found for path: #{search_file_path}", reset, "\n"
      return 1
    end
    
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
end
read_file(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1346
def read_file(args)
  options = {}
  outfile = nil
  do_overwrite = false
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[bucket:/path]")
    build_common_options(opts, options, [:auto_confirm, :dry_run, :remote])
    opts.footer = "Print the contents of an archive file.\n" + 
                  "[bucket:/path] is required. This is the name of the bucket and /path the file or folder to be downloaded.\n" +
                  "Confirmation is needed if the specified file is more than 1KB.\n" +
                  "This confirmation can be skipped with the -y option."
  end
  optparse.parse!(args)
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} read expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
    return 1
  end
  connect(options)
  begin
    bucket_id, file_path  = parse_bucket_id_and_file_path(args[0])
    archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
    return 1 if archive_file.nil?
    full_file_path = "#{bucket_id}/#{file_path}".squeeze('/')
    @archive_files_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @archive_files_interface.dry.download_file_by_path(full_file_path)
      return 1
    end
    if archive_file['rawSize'].to_i > 1024
      pretty_size = format_bytes(archive_file['rawSize'])
      unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to print the contents of this file (#{pretty_size}) ?")
        return 9, "aborted command"
      end
    end
    file_response = @archive_files_interface.download_file_by_path(full_file_path)
    puts file_response.body.to_s
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
  
end
remove_bucket(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 385
def remove_bucket(args)
  options = {}
  query_params = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[bucket]")
    build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
  end
  optparse.parse!(args)

  if args.count < 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} remove expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
    return 1
  end
  bucket_id = args[0]
  connect(options)
  begin
    # archive_bucket = find_archive_bucket_by_name_or_id(args[0])
    json_response = @archive_buckets_interface.get(bucket_id, {})
    archive_bucket = json_response['archiveBucket']
    is_owner = json_response['isOwner']
    return 1 if archive_bucket.nil?
    if is_owner == false
      print_red_alert "You must be the owner of archive bucket to remove it."
      return 3
    end
    unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the archive bucket: #{archive_bucket['name']}?")
      return 9, "aborted command"
    end
    @archive_buckets_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @archive_buckets_interface.dry.destroy(archive_bucket['id'], query_params)
      return 0
    end
    json_response = @archive_buckets_interface.destroy(archive_bucket['id'], query_params)
    if options[:json]
      print JSON.pretty_generate(json_response)
      print "\n"
    else
      print_green_success "Removed archive bucket #{archive_bucket['name']}"
      # list([])
    end
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
end
remove_file(args) click to toggle source

Use upload file bucket:/path def add_file(args)

raise "not yet implemented"

end

# File lib/morpheus/cli/commands/archives_command.rb, line 1024
def remove_file(args)
  options = {}
  query_params = {}
  do_recursive = false
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[bucket:/path]")
    opts.on( '-R', '--recursive', "Delete a directory and all of its files. This must be passed if specifying a directory." ) do
      do_recursive = true
    end
    build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
    opts.footer = "Delete an archive file or directory."
  end
  optparse.parse!(args)
  # consider only allowing args.count == 1 here in the format [bucket:/path]
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} remove-file expects 1 argument and received #{args.count}: #{args.join(' ')}\n#{optparse}"
    return 1
  end
  bucket_id, file_path  = parse_bucket_id_and_file_path(args[0])
  connect(options)
  begin
    
    archive_file = find_archive_file_by_bucket_and_path(bucket_id, file_path)
    return 1 if archive_file.nil?
    if archive_file['isDirectory']
      if !do_recursive
        print_error Morpheus::Terminal.angry_prompt
        puts_error  "bad argument: '#{file_path}' is a directory.  Use -R or --recursive to delete a directory.\n#{optparse}"
        return 1
      end
      unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the archive directory: #{args[0]}?")
        return 9, "aborted command"
      end
    else
      unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to delete the archive file: #{args[0]}?")
        return 9, "aborted command"
      end
    end
    @archive_files_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @archive_files_interface.dry.destroy(archive_file['id'], query_params)
      return 0
    end
    json_response = @archive_files_interface.destroy(archive_file['id'], query_params)
    if options[:json]
      print JSON.pretty_generate(json_response)
      print "\n"
    else
      print_green_success "Removed archive file #{args[0]}"
    end
    return 0

  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end

end
update_bucket(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 307
def update_bucket(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[bucket] [options]")
    opts.on('--name VALUE', String, "Name") do |val|
      options['name'] = val
    end
    opts.on('--description VALUE', String, "Description") do |val|
      options['description'] = val
    end
    # storage provider cannot be changed
    # opts.on('--storageProvider VALUE', String, "Storage Provider ID") do |val|
    #   options['storageProvider'] = val.to_s
    # end
    opts.on('--payload JSON', String, "JSON Payload") do |val|
      options['payload'] = JSON.parse(val.to_s)
    end
    opts.on('--payload-file FILE', String, "JSON Payload from a local file") do |val|
      payload_file = val.to_s
      options['payload'] = JSON.parse(File.read(payload_file))
    end
    
    opts.on('--visibility [private|public]', String, "Visibility determines if read access is restricted to the specified Tenants (Private) or all tenants (Public).") do |val|
      options['visibility'] = val.to_s
    end
    opts.on('--accounts LIST', String, "Tenant Accounts (comma separated ids)") do |val|
      # uh don't put commas or leading/trailing spaces in script names pl
      options['accounts'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
    end
    opts.on('--isPublic [on|off]', String, "Enabling Public URL allows files to be downloaded without any authentication.") do |val|
      options['isPublic'] = (val.to_s == 'on' || val.to_s == 'true')
    end
    build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
    opts.footer = "Update an existing archive bucket."
  end
  optparse.parse!(args)
  if args.count < 1
    puts optparse
    return 1
  end
  connect(options)

  begin
    archive_bucket = find_archive_bucket_by_name_or_id(args[0])

    options.merge!(options[:options]) if options[:options] # so -O var= works..
    option_params = options.reject {|k,v| k.is_a?(Symbol) }

    payload = nil
    if options[:payload]
      payload = options[:payload]
      payload.deep_merge!({'archiveBucket' => option_params}) if !option_params.empty?
    else
      archive_bucket_payload = prompt_edit_archive_bucket(archive_bucket, options)
      return 1 if !archive_bucket_payload
      payload = {'archiveBucket' => archive_bucket_payload}
    end
    @archive_buckets_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @archive_buckets_interface.dry.update(archive_bucket["id"], payload)
      return
    end

    json_response = @archive_buckets_interface.update(archive_bucket["id"], payload)
    if options[:json]
      print JSON.pretty_generate(json_response)
      print "\n"
    elsif !options[:quiet]
      print_green_success "Updated archive bucket #{archive_bucket['name']}"
      get([archive_bucket['id']])
    end
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
end
upload_file(args) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 434
def upload_file(args)
  options = {}
  query_params = {}
  do_recursive = false
  ignore_regexp = nil
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[local-file] [bucket:/path]")
    # 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|
    #   options['type'] = val
    # end
    opts.on( '-R', '--recursive', "Upload a directory and all of its files. This must be passed if [local-file] is a directory." ) do
      do_recursive = true
    end
    opts.on('--ignore-files PATTERN', String, "Pattern of files to be ignored when uploading a directory." ) do |val|
      ignore_regexp = /#{Regexp.escape(val)}/
    end
    opts.footer = "Upload a local file or folder to an archive bucket. " +
                  "\nThe first argument [local-file] should be the path of a local file or directory." +
                  "\nThe second argument [bucket:/path] should contain the bucket name." +
                  "\nThe [:/path] component is optional and can be used to specify the destination of the uploaded file or folder." +
                  "\nThe default destination is the same name as the [local-file], under the root bucket directory '/'. " +
                  "\nThis will overwrite any existing remote files that match the destination /path."
    build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :remote])
  end
  optparse.parse!(args)
  
  if args.count != 2
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} upload expects 2 arguments and received #{args.count}: #{args.join(' ')}\n#{optparse}"
    return 1
  end
  # validate local file path
  local_file_path = File.expand_path(args[0].squeeze('/'))
  if local_file_path == "" || local_file_path == "/" || local_file_path == "."
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} missing argument: [local-file]\n#{optparse}"
    return 1
  end
  if !File.exist?(local_file_path)
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} bad argument: [local-file]\nFile '#{local_file_path}' was not found.\n#{optparse}"
    return 1
  end

  # validate bucket:/path
  bucket_id, remote_file_path  = parse_bucket_id_and_file_path(args[1])

  # if local_file_path.include?('../') # || options[:yes]
  #   raise_command_error "Sorry, you may not use relative paths in your local filepath."
  # end
  
  # validate bucket name (or id)
  if !bucket_id
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} missing argument: [bucket]\n#{optparse}"
    return 1
  end
  
  # strip leading slash of remote name
  # if remote_file_path[0].chr == "/"
  #   remote_file_path = remote_file_path[1..-1]
  # end

  if remote_file_path.include?('./') # || options[:yes]
    raise_command_error "Sorry, you may not use relative paths in your remote filepath."
  end

  # if !options[:yes]
  scary_local_paths = ["/", "/root", "C:\\"]
  if scary_local_paths.include?(local_file_path)
    unless Morpheus::Cli::OptionTypes.confirm("Are you sure you want to upload all the files in local directory '#{local_file_path}' !?")
      return 9, "aborted command"
    end
  end
  # end

  connect(options)
  begin
    archive_bucket = find_archive_bucket_by_name_or_id(bucket_id)
    return 1 if archive_bucket.nil?

    # how many files we dealing with?
    files_to_upload = []
    if File.directory?(local_file_path)
      # upload directory
      if !do_recursive
        print_error Morpheus::Terminal.angry_prompt
        puts_error  "bad argument: '#{local_file_path}' is a directory.  Use -R or --recursive to upload a directory.\n#{optparse}"
        return 1
      end
      found_files = Dir.glob("#{local_file_path}/**/*")
      # note:  api call for directories is not needed
      found_files = found_files.select {|file| File.file?(file) }
      if ignore_regexp
        found_files = found_files.reject {|it| it =~ ignore_regexp} 
      end
      files_to_upload = found_files

      if files_to_upload.size == 0
        print_error Morpheus::Terminal.angry_prompt
        puts_error  "bad argument: Local directory '#{local_file_path}' contains 0 files."
        return 1
      end

      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}?")
        return 9, "aborted command"
      end

      if !options[:yes]
        if files_to_upload.size > 100
          unless Morpheus::Cli::OptionTypes.confirm("Are you REALLY sure you want to upload #{files_to_upload.size} files ?")
            return 9, "aborted command"
          end
        end
      end

      # local_dirname = File.dirname(local_file_path)
      # local_basename = File.basename(local_file_path)
      upload_file_list = []
      files_to_upload.each do |file|
        destination = file.sub(local_file_path, (remote_file_path || "")).squeeze('/')
        upload_file_list << {file: file, destination: destination}
      end

      @archive_buckets_interface.setopts(options)
      if options[:dry_run]
        # print_h1 "DRY RUN"
        print "\n",cyan, bold, "Uploading #{upload_file_list.size} Files...", reset, "\n"
        upload_file_list.each do |obj|
          file, destination = obj[:file], obj[:destination]
          #print cyan,bold, "  - Uploading #{file} to #{bucket_id}:#{destination} DRY RUN", reset, "\n"
          print_dry_run @archive_buckets_interface.dry.upload_file(bucket_id, file, destination)
          print "\n"
        end
        return 0
      end

      print "\n",cyan, bold, "Uploading #{upload_file_list.size} Files...", reset, "\n"
      bad_upload_responses = []
      upload_file_list.each do |obj|
        file, destination = obj[:file], obj[:destination]
        print cyan,bold, "  - Uploading #{file} to #{bucket_id}:#{destination}", reset
        upload_response = @archive_buckets_interface.upload_file(bucket_id, file, destination)
        if upload_response['success']
          print bold," #{green}SUCCESS#{reset}"
        else
          print bold," #{red}ERROR#{reset}"
          if upload_response['msg']
            bad_upload_responses << upload_response
            print " #{upload_response['msg']}#{reset}"
          end
        end
        print "\n"
      end
      if bad_upload_responses.size > 0
        print cyan, bold, "Completed Upload of #{upload_file_list.size} Files. #{red}#{bad_upload_responses.size} Errors!", reset, "\n"
      else
        print cyan, bold, "Completed Upload of #{upload_file_list.size} Files!", reset, "\n"
      end

    else

      # upload file
      if !File.exist?(local_file_path) && !File.file?(local_file_path)
        print_error Morpheus::Terminal.angry_prompt
        puts_error  "#{command_name} bad argument: [local-file]\nFile '#{local_file_path}' was not found.\n#{optparse}"
        return 1
      end

      # local_dirname = File.dirname(local_file_path)
      # local_basename = File.basename(local_file_path)
      
      file = local_file_path
      destination = File.basename(file)
      if remote_file_path[-1].chr == "/"
        # work like `cp`, and place into the directory
        destination = remote_file_path + File.basename(file)
      elsif remote_file_path
        # renaming file
        destination = remote_file_path
      end
      destination = destination.squeeze('/')

      unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to upload #{local_file_path} to #{archive_bucket['name']}:#{destination}?")
        return 9, "aborted command"
      end

      @archive_buckets_interface.setopts(options)
      if options[:dry_run]
        #print cyan,bold, "  - Uploading #{file} to #{bucket_id}:#{destination} DRY RUN", reset, "\n"
        # print_h1 "DRY RUN"
        print_dry_run @archive_buckets_interface.dry.upload_file(bucket_id, file, destination)
        print "\n"
        return 0
      end
    
      print cyan,bold, "  - Uploading #{file} to #{bucket_id}:#{destination}", reset
      upload_response = @archive_buckets_interface.upload_file(bucket_id, file, destination)
      if upload_response['success']
        print bold," #{green}Success#{reset}"
      else
        print bold," #{red}Error#{reset}"
        if upload_response['msg']
          print " #{upload_response['msg']}#{reset}"
        end
      end
      print "\n"

    end
    #print cyan, bold, "Upload Complete!", reset, "\n"

    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
end

Private Instance Methods

find_archive_bucket_by_id(id) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1730
def find_archive_bucket_by_id(id)
  begin
    # this is typically passed as name, the api supports either name or id
    json_response = @archive_buckets_interface.get(id.to_s)
    archive_bucket = json_response['archiveBucket']
    archive_bucket['isOwner'] = !!json_response['isOwner']
    return archive_bucket
  rescue RestClient::Exception => e
    if e.response && e.response.code == 404
      print_red_alert "Archive bucket not found by id #{id}"
      return nil
    else
      raise e
    end
  end
end
find_archive_bucket_by_name(name) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1747
def find_archive_bucket_by_name(name)
  archive_buckets = @archive_buckets_interface.list({name: name.to_s})['archiveBuckets']
  if archive_buckets.empty?
    print_red_alert "Archive bucket not found by name #{name}"
    return nil
  elsif archive_buckets.size > 1
    print_red_alert "#{archive_buckets.size} archive buckets found by name #{name}"
    # print_archive_buckets_table(archive_buckets, {color: red})
    rows = archive_buckets.collect do |it|
      {id: it['id'], name: it['name']}
    end
    puts as_pretty_table(rows, [:id, :name], {color:red})
    return nil
  else
    return archive_buckets[0]
  end
end
find_archive_bucket_by_name_or_id(val) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1721
def find_archive_bucket_by_name_or_id(val)
  return find_archive_bucket_by_id(val)
  # if val.to_s =~ /\A\d{1,}\Z/
  #   return find_archive_bucket_by_id(val)
  # else
  #   return find_archive_bucket_by_name(val)
  # end
end
find_archive_file_by_bucket_and_path(bucket_id, file_path) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1779
def find_archive_file_by_bucket_and_path(bucket_id, file_path)
  if file_path.to_s.empty? || file_path.to_s.strip == "/"
    print_red_alert "Archive file not found for bucket: '#{bucket_id}' file: (blank)"
    return nil
  end
  # chomp leading and trailing slashes, the api isn't doin this right now.
  if file_path.size > 1 && file_path[-1] == "/"
    file_path = file_path[0..-2]
  end
  if file_path[0] && file_path[0].chr == "/"
    file_path = file_path[1..-1]
  end
  # ok, find the file id by searching /archives/buckets/$bucketId/files/$filePath
  json_response = @archive_buckets_interface.list_files(bucket_id, file_path)
  # json_response = @archive_buckets_interface.list_files(bucket_id, "/", {phrase: file_path})
  # json_response = @archive_buckets_interface.list_files(bucket_id, "/", {absoluteFilePath: file_path})
  # puts "find_archive_file() json_response is: ", JSON.pretty_generate(json_response)
  archive_file = nil
  archive_files = json_response['archiveFiles']
  # silly hack, not needed while using ?absoluteFilePath=
  if json_response['parentDirectory'] && json_response['parentDirectory']['filePath'] == file_path
    archive_file = json_response['parentDirectory']
  else
    archive_file = archive_files[0]
  end
  if archive_file.nil?
    print_red_alert "Archive file not found for bucket: '#{bucket_id}' file: '#{file_path}'"
    return nil
  end
  return archive_file
end
find_archive_file_by_id(id) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1765
 def find_archive_file_by_id(id)
  begin
    json_response = @archive_files_interface.get(id.to_s)
    return json_response['archiveFile']
  rescue RestClient::Exception => e
    if e.response && e.response.code == 404
      print_red_alert "Archive file not found by id #{id}"
      return nil
    else
      raise e
    end
  end
end
find_boot_script(val) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1891
def find_boot_script(val)
  if val.nil? || val.to_s.empty?
    return nil
  else
    return get_available_boot_scripts().find { |it| 
      (it['id'].to_s.downcase == val.to_s.downcase) || 
      (it['name'].to_s.downcase == val.to_s.downcase)
    }
  end
end
find_preseed_script(val) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1917
def find_preseed_script(val)
  if val.nil? || val.to_s.empty?
    return nil
  else
    return get_available_preseed_scripts().find { |it| 
      (it['id'].to_s.downcase == val.to_s.downcase) || 
      (it['name'].to_s.downcase == val.to_s.downcase)
    }
  end
end
format_archive_bucket_execution_result(archive_bucket_execution, return_color=cyan) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1850
def format_archive_bucket_execution_result(archive_bucket_execution, return_color=cyan)
  out = ""
  status_string = archive_bucket_execution['status']
  if status_string == 'running' # || status_string == 'pending'
    out << generate_usage_bar(archive_bucket_execution['statusPercent'], 100, {max_bars: 10})
    out << return_color if return_color
    out << " - #{archive_bucket_execution['statusMessage']}"
  elsif archive_bucket_execution['statusMessage']
    out << "#{archive_bucket_execution['statusMessage']}"
  end
  if archive_bucket_execution['errorMessage']
    out << " - #{red}#{archive_bucket_execution['errorMessage']}#{return_color}"
  end
  out
end
format_archive_bucket_execution_status(archive_bucket_execution, return_color=cyan) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1831
def format_archive_bucket_execution_status(archive_bucket_execution, return_color=cyan)
  out = ""
  status_string = archive_bucket_execution['status']
  if status_string == 'running'
    out <<  "#{green}#{status_string.upcase}#{return_color}"
  elsif status_string == 'success'
    out <<  "#{green}#{status_string.upcase}#{return_color}"
  elsif status_string == 'failed'
    out <<  "#{red}#{status_string.upcase}#{return_color}"
  elsif status_string == 'pending'
    out <<  "#{white}#{status_string.upcase}#{return_color}"
  elsif status_string
    out <<  "#{yellow}#{status_string.upcase}#{return_color}"
  else
    out <<  ""
  end
  out
end
format_archive_bucket_full_status(archive_bucket, return_color=cyan) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1811
def format_archive_bucket_full_status(archive_bucket, return_color=cyan)
  out = ""
  if archive_bucket['lastResult']
    out << format_archive_bucket_execution_status(archive_bucket['lastResult'])
  else
    out << ""
  end
  out
end
format_archive_bucket_status(archive_bucket, return_color=cyan) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1821
def format_archive_bucket_status(archive_bucket, return_color=cyan)
  out = ""
  if archive_bucket['lastResult']
    out << format_archive_bucket_execution_status(archive_bucket['lastResult'])
  else
    out << ""
  end
  out
end
get_available_boot_scripts(refresh=false) click to toggle source

def get_available_boot_scripts()

boot_scripts_dropdown = []
scripts = @boot_scripts_interface.list({max:1000})['bootScripts']
scripts.each do |it| 
  boot_scripts_dropdown << {'name'=>it['fileName'],'value'=>it['id']}
end
boot_scripts_dropdown << {'name'=>'Custom','value'=> 'custom'}
return boot_scripts_dropdown

end

# File lib/morpheus/cli/commands/archives_command.rb, line 1876
def get_available_boot_scripts(refresh=false)
  if !@available_boot_scripts || refresh
    # option_results = options_interface.options_for_source('bootScripts',{})['data']
    boot_scripts_dropdown = []
    scripts = @boot_scripts_interface.list({max:1000})['bootScripts']
    scripts.each do |it| 
      boot_scripts_dropdown << {'name'=>it['fileName'],'value'=>it['id'],'id'=>it['id']}
    end
    boot_scripts_dropdown << {'name'=>'Custom','value'=> 'custom','id'=> 'custom'}
    @available_boot_scripts = boot_scripts_dropdown
  end
  #puts "available_boot_scripts() rtn: #{@available_boot_scripts.inspect}"
  return @available_boot_scripts
end
get_available_preseed_scripts(refresh=false) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1902
def get_available_preseed_scripts(refresh=false)
  if !@available_preseed_scripts || refresh
    # option_results = options_interface.options_for_source('preseedScripts',{})['data']
    preseed_scripts_dropdown = []
    scripts = @preseed_scripts_interface.list({max:1000})['preseedScripts']
    scripts.each do |it| 
      preseed_scripts_dropdown << {'name'=>it['fileName'],'value'=>it['id'],'id'=>it['id']}
    end
    # preseed_scripts_dropdown << {'name'=>'Custom','value'=> 'custom','value'=> 'custom'}
    @available_preseed_scripts = preseed_scripts_dropdown
  end
  #puts "available_preseed_scripts() rtn: #{@available_preseed_scripts.inspect}"
  return @available_preseed_scripts
end
parse_bucket_id_and_file_path(*args) click to toggle source

parse_bucket_id_and_file_path() provides flexible argument formats for bucket and path it looks for [bucket:/path] or [bucket] [path] @param delim [String] Default is a comma and any surrounding white space. @return [Array] 2 elements, bucket name (or id) and the file path.

The default file path is "/".

Examples:

parse_bucket_id_and_file_path("test") == ["test", "/"]
parse_bucket_id_and_file_path("test:/global.cfg") == ["test", "/global.cfg"]
parse_bucket_id_and_file_path("test:/node1/node.cfg") == ["test", "/node1/node.cfg"]
parse_bucket_id_and_file_path("test/node1/node.cfg") == ["test", "/node1/node.cfg"]
parse_bucket_id_and_file_path("test", "node1/node.cfg") == ["test", "/node1/node.cfg"]
# File lib/morpheus/cli/commands/archives_command.rb, line 2135
def parse_bucket_id_and_file_path(*args)
  if args.size < 1 || args.size > 2
    return nil, nil
  end
  if !args[0]
    return nil, nil
  end
  full_path = args[0].to_s
  if args[1]
    if full_path.include?(":")
      full_path = "#{full_path}/#{args[1]}"
    else
      full_path = "#{full_path}:#{args[1]}"
    end
  end
  # ok fine, allow just bucketId/filePath, without a colon.
  if !full_path.include?(":") && full_path.include?("/")
    path_elements = full_path.split("/")
    full_path = path_elements[0] + ":" + path_elements[1..-1].join("/")
  end
  uri_elements = full_path.split(":")
  bucket_id = uri_elements[0]
  file_path = uri_elements[1..-1].join("/") # [1]
  file_path = "/#{file_path}".squeeze("/")
  return bucket_id, file_path
end
print_archive_files_table(archive_files, options={}) click to toggle source
print_archive_logs_table(archive_logs, opts={}) click to toggle source
prompt_edit_archive_bucket(archive_bucket, options={}) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1928
def prompt_edit_archive_bucket(archive_bucket, options={})
  # populate default prompt values with the existing archive bucket
  default_values = archive_bucket.dup # lazy, but works as long as GET matches POST api structure
  # storage provider (cannot be edited anyhow..)
  if archive_bucket['storageProvider'].kind_of?(Hash)
    default_values['storageProvider'] = archive_bucket['storageProvider']['id']
  end
  # tenants
  if archive_bucket['accounts'].kind_of?(Array) && archive_bucket['accounts'].size > 0
    default_values['accounts'] = archive_bucket['accounts'].collect {|it| it['name'] }.join(", ")
  end
  # any other mismatches? preseedScript, bootScript?
  options[:is_edit] = true
  return prompt_new_archive_bucket(options, default_values)
end
prompt_new_archive_bucket(options={}, default_values={}) click to toggle source
# File lib/morpheus/cli/commands/archives_command.rb, line 1944
def prompt_new_archive_bucket(options={}, default_values={})
  payload = {}

  # Name
  if options['name']
    payload['name'] = options['name']
  else
    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)
    payload['name'] = v_prompt['name']
  end

  # Description
  if options['description']
    payload['description'] = options['description']
  else
    v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'description', 'fieldLabel' => 'Description', 'type' => 'text', 'defaultValue' => default_values['description']}], options, @api_client)
    payload['description'] = v_prompt['description']
  end

  # Storage Provider
  unless options[:is_edit]
    if options['storageProvider']
      # payload['storageProvider'] = options['storageProvider']
      # prompt is skipped when options['fieldName'] is passed in, this will return an id from a name
      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, {})
      payload['storageProvider'] = {id: v_prompt['storageProvider']}
    else
      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, {})
      payload['storageProvider'] = {id: v_prompt['storageProvider']}
    end
  end

  # Tenants
  # TODO: a nice select component for adding/removing from this array
  if options['accounts']
    payload['accounts'] = options['accounts'] #.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
  else
    tenants_default_value = default_values['accounts']
    if tenants_default_value.kind_of?(Array)
      tenants_default_value = tenants_default_value.collect {|it| it["id"] }.join(", ")
    end
    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)
    payload['accounts'] = v_prompt['accounts'].to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
  end

  # Visibility
  if options['visibility']
    payload['visibility'] = options['visibility']
  else
    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, {})
    payload['visibility'] = v_prompt['visibility']
  end

  # Public URL
  if options['isPublic']
    payload['isPublic'] = options['isPublic']
  else
    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, {})
    payload['isPublic'] = v_prompt['isPublic']
  end

  return payload
end