class Object

Constants

ALTERNATE_TIME_FORMAT
DEFAULT_DATE_FORMAT
DEFAULT_TIME_FORMAT

Public Instance Methods

_restart(app_id, options) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1775
def _restart(app_id, options)
  app = find_app_by_name_or_id(app_id)
  return 1 if app.nil?
  tier_records = extract_app_tiers(app)
  if options[:dry_run]
    print_h1 "Dry Run", [], options
  end
  tier_records.each do |tier_record|
    tier_record[:instances].each do |instance|
      restart_cmd = "instances restart #{instance['id']} -y"
      if options[:dry_run]
        puts restart_cmd
      else
        my_terminal.execute(restart_cmd)
      end
    end
  end
  return 0
end
_start(app_id, options) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1731
def _start(app_id, options)
  app = find_app_by_name_or_id(app_id)
  return 1 if app.nil?
  tier_records = extract_app_tiers(app)
  if options[:dry_run]
    print_h1 "Dry Run", [], options
  end
  tier_records.each do |tier_record|
    tier_record[:instances].each do |instance|
      start_cmd = "instances start #{instance['id']} -y"
      if options[:dry_run]
        puts start_cmd
      else
        my_terminal.execute(start_cmd)
      end
    end
  end
  return 0
end
_stop(app_id, options) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1687
def _stop(app_id, options)
  app = find_app_by_name_or_id(app_id)
  return 1 if app.nil?
  tier_records = extract_app_tiers(app)
  if options[:dry_run]
    print_h1 "Dry Run", [], options
  end
  tier_records.each do |tier_record|
    tier_record[:instances].each do |instance|
      stop_cmd = "instances stop #{instance['id']} -y"
      if options[:dry_run]
        puts stop_cmd
      else
        my_terminal.execute(stop_cmd)
      end
    end
  end
  return 0
end
_view(arg, options={}) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2131
def _view(arg, options={})
  begin
    app = find_app_by_name_or_id(arg)
    return 1 if app.nil?

    link = "#{@appliance_url}/login/oauth-redirect?access_token=#{@access_token}\\&redirectUri=/provisioning/apps/#{app['id']}"
    if options[:link_tab]
      link << "#!#{options[:link_tab]}"
    end

    if options[:dry_run]
      puts Morpheus::Util.open_url_command(link)
      return 0
    end
    return Morpheus::Util.open_url(link)
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
a_or_an(v) click to toggle source
# File lib/morpheus/formatters.rb, line 493
def a_or_an(v)
  v.to_s =~ /^[aeiou]/i ? "an" : "a"
end
add(args) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 258
def add(args)
  params = {}
  file_params = {}
  options = {}
  task_name = nil
  task_code = nil
  task_type_name = nil
  task_visibility = nil
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[name] -t TASK_TYPE")
    opts.on( '-t', '--type TASK_TYPE', "Task Type" ) do |val|
      task_type_name = val
    end
    opts.on('--name NAME', String, "Task Name" ) do |val|
      task_name = val
    end
     opts.on('--visibility VISIBILITY', String, "Task Visibility" ) do |val|
      task_visibility = val
    end
    opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
      options[:options]['labels'] = parse_labels(val)
    end
    opts.on('--code CODE', String, "Task Code" ) do |val|
      task_code = val
    end
    opts.on('--source VALUE', String, "Source Type. local, repository, url. Only applies to script task types.") do |val|
      file_params['sourceType'] = val
    end
    opts.on('--content TEXT', String, "Contents of the task script. This implies source is local.") do |val|
      file_params['sourceType'] = 'local' if file_params['sourceType'].nil?
      file_params['content'] = val
    end
    opts.on('--file FILE', "File containing the task script. This can be used instead of --content" ) do |filename|
      file_params['sourceType'] = 'local' if file_params['sourceType'].nil?
      full_filename = File.expand_path(filename)
      if File.exist?(full_filename)
        file_params['content'] = File.read(full_filename)
      else
        print_red_alert "File not found: #{full_filename}"
        exit 1
      end
    end
    opts.on('--url VALUE', String, "URL, for use when source is url") do |val|
      file_params['contentPath'] = val
    end
    opts.on('--content-path VALUE', String, "Content Path, for use when source is repository or url") do |val|
      file_params['contentPath'] = val
    end
    opts.on('--content-ref VALUE', String, "Content Ref (Version Ref), for use when source is repository") do |val|
      file_params['contentRef'] = val
    end
    opts.on('--result-type VALUE', String, "Result Type" ) do |val|
      options[:options]['resultType'] = val
    end
    opts.on('--result-type VALUE', String, "Result Type" ) do |val|
      options[:options]['executeTarget'] = val
    end
    opts.on('--execute-target VALUE', String, "Execute Target" ) do |val|
      options[:options]['executeTarget'] = val
    end
    opts.on('--credential VALUE', String, "Credential ID or \"local\"" ) do |val|
      options[:options]['credential'] = val
    end
    opts.on('--target-host VALUE', String, "Target Host" ) do |val|
      options[:options]['taskOptions'] ||= {}
      options[:options]['taskOptions']['host'] = val
    end
    opts.on('--target-port VALUE', String, "Target Port" ) do |val|
      options[:options]['taskOptions'] ||= {}
      options[:options]['taskOptions']['port'] = val
    end
    opts.on('--target-username VALUE', String, "Target Username" ) do |val|
      options[:options]['taskOptions'] ||= {}
      options[:options]['taskOptions']['username'] = val
    end
    opts.on('--target-password VALUE', String, "Target Password" ) do |val|
      options[:options]['taskOptions'] ||= {}
      options[:options]['taskOptions']['password'] = val
    end
    opts.on('--target-ssh-key VALUE', String, "Target SSH Key" ) do |val|
      options[:options]['taskOptions'] ||= {}
      options[:options]['taskOptions']['sshKey'] = val
    end
    opts.on('--git-repo VALUE', String, "Git Repo ID" ) do |val|
      options[:options]['taskOptions'] ||= {}
      options[:options]['taskOptions']['localScriptGitId'] = val
    end
    opts.on('--git-ref VALUE', String, "Git Ref" ) do |val|
      options[:options]['taskOptions'] ||= {}
      options[:options]['taskOptions']['localScriptGitRef'] = val
    end
    opts.on('--retryable [on|off]', String, "Retryable" ) do |val|
      options[:options]['retryable'] = val.to_s == 'on' || val.to_s == 'true' || val == '' || val.nil?
    end
    opts.on('--retry-count COUNT', String, "Retry Count" ) do |val|
      options[:options]['retryCount'] = val.to_i
    end
    opts.on('--retry-delay SECONDS', String, "Retry Delay Seconds" ) do |val|
      options[:options]['retryDelaySeconds'] = val.to_i
    end
    opts.on('--allow-custom-config [on|off]', String, "Allow Custom Config") do |val|
      options[:options]['allowCustomConfig'] = val.to_s == 'on' || val.to_s == 'true' || val.to_s == ''
    end
    build_common_options(opts, options, [:options, :payload, :json, :dry_run, :quiet, :remote])
  end
  optparse.parse!(args)
  #verify_args!(args:args, count:1, optparse:optparse)
  if args[0]
    # task_name = args[0]
    task_name = args[0]
  end
  # if task_name.nil? || task_type_name.nil?
  #   puts optparse
  #   exit 1
  # end
  connect(options)
  begin
    passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
    if passed_options['type']
      task_type_name = passed_options.delete('type')
    end
    payload = nil
    
    if options[:payload]
      payload = options[:payload]
      payload.deep_merge!({'task' => passed_options})  unless passed_options.empty?
      payload.deep_merge!({'task' => {'file' => file_params}}) unless file_params.empty?
    else
      # construct payload
      payload = {
        "task" => {
          #"name" => task_name,
          #"code" => task_code,
          #"taskType" {"id" => task_type['id'], "code" => task_type['code']},
          #"taskOptions" => {}
        }
      }
      payload.deep_merge!({'task' => passed_options})  unless passed_options.empty?

      
      
      # Name
      if task_name
        payload['task']['name'] = task_name
      else
        v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'description' => 'Name'}], options[:options], @api_client)
        payload['task']['name'] = v_prompt['name'] unless v_prompt['name'].to_s.empty?
      end

      # Code
      if task_code
        payload['task']['code'] = task_code
      else
        v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'code', 'fieldLabel' => 'Code', 'type' => 'text', 'description' => 'Code'}], options[:options], @api_client)
        payload['task']['code'] = v_prompt['code'] unless v_prompt['code'].to_s.empty?
      end

      # Task Type
      @all_task_types ||= @tasks_interface.list_types({max:1000})['taskTypes']
      task_types_dropdown = @all_task_types.collect {|it| {"name" => it["name"], "value" => it["code"]}}
      
      if task_type_name
        #payload['task']['taskType'] = task_type_name
      else
        v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'type', 'fieldLabel' => 'Type', 'type' => 'select', 'selectOptions' => task_types_dropdown, 'required' => true}], options[:options], @api_client)
        task_type_name = v_prompt['type']
      end

      task_type = find_task_type_by_name(task_type_name)
      if task_type.nil?
        print_red_alert "Task Type not found by code '#{task_type_name}'"
        return 1
      end

      # Visibility
      if task_visibility != nil
        payload['task']['visibility'] = task_visibility
      else
        payload['task']['visibility'] = 'private'
      end

      payload['task']['taskType'] = {"id" => task_type['id'], "code" => task_type['code']}


      # Result Type
      if options[:options]['resultType']
        payload['task']['resultType'] = options[:options]['resultType']
      elsif task_type['hasResults']
        result_types_dropdown = [{"name" => "Value", "value" => "value"}, {"name" => "Exit Code", "value" => "exitCode"}, {"name" => "Key Value", "value" => "keyValue"}, {"name" => "JSON", "value" => "json"}]
        v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'resultType', 'fieldLabel' => 'Result Type', 'type' => 'select', 'selectOptions' => result_types_dropdown}], options[:options], @api_client)
        payload['task']['resultType'] = v_prompt['resultType'] unless v_prompt['resultType'].to_s.empty?
      end

      # Task Type Option Types

      # correct fieldContext
      has_file_content = false
      task_option_types = task_type['optionTypes'] || []
      task_option_types.each do |it|
        if it['type'] == 'file-content'
          has_file_content = true
          it['fieldContext'] = nil
          it['fieldName'] = 'file'
          # this should be required right!? fix api optionType data plz
          it['required'] = true
          it['defaultValue'] = 'local'
        else
          if it['fieldContext'].nil? || it['fieldContext'] == ''
            it['fieldContext'] = 'taskOptions'
          end
          # taskOptions should prompt for code instead of fieldName, oy vey
          if it['fieldContext'] == 'taskOptions'
            it['fieldName'] = it['code']
          end
        end
      end
      process_special_task_option_typeaheads(task_option_types)
      # inject file_params into options for file-content prompt
      # or into taskOptions.script for types not yet using file-content
      unless file_params.empty?
        if has_file_content
          options[:options]['file'] ||= {}
          options[:options]['file'].merge!(file_params)
        else
          options[:options]['taskOptions'] ||= {}
          options[:options]['taskOptions']['script'] = file_params['content'] if file_params['content']
        end
      end
      # prompt

      # tasks are different in that they use the optionType code instead of fieldName for the key values
      input_options = Morpheus::Cli::OptionTypes.prompt(task_option_types, options[:options],@api_client, options[:params])
      # flatten taskOptions as serverside expects
      if input_options['taskOptions']
        input_options['taskOptions'] = Morpheus::RestClient.grails_params(input_options['taskOptions'])
        # remove "off" checkbox values, like the UI does
        input_options['taskOptions'].keys.each do |k|
          if input_options['taskOptions'][k] == "off"
            input_options['taskOptions'].delete(k)
          end
        end
      end
      payload.deep_merge!({'task' => input_options}) unless input_options.empty?
      

      # Target Options

      if options[:options]['executeTarget'] != nil
        payload['task']['executeTarget'] = options[:options]['executeTarget']
      else
        default_target = nil
        execute_targets_dropdown = []
        if task_type['allowExecuteLocal']
          default_target = 'local'
          execute_targets_dropdown << {"name" => "Local", "value" => "local"}
        end
        if task_type['allowExecuteRemote']
          default_target = 'remote'
          execute_targets_dropdown << {"name" => "Remote", "value" => "remote"}
        end
        if task_type['allowExecuteResource']
          default_target = 'resource'
          execute_targets_dropdown << {"name" => "Resource", "value" => "resource"}
        end
        if !execute_targets_dropdown.empty?
          v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'executeTarget', 'fieldLabel' => 'Execute Target', 'type' => 'select', 'selectOptions' => execute_targets_dropdown, 'defaultValue' => default_target, 'required' => true}], options[:options], @api_client)
          payload['task']['executeTarget'] = v_prompt['executeTarget'].to_s unless v_prompt['executeTarget'].to_s.empty?
        end
      end

      if payload['task']['executeTarget'] == 'local'
        if task_type['allowLocalRepo']
          # Git Repo
          v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'localScriptGitId', 'fieldLabel' => 'Git Repo', 'type' => 'text', 'description' => 'Git Repo ID'}], options[:options], @api_client)
          if v_prompt['taskOptions'] && !v_prompt['taskOptions']['localScriptGitId'].to_s.empty?
            payload['task']['taskOptions'] ||= {}
            payload['task']['taskOptions']['localScriptGitId'] = v_prompt['taskOptions']['localScriptGitId']
          end
          # Git Ref
          v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'localScriptGitRef', 'fieldLabel' => 'Git Ref', 'type' => 'text', 'description' => 'Git Ref eg. master'}], options[:options], @api_client)
          if v_prompt['taskOptions'] && !v_prompt['taskOptions']['localScriptGitRef'].to_s.empty?
            payload['task']['taskOptions'] ||= {}
            payload['task']['taskOptions']['localScriptGitRef'] = v_prompt['taskOptions']['localScriptGitRef']
          end
        end

      elsif payload['task']['executeTarget'] == 'remote'
        # Host
        v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'host', 'fieldLabel' => 'IP Address', 'type' => 'text', 'description' => 'IP Address / Host for remote execution'}], options[:options], @api_client)
        if v_prompt['taskOptions'] && !v_prompt['taskOptions']['host'].to_s.empty?
          payload['task']['taskOptions'] ||= {}
          payload['task']['taskOptions']['host'] = v_prompt['taskOptions']['host']
        end
        # Port
        v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'port', 'fieldLabel' => 'Port', 'type' => 'text', 'description' => 'Port for remote execution', 'defaultValue' => '22'}], options[:options], @api_client)
        if v_prompt['taskOptions'] && !v_prompt['taskOptions']['port'].to_s.empty?
          payload['task']['taskOptions'] ||= {}
          payload['task']['taskOptions']['port'] = v_prompt['taskOptions']['port']
        end
        # Credentials
        credential_code = "credential"
        credential_option_type = {'code' => credential_code, 'fieldName' => credential_code, 'fieldLabel' => 'Credentials', 'type' => 'select', 'optionSource' => 'credentials', 'description' => 'Enter an existing credential ID or choose "local"', 'defaultValue' => "local", 'required' => true}
        supported_credential_types = ['username-keypair', 'username-password', 'username-password-keypair'].compact.flatten.join(",").split(",").collect {|it| it.strip }
        credential_params = {"new" => false, "credentialTypes" => supported_credential_types}
        credential_value = Morpheus::Cli::OptionTypes.select_prompt(credential_option_type, @api_client, credential_params, options[:no_prompt], options[:options][credential_code])
        if !credential_value.to_s.empty?
          if credential_value == "local"
            payload['task'][credential_code] = {"type" => credential_value}
          elsif credential_value.to_s =~ /\A\d{1,}\Z/
            payload['task'][credential_code] = {"id" => credential_value.to_i}
          end
        end
        if credential_value == "local"
          # Username
          v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'username', 'fieldLabel' => 'Username', 'type' => 'text', 'description' => 'Username for remote execution'}], options[:options], @api_client)
          if v_prompt['taskOptions'] && !v_prompt['taskOptions']['username'].to_s.empty?
            payload['task']['taskOptions'] ||= {}
            payload['task']['taskOptions']['username'] = v_prompt['taskOptions']['username']
          end
          # Password
          v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'password', 'fieldLabel' => 'Password', 'type' => 'password', 'description' => 'Password for remote execution'}], options[:options], @api_client)
          if v_prompt['taskOptions'] && !v_prompt['taskOptions']['password'].to_s.empty?
            payload['task']['taskOptions'] ||= {}
            payload['task']['taskOptions']['password'] = v_prompt['taskOptions']['password']
          end
          # SSH Key
          v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldContext' => 'taskOptions', 'fieldName' => 'sshKey', 'fieldLabel' => 'Key', 'type' => 'select', 'optionSource' => 'keyPairs', 'description' => 'SSH Key for remote execution'}], options[:options], @api_client)
          if v_prompt['taskOptions'] && !v_prompt['taskOptions']['sshKey'].to_s.empty?
            payload['task']['taskOptions'] ||= {}
            payload['task']['taskOptions']['sshKey'] = v_prompt['taskOptions']['sshKey']
          end
        end
      end


      # Retryable
      if options[:options]['retryable'] != nil
        payload['task']['retryable'] = options[:options]['retryable']
      else
        v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'retryable', 'fieldLabel' => 'Retryable', 'type' => 'checkbox', 'defaultValue' => false}], options[:options], @api_client)
        payload['task']['retryable'] = ['true','on'].include?(v_prompt['retryable'].to_s) unless v_prompt['retryable'].nil?
      end

      if payload['task']['retryable']
        # Retry Count
        if options[:options]['retryCount']
          payload['task']['retryCount'] = options[:options]['retryCount'].to_i
        else
          v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'retryCount', 'fieldLabel' => 'Retry Count', 'type' => 'number', 'defaultValue' => 5}], options[:options], @api_client)
          payload['task']['retryCount'] = v_prompt['retryCount'].to_i unless v_prompt['retryCount'].nil?
        end
        # Retry Delay
        if options[:options]['retryDelaySeconds']
          payload['task']['retryDelaySeconds'] = options[:options]['retryDelaySeconds'].to_i
        else
          v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'retryDelaySeconds', 'fieldLabel' => 'Retry Delay', 'type' => 'number', 'defaultValue' => 10}], options[:options], @api_client)
          payload['task']['retryDelaySeconds'] = v_prompt['retryDelaySeconds'].to_i unless v_prompt['retryDelaySeconds'].nil?
        end
      end


      # Allow Custom Config
      if options[:options]['allowCustomConfig'] != nil
        payload['task']['allowCustomConfig'] = options[:options]['allowCustomConfig']
      else
        v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'allowCustomConfig', 'fieldLabel' => 'Allow Custom Config', 'type' => 'checkbox', 'defaultValue' => false}], options[:options], @api_client)
        payload['task']['allowCustomConfig'] = ['true','on'].include?(v_prompt['allowCustomConfig'].to_s) unless v_prompt['allowCustomConfig'].nil?
      end
     

    end
    @tasks_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @tasks_interface.dry.create(payload)
      return
    end
    json_response = @tasks_interface.create(payload)
    task = json_response['task']
    if options[:json]
      print JSON.pretty_generate(json_response),"\n"
    elsif !options[:quiet]
      task = json_response['task']
      print_green_success "Task #{task['name']} created"
      get([task['id']])
    end
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
add_instance(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1304
def add_instance(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app] [instance] [tier]")
    build_common_options(opts, options, [:options, :json, :dry_run])
    opts.footer = "Add an existing instance to an app.\n" +
                  "[app] is required. This is the name or id of an app." + "\n" +
                  "[instance] is required. This is the name or id of an instance." + "\n" +
                  "[tier] is required. This is the name of the tier."
  end
  optparse.parse!(args)
  if args.count < 1 || args.count > 3
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} add-instance expects 1-3 arguments and received #{args.count}: #{args}\n#{optparse}"
    return 1
  end
  # optional [tier] and [instance] arguments
  if args[1] && args[1] !~ /\A\-/
    options[:instance_name] = args[1]
    if args[2] && args[2] !~ /\A\-/
      options[:tier_name] = args[2]
    end
  end
  connect(options)
  begin
    app = find_app_by_name_or_id(args[0])

    # Only supports adding an existing instance right now..

    payload = {}

    if options[:instance_name]
      instance = find_instance_by_name_or_id(options[:instance_name])
    else
      v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'instance', 'fieldLabel' => 'Instance', 'type' => 'text', 'required' => true, 'description' => 'Enter the instance name or id'}], options[:options])
      instance = find_instance_by_name_or_id(v_prompt['instance'])
    end
    payload[:instanceId] = instance['id']

    if options[:tier_name]
      payload[:tierName] = options[:tier_name]
    else
      v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'tier', 'fieldLabel' => 'Tier', 'type' => 'text', 'required' => true, 'description' => 'Enter the name of the tier'}], options[:options])
      payload[:tierName] = v_prompt['tier']
    end
    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.add_instance(app['id'], payload)
      return
    end
    json_response = @apps_interface.add_instance(app['id'], payload)
    if options[:json]
      print JSON.pretty_generate(json_response)
      print "\n"
    else
      print_green_success "Added instance #{instance['name']} to app #{app['name']}"
      #get(app['id'])
    end
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
anded_list(items, limit=nil) click to toggle source
# File lib/morpheus/formatters.rb, line 465
def anded_list(items, limit=nil)
  format_list(items, "and", limit)
