module Moose::Inventory::DB
Module for DB-related functionality
Attributes
db[R]
exceptions[R]
models[R]
Public Class Methods
init()
click to toggle source
# File lib/moose_inventory/db/db.rb, line 24 def self.init # If we allow init more than once, then the db connection is remade, # which changes Sequel:DATABASES[0], thereby invalidating the sequel # models. This causes unexpected behavour. That is to say, because # of the way Sequel initializes models, this method is not idempotent. # In our single-shot application, this shouldn't be a problem. However, # our unit tests like to call init multiple times, which borks things. # So, we allow init only once, gated by whether @db is nil. In effect, # this means we pool the DB connection for the life of the application. # Again, not a problem for our one-shot app, but it may be an issue in # long-running code. Personally, I don't like this pooling regime - # perhaps I'm not understanding how it's supposed to be used? # # QUESTION: can the models be refreshed, to make then again valid? What if # we "load" instead of "require" the models? # ANSWER: Nope, still borks even if we use a load. # # @db = nil # <- fails for unit tests return unless @db.nil? # <- works for unit tests Sequel::Model.plugin :json_serializer connect create_tables # Make our models work Sequel::DATABASES[0] = @db require_relative 'models' # load( load_dir = File.join(File.dirname(__FILE__), "models.rb") ) # For convenience @models = {} @models[:host] = Moose::Inventory::DB::Host @models[:hostvar] = Moose::Inventory::DB::Hostvar @models[:group] = Moose::Inventory::DB::Group @models[:groupvar] = Moose::Inventory::DB::Groupvar @exceptions = {} @exceptions[:moose] = Moose::Inventory::DB::MooseDBException end
reset()
click to toggle source
# File lib/moose_inventory/db/db.rb, line 119 def self.reset fail('Database connection has not been established') if @db.nil? # @debug << 'reset' purge create_tables end
transaction() { || ... }
click to toggle source
# File lib/moose_inventory/db/db.rb, line 66 def self.transaction fail('Database connection has not been established') if @db.nil? tries = 0 begin @db.transaction(savepoint: true) do yield end rescue Sequel::DatabaseError => e # We want to rescue Sqlite3::BusyException. But, sequel catches that # and re-raises it as Sequel::DatabaseError, with a message referencing # the original exception class # We look into e, to see whether it is a BusyException. If not, # we re-raise immediately. raise unless e.message.include?("BusyException") # Looks like a BusyException, so we retry, after a random delay. tries += 1 case tries when 1..10 if Moose::Inventory::Config._confopts[:trace] == true STDERR.puts e.message end sleep rand() retry else warn('The database appears to be locked by another process, and '\ " did not become free after #{tries} tries. Giving up. ") # TODO: Some useful advice to the user, as to what to do about this error. raise end rescue @exceptions[:moose] => e warn 'An error occurred during a transaction, any changes have been rolled back.' if Moose::Inventory::Config._confopts[:trace] == true STDERR.puts $!.backtrace abort("ERROR: #{e}") else abort("ERROR: #{e.message}") end rescue Exception => e warn 'An error occurred during a transaction, any changes have been rolled back.' raise e end end
Private Class Methods
connect()
click to toggle source
# File lib/moose_inventory/db/db.rb, line 218 def self.connect return unless @db.nil? adapter = Moose::Inventory::Config._settings[:config][:db][:adapter] adapter.downcase! case adapter when 'sqlite3' init_sqlite3 when 'msqsql' init_mysql when 'postgresql' init_postgresql else fail @exceptions[:moose ], "database adapter #{adapter} is not yet supported." end end
create_tables()
click to toggle source
# File lib/moose_inventory/db/db.rb, line 167 def self.create_tables # rubocop:disable Metrics/AbcSize unless @db.table_exists? :hosts @db.create_table(:hosts) do primary_key :id column :name, :text, unique: true end end unless @db.table_exists? :hostvars @db.create_table(:hostvars) do primary_key :id foreign_key :host_id column :name, :text column :value, :text end end unless @db.table_exists? :groups @db.create_table(:groups) do primary_key :id column :name, :text, unique: true end end unless @db.table_exists? :groups_groups @db.create_table(:groups_groups) do primary_key :id foreign_key :parent_id, :groups foreign_key :child_id, :groups end end unless @db.table_exists? :groupvars @db.create_table(:groupvars) do primary_key :id foreign_key :group_id column :name, :text column :value, :text end end unless @db.table_exists? :groups_hosts @db.create_table(:groups_hosts) do primary_key :id foreign_key :host_id, :hosts foreign_key :group_id, :groups end end end
init_mysql()
click to toggle source
# File lib/moose_inventory/db/db.rb, line 264 def self.init_mysql require 'mysql' # TODO: native MySQL driver vs the pure ruby one? # Sequel requires the native on. # gem('mysql') # Quick check that expected keys are at least present config = Moose::Inventory::Config._settings[:config][:db] [:host, :database, :user, :password].each do |key| if config[key].nil? fail @exceptions[:moose ], "Expected key #{key} missing in mysql configuration" end end @db = Sequel.mysql(user: config[:user], password: config[:password], host: config[:host], database: config[:database] ) end
init_sqlite3()
click to toggle source
# File lib/moose_inventory/db/db.rb, line 241 def self.init_sqlite3 # rubocop:disable Metrics/AbcSize require 'sqlite3' # Quick check that expected keys are at least present & sensible config = Moose::Inventory::Config._settings[:config][:db] [:file].each do |key| if config[key].nil? fail @exceptions[:moose ], "Expected key #{key} missing in sqlite3 configuration" end end config[:file].empty? && fail("SQLite3 DB 'file' cannot be empty") # Make sure the directory exists dbfile = File.expand_path(config[:file]) dbdir = File.dirname(dbfile) Dir.mkdir(dbdir) unless Dir.exist?(dbdir) # Create and/or open the database file @db = Sequel.sqlite(dbfile) end
purge()
click to toggle source
# File lib/moose_inventory/db/db.rb, line 131 def self.purge # rubocop:disable Metrics/AbcSize adapter = Moose::Inventory::Config._settings[:config][:db][:adapter] adapter.downcase! if adapter == 'sqlite3' # HACK: SQLite3 supposedly supports CASCADE, see # https://www.sqlite.org/foreignkeys.html#fk_actions # However, when we do a drop_table with :cascade=>true # on an sqlite3 database, it throws errors regarding # foreign keys constraints. Instead, the following is # less efficient, but does work. Group.all.each do |g| g.remove_all_hosts g.remove_all_groupvars g.remove_all_children g.destroy end Host.all.each do |h| h.remove_all_groups h.remove_all_hostvars h.destroy end Groupvar.all.each(&:destroy) Hostvar.all.each(&:destroy) else @db.drop_table(:hosts, :hostvars, :groups, :groupvars, :group_hosts, if_exists: true, cascade: true) end end