Class: Geordi::DBCleaner
- Inherits:
-
Object
- Object
- Geordi::DBCleaner
- Defined in:
- lib/geordi/db_cleaner.rb
Instance Method Summary collapse
- #allowlist_fname(dbtype) ⇒ Object
- #clean_mysql ⇒ Object
- #clean_postgres ⇒ Object
- #edit_allowlist(dbtype) ⇒ Object
-
#initialize(extra_flags, sudo: false) ⇒ DBCleaner
constructor
A new instance of DBCleaner.
- #is_allowlisted?(dbtype, database_name) ⇒ Boolean
- #is_protected?(dbtype, database_name) ⇒ Boolean
- #legacy_allowlist_directory ⇒ Object
- #list_all_dbs(dbtype) ⇒ Object
- #list_all_mysql_dbs ⇒ Object
- #list_all_postgres_dbs ⇒ Object
- #move_allowlist_files ⇒ Object
Constructor Details
#initialize(extra_flags, sudo: false) ⇒ DBCleaner
Returns a new instance of DBCleaner.
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/geordi/db_cleaner.rb', line 11 def initialize(extra_flags, sudo: false) @sudo = sudo if @sudo Interaction.note 'Please enter your sudo password when asked.' puts "We're going to run `sudo -u postgres psql` for PostgreSQL" puts ' and `sudo mysql` for MariaDB (which uses PAM auth)' `sudo true` Interaction.fail 'sudo access is required for database operations as database users' if $? != 0 end @derivative_dbname = /_(test\d*|development|cucumber)$/ @base_directory = ENV['XDG_CONFIG_HOME'] @base_directory ||= Dir.home.to_s @allowlist_directory = File.join(@base_directory, '.config', 'geordi', 'allowlists') FileUtils.mkdir_p(@allowlist_directory) unless File.directory? @allowlist_directory if File.directory?(legacy_allowlist_directory) move_allowlist_files end @mysql_command = decide_mysql_command(extra_flags['mysql']) @postgres_command = decide_postgres_command(extra_flags['postgres']) end |
Instance Method Details
#allowlist_fname(dbtype) ⇒ Object
224 225 226 |
# File 'lib/geordi/db_cleaner.rb', line 224 def allowlist_fname(dbtype) File.join(@allowlist_directory, dbtype) << '.txt' end |
#clean_mysql ⇒ Object
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
# File 'lib/geordi/db_cleaner.rb', line 197 def clean_mysql Interaction.announce 'Checking for MySQL databases' database_list = list_all_dbs('mysql') # confirm_deletion includes option for allowlist editing deletable_dbs = confirm_deletion('mysql', database_list) return if deletable_dbs.nil? deletable_dbs.each do |db| if @mysql_command.include? '-p' Interaction.note "Please enter your MySQL/MariaDB account 'root' for: DROP DATABASE #{db}" else puts "Dropping MySQL/MariaDB database #{db}" end `#{@mysql_command} -e 'DROP DATABASE \`#{db}\`;'` end end |
#clean_postgres ⇒ Object
213 214 215 216 217 218 219 220 221 222 |
# File 'lib/geordi/db_cleaner.rb', line 213 def clean_postgres Interaction.announce 'Checking for PostgreSQL databases' database_list = list_all_dbs('postgres') deletable_dbs = confirm_deletion('postgres', database_list) return if deletable_dbs.nil? deletable_dbs.each do |db| Interaction.note "Dropping PostgreSQL database `#{db}`." `#{@postgres_command} -c 'DROP DATABASE "#{db}";'` end end |
#edit_allowlist(dbtype) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/geordi/db_cleaner.rb', line 34 def edit_allowlist(dbtype) allowlist = allowlist_fname(dbtype) allowlisted_dbs = if File.exist? allowlist Geordi::Util.stripped_lines(File.read(allowlist))\ .delete_if { |l| l.start_with? '#' } else [] end all_dbs = list_all_dbs(dbtype) tmp = Tempfile.open("geordi_allowlist_#{dbtype}") tmp.write <<~HEREDOC # Put each allowlisted database on a new line. # System databases will never be deleted. # When you allowlist foo, foo_development and foo_test\\d* are allowlisted, too. # This works even if foo does not exist. Also, you will only see foo in this list. # # Syntax: keep foo # drop bar HEREDOC tmpfile_content = Array.new all_dbs.each do |db| next if is_allowlisted?(dbtype, db) next if is_protected?(dbtype, db) db.sub!(@derivative_dbname, '') tmpfile_content.push(['drop', db]) end warn_manual_allowlist = false allowlisted_dbs.each do |db_name| # Remove 'keep' word from allowlist entries. This is not normally required since geordi # does not save 'keep' or 'drop' to the allowlist file on disk but rather saves a list # of all allowlisted db names and just presents the keep/drop information while editing # the allowlist to supply users a list of databases they can allowlist by changing the # prefix to 'keep'. Everything prefixed 'drop' is not considered allowlisted and thus # not written to the allowlist file on disk. # # However, if users manually edit their allowlist files they might use the keep/drop # syntax they're familiar with. if db_name.start_with? 'keep ' db_name.gsub!(/keep /, '') db_name = db_name.split[1..-1].join(' ') warn_manual_allowlist = true end tmpfile_content.push(['keep', db_name]) unless db_name.empty? end if warn_manual_allowlist Interaction.warn <<~ERROR_MSG Your allowlist #{allowlist} seems to have been generated manually. In that case, make sure to use only one database name per line and omit the 'keep' prefix." Launching the editor. ERROR_MSG end tmpfile_content.sort_by! { |k| k[1] } tmpfile_content.uniq! tmpfile_content.each do |line| tmp.write("#{line[0]} #{line[1]}\n") end tmp.close texteditor = Geordi::Util.decide_texteditor system("#{texteditor} #{tmp.path}") File.open(tmp.path, 'r') do |wl_edited| allowlisted_dbs = [] allowlist_storage = File.open(allowlist, 'w') lines = Geordi::Util.stripped_lines(wl_edited.read) lines.each do |line| next if line.start_with?('#') unless line.split.length == 2 Interaction.fail "Invalid edit to allowlist file: \`#{line}\` - Syntax is: ^[keep|drop] dbname$" end unless %w[keep drop k d].include? line.split.first Interaction.fail "Invalid edit to allowlist file: \`#{line}\` - must start with either drop or keep." end db_status, db_name = line.split if db_status == 'keep' allowlisted_dbs.push db_name allowlist_storage.write(db_name << "\n") end end allowlist_storage.close end end |
#is_allowlisted?(dbtype, database_name) ⇒ Boolean
270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/geordi/db_cleaner.rb', line 270 def is_allowlisted?(dbtype, database_name) allowlist_content = if File.exist? allowlist_fname(dbtype) Geordi::Util.stripped_lines(File.open(allowlist_fname(dbtype), 'r').read) else [] end # Allow explicit allowlisting of derivative databases like projectname_test2 if allowlist_content.include? database_name true # allowlisting `projectname` also allowlists `projectname_test\d*`, `projectname_development` elsif allowlist_content.include? database_name.sub(@derivative_dbname, '') true else false end end |
#is_protected?(dbtype, database_name) ⇒ Boolean
262 263 264 265 266 267 268 |
# File 'lib/geordi/db_cleaner.rb', line 262 def is_protected?(dbtype, database_name) protected = { 'mysql' => %w[mysql information_schema performance_schema sys], 'postgres' => ['postgres'], } protected[dbtype].include? database_name end |
#legacy_allowlist_directory ⇒ Object
297 298 299 |
# File 'lib/geordi/db_cleaner.rb', line 297 def legacy_allowlist_directory @legacy_allowlist_directory ||= File.join(@base_directory, '.config', 'geordi', 'whitelists') end |
#list_all_dbs(dbtype) ⇒ Object
168 169 170 171 172 173 174 175 176 |
# File 'lib/geordi/db_cleaner.rb', line 168 def list_all_dbs(dbtype) if dbtype == 'postgres' list_all_postgres_dbs else list_all_mysql_dbs end rescue DatabaseError Interaction.fail 'Connection to database could not be established. Try running again with --sudo.' end |
#list_all_mysql_dbs ⇒ Object
186 187 188 189 190 191 192 193 194 195 |
# File 'lib/geordi/db_cleaner.rb', line 186 def list_all_mysql_dbs if @mysql_command.include? '-p' Interaction.note "Please enter your MySQL/MariaDB account 'root' for: list all databases" end output, _error, status = Open3.capture3("#{@mysql_command} -B -N -e 'show databases'") raise DatabaseError unless status.success? output.split end |
#list_all_postgres_dbs ⇒ Object
178 179 180 181 182 183 184 |
# File 'lib/geordi/db_cleaner.rb', line 178 def list_all_postgres_dbs output, _error, status = Open3.capture3("#{@postgres_command} -t -A -c 'SELECT DATNAME FROM pg_database WHERE datistemplate = false'") raise DatabaseError unless status.success? output.split end |
#move_allowlist_files ⇒ Object
301 302 303 304 305 306 307 308 309 310 311 312 313 |
# File 'lib/geordi/db_cleaner.rb', line 301 def move_allowlist_files %w[postgres mysql].each do |dbtype| new_path = allowlist_fname(dbtype) next if File.exist?(new_path) legacy_path = File.join(legacy_allowlist_directory, dbtype) << '.txt' FileUtils.mv(legacy_path, new_path) if Dir.exist?(legacy_allowlist_directory) && Dir.empty?(legacy_allowlist_directory) Dir.delete(legacy_allowlist_directory) end end end |