end
apply(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1022
  def apply(args)
    default_refresh_interval = 5
    params, payload, options = {}, {}, {}
    optparse = Morpheus::Cli::OptionParser.new do |opts|
      opts.banner = subcommand_usage("[app] [options]")
      opts.on( '-p', '--parameter NAME=VALUE', "Template parameter name and value" ) do |val|
        k, v = val.split("=")
        options[:options]['templateParameter'] ||= {}
        options[:options]['templateParameter'][k] = v
      end
      opts.on('--refresh [SECONDS]', String, "Refresh until execution is complete. Default interval is #{default_refresh_interval} seconds.") do |val|
        options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
      end
      opts.on(nil, '--no-refresh', "Do not refresh" ) do
        options[:no_refresh] = true
      end
      opts.on(nil, '--no-validate', "Do not validate planned changes before apply" ) do
        options[:no_validate] = true
      end
      opts.on(nil, '--validate-only', "Only validate planned changes, do not execute the apply command." ) do
        options[:validate_only] = true
      end
      build_standard_update_options(opts, options, [:auto_confirm])
      opts.footer = <<-EOT
Apply an app.
[app] is required. This is the name or id of an app.
This is only supported by certain types of apps such as terraform.
By default this executes two requests to validate and then apply the changes.
The first request corresponds to the terraform plan command only.
Use --no-validate to skip this step apply changes in one step.
EOT
    end
    optparse.parse!(args)
    verify_args!(args:args, optparse:optparse, count:1)
    connect(options)

    app = find_app_by_name_or_id(args[0])
    return 1 if app.nil?
    # construct request
    params.merge!(parse_query_options(options))
    payload = {}
    if options[:payload]
      payload = options[:payload]
      payload.deep_merge!(parse_passed_options(options))
    else
      payload.deep_merge!(parse_passed_options(options))
      # attempt to load prepare-apply to get templateParameter values and prompt for them
      # ok, actually use options/layoutParameters to get the list of parameters
      begin
        prepare_apply_json_response = @apps_interface.prepare_apply(app["id"])
        config = prepare_apply_json_response['data']
        variable_map = config['templateParameter']
        # need to load the instance details to get the app cloud...ugh
        first_instance = app['instances'][0]
        instance = first_instance ? find_instance_by_name_or_id(first_instance['id']) : nil
        zone_id = instance ? instance['cloud']['id'] : nil
        api_params = {templateId: app['blueprint']['id'], appId: app['id'], zoneId: zone_id, siteId: app['group']['id']}
        layout_parameters = @options_interface.options_for_source('templateParameters',api_params)['data']

        if layout_parameters && !layout_parameters.empty?
          variable_option_types = []
          i = 0
          layout_parameters.each do |layout_parameter|
            var_label = layout_parameter['displayName'] || layout_parameter['name']
            var_name = layout_parameter['name']
            var_value = variable_map ? variable_map[var_name] : layout_parameter['defaultValue']
            if var_value.nil? && layout_parameter['defaultValue']
              var_value = layout_parameter['defaultValue']
            end
            var_type = (layout_parameter['passwordType'] || layout_parameter['sensitive']) ? 'password' : 'text'
            option_type = {'fieldContext' => 'templateParameter', 'fieldName' => var_name, 'fieldLabel' => var_label, 'type' => var_type, 'required' => true, 'defaultValue' => (var_value.to_s.empty? ? nil : var_value.to_s), 'displayOrder' => (i+1) }
            variable_option_types << option_type
            i+=1
          end
          blueprint_type_display = format_blueprint_type(config['type'])
          if blueprint_type_display == "terraform"
            blueprint_type_display = "Terraform"
          end
          print_h2 "#{blueprint_type_display} Variables"
          v_prompt = Morpheus::Cli::OptionTypes.prompt(variable_option_types, options[:options], @api_client)
          v_prompt.deep_compact!
          payload.deep_merge!(v_prompt)
        end
      rescue RestClient::Exception => ex
        # if e.response && e.response.code == 404
        Morpheus::Logging::DarkPrinter.puts "Unable to load config for app apply, skipping parameter prompting" if Morpheus::Logging.debug?
        # print_rest_exception(ex, options)
        # end
      end
    end

    @apps_interface.setopts(options)
    if options[:validate_only]
      # validate only
      if options[:dry_run]
        print_dry_run @apps_interface.dry.validate_apply(app["id"], params, payload)
        return
      end
      json_response = @apps_interface.validate_apply(app["id"], params, payload)
      print_green_success "Validating app #{app['name']}"
      execution_id = json_response['executionId']
      if !options[:no_refresh]
        #Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
        validate_execution_request = wait_for_execution_request(execution_id, options)
      end
    elsif options[:no_validate]
      # skip validate, apply only
      if options[:dry_run]
        print_dry_run @apps_interface.dry.apply(app["id"], params, payload)
        return
      end
      json_response = @apps_interface.apply(app["id"], params, payload)
      render_response(json_response, options) do
        print_green_success "Applying app #{app['name']}"
        execution_id = json_response['executionId']        
        if !options[:no_refresh]
          #Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
          apply_execution_request = wait_for_execution_request(execution_id, options)
        end
      end
    else
      # validate and then apply
      if options[:dry_run]
        print_dry_run @apps_interface.dry.validate_apply(app["id"], params, payload)
        print_dry_run @apps_interface.dry.apply(app["id"], params, payload)
        return
      end
      json_response = @apps_interface.validate_apply(app["id"], params, payload)
      print_green_success "Validating app #{app['name']}"
      execution_id = json_response['executionId']
      validate_execution_request = wait_for_execution_request(execution_id, options)
      if validate_execution_request['status'] != 'complete'
        print_red_alert "Validation failed. Changes will not be applied."
        return 1, "Validation failed. Changes will not be applied."
      else
        unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to apply these changes?")
          return 9, "aborted command"
        end
        json_response = @apps_interface.apply(app["id"], params, payload)
        render_response(json_response, options) do
          print_green_success "Applying app #{app['name']}"
          execution_id = json_response['executionId']        
          if !options[:no_refresh]
            #Morpheus::Cli::ExecutionRequestCommand.new.handle(["get", execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
            apply_execution_request = wait_for_execution_request(execution_id, options)
          end
        end
      end
    end
    return 0, nil
  end
apply_security_groups(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1895
def apply_security_groups(args)
  options = {}
  params = {}
  clear_or_secgroups_specified = false
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app] [--clear] [-s]")
    opts.on( '-c', '--clear', "Clear all security groups" ) do
      params[:securityGroupIds] = []
      clear_or_secgroups_specified = true
    end
    opts.on( '-s', '--secgroups SECGROUPS', "Apply the specified comma separated security group ids" ) do |secgroups|
      params[:securityGroupIds] = secgroups.split(",")
      clear_or_secgroups_specified = true
    end
    opts.on( '-h', '--help', "Print this help" ) do
      puts opts
      exit
    end
    build_common_options(opts, options, [:json, :dry_run])
  end
  optparse.parse!(args)
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} apply-security-groups expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
    return 1
  end
  if !clear_or_secgroups_specified
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} apply-security-groups requires either --clear or --secgroups\n#{optparse}"
    return 1
  end

  connect(options)

  begin
    app = find_app_by_name_or_id(args[0])
    payload = params
    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.apply_security_groups(app['id'], payload)
      return
    end
    @apps_interface.apply_security_groups(app['id'], payload)
    security_groups([args[0]])
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
cancel_removal(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1436
def cancel_removal(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    build_common_options(opts, options, [:json, :dry_run, :quiet, :remote])
  end
  optparse.parse!(args)
  if args.count < 1
    puts optparse
    exit 1
  end
  connect(options)
  begin
    app = find_app_by_name_or_id(args[0])
    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.cancel_removal(app['id'])
      return
    end
    json_response = @apps_interface.cancel_removal(app['id'])
    if options[:json]
      print as_json(json_response, options), "\n"
      return
    elsif !options[:quiet]
      get([app['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
    end
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
currency_sym(currency) click to toggle source
# File lib/morpheus/formatters.rb, line 406
def currency_sym(currency)
  Money::Currency.new((currency || 'USD').to_sym).symbol
end
display_appliance(name, url) click to toggle source
# File lib/morpheus/formatters.rb, line 196
def display_appliance(name, url)
  if name.to_s == "" || name == 'remote-url'
    # "#{url}"
    "#{url}"
  else
    # "#{name} #{url}"
    "[#{name}] #{url}"
  end
end
escape_filepath(filepath) click to toggle source
# File lib/morpheus/formatters.rb, line 509
def escape_filepath(filepath)
  filepath.to_s.split("/").select {|it| !it.to_s.empty? }.collect {|it| CGI::escape(it) }.join("/")
end
execute(args) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 857
def execute(args)
  params = {}
  options = {}
  target_type = nil
  instance_ids = []
  instances = []
  instance_label = nil
  server_ids = []
  servers = []
  server_label = nil
  default_refresh_interval = 5
  all_target_types = ['appliance', 'instance', 'instance-label', 'server', 'server-label']
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[task] [options]")
    opts.on('--context-type VALUE', String, "Context Type, #{ored_list(all_target_types)}") do |val|
      val = val.downcase
      val = 'appliance' if val == 'none'
      if target_type && target_type != val
        raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
      end
      if !all_target_types.include?(val)
        raise ::OptionParser::InvalidOption.new("'#{val}' is invalid. It must be one of the following: instance, instance-label, server, server-label or appliance")
      end
      target_type = val
    end
    opts.on('--target-type VALUE', String, "alias for context-type") do |val|
      val = val.downcase
      val = 'appliance' if val == 'none'
      if target_type && target_type != val
        raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
      end
      if !all_target_types.include?(val)
        raise ::OptionParser::InvalidOption.new("'#{val}' is invalid. It must be one of the following: instance, instance-label, server, server-label or appliance")
      end
      target_type = val
    end
    opts.add_hidden_option('--target-type')
    opts.on('--instance INSTANCE', String, "Instance name or id to target for execution. This option can be passed more than once.") do |val|
      if target_type && target_type != 'instance'
        raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
      end
      target_type = 'instance'
      instance_ids << val
    end
    opts.on('--instances LIST', Array, "Instances, comma separated list of instance names or IDs.") do |list|
      if target_type && target_type != 'instance'
        raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
      end
      target_type = 'instance'
      instance_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
    end
    opts.on('--instance-label LABEL', String, "Instance Label") do |val|
      if target_type && target_type != 'instance-label'
        raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
      end
      target_type = 'instance-label'
      instance_label = val
    end
    opts.on('--server SERVER', String, "Server name or id to target for execution. This option can be passed more than once.") do |val|
      if target_type && target_type != 'server'
        raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
      end
      target_type = 'server'
      server_ids << val
    end
    opts.on('--servers LIST', Array, "Servers, comma separated list of host names or IDs.") do |list|
      if target_type && target_type != 'server'
        raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
      end
      target_type = 'server'
      server_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
    end
    opts.on('--server-label LABEL', String, "Server Label") do |val|
      if target_type && target_type != 'server-label'
        raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
      end
      target_type = 'server-label'
      server_label = val
    end
    opts.on('--host HOST', String, "alias for --server") do |val|
      if target_type && target_type != 'server'
        raise ::OptionParser::InvalidOption.new("cannot be combined with another context (#{target_type})")
      end
      target_type = 'server'
      server_ids << val
    end
    opts.add_hidden_option('--host')
    opts.on('--hosts HOSTS', Array, "alias for --servers") do |list|
      if target_type && target_type != 'server'
        raise ::OptionParser::InvalidOption.new("The --hosts option cannot be combined with another context (#{target_type})")
      end
      target_type = 'server'
      server_ids = list.collect {|it| it.to_s.strip.empty? ? nil : it.to_s.strip }.compact.uniq
    end
    opts.add_hidden_option('--hosts')
    opts.on('-a', '--appliance', "Execute on the appliance, the target is the appliance itself.") do
      if target_type && target_type != 'appliance'
        raise ::OptionParser::InvalidOption.new("The --appliance option cannot be combined with another context (#{target_type})")
      end
      target_type = 'appliance'
    end
    opts.on('--config [TEXT]', String, "Custom config") do |val|
      params['customConfig'] = val.to_s
    end
    opts.on('--refresh [SECONDS]', String, "Refresh until execution is complete. Default interval is #{default_refresh_interval} seconds.") do |val|
      options[:refresh_interval] = val.to_s.empty? ? default_refresh_interval : val.to_f
    end
    opts.on(nil, '--no-refresh', "Do not refresh" ) do
      options[:no_refresh] = true
    end
    build_standard_post_options(opts, options)
  end
  optparse.parse!(args)
  if args.count != 1
    raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
  end
  verify_args!(args:args, count:1, optparse:optparse)
  task_name = args[0]
  connect(options)
  begin
    task = find_task_by_name_or_id(task_name)
    return 1 if task.nil?

    passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
    payload = nil
    if options[:payload]
      payload = options[:payload]
      payload.deep_merge!({'job' => passed_options})  unless passed_options.empty?
    else
      # prompt for target type and target
      if target_type.nil?
        # todo: Need api to fetch available Context Types for taskId/workflowId
        available_target_types = get_available_contexts_for_task(task)
        default_target_type = available_target_types.first ? available_target_types.first['name'] : nil
        if !available_target_types.empty?
          default_target_type = available_target_types.first ? available_target_types.first['name'] : nil
          target_type = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'context-type', 'fieldName' => 'targetType', 'fieldLabel' => 'Context Type', 'type' => 'select', 'selectOptions' => available_target_types, 'defaultValue' => default_target_type, 'required' => true, 'description' => 'Context Type determines the type of target(s) for the execution'}], options[:options], @api_client)['targetType']
        end
      end
      if target_type
        params['targetType'] = target_type
      end
      if target_type == 'instance'
        if instance_ids.empty?
          instance_ids_value = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'instances', 'fieldName' => 'instances', 'fieldLabel' => 'Instance(s)', 'type' => 'text', 'required' => true, 'description' => 'Instances, comma separated list of instance names or IDs.'}], options[:options], @api_client)['instances']
          instance_ids = parse_array(instance_ids_value)
        end
        instance_ids.each do |instance_id|
          instance = find_instance_by_name_or_id(instance_id)
          return 1 if instance.nil?
          instances << instance
        end
        params['instances'] = instances.collect {|it| it['id'] }
      elsif target_type == 'instance-label'
        if instance_label.nil?
          instance_label = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'instance-label', 'fieldName' => 'instanceLabel', 'fieldLabel' => 'Instance Label', 'type' => 'text', 'required' => true, 'description' => 'Instance Label'}], options[:options], @api_client)['instanceLabel']
        end
        # params['config'] ||= {}
        # params['config']['instanceLabel'] = instance_label
        params['instanceLabel'] = instance_label
      elsif target_type == 'server'
        if server_ids.empty?
          server_ids_value = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'servers', 'fieldName' => 'servers', 'fieldLabel' => 'Server(s)', 'type' => 'text', 'required' => true, 'description' => 'Servers, comma separated list of server names or IDs.'}], options[:options], @api_client)['servers']
          server_ids = parse_array(server_ids_value)
        end
        server_ids.each do |server_id|
          server = find_server_by_name_or_id(server_id)
          return 1 if server.nil?
          servers << server
        end
        params['servers'] = servers.collect {|it| it['id'] }
      elsif target_type == 'server-label'
        if server_label.nil?
          server_label = Morpheus::Cli::OptionTypes.prompt([{'switch' => 'server-label', 'fieldName' => 'serverLabel', 'fieldLabel' => 'Server Label', 'type' => 'text', 'required' => true, 'description' => 'Server Label'}], options[:options], @api_client)['serverLabel']
        end
        # params['config'] ||= {}
        # params['config']['serverLabel'] = server_label
        params['serverLabel'] = server_label
      end
      # todo: prompt to task optionTypes for customOptions
      if task['optionTypes']
        
      end
      job_payload = {}
      job_payload.deep_merge!(params)
      job_payload.deep_merge!(passed_options) unless passed_options.empty?
      payload = {'job' => job_payload}
    end

    @tasks_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @tasks_interface.dry.run(task['id'], payload)
      return
    end
    json_response = @tasks_interface.run(task['id'], payload)
    render_response(json_response, options) do
      target_desc = nil
      if instances.size() > 0
        target_desc = (instances.size() == 1) ? "instance #{instances[0]['name']}" : "#{instances.size()} instances"
      elsif servers.size() > 0
        target_desc = (servers.size() == 1) ? "host #{servers[0]['name']}" : "#{servers.size()} hosts"
      end
      if target_desc
        print_green_success "Executing task #{task['name']} on #{target_desc}"
      else
        print_green_success "Executing task #{task['name']}"
      end
      if json_response["jobExecution"] && json_response["jobExecution"]["id"]
        job_execution_id = json_response["jobExecution"]["id"]
        if options[:no_refresh]
          get_args = [json_response["jobExecution"]["id"], "--details"] + (options[:remote] ? ["-r",options[:remote]] : [])
          Morpheus::Logging::DarkPrinter.puts((['jobs', 'get-execution'] + get_args).join(' ')) if Morpheus::Logging.debug?
          ::Morpheus::Cli::JobsCommand.new.handle(['get-execution'] + get_args)
        else
          #Morpheus::Cli::JobsCommand.new.handle(["get-execution", job_execution_id, "--refresh", options[:refresh_interval].to_s]+ (options[:remote] ? ["-r",options[:remote]] : []))
          job_execution_results = wait_for_job_execution(job_execution_id, options.merge({:details => true}))
        end
      end
    end
    return 0, nil
  end
