class Shelly::CLI::Main
Public Instance Methods
add()
click to toggle source
# File lib/shelly/cli/main/add.rb, line 25 def add check_options(options) unless options["skip-requirements-check"] return unless check(verbose = false) end app = Shelly::App.new app.code_name = options["code-name"] || ask_for_code_name app.databases = options["databases"] || ask_for_databases app.size = options["size"] || "small" app.organization_name = options["organization"] || ask_for_organization(options) if options["zone"].present? app.zone = options["zone"] else app.region = options["region"] || ask_for_region end app.create say "Cloud '#{app}' created in '#{app.organization_name}' organization", :green say_new_line git_remote = add_remote(app) say "Creating Cloudfile", :green app.create_cloudfile if app.credit > 0 || !app.organization_details_present? say_new_line say "Billing information", :green if app.credit > 0 say "#{app.credit.to_i} Euro credit remaining." end if !app.organization_details_present? say "Remember to provide billing details before trial ends." say app.edit_billing_url end end info_adding_cloudfile_to_repository info_deploying_to_shellycloud(git_remote) rescue Client::ConflictException => e say_error e[:error] rescue Client::ValidationException => e e.each_error { |error| say_error error, :with_exit => false } say_new_line say_error "Fix erros in the below command and type it again to create your cloud" , :with_exit => false say_error "shelly add --code-name=#{app.code_name.downcase.dasherize} --databases=#{app.databases.join(',')} --organization=#{app.organization_name} --size=#{app.size} --region=#{app.region}" rescue Client::ForbiddenException say_error "You have to be the owner of '#{app.organization_name}' organization to add clouds" rescue Client::NotFoundException => e raise unless e.resource == :organization say_error "Organization '#{app.organization_name}' not found", :with_exit => false say_error "You can list organizations you have access to with `shelly organization list`" end
add_remote(app)
click to toggle source
# File lib/shelly/cli/main.rb, line 444 def add_remote(app) remote = if overwrite_default_remote?(app) say "Running: git remote add shelly #{app.git_url}", :green "shelly" else loop do remote = ask('Specify remote name:') if app.git_remote_exist?(remote) say("Remote '#{remote}' already exists") else say "Running: git remote add #{remote} #{app.git_url}", :green break remote end end end app.add_git_remote(remote) remote end
ask_for_code_name()
click to toggle source
# File lib/shelly/cli/main/add.rb, line 82 def ask_for_code_name default_code_name = default_name_from_dir_name code_name = ask("Cloud code name (#{default_code_name} - default):") code_name.blank? ? default_code_name : code_name end
ask_for_databases()
click to toggle source
# File lib/shelly/cli/main/add.rb, line 88 def ask_for_databases kinds = Shelly::App::DATABASE_CHOICES databases = ask("Which databases do you want to use " \ "#{kinds.join(", ")} (postgresql - default):") begin databases = databases.split(/[\s,]/).reject(&:blank?) valid = valid_databases?(databases) break if valid databases = ask("Unknown database kind. Supported are: #{kinds.join(", ")}:") end while not valid databases.empty? ? ["postgresql"] : databases end
ask_for_new_organization(options = {})
click to toggle source
# File lib/shelly/cli/main/add.rb, line 151 def ask_for_new_organization(options = {}) loop do begin return create_new_organization(options) rescue Client::ValidationException => e e.each_error { |error| say_error error, :with_exit => false } end end end
ask_for_organization(options)
click to toggle source
# File lib/shelly/cli/main/add.rb, line 120 def ask_for_organization(options) organizations = Shelly::User.new.organizations if organizations.blank? ask_for_new_organization(options) else say "Select organization for this cloud:" say_new_line organizations.each do |organization| print_wrapped "\u2219 #{organization.name}", :ident => 2 end say green "Or leave empty to create a new organization" say_new_line loop do selected = ask("Organization:") if organizations.select { |o| o.name == selected }.present? return selected elsif selected.empty? say_new_line return ask_for_new_organization(options) else say_warning "#{selected} organization does not exist" say_new_line end end end end
ask_for_password(options = {})
click to toggle source
# File lib/shelly/cli/main.rb, line 464 def ask_for_password(options = {}) options = {:with_confirmation => true}.merge(options) loop do say "Password: " password = capture_input_without_echo_if_tty say_new_line return password unless options[:with_confirmation] say "Password confirmation: " password_confirmation = capture_input_without_echo_if_tty say_new_line if password.present? return password if password == password_confirmation say_error "Password and password confirmation don't match, please type them again" else say_error "Password can't be blank" end end end
ask_for_region()
click to toggle source
# File lib/shelly/cli/main/add.rb, line 161 def ask_for_region regions = Shelly::App::REGIONS say_new_line say "Select region for this cloud:" say_new_line regions.each do |region| print_wrapped "\u2219 #{region}", :ident => 2 end say_new_line loop do selected = ask("Region (EU - default):").upcase if regions.include?(selected) return selected elsif selected.empty? return "EU" else say_warning "#{selected} region is not available" say_new_line end end end
capture_input_without_echo_if_tty()
click to toggle source
# File lib/shelly/cli/main.rb, line 504 def capture_input_without_echo_if_tty $stdin.tty? ? $stdin.noecho(&:gets).strip : $stdin.gets.strip end
check(verbose = true)
click to toggle source
Public: Check if application fulfills shelly’s requirements
and print them
verbose - when true all requirements will be printed out
together with header and a summary at the end when false only not fulfilled requirements will be printed
When any requirements is not fulfilled header and summary will be displayed regardless of verbose value
# File lib/shelly/cli/main/check.rb, line 15 def check(verbose = true) structure = Shelly::StructureValidator.new if verbose or structure.invalid? or structure.warnings? say "Checking Shelly Cloud requirements\n\n" end print_check(structure.gemfile?, "Gemfile is present", "Gemfile is missing in git repository", :show_fulfilled => verbose) print_check(structure.gemfile_lock?, "Gemfile.lock is present", "Gemfile.lock is missing in git repository", :show_fulfilled => verbose) if structure.gemfile_ruby_version? if structure.gemfile_engine == 'ruby' print_check(!['1.8.7', '1.9.2'].include?(structure.gemfile_ruby_version), "#{structure.gemfile_engine} #{structure.gemfile_ruby_version} is supported", "#{structure.gemfile_engine} #{structure.gemfile_ruby_version} is currently unsupported\n See more at https://shellycloud.com/documentation/requirements#ruby_versions", :show_fulfilled => verbose) end if structure.gemfile_ruby_patchlevel print_check(false, "", "Remove Ruby patchlevel from Gemfile\n Shelly Cloud takes care of upgrading Rubies whenever they are released\n See more at https://shellycloud.com/documentation/requirements#ruby_versions", :show_fulfilled => verbose) end supported_jruby = '1.7.10' if structure.gemfile_engine == 'jruby' print_check(!(structure.gemfile_ruby_version == '1.8.7' || structure.gemfile_engine_version != supported_jruby), "jruby #{supported_jruby} (1.9 mode) is supported", "Only jruby #{supported_jruby} (1.9 mode) is currently supported\n See more at https://shellycloud.com/documentation/requirements#ruby_versions", :show_fulfilled => verbose) end # other platforms: mingw, mswin show instant error unless ['jruby', 'ruby', 'rbx'].include?(structure.gemfile_engine) print_check(false, "", "Your ruby engine: #{structure.gemfile_engine} is currently unsupported\n See more at https://shellycloud.com/documentation/requirements#ruby_versions", :show_fulfilled => verbose) end end print_check(structure.config_ru?, "config.ru is present", "config.ru is missing", :show_fulfilled => verbose) print_check(structure.rakefile?, "Rakefile is present", "Rakefile is missing", :show_fulfilled => verbose) print_check(!structure.gem?("shelly"), "Gem 'shelly' is not a part of Gemfile", "Gem 'shelly' should not be a part of Gemfile.\n The versions of the thor gem used by shelly and Rails may be incompatible.", :show_fulfilled => verbose || structure.warnings?, :failure_level => :warning) print_check(structure.gem?("shelly-dependencies"), "Gem 'shelly-dependencies' is present", "Gem 'shelly-dependencies' is missing, we recommend to install it\n See more at https://shellycloud.com/documentation/requirements#shelly-dependencies", :show_fulfilled => verbose || structure.warnings?, :failure_level => :warning) print_check(structure.gem?("thin") || structure.gem?("puma"), "Web server gem is present", "Missing web server gem in Gemfile. Currently supported: 'thin' and 'puma'", :show_fulfilled => verbose) print_check(structure.gem?("rake"), "Gem 'rake' is present", "Gem 'rake' is missing in the Gemfile", :show_fulfilled => verbose) print_check(structure.task?("db:migrate"), "Task 'db:migrate' is present", "Task 'db:migrate' is missing", :show_fulfilled => verbose) print_check(structure.task?("db:setup"), "Task 'db:setup' is present", "Task 'db:setup' is missing", :show_fulfilled => verbose) cloudfile = Cloudfile.new if cloudfile.present? cloudfile.clouds.each do |app| if app.cloud_databases.include?('postgresql') print_check(structure.gem?("pg") || structure.gem?("postgres"), "Postgresql driver is present for '#{app}' cloud", "Postgresql driver is missing in the Gemfile for '#{app}' cloud,\n we recommend adding 'pg' gem to Gemfile", :show_fulfilled => verbose) end if app.delayed_job? print_check(structure.gem?("delayed_job"), "Gem 'delayed_job' is present for '#{app}' cloud", "Gem 'delayed_job' is missing in the Gemfile for '#{app}' cloud", :show_fulfilled => verbose) end if app.whenever? print_check(structure.gem?("whenever"), "Gem 'whenever' is present for '#{app}' cloud", "Gem 'whenever' is missing in the Gemfile for '#{app}' cloud", :show_fulfilled => verbose) end if app.sidekiq? print_check(structure.gem?("sidekiq"), "Gem 'sidekiq' is present for '#{app}' cloud", "Gem 'sidekiq' is missing in the Gemfile for '#{app}' cloud", :show_fulfilled => verbose) end if app.thin? print_check(structure.gem?("thin"), "Web server gem 'thin' is present for '#{app}' cloud", "Gem 'thin' is missing in the Gemfile for '#{app}' cloud", :show_fulfilled => verbose) end if app.puma? print_check(structure.gem?("puma"), "Web server gem 'puma' is present for '#{app}' cloud", "Gem 'puma' is missing in the Gemfile for '#{app}' cloud", :show_fulfilled => verbose) end end end if structure.valid? if verbose say "\nGreat! Your application is ready to run on Shelly Cloud" end else say "\nFix points marked with #{red("✗")} to run your application on the Shelly Cloud" say "See more about requirements on https://shellycloud.com/documentation/requirements" end say_new_line structure.valid? rescue Bundler::BundlerError => e say_new_line say_error e.message, :with_exit => false say_error "Try to run `bundle install`" end
check_options(options)
click to toggle source
# File lib/shelly/cli/main.rb, line 419 def check_options(options) unless options.empty? if !valid_size?(options["size"]) or !valid_databases?(options["databases"]) say_error "Try `shelly help add` for more information" end end end
console()
click to toggle source
# File lib/shelly/cli/main.rb, line 376 def console app = multiple_clouds(options[:cloud], "console") app.console(options[:server]) rescue Client::ConflictException say_error "Cloud #{app} is not running. Cannot run console." rescue Client::NotFoundException => e raise unless e.resource == :virtual_server say_error "Virtual server '#{options[:server]}' not found or not configured for running console" end
dbconsole(task = nil)
click to toggle source
# File lib/shelly/cli/main.rb, line 316 def dbconsole(task = nil) app = multiple_clouds(options[:cloud], "dbconsole") app.dbconsole rescue Client::ConflictException say_error "Cloud #{app} wasn't deployed properly. Can not run dbconsole." end
delete()
click to toggle source
# File lib/shelly/cli/main.rb, line 255 def delete app = multiple_clouds(options[:cloud], "delete") say_new_line say "You are going to:" say " * remove all files stored in the persistent storage for #{app}," say " * remove all database data for #{app}," say " * remove #{app} cloud from Shelly Cloud" say_new_line say "This action is permanent and can not be undone.", :red say_new_line ask_to_delete_application app # load git info so remote can be removed later on app.git_info app.delete say_new_line say "Scheduling application delete - done" if App.inside_git_repository? app.remove_git_remote say "Removing git remote - done" else say "Missing git remote" end rescue Client::ConflictException => e say_error e[:message] end
info()
click to toggle source
# File lib/shelly/cli/main.rb, line 109 def info app = multiple_clouds(options[:cloud], "info") msg = info_show_last_deploy_logs(app) say "Cloud #{app}:", app.in_deploy_failed_state? ? :red : :green print_wrapped "Region: #{app.region}", :ident => 2 print_wrapped "State: #{app.state_description}#{msg}", :ident => 2 say_new_line print_wrapped "Deployed commit sha: #{app.git_info["deployed_commit_sha"]}", :ident => 2 print_wrapped "Deployed commit message: #{app.git_info["deployed_commit_message"]}", :ident => 2 print_wrapped "Deployed by: #{app.git_info["deployed_push_author"]}", :ident => 2 say_new_line print_wrapped "Repository URL: #{app.git_info["repository_url"]}", :ident => 2 print_wrapped "Web server IP: #{app.web_server_ip.join(', ')}", :ident => 2 say_new_line print_wrapped "Usage:", :ident => 2 app.usage.each do |usage| print_wrapped "#{usage['kind'].capitalize}:", :ident => 4 print_wrapped "Current: #{number_to_human_size(usage['current'])}", :ident => 6 print_wrapped "Average: #{number_to_human_size(usage['avg'])}", :ident => 6 end print_wrapped "Traffic:", :ident => 4 print_wrapped "Incoming: #{number_to_human_size(app.traffic['incoming'].to_i)}", :ident => 6 print_wrapped "Outgoing: #{number_to_human_size(app.traffic['outgoing'].to_i)}", :ident => 6 print_wrapped "Total: #{number_to_human_size(app.traffic['total'].to_i)}", :ident => 6 say_new_line if app.statistics.present? print_wrapped "Statistics:", :ident => 2 app.statistics.each do |stat| print_wrapped "#{stat['name']}:", :ident => 4 print_wrapped "Load average: 1m: #{stat['load']['avg01']}, 5m: #{stat['load']['avg05']}, 15m: #{stat['load']['avg15']}", :ident => 6 print_wrapped "CPU: #{stat['cpu']['wait']}%, MEM: #{stat['memory']['percent']}%, SWAP: #{stat['swap']['percent']}%", :ident => 6 end end rescue Client::GatewayTimeoutException say_error "Server statistics temporarily unavailable" end
info_adding_cloudfile_to_repository()
click to toggle source
# File lib/shelly/cli/main/add.rb, line 102 def info_adding_cloudfile_to_repository say_new_line say "Project is now configured for use with Shelly Cloud:", :green say "You can review changes using", :green say " git status" end
info_deploying_to_shellycloud(remote = 'shelly')
click to toggle source
# File lib/shelly/cli/main/add.rb, line 109 def info_deploying_to_shellycloud(remote = 'shelly') say_new_line say "When you make sure all settings are correct, add changes to your repository:", :green say " git add ." say ' git commit -m "Application added to Shelly Cloud"' say_new_line say "Deploy to your cloud using:", :green say " git push #{remote} master" say_new_line end
list()
click to toggle source
# File lib/shelly/cli/main.rb, line 95 def list user = Shelly::User.new apps = user.apps unless apps.empty? say "You have following clouds available:", :green print_table(apps_table(apps), :ident => 2) else say "You have no clouds yet", :green end end
login(email = nil)
click to toggle source
# File lib/shelly/cli/main.rb, line 62 def login(email = nil) user = Shelly::User.new if options[:key] given_key = Shelly::SshKey.new(options[:key]) say "Your given SSH key (#{given_key.path}) will be uploaded to Shelly Cloud after login." raise Errno::ENOENT, given_key.path unless given_key.exists? else say "Your public SSH key will be uploaded to Shelly Cloud after login." raise Errno::ENOENT, user.ssh_key.path unless user.ssh_key.exists? end email ||= ask_for_email password = ask_for_password(:with_confirmation => false) user.login(email, password) upload_ssh_key(options[:key]) say "Login successful", :green list rescue Client::ValidationException => e e.each_error { |error| say_error "#{error}", :with_exit => false } rescue Client::UnauthorizedException => e say_error e[:error], :with_exit => false if e[:url] say_error "You can reset password by using link:", :with_exit => false say_error e[:url] end exit 1 rescue Errno::ENOENT => e say_error e, :with_exit => false say_error "Use ssh-keygen to generate ssh key pair" end
logout()
click to toggle source
# File lib/shelly/cli/main.rb, line 287 def logout user = Shelly::User.new key = Shelly::SshKey.new(options[:key]) if options[:key] if (key || user.ssh_keys).destroy say "Your public SSH key has been removed from Shelly Cloud" end say "You have been successfully logged out" if user.logout end
mongoconsole()
click to toggle source
# File lib/shelly/cli/main.rb, line 325 def mongoconsole app = multiple_clouds(options[:cloud], "mongoconsole") app.mongoconsole rescue Client::ConflictException say_error "Cloud #{app} wasn't deployed properly. Can not run MongoDB console." end
open()
click to toggle source
# File lib/shelly/cli/main.rb, line 367 def open app = multiple_clouds(options[:cloud], "open") app.open end
overwrite_default_remote?(app)
click to toggle source
# File lib/shelly/cli/main.rb, line 439 def overwrite_default_remote?(app) git_remote = app.git_remote_exist? !git_remote or (git_remote and yes?("Git remote shelly exists, overwrite (yes/no): ")) end
rake(task = nil)
click to toggle source
# File lib/shelly/cli/main.rb, line 302 def rake(task = nil) task = rake_args.join(" ") app = multiple_clouds(options[:cloud], "rake #{task}") app.rake(task, options[:server]) rescue Client::ConflictException say_error "Cloud #{app} is not running. Cannot run rake task." rescue Client::NotFoundException => e raise unless e.resource == :virtual_server say_error "Virtual server '#{options[:server]}' not found or" \ " not configured for running rake task." end
rake_args(args = ARGV)
click to toggle source
Returns valid arguments for rake, removes shelly gem arguments
# File lib/shelly/cli/main.rb, line 403 def rake_args(args = ARGV) skip_next = false [].tap do |out| args.each do |arg| case arg when "rake", "--debug" when "--cloud", "-c", "--server", "-s" skip_next = true else out << arg unless skip_next skip_next = false end end end end
redeploy()
click to toggle source
# File lib/shelly/cli/main.rb, line 344 def redeploy app = multiple_clouds(options[:cloud], "redeploy") deployment_id = app.redeploy say "Redeploying your application for cloud '#{app}'", :green deployment_progress(app, deployment_id, "Cloud redeploy") rescue Client::ConflictException => e case e[:state] when "deploying" say_error "Your application is being redeployed at the moment" when "no_code", "no_billing", "turned_off" say_error "Cloud #{app} is not running", :with_exit => false say "Start your cloud with `shelly start --cloud #{app}`" exit 1 else raise end rescue Client::LockedException => e say_error "Deployment is currently blocked:", :with_exit => false say_error e[:message] exit 1 end
redis_cli()
click to toggle source
# File lib/shelly/cli/main.rb, line 334 def redis_cli app = multiple_clouds(options[:cloud], "redis-cli") app.redis_cli rescue Client::ConflictException say_error "Cloud #{app} wasn't deployed properly. Can not run redis-cli." end
register(email = nil)
click to toggle source
# File lib/shelly/cli/main.rb, line 47 def register(email = nil) say "Registering with email: #{email}" if email user = Shelly::User.new email ||= ask_for_email password = ask_for_password ask_for_acceptance_of_terms user.register(email, password) say "Successfully registered!", :green rescue Client::ValidationException => e e.each_error { |error| say_error "#{error}", :with_exit => false } exit 1 end
setup()
click to toggle source
# File lib/shelly/cli/main.rb, line 198 def setup app = multiple_clouds(options[:cloud], "setup") say "Setting up #{app} cloud", :green say_new_line app.git_url = app.attributes["git_info"]["repository_url"] if overwrite_default_remote?(app) say "Running: git remote add shelly #{app.git_url}" app.add_git_remote say "Running: git fetch shelly" app.git_fetch_remote else loop do remote = ask('Specify remote name:') if app.git_remote_exist?(remote) say("Remote '#{remote}' already exists") else say "Running: git remote add #{remote} #{app.git_url}" app.add_git_remote(remote) say "Running: git fetch #{remote}" app.git_fetch_remote(remote) break end end end say_new_line say "Your application is set up.", :green end
ssh()
click to toggle source
# File lib/shelly/cli/main.rb, line 390 def ssh app = multiple_clouds(options[:cloud], "ssh") app.ssh_console(options[:server]) rescue Client::ConflictException say_error "Cloud #{app} is not running. Cannot run ssh console." rescue Client::NotFoundException => e raise unless e.resource == :virtual_server say_error "Virtual server '#{options[:server]}' not found or not configured for running ssh console" end
start()
click to toggle source
# File lib/shelly/cli/main.rb, line 151 def start app = multiple_clouds(options[:cloud], "start") deployment_id = app.start say "Starting cloud #{app}.", :green say_new_line deployment_progress(app, deployment_id, "Starting cloud") rescue Client::ConflictException => e case e[:state] when "running" say_error "Not starting: cloud '#{app}' is already running" when "deploying" say_error "Not starting: cloud '#{app}' is currently deploying" when "no_code" say_error "Not starting: no source code provided", :with_exit => false say_error "Push source code using:", :with_exit => false say "`git push #{app.git_remote_name} master`" when "deploy_failed" say_error "Not starting: deployment failed", :with_exit => false say_error "Support has been notified", :with_exit => false say_error "Check `shelly deploys show last --cloud #{app}` for reasons of failure" when "not_enough_resources" say_error %{Sorry, There are no resources for your servers. We have been notified about it. We will be adding new resources shortly} when "no_billing" say_error "Please fill in billing details to start #{app}.", :with_exit => false say_error "Visit: #{app.edit_billing_url}", :with_exit => false when "turning_off" say_error %{Not starting: cloud '#{app}' is turning off. Wait until cloud is in 'turned off' state and try again.} end exit 1 rescue Client::LockedException => e say_error "Deployment is currently blocked:", :with_exit => false say_error e[:message] exit 1 end
stop()
click to toggle source
# File lib/shelly/cli/main.rb, line 229 def stop app = multiple_clouds(options[:cloud], "stop") if yes?("Are you sure you want to shut down '#{app}' cloud (yes/no):") deployment_id = app.stop say_new_line deployment_progress(app, deployment_id, "Stopping cloud") end rescue Client::ConflictException => e case e[:state] when "deploying" say_error "Your cloud is currently being deployed and it can not be stopped." when "no_code" say_error "You need to deploy your cloud first.", :with_exit => false say "More information can be found at:" say "#{app.shelly.shellyapp_url}/documentation/deployment" exit 1 when "turning_off" say_error "Your cloud is turning off." end rescue Client::NotFoundException => e raise unless e.resource == :cloud say_error "You have no access to '#{app}' cloud defined in Cloudfile" end
upload_ssh_key(given_key_path = nil)
click to toggle source
# File lib/shelly/cli/main.rb, line 483 def upload_ssh_key(given_key_path = nil) user = Shelly::User.new ssh_key = given_key_path ? Shelly::SshKey.new(given_key_path) : user.ssh_key if ssh_key.exists? if ssh_key.uploaded? say "Your SSH key from #{ssh_key.path} is already uploaded" else say "Uploading your public SSH key from #{ssh_key.path}" ssh_key.upload end else say_error "No such file or directory - #{ssh_key_path}", :with_exit => false say_error "Use ssh-keygen to generate ssh key pair, after that use: `shelly login`", :with_exit => false end rescue Client::ValidationException => e e.each_error { |error| say_error error, :with_exit => false } user.logout exit 1 end
valid_databases?(databases)
click to toggle source
# File lib/shelly/cli/main.rb, line 433 def valid_databases?(databases) return true unless databases.present? kinds = Shelly::App::DATABASE_CHOICES databases.all? { |kind| kinds.include?(kind) } end
valid_size?(size)
click to toggle source
# File lib/shelly/cli/main.rb, line 427 def valid_size?(size) return true unless size.present? sizes = Shelly::App::SERVER_SIZES sizes.include?(size) end
version()
click to toggle source
# File lib/shelly/cli/main.rb, line 42 def version say "shelly version #{Shelly::VERSION}" end