class RHC::Commands::App

Constants

DEFAULT_DELAY_THROTTLE
MAX_RETRIES

Public Instance Methods

create(name, cartridges) click to toggle source
# File lib/rhc/commands/app.rb, line 64
def create(name, cartridges)
  check_config!

  check_name!(name)

  arg_envs, cartridges = cartridges.partition{|item| item.match(env_var_regex_pattern)}

  cartridges = check_cartridges(cartridges, &require_one_web_cart)

  options.default          :dns => true,
    :git => true

  raise ArgumentError, "You have named both your main application and your Jenkins application '#{name}'. In order to continue you'll need to specify a different name with --enable-jenkins or choose a different application name." if jenkins_app_name == name && enable_jenkins?

  rest_domain = check_domain!
  rest_app = nil
  repo_dir = nil

  cart_names = cartridges.collect do |c|
    c.usage_rate? ? "#{c.short_name} (addtl. costs may apply)" : c.short_name
  end.join(', ')

  if rest_domain.supports_add_application_with_env_vars?
    environment_variables = collect_env_vars(arg_envs.concat(Array(options.env)))
  else
    warn "Server does not support environment variables."
  end

  paragraph do
    header "Application Options"
    table([["Namespace:", options.namespace],
           ["Cartridges:", cart_names],
          (["Source Code:", options.from_code] if options.from_code),
           ["Gear Size:", options.gear_size || "default"],
           ["Scaling:", options.scaling ? "yes" : "no"],
          (["Environment Variables:", environment_variables.map{|item| "#{item.name}=#{item.value}"}.join(', ')] if environment_variables.present?),
          ].compact
         ).each { |s| say "  #{s}" }
  end

  paragraph do
    say "Creating application '#{name}' ... "

    # create the main app
    rest_app = create_app(name, cartridges, rest_domain, options.gear_size, options.scaling, options.from_code, environment_variables)
    success "done"

    paragraph{ indent{ success rest_app.messages.map(&:strip) } }
  end

  build_app_exists = rest_app.building_app

  if enable_jenkins?

    unless build_app_exists
      paragraph do
        say "Setting up a Jenkins application ... "

        begin
          build_app_exists = add_jenkins_app(rest_domain)

          success "done"
          paragraph{ indent{ success build_app_exists.messages.map(&:strip) } }

        rescue Exception => e
          warn "not complete"
          add_issue("Jenkins failed to install - #{e}",
                    "Installing jenkins and jenkins-client",
                    "rhc create-app jenkins",
                    "rhc add-cartridge jenkins-client -a #{rest_app.name}")
        end
      end
    end

    paragraph do
      messages = []
      add_jenkins_client_to(rest_app, messages)
      paragraph{ indent{ success messages.map(&:strip) } }
    end if build_app_exists
  end

  debug "Checking SSH keys through the wizard"
  check_sshkeys! unless options.no_keys

  if options.dns
    paragraph do
      say "Waiting for your DNS name to be available ... "
      if dns_propagated? rest_app.host
        success "done"
      else
        warn "failure"
        add_issue("We were unable to lookup your hostname (#{rest_app.host}) in a reasonable amount of time and can not clone your application.",
                  "Clone your git repo",
                  "rhc git-clone #{rest_app.name}")

        output_issues(rest_app)
        return 0
      end
    end

    if options.git
      section(:now => true, :top => 1, :bottom => 1) do
        begin
          repo_dir = git_clone_application(rest_app)
        rescue RHC::GitException => e
          warn "#{e}"
          unless RHC::Helpers.windows? and windows_nslookup_bug?(rest_app)
            add_issue("We were unable to clone your application's git repo - #{e}",
                      "Clone your git repo",
                      "rhc git-clone #{rest_app.name}")
          end
        end
      end
    end
  end

  output_issues(rest_app) if issues?

  paragraph do
    say "Your application '#{rest_app.name}' is now available."
    paragraph do
      indent do
        say table [
            ['URL:', rest_app.app_url],
            ['SSH to:', rest_app.ssh_string],
            ['Git remote:', rest_app.git_url],
            (['Cloned to:', repo_dir] if repo_dir)
          ].compact
      end
    end
  end
  paragraph{ say "Run 'rhc show-app #{name}' for more details about your app." }

  0
end
delete(app) click to toggle source
# File lib/rhc/commands/app.rb, line 211
def delete(app)
  rest_app = rest_client.find_application(options.namespace, app)

  confirm_action "#{color("This is a non-reversible action! Your application code and data will be permanently deleted if you continue!", :yellow)}\n\nAre you sure you want to delete the application '#{app}'?"

  say "Deleting application '#{rest_app.name}' ... "
  rest_app.destroy
  success "deleted"

  paragraph{ rest_app.messages.each{ |s| success s } }

  0