end
extract_app_tiers(app) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2341
def extract_app_tiers(app)
  tier_rows = []
  begin
    app_tiers = app['appTiers'] || []
    sorted_app_tiers = app_tiers.sort {|a,b| a['bootSequence'] <=> b['bootSequence'] }
    sorted_app_tiers.each do |app_tier|
      tier_name = app_tier['tier']['name']
      boot_sequence = app_tier['bootSequence'] || 0
      instances = (app_tier['appInstances'] || []).collect {|it| it['instance']}
      row = {tier_name: tier_name, boot_sequence: boot_sequence, instances: instances}
      tier_rows << row
    end
  rescue => ex
    Morpheus::Logging::DarkPrinter.puts "Error extracting app instances: #{ex}" if Morpheus::Logging.debug?
  end
  return tier_rows
end
filter_data(data, include_fields=nil, exclude_fields=nil) click to toggle source

filter_data filters Hash-like data to only the specified fields To specify fields of child objects, use a “.” Usage: filter_data(instance, [“id”, “name”, “plan.name”])

# File lib/morpheus/formatters.rb, line 250
def filter_data(data, include_fields=nil, exclude_fields=nil)
  if !data
    return data
  elsif data.is_a?(Array)
    new_data = data.collect { |it| filter_data(it, include_fields, exclude_fields) }
    return new_data
  elsif data.is_a?(Hash)
    if include_fields
      #new_data = data.select {|k, v| include_fields.include?(k.to_s) || include_fields.include?(k.to_sym) }
      # allow extracting dot pathed fields, just like get_object_value
      my_data = {}
      include_fields.each do |field|
        if field.nil?
          next
        end
        field = field.to_s
        if field.empty?
          next
        end

        # supports "field as Label"
        field_key = field.strip
        field_label = field_key

        if field.index(/\s+as\s+/)
          field_key, field_label = field.split(/\s+as\s+/)
          if !field_label
            field_label = field_key
          end
        end

        if field.include?(".")
          #if field.index(/\s+as\s+/)
          if field_label != field_key
            # collapse to a value
            my_data[field_label] = get_object_value(data, field_key)
          else
            # keep the full object structure
            namespaces = field.split(".")
            cur_data = data
            cur_filtered_data = my_data
            namespaces.each_with_index do |ns, index|
              if index != namespaces.length - 1
                if cur_data && cur_data.respond_to?("key?")
                  cur_data = cur_data.key?(ns) ? cur_data[ns] : cur_data[ns.to_sym]
                else
                  cur_data = nil
                end
                cur_filtered_data[ns] ||= {}
                cur_filtered_data = cur_filtered_data[ns]
              else
                if cur_data && cur_data.respond_to?("key?")
                  cur_filtered_data[ns] = cur_data.key?(ns) ? cur_data[ns] : cur_data[ns.to_sym]
                else
                  cur_filtered_data[ns] = nil
                end
              end
            end
          end
        else
          #my_data[field] = data[field] || data[field.to_sym]
          my_data[field_label] = data.key?(field_key) ? data[field_key] : data[field_key.to_sym]
        end
      end
      return my_data
    elsif exclude_fields
      new_data = data.reject {|k, v| exclude_fields.include?(k.to_s) || exclude_fields.include?(k.to_sym) }
      return new_data
    end
  else
    return data # .clone
  end
