class RHC::Commands::App

Public Instance Methods

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

  check_name!(name)

  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

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

  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"],
          ].compact
         ).each { |s| say "  #{s}" }
  end

  messages = []

  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)
    messages.concat(rest_app.messages)
    success "done"
  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"
          messages.concat(build_app_exists.messages)

        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
      add_jenkins_client_to(rest_app, messages)
    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
      paragraph do
        say "Downloading the application Git repository ..."
        paragraph do
          begin
            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
  end

  display_app(rest_app, rest_app.cartridges)

  if issues?
    output_issues(rest_app)
  else
    results{ messages.each { |msg| success msg } }.blank? and "Application created"
  end

  0
end
delete(app) click to toggle source
# File lib/rhc/commands/app.rb, line 192
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"

  0
end
force_stop(app) click to toggle source
# File lib/rhc/commands/app.rb, line 230
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 252
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 241
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 276
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
    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
ssh(app_name) click to toggle source
# File lib/rhc/commands/app.rb, line 310
def ssh(app_name)
  raise ArgumentError, "No application specified" unless app_name.present?
  raise OptionParser::InvalidOption, "No system SSH available. Please use the --ssh option to specify the path to your SSH executable, or install SSH." unless options.ssh or has_ssh?

  rest_app = rest_client.find_application(options.namespace, app_name)

  say "Connecting to #{rest_app.ssh_string.to_s} ..."
  if options.ssh
    debug "Using user specified SSH: #{options.ssh}"
    Kernel.send(:system, "#{options.ssh} #{rest_app.ssh_string.to_s}")
  else
    debug "Using system ssh"
    Kernel.send(:system, "ssh #{rest_app.ssh_string.to_s}")
  end
end
start(app) click to toggle source
# File lib/rhc/commands/app.rb, line 208
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 331
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 219
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 263
def tidy(app)
  app_action app, :tidy

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

Private Instance Methods

add_jenkins_app(rest_domain) click to toggle source
# File lib/rhc/commands/app.rb, line 427
def add_jenkins_app(rest_domain)
  create_app(jenkins_app_name, "jenkins-1.4", rest_domain)
end
add_jenkins_cartridge(rest_app) click to toggle source
# File lib/rhc/commands/app.rb, line 431
def add_jenkins_cartridge(rest_app)
  rest_app.add_cartridge("jenkins-client-1.4")
end
add_jenkins_client_to(rest_app, messages) click to toggle source
# File lib/rhc/commands/app.rb, line 435
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 402
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 372
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 377
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 363
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 358
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) click to toggle source
# File lib/rhc/commands/app.rb, line 408
def create_app(name, cartridges, rest_domain, gear_size=nil, scale=nil, from_code=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
  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 469
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 496
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
gear_group_state(states) click to toggle source
# File lib/rhc/commands/app.rb, line 397
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 393
def gear_groups_for_app(app_name)
  rest_client.find_application_gear_groups(options.namespace, app_name)
end
has_ssh?() click to toggle source

return whether or not SSH is installed

# File lib/rhc/commands/app.rb, line 527
def has_ssh?
  @has_ssh ||= begin
    @ssh_version = nil
    ssh_version
    $?.success?
  rescue
    false
  end
end
jenkins_app_name() click to toggle source
# File lib/rhc/commands/app.rb, line 501
def jenkins_app_name
  if options.enable_jenkins.is_a? String
    options.enable_jenkins
  end || "jenkins"
end
output_issues(rest_app) click to toggle source
# File lib/rhc/commands/app.rb, line 562
      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 can't get your application '#{rest_app.name}' running in the browser,
  you can try destroying and recreating the application:

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

  If this doesn't work for you, let us know in the forums or in IRC and we'll
  make sure to get you up and running.

    Forums - https://www.openshift.com/forums/openshift
    IRC - #openshift (on Freenode)

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

WARNING_OUTPUT
      end
require_one_web_cart() click to toggle source
# File lib/rhc/commands/app.rb, line 341
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
      carts.map! &web_carts_only
    end
  }
end
run_nslookup(host) click to toggle source
# File lib/rhc/commands/app.rb, line 507
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 514
def run_ping(host)
  # :nocov:
  %xping #{host} -n 2`
  $?.exitstatus == 0
  # :nocov:
end
ssh_version() click to toggle source

check the version of SSH that is installed

# File lib/rhc/commands/app.rb, line 522
def ssh_version
  @ssh_version ||= %xssh -V 2>&1`.strip
end
windows_nslookup_bug?(rest_app) click to toggle source
# File lib/rhc/commands/app.rb, line 537
      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