Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/msfdb_helpers/pg_ctl.rb
Views: 11766
require 'msfdb_helpers/db_interface'12module MsfdbHelpers3class PgCtl < DbInterface45def initialize(db_path:, options:, localconf:, db_conf:)6@db = db_path7@options = options8@localconf = localconf9@db_conf = db_conf10@socket_directory = db_path11super(options)12end1314def init(msf_pass, msftest_pass)15puts "Creating database at #{@db}"16Dir.mkdir(@db)17run_cmd("initdb --auth-host=trust --auth-local=trust -E UTF8 #{@db.shellescape}")1819File.open("#{@db}/postgresql.conf", 'a') do |f|20f.puts "port = #{@options[:db_port]}"21end2223# Try creating a test file at {Dir.tmpdir},24# Else fallback to creation at @{db}25# Else fail with error.26if test_executable_file("#{Dir.tmpdir}")27@socket_directory = Dir.tmpdir28elsif test_executable_file("#{@db}")29@socket_directory = @db30else31print_error("Attempt to create DB socket file at Temporary Directory and `~/.msf4/db` failed. Possibly because they are mounted with NOEXEC flags. Database initialization failed.")32end3334start3536create_db_users(msf_pass, msftest_pass)3738write_db_client_auth_config39restart40end4142# Creates and attempts to execute a testfile in the specified directory,43# to determine if it is mounted with NOEXEC flags.44def test_executable_file(path)45begin46file_name = File.join(path, 'msfdb_testfile')47File.open(file_name, 'w') do |f|48f.puts "#!/bin/bash\necho exec"49end50File.chmod(0744, file_name)5152if run_cmd(file_name)53File.open("#{@db}/postgresql.conf", 'a') do |f|54f.puts "unix_socket_directories = \'#{path}\'"55end56puts "Creating db socket file at #{path}"57end58return true5960rescue => e61return false6263ensure64begin65File.delete(file_name)66rescue67print_error("Unable to delete test file #{file_name}")68end69end7071end7273def delete74if exists?75stop7677if @options[:delete_existing_data]78puts "Deleting all data at #{@db}"79FileUtils.rm_rf(@db)80end8182if @options[:delete_existing_data]83FileUtils.rm_r(@db_conf, force: true)84end85else86puts "No data at #{@db}, doing nothing"87end88end8990def start91if status == DatabaseStatus::RUNNING92puts "Database already started at #{@db}"93return true94end9596print "Starting database at #{@db}..."97pg_ctl_spawn_cmd = "pg_ctl -o \"-p #{@options[:db_port]}\" -D #{@db.shellescape} -l #{@db.shellescape}/log start &"98puts "spawn_cmd: #{pg_ctl_spawn_cmd}" if @options[:debug]99pg_ctl_pid = Process.spawn(pg_ctl_spawn_cmd)100Process.detach(pg_ctl_pid)101is_database_running = retry_until_truthy(timeout: 60) do102status == DatabaseStatus::RUNNING103end104105if is_database_running106puts 'success'.green.bold.to_s107true108else109begin110Process.kill(:KILL, pg_ctl_pid)111rescue => e112puts "Failed to kill pg_ctl_pid=#{pg_ctl_pid} - #{e.class} #{e.message}" if @options[:debug]113end114puts 'failed'.red.bold.to_s115false116end117end118119def stop120if status == DatabaseStatus::RUNNING121puts "Stopping database at #{@db}"122run_cmd("pg_ctl -o \"-p #{@options[:db_port]}\" -D #{@db.shellescape} stop")123else124puts "Database is no longer running at #{@db}"125end126end127128def restart129stop130start131end132133def exists?134Dir.exist?(@db)135end136137def status138if exists?139if run_cmd("pg_ctl -o \"-p #{@options[:db_port]}\" -D #{@db.shellescape} status") == 0140DatabaseStatus::RUNNING141else142DatabaseStatus::INACTIVE143end144else145DatabaseStatus::NOT_FOUND146end147end148149def create_db_users(msf_pass, msftest_pass)150puts 'Creating database users'151run_psql("create user #{@options[:msf_db_user].shellescape} with password '#{msf_pass}'", @socket_directory)152run_psql("create user #{@options[:msftest_db_user].shellescape} with password '#{msftest_pass}'", @socket_directory)153run_psql("alter role #{@options[:msf_db_user].shellescape} createdb", @socket_directory)154run_psql("alter role #{@options[:msftest_db_user].shellescape} createdb", @socket_directory)155run_psql("alter role #{@options[:msf_db_user].shellescape} with password '#{msf_pass}'", @socket_directory)156run_psql("alter role #{@options[:msftest_db_user].shellescape} with password '#{msftest_pass}'", @socket_directory)157158conn = PG.connect(host: @options[:db_host], dbname: 'postgres', port: @options[:db_port], user: @options[:msf_db_user], password: msf_pass)159conn.exec("CREATE DATABASE #{@options[:msf_db_name]}")160conn.exec("CREATE DATABASE #{@options[:msftest_db_name]}")161conn.finish162end163164def write_db_client_auth_config165client_auth_config = "#{@db}/pg_hba.conf"166super(client_auth_config)167end168169def self.requirements170%w[psql pg_ctl initdb createdb]171end172173protected174175def retry_until_truthy(timeout:)176start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)177ending_time = start_time + timeout178retry_count = 0179while Process.clock_gettime(Process::CLOCK_MONOTONIC, :second) < ending_time180result = yield181return result if result182183retry_count += 1184remaining_time_budget = ending_time - Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)185break if remaining_time_budget <= 0186187delay = 2**retry_count188if delay >= remaining_time_budget189delay = remaining_time_budget190puts("Final attempt. Sleeping for the remaining #{delay} seconds out of total timeout #{timeout}") if @options[:debug]191else192puts("Sleeping for #{delay} seconds before attempting again") if @options[:debug]193end194195sleep delay196end197198nil199end200end201end202203204