end
find_blueprint_by_id(id) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2490
def find_blueprint_by_id(id)
  begin
    json_response = @blueprints_interface.get(id.to_i)
    return json_response['blueprint']
  rescue RestClient::Exception => e
    if e.response && e.response.code == 404
      print_red_alert "Blueprint not found by id #{id}"
    else
      raise e
    end
  end
end
find_blueprint_by_name(name) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2503
def find_blueprint_by_name(name)
  blueprints = @blueprints_interface.list({name: name.to_s})['blueprints']
  if blueprints.empty?
    print_red_alert "Blueprint not found by name #{name}"
    return nil
  elsif blueprints.size > 1
    print_red_alert "#{blueprints.size} blueprints found by name #{name}"
    # print_blueprints_table(blueprints, {color: red})
    rows = blueprints.collect { |it| {id: it['id'], name: it['name']} }
    print red
    print as_pretty_table(rows, [:id, :name], {color:red})
    print reset,"\n"
    return nil
  else
    return blueprints[0]
  end
end
find_blueprint_by_name_or_id(val) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2482
def find_blueprint_by_name_or_id(val)
  if val.to_s =~ /\A\d{1,}\Z/
    return find_blueprint_by_id(val)
  else
    return find_blueprint_by_name(val)
  end
end
find_instance_by_id(id) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1303
def find_instance_by_id(id)
  begin
    json_response = @instances_interface.get(id.to_i)
    return json_response['instance']
  rescue RestClient::Exception => e
    if e.response && e.response.code == 404
      print_red_alert "Instance not found by id #{id}"
      return nil
    else
      raise e
    end
  end
end
find_instance_by_name(name) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1317
def find_instance_by_name(name)
  instances = @instances_interface.list({name: name.to_s})['instances']
  if instances.empty?
    print_red_alert "Instance not found by name #{name}"
    return nil
  elsif instances.size > 1
    print_red_alert "#{instances.size} instances found by name #{name}"
    as_pretty_table(instances, [:id, :name], {color: red})
    print_red_alert "Try using ID instead"
    print reset,"\n"
    return nil
  else
    return instances[0]
  end
end
find_instance_by_name_or_id(val) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1295
def find_instance_by_name_or_id(val)
  if val.to_s =~ /\A\d{1,}\Z/
    return find_instance_by_id(val)
  else
    return find_instance_by_name(val)
  end
end
find_server_by_id(id) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1341
def find_server_by_id(id)
  begin
    json_response = @servers_interface.get(id.to_i)
    return json_response['server']
  rescue RestClient::Exception => e
    if e.response && e.response.code == 404
      print_red_alert "Server not found by id #{id}"
      return nil
    else
      raise e
    end
  end
end
find_server_by_name(name) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1355
def find_server_by_name(name)
  servers = @servers_interface.list({name: name.to_s})['servers']
  if servers.empty?
    print_red_alert "Host not found by name #{name}"
    return nil
  elsif servers.size > 1
    print_red_alert "#{servers.size} hosts found by name #{name}"
    as_pretty_table(servers, [:id, :name], {color: red})
    print_red_alert "Try using ID instead"
    print reset,"\n"
    return nil
  else
    return servers[0]
  end
end
find_server_by_name_or_id(val) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1333
def find_server_by_name_or_id(val)
  if val.to_s =~ /\A\d{1,}\Z/
    return find_server_by_id(val)
  else
    return find_server_by_name(val)
  end
end
find_task_by_id(id) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1219
def find_task_by_id(id)
  begin
    json_response = @tasks_interface.get(id.to_i)
    return json_response['task']
  rescue RestClient::Exception => e
    if e.response && e.response.code == 404
      print_red_alert "Task not found by id #{id}"
      return nil
    else
      raise e
    end
  end
end
find_task_by_name(name) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1233
def find_task_by_name(name)
  tasks = @tasks_interface.list({name: name.to_s})['tasks']
  if tasks.empty?
    print_red_alert "Task not found by name #{name}"
    return nil
  elsif tasks.size > 1
    print_red_alert "#{tasks.size} tasks by name #{name}"
    print_tasks_table(tasks, {color: red})
    print reset,"\n\n"
    return nil
  else
    return tasks[0]
  end
end
find_task_by_name_or_id(val) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1211
def find_task_by_name_or_id(val)
  if val.to_s =~ /\A\d{1,}\Z/
    return find_task_by_id(val)
  else
    return find_task_by_name(val)
  end
end
find_task_type_by_name(val) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1248
def find_task_type_by_name(val)
  raise "find_task_type_by_name passed a bad name: #{val.inspect}" if val.to_s == ''
  @all_task_types ||= @tasks_interface.list_types({max:1000})['taskTypes']

  if @all_task_types.nil? && !@all_task_types.empty?
    print_red_alert "No task types found"
    return nil
  end
  matching_task_types = @all_task_types.select { |it| val && (it['name'] == val || it['code'] == val ||  it['id'].to_s == val.to_s) }
  if matching_task_types.size == 1
    return matching_task_types[0]
  elsif matching_task_types.size == 0
    print_red_alert "Task Type not found by '#{val}'"
  else
    print_red_alert "#{matching_task_types.size} task types found by name #{name}"
    rows = matching_task_types.collect do |it|
      {id: it['id'], name: it['name'], code: it['code']}
    end
    print "\n"
    puts as_pretty_table(rows, [:name, :code], {color:red})
    return nil
  end
