lib/cli/commands/apps.rb in af-0.3.12.3 vs lib/cli/commands/apps.rb in af-0.3.13.beta.5

- old
+ new

@@ -42,10 +42,32 @@ app = client.app_info(appname) return display "Application '#{appname}' could not be found".red if app.nil? return display "Application '#{appname}' already started".yellow if app[:state] == 'STARTED' + if @options[:debug] + runtimes = client.runtimes_info + return display "Cannot get runtime information." unless runtimes + + runtime = runtimes[app[:staging][:stack].to_sym] + return display "Unknown runtime." unless runtime + + unless runtime[:debug_modes] and runtime[:debug_modes].include? @options[:debug] + modes = runtime[:debug_modes] || [] + + display "\nApplication '#{appname}' cannot start in '#{@options[:debug]}' mode" + + if push + display "Try `vmc start' with one of the following modes: #{modes.inspect}" + else + display "Available modes: #{modes.inspect}" + end + + return + end + end + banner = 'Staging Application: ' display banner, false t = Thread.new do count = 0 @@ -55,10 +77,11 @@ count += 1 end end app[:state] = 'STARTED' + app[:debug] = @options[:debug] client.update_app(appname, app) Thread.kill(t) clear(LINE_LENGTH) display "#{banner}#{'OK'.green}" @@ -77,14 +100,13 @@ break if app_started_properly(appname, count > HEALTH_TICKS) if !crashes(appname, false, start_time).empty? # Check for the existance of crashes display "\nError: Application [#{appname}] failed to start, logs information below.\n".red grab_crash_logs(appname, '0', true) - if push + if push and !no_prompt display "\n" - should_delete = ask 'Should I delete the application? (Y/n)? ' unless no_prompt - delete_app(appname, false) unless no_prompt || should_delete.upcase == 'N' + delete_app(appname, false) if ask "Delete the application?", :default => true end failed = true break elsif count > TAIL_TICKS log_lines_displayed = grab_startup_tail(appname, log_lines_displayed) @@ -128,18 +150,15 @@ def mem(appname, memsize=nil) app = client.app_info(appname) mem = current_mem = mem_quota_to_choice(app[:resources][:memory]) memsize = normalize_mem(memsize) if memsize - unless memsize - choose do |menu| - menu.layout = :one_line - menu.prompt = "Update Memory Reservation? [Current:#{current_mem}] " - menu.default = current_mem - mem_choices.each { |choice| menu.choice(choice) { memsize = choice } } - end - end + memsize ||= ask( + "Update Memory Reservation?", + :default => current_mem, + :choices => mem_choices + ) mem = mem_choice_to_quota(mem) memsize = mem_choice_to_quota(memsize) current_mem = mem_choice_to_quota(current_mem) @@ -182,15 +201,11 @@ end def delete(appname=nil) force = @options[:force] if @options[:all] - should_delete = force && no_prompt ? 'Y' : 'N' - unless no_prompt || force - should_delete = ask 'Delete ALL Applications and Services? (y/N)? ' - end - if should_delete.upcase == 'Y' + if no_prompt || force || ask("Delete ALL applications and services?", :default => false) apps = client.apps apps.each { |app| delete_app(app[:name], force) } end else err 'No valid appname given' unless appname @@ -200,25 +215,38 @@ def delete_app(appname, force) app = client.app_info(appname) services_to_delete = [] app_services = app[:services] + services_apps_hash = provisioned_services_apps_hash app_services.each { |service| - del_service = force && no_prompt ? 'Y' : 'N' + del_service = force && no_prompt unless no_prompt || force - del_service = ask("Provisioned service [#{service}] detected, would you like to delete it? [yN]: ") + del_service = ask( + "Provisioned service [#{service}] detected, would you like to delete it?", + :default => false + ) + + if del_service + apps_using_service = services_apps_hash[service].reject!{ |app| app == appname} + if apps_using_service.size > 0 + del_service = ask( + "Provisioned service [#{service}] is also used by #{apps_using_service.size == 1 ? "app" : "apps"} #{apps_using_service.entries}, are you sure you want to delete it?", + :default => false + ) + end + end end - services_to_delete << service if del_service.upcase == 'Y' + services_to_delete << service if del_service } + display "Deleting application [#{appname}]: ", false client.delete_app(appname) display 'OK'.green services_to_delete.each do |s| - display "Deleting service [#{s}]: ", false - client.delete_service(s) - display 'OK'.green + delete_service_banner(s) end end def all_files(appname, path) instances_info_envelope = client.app_instances(appname) @@ -342,14 +370,14 @@ exec = @options[:exec] || 'thin start' ignore_framework = @options[:noframework] no_start = @options[:nostart] path = @options[:path] || '.' - appname = @options[:name] unless appname - url = @options[:url] + appname ||= @options[:name] mem, memswitch = nil, @options[:mem] memswitch = normalize_mem(memswitch) if memswitch + url = @options[:url] # Check app existing upfront if we have appname app_checked = false if appname err "Application '#{appname}' already exists, use update" if app_exists?(appname) @@ -365,77 +393,78 @@ if memswitch && !no_start check_has_capacity_for(mem_choice_to_quota(memswitch) * instances) end unless no_prompt || @options[:path] - proceed = ask('Would you like to deploy from the current directory? [Yn]: ') - if proceed.upcase == 'N' - path = ask('Please enter in the deployment path: ') + unless ask('Would you like to deploy from the current directory?', :default => true) + path = ask('Please enter in the deployment path') end end path = File.expand_path(path) check_deploy_directory(path) - appname = ask("Application Name: ") unless no_prompt || appname + appname ||= ask("Application Name") unless no_prompt err "Application Name required." if appname.nil? || appname.empty? - unless app_checked - err "Application '#{appname}' already exists, use update or delete." if app_exists?(appname) + if !app_checked and app_exists?(appname) + err "Application '#{appname}' already exists, use update or delete." end + default_url = "#{appname}.#{VMC::Cli::Config.suggest_url}" + unless no_prompt || url - url = ask("Application Deployed URL: '#{appname}.#{VMC::Cli::Config.suggest_url}'? ") + url = ask( + "Application Deployed URL", + :default => default_url + ) - # common error case is for prompted users to answer y or Y or yes or YES to this ask() resulting in an - # unintended URL of y. Special case this common error - if YES_SET.member?(url) - #silently revert to the stock url - url = "#{appname}.#{VMC::Cli::Config.suggest_url}" - end + # common error case is for prompted users to answer y or Y or yes or + # YES to this ask() resulting in an unintended URL of y. Special case + # this common error + url = nil if YES_SET.member? url end - url = "#{appname}.#{VMC::Cli::Config.suggest_url}" if url.nil? || url.empty? + url ||= default_url # Detect the appropriate framework. framework = nil unless ignore_framework framework = VMC::Cli::Framework.detect(path) - framework_correct = ask("Detected a #{framework}, is this correct? [Yn]: ") if prompt_ok && framework - framework_correct ||= 'y' - if prompt_ok && (framework.nil? || framework_correct.upcase == 'N') + + if prompt_ok and framework + framework_correct = + ask("Detected a #{framework}, is this correct?", :default => true) + end + + if prompt_ok && (framework.nil? || !framework_correct) display "#{"[WARNING]".yellow} Can't determine the Application Type." unless framework - framework = nil if framework_correct.upcase == 'N' - choose do |menu| - menu.layout = :one_line - menu.prompt = "Select Application Type: " - menu.default = framework - VMC::Cli::Framework.known_frameworks.each do |f| - menu.choice(f) { framework = VMC::Cli::Framework.lookup(f) } - end - end + framework = VMC::Cli::Framework.lookup( + ask( + "Select Application Type", + { :indexed => true, + :choices => VMC::Cli::Framework.known_frameworks + } + ) + ) display "Selected #{framework}" end # Framework override, deprecated exec = framework.exec if framework && framework.exec else framework = VMC::Cli::Framework.new end err "Application Type undetermined for path '#{path}'" unless framework - unless memswitch - mem = framework.memory - if prompt_ok - choose do |menu| - menu.layout = :one_line - menu.prompt = "Memory Reservation [Default:#{mem}] " - menu.default = mem - mem_choices.each { |choice| menu.choice(choice) { mem = choice } } - end - end - else + + if memswitch mem = memswitch + elsif prompt_ok + mem = ask("Memory Reservation", + :default => framework.memory, :choices => mem_choices) + else + mem = framework.memory end # Set to MB number mem_quota = mem_choice_to_quota(mem) @@ -463,12 +492,12 @@ # Services check unless no_prompt || @options[:noservices] services = client.services_info unless services.empty? - proceed = ask("Would you like to bind any services to '#{appname}'? [yN]: ") - bind_services(appname, services) if proceed.upcase == 'Y' + proceed = ask("Would you like to bind any services to '#{appname}'?", :default => false) + bind_services(appname, services) if proceed end end # Stage and upload the app bits. upload_app_bits(appname, path) @@ -598,34 +627,37 @@ display 'OK'.green end end + # If no resource needs to be sent, add an empty file to ensure we have + # a multi-part request that is expected by nginx fronting the CC. + if VMC::Cli::ZipUtil.get_files_to_pack(explode_dir).empty? + Dir.chdir(explode_dir) do + File.new(".__empty__", "w") + end + end # Perform Packing of the upload bits here. - unless VMC::Cli::ZipUtil.get_files_to_pack(explode_dir).empty? - display ' Packing application: ', false - VMC::Cli::ZipUtil.pack(explode_dir, upload_file) - display 'OK'.green + display ' Packing application: ', false + VMC::Cli::ZipUtil.pack(explode_dir, upload_file) + display 'OK'.green - upload_size = File.size(upload_file); - if upload_size > 1024*1024 - upload_size = (upload_size/(1024.0*1024.0)).round.to_s + 'M' - elsif upload_size > 0 - upload_size = (upload_size/1024.0).round.to_s + 'K' - end + upload_size = File.size(upload_file); + if upload_size > 1024*1024 + upload_size = (upload_size/(1024.0*1024.0)).round.to_s + 'M' + elsif upload_size > 0 + upload_size = (upload_size/1024.0).round.to_s + 'K' else upload_size = '0K' end upload_str = " Uploading (#{upload_size}): " display upload_str, false - unless VMC::Cli::ZipUtil.get_files_to_pack(explode_dir).empty? - FileWithPercentOutput.display_str = upload_str - FileWithPercentOutput.upload_size = File.size(upload_file); - file = FileWithPercentOutput.open(upload_file, 'rb') - end + FileWithPercentOutput.display_str = upload_str + FileWithPercentOutput.upload_size = File.size(upload_file); + file = FileWithPercentOutput.open(upload_file, 'rb') client.upload_app(appname, file, appcloud_resources) display 'OK'.green if VMC::Cli::ZipUtil.get_files_to_pack(explode_dir).empty? display 'Push Status: ', false @@ -638,67 +670,80 @@ FileUtils.rm_rf(explode_dir) if explode_dir end def choose_existing_service(appname, user_services) return unless prompt_ok - selected = false - choose do |menu| - menu.header = "The following provisioned services are available" - menu.prompt = 'Please select one you wish to provision: ' - menu.select_by = :index_or_name - user_services.each do |s| - menu.choice(s[:name]) do - display "Binding Service: ", false - client.bind_service(s[:name], appname) - display 'OK'.green - selected = true - end - end - end - selected + + display "The following provisioned services are available" + name = ask( + "Please select one you which to prevision", + { :indexed => true, + :choices => user_services.collect { |s| s[:name] } + } + ) + + bind_service_banner(name, appname, false) + + true end def choose_new_service(appname, services) return unless prompt_ok - choose do |menu| - menu.header = "The following system services are available" - menu.prompt = 'Please select one you wish to provision: ' - menu.select_by = :index_or_name - service_choices = [] - services.each do |service_type, value| - value.each do |vendor, version| - service_choices << vendor - end - end - service_choices.sort! {|a, b| a.to_s <=> b.to_s } - service_choices.each do |vendor| - menu.choice(vendor) do - default_name = random_service_name(vendor) - service_name = ask("Specify the name of the service [#{default_name}]: ") - service_name = default_name if service_name.empty? - create_service_banner(vendor, service_name) - bind_service_banner(service_name, appname) - end - end - end + + display "The following system services are available" + + vendor = ask( + "Please select one you wish to provision", + { :indexed => true, + :choices => + services.values.collect { |type| + type.keys.collect(&:to_s) + }.flatten.sort! + } + ) + + default_name = random_service_name(vendor) + service_name = ask("Specify the name of the service", + :default => default_name) + + create_service_banner(vendor, service_name) + bind_service_banner(service_name, appname) end def bind_services(appname, services) user_services = client.services + selected_existing = false unless no_prompt || user_services.empty? - use_existing = ask "Would you like to use an existing provisioned service [yN]? " - if use_existing.upcase == 'Y' + if ask("Would you like to use an existing provisioned service?", + :default => false) selected_existing = choose_existing_service(appname, user_services) end end + # Create a new service and bind it here unless selected_existing choose_new_service(appname, services) end end + def provisioned_services_apps_hash + apps = client.apps + services_apps_hash = {} + apps.each {|app| + app[:services].each { |svc| + svc_apps = services_apps_hash[svc] + unless svc_apps + svc_apps = Set.new + services_apps_hash[svc] = svc_apps + end + svc_apps.add(app[:name]) + } unless app[:services] == nil + } + services_apps_hash + end + def check_app_limit usage = client_info[:usage] limits = client_info[:limits] return unless usage and limits and limits[:apps] if limits[:apps] == usage[:apps] @@ -769,13 +814,23 @@ return display JSON.pretty_generate(instances_info) if @options[:json] return display "No running instances for [#{appname}]".yellow if instances_info.empty? instances_table = table do |t| - t.headings = 'Index', 'State', 'Start Time' + show_debug = instances_info.any? { |e| e[:debug_port] } + + headings = ['Index', 'State', 'Start Time'] + headings << 'Debug IP' if show_debug + headings << 'Debug Port' if show_debug + + t.headings = headings + instances_info.each do |entry| - t << [entry[:index], entry[:state], Time.at(entry[:since]).strftime("%m/%d/%Y %I:%M%p")] + row = [entry[:index], entry[:state], Time.at(entry[:since]).strftime("%m/%d/%Y %I:%M%p")] + row << entry[:debug_ip] if show_debug + row << entry[:debug_port] if show_debug + t << row end end display "\n" display instances_table end @@ -823,15 +878,20 @@ def app_started_properly(appname, error_on_health) app = client.app_info(appname) case health(app) when 'N/A' # Health manager not running. - err "\Application '#{appname}'s state is undetermined, not enough information available." if error_on_health + err "\nApplication '#{appname}'s state is undetermined, not enough information available." if error_on_health return false when 'RUNNING' return true else - return false + if app[:meta][:debug] == "suspend" + display "\nApplication [#{appname}] has started in a mode that is waiting for you to trigger startup." + return true + else + return false + end end end def display_logfile(path, content, instance='0', banner=nil) banner ||= "====> #{path} <====\n\n"