263 lines
9.2 KiB
Ruby
263 lines
9.2 KiB
Ruby
class HomeController < ApplicationController
|
|
layout 'frontend'
|
|
|
|
STATUS = Hash.new('warning')
|
|
STATUS[true] = 'ok'
|
|
STATUS[false] = 'error'
|
|
|
|
OS_VERSION = "Description:\tUbuntu 18.04"
|
|
PG_VERSION = 'PostgreSQL 12'.freeze
|
|
POSTGIS_VERSION = '3.0'.freeze
|
|
CDB_VALID_VERSION = '0.36.0'.freeze
|
|
CDB_LATEST_VERSION = '0.36.0'.freeze
|
|
REDIS_VERSION = '4'.freeze
|
|
RUBY_BIN_VERSION = 'ruby 2.2'.freeze
|
|
NODE_VERSION = 'v6.9.2'.freeze
|
|
GEOS_VERSION = '3.5.0'.freeze
|
|
GDAL_VERSION = '2.2'.freeze
|
|
|
|
WINDSHAFT_VALID_VERSION = '6.1'.freeze
|
|
WINDSHAFT_LATEST_VERSION = '6.1.1'.freeze
|
|
RUN_WINDSHAFT_INSTRUCTIONS = 'Run Windshaft: <span class="code">cd /Windshaft-cartodb && node app.js development'\
|
|
'</span>'.freeze
|
|
SQL_API_VALID_VERSION = '2.1'.freeze
|
|
SQL_API_LATEST_VERSION = '2.1.1'.freeze
|
|
RUN_SQL_API_INSTRUCTIONS = 'Run SQL API <span class="code">cd /CartoDB-SQL-API; node app.js development</span>'.freeze
|
|
RUN_RESQUE_INSTRUCTIONS = 'Run Resque <span class="code">bundle exec script/resque</span>'.freeze
|
|
|
|
skip_before_filter :browser_is_html5_compliant?, only: :app_status
|
|
# Don't force org urls
|
|
skip_before_filter :ensure_org_url_if_org_user
|
|
|
|
def app_status
|
|
return head(503) if Cartodb.config[:disable_file] && File.exists?(File.expand_path(Cartodb.config[:disable_file]))
|
|
db_ok = check_db
|
|
redis_ok = check_redis
|
|
api_ok = true
|
|
head (db_ok && redis_ok && api_ok) ? 200 : 500
|
|
rescue StandardError => e
|
|
log_info(message: 'status method failed', exception: e)
|
|
head 500
|
|
end
|
|
|
|
def app_diagnosis
|
|
return head(400) if Cartodb.config[:cartodb_com_hosted] == false
|
|
|
|
@diagnosis = [
|
|
diagnosis_output('Configuration') { configuration_diagnosis },
|
|
diagnosis_output('Operating System') { single_line_command_version_diagnosis('lsb_release -a', OS_VERSION, 1) },
|
|
diagnosis_output('Ruby') { single_line_command_version_diagnosis('ruby --version', RUBY_BIN_VERSION) },
|
|
diagnosis_output('Node') { single_line_command_version_diagnosis('node --version', minor_version: NODE_VERSION) },
|
|
diagnosis_output('PostgreSQL') { pg_diagnosis },
|
|
diagnosis_output('PostGIS') { extension_diagnosis('postgis', POSTGIS_VERSION) },
|
|
diagnosis_output('CartoDB extension') { extension_diagnosis('cartodb', CDB_VALID_VERSION, CDB_LATEST_VERSION) },
|
|
diagnosis_output('Database connection') { db_diagnosis },
|
|
diagnosis_output('Redis') { redis_diagnosis },
|
|
diagnosis_output('Redis connection') { redis_connection_diagnosis },
|
|
diagnosis_output('Windshaft', RUN_WINDSHAFT_INSTRUCTIONS) {
|
|
windshaft_diagnosis(WINDSHAFT_VALID_VERSION, WINDSHAFT_LATEST_VERSION) },
|
|
diagnosis_output('SQL API', RUN_SQL_API_INSTRUCTIONS) {
|
|
sql_api_diagnosis(SQL_API_VALID_VERSION, SQL_API_LATEST_VERSION) },
|
|
diagnosis_output('Resque') { resque_diagnosis(RUN_RESQUE_INSTRUCTIONS) },
|
|
diagnosis_output('GEOS') do
|
|
single_line_command_version_diagnosis('geos-config --version', minor_version: GEOS_VERSION)
|
|
end,
|
|
diagnosis_output('GDAL') { single_line_command_version_diagnosis('gdal-config --version', GDAL_VERSION) },
|
|
]
|
|
end
|
|
|
|
private
|
|
|
|
def configuration_diagnosis
|
|
# favor displaying an organization user if any present
|
|
organization = Carto::Organization.first
|
|
user = organization ? organization.owner : Carto::User.first
|
|
|
|
['', [
|
|
"Environment: #{environment}",
|
|
"Subdomainless URLs: #{Cartodb.config[:subdomainless_urls]}",
|
|
"Sample Editor URL: #{CartoDB.url(self, 'datasets_index', user: user)}",
|
|
"Sample Editor APIs URL: #{CartoDB.url(self, 'api_v1_visualizations_index', user: user)}"
|
|
]]
|
|
end
|
|
|
|
def environment
|
|
Rails.env
|
|
end
|
|
|
|
def pg_diagnosis
|
|
version_diagnosis(PG_VERSION) {
|
|
version = SequelRails.connection.fetch('select version()').first.values[0]
|
|
[version, [version]]
|
|
}
|
|
end
|
|
|
|
def db_diagnosis
|
|
[STATUS[check_db], []]
|
|
end
|
|
|
|
def redis_diagnosis
|
|
version_diagnosis(REDIS_VERSION) {
|
|
version = $tables_metadata.info['redis_version']
|
|
[version, [version]]
|
|
}
|
|
end
|
|
|
|
def redis_connection_diagnosis
|
|
[STATUS[check_redis], []]
|
|
end
|
|
|
|
def windshaft_diagnosis(supported_version, latest_version)
|
|
config = Cartodb.config[:tiler]
|
|
url_config_key = 'internal'
|
|
endpoint_prefix = ""
|
|
version_key = 'windshaft_cartodb'
|
|
|
|
api_service_diagnosis(config, url_config_key, supported_version, latest_version, endpoint_prefix, version_key)
|
|
end
|
|
|
|
def sql_api_diagnosis(supported_version, latest_version)
|
|
config = Cartodb.config[:sql_api]
|
|
url_config_key = 'private'
|
|
endpoint_prefix = "api/v1/"
|
|
version_key = 'cartodb_sql_api'
|
|
|
|
api_service_diagnosis(config, url_config_key, supported_version, latest_version, endpoint_prefix, version_key)
|
|
end
|
|
|
|
def api_service_diagnosis(config, url_config_key, supported_version, latest_version, endpoint_prefix, version_key)
|
|
service_url = configuration_url(config[url_config_key])
|
|
info = safe_json_get("#{service_url}/#{endpoint_prefix}version")
|
|
|
|
version = info[version_key]
|
|
messages = ["Service url: #{service_url}"]
|
|
messages << "Full config: #{config}"
|
|
messages.concat info.to_a.map { |s, v| "<span class='lib'>#{s}</strong>: <span class='version'>#{v}</span>" }
|
|
valid = valid?(supported_version, latest_version, version)
|
|
|
|
if valid != false
|
|
messages << "Currently we support #{supported_version}. Latest: #{latest_version}" unless valid
|
|
|
|
health = safe_json_get("#{service_url}/#{endpoint_prefix}health")
|
|
unless health['enabled'] == true
|
|
health['instructions'] = "Enable health checking at config/environments/#{environment}.js"
|
|
end
|
|
health_ok = health['ok'] == true
|
|
messages.concat(health.reject { |k, v| k == 'result' }
|
|
.to_a
|
|
.map { |s, v| "<span class='lib'>Health #{s}</strong>: <span class='version'>#{v}</span>" })
|
|
end
|
|
|
|
[STATUS[response.response_code == 200 && valid && health_ok], messages]
|
|
end
|
|
|
|
# true: latest
|
|
# nil: supported
|
|
# false: not supported
|
|
def valid?(supported_version, latest_version, version)
|
|
if (version =~ /\A#{latest_version}/ ? true : nil)
|
|
true
|
|
else
|
|
version =~ /\A#{supported_version}/ ? nil : false
|
|
end
|
|
end
|
|
|
|
def resque_diagnosis(help)
|
|
Open3.popen3('ps xah | grep "[s]cript/resque"') do |stdin, stdout, stderr, process|
|
|
output = stdout.read
|
|
status = output != nil && output != ''
|
|
messages = output.split("\n")
|
|
[STATUS[status], messages.append(status ? ("Running pids: #{running_import_ids}") : help)]
|
|
end
|
|
end
|
|
|
|
def running_import_ids
|
|
Resque::Worker.all.map do |worker|
|
|
next unless worker.job['queue'] == 'imports'
|
|
worker.job['payload']['args'].first['job_id'] rescue nil
|
|
end.compact
|
|
end
|
|
|
|
def check_db
|
|
SequelRails.connection.select('OK').first.values.include?('OK')
|
|
end
|
|
|
|
def check_redis
|
|
$tables_metadata.dbsize != nil
|
|
end
|
|
|
|
def http_client
|
|
@http_client ||= Carto::Http::Client.get('diagnosis')
|
|
end
|
|
|
|
def diagnosis_output(title, help = nil)
|
|
[title, safe_diagnosis(help) { yield } ].flatten(1)
|
|
end
|
|
|
|
def safe_diagnosis(help = nil)
|
|
yield
|
|
rescue StandardError => e
|
|
[ STATUS[false], [ help, e.to_s ].compact ]
|
|
end
|
|
|
|
def extension_diagnosis(extension, supported_version, latest_version = nil)
|
|
version = SequelRails.connection.fetch("select default_version from pg_available_extensions where name = '#{extension}'").first.values[0]
|
|
|
|
status_and_messages(version, [], supported_version, latest_version)
|
|
end
|
|
|
|
def version_diagnosis(supported_version, latest_version = nil, minor_version: nil)
|
|
version_and_messages = yield
|
|
version = version_and_messages[0]
|
|
messages = version_and_messages[1]
|
|
status_and_messages(version, messages, supported_version, latest_version, minor_version: minor_version)
|
|
end
|
|
|
|
def status_and_messages(version, messages, supported_version, latest_version, minor_version: nil)
|
|
valid = if minor_version.present?
|
|
Gem::Version.new(version.delete('v')) >= Gem::Version.new(minor_version.delete('v'))
|
|
else
|
|
version =~ /\A#{supported_version}/ ? true : false
|
|
end
|
|
messages = ["Installed version: #{version}"]
|
|
unless valid
|
|
latest = latest_version.nil? ? '' : "Latest version: #{latest_version}"
|
|
messages << "Current supported version: #{supported_version}.#{latest}"
|
|
end
|
|
if latest_version && valid
|
|
latest = version =~ /\A#{latest_version}/ ? true : false
|
|
messages << "Latest version: #{latest_version}" unless latest
|
|
[STATUS[latest || 'supported'], messages]
|
|
else
|
|
[STATUS[valid], messages]
|
|
end
|
|
end
|
|
|
|
def single_line_command_version_diagnosis(
|
|
command,
|
|
supported_version = nil,
|
|
line_index = 0,
|
|
latest_version = nil,
|
|
minor_version: nil
|
|
)
|
|
version_diagnosis(supported_version, latest_version, minor_version: minor_version) do
|
|
stdin, stdout, stderr, process = Open3.popen3(command)
|
|
output = stdout.read.split("\n")
|
|
if latest_version.nil?
|
|
[output[line_index], output]
|
|
end
|
|
end
|
|
end
|
|
|
|
def configuration_url(conf)
|
|
"#{conf['protocol']}://#{conf['domain']}:#{conf['port']}"
|
|
end
|
|
|
|
def safe_json_get(url)
|
|
JSON.parse(http_client.get(url).body)
|
|
rescue StandardError => e
|
|
{ 'error fetching info' => e.message }
|
|
end
|
|
|
|
end
|