require 'base64' require 'kubeclient' require 'picsolve_docker_builder/composer/requirements/base' require 'picsolve_docker_builder/helpers/ssh_connection' require 'pg' module PicsolveDockerBuilder module Composer module Requirements # Postgres db requirements class Postgres < Base def ssh_forward(host, port) @ssh = PicsolveDockerBuilder::Helpers::SshConnection.new( ssh_host: kubernetes.host, ssh_port: kubernetes.port ) forward = @ssh.forward(host, port) @ssh.start forward end # get postgres secret for current container def postgres_secret get_secret(user_and_db_name) rescue KubeException => e raise e unless e.message.match(/not found/) log.info "Secret for #{user_and_db_name} database not found" create_postgres_secret end # ssh port forward def forward return @forward unless @forward.nil? @forward = ssh_forward( admin_postgres_secret['host'], admin_postgres_secret['port'].to_i ) @forward end # create a new postgres secret and create the according database def create_postgres_secret log.info "Create secret for #{user_and_db_name} database" container_secret = { 'name' => user_and_db_name, 'user' => user_and_db_name, 'password' => gen_password, 'host' => admin_postgres_secret['host'], 'port' => admin_postgres_secret['port'] } create_postgres_database( container_secret ) create_secret( user_and_db_name, container_secret ) container_secret end def create_admin_conn admin_secret = admin_postgres_secret.clone admin_secret['host'] = '127.0.0.1' admin_secret['port'] = forward.local_port log.info 'open postgres connection to ' \ "#{admin_secret['host']}:#{admin_secret['port']}" \ " with user #{admin_secret['user']}" PG::Connection.open(admin_secret) end def admin_conn @admin_conn ||= create_admin_conn end def admin_conn_close admin_conn.close @admin_conn = nil end # cleanup postgres database def cleanup_postgres_database dbname = admin_conn.escape_string(user_and_db_name) begin sql = "DROP DATABASE \"#{dbname}\";" admin_conn.exec sql log.info "removed database #{user_and_db_name}" rescue log.warn "removing database #{user_and_db_name} failed" end begin sql = "DROP USER \"#{dbname}\";" admin_conn.exec sql log.info "removed user #{user_and_db_name}" rescue log.warn "removing user #{user_and_db_name} failed" end begin delete_secret user_and_db_name log.info "removed secret #{user_and_db_name}" rescue log.warn "removing secret #{user_and_db_name} failed" end end # create postgres database # rubocop:disable Metrics/MethodLength def create_postgres_database(container_secret) log.info "Create database #{user_and_db_name}" user = admin_conn.escape_string(container_secret['user']) password = admin_conn.escape_string(container_secret['password']) name = admin_conn.escape_string(container_secret['name']) # create user admin_conn.exec( "CREATE USER \"#{user}\" " \ "WITH PASSWORD '#{password}'" ) # create db admin_conn.exec( "CREATE DATABASE \"#{name}\"" ) # grant all rights to the user and the aws user admin_conn.exec(" GRANT ALL PRIVILEGES ON DATABASE \"#{name}\" to \"#{user}\"; GRANT ALL PRIVILEGES ON DATABASE \"#{name}\" to \"rds_superuser\"; ") admin_conn_close admin_secret = admin_postgres_secret.clone admin_secret['dbname'] = name admin_secret['user'] = user admin_secret['password'] = password conn = PG::Connection.open(admin_secret) conn.exec(" ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO \"#{user}\"; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO \"rds_superuser\"; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO \"#{user}\"; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO \"rds_superuser\"; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO \"#{user}\"; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON FUNCTIONS TO \"rds_superuser\"; ") conn.close end # rubocop:enable Metrics/MethodLength # get administrative postgres secrets def admin_postgres_secret return @admin_postgres_secret unless @admin_postgres_secret.nil? @admin_postgres_secret = get_secret('postgres') @admin_postgres_secret.update('dbname' => 'postgres') @admin_postgres_secret end def user_and_db_name "#{stage}-#{container.name}-#{name}" end def postgres_secret_name "postgres-#{user_and_db_name}" end def environment_secret postgres_secret.map do |key, value| { 'name' => "#{name.upcase}_#{key.upcase}", 'value' => value } end end def environment environment_secret end end end end end