end
firewall_disable(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1795
def firewall_disable(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    build_common_options(opts, options, [:json, :dry_run])
  end
  optparse.parse!(args)
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} firewall-disable expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
    return 1
  end
  connect(options)

  begin
    app = find_app_by_name_or_id(args[0])
    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.firewall_disable(app['id'])
      return
    end
    @apps_interface.firewall_disable(app['id'])
    security_groups([args[0]])
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
firewall_enable(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1824
def firewall_enable(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    build_common_options(opts, options, [:json, :dry_run])
  end
  optparse.parse!(args)
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} firewall-enable expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
    return 1
  end
  connect(options)

  begin
    app = find_app_by_name_or_id(args[0])
    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.firewall_enable(app['id'])
      return
    end
    @apps_interface.firewall_enable(app['id'])
    security_groups([args[0]])
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
format_app_tiers(app) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2451
def format_app_tiers(app)
  out = ""
  begin
    app_tiers = app['appTiers']
    if app_tiers
      app_tier_names = app_tiers.collect { |app_tier| app_tier['tier']['name'] }
      out << app_tier_names.join(", ")
    end
    if out.empty?
      #out = "(Empty)"
    end
  rescue => ex
    Morpheus::Logging::DarkPrinter.puts "A formatting exception occured: #{ex}" if Morpheus::Logging.debug?
  end
  out
end
format_bytes(bytes, units="B", round=nil) click to toggle source
# File lib/morpheus/formatters.rb, line 324
def format_bytes(bytes, units="B", round=nil)
  out = ""
  if bytes
    if bytes < 1024 && units == "B"
      out = "#{bytes.to_i} B"
    else
      out = Filesize.from("#{bytes}#{units == 'auto' ? '' : " #{units}"}").pretty.strip
      out = out.split(' ')[0].to_f.round(round).to_s + ' ' + out.split(' ')[1] if round
    end
  end
  out
end
format_bytes_short(bytes) click to toggle source

returns bytes in an abbreviated format eg. 3.1K instead of 3.10 KiB

# File lib/morpheus/formatters.rb, line 339
def format_bytes_short(bytes)
  out = format_bytes(bytes)
  if out.include?(" ")
    val, units = out.split(" ")
    val = val.to_f
    # round to 0 or 1 decimal point
    if val % 1 == 0
      val = val.round(0).to_s
    else
      val = val.round(1).to_s
    end
    # K instead of KiB
    units = units[0].chr
    out = "#{val}#{units}"
  end
  return out
end
format_currency(amount, currency='USD', opts={}) click to toggle source

returns currency amount formatted like “$4,5123.00”. 0.00 is formatted as “$0” this is not ideal

# File lib/morpheus/formatters.rb, line 412
def format_currency(amount, currency='USD', opts={})
  # currency '' should probably error, like money gem does
  if currency.to_s.empty?
    currency = 'USD'
  end
  currency = currency.to_s.upcase

  amount = amount.to_f
  if amount == 0
    return currency_sym(currency).to_s + "0"
  # elsif amount.to_f < 0.01
  #   # return exponent notation like 3.4e-09
  #   return currency_sym(currency).to_s + "#{amount}"
  else
    sigdig = opts[:sigdig] ? opts[:sigdig].to_i : 2 # max decimal digits
    min_sigdig = opts[:min_sigdig] ? opts[:min_sigdig].to_i : (sigdig || 2) # min decimal digits
    display_value = format_sig_dig(amount, sigdig, min_sigdig, opts[:pad_zeros])
    display_value = format_number(display_value) # commas
    rtn = currency_sym(currency).to_s + display_value
    if amount.to_i < 0
      rtn = "(#{rtn})"
      if opts[:minus_color]
        rtn = "#{opts[:minus_color]}#{rtn}#{opts[:return_color] || cyan}"
      end
    end
    rtn
  end
end
Also aliased as: format_money
format_date(dt, options={}) click to toggle source
# File lib/morpheus/formatters.rb, line 78
def format_date(dt, options={})
  format_dt(dt, {format: DEFAULT_DATE_FORMAT}.merge(options))
end
format_dt(dt, options={}) click to toggle source
# File lib/morpheus/formatters.rb, line 64
def format_dt(dt, options={})
  dt = parse_time(dt)
  return "" if dt.nil?
  if options[:local]
    dt = dt.getlocal
  end
  format = options[:format] || DEFAULT_TIME_FORMAT
  return dt.strftime(format)
end
format_dt_as_param(dt) click to toggle source
# File lib/morpheus/formatters.rb, line 86
def format_dt_as_param(dt)
  dt = dt.getutc
  format_dt(dt, {format: "%Y-%m-%d %X"})
end
format_duration(start_time, end_time=nil, format="human") click to toggle source
# File lib/morpheus/formatters.rb, line 91
def format_duration(start_time, end_time=nil, format="human")
  if !start_time
    return ""
  end
  start_time = parse_time(start_time)
  if end_time
    end_time = parse_time(end_time)
  else
    end_time = Time.now
  end
  seconds = (end_time - start_time).abs
  format_duration_seconds(seconds, format)
end
format_duration_ago(start_time, end_time=nil) click to toggle source
# File lib/morpheus/formatters.rb, line 105
def format_duration_ago(start_time, end_time=nil)
  if !start_time
    return ""
  end
  start_time = parse_time(start_time)
  if end_time
    end_time = parse_time(end_time)
  else
    end_time = Time.now
  end
  seconds = (end_time - start_time).abs
  format_human_duration(seconds, true)
end
format_duration_milliseconds(milliseconds, format="human", ms_threshold=1000) click to toggle source
# File lib/morpheus/formatters.rb, line 135
def format_duration_milliseconds(milliseconds, format="human", ms_threshold=1000)
  out = ""
  milliseconds = milliseconds.to_i.abs
  if ms_threshold && ms_threshold > milliseconds
    out = "#{milliseconds}ms"
  else
    out = format_duration_seconds((milliseconds.to_f / 1000).floor, format)
  end
  out
end
format_duration_seconds(seconds, format="human") click to toggle source
# File lib/morpheus/formatters.rb, line 119
def format_duration_seconds(seconds, format="human")
  seconds = seconds.abs
  out = ""
  # interval = Math.abs(interval)
  if format == "human"
    out = format_human_duration(seconds)
  elsif format
    interval_time = Time.mktime(0) + seconds
    out = interval_time.strftime(format)
  else
    interval_time = Time.mktime(0) + seconds
    out = interval_time.strftime("%H:%M:%S")
  end
  out
end
format_human_duration(seconds, show_relative=false) click to toggle source

returns a human readable time duration @param seconds - duration in seconds

# File lib/morpheus/formatters.rb, line 148
def format_human_duration(seconds, show_relative=false)
  out = ""
  #seconds = seconds.round
  days, hours, minutes = (seconds / (60*60*24)).floor, (seconds / (60*60)).floor, (seconds / (60)).floor
  if days > 365
    out << "#{days.floor} days"
  elsif days > 61
    out << "#{days.floor} days"
  elsif days > 31
    out << "#{days.floor} days"
  elsif days > 0
    if days.floor == 1
      out << "1 day"
    else
      out << "#{days.floor} days"
    end
  elsif hours > 1
    if hours == 1
      out << "1 hour"
    else
      out << "#{hours.floor} hours"
    end
  elsif minutes > 1
    if minutes == 1
      out << "1 minute"
    else
      out << "#{minutes.floor} minutes"
    end
  elsif seconds > 0 && seconds < 1
    ms = (seconds.to_f * 1000).to_i
    out << "#{ms}ms"
  else
    if seconds.floor == 1
      out << "1 second"
    else
      out << "#{seconds.floor} seconds"
    end
  end
  if show_relative
    if seconds < 1
      out = "just now"
    else
      out << " ago"
    end
  end
  out
end
format_list(items, conjunction="", limit=nil) click to toggle source

def format_money(amount, currency=‘usd’, opts={})

format_currency(amount, currency, opts)

end

# File lib/morpheus/formatters.rb, line 447
def format_list(items, conjunction="", limit=nil)
  items = items ? items.clone : []
  num_items = items.size
  if limit
    items = items.first(limit)
  end
  last_item = items.pop
  if items.empty?
    return "#{last_item}"
  else
    if limit && limit < num_items
      items << last_item
      last_item = "(#{num_items - items.size} more)"
    end
    return items.join(", ") + (conjunction.to_s.empty? ? ", " : " #{conjunction} ") + "#{last_item}"
  end
end
format_local_date(dt, options={}) click to toggle source
# File lib/morpheus/formatters.rb, line 82
def format_local_date(dt, options={})
  format_dt(dt, {local: true, format: DEFAULT_DATE_FORMAT}.merge(options))
end
format_local_dt(dt, options={}) click to toggle source
# File lib/morpheus/formatters.rb, line 74
def format_local_dt(dt, options={})
  format_dt(dt, {local: true}.merge(options))
end
format_money(amount, currency='USD', opts={})
Alias for: format_currency
format_name_and_id(obj) click to toggle source
# File lib/morpheus/formatters.rb, line 481
def format_name_and_id(obj)
  if(obj.is_a?(Array))
    return "" if obj.empty? # && hide_empty
    names, ids = obj.collect {|it| it['name'] rescue "" }, obj.collect {|it| it['id'] rescue "" }
    "#{names.join(", ")} [#{ids.join(",")}]"
  elsif(obj.is_a?(Hash))
    "#{obj['name']} [#{obj['id']}]" rescue ""
  else
    object.to_s
  end
end
format_name_values(obj) click to toggle source
# File lib/morpheus/formatters.rb, line 473
def format_name_values(obj)
  if obj.is_a?(Hash)
    obj.collect {|k,v| "#{k}: #{v}"}.join(", ")
  else
    ""
  end
end
format_number(n, opts={}) click to toggle source
# File lib/morpheus/formatters.rb, line 362
def format_number(n, opts={})
  delim = opts[:delimiter] || ','
  out = ""
  parts = n.to_s.split(".")
  whole_number = parts[0].to_s
  decimal = parts[1] ? parts[1..-1].join('.') : nil
  i = 0
  whole_number.reverse.each_char do |c|
    if c == "-"
      out = "#{c}#{out}"
    else
      out = (i > 0 && i % 3 == 0) ? "#{c}#{delim}#{out}" : "#{c}#{out}"
    end
    i+= 1
  end
  if decimal
    out << "." + decimal
  end
  return out
end
format_ok_status(status) click to toggle source
# File lib/morpheus/formatters.rb, line 497
def format_ok_status(status)
  color = cyan
  if ['ok'].include? status
    color = green
  elsif ['error'].include? status
    color = red
  elsif ['warning'].include? status
    color = yellow
  end
  "#{color}#{status.to_s.upcase}#{cyan}"
end
format_sig_dig(n, sigdig=3, min_sigdig=nil, pad_zeros=false) click to toggle source
# File lib/morpheus/formatters.rb, line 383
def format_sig_dig(n, sigdig=3, min_sigdig=nil, pad_zeros=false)
  v = ""
  if sigdig && sigdig > 0
    # v = n.to_i.round(sigdig).to_s
    v = sprintf("%.#{sigdig}f", n)
  else
    v = n.to_i.round().to_s
  end
  # if pad_zeros != true
  #   v = v.to_f.to_s
  # end
  if min_sigdig
    v_parts =  v.split(".")
    decimal_str = v_parts[1]
    if decimal_str == nil
      v = v + "." + ('0' * min_sigdig)
    elsif decimal_str.size < min_sigdig
      v = v + ('0' * (min_sigdig - decimal_str.size))
    end
  end
  v
end
get_available_blueprints(refresh=false) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2468
def get_available_blueprints(refresh=false)
  if !@available_blueprints || refresh
    #results = @options_interface.options_for_source('appTemplates',{}) # still exists
    results = @options_interface.options_for_source('blueprints',{})
    @available_blueprints = results['data'].collect {|it|
      {"id" => it["value"], "name" => it["name"], "value" => it["value"]}
    }
    default_option = {"id" => "existing", "name" => "Existing Instances", "value" => "existing", "type" => "morpheus"}
    @available_blueprints.unshift(default_option)
  end
  #puts "get_available_blueprints() rtn: #{@available_blueprints.inspect}"
  return @available_blueprints
end
get_object_value(data, key) click to toggle source

get_object_value returns a value within a Hash like object Usage: get_object_value(host, “plan.name”)

# File lib/morpheus/formatters.rb, line 212
def get_object_value(data, key)
  value = nil
  if key.is_a?(Proc)
    return key.call(data)
  end
  key = key.to_s
  if key.include?(".")
    namespaces = key.split(".")
    value = data
    namespaces.each do |ns|
      if value.respond_to?("key?")
        if value.key?(ns.to_s)
          value = value[ns]
        elsif value.key?(ns.to_sym)
          value = value[ns.to_sym]
        else
          value = nil
        end
      else
        value = nil
      end
    end
  else
    # value = data.key?(key) ? data[key] : data[key.to_sym]
    if data.respond_to?("key?")
      if data.key?(key.to_s)
        value = data[key.to_s]
      elsif data.key?(key.to_sym)
        value = data[key.to_sym]
      end
    end
  end
  return value
end
get_scoped_instance_config(instance_config, env_name, group_name, cloud_name) click to toggle source

lookup scoped instance config in a blueprint this only finds one right now

# File lib/morpheus/cli/commands/apps.rb, line 2523
def get_scoped_instance_config(instance_config, env_name, group_name, cloud_name)
    config = instance_config.clone
    if env_name.to_s != '' && config['environments'] && config['environments'][env_name]
      config = config['environments'][env_name].clone
    end
    if group_name.to_s != '' && config['groups'] && config['groups'][group_name]
      config = config['groups'][group_name].clone
    end
    if cloud_name.to_s != '' && config['clouds'] && config['clouds'][cloud_name]
      config = config['clouds'][cloud_name].clone
    end
    config.delete('environments')
    config.delete('groups')
    config.delete('clouds')
    # puts "get_scoped_instance_config(instance_config, #{env_name}, #{group_name}, #{cloud_name})"
    # puts "returned config: #{config}"
    return config
end
get_task_type(args) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1130
def get_task_type(args)
  params = {}
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[type]")
    build_common_options(opts, options, [:query, :json, :yaml, :csv, :fields, :dry_run, :remote])
    opts.footer = "Get details about a task type.\n" +
                  "[type] is required. This is the id or code or name of a task type."
  end
  optparse.parse!(args)
  if args.count != 1
    raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
  end
  connect(options)
  begin
    @tasks_interface.setopts(options)
    if options[:dry_run]
      if args[0].to_s =~ /\A\d{1,}\Z/
        print_dry_run @tasks_interface.dry.get_type(args[0].to_i)
      else
        print_dry_run @tasks_interface.dry.list_types({name:args[0]})
      end
      return
    end
    # find_task_type_by_name actually finds by name or code id
    task_type = find_task_type_by_name(args[0])
    return 1 if task_type.nil?
    json_response = {'taskType' => task_type}  # skip redundant request
    # json_response = @tasks_interface.get(task_type['id'])
    
    render_result = render_with_format(json_response, options, 'taskType')
    return 0 if render_result

    task_type = json_response['taskType']

    title = "Morpheus Task Type"
    
    print_h1 "Morpheus Task Type", [], options
    
    print cyan
    description_cols = {
      "ID" => 'id',
      "Name" => 'name',
      "Code" => 'name',
      #"Description" => 'description',
      "Scriptable" => lambda {|it| format_boolean(it['scriptable']) },
      # lots more here
      # "enabled" => lambda {|it| format_boolean(it['enabled']) },
      # "hasResults" => lambda {|it| format_boolean(it['hasResults']) },
      # "allowRemoteKeyAuth" => lambda {|it| format_boolean(it['allowRemoteKeyAuth']) },
      # "allowExecuteLocal" => lambda {|it| format_boolean(it['allowExecuteLocal']) },
      # "allowExecuteRemote" => lambda {|it| format_boolean(it['allowExecuteRemote']) },
      # "allowExecuteResource" => lambda {|it| format_boolean(it['allowExecuteResource']) },
      # "allowLocalRepo" => lambda {|it| format_boolean(it['allowLocalRepo']) },
      # "allowRemoteKeyAuth" => lambda {|it| format_boolean(it['allowRemoteKeyAuth']) },
    }
    print_description_list(description_cols, task_type)

    option_types = task_type['optionTypes'] || []
    option_types = option_types.sort {|x,y| x['displayOrder'] <=> y['displayOrder'] }
    if !option_types.empty?
      print_h2 "Config Option Types", [], options
      option_type_cols = {
        "Name" => lambda {|it| it['fieldContext'].to_s != '' ? "#{it['fieldContext']}.#{it['fieldName']}" : it['fieldName'] },
        "Label" => lambda {|it| it['fieldLabel'] },
        "Type" => lambda {|it| it['type'] },
      }
      print cyan
      print as_pretty_table(option_types, option_type_cols)
    end
    
    print reset,"\n"
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    return 1
  end
end
history(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1945
def history(args)
  raw_args = args.dup
  options = {}
  #options[:show_output] = true
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    # opts.on( '-n', '--node NODE_ID', "Scope history to specific Container or VM" ) do |node_id|
    #   options[:node_id] = node_id.to_i
    # end
    opts.on( nil, '--events', "Display sub processes (events)." ) do
      options[:show_events] = true
    end
    opts.on( nil, '--output', "Display process output." ) do
      options[:show_output] = true
    end
    opts.on(nil, '--details', "Display more details. Shows everything, untruncated." ) do
      options[:show_events] = true
      options[:show_output] = true
      options[:details] = true
    end
    build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
    opts.footer = "List historical processes for a specific app.\n" + 
                  "[app] is required. This is the name or id of an app."
  end
  optparse.parse!(args)

  if args.count != 1
    puts optparse
    return 1
  end
  connect(options)
  begin
    app = find_app_by_name_or_id(args[0])

    instance_ids = []
    # API used to only return apps.appTiers
    # now returns detailed instance list as "instances"
    app_tiers = app['appTiers'] || []
    instances = app['instances']
    if instances.nil?
      instances = []
      app_tiers.each do |app_tier|
        instances += (app_tier['appInstances'] || []).collect {|it| it['instance']}.flatten().compact
      end
    end
    instances.each do |instance|
      instance_ids << instance['id']
    end
    
    # container_ids = instance['containers']
    # if options[:node_id] && container_ids.include?(options[:node_id])
    #   container_ids = [options[:node_id]]
    # end
    params = {}
    params['instanceIds'] = instance_ids
    params.merge!(parse_list_options(options))
    # params['query'] = params.delete('phrase') if params['phrase']
    @processes_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @processes_interface.dry.list(params)
      return
    end
    json_response = @processes_interface.list(params)
    if options[:json]
      puts as_json(json_response, options, "processes")
      return 0
    elsif options[:yaml]
      puts as_yaml(json_response, options, "processes")
      return 0
    elsif options[:csv]
      puts records_as_csv(json_response['processes'], options)
      return 0
    else

      title = "App History: #{app['name']}"
      subtitles = []
      if params[:query]
        subtitles << "Search: #{params[:query]}".strip
      end
      subtitles += parse_list_subtitles(options)
      print_h1 title, subtitles, options
      if json_response['processes'].empty?
        print "#{cyan}No process history found.#{reset}\n\n"
      else
        history_records = []
        json_response["processes"].each do |process|
          row = {
            id: process['id'],
            eventId: nil,
            uniqueId: process['uniqueId'],
            name: process['displayName'],
            description: process['description'],
            processType: process['processType'] ? (process['processType']['name'] || process['processType']['code']) : process['processTypeName'],
            createdBy: process['createdBy'] ? (process['createdBy']['displayName'] || process['createdBy']['username']) : '',
            startDate: format_local_dt(process['startDate']),
            duration: format_process_duration(process),
            status: format_process_status(process),
            error: format_process_error(process),
            output: format_process_output(process)
          }
          history_records << row
          process_events = process['events'] || process['processEvents']
          if options[:show_events]
            if process_events
              process_events.each do |process_event|
                event_row = {
                  id: process['id'],
                  eventId: process_event['id'],
                  uniqueId: process_event['uniqueId'],
                  name: process_event['displayName'], # blank like the UI
                  description: process_event['description'],
                  processType: process_event['processType'] ? (process_event['processType']['name'] || process_event['processType']['code']) : process['processTypeName'],
                  createdBy: process_event['createdBy'] ? (process_event['createdBy']['displayName'] || process_event['createdBy']['username']) : '',
                  startDate: format_local_dt(process_event['startDate']),
                  duration: format_process_duration(process_event),
                  status: format_process_status(process_event),
                  error: format_process_error(process_event, options[:details] ? nil : 20),
                  output: format_process_output(process_event, options[:details] ? nil : 20)
                }
                history_records << event_row
              end
            else
              
            end
          end
        end
        columns = [
          {:id => {:display_name => "PROCESS ID"} },
          :name, 
          :description, 
          {:processType => {:display_name => "PROCESS TYPE"} },
          {:createdBy => {:display_name => "CREATED BY"} },
          {:startDate => {:display_name => "START DATE"} },
          {:duration => {:display_name => "ETA/DURATION"} },
          :status, 
          :error
        ]
        if options[:show_events]
          columns.insert(1, {:eventId => {:display_name => "EVENT ID"} })
        end
        if options[:show_output]
          columns << :output
        end
        # custom pretty table columns ...
        if options[:include_fields]
          columns = options[:include_fields]
        end
        print cyan
        print as_pretty_table(history_records, columns, options)
        #print_results_pagination(json_response)
        print_results_pagination(json_response, {:label => "process", :n_label => "processes"})
        print reset, "\n"
        return 0
      end
    end
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
iso8601(dt) click to toggle source
# File lib/morpheus/formatters.rb, line 206
def iso8601(dt)
  dt.instance_of(Time) ? dt.iso8601 : "#{dt}"
end
list_task_types(args) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1080
def list_task_types(args)
  params = {}
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage()
    build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
    opts.footer = "List task types."
  end
  optparse.parse!(args)
  if args.count > 0
    raise_command_error "wrong number of arguments, expected 0 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
  end
  connect(options)
  begin
    params.merge!(parse_list_options(options))
    @tasks_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @tasks_interface.dry.list_types(params)
      return
    end
    json_response = @tasks_interface.list_types(params)


    render_result = render_with_format(json_response, options, 'taskTypes')
    return 0 if render_result
    
    title = "Morpheus Task Types"
    subtitles = []
    subtitles += parse_list_subtitles(options)
    print_h1 title, subtitles
    task_types = json_response['taskTypes']
    if task_types.empty?
      print cyan,"No task types found.",reset,"\n"
    else
      print cyan
      rows = task_types.collect do |task_type|
        {name: task_type['name'], id: task_type['id'], code: task_type['code'], description: task_type['description']}
      end
      print as_pretty_table(rows, [:id, :name, :code], options)
      #print_results_pagination(json_response)
      print_results_pagination({size:task_types.size,total:task_types.size})
    end
    print reset,"\n"
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
logs(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1524
def logs(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    # opts.on('--hosts HOSTS', String, "Filter logs to specific Host ID(s)") do |val|
    #   params['servers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
    # end
    # opts.on('--servers HOSTS', String, "alias for --hosts") do |val|
    #   params['servers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
    # end
    # opts.on('--vms HOSTS', String, "alias for --hosts") do |val|
    #   params['servers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
    # end
    # opts.on('--container CONTAINER', String, "Filter logs to specific Container ID(s)") do |val|
    #   params['containers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
    # end
    opts.on( '-n', '--node NODE_ID', "Scope logs to specific Container or VM" ) do |node_id|
      options[:node_id] = node_id.to_i
    end
    # opts.on('--nodes HOST', String, "alias for --containers") do |val|
    #   params['containers'] = val.to_s.split(",").collect {|it| it.to_s.strip }.select {|it| it }.compact
    # end
    opts.on('--start TIMESTAMP','--start TIMESTAMP', "Start timestamp. Default is 30 days ago.") do |val|
      options[:start] = parse_time(val) #.utc.iso8601
    end
    opts.on('--end TIMESTAMP','--end TIMESTAMP', "End timestamp. Default is now.") do |val|
      options[:end] = parse_time(val) #.utc.iso8601
    end
    # opts.on('--interval TIME','--interval TIME', "Interval of time to include, in seconds. Default is 30 days ago.") do |val|
    #   options[:interval] = parse_time(val).utc.iso8601
    # end
    opts.on('--level VALUE', String, "Log Level. DEBUG,INFO,WARN,ERROR") do |val|
      params['level'] = params['level'] ? [params['level'], val].flatten : [val]
    end
    opts.on('--table', '--table', "Format ouput as a table.") do
      options[:table] = true
    end
    opts.on('-a', '--all', "Display all details: entire message." ) do
      options[:details] = true
    end
    build_common_options(opts, options, [:list, :query, :json, :yaml, :csv, :fields, :dry_run, :remote])
    opts.footer = "List logs for an app.\n" +
                  "[app] is required. This is the name or id of an app."
  end
  optparse.parse!(args)
  if args.count !=1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} logs expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
    return 1
  end
  connect(options)
  begin
    app = find_app_by_name_or_id(args[0])
    container_ids = []
    # API used to only return apps.appTiers
    # now returns detailed instance list as 'instances'
    app_tiers = app['appTiers'] || []
    instances = app['instances']
    if instances.nil?
      instances = []
      app_tiers.each do |app_tier|
        instances += (app_tier['appInstances'] || []).collect {|it| it['instance']}.flatten().compact
      end
    end
    instances.each do |instance|
      container_ids += instance['containers']
    end
    if container_ids.empty?
      print cyan,"app is empty",reset,"\n"
      return 0
      # print_error yellow,"app is empty",reset,"\n"
      # return 1
    end

    if options[:node_id]
      if container_ids.include?(options[:node_id])
        container_ids = [options[:node_id]]
      else
        print_red_alert "App does not include node #{options[:node_id]}"
        return 1
      end
    end
    params = {}
    params['level'] = params['level'].collect {|it| it.to_s.upcase }.join('|') if params['level'] # api works with INFO|WARN
    params.merge!(parse_list_options(options))
    params['query'] = params.delete('phrase') if params['phrase']
    params['order'] = params['direction'] unless params['direction'].nil? # old api version expects order instead of direction
    params['startMs'] = (options[:start].to_i * 1000) if options[:start]
    params['endMs'] = (options[:end].to_i * 1000) if options[:end]
    params['interval'] = options[:interval].to_s if options[:interval]
    @logs_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @logs_interface.dry.container_logs(container_ids, params)
      return
    end
    json_response = @logs_interface.container_logs(container_ids, params)
    render_result = json_response['logs'] ? render_with_format(json_response, options, 'logs') : render_with_format(json_response, options, 'data')
    return 0 if render_result

    title = "App Logs: #{app['name']}"
    subtitles = parse_list_subtitles(options)
    if options[:start]
      subtitles << "Start: #{options[:start]}".strip
    end
    if options[:end]
      subtitles << "End: #{options[:end]}".strip
    end
    if params[:query]
      subtitles << "Search: #{params[:query]}".strip
    end
    if params['servers']
      subtitles << "Servers: #{params['servers']}".strip
    end
    if params['containers']
      subtitles << "Containers: #{params['containers']}".strip
    end
    if params[:query]
      subtitles << "Search: #{params[:query]}".strip
    end
    if params['level']
      subtitles << "Level: #{params['level']}"
    end
    logs = json_response['data'] || json_response['logs']
    print_h1 title, subtitles, options
    if logs.empty?
      print "#{cyan}No logs found.#{reset}\n"
    else
      print format_log_records(logs, options)
      print_results_pagination({'meta'=>{'total'=>(json_response['total']['value'] rescue json_response['total']),'size'=>logs.size,'max'=>(json_response['max'] || options[:max]),'offset'=>(json_response['offset'] || options[:offset] || 0)}})
    end
    print reset,"\n"
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
no_colors(str) click to toggle source
# File lib/morpheus/formatters.rb, line 357
def no_colors(str)
  str.to_s.gsub(/\e\[\d+m/, "")
end
ored_list(items, limit=nil) click to toggle source
# File lib/morpheus/formatters.rb, line 469
def ored_list(items, limit=nil)
  format_list(items, "or", limit)
end
parse_time(dt, format=nil) click to toggle source

returns an instance of Time

# File lib/morpheus/formatters.rb, line 11
def parse_time(dt, format=nil)
  if dt.nil? || dt == '' || dt.to_i == 0
    return nil
  elsif dt.is_a?(Time)
    return dt
  elsif dt.is_a?(String)
    result = nil
    err = nil
    
    if !result
      format ||= DEFAULT_TIME_FORMAT
      if format
        begin
          result = Time.strptime(dt, format)
        rescue => e
          err = e
        end
      end
    end
    if !result
      begin
        result = Time.strptime(dt, ALTERNATE_TIME_FORMAT)
      rescue => e
        # err = e
      end
    end
    if !result
      begin
        result = Time.strptime(dt, DEFAULT_DATE_FORMAT)
      rescue => e
        # err = e
      end
    end
    if !result
      begin
        result = Time.parse(dt)
      rescue => e
        err = e
      end
    end
    if result
      return result
    else
      raise "unable to parse time '#{dt}'. #{err}"
    end
    
  elsif dt.is_a?(Numeric)
    return Time.at(dt)
  else
    raise "bad argument type for parse_time() #{dt.class} #{dt.inspect}"
  end
end
prepare_apply(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 969
  def prepare_apply(args)
    params, payload, options = {}, {}, {}
    optparse = Morpheus::Cli::OptionParser.new do |opts|
      opts.banner = subcommand_usage("[app] [options]")
      build_standard_update_options(opts, options, [:auto_confirm])
      opts.footer = <<-EOT
Prepare to apply an app.
[app] is required. This is the name or id of an app.
Displays the current configuration data used by the apply command.
This is only supported by certain types of apps such as terraform.
EOT
    end
    optparse.parse!(args)
    if args.count != 1
      raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(', ')}\n#{optparse}"
    end
    connect(options)

    begin
      app = find_app_by_name_or_id(args[0])
      return 1 if app.nil?
      # construct request
      params.merge!(parse_query_options(options))
      payload = {}
      if options[:payload]
        payload = options[:payload]
        payload.deep_merge!(parse_passed_options(options))
      else
        payload.deep_merge!(parse_passed_options(options))
        # raise_command_error "Specify at least one option to update.\n#{optparse}" if payload.empty?
      end
      @apps_interface.setopts(options)
      if options[:dry_run]
        print_dry_run @apps_interface.dry.prepare_apply(app["id"], params)
        return
      end
      json_response = @apps_interface.prepare_apply(app["id"], params)
      render_result = render_with_format(json_response, options)
      return 0 if render_result
      # print_green_success "Prepared to apply app: #{app['name']}"
      print_h1 "Prepared App: #{app['name']}"
      app_config = json_response['data'] 
      # app_config = json_response if app_config.nil?
      puts as_yaml(app_config, options)
      #return get([app['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
      print "\n", reset
      return 0
    rescue RestClient::Exception => e
      print_rest_exception(e, options)
      exit 1
    end
  end
print_apps_table(apps, options={}) click to toggle source

def update_app_option_types(connected=true)

list = add_app_option_types(connected)
list = list.reject {|it| ["blueprint", "group"].include? it['fieldName'] }
list.each {|it| it['required'] = false }
list

end

print_tasks_table(tasks, opts={}) click to toggle source
process_special_task_option_typeaheads(option_types) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1371
def process_special_task_option_typeaheads(option_types)
  # massage special task typeaheads options
  # this makes us all sad
  option_types.each do |option_type|
    if option_type['type'] == 'typeahead'
      if ['operationalWorkflowName','ifOperationalWorkflowName','elseOperationalWorkflowName','containerScript','containerTemplate'].include?(option_type['code'])
        option_type.deep_merge!({'config' => {'valueField' => 'name'}})
      end
    elsif option_type['type'] == 'hidden'
      if ['operationalWorkflowId','ifOperationalWorkflowId','elseOperationalWorkflowId','containerScriptId','containerTemplateId'].include?(option_type['code'])
        option_type['processValue'] = lambda {|val| 
          if val.to_s.empty?
            selected_option = Morpheus::Cli::OptionTypes.get_last_select()
            selected_option ? selected_option['value'] : nil
          end
        }
      end
    end
  end
end
refresh(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 921
  def refresh(args)
    params, payload, options = {}, {}, {}
    optparse = Morpheus::Cli::OptionParser.new do |opts|
      opts.banner = subcommand_usage("[app] [options]")
      build_standard_update_options(opts, options, [:auto_confirm])
      opts.footer = <<-EOT
Refresh an app.
[app] is required. This is the name or id of an app.
This is only supported by certain types of apps.
EOT
    end
    optparse.parse!(args)
    verify_args!(args:args, optparse:optparse, count:1)
    connect(options)

    begin
      app = find_app_by_name_or_id(args[0])
      return 1 if app.nil?
      # construct request
      params.merge!(parse_query_options(options))
      payload = {}
      if options[:payload]
        payload = options[:payload]
        payload.deep_merge!(parse_passed_options(options))
      else
        payload.deep_merge!(parse_passed_options(options))
        # raise_command_error "Specify at least one option to update.\n#{optparse}" if payload.empty?
      end
      unless options[:yes] || Morpheus::Cli::OptionTypes.confirm("Are you sure you want to refresh this app: #{app['name']}?")
        return 9, "aborted command"
      end
      @apps_interface.setopts(options)
      if options[:dry_run]
        print_dry_run @apps_interface.dry.refresh(app["id"], params, payload)
        return
      end
      json_response = @apps_interface.refresh(app["id"], params, payload)
      render_response(json_response, options) do
        print_green_success "Refreshing app #{app['name']}"
        # return _get(app['id'], options)
      end
      return 0, nil
    rescue RestClient::Exception => e
      print_rest_exception(e, options)
      exit 1
    end
  end
remove(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1369
def remove(args)
  options = {}
  query_params = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    #JD: UI defaults to on, but perhaps better to be explicate for now.
    opts.on('--remove-instances [on|off]', ['on','off'], "Remove instances. Default is off.") do |val|
      query_params[:removeInstances] = val.nil? ? 'on' : val
    end
    opts.on('--preserve-volumes [on|off]', ['on','off'], "Preserve Volumes. Default is off. Applies to certain types only.") do |val|
      query_params[:preserveVolumes] = val.nil? ? 'on' : val
    end
    opts.on( '--keep-backups', '--keep-backups', "Preserve copy of backups" ) do
      query_params[:keepBackups] = 'on'
    end
    opts.on('--release-ips [on|off]', ['on','off'], "Release Floating IPs. Default is on. Applies to certain types only. Only applies when used with --remove-instances") do |val|
      query_params[:releaseFloatingIps] = val.nil? ? 'on' : val
      query_params[:releaseEIPs] = query_params[:releaseFloatingIps] # old parameter before 6.0
    end
    opts.on('--releaseEIPs [on|off]', ['on','off'], "Alias for Release Floating IPs") do |val|
      query_params[:releaseFloatingIps] = val.nil? ? 'on' : val
      query_params[:releaseEIPs] = query_params[:releaseFloatingIps] # old parameter before 6.0
    end
    opts.add_hidden_option('--releaseEIPs')
    opts.on( '-f', '--force', "Force Delete" ) do
      query_params[:force] = 'on'
    end
    build_common_options(opts, options, [:json, :dry_run, :quiet, :auto_confirm])
    opts.footer = "Delete an app.\n" +
                  "[app] is required. This is the name or id of an app."
  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}\n#{optparse}"
    return 1
  end
  connect(options)

  begin
    app = find_app_by_name_or_id(args[0])
    unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to remove the app '#{app['name']}'?", options)
      return 9
    end
    # JD: removeVolumes to maintain the old behavior with pre-3.5.2 appliances, remove me later
    if query_params[:preserveVolumes].nil?
      query_params[:removeVolumes] = 'on'
    end
    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.destroy(app['id'], query_params)
      return
    end
    json_response = @apps_interface.destroy(app['id'], query_params)
    if options[:json]
      print JSON.pretty_generate(json_response)
      print "\n"
    elsif !options[:quiet]
      print_green_success "Removed app #{app['name']}"
      #list([])
    end
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
remove_instance(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1468
def remove_instance(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app] [instance]")
    build_common_options(opts, options, [:options, :json, :dry_run])
    opts.footer = "Remove an instance from an app.\n" +
                  "[app] is required. This is the name or id of an app." + "\n" +
                  "[instance] is required. This is the name or id of an instance."
  end
  optparse.parse!(args)
  if args.count < 1 || args.count > 2
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} remove-instance expects 1-2 arguments and received #{args.count}: #{args}\n#{optparse}"
    return 1
  end
  # optional [tier] and [instance] arguments
  if args[1] && args[1] !~ /\A\-/
    options[:instance_name] = args[1]
  end
  connect(options)
  begin
    app = find_app_by_name_or_id(args[0])

    payload = {}

    if options[:instance_name]
      instance = find_instance_by_name_or_id(options[:instance_name])
    else
      v_prompt = Morpheus::Cli::OptionTypes.prompt([{'fieldName' => 'instance', 'fieldLabel' => 'Instance', 'type' => 'text', 'required' => true, 'description' => 'Enter the instance name or id'}], options[:options])
      instance = find_instance_by_name_or_id(v_prompt['instance'])
    end
    payload[:instanceId] = instance['id']
    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.remove_instance(app['id'], payload)
      return
    end

    json_response = @apps_interface.remove_instance(app['id'], payload)

    if options[:json]
      print JSON.pretty_generate(json_response)
      print "\n"
    else
      print_green_success "Removed instance #{instance['name']} from app #{app['name']}"
      #list([])
      # details_options = [app['name']]
      # details(details_options)
    end

  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
restart(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1751
def restart(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
    opts.footer = "Restart an app.\n" +
                  "[app] is required. This is the name or id of an app. Supports 1-N [app] arguments."
  end
  optparse.parse!(args)
  if args.count < 1
    puts_error "[id] argument is required"
    puts_error optparse
    return 1
  end
  connect(options)
  id_list = parse_id_list(args)
  unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to restart #{id_list.size == 1 ? 'app' : 'apps'} #{anded_list(id_list)}?", options)
    return 9, "aborted command"
  end
  return run_command_for_each_arg(id_list) do |arg|
    _restart(arg, options)
  end
end
security_groups(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1853
def security_groups(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    build_common_options(opts, options, [:json, :dry_run])
  end
  optparse.parse!(args)
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} security-groups expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
    return 1
  end
  connect(options)

  begin
    app = find_app_by_name_or_id(args[0])
    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.security_groups(app['id'])
      return
    end
    json_response = @apps_interface.security_groups(app['id'])
    securityGroups = json_response['securityGroups']
    print_h1 "Morpheus Security Groups for App: #{app['name']}", options
    print cyan
    print_description_list({"Firewall Enabled" => lambda {|it| format_boolean it['firewallEnabled'] } }, json_response)
    if securityGroups.empty?
      print cyan,"\n","No security groups currently applied.",reset,"\n"
    else
      print "\n"
      securityGroups.each do |securityGroup|
        print cyan, "=  #{securityGroup['id']} (#{securityGroup['name']}) - (#{securityGroup['description']})\n"
      end
    end
    print reset,"\n"

  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
start(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1707
def start(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
    opts.footer = "Start an app.\n" +
                  "[app] is required. This is the name or id of an app. Supports 1-N [app] arguments."
  end
  optparse.parse!(args)
  if args.count < 1
    puts_error "[id] argument is required"
    puts_error optparse
    return 1
  end
  connect(options)
  id_list = parse_id_list(args)
  unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to start #{id_list.size == 1 ? 'app' : 'apps'} #{anded_list(id_list)}?", options)
    return 9, "aborted command"
  end
  return run_command_for_each_arg(id_list) do |arg|
    _start(arg, options)
  end
end
state(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1174
  def state(args)
    params, payload, options = {}, {}, {}
    optparse = Morpheus::Cli::OptionParser.new do |opts|
      opts.banner = subcommand_usage("[app] [options]")
      opts.on('--data', "Display State Data") do
        options[:include_state_data] = true
      end
      opts.on('--specs', "Display Spec Templates") do
        options[:include_spec_templates] = true
      end
      opts.on('--plan', "Display Plan Data") do
        options[:include_plan_data] = true
      end
      opts.on('--input', "Display Input") do
        options[:include_input] = true
      end
      opts.on('--output', "Display Output") do
        options[:include_output] = true
      end
      opts.on('-a','--all', "Display All Details") do
        options[:include_state_data] = true
        options[:include_spec_templates] = true
        options[:include_plan_data] = true
        options[:include_input] = true
        options[:include_output] = true
        options[:details] = true
      end
      build_standard_get_options(opts, options)
      opts.footer = <<-EOT
View state of an app.
[app] is required. This is the name or id of an app.
This is only supported by certain types of apps such as terraform.
EOT
    end
    optparse.parse!(args)
    verify_args!(args:args, optparse:optparse, count:1)
    connect(options)
    app = find_app_by_name_or_id(args[0])
    return 1 if app.nil?
    # construct request
    params.merge!(parse_query_options(options))
    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.state(app["id"], params)
      return
    end
    json_response = @apps_interface.state(app["id"], params)
    render_result = render_with_format(json_response, options)
    return 0 if render_result
    print_h1 "App State: #{app['name']}", options
    # print_h2 "Workloads", options
    if json_response['workloads'] && !json_response['workloads'].empty?
      workload_columns = {
        "Name" => lambda {|it| it['subRefName'].to_s.empty? ? "#{it['refName']}" : "#{it['refName']} - #{it['subRefName']}" },
        "Last Check" => lambda {|it| format_local_dt(it['stateDate']) },
        "Status" => lambda {|it| format_ok_status(it['status'] || 'ok') },
        "Drift Status" => lambda {|it| it['iacDrift'] ? "Drift" : "No Drift" }
      }
      print as_pretty_table(json_response['workloads'], workload_columns.upcase_keys!, options)
    else
      print cyan,"No workloads found.",reset,"\n"
    end
    if options[:include_state_data]
      print_h2 "State Data", options
      puts json_response['stateData']
    end
    if options[:include_spec_templates]
      print_h2 "Spec Templates", options
      spec_templates_columns = {
        "Resource Spec" => lambda {|it| it['name'] || (it['template'] ? it['template']['name'] : nil) },
        "Attached to Source Template" => lambda {|it| format_boolean(!it['isolated']) },
        "Source Spec Template" => lambda {|it| (it['template'] ? it['template']['name'] : nil) || it['name'] }
      }
      print as_pretty_table(json_response['specs'], spec_templates_columns.upcase_keys!, options)
      # print "\n", reset
    end
    if options[:include_plan_data]
      # print_h2 "Plan Data", options
      if app['type'] == 'terraform'
        print_h2 "Terraform Plan", options
      else
        print_h2 "Plan Data", options
      end
      puts json_response['planData']
      # print "\n", reset
    end
    if options[:include_input]
      # print_h2 "Input", options
      if json_response['input'] && json_response['input']['variables']
        print_h2 "VARIABLES", options
        input_variable_columns = {
          "Name" => lambda {|it| it['name'] },
          "Value" => lambda {|it| it['value'] }
        }
        print as_pretty_table(json_response['input']['variables'], input_variable_columns.upcase_keys!, options)
      end
      if json_response['input'] && json_response['input']['providers']
        print_h2 "PROVIDERS", options
        input_provider_columns = {
          "Name" => lambda {|it| it['name'] }
        }
        print as_pretty_table(json_response['input']['providers'], input_provider_columns.upcase_keys!, options)
      end
      if json_response['input'] && json_response['input']['data']
        print_h2 "DATA", options
        input_data_columns = {
          "Type" => lambda {|it| it['type'] },
          "Key" => lambda {|it| it['key'] },
          "Name" => lambda {|it| it['name'] }
        }
        print as_pretty_table(json_response['input']['data'], input_data_columns.upcase_keys!, options)
      end
      # print "\n", reset
    end
    if options[:include_output]
      # print_h2 "Output", options
      if json_response['output'] && json_response['output']['outputs']
        print_h2 "OUTPUTS", options
        input_variable_columns = {
          "Name" => lambda {|it| it['name'] },
          "Value" => lambda {|it| it['value'] }
        }
        print as_pretty_table(json_response['output']['outputs'], input_variable_columns.upcase_keys!, options)
      end
      # print "\n", reset
    end
    print "\n", reset
    return 0
  end
stop(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 1663
def stop(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    build_common_options(opts, options, [:auto_confirm, :json, :dry_run, :quiet, :remote])
    opts.footer = "Stop an app.\n" +
                  "[app] is required. This is the name or id of an app. Supports 1-N [app] arguments."
  end
  optparse.parse!(args)
  if args.count < 1
    puts_error "[id] argument is required"
    puts_error optparse
    return 1
  end
  connect(options)
  id_list = parse_id_list(args)
  unless options[:yes] || ::Morpheus::Cli::OptionTypes::confirm("Are you sure you would like to stop #{id_list.size == 1 ? 'app' : 'apps'} #{anded_list(id_list)}?", options)
    return 9, "aborted command"
  end
  return run_command_for_each_arg(id_list) do |arg|
    _stop(arg, options)
  end
end
update(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 823
def update(args)
  params, payload, options = {}, {}, {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app] [options]")
    #build_option_type_options(opts, options, update_app_option_types(false))
    opts.on( '-g', '--group GROUP', "Group Name or ID" ) do |val|
      options[:group] = val
    end
    opts.on( '--name VALUE', String, "Name" ) do |val|
      options[:name] = val
    end
    opts.on('-l', '--labels [LIST]', String, "Labels") do |val|
      options[:options]['labels'] = parse_labels(val)
    end
    opts.on( '--description VALUE', String, "Description" ) do |val|
      options[:description] = val
    end
    opts.on( '--environment VALUE', String, "Environment" ) do |val|
      options[:environment] = val
    end
    opts.on( '--owner USER', "Owner Username or ID" ) do |val|
      options[:owner] = val == 'null' ? nil : val
    end
    build_common_options(opts, options, [:options, :payload, :json, :dry_run])
    opts.footer = "Update an app.\n" +
                  "[app] is required. This is the name or id of an app."
  end
  optparse.parse!(args)
  if args.count != 1
    print_error Morpheus::Terminal.angry_prompt
    puts_error  "#{command_name} update expects 1 argument and received #{args.count}: #{args}\n#{optparse}"
    return 1
  end
  connect(options)

  begin
    app = find_app_by_name_or_id(args[0])
    return 1 if app.nil?
    if options[:payload]
      payload = options[:payload]
    end
    payload['app'] ||= {}
    payload.deep_merge!({'app' => parse_passed_options(options)})
    if options[:name]
      payload['app']['name'] = options[:name]
    end
    if options[:description]
      payload['app']['description'] = options[:description]
    end
    if options[:environment]
      # payload['app']['environment'] = options[:environment]
      payload['app']['appContext'] = options[:environment]
    end
    if options[:group]
      group = find_group_by_name_or_id_for_provisioning(options[:group])
      return 1 if group.nil?
      payload['app']['group'] = {'id' => group['id'], 'name' => group['name']}
    end
    if options.key?(:owner)
      owner_id = options[:owner]
      if owner_id.to_s.empty?
        # allow clearing
        owner_id = nil
      elsif options[:owner]
        if owner_id.to_s =~ /\A\d{1,}\Z/
          # allow id without lookup
        else
          user = find_available_user_option(owner_id)
          return 1 if user.nil?
          owner_id = user['id']
        end
      end
      payload['app']['ownerId'] = owner_id
    end
    if payload['app'] && payload['app'].empty?
      payload.delete('app')
    end
    if payload.empty?
      raise_command_error "Specify at least one option to update.\n#{optparse}" if payload.empty?
    end
    
    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.update(app["id"], payload)
      return
    end
    json_response = @apps_interface.update(app["id"], payload)
    render_result = render_with_format(json_response, options)
    return 0 if render_result
    print_green_success "Updated app #{app['name']}"
    get([app['id']] + (options[:remote] ? ["-r",options[:remote]] : []))
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
update_task_option_types(task_type) click to toggle source
# File lib/morpheus/cli/commands/tasks.rb, line 1272
def update_task_option_types(task_type)
  [
    {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => true, 'displayOrder' => 0}
  ] + task_type['optionTypes']
end
update_wiki(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2261
def update_wiki(args)
  options = {}
  params = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app] [options]")
    build_option_type_options(opts, options, update_wiki_page_option_types)
    opts.on('--file FILE', "File containing the wiki content. This can be used instead of --content") do |filename|
      full_filename = File.expand_path(filename)
      if File.exist?(full_filename)
        params['content'] = File.read(full_filename)
      else
        print_red_alert "File not found: #{full_filename}"
        return 1
      end
      # use the filename as the name by default.
      if !params['name']
        params['name'] = File.basename(full_filename)
      end
    end
    opts.on(nil, '--clear', "Clear current page content") do |val|
      params['content'] = ""
    end
    build_common_options(opts, options, [:options, :payload, :json, :dry_run, :remote])
  end
  optparse.parse!(args)
  if args.count != 1
    puts_error  "#{Morpheus::Terminal.angry_prompt}wrong number of arguments. Expected 1 and received #{args.count} #{args.inspect}\n#{optparse}"
    return 1
  end
  connect(options)

  begin
    app = find_app_by_name_or_id(args[0])
    return 1 if app.nil?
    # construct payload
    passed_options = options[:options] ? options[:options].reject {|k,v| k.is_a?(Symbol) } : {}
    payload = nil
    if options[:payload]
      payload = options[:payload]
      payload.deep_merge!({'page' => passed_options}) unless passed_options.empty?
    else
      payload = {
        'page' => {
        }
      }
      # allow arbitrary -O options
      payload.deep_merge!({'page' => passed_options}) unless passed_options.empty?
      # prompt for options
      #params = Morpheus::Cli::OptionTypes.prompt(update_wiki_page_option_types, options[:options], @api_client, options[:params])
      #params = passed_options
      params.deep_merge!(passed_options)

      if params.empty?
        raise_command_error "Specify at least one option to update.\n#{optparse}"
      end

      payload.deep_merge!({'page' => params}) unless params.empty?
    end
    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.update_wiki(app["id"], payload)
      return
    end
    json_response = @apps_interface.update_wiki(app["id"], payload)

    if options[:json]
      puts as_json(json_response, options)
    else
      print_green_success "Updated wiki page for app #{app['name']}"
      wiki([app['id']])
    end
    return 0
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
update_wiki_page_option_types() click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2542
def update_wiki_page_option_types
  [
    {'fieldName' => 'name', 'fieldLabel' => 'Name', 'type' => 'text', 'required' => false, 'displayOrder' => 1, 'description' => 'The name of the wiki page for this instance. Default is the instance name.'},
    #{'fieldName' => 'category', 'fieldLabel' => 'Category', 'type' => 'text', 'required' => false, 'displayOrder' => 2},
    {'fieldName' => 'content', 'fieldLabel' => 'Content', 'type' => 'textarea', 'required' => false, 'displayOrder' => 3, 'description' => 'The content (markdown) of the wiki page.'}
  ]
end
view(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2106
def view(args)
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    opts.on('-w','--wiki', "Open the wiki tab for this app") do
      options[:link_tab] = "wiki"
    end
    opts.on('--tab VALUE', String, "Open a specific tab") do |val|
      options[:link_tab] = val.to_s
    end
    build_common_options(opts, options, [:dry_run, :remote])
    opts.footer = "View an app in a web browser" + "\n" +
                  "[app] is required. This is the name or id of an app. Supports 1-N [app] arguments."
  end
  optparse.parse!(args)
  if args.count < 1
    raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
  end
  connect(options)
  id_list = parse_id_list(args)
  return run_command_for_each_arg(id_list) do |arg|
    _view(arg, options)
  end
end
view_wiki(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2230
def view_wiki(args)
  params = {}
  options = {}
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[id]")
    build_common_options(opts, options, [:dry_run, :remote])
    opts.footer = "View app wiki page in a web browser" + "\n" +
                  "[app] is required. This is the name or id of an app."
  end
  optparse.parse!(args)
  if args.count != 1
    raise_command_error "wrong number of arguments, expected 1 and got (#{args.count}) #{args.join(' ')}\n#{optparse}"
  end
  connect(options)
  begin
    app = find_app_by_name_or_id(args[0])
    return 1 if app.nil?

    link = "#{@appliance_url}/login/oauth-redirect?access_token=#{@access_token}\\&redirectUri=/provisioning/apps/#{app['id']}#!wiki"

    if options[:dry_run]
    puts Morpheus::Util.open_url_command(link)
      return 0
    end
    return Morpheus::Util.open_url(link)
  rescue RestClient::Exception => e
    print_rest_exception(e, options)
    exit 1
  end
end
wiki(args) click to toggle source
# File lib/morpheus/cli/commands/apps.rb, line 2152
def wiki(args)
  options = {}
  params = {}
  open_wiki_link = false
  optparse = Morpheus::Cli::OptionParser.new do |opts|
    opts.banner = subcommand_usage("[app]")
    opts.on('--view', '--view', "View wiki page in web browser.") do
      open_wiki_link = true
    end
    build_common_options(opts, options, [:json, :dry_run, :remote])
    opts.footer = "View wiki page details for an app." + "\n" +
                  "[app] is required. This is the name or id of an app."
  end
  optparse.parse!(args)
  if args.count != 1
    puts_error  "#{Morpheus::Terminal.angry_prompt}wrong number of arguments. Expected 1 and received #{args.count} #{args.inspect}\n#{optparse}"
    return 1
  end
  connect(options)

  begin
    app = find_app_by_name_or_id(args[0])
    return 1 if app.nil?


    @apps_interface.setopts(options)
    if options[:dry_run]
      print_dry_run @apps_interface.dry.wiki(app["id"], params)
      return
    end
    json_response = @apps_interface.wiki(app["id"], params)
    page = json_response['page']

    render_result = render_with_format(json_response, options, 'page')
    return 0 if render_result

    if page

      # my_terminal.exec("wiki get #{page['id']}")

      print_h1 "App Wiki Page: #{app['name']}"
      # print_h1 "Wiki Page Details"
      print cyan

      print_description_list({
        "Page ID" => 'id',
        "Name" => 'name',
        #"Category" => 'category',
        #"Ref Type" => 'refType',
        #"Ref ID" => 'refId',
        #"Owner" => lambda {|it| it['account'] ? it['account']['name'] : '' },
        "Created" => lambda {|it| format_local_dt(it['dateCreated']) },
        "Created By" => lambda {|it| it['createdBy'] ? it['createdBy']['username'] : '' },
        "Updated" => lambda {|it| format_local_dt(it['lastUpdated']) },
        "Updated By" => lambda {|it| it['updatedBy'] ? it['updatedBy']['username'] : '' }
      }, page)
      print reset,"\n"

      print_h2 "Page Content"
      print cyan, page['content'], reset, "\n"

    else
      print "\n"
      print cyan, "No wiki page found.", reset, "\n"
    end
    print reset,"\n"

    if open_wiki_link
      return view_wiki([args[0]])
    end

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