1450 lines
60 KiB
Ruby
1450 lines
60 KiB
Ruby
require_relative 'thread_pool'
|
|
require_relative '../../services/dataservices-metrics/lib/geocoder_usage_metrics'
|
|
require 'timeout'
|
|
require 'date'
|
|
|
|
namespace :cartodb do
|
|
namespace :db do
|
|
|
|
#################
|
|
# LOAD TABLE OIDS
|
|
#################
|
|
desc 'Load table oids'
|
|
task :load_oids => :environment do
|
|
count = ::User.count
|
|
::User.all.each_with_index do |user, i|
|
|
begin
|
|
user.link_outdated_tables
|
|
printf "OK %-#{20}s (%-#{4}s/%-#{4}s)\n", user.username, i, count
|
|
rescue => e
|
|
printf "FAIL %-#{20}s (%-#{4}s/%-#{4}s) #{e.message}\n", user.username, i, count
|
|
end
|
|
#sleep(1.0/5.0)
|
|
end
|
|
end
|
|
|
|
desc 'Copy user api_keys from redis to postgres'
|
|
task :copy_api_keys_from_redis => :environment do
|
|
count = ::User.count
|
|
::User.all.each_with_index do |user, i|
|
|
begin
|
|
user.this.update api_key: $users_metadata.HGET(user.key, 'map_key')
|
|
raise 'No API key!!' if user.reload.api_key.blank?
|
|
puts "(#{i+1} / #{count}) OK #{user.username}"
|
|
rescue => e
|
|
puts "(#{i+1} / #{count}) FAIL #{user.username} #{e.message}"
|
|
end
|
|
end
|
|
end # copy_api_keys_from_redis
|
|
|
|
desc 'Rebuild user tables/layers join table'
|
|
task :register_table_dependencies => :environment do
|
|
count = Map.count
|
|
|
|
Map.all.each_with_index do |map, i|
|
|
begin
|
|
map.data_layers.each do |layer|
|
|
layer.register_table_dependencies
|
|
printf "OK (%-#{4}s/%-#{4}s)\n", i, count
|
|
end
|
|
rescue => e
|
|
printf "FAIL (%-#{4}s/%-#{4}s) #{e}\n", i, count
|
|
end
|
|
end
|
|
end
|
|
|
|
desc 'Removes duplicated indexes created in some accounts'
|
|
task :remove_duplicate_indexes, [:database_host, :sleep, :dryrun] => :environment do |t, args|
|
|
threads = 1
|
|
thread_sleep = 1
|
|
database_host = args[:database_host].blank? ? nil : args[:database_host]
|
|
sleep = args[:sleep].blank? ? 3 : args[:sleep].to_i
|
|
dryrun = args[:dryrun] == 'false' ? 'false' : 'true'
|
|
|
|
if database_host.nil?
|
|
count = ::User.count
|
|
else
|
|
count = ::User.where(database_host: database_host).count
|
|
end
|
|
execute_on_users_with_index(:remove_duplicate_indexes.to_s, Proc.new { |user, i|
|
|
begin
|
|
user.in_database(:as => :superuser) do |db|
|
|
db.transaction do
|
|
db.run(%Q{
|
|
CREATE OR REPLACE FUNCTION CDB_DropDupUnique(dryrun boolean DEFAULT true)
|
|
RETURNS void
|
|
LANGUAGE plpgsql
|
|
VOLATILE
|
|
AS $$
|
|
DECLARE
|
|
rec RECORD;
|
|
sql TEXT;
|
|
BEGIN
|
|
|
|
FOR rec IN SELECT
|
|
c.conname, r.oid tab
|
|
FROM
|
|
pg_constraint c,
|
|
pg_class r
|
|
WHERE c.conrelid > 0
|
|
AND c.conrelid = r.oid
|
|
AND c.contype = 'u'
|
|
AND EXISTS (
|
|
SELECT * FROM pg_constraint pc
|
|
WHERE pc.conrelid = c.conrelid -- same target table
|
|
AND pc.conkey = c.conkey -- samekey
|
|
AND pc.contype = 'p' -- index is a primary one
|
|
)
|
|
LOOP
|
|
|
|
IF NOT dryrun THEN
|
|
RAISE NOTICE 'Constraint % on table % is not needed, dropping',
|
|
rec.conname, rec.tab::regclass::text;
|
|
sql := 'ALTER TABLE ' || rec.tab::regclass::text
|
|
|| ' DROP CONSTRAINT ' || quote_ident (rec.conname);
|
|
RAISE DEBUG 'Running: %', sql;
|
|
EXECUTE sql;
|
|
ELSE
|
|
RAISE NOTICE 'Constraint % on table % is not needed (dry run)',
|
|
rec.conname, rec.tab::regclass::text;
|
|
END IF;
|
|
|
|
END LOOP;
|
|
|
|
END;
|
|
$$;
|
|
})
|
|
db.run(%Q{
|
|
SELECT CDB_DropDupUnique(#{dryrun});
|
|
})
|
|
db.run(%Q{
|
|
DROP FUNCTION IF EXISTS CDB_DropDupUnique(boolean);
|
|
})
|
|
end
|
|
end
|
|
|
|
log(sprintf("OK %-#{20}s %-#{20}s (%-#{4}s/%-#{4}s)\n", user.username, user.database_name, i+1, count), :remove_duplicate_indexes.to_s, database_host)
|
|
sleep(sleep)
|
|
rescue => e
|
|
log(sprintf("FAIL %-#{20}s (%-#{4}s/%-#{4}s) #{e.message}\n", user.username, i+1, count), :remove_duplicate_indexes.to_s, database_host)
|
|
puts "FAIL:#{i} #{e.message}"
|
|
end
|
|
}, threads, thread_sleep, database_host)
|
|
end
|
|
|
|
desc 'Unregisters extraneous members from the "cartodb" extension'
|
|
task :unregister_extraneous_cartodb_members, [:database_host, :sleep, :dryrun] => :environment do |t, args|
|
|
threads = 1
|
|
thread_sleep = 1
|
|
database_host = args[:database_host].blank? ? nil : args[:database_host]
|
|
sleep = args[:sleep].blank? ? 3 : args[:sleep].to_i
|
|
dryrun = args[:dryrun] == 'false' ? 'false' : 'true'
|
|
|
|
if database_host.nil?
|
|
count = ::User.count
|
|
else
|
|
count = ::User.where(database_host: database_host).count
|
|
end
|
|
execute_on_users_with_index(:unregister_extraneous_cartodb_members.to_s, Proc.new { |user, i|
|
|
begin
|
|
user.in_database(:as => :superuser) do |db|
|
|
db.transaction do
|
|
db.run(%Q{
|
|
CREATE OR REPLACE FUNCTION CDB_DropExtraneousExtMembers(dryrun boolean DEFAULT true)
|
|
RETURNS void
|
|
LANGUAGE plpgsql
|
|
VOLATILE
|
|
AS $$
|
|
DECLARE
|
|
rec RECORD;
|
|
sql TEXT;
|
|
BEGIN
|
|
|
|
FOR rec IN SELECT 'ALTER EXTENSION cartodb DROP '
|
|
|| pg_describe_object(d.classid, d.objid, 0) || ';' as q
|
|
FROM pg_extension e LEFT OUTER JOIN pg_depend d
|
|
ON ( d.refobjid = e.oid )
|
|
WHERE d.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass
|
|
AND e.extname = 'cartodb'
|
|
AND d.classid = 'pg_catalog.pg_class'::pg_catalog.regclass -- relations
|
|
AND d.objid != 'cartodb.cdb_tablemetadata'::pg_catalog.regclass
|
|
AND d.deptype = 'e' -- members
|
|
LOOP
|
|
|
|
IF NOT dryrun THEN
|
|
RAISE NOTICE 'Running on %: %', current_database(), rec.q;
|
|
EXECUTE rec.q;
|
|
ELSE
|
|
RAISE NOTICE 'Would run on %: %', current_database(), rec.q;
|
|
END IF;
|
|
|
|
END LOOP;
|
|
|
|
END;
|
|
$$;
|
|
})
|
|
db.run(%Q{
|
|
SET client_min_messages TO notice;
|
|
})
|
|
db.run(%Q{
|
|
SELECT CDB_DropExtraneousExtMembers(#{dryrun});
|
|
})
|
|
db.run(%Q{
|
|
DROP FUNCTION IF EXISTS CDB_DropExtraneousExtMembers(boolean);
|
|
})
|
|
end
|
|
end
|
|
|
|
log(sprintf("OK %-#{20}s %-#{20}s (%-#{4}s/%-#{4}s)\n", user.username, user.database_name, i+1, count), :unregister_extraneous_cartodb_members.to_s, database_host)
|
|
sleep(sleep)
|
|
rescue => e
|
|
log(sprintf("FAIL %-#{20}s (%-#{4}s/%-#{4}s) #{e.message}\n", user.username, i+1, count), :unregister_extraneous_cartodb_members.to_s, database_host)
|
|
puts "FAIL:#{i} #{e.message}"
|
|
end
|
|
}, threads, thread_sleep, database_host)
|
|
end
|
|
|
|
########################
|
|
# LOAD CARTODB FUNCTIONS
|
|
########################
|
|
# e.g. bundle exec rake cartodb:db:load_functions['127.0.0.1','0.5.0']
|
|
# bundle exec rake cartodb:db:load_functions[,'0.5.0']
|
|
desc 'Install/upgrade CARTODB SQL functions'
|
|
task :load_functions, [:database_host, :version, :num_threads, :thread_sleep, :sleep, :statement_timeout] => :environment do |task_name, args|
|
|
# Send this as string, not as number
|
|
extension_version = args[:version].blank? ? nil : args[:version]
|
|
database_host = args[:database_host].blank? ? nil : args[:database_host]
|
|
threads = args[:num_threads].blank? ? 1 : args[:num_threads].to_i
|
|
thread_sleep = args[:thread_sleep].blank? ? 0.25 : args[:thread_sleep].to_f
|
|
sleep = args[:sleep].blank? ? 1 : args[:sleep].to_i
|
|
statement_timeout = args[:statement_timeout].blank? ? 180000 : args[:statement_timeout]
|
|
|
|
puts "Running extension update with following config:"
|
|
puts "extension_version: #{extension_version.nil? ? 'UNSPECIFIED/LATEST' : extension_version}"
|
|
puts "database_host: #{database_host.nil? ? 'ALL' : database_host}"
|
|
puts "threads: #{threads}"
|
|
puts "thread_sleep: #{thread_sleep}"
|
|
puts "sleep: #{sleep}"
|
|
puts "statement_timeout: #{statement_timeout}"
|
|
|
|
if database_host.nil?
|
|
count = ::User.count
|
|
else
|
|
count = ::User.where(database_host: database_host).count
|
|
end
|
|
execute_on_users_with_index(task_name, Proc.new { |user, i|
|
|
begin
|
|
log(sprintf("Trying on %-#{20}s %-#{20}s (%-#{4}s/%-#{4}s)...", user.username, user.database_name, i+1, count), task_name, database_host)
|
|
user.db_service.load_cartodb_functions(statement_timeout, extension_version)
|
|
log(sprintf("OK %-#{20}s %-#{20}s (%-#{4}s/%-#{4}s)", user.username, user.database_name, i+1, count), task_name, database_host)
|
|
sleep(sleep)
|
|
rescue => e
|
|
log(sprintf("FAIL %-#{20}s (%-#{4}s/%-#{4}s) #{e.message}", user.username, i+1, count), task_name, database_host)
|
|
puts "FAIL:#{i} #{e.message}"
|
|
end
|
|
}, threads, thread_sleep, database_host)
|
|
end
|
|
|
|
desc 'Upgrade cartodb postgresql extension'
|
|
task :upgrade_postgres_extension, [:database_host, :version, :sleep, :statement_timeout] => :environment do |task_name, args|
|
|
# Send this as string, not as number
|
|
extension_version = args[:version]
|
|
database_host = args[:database_host]
|
|
sleep = args[:sleep].blank? ? 0.5 : args[:sleep].to_i
|
|
statement_timeout = args[:statement_timeout].blank? ? 180000 : args[:statement_timeout] # 3 min by default
|
|
|
|
puts "Upgrading cartodb extension with following config:"
|
|
puts "extension_version: #{extension_version || 'LATEST'}"
|
|
puts "database_host: #{database_host || 'ALL'}"
|
|
puts "sleep: #{sleep}"
|
|
puts "statement_timeout: #{statement_timeout}"
|
|
|
|
query = User
|
|
query = query.where(database_host: database_host) if database_host
|
|
|
|
count = query.count
|
|
|
|
query.order(Sequel.asc(:created_at)).each_with_index do |user, i|
|
|
begin
|
|
# We grant 2 x statement_timeout, by default 6 min
|
|
Timeout::timeout(statement_timeout/1000 * 2) do
|
|
log(sprintf("Trying on %-#{20}s %-#{20}s (%-#{4}s/%-#{4}s)...", user.username, user.database_name, i+1, count), task_name, database_host)
|
|
user.db_service.upgrade_cartodb_postgres_extension(statement_timeout, extension_version)
|
|
log(sprintf("OK %-#{20}s %-#{20}s (%-#{4}s/%-#{4}s)", user.username, user.database_name, i+1, count), task_name, database_host)
|
|
end
|
|
rescue => e
|
|
log(sprintf("FAIL %-#{20}s (%-#{4}s/%-#{4}s) #{e.message}", user.username, i+1, count), task_name, database_host)
|
|
puts "FAIL:#{i} #{e.message}"
|
|
end
|
|
sleep(sleep)
|
|
end
|
|
end
|
|
|
|
|
|
desc 'Install/upgrade Varnish trigger for a single user'
|
|
task :load_varnish_trigger_user, [:username] => :environment do |t, args|
|
|
user = ::User.find(username: args[:username])
|
|
user.db_service.create_function_invalidate_varnish
|
|
end
|
|
|
|
desc 'Move user to its own schema'
|
|
task :move_user_to_schema, [:username] => :environment do |t, args|
|
|
user = ::User.find(username: args[:username])
|
|
user.db_service.move_to_own_schema
|
|
user.db_service.setup_organization_user_schema
|
|
user.save
|
|
end
|
|
|
|
desc 'Install/upgrade Varnish invalidation trigger'
|
|
task :load_varnish_trigger, [:num_threads, :thread_sleep, :database_host, :sleep] => :environment do |t, args|
|
|
threads = args[:num_threads].blank? ? 1 : args[:num_threads].to_i
|
|
thread_sleep = args[:thread_sleep].blank? ? 0.1 : args[:thread_sleep].to_f
|
|
database_host = args[:database_host].blank? ? nil : args[:database_host]
|
|
sleep = args[:sleep].blank? ? 5 : args[:sleep].to_i
|
|
|
|
if database_host.nil?
|
|
count = ::User.count
|
|
else
|
|
count = ::User.where(database_host: database_host).count
|
|
end
|
|
execute_on_users_with_index(:load_varnish_trigger.to_s, Proc.new { |user, i|
|
|
begin
|
|
user.db_service.create_function_invalidate_varnish
|
|
log(sprintf("OK %-#{20}s %-#{20}s (%-#{4}s/%-#{4}s)\n", user.username, user.database_name, i+1, count), :load_varnish_trigger.to_s, database_host)
|
|
sleep(sleep)
|
|
rescue => e
|
|
log(sprintf("FAIL %-#{20}s (%-#{4}s/%-#{4}s) #{e.message}\n", user.username, i+1, count), :load_varnish_trigger.to_s, database_host)
|
|
puts "FAIL:#{i} #{e.message}"
|
|
end
|
|
}, threads, thread_sleep, database_host)
|
|
end
|
|
|
|
######################################
|
|
# GRANT `publicuser` ROLE TO ALL USERS
|
|
######################################
|
|
desc 'Grant `publicuser` role to all users'
|
|
task :grant_publicuser_to_all_users => :environment do
|
|
Carto::User.pluck(:id).each do |user_id|
|
|
user = Carto::User.where(id: user_id).first
|
|
# already granted users will raise a NOTICE
|
|
grant_query = "GRANT publicuser to \"#{user.database_username}\""
|
|
begin
|
|
conn = user.in_database(as: :cluster_admin)
|
|
conn.execute(grant_query)
|
|
rescue => e
|
|
log("Failed to execute `#{grant_query}`", :grant_publicuser_to_all_users.to_s, user.database_host)
|
|
ensure
|
|
conn.close unless conn.nil?
|
|
end
|
|
end
|
|
end
|
|
|
|
##########################################
|
|
# SET ORGANIZATION GROUP ROLE TO ALL USERS
|
|
##########################################
|
|
desc 'Set organization member group role'
|
|
task :set_user_as_organization_member => :environment do
|
|
::User.all.each do |user|
|
|
next if !user.respond_to?('database_name') || user.database_name.blank?
|
|
user.db_service.set_user_as_organization_member
|
|
end
|
|
end
|
|
|
|
##############
|
|
# SET DB PERMS
|
|
##############
|
|
desc "Set/Fix DB Permissions"
|
|
task set_permissions: :environment do
|
|
::User.all.each do |user|
|
|
next if !user.respond_to?('database_name') || user.database_name.blank?
|
|
# !!! WARNING
|
|
# This will delete all database permissions, and try to recreate them from scratch.
|
|
# Use only if you know what you're doing. (or, better, don't use it)
|
|
user.db_service.reset_database_permissions
|
|
user.db_service.reset_user_schema_permissions
|
|
user.db_service.grant_publicuser_in_database
|
|
user.db_service.set_user_privileges_at_db
|
|
user.db_service.fix_table_permissions
|
|
end
|
|
end
|
|
|
|
desc 'Set user privileges in CartoDB schema and CDB_TableMetadata'
|
|
task :set_user_privileges_in_cartodb_schema, [:username] => :environment do |t, args|
|
|
user = ::User.find(username: args[:username])
|
|
user.db_service.set_user_privileges_in_cartodb_schema
|
|
end
|
|
|
|
desc 'Set all user privileges'
|
|
task :set_all_user_privileges, [:username] => :environment do |t, args|
|
|
user = ::User.find(username: args[:username])
|
|
user.db_service.grant_user_in_database
|
|
user.db_service.set_user_privileges_at_db
|
|
end
|
|
|
|
desc 'Set org role privileges in all organizations'
|
|
task :set_org_privileges_in_cartodb_schema, [:org_name] => :environment do |_t, args|
|
|
org = ::Organization.find(name: args[:org_name])
|
|
owner = org.owner
|
|
if owner
|
|
owner.db_service.setup_organization_role_permissions
|
|
else
|
|
puts 'Organization without owner'
|
|
end
|
|
end
|
|
|
|
desc 'Set org role privileges in all organizations'
|
|
task set_all_orgs_privileges_in_cartodb_schema: :environment do |_t, _args|
|
|
Organization.each do |org|
|
|
owner = org.owner
|
|
if owner
|
|
owner.db_service.setup_organization_role_permissions
|
|
else
|
|
puts "Organization without owner: #{org.name}"
|
|
end
|
|
end
|
|
end
|
|
|
|
##########################
|
|
# SET TRIGGER CHECK QUOTA
|
|
##########################
|
|
desc 'reset check quota trigger on all user tables'
|
|
task :reset_trigger_check_quota, [:start] => :environment do |_, args|
|
|
start = args[:start]
|
|
puts "Resetting check quota trigger for ##{::User.count} users"
|
|
i = 0
|
|
|
|
query = ::User.order(:id)
|
|
query = query.where('id > ?', start) if start
|
|
query.use_cursor(rows_per_fetch: 500).each do |user|
|
|
begin
|
|
puts "Setting user quota in db '#{user.database_name}' (#{user.id} #{user.username})"
|
|
user.db_service.rebuild_quota_trigger
|
|
rescue => exception
|
|
puts "\nERRORED #{user.id} (#{user.username}): #{exception.message}\n"
|
|
end
|
|
|
|
i += 1
|
|
if (i % 500).zero?
|
|
puts "\nProcessed ##{i} users"
|
|
end
|
|
end
|
|
end
|
|
|
|
desc 'reset check quota trigger for a given user'
|
|
task :reset_trigger_check_quota_for_user, [:username] => :environment do |t, args|
|
|
raise 'usage: rake cartodb:db:reset_trigger_check_quota_for_user[username]' if args[:username].blank?
|
|
puts "Resetting trigger check quota for user '#{args[:username]}'"
|
|
user = ::User.filter(:username => args[:username]).first
|
|
user.db_service.rebuild_quota_trigger
|
|
end
|
|
|
|
desc "set users quota to amount in mb"
|
|
task :set_user_quota, [:username, :quota_in_mb] => :environment do |t, args|
|
|
usage = 'usage: rake cartodb:db:set_user_quota[username,quota_in_mb]'
|
|
raise usage if args[:username].blank? || args[:quota_in_mb].blank?
|
|
|
|
user = ::User.filter(:username => args[:username]).first
|
|
quota = args[:quota_in_mb].to_i * 1024 * 1024
|
|
user.update(:quota_in_bytes => quota)
|
|
|
|
puts "Setting user quota in db '#{user.database_name}' (#{user.username})"
|
|
user.db_service.rebuild_quota_trigger
|
|
|
|
puts "User: #{user.username} quota updated to: #{args[:quota_in_mb]}MB. #{user.tables.count} tables updated."
|
|
end
|
|
|
|
|
|
###############
|
|
# SET ORG QUOTA
|
|
###############
|
|
desc "set organization quota to amount in GB"
|
|
task :set_organization_quota, [:organization_name, :quota_in_gb] => :environment do |t, args|
|
|
usage = 'usage: rake cartodb:db:set_organization_quota[organization_name,quota_in_gb]'
|
|
raise usage if args[:organization_name].blank? || args[:quota_in_gb].blank?
|
|
|
|
organization = Organization.filter(:name=> args[:organization_name]).first
|
|
quota = args[:quota_in_gb].to_i * 1024 * 1024 * 1024
|
|
organization.quota_in_bytes = quota
|
|
organization.save
|
|
|
|
puts "Organization: #{organization.name} quota updated to: #{args[:quota_in_gb]}GB."
|
|
end
|
|
|
|
desc "set organization seats"
|
|
task :set_organization_seats, [:organization_name, :seats] => :environment do |t, args|
|
|
usage = 'usage: rake cartodb:db:set_organization_seats[organization_name,seats]'
|
|
raise usage if args[:organization_name].blank? || args[:seats].blank?
|
|
|
|
organization = Organization.filter(:name=> args[:organization_name]).first
|
|
seats = args[:seats].to_i
|
|
organization.seats = seats
|
|
organization.save
|
|
|
|
puts "Organization: #{organization.name} seats updated to: #{args[:seats]}."
|
|
end
|
|
|
|
desc "set organization viewer_seats"
|
|
task :set_organization_viewer_seats, [:organization_name, :seats] => :environment do |_, args|
|
|
usage = 'usage: rake cartodb:db:set_organization_viewer_seats[organization_name,seats]'
|
|
raise usage if args[:organization_name].blank? || args[:seats].blank?
|
|
|
|
organization = Organization.filter(name: args[:organization_name]).first
|
|
seats = args[:seats].to_i
|
|
organization.viewer_seats = seats
|
|
organization.save
|
|
|
|
puts "Organization: #{organization.name} seats updated to: #{args[:seats]}."
|
|
end
|
|
|
|
#################
|
|
# SET TABLE QUOTA
|
|
#################
|
|
desc "set users table quota"
|
|
task :set_user_table_quota, [:username, :table_quota] => :environment do |t, args|
|
|
usage = "usage: rake cartodb:db:set_user_table_quota[username,table_quota]"
|
|
raise usage if args[:username].blank? || args[:table_quota].blank?
|
|
|
|
user = ::User.filter(:username => args[:username]).first
|
|
user.update(:table_quota => args[:table_quota].to_i)
|
|
|
|
puts "User: #{user.username} table quota updated to: #{args[:table_quota]}"
|
|
end
|
|
|
|
desc "set unlimited table quota"
|
|
task :set_unlimited_table_quota, [:username] => :environment do |t, args|
|
|
usage = "usage: rake cartodb:db:set_unlimited_table_quota[username]"
|
|
raise usage if args[:username].blank?
|
|
|
|
user = ::User.filter(:username => args[:username]).first
|
|
user.update(:table_quota => nil)
|
|
|
|
puts "User: #{user.username} table quota updated to: unlimited"
|
|
end
|
|
|
|
|
|
desc "reset Users table quota to 5"
|
|
task :set_all_users_to_free_table_quota => :environment do
|
|
::User.all.each do |user|
|
|
next if !user.respond_to?('database_name') || user.database_name.blank?
|
|
user.update(:table_quota => 5) if user.table_quota.blank?
|
|
end
|
|
end
|
|
|
|
|
|
##################
|
|
# SET ACCOUNT TYPE
|
|
##################
|
|
desc "Set users account type. DEDICATED or FREE"
|
|
task :set_user_account_type, [:username, :account_type] => :environment do |t, args|
|
|
usage = "usage: rake cartodb:db:set_user_account_type[username,account_type]"
|
|
raise usage if args[:username].blank? || args[:account_type].blank?
|
|
|
|
user = ::User.filter(:username => args[:username]).first
|
|
user.update(:account_type => args[:account_type])
|
|
|
|
puts "User: #{user.username} table account type updated to: #{args[:account_type]}"
|
|
end
|
|
|
|
desc "reset all Users account type to FREE"
|
|
task :set_all_users_account_type_to_free => :environment do
|
|
::User.all.each do |user|
|
|
next if !user.respond_to?('database_name') || user.database_name.blank?
|
|
user.update(:account_type => 'FREE') if user.account_type.blank?
|
|
end
|
|
end
|
|
|
|
|
|
##########################################
|
|
# SET USER PRIVATE TABLES ENABLED/DISABLED
|
|
##########################################
|
|
desc "set users private tables enabled"
|
|
task :set_user_private_tables_enabled, [:username, :private_tables_enabled] => :environment do |t, args|
|
|
usage = "usage: rake cartodb:db:set_user_private_tables_enabled[username,private_tables_enabled]"
|
|
raise usage if args[:username].blank? || args[:private_tables_enabled].blank?
|
|
|
|
user = ::User.filter(:username => args[:username]).first
|
|
user.update(:private_tables_enabled => args[:private_tables_enabled])
|
|
|
|
puts "User: #{user.username} private tables enabled: #{args[:private_tables_enabled]}"
|
|
end
|
|
|
|
desc "reset all Users privacy tables permissions type to false"
|
|
task :set_all_users_private_tables_enabled_to_false => :environment do
|
|
::User.all.each do |user|
|
|
next if !user.respond_to?('database_name') || user.database_name.blank?
|
|
user.update(:private_tables_enabled => false) if user.private_tables_enabled.blank?
|
|
end
|
|
end
|
|
|
|
|
|
##########################
|
|
# REBUILD GEOM WEBMERCATOR
|
|
##########################
|
|
desc "Add the_geom_webmercator column to every table which needs it"
|
|
task :add_the_geom_webmercator => :environment do
|
|
::User.all.each do |user|
|
|
tables = Table.filter(:user_id => user.id).all
|
|
next if tables.empty?
|
|
user.db_service.load_cartodb_functions
|
|
puts "Updating tables in db '#{user.database_name}' (#{user.username})"
|
|
tables.each do |table|
|
|
has_the_geom = false
|
|
user.in_database do |user_database|
|
|
begin
|
|
flatten_schema = user_database.schema(table.name.to_sym).flatten
|
|
rescue => e
|
|
puts " Skipping table #{table.name}: #{e}"
|
|
next
|
|
end
|
|
has_the_geom = true if flatten_schema.include?(:the_geom)
|
|
if flatten_schema.include?(:the_geom) && !flatten_schema.include?(Table::THE_GEOM_WEBMERCATOR.to_sym)
|
|
puts " Updating table #{table.name}"
|
|
geometry_type = if col = user_database["select GeometryType(the_geom) FROM #{table.name} limit 1"].first
|
|
col[:geometrytype]
|
|
end
|
|
geometry_type ||= "POINT"
|
|
user_database.run("SELECT public.AddGeometryColumn('#{user.database_schema}','#{table.name}','#{Table::THE_GEOM_WEBMERCATOR}',3857,'#{geometry_type}',2)")
|
|
user_database.run("CREATE INDEX #{table.name}_#{Table::THE_GEOM_WEBMERCATOR}_idx ON #{table.name} USING GIST(#{Table::THE_GEOM_WEBMERCATOR})")
|
|
user_database.run("ANALYZE #{table.name}")
|
|
table.save_changes
|
|
else
|
|
puts " Skipping table #{table.name}: does not have 'the_geom' or has '#{Table::THE_GEOM_WEBMERCATOR}' already"
|
|
end
|
|
end
|
|
if has_the_geom
|
|
table.set_trigger_the_geom_webmercator
|
|
|
|
user.in_database do |user_database|
|
|
user_database.run("ALTER TABLE #{table.name} DROP CONSTRAINT IF EXISTS enforce_srid_the_geom")
|
|
user_database.run("update #{table.name} set \"#{Table::THE_GEOM_WEBMERCATOR}\" = CDB_TransformToWebmercator(the_geom)")
|
|
user_database.run("ALTER TABLE #{table.name} ADD CONSTRAINT enforce_srid_the_geom CHECK (st_srid(the_geom) = #{CartoDB::SRID})")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
desc "Update test_quota trigger"
|
|
task :update_test_quota_trigger => :environment do
|
|
::User.all.each do |user|
|
|
puts "Setting user quota in db '#{user.database_name}' (#{user.username})"
|
|
user.db_service.rebuild_quota_trigger
|
|
end
|
|
end
|
|
|
|
desc "Recreates all table triggers for non-orgs users. Optionally you can pass a username param to do this only for one user. Example: cartodb:db:recreate_table_triggers['my_username']"
|
|
task :recreate_table_triggers, [:username] => :environment do |t, args|
|
|
username = args[:username]
|
|
|
|
users = username.nil? ? ::User.where('organization_id IS NOT NULL') : ::User.where(username: username)
|
|
users.each do |user|
|
|
if user.db_service.cartodb_extension_version_pre_mu? || user.database_schema=='public'
|
|
puts "SKIP: #{user.username} / #{user.id}"
|
|
else
|
|
schema_name = user.database_schema
|
|
UserTable.filter(:user_id => user.id).each do |table|
|
|
table_name = "#{user.database_schema}.#{table.name}"
|
|
begin
|
|
user.in_database do |user_db|
|
|
user_db.run(%Q{
|
|
SELECT cartodb._CDB_drop_triggers('#{table_name}'::REGCLASS);
|
|
})
|
|
user_db.run(%Q{
|
|
SELECT cartodb._CDB_create_triggers('#{schema_name}'::TEXT, '#{table_name}'::REGCLASS);
|
|
})
|
|
end
|
|
rescue => exception
|
|
puts "ERROR: #{user.username} / #{user.id} : #{table_name} #{exception}"
|
|
end
|
|
end
|
|
puts "DONE: #{user.username} / #{user.id}"
|
|
end
|
|
end
|
|
puts 'Finished'
|
|
end
|
|
|
|
desc "Update update_the_geom_webmercator_trigger"
|
|
task :update_the_geom_webmercator_trigger => :environment do
|
|
::User.all.each do |user|
|
|
user.db_service.load_cartodb_functions
|
|
|
|
tables = Table.filter(:user_id => user.id).all
|
|
next if tables.empty?
|
|
puts "Updating tables in db '#{user.database_name}' (#{user.username})"
|
|
tables.each do |table|
|
|
has_the_geom = false
|
|
user.in_database do |user_database|
|
|
begin
|
|
has_the_geom = true if user_database.schema(table.name.to_sym).flatten.include?(:the_geom)
|
|
rescue => e
|
|
puts " Skipping table #{table.name}: #{e}"
|
|
next
|
|
end
|
|
end
|
|
if has_the_geom
|
|
puts " Updating the_geom_webmercator triggers for table #{table.name}"
|
|
table.set_trigger_the_geom_webmercator
|
|
else
|
|
puts " Skipping table #{table.name}: no 'the_geom' column"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
desc "update created_at and updated_at to correct type and add the default value to now"
|
|
task :update_timestamp_fields => :environment do
|
|
::User.all.each do |user|
|
|
next if !user.respond_to?('database_name') || user.database_name.blank?
|
|
puts "user => " + user.username
|
|
user.in_database do |user_database|
|
|
user.tables.all.each do |table|
|
|
table.normalize_timestamp_field!(:created_at, user_database)
|
|
table.normalize_timestamp_field!(:updated_at, user_database)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
desc 'update the old cache trigger which was using redis to the varnish one'
|
|
task :update_cache_trigger => :environment do
|
|
::User.all.each do |user|
|
|
puts 'Update cache trigger => ' + user.username
|
|
next if !user.respond_to?('database_name') || user.database_name.blank?
|
|
user.in_database do |user_database|
|
|
user.tables.all.each do |table|
|
|
puts "\t=> #{table.name} updated"
|
|
begin
|
|
table.set_trigger_cache_timestamp
|
|
rescue => e
|
|
puts "\t=> [ERROR] #{table.name}: #{e.inspect}"
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
desc 'Save users metadata in redis'
|
|
task :save_users_metadata => :environment do
|
|
::User.all.each do |u|
|
|
u.save_metadata
|
|
end
|
|
end
|
|
|
|
desc 'Create public users for users beloging to an organization'
|
|
task :create_organization_members_public_users => :environment do
|
|
::User.exclude(organization_id: nil).each do |user|
|
|
begin
|
|
user.db_service.create_public_db_user
|
|
user.save_metadata
|
|
rescue
|
|
puts "user #{user.username} already has the public user"
|
|
end
|
|
end
|
|
end
|
|
|
|
desc 'Setup default permissions on existing visualizations'
|
|
task :create_default_vis_permissions, [:page_size, :page] => :environment do |t, args|
|
|
page_size = args[:page_size].blank? ? 999999 : args[:page_size].to_i
|
|
page = args[:page].blank? ? 1 : args[:page].to_i
|
|
|
|
progress_each = (page_size > 10) ? (page_size / 10).ceil : 1
|
|
|
|
begin
|
|
items = Carto::VisualizationQueryBuilder.new.build_paged(page, page_size)
|
|
|
|
count = items.count
|
|
puts "\n>Running :create_default_vis_permissions for page #{page} (#{count} vis)" if count > 0
|
|
items.each do |vis|
|
|
if vis.permission_id.nil?
|
|
begin
|
|
raise 'No owner' if vis.user.nil?
|
|
# Just saving will trigger the permission creation
|
|
vis.save!
|
|
puts "OK #{vis.id}"
|
|
rescue => e
|
|
owner_id = vis.user.nil? ? 'nil' : vis.user.id
|
|
message = "FAIL u:#{owner_id} v:#{vis.id}: #{e.message}"
|
|
puts message
|
|
log(message, :create_default_vis_permissions.to_s)
|
|
end
|
|
end
|
|
end
|
|
page += 1
|
|
end while count > 0
|
|
|
|
puts "\n>Finished :create_default_vis_permissions"
|
|
end
|
|
|
|
# Executes a ruby code proc/block on all existing users, outputting some info
|
|
# @param task_name string
|
|
# @param block Proc
|
|
# @example:
|
|
# execute_on_users_with_index(:populate_new_fields.to_s, Proc.new { |user, i| ... })
|
|
def execute_on_users_with_index(task_name, block, num_threads=1, sleep_time=0.1, database_host=nil)
|
|
if database_host.nil?
|
|
count = ::User.count
|
|
else
|
|
count = ::User.where(database_host: database_host).count
|
|
end
|
|
|
|
start_message = ">Running #{task_name} for #{count} users"
|
|
puts start_message
|
|
log(start_message, task_name, database_host)
|
|
if database_host.nil?
|
|
puts "Detailed log stored at log/rake_db_maintenance_#{task_name}.log"
|
|
else
|
|
puts "Detailed log stored at log/rake_db_maintenance_#{task_name}_#{database_host}.log"
|
|
end
|
|
|
|
thread_pool = ThreadPool.new(num_threads, sleep_time)
|
|
|
|
if database_host.nil?
|
|
::User.order(Sequel.asc(:created_at)).each_with_index do |user, i|
|
|
thread_pool.schedule do
|
|
if i % 100 == 0
|
|
puts "PROGRESS: #{i}/#{count} users queued"
|
|
end
|
|
block.call(user, i)
|
|
end
|
|
end
|
|
else
|
|
::User.where(database_host: database_host).order(Sequel.asc(:created_at)).each_with_index do |user, i|
|
|
thread_pool.schedule do
|
|
if i % 100 == 0
|
|
puts "PROGRESS: #{i}/#{count} users queued"
|
|
end
|
|
block.call(user, i)
|
|
end
|
|
end
|
|
end
|
|
|
|
at_exit { thread_pool.shutdown }
|
|
|
|
puts "PROGRESS: #{count}/#{count} users queued"
|
|
end_message = "\n>Finished #{task_name}\n"
|
|
puts end_message
|
|
log(end_message, task_name, database_host)
|
|
end
|
|
|
|
def log(entry, task_name, filename_suffix='')
|
|
if filename_suffix.nil? || filename_suffix.empty?
|
|
log_path = Rails.root.join('log', "rake_db_maintenance_#{task_name}.log")
|
|
else
|
|
log_path = Rails.root.join('log', "rake_db_maintenance_#{task_name}_#{filename_suffix}.log")
|
|
end
|
|
File.open(log_path, 'a') do |file_handle|
|
|
file_handle.puts "[#{Time.now}] #{entry}\n"
|
|
end
|
|
end
|
|
|
|
desc 'Load api calls from ES to redis'
|
|
task :load_api_calls_from_es => :environment do
|
|
raise "You should provide a valid username" if ENV['USERNAME'].blank?
|
|
u = ::User.where(:username => ENV['USERNAME']).first
|
|
puts "Old API Calls from ES: #{u.get_es_api_calls_from_redis}"
|
|
u.set_api_calls_from_es({:force_update => true})
|
|
puts "New API Calls from ES: #{u.get_es_api_calls_from_redis}"
|
|
end
|
|
|
|
desc "Create new organization with owner"
|
|
task :create_new_organization_with_owner => :environment do
|
|
raise "You should provide a ORGANIZATION_NAME" if ENV['ORGANIZATION_NAME'].blank?
|
|
raise "You should provide a ORGANIZATION_DISPLAY_NAME" if ENV['ORGANIZATION_DISPLAY_NAME'].blank?
|
|
raise "You should provide a ORGANIZATION_SEATS" if ENV['ORGANIZATION_SEATS'].blank?
|
|
raise "You should provide a ORGANIZATION_QUOTA (in Bytes)" if ENV['ORGANIZATION_QUOTA'].blank?
|
|
raise "You should provide a USERNAME" if ENV['USERNAME'].blank?
|
|
user = ::User.where(:username => ENV['USERNAME']).first
|
|
raise "User #{ENV['USERNAME']} does not exist" if user.nil?
|
|
organization = Organization.where(:name => ENV['ORGANIZATION_NAME']).first
|
|
if organization.nil?
|
|
organization = Organization.new
|
|
organization.name = ENV['ORGANIZATION_NAME']
|
|
organization.display_name = ENV['ORGANIZATION_DISPLAY_NAME']
|
|
organization.seats = ENV['ORGANIZATION_SEATS']
|
|
organization.quota_in_bytes = ENV['ORGANIZATION_QUOTA']
|
|
if ENV['BUILDER_ENABLED'] == "true"
|
|
organization.builder_enabled = true
|
|
end
|
|
organization.save
|
|
end
|
|
uo = CartoDB::UserOrganization.new(organization.id, user.id)
|
|
uo.promote_user_to_admin
|
|
end
|
|
|
|
desc "Create new organization without owner"
|
|
task :create_new_organization_without_owner => :environment do
|
|
raise "You should provide a ORGANIZATION_NAME" if ENV['ORGANIZATION_NAME'].blank?
|
|
raise "You should provide a ORGANIZATION_DISPLAY_NAME" if ENV['ORGANIZATION_DISPLAY_NAME'].blank?
|
|
raise "You should provide a ORGANIZATION_SEATS" if ENV['ORGANIZATION_SEATS'].blank?
|
|
raise "You should provide a ORGANIZATION_QUOTA (in Bytes)" if ENV['ORGANIZATION_QUOTA'].blank?
|
|
|
|
organization = Organization.where(:name => ENV['ORGANIZATION_NAME']).first
|
|
if organization.nil?
|
|
organization = Organization.new
|
|
organization.name = ENV['ORGANIZATION_NAME']
|
|
organization.display_name = ENV['ORGANIZATION_DISPLAY_NAME']
|
|
organization.seats = ENV['ORGANIZATION_SEATS']
|
|
organization.quota_in_bytes = ENV['ORGANIZATION_QUOTA']
|
|
if ENV['BUILDER_ENABLED'] == "true"
|
|
organization.builder_enabled = true
|
|
end
|
|
organization.save
|
|
end
|
|
end
|
|
|
|
def create_user(username, organization, quota_in_bytes)
|
|
u = ::User.new
|
|
u.email = "#{username}@test-org.com"
|
|
u.password = username
|
|
u.password_confirmation = username
|
|
u.username = username
|
|
u.quota_in_bytes = quota_in_bytes
|
|
#u.database_host = ::SequelRails.configuration.environment_for(Rails.env)['host']
|
|
|
|
if organization.owner_id.nil?
|
|
u.save(raise_on_failure: true)
|
|
CartoDB::UserOrganization.new(organization.id, u.id).promote_user_to_admin
|
|
organization.reload
|
|
else
|
|
u.organization = organization
|
|
u.save(raise_on_failure: true)
|
|
end
|
|
|
|
u
|
|
end
|
|
|
|
def create_users(first_index, last_index, organization, bytes_per_user)
|
|
org_name = organization.name
|
|
|
|
(first_index..last_index).each { |i|
|
|
username = "#{org_name}-#{i}"
|
|
print "Creating user #{username}... "
|
|
user = create_user(username, organization, bytes_per_user)
|
|
puts "Done."
|
|
}
|
|
end
|
|
|
|
desc "Create an organization with an arbitrary number of users for test purposes. Owner user: <org-name>-admin. Users: <org-name>-<i>. You might need to set conn_validator_timeout to -1 in config/database.yml (development)"
|
|
task :create_test_organization, [:org_name, :n_users] => [:environment] do |t, args|
|
|
org_name = args[:org_name]
|
|
n_users = args[:n_users].to_i
|
|
bytes_per_user = 50 * 1024 * 1024
|
|
|
|
def create_organization(org_name, n_users, bytes_per_user)
|
|
organization = Organization.new
|
|
organization.name = org_name
|
|
organization.display_name = org_name
|
|
organization.seats = n_users * 2
|
|
organization.quota_in_bytes = bytes_per_user * organization.seats
|
|
organization.save
|
|
end
|
|
|
|
print "Creating organization #{org_name}... "
|
|
organization = create_organization(org_name, n_users, bytes_per_user)
|
|
puts "Done."
|
|
|
|
owner_name = "#{org_name}-admin"
|
|
print "Creating owner #{owner_name}... "
|
|
owner = create_user(owner_name, organization, bytes_per_user)
|
|
puts "Done."
|
|
|
|
create_users(1, n_users, organization, bytes_per_user)
|
|
end
|
|
|
|
desc "Add users to an organization. Used to resume a broken :create_test_organization task"
|
|
task :add_test_users_to_organization, [:org_name, :first_index, :last_index] => [:environment] do |t, args|
|
|
org_name = args[:org_name]
|
|
first_index = args[:first_index]
|
|
last_index = args[:last_index]
|
|
bytes_per_user = 50 * 1024 * 1024
|
|
|
|
organization = Organization.where(name: org_name).first
|
|
create_users(first_index, last_index, organization, bytes_per_user)
|
|
end
|
|
|
|
desc "Reload users avatars"
|
|
task :reload_users_avatars => :environment do
|
|
if ENV['ONLY_GRAVATAR'].blank?
|
|
users = ::User.all
|
|
else
|
|
users = ::User.where(Sequel.like(:avatar_url, '%gravatar.com%'))
|
|
end
|
|
count = users.count
|
|
users.each_with_index do |user, i|
|
|
begin
|
|
user.reload_avatar
|
|
message = "OK %-#{20}s (%-#{4}s/%-#{4}s)\n" % [user.username, i, count]
|
|
print message
|
|
log(message, :reload_users_avatars.to_s)
|
|
rescue => e
|
|
message = "FAIL %-#{20}s (%-#{4}s/%-#{4}s) #{e.message}\n" % [user.username, i, count]
|
|
print message
|
|
log(message, :reload_users_avatars.to_s)
|
|
end
|
|
end
|
|
end
|
|
|
|
desc "Grant general raster permissions"
|
|
task :grant_general_raster_permissions => :environment do
|
|
users = ::User.all
|
|
count = users.count
|
|
users.each_with_index do |user, i|
|
|
begin
|
|
user.db_service.set_raster_privileges
|
|
message = "OK %-#{20}s (%-#{4}s/%-#{4}s)\n" % [user.username, i, count]
|
|
print message
|
|
log(message, :grant_general_raster_permissions.to_s)
|
|
rescue => e
|
|
message = "FAIL %-#{20}s (%-#{4}s/%-#{4}s) MSG:#{e.message}\n" % [user.username, i, count]
|
|
print message
|
|
log(message, :grant_general_raster_permissions.to_s)
|
|
end
|
|
end
|
|
end
|
|
|
|
desc "Drop other users privileges on user schema"
|
|
task :drop_other_privileges_on_user_schema, [:username] => :environment do |t,args|
|
|
user = ::User.where(:username => args[:username].to_s).first
|
|
user.in_database({as: :superuser}) do |db|
|
|
db.transaction do
|
|
oids = db.fetch("with oids as (select (aclexplode(n.nspacl)).grantee as grantee_oid from pg_catalog.pg_namespace n
|
|
WHERE n.nspname = '#{user.database_schema}')
|
|
select distinct pg_roles.rolname from oids, pg_roles where grantee_oid = oid")
|
|
oids.each do |oid|
|
|
role = oid[:rolname]
|
|
unless [user.database_username, CartoDB::PUBLIC_DB_USER].include? role
|
|
db.run("REVOKE ALL ON SCHEMA #{user.database_schema} FROM \"#{role}\"")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
desc "Enable oracle_fdw extension in database"
|
|
task :enable_oracle_fdw_extension, [:username, :oracle_url, :remote_user, :remote_password, :remote_schema, :table_definition_json_path] => :environment do |t, args|
|
|
u = ::User.where(:username => args[:username].to_s).first
|
|
tables = JSON.parse(File.read(args['table_definition_json_path'].to_s))
|
|
u.in_database({as: :superuser, no_cartodb_in_schema: true}) do |db|
|
|
db.transaction do
|
|
server_name = "oracle_#{args[:oracle_url].sanitize}_#{Time.now.to_i}"
|
|
db.run('CREATE EXTENSION oracle_fdw') unless db.fetch(%Q{
|
|
SELECT count(*) FROM pg_extension WHERE extname='oracle_fdw'
|
|
}).first[:count] > 0
|
|
db.run("CREATE SERVER #{server_name} FOREIGN DATA WRAPPER oracle_fdw OPTIONS (dbserver '#{args[:oracle_url].to_s}')")
|
|
db.run("GRANT USAGE ON FOREIGN SERVER #{server_name} TO \"#{u.database_username}\"")
|
|
db.run("CREATE USER MAPPING FOR \"#{u.database_username}\" SERVER #{server_name} OPTIONS (user '#{args[:remote_user].to_s}', password '#{args[:remote_password].to_s}');")
|
|
db.run("CREATE USER MAPPING FOR \"#{u.database_public_username}\" SERVER #{server_name} OPTIONS (user '#{args[:remote_user].to_s}', password '#{args[:remote_password].to_s}');")
|
|
tables["tables"].each do |table_name, th|
|
|
table_readonly = th["read_only"] ? "true" : "false"
|
|
table_columns = th["columns"].map {|name,attrs| "#{name} #{attrs['column_type']}"}
|
|
db.run("DROP FOREIGN TABLE IF EXISTS #{table_name}")
|
|
db.run("CREATE FOREIGN TABLE #{table_name} (#{table_columns.join(', ')}) SERVER #{server_name} OPTIONS (schema '#{args[:remote_schema]}', table '#{th["remote_table"]}', readonly '#{table_readonly}')")
|
|
db.run("GRANT SELECT ON #{table_name} TO \"#{u.database_username}\"")
|
|
db.run("GRANT SELECT ON #{table_name} TO \"#{CartoDB::PUBLIC_DB_USER}\"")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
desc "Get from redis the list of visualization ids viewed"
|
|
task :get_viewed_visualization_ids, [:redis_ip, :output_file] => :environment do |t, args|
|
|
if args[:redis_ip]
|
|
redis_client_config = $users_metadata.client.options
|
|
redis_client_config[:host] = args[:redis_ip]
|
|
redis_client = Redis.new(redis_client_config)
|
|
else
|
|
redis_client = $users_metadata
|
|
end
|
|
|
|
stat_tag_keys = [
|
|
"user:*:mapviews:stat_tag:*",
|
|
"user:*:mapviews_es:stat_tag:*"
|
|
]
|
|
|
|
visualization_ids = []
|
|
|
|
stat_tag_keys.each do |stat_tag_key|
|
|
redis_client.keys(stat_tag_key).each do |key|
|
|
key_parts = key.split(':')
|
|
visualization_ids << "#{key_parts[1]}:#{key_parts[4]}"
|
|
end
|
|
end
|
|
|
|
visualization_ids.uniq!
|
|
|
|
if args[:output_file]
|
|
File.write(args[:output_file], visualization_ids.join("\n"))
|
|
else
|
|
puts visualization_ids.join("\n")
|
|
end
|
|
end
|
|
|
|
|
|
desc "Populate visualization total map views from partial days"
|
|
task :populate_visualization_total_map_views, [:visualizations_list] => :environment do |t, args|
|
|
if args[:visualizations_list].blank?
|
|
raise "Missing visualization_list param which must be a plain text list of <user>:<visualization_id>"
|
|
end
|
|
File.open(args[:visualizations_list], 'r') do |f|
|
|
f.each_line do |line|
|
|
key_parts = line.strip.split(':')
|
|
username = key_parts[0]
|
|
visualization_id = key_parts[1]
|
|
stat_tag_keys = [
|
|
"user:#{username}:mapviews:stat_tag:#{visualization_id}",
|
|
"user:#{username}:mapviews_es:stat_tag:#{visualization_id}"
|
|
]
|
|
puts "Processing visualization #{visualization_id} of user #{username}"
|
|
stat_tag_keys.each do |stat_tag_key|
|
|
visualization_counter = 0
|
|
$users_metadata.zrange(stat_tag_key, 0, -1).each do |mapviews_day|
|
|
if mapviews_day =~ /[0-9]{4}(0|1)[0-9][0-3][0-9]/
|
|
count = $users_metadata.zscore(stat_tag_key, mapviews_day)
|
|
visualization_counter = visualization_counter + count unless count.nil?
|
|
end
|
|
end
|
|
$users_metadata.zadd(stat_tag_key, visualization_counter, 'total') unless visualization_counter == 0
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
desc "Assign permissions to organization shared role. See #3859 and #3881. This is used to upgrade existing organizations to new permission schema. You can optionally speciy an organization name to restrict the execution to it."
|
|
task :assign_org_permissions_to_org_role, [:organization_name] => :environment do |t, args|
|
|
organizations = args[:organization_name].present? ? Organization.where(name: args[:organization_name]).all : Organization.all
|
|
puts "Updating #{organizations.count} organizations"
|
|
organizations.each { |o|
|
|
owner = o.owner
|
|
if owner
|
|
puts "#{o.name}\t#{o.id}\tOwner: #{owner.username}\t#{owner.id}"
|
|
begin
|
|
owner.db_service.setup_organization_role_permissions
|
|
rescue => e
|
|
puts "Error: #{e.message}"
|
|
CartoDB.notify_exception(e)
|
|
end
|
|
else
|
|
puts "#{o.name}\t#{o.id}\t Has no owner, skipping"
|
|
end
|
|
}
|
|
end
|
|
|
|
def run_for_organizations_owner(organizations)
|
|
puts "Updating #{organizations.count} organizations"
|
|
organizations.each { |o|
|
|
owner = o.owner
|
|
if owner
|
|
puts "#{o.id} Owner: #{owner.id} #{owner.username}\t\tName: #{o.name}"
|
|
begin
|
|
yield owner
|
|
rescue => e
|
|
puts "Error: #{e.message}"
|
|
CartoDB.notify_exception(e)
|
|
end
|
|
else
|
|
puts "#{o.name}\t#{o.id}\t Has no owner, skipping"
|
|
end
|
|
}
|
|
end
|
|
|
|
desc "Revokes access to cdb_conf"
|
|
task :revoke_cdb_conf_access => :environment do |t, args|
|
|
execute_on_users_with_index(:revoke_cdb_conf_access.to_s, Proc.new { |user, i|
|
|
errors = user.db_service.revoke_cdb_conf_access
|
|
if errors.empty?
|
|
puts "OK #{user.username}"
|
|
else
|
|
puts "ERROR #{user.username}: #{errors.join(';')}"
|
|
end
|
|
}, 1, 0.3)
|
|
end
|
|
|
|
desc "Assign organization owner admin role at database. See CartoDB/cartodb-postgresql#104 and #5187"
|
|
task :assign_org_owner_role, [:organization_name] => :environment do |t, args|
|
|
organizations = args[:organization_name].present? ? Organization.where(name: args[:organization_name]).all : Organization.all
|
|
run_for_organizations_owner(organizations) do |owner|
|
|
begin
|
|
owner.db_service.grant_admin_permissions
|
|
rescue => e
|
|
puts "ERROR for #{owner.organization.name}: #{e.message}"
|
|
end
|
|
end
|
|
end
|
|
|
|
desc "Configure extension org metadata API endpoint, the one used by the extension to keep groups synched. See CartoDB/cartodb-postgresql#104 and CartoDB/cartodb/issues/5244"
|
|
task :configure_extension_org_metadata_api_endpoint, [:organization_name] => :environment do |t, args|
|
|
organizations = args[:organization_name].present? ? Organization.where(name: args[:organization_name]).all : Organization.all
|
|
run_for_organizations_owner(organizations) do |owner|
|
|
begin
|
|
owner.db_service.configure_extension_org_metadata_api_endpoint
|
|
rescue => e
|
|
puts "ERROR for #{owner.organization.name}: #{e.message}"
|
|
end
|
|
end
|
|
end
|
|
|
|
# e.g. bundle exec rake cartodb:db:configure_geocoder_extension_for_organizations[org-name]
|
|
# bundle exec rake cartodb:db:configure_geocoder_extension_for_organizations['',true]
|
|
desc "Configure geocoder extension configuration for organizations"
|
|
task :configure_geocoder_extension_for_organizations, [:organization_name, :all_organizations] => :environment do |t, args|
|
|
args.with_defaults(:organization_name => nil, :all_organizations => false)
|
|
if args[:organization_name].blank? and args[:all_organizations] != 'true'
|
|
# Double check before launch an update to all the orgs
|
|
raise "ERROR: You haven't passed an organization name and/or put the :all_organizations flag to true"
|
|
end
|
|
organizations = args[:organization_name].blank? ? ::Organization.all : ::Organization.where(name: args[:organization_name]).all
|
|
raise "ERROR: Organization #{args[:organization_name]} don't exists" if organizations.blank? and not args[:all_organizations]
|
|
run_for_organizations_owner(organizations) do |owner|
|
|
begin
|
|
result = owner.db_service.install_and_configure_geocoder_api_extension
|
|
puts "Owner #{owner.username}: #{result ? 'OK' : 'ERROR'}"
|
|
# TODO Improved using the execute_on_users_with_index when orgs have a lot more users
|
|
owner.organization.users.each do |u|
|
|
if not u.organization_owner?
|
|
result = u.db_service.install_and_configure_geocoder_api_extension
|
|
puts "Organization user #{u.username}: #{result ? 'OK' : 'ERROR'}"
|
|
end
|
|
end
|
|
rescue => e
|
|
puts "Error trying to configure geocoder extension for org #{owner.organization.name}: #{e.message}"
|
|
end
|
|
end
|
|
end
|
|
|
|
# e.g. bundle exec rake cartodb:db:configure_geocoder_extension_for_non_org_users[username]
|
|
# bundle exec rake cartodb:db:configure_geocoder_extension_for_non_org_users['',true]
|
|
desc "Configure geocoder extension configuration for non-organization users"
|
|
task :configure_geocoder_extension_for_non_org_users, [:username, :all_users] => :environment do |t, args|
|
|
args.with_defaults(:username => nil, :all_users => false)
|
|
if args[:username].blank? and args[:all_users] != 'true'
|
|
# Double check before launch an update to all the orgs
|
|
raise "ERROR: You haven't passed an username and/or put the :all_users flag to true"
|
|
end
|
|
if not args[:username].blank?
|
|
user = ::User.where(username: args[:username]).first
|
|
raise "ERROR: User #{args[:username]} don't exists" if user.nil?
|
|
begin
|
|
result = user.db_service.install_and_configure_geocoder_api_extension
|
|
puts "#{result ? 'OK' : 'ERROR'} #{user.username}"
|
|
rescue => e
|
|
puts "Error trying to configure geocoder extension for user #{u.name}: #{e.message}"
|
|
end
|
|
elsif args[:all_users]
|
|
# TODO Could be improved passing the query to execute_on_users_with_index function to filter by non-org-users
|
|
execute_on_users_with_index(:configure_geocoder_extension_for_non_org_users.to_s, Proc.new { |user, i|
|
|
begin
|
|
if not user.organization_user?
|
|
result = user.db_service.install_and_configure_geocoder_api_extension
|
|
puts "#{result ? 'OK' : 'ERROR'} #{user.username}"
|
|
end
|
|
rescue => e
|
|
puts "Error trying to configure geocoder extension for user #{u.name}: #{e.message}"
|
|
end
|
|
}, 1, 0.3)
|
|
end
|
|
end
|
|
|
|
# e.g. bundle exec rake cartodb:db:migrate_current_geocoder_billing_to_redis[YYYYMM,YYYYMMDD]
|
|
desc 'Migrate the current billing geocoding data to Redis'
|
|
task :migrate_current_geocoder_billing_to_redis, [:date_from, :date_to] => [:environment] do |task, args|
|
|
args.with_defaults(:date_from => nil, :date_to => nil)
|
|
if args[:date_from].blank? or args[:date_to].blank?
|
|
raise "ERROR: Is mandatory to pass a date from and to for the migration"
|
|
end
|
|
begin
|
|
date_from = DateTime.parse(args[:date_from])
|
|
date_to = DateTime.parse(args[:date_to])
|
|
rescue => e
|
|
raise "Error converting argument dates, check the arguments"
|
|
end
|
|
execute_on_users_with_index(:migrate_current_geocoder_billing_to_redis.to_s, Proc.new { |user, i|
|
|
begin
|
|
# We are working on the v2 which is only Nokia
|
|
next if user.google_maps_geocoder_enabled?
|
|
orgname = user.organization.nil? ? nil : user.organization.name
|
|
usage_metrics = CartoDB::GeocoderUsageMetrics.new(user.username, orgname)
|
|
geocoding_calls = user.get_not_aggregated_geocoding_calls({from: date_from, to: date_to})
|
|
geocoding_calls.each do |metric|
|
|
usage_metrics.incr(:geocoder_here, :success_responses, metric[:processed_rows], metric[:date])
|
|
usage_metrics.incr(:geocoder_here, :total_requests, metric[:processed_rows], metric[:date])
|
|
usage_metrics.incr(:geocoder_cache, :success_responses, metric[:cache_hits], metric[:date])
|
|
usage_metrics.incr(:geocoder_cache, :total_requests, metric[:cache_hits], metric[:date])
|
|
puts "Imported metrics for day #{metric[:date]} and user #{user.username}: #{metric}"
|
|
end
|
|
rescue => e
|
|
puts "Error trying to migrate user current billing cycle to redis #{user.username}: #{e.message}"
|
|
end
|
|
}, 1, 0.3)
|
|
end
|
|
|
|
# e.g. bundle exec rake cartodb:db:update_user_and_org_redis_metadata[username]
|
|
# bundle exec rake cartodb:db:update_user_and_org_redis_metadata
|
|
desc 'Update users and organizations metadata in Redis'
|
|
task :update_user_and_org_redis_metadata, [:username] => [:environment] do |task, args|
|
|
args.with_defaults(:username => nil)
|
|
if args[:username].blank?
|
|
execute_on_users_with_index(:update_user_and_org_redis_metadata.to_s, Proc.new { |user, i|
|
|
update_user_metadata(user)
|
|
update_organization_metadata(user)
|
|
}, 1, 0.3)
|
|
else
|
|
user = ::User.where(username: args[:username]).first
|
|
update_user_metadata(user)
|
|
update_organization_metadata(user)
|
|
end
|
|
end
|
|
|
|
# usage:
|
|
# bundle exec rake cartodb:db:connect_aggregation_fdw_tables[username]
|
|
desc 'Connect aggregation tables through FDW to user'
|
|
task :connect_aggregation_fdw_tables_to_user, [:username] => [:environment] do |task, args|
|
|
args.with_defaults(:username => nil)
|
|
raise 'Not a valid username' if args[:username].blank?
|
|
user = ::User.find(username: args[:username])
|
|
user.db_service.connect_to_aggregation_tables
|
|
end
|
|
|
|
# usage:
|
|
# bundle exec rake cartodb:db:connect_aggregation_fdw_tables[orgname]
|
|
desc 'Connect aggregation tables through FDW to orgname'
|
|
task :connect_aggregation_fdw_tables_to_org, [:orgname] => [:environment] do |task, args|
|
|
args.with_defaults(:orgname => nil)
|
|
raise 'Not a valid orgname' if args[:orgname].blank?
|
|
org = ::Organization.find(name: args[:orgname])
|
|
org.users.each do |u|
|
|
begin
|
|
u.db_service.connect_to_aggregation_tables
|
|
rescue => e
|
|
puts "Error trying to connect #{u.username}: #{e.message}"
|
|
end
|
|
end
|
|
end
|
|
|
|
desc 'Connect aggregation tables through FDW to builder users'
|
|
task :connect_aggregation_fdw_tables_to_builder_users => :environment do
|
|
unless Cartodb.get_config(:aggregation_tables).present?
|
|
raise "Aggregation tables not configured!"
|
|
end
|
|
# We're using a PostgreSQL cursor to avoid loading all the users
|
|
# in memory at once. (For ActiveRecord we'd use `find_each`)
|
|
User.where.use_cursor(rows_per_fetch: 100).each do |user|
|
|
if user.has_feature_flag?('editor-3')
|
|
begin
|
|
user.db_service.connect_to_aggregation_tables
|
|
rescue => e
|
|
puts "Error trying to connect #{user.username}: #{e.message}"
|
|
puts e.backtrace
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
desc 'Connect aggregation tables through FDW to all users'
|
|
task :connect_aggregation_fdw_tables_to_all_users => :environment do
|
|
unless Cartodb.get_config(:aggregation_tables).present?
|
|
raise "Aggregation tables not configured!"
|
|
end
|
|
# We're using a PostgreSQL cursor to avoid loading all the users
|
|
# in memory at once. (For ActiveRecord we'd use `find_each`)
|
|
User.where.use_cursor(rows_per_fetch: 100).each do |user|
|
|
begin
|
|
user.db_service.connect_to_aggregation_tables
|
|
rescue => e
|
|
puts "Error trying to connect #{user.username}: #{e.message}"
|
|
puts e.backtrace
|
|
end
|
|
end
|
|
end
|
|
|
|
# usage:
|
|
# bundle exec rake cartodb:db:obs_quota_enterprise[1000]
|
|
desc 'Give data observatory quota to all the enterprise users'
|
|
task :obs_quota_enterprise, [:quota] => [:environment] do |task, args|
|
|
args.with_defaults(:quota => 1000)
|
|
raise 'Not a valid quota' if args[:quota].blank?
|
|
do_quota = args[:quota]
|
|
users = User.where("account_type ilike 'enterprise%' or account_type ilike 'partner'").all
|
|
puts "Number of enterprise users to process: #{users.size}"
|
|
users.each do |u|
|
|
begin
|
|
if u.organization_owner? && !u.organization.nil?
|
|
u.organization.obs_general_quota = do_quota if u.organization.obs_general_quota.to_i == 0
|
|
u.organization.obs_snapshot_quota = do_quota if u.organization.obs_snapshot_quota.to_i == 0
|
|
u.organization.save
|
|
puts "Organization #{u.organization.name} processed OK"
|
|
else
|
|
u.obs_general_quota = do_quota if u.obs_general_quota.to_i == 0
|
|
u.obs_snapshot_quota = do_quota if u.obs_snapshot_quota.to_i == 0
|
|
u.save
|
|
puts "User #{u.username} processed OK"
|
|
end
|
|
rescue => e
|
|
puts "Error trying to give DO quota to #{u.username}: #{e.message}"
|
|
end
|
|
end
|
|
end
|
|
|
|
desc 'Fix analysis table the_geom type'
|
|
task fix_analysis_the_geom_type: [:environment] do
|
|
total_users = User.count
|
|
current = 0
|
|
User.where.use_cursor(rows_per_fetch: 100).each do |user|
|
|
puts "User #{current += 1} / #{total_users}"
|
|
next if user.organization && user.organization.owner != user # Filter out admin not owner users
|
|
begin
|
|
user.in_database do |db|
|
|
db.fetch("SELECT DISTINCT f_table_schema, f_table_name FROM geometry_columns WHERE f_table_name LIKE 'analysis%' AND type = 'MULTIPOLYGON'").map { |r| { schema: r[:f_table_schema], table: r[:f_table_name] } }.each do |entry|
|
|
db.execute("UPDATE \"#{entry[:schema]}\".\"#{entry[:table]}\" SET the_geom = ST_Multi(the_geom) where ST_GeometryType(the_geom) = 'ST_Polygon'")
|
|
end
|
|
end
|
|
rescue => e
|
|
puts "Error processing user #{user.username}: #{e.inspect}"
|
|
end
|
|
end
|
|
end
|
|
|
|
desc 'Fix analysis table the_geom type for batch geocoding bug (dataservices-api#538)'
|
|
task fix_batch_geocoding_the_geom_type: [:environment] do
|
|
total_users = User.count
|
|
current = 0
|
|
User.where.use_cursor(rows_per_fetch: 100).each do |user|
|
|
puts "User #{current += 1} / #{total_users}"
|
|
next if user.organization && user.organization.owner != user # Filter out admin not owner users
|
|
begin
|
|
user.in_database(as: :superuser) do |db|
|
|
db.fetch("SELECT DISTINCT f_table_schema, f_table_name FROM geometry_columns " \
|
|
"WHERE f_table_name LIKE 'analysis_1ea6dec9f3_%' AND type = 'MULTIPOLYGON'").each do |entry|
|
|
db.execute("ALTER TABLE \"#{entry[:f_table_schema]}\".\"#{entry[:f_table_name]}\" " \
|
|
"ALTER COLUMN the_geom TYPE geometry(Point, 4326)")
|
|
end
|
|
end
|
|
rescue StandardError => e
|
|
puts "Error processing user #{user.username}: #{e.inspect}"
|
|
end
|
|
end
|
|
end
|
|
|
|
def update_user_metadata(user)
|
|
begin
|
|
user.save_metadata
|
|
puts "Updated redis metadata for user #{user.username}"
|
|
rescue => e
|
|
puts "Error trying to update the user metadata for user #{user.username}: #{e.message}"
|
|
end
|
|
end
|
|
|
|
def update_organization_metadata(user)
|
|
begin
|
|
if user.organization_owner?
|
|
user.organization.save_metadata
|
|
puts "Updated redis metadata for organization #{user.organization.name}"
|
|
end
|
|
rescue => e
|
|
puts "Error trying to update the user and/or org metadata for user #{user.username}: #{e.message}"
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|