end
force_stop(app) click to toggle source
# File lib/rhc/commands/app.rb, line 251
def force_stop(app)
  app_action app, :stop, true

  results { say "#{app} force stopped" }
  0
end
reload(app) click to toggle source
# File lib/rhc/commands/app.rb, line 273
def reload(app)
  app_action app, :reload

  results { say "#{app} config reloaded" }
  0
end
restart(app) click to toggle source
# File lib/rhc/commands/app.rb, line 262
def restart(app)
  app_action app, :restart

  results { say "#{app} restarted" }
  0
end
show(app_name) click to toggle source
# File lib/rhc/commands/app.rb, line 316
def show(app_name)

  if options.state
    gear_groups_for_app(app_name).each do |gg|
      say "Cartridge #{gg.cartridges.collect { |c| c['name'] }.join(', ')} is #{gear_group_state(gg.gears.map{ |g| g['state'] })}"
    end

  elsif options.gears && options.gears != true
    groups = rest_client.find_application_gear_groups(options.namespace, app_name)

    case options.gears
    when 'quota'
      opts = {:as => :gear, :split_cells_on => /\s*\t/, :header => ['Gear', 'Cartridges', 'Used', 'Limit'], :align => [nil, nil, :right, :right]}
      table_from_gears('echo "$(du --block-size=1 -s 2>/dev/null | cut -f 1)"', groups, opts) do |gear, data, group|
        [gear['id'], group.cartridges.collect{ |c| c['name'] }.join(' '), (human_size(data.chomp) rescue 'error'), human_size(group.quota)]
      end
    when 'ssh'
      groups.each{ |group| group.gears.each{ |g| say (ssh_string(g['ssh_url']) or raise NoPerGearOperations) } }
    else
      run_on_gears(ssh_command_for_op(options.gears), groups)
    end

  elsif options.gears
    gear_info = gear_groups_for_app(app_name).map do |group|
      group.gears.map do |gear|
        [
          gear['id'],
          gear['state'] == 'started' ? gear['state'] : color(gear['state'], :yellow),
          group.cartridges.collect{ |c| c['name'] }.join(' '),
          group.gear_profile,
          ssh_string(gear['ssh_url'])
        ]
      end
    end.flatten(1)

    say table(gear_info, :header => ['ID', 'State', 'Cartridges', 'Size', 'SSH URL'])
  else
    app = rest_client.find_application(options.namespace, app_name, :include => :cartridges)
    display_app(app, app.cartridges)
  end

  0
end
start(app) click to toggle source
# File lib/rhc/commands/app.rb, line 229
def start(app)
  app_action app, :start

  results { say "#{app} started" }
  0
end
status(app) click to toggle source
# File lib/rhc/commands/app.rb, line 365
def status(app)
  # TODO: add a way to deprecate this and alias to show --apache
  options.state = true
  show(app)
end
stop(app) click to toggle source
# File lib/rhc/commands/app.rb, line 240
def stop(app)
  app_action app, :stop

  results { say "#{app} stopped" }
  0
end
tidy(app) click to toggle source
# File lib/rhc/commands/app.rb, line 284
def tidy(app)
  app_action app, :tidy

  results { say "#{app} cleaned up" }
  0
end

Private Instance Methods

add_issue(reason, commands_header, *commands) click to toggle source

Issues collector collects a set of recoverable issues and steps to fix them for output at the end of a complex command

# File lib/rhc/commands/app.rb, line 624
def add_issue(reason, commands_header, *commands)
  @issues ||= []
  issue = {:reason => reason,
           :commands_header => commands_header,
           :commands => commands}
  @issues << issue
end
add_jenkins_app(rest_domain) click to toggle source
# File lib/rhc/commands/app.rb, line 466
def add_jenkins_app(rest_domain)
  create_app(jenkins_app_name, jenkins_cartridge_name, rest_domain)
end
add_jenkins_cartridge(rest_app) click to toggle source
# File lib/rhc/commands/app.rb, line 470
def add_jenkins_cartridge(rest_app)
  rest_app.add_cartridge(jenkins_client_cartridge_name)
