require "skylight/util/http" module Skylight module CLI class Doctor < Thor::Group include Helpers desc "Run some basic tests to look out for common errors" def check_ssl say "Checking SSL" http = Util::HTTP.new(config) indent do req = http.get("/status") if req.success? say "OK", :green else encountered_error! if req.exception.is_a?(Util::HTTP::StartError) && req.exception.original.is_a?(OpenSSL::SSL::SSLError) say "Failed to verify SSL certificate.", :red if Util::SSL.ca_cert_file? say "Certificates located at #{Util::SSL.ca_cert_file_or_default} may be out of date.", :yellow if mac? && rvm_present? say "Please update your certificates with RVM by running `rvm osx-ssl-certs update all`.", :yellow say "Alternatively, try setting `SKYLIGHT_FORCE_OWN_CERTS=1` in your environment.", :yellow else say "Please update your local certificates or try setting `SKYLIGHT_FORCE_OWN_CERTS=1` in your " \ "environment.", :yellow end end else say "Unable to reach Skylight servers.", :red end end end say "\n" end def check_rails say "Checking for Rails" indent do if rails? say "Rails application detected", :green else say "No Rails application detected", :yellow say "Additional `skylight doctor` checks only work with Rails applications.", :yellow done! end end say "\n" end def check_native say "Checking for native agent" indent do if Skylight.native? say "Native agent installed", :green else encountered_error! say "Unable to load native extension", :yellow indent do install_log = File.expand_path("../../ext/install.log", __dir__) if File.exist?(install_log) File.readlines(install_log).each { |line| say line, :red } else say "Reason unknown", :red end end done! end end say "\n" end def check_config say "Checking for valid configuration" indent do config.validate! say "Configuration is valid", :green rescue ConfigError => e encountered_error! say "Configuration is invalid", :red indent do say e.message, :red say "This may occur if you are configuring with ENV variables and didn't set them in this shell." end done! end puts "\n" end def check_daemon say "Checking Skylight startup" indent do # Set this after we validate. It will give us more detailed information on start. logger = Logger.new("/dev/null") # Rely on `say` in the formatter instead # Log everything logger.level = Logger::DEBUG # Remove excess formatting logger.formatter = proc do |severity, _datetime, _progname, msg| msg = msg.sub("[SKYLIGHT] [#{Skylight::VERSION}] ", "") say "#{severity} - #{msg}" # Definitely non-standard end config.logger = logger config.set(:"daemon.lazy_start", false) started = Skylight.start!(config) if started say "Successfully started", :green else encountered_error! say "Failed to start", :red done! end say "Waiting for daemon... " # Doesn't start immediately tries = 0 daemon_running = false while tries < 5 `ps cax | grep skylightd` if $CHILD_STATUS.success? daemon_running = true break end tries += 1 sleep 1 end if daemon_running say "Success", :green else encountered_error! say "Failed", :red end end say "\n" end def wrap_up done! end private # Overwrite the default helper method to load from Rails def config return @config if @config # MEGAHAX if rails? # Normally auto-loaded, but we haven't loaded Rails by the time Skylight is loaded require "skylight/railtie" require rails_rb railtie = Skylight::Railtie.send(:new) @config = railtie.send(:load_skylight_config, Rails.application) else super end end def mac? Util::Platform::OS == "darwin" end # NOTE: This check won't work correctly on Windows def rvm_present? @has_rvm = system("which rvm > /dev/null") if @has_rvm.nil? @has_rvm end def encountered_error! @has_errors = true end def has_errors? @has_errors end def done! shell.padding = 0 say "\n\n" if has_errors? say "Skylight Doctor found some errors. Please review the output above.", :red say "If you have any further questions, please contact support@skylight.io.", :yellow exit 1 else say "All checks passed!", :green say "If you're still having trouble, please contact support@skylight.io.", :yellow exit 0 end end end end end