# File lib/rhc/commands/app.rb, line 512 def configure(app_name) rest_app = find_app app_options = {} app_options[:auto_deploy] = options.auto_deploy if !options.auto_deploy.nil? app_options[:keep_deployments] = options.keep_deployments if options.keep_deployments app_options[:deployment_branch] = options.deployment_branch if options.deployment_branch app_options[:deployment_type] = options.deployment_type if options.deployment_type if app_options.present? paragraph do say "Configuring application '#{app_name}' ... " rest_app.configure(app_options) success "done" end end paragraph { display_app(find_app, nil, [:auto_deploy, :keep_deployments, :deployment_type, :deployment_branch]) } paragraph { say "Your application '#{rest_app.name}' is #{app_options.empty? ? '' : 'now '}configured as listed above." } paragraph { say "Use 'rhc show-app #{rest_app.name} --configuration' to check your configuration values any time." } if app_options.present? 0 end
# File lib/rhc/commands/app.rb, line 68 def create(name, cartridges) check_config! check_name!(name) arg_envs, cartridges = cartridges.partition{|item| item.match(env_var_regex_pattern)} rest_domain = check_domain! rest_app = nil repo_dir = nil if options.from_app raise RHC::AppCloneNotSupportedException, "The server does not support creating apps based on others (rhc create-app --from-app)." if (!rest_domain.has_param?('ADD_APPLICATION', 'cartridges[][name]') || !rest_domain.has_param?('ADD_APPLICATION', 'cartridges[][url]')) raise ArgumentError, "Option --from-code is incompatible with --from-app. When creating an app based on another resource you can either specify a Git repository URL with --from-code or an existing app name with --from-app." if options.from_code raise ArgumentError, "Option --no-dns is incompatible with --from-app. We need to propagate the new app DNS to be able to configure it." if options.dns == false raise ArgumentError, "Do not specify cartridges when creating an app based on another one. All cartridges will be copied from the original app." if !(cartridges || []).empty? from_app = find_app(:app => options.from_app) arg_envs = from_app.environment_variables.collect {|env| "#{env.name}=#{env.value}"} + arg_envs cartridges = from_app.cartridges.reject{|c| c.tags.include?('web_proxy')}.collect{|c| c.custom? ? c.url : c.name} end 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? cart_names = cartridges.collect do |c| c.usage_rate? ? "#{c.short_name} (addtl. costs may apply)" : c.short_name end.join(', ') env = collect_env_vars(arg_envs.concat(Array(options.env))) if options.env && env.empty? raise RHC::EnvironmentVariableNotProvidedException.new( "Environment variable(s) not provided.\n" + "Please provide at least one environment variable using the syntax VARIABLE=VALUE. VARIABLE can only contain letters, digits and underscore ('_') and can't begin with a digit.") end if env.present? && !rest_domain.supports_add_application_with_env_vars? env = [] warn "Server does not support environment variables." end scaling = options.scaling region = options.region gear_profile = options.gear_size ha = nil raise RHC::RegionsAndZonesNotSupportedException if region.present? && !rest_client.supports_regions_and_zones? if from_app scaling = from_app.scalable if scaling.nil? region = from_app.region if region.nil? gear_profile = from_app.gear_profile if gear_profile.nil? ha = from_app.ha? if !from_app.ha.nil? if region.present? && !rest_client.allows_region_selection? region = nil warn 'Server does not allow selecting regions. Region is being ignored.' end cartridges = from_app.cartridges.reject{|c| c.tags.include?('web_proxy')}.collect do |cartridge| { :name => (cartridge.name if !cartridge.custom?), :url => (cartridge.url if cartridge.custom?), :gear_size => options.gear_size || cartridge.gear_profile, :additional_gear_storage => (cartridge.additional_gear_storage if cartridge.additional_gear_storage > 0), :scales_from => (cartridge.scales_from if scaling && cartridge.scalable?), :scales_to => (cartridge.scales_to if scaling && cartridge.scalable?) }.reject{|k,v| v.nil? } end end paragraph do header "Application Options" say table([["Domain:", options.namespace], ["Cartridges:", cart_names], (["Source Code:", options.from_code] if options.from_code), (["From app:", from_app.name] if from_app), ["Gear Size:", options.gear_size || (from_app ? "Copied from '#{from_app.name}'" : "default")], ["Scaling:", (scaling ? "yes" : "no") + (from_app && options.scaling.nil? ? " (copied from '#{from_app.name}')" : '')], (["HA:", (ha ? "yes" : "no") + (from_app ? " (copied from '#{from_app.name}')" : '')] if ha.present?), (["Environment Variables:", env.map{|item| "#{item.name}=#{item.value}"}.join(', ')] if env.present?), (["Region:", region + (from_app && options.region.nil? ? " (copied from '#{from_app.name}')" : '')] if region), ].compact ) end paragraph do say "Creating application '#{name}' ... " # create the main app rest_app = create_app(name, cartridges, rest_domain, gear_profile, scaling, options.from_code, env, options.auto_deploy, options.keep_deployments, options.deployment_branch, options.deployment_type, region, ha) 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 jenkins-1", "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 end if from_app say "Setting deployment configuration ... " rest_app.configure({:auto_deploy => from_app.auto_deploy, :keep_deployments => from_app.keep_deployments , :deployment_branch => from_app.deployment_branch, :deployment_type => from_app.deployment_type}) success 'done' snapshot_filename = temporary_snapshot_filename(from_app.name) save_snapshot(from_app, snapshot_filename) restore_snapshot(rest_app, snapshot_filename) File.delete(snapshot_filename) if File.exist?(snapshot_filename) paragraph { warn "The application '#{from_app.name}' has aliases set which were not copied. Please configure the aliases of your new application manually." } unless from_app.aliases.empty? end if options.git section(:now => true, :top => 1, :bottom => 1) do begin if has_git? repo_dir = git_clone_application(rest_app) else warn "You do not have git installed, so your application's git repo will not be cloned" end 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 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
# File lib/rhc/commands/app.rb, line 281 def delete(app) rest_app = find_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
# File lib/rhc/commands/app.rb, line 495 def deploy(ref) rest_app = find_app raise RHC::DeploymentsNotSupportedException.new if !rest_app.supports? "DEPLOY" deploy_artifact(rest_app, ref, options.hot_deploy, options.force_clean_build) 0 end
# File lib/rhc/commands/app.rb, line 378 def enable_ha(app) app_action :enable_ha results { say "#{app} is now highly available" } 0 end
# File lib/rhc/commands/app.rb, line 338 def force_stop(app) app_action :stop, true results { say "#{app} force stopped" } 0 end
# File lib/rhc/commands/app.rb, line 358 def reload(app) app_action :reload results { say "#{app} config reloaded" } 0 end
# File lib/rhc/commands/app.rb, line 348 def restart(app) app_action :restart results { say "#{app} restarted" } 0 end
# File lib/rhc/commands/app.rb, line 328 def scale_down(app) app_action :scale_down results { say "#{app} scaled down" } 0 end
# File lib/rhc/commands/app.rb, line 318 def scale_up(app) app_action :scale_up results { say "#{app} scaled up" } 0 end
# File lib/rhc/commands/app.rb, line 415 def show(app_name) if options.state find_app(:with_gear_groups => true).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 = find_app(:with_gear_groups => true) 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 domain, app = discover_domain_and_app gear_info = rest_client.find_application_gear_groups_endpoints(domain, app).map do |group| group.gears.map do |gear| color_cart = if gear['endpoints'].present? e = gear['endpoints'].collect{ |c| c['cartridge_name'] }.uniq lambda { |c| e.include?(c) ? color(c, :green) : c } else lambda { |c| c } end [ gear['id'], gear['state'] == 'started' ? color(gear['state'], :green) : color(gear['state'], :yellow), group.cartridges.collect{ |c| color_cart.call(c['name']) }.join(' '), group.gear_profile, gear['region'], gear['zone'], ssh_string(gear['ssh_url']) ] end end.flatten(1) explicit_regions = gear_info.select{|i| !i[4].nil?}.present? explicit_zones = gear_info.select{|i| !i[5].nil?}.present? say table(gear_info.map(&:compact), :header => ['ID', 'State', 'Cartridges', 'Size', explicit_regions ? 'Region' : nil, explicit_zones ? 'Zone' : nil, 'SSH URL'].compact) elsif options.configuration display_app_configurations(find_app) paragraph { say "Use 'rhc configure-app' to change the configuration values of this application." } else app = find_app(:include => :cartridges) display_app(app, app.cartridges, nil, options.verbose) end 0 end
# File lib/rhc/commands/app.rb, line 298 def start(app) app_action :start results { say "#{app} started" } 0 end
# File lib/rhc/commands/app.rb, line 308 def stop(app) app_action :stop results { say "#{app} stopped" } 0 end
# File lib/rhc/commands/app.rb, line 368 def tidy(app) app_action :tidy results { say "#{app} cleaned up" } 0 end
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 789 def add_issue(reason, commands_header, *commands) @issues ||= [] issue = {:reason => reason, :commands_header => commands_header, :commands => commands} @issues << issue end
# File lib/rhc/commands/app.rb, line 631 def add_jenkins_app(rest_domain) create_app(jenkins_app_name, jenkins_cartridge_name, rest_domain) end
# File lib/rhc/commands/app.rb, line 635 def add_jenkins_cartridge(rest_app) rest_app.add_cartridge(jenkins_client_cartridge_name) end
# File lib/rhc/commands/app.rb, line 639 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
# File lib/rhc/commands/app.rb, line 602 def app_action(action, *args) rest_app = find_app result = rest_app.send action, *args result end
# File lib/rhc/commands/app.rb, line 577 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
# File lib/rhc/commands/app.rb, line 582 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.name domain end end
# File lib/rhc/commands/app.rb, line 568 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
# File lib/rhc/commands/app.rb, line 563 def check_sshkeys! return unless interactive? RHC::SSHWizard.new(rest_client, config, options).run end
# File lib/rhc/commands/app.rb, line 608 def create_app(name, cartridges, rest_domain, gear_profile=nil, scale=nil, from_code=nil, environment_variables=nil, auto_deploy=nil, keep_deployments=nil, deployment_branch=nil, deployment_type=nil, region=nil, ha=nil) app_options = {:cartridges => Array(cartridges)} app_options[:gear_profile] = gear_profile if gear_profile 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{|i| i.to_hash}.group_by{|i| i[:name]}.values.map(&:last) if environment_variables.present? app_options[:auto_deploy] = auto_deploy if !auto_deploy.nil? app_options[:keep_deployments] = keep_deployments if keep_deployments app_options[:deployment_branch] = deployment_branch if deployment_branch app_options[:deployment_type] = deployment_type if deployment_type app_options[:region] = region if region app_options[:ha] = ha if ha debug "Creating application '#{name}' with these options - #{app_options.inspect}" rest_domain.add_application(name, app_options) rescue RHC::Rest::Exception => e if e.code == 109 paragraph{ say "Valid cartridge types:" } paragraph{ list_cartridges(standalone_cartridges) } end raise end
# File lib/rhc/commands/app.rb, line 673 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=#{host_found}'" host_found end
# File lib/rhc/commands/app.rb, line 700 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
# File lib/rhc/commands/app.rb, line 797 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
# File lib/rhc/commands/app.rb, line 597 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
# File lib/rhc/commands/app.rb, line 813 def issues? not @issues.nil? end
# File lib/rhc/commands/app.rb, line 705 def jenkins_app_name if options.enable_jenkins.is_a? String options.enable_jenkins end || "jenkins" end
# File lib/rhc/commands/app.rb, line 711 def jenkins_cartridge_name jenkins_cartridges.last.name end
# File lib/rhc/commands/app.rb, line 715 def jenkins_client_cartridge_name jenkins_client_cartridges.last.name end
# File lib/rhc/commands/app.rb, line 758 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
# File lib/rhc/commands/app.rb, line 546 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
# File lib/rhc/commands/app.rb, line 719 def run_nslookup(host) # :nocov: %xnslookup #{host}` $?.exitstatus == 0 # :nocov: end
# File lib/rhc/commands/app.rb, line 726 def run_ping(host) # :nocov: %xping #{host} -n 2` $?.exitstatus == 0 # :nocov: end
# File lib/rhc/commands/app.rb, line 817 def temporary_snapshot_filename(app_name) "#{Dir.tmpdir}/#{app_name}_temp_clone.tar.gz" end
# File lib/rhc/commands/app.rb, line 733 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