end
add_jenkins_client_to(rest_app, messages) click to toggle source
# File lib/rhc/commands/app.rb, line 474
def add_jenkins_client_to(rest_app, messages)
  say "Setting up Jenkins build ... "
  successful, attempts, exit_code, exit_message = false, 1, 157, nil
  while (!successful && exit_code == 157 && attempts < MAX_RETRIES)
    begin
      cartridge = add_jenkins_cartridge(rest_app)
      successful = true

      success "done"
      messages.concat(cartridge.messages)

    rescue RHC::Rest::ServerErrorException => e
      if (e.code == 157)
        # error downloading Jenkins /jnlpJars/jenkins-cli.jar
        attempts += 1
        debug "Jenkins server could not be contacted, sleep and then retry: attempt #{attempts}\n    #{e.message}"
        Kernel.sleep(10)
      end
      exit_code = e.code
      exit_message = e.message
    rescue Exception => e
      # timeout and other exceptions
      exit_code = 1
      exit_message = e.message
    end
  end
  unless successful
    warn "not complete"
    add_issue("Jenkins client failed to install - #{exit_message}",
              "Install the jenkins client",
              "rhc add-cartridge jenkins-client -a #{rest_app.name}")
  end
end
app_action(app, action, *args) click to toggle source
# File lib/rhc/commands/app.rb, line 440
def app_action(app, action, *args)
  rest_app = rest_client.find_application(options.namespace, app)
  result = rest_app.send action, *args
  result
end
check_config!() click to toggle source
# File lib/rhc/commands/app.rb, line 410
def check_config!
  return if not interactive? or (!options.clean && config.has_local_config?) or (options.server && (options.rhlogin || options.token))
  RHC::EmbeddedWizard.new(config, options).run
end
check_domain!() click to toggle source
# File lib/rhc/commands/app.rb, line 415
def check_domain!
  if options.namespace
    rest_client.find_domain(options.namespace)
  else
    if rest_client.domains.empty?
      raise RHC::Rest::DomainNotFoundException, "No domains found. Please create a domain with 'rhc create-domain <namespace>' before creating applications." unless interactive?
      RHC::DomainWizard.new(config, options, rest_client).run
    end
    domain = rest_client.domains.first
    raise RHC::Rest::DomainNotFoundException, "No domains found. Please create a domain with 'rhc create-domain <namespace>' before creating applications." unless domain
    options.namespace = domain.id
    domain
  end
end
check_name!(name) click to toggle source
# File lib/rhc/commands/app.rb, line 401
def check_name!(name)
  return unless name.blank?

  paragraph{ say "When creating an application, you must provide a name and a cartridge from the list below:" }
  paragraph{ list_cartridges(standalone_cartridges) }

  raise ArgumentError, "Please specify the name of the application and the web cartridge to install"
end
check_sshkeys!() click to toggle source
# File lib/rhc/commands/app.rb, line 396
def check_sshkeys!
  return unless interactive?
  RHC::SSHWizard.new(rest_client, config, options).run
end
create_app(name, cartridges, rest_domain, gear_size=nil, scale=nil, from_code=nil, environment_variables=nil) click to toggle source
# File lib/rhc/commands/app.rb, line 446
def create_app(name, cartridges, rest_domain, gear_size=nil, scale=nil, from_code=nil, environment_variables=nil)
  app_options = {:cartridges => Array(cartridges)}
  app_options[:gear_profile] = gear_size if gear_size
  app_options[:scale] = scale if scale
  app_options[:initial_git_url] = from_code if from_code
  app_options[:debug] = true if @debug
  app_options[:environment_variables] = environment_variables.map{ |item| item.to_hash } if environment_variables.present?
  debug "Creating application '#{name}' with these options - #{app_options.inspect}"
  rest_app = rest_domain.add_application(name, app_options)
  debug "'#{rest_app.name}' created"

  rest_app
rescue RHC::Rest::Exception => e
  if e.code == 109
    paragraph{ say "Valid cartridge types:" }
    paragraph{ list_cartridges(standalone_cartridges) }
  end
  raise
end
dns_propagated?(host, sleep_time=2) click to toggle source
# File lib/rhc/commands/app.rb, line 508
def dns_propagated?(host, sleep_time=2)
  #
  # Confirm that the host exists in DNS
  #
  debug "Start checking for application dns @ '#{host}'"

  found = false

  # Allow DNS to propagate
  Kernel.sleep 5

  # Now start checking for DNS
  host_found = hosts_file_contains?(host) or
  1.upto(MAX_RETRIES) { |i|
    host_found = host_exists?(host)
    break found if host_found

    say "    retry # #{i} - Waiting for DNS: #{host}"
    Kernel.sleep sleep_time.to_i
    sleep_time *= DEFAULT_DELAY_THROTTLE
  }

  debug "End checking for application dns @ '#{host} - found=#{found}'"

  host_found
end
enable_jenkins?() click to toggle source
# File lib/rhc/commands/app.rb, line 535
def enable_jenkins?
  # legacy issue, commander 4.0.x will place the option in the hash with nil value (BZ878407)
  options.__hash__.has_key?(:enable_jenkins)
end
format_issues(indent) click to toggle source
# File lib/rhc/commands/app.rb, line 632
def format_issues(indent)
  return nil unless issues?

  indentation = " " * indent
  reasons = ""
  steps = ""

  @issues.each_with_index do |issue, i|
    reasons << "#{indentation}#{i+1}. #{issue[:reason].strip}\n"
    steps << "#{indentation}#{i+1}. #{issue[:commands_header].strip}\n"
    issue[:commands].each { |cmd| steps << "#{indentation}  $ #{cmd}\n" }
  end

  [reasons, steps]
end
gear_group_state(states) click to toggle source
# File lib/rhc/commands/app.rb, line 435
def gear_group_state(states)
  return states[0] if states.length == 1 || states.uniq.length == 1
  "#{states.select{ |s| s == 'started' }.count}/#{states.length} started"
end
gear_groups_for_app(app_name) click to toggle source
# File lib/rhc/commands/app.rb, line 431
def gear_groups_for_app(app_name)
  rest_client.find_application_gear_groups(options.namespace, app_name)
end
issues?() click to toggle source
# File lib/rhc/commands/app.rb, line 648
def issues?
  not @issues.nil?
end
jenkins_app_name() click to toggle source
# File lib/rhc/commands/app.rb, line 540
def jenkins_app_name
  if options.enable_jenkins.is_a? String
    options.enable_jenkins
  end || "jenkins"
end
jenkins_cartridge_name() click to toggle source
# File lib/rhc/commands/app.rb, line 546
def jenkins_cartridge_name
  jenkins_cartridges.last.name
end
jenkins_client_cartridge_name() click to toggle source
# File lib/rhc/commands/app.rb, line 550
def jenkins_client_cartridge_name
  jenkins_client_cartridges.last.name
end
output_issues(rest_app) click to toggle source
# File lib/rhc/commands/app.rb, line 593
      def output_issues(rest_app)
        reasons, steps = format_issues(4)
        warn <<WARNING_OUTPUT
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
WARNING:  Your application was created successfully but had problems during
          configuration. Below is a list of the issues and steps you can
          take to complete the configuration of your application.

  Application URL: #{rest_app.app_url}

  Issues:
#{reasons}
  Steps to complete your configuration:
#{steps}
  If you continue to experience problems after completing these steps,
  you can try destroying and recreating the application:

    $ rhc app delete #{rest_app.name} --confirm

  Please contact us if you are unable to successfully create your
  application:

    Support - https://www.openshift.com/support

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

WARNING_OUTPUT
      end
require_one_web_cart() click to toggle source
# File lib/rhc/commands/app.rb, line 379
def require_one_web_cart
  lambda{ |carts|
    match, ambiguous = carts.partition{ |c| not c.is_a?(Array) }
    selected_web = match.any?{ |c| not c.only_in_existing? }
    possible_web = ambiguous.flatten.any?{ |c| not c.only_in_existing? }
    if not (selected_web or possible_web)
      section(:bottom => 1){ list_cartridges(standalone_cartridges) }
      raise RHC::CartridgeNotFoundException, "Every application needs a web cartridge to handle incoming web requests. Please provide the short name of one of the carts listed above."
    end
    if selected_web
      carts.map! &other_carts_only
    elsif possible_web && ambiguous.length == 1
      carts.map! &web_carts_only
    end
  }
end
run_nslookup(host) click to toggle source
# File lib/rhc/commands/app.rb, line 554
def run_nslookup(host)
  # :nocov:
  %xnslookup #{host}`
  $?.exitstatus == 0
  # :nocov:
end
run_ping(host) click to toggle source
# File lib/rhc/commands/app.rb, line 561
def run_ping(host)
  # :nocov:
  %xping #{host} -n 2`
  $?.exitstatus == 0
  # :nocov:
end
windows_nslookup_bug?(rest_app) click to toggle source
# File lib/rhc/commands/app.rb, line 568
      def windows_nslookup_bug?(rest_app)
        windows_nslookup = run_nslookup(rest_app.host)
        windows_ping = run_ping(rest_app.host)

        if windows_nslookup and !windows_ping # this is related to BZ #826769
          issue = <<WINSOCKISSUE
We were unable to lookup your hostname (#{rest_app.host})
in a reasonable amount of time.  This can happen periodically and may
take up to 10 extra minutes to propagate depending on where you are in the
world. This may also be related to an issue with Winsock on Windows [1][2].
We recommend you wait a few minutes then clone your git repository manually.

[1] http://support.microsoft.com/kb/299357
[2] http://support.microsoft.com/kb/811259
WINSOCKISSUE
          add_issue(issue,
                    "Clone your git repo",
                    "rhc git-clone #{rest_app.name}")

          return true
        end

        false
      end