class Gratan::Client

Public Class Methods

new(options = {}) click to toggle source
# File lib/gratan/client.rb, line 4
def initialize(options = {})
  @options = options
  @options[:identifier] ||= Gratan::Identifier::Null.new
  client = Mysql2::Client.new(options)
  @driver = Gratan::Driver.new(client, options)
end

Public Instance Methods

apply(file, options = {}) click to toggle source
# File lib/gratan/client.rb, line 58
def apply(file, options = {})
  options = @options.merge(options)

  in_progress do
    walk(file, options)
  end
end
chunk_by_user(exported) click to toggle source
# File lib/gratan/client.rb, line 35
def chunk_by_user(exported)
  chunked = {}

  exported.sort_by {|user_host, attrs|
    user_host[0]
  }.chunk {|user_host, attrs|
    user_host[0]
  }.each {|user, grants|
    merged_attrs = {}
    hosts = []

    grants.each do |user_host, attrs|
      hosts << user_host[1]
      merged_attrs.deep_merge!(attrs)
    end

    user_host = [user, hosts.sort]
    chunked[user_host] = merged_attrs
  }

  chunked
end
export(options = {}) { |user, dsl| ... } click to toggle source
# File lib/gratan/client.rb, line 11
def export(options = {})
  options = @options.merge(options)
  exported = Gratan::Exporter.export(@driver, options)

  if options[:chunk_by_user]
    exported = chunk_by_user(exported)
  end

  if block_given?
    exported.sort_by {|user_host, attrs|
      user_host[0].empty? ? 'root' : user_host[0]
    }.chunk {|user_host, attrs|
      user_host[0].empty? ? 'root' : user_host[0]
    }.each {|user, grants|
      h = {}
      grants.sort_by {|k, v| k }.each {|k, v| h[k] = v }
      dsl = Gratan::DSL.convert(h, options)
      yield(user, dsl)
    }
  else
    Gratan::DSL.convert(exported, options)
  end
end

Private Instance Methods

create_user(user, host, attrs) click to toggle source
# File lib/gratan/client.rb, line 99
def create_user(user, host, attrs)
  attrs[:options] ||= {}

  unless attrs[:options].has_key?(:identified)
    identified = @options[:identifier].identify(user, host)

    if identified
      attrs = attrs.dup
      attrs[:options] = attrs[:options].dup
      attrs[:options][:identified] = identified
    end
  end

  @driver.create_user(user, host, attrs)
  update!
end
drop_user(user, host) click to toggle source
# File lib/gratan/client.rb, line 116
def drop_user(user, host)
  @driver.drop_user(user, host)
  update!
end
in_progress() { || ... } click to toggle source
# File lib/gratan/client.rb, line 243
def in_progress
  updated = false

  begin
    @driver.disable_log_bin_local
    @driver.override_sql_mode
    @driver.set_wait_timeout
    @updated = false
    yield
    updated = @updated
    @driver.flush_privileges if updated
  ensure
    @updated = nil
  end

  if @options[:dry_run]
    false
  else
    updated
  end
end
load_file(file, options) click to toggle source
# File lib/gratan/client.rb, line 231
def load_file(file, options)
  if file.kind_of?(String)
    open(file) do |f|
      Gratan::DSL.parse(f.read, file, options)
    end
  elsif file.respond_to?(:read)
    Gratan::DSL.parse(file.read, file.path, options)
  else
    raise TypeError, "can't convert #{file} into File"
  end
end
normalize_privs(privs) click to toggle source
# File lib/gratan/client.rb, line 211
def normalize_privs(privs)
  privs.map do |priv|
    priv = priv.split('(', 2)
    priv[0].upcase!

    if priv[1]
      priv[1] = priv[1].split(',').map {|i| i.gsub(')', '').strip }.sort.join(', ')
      priv[1] << ')'
    end

    priv = priv.join('(')

    if priv == 'ALL'
      priv = 'ALL PRIVILEGES'
    end

    priv
  end
end
update!() click to toggle source
# File lib/gratan/client.rb, line 265
def update!
  @updated = true
end
walk(file, options) click to toggle source
# File lib/gratan/client.rb, line 68
def walk(file, options)
  expected = load_file(file, options)
  actual = Gratan::Exporter.export(@driver, options.merge(:with_identifier => true))

  expected.each do |user_host, expected_attrs|
    next if user_host[0] =~ options[:ignore_user]

    if options[:target_user]
      next unless user_host[0] =~ options[:target_user]
    end

    actual_attrs = actual.delete(user_host)

    if actual_attrs
      walk_user(*user_host, expected_attrs, actual_attrs)
    else
      create_user(*user_host, expected_attrs)
    end
  end

  actual.each do |user_host, attrs|
    next if user_host[0] =~ options[:ignore_user]

    if options[:target_user]
      next unless user_host[0] =~ options[:target_user]
    end

    drop_user(*user_host)
  end
end
walk_identified(user, host, expected_identified, actual_identified) click to toggle source
# File lib/gratan/client.rb, line 134
def walk_identified(user, host, expected_identified, actual_identified)
  if actual_identified == 'PASSWORD <secret>'
    unless @options[:ignore_password_secret]
      log(:warn, "cannot change the password (`PASSWORD <secret>`)", :color => :yellow)
    end
  elsif expected_identified != actual_identified
    @driver.identify(user, host, expected_identified)
    update!
  end
end
walk_object(user, host, object, expected_options, actual_options) click to toggle source
# File lib/gratan/client.rb, line 174
def walk_object(user, host, object, expected_options, actual_options)
  walk_with_option(user, host, object, expected_options[:with], actual_options[:with])
  walk_privs(user, host, object, expected_options[:privs], actual_options[:privs])
end
walk_objects(user, host, expected_objects, actual_objects) click to toggle source
# File lib/gratan/client.rb, line 152
def walk_objects(user, host, expected_objects, actual_objects)
  expected_objects.each do |object_or_regexp, expected_options|
    @driver.expand_object(object_or_regexp).each do |object|
      expected_options ||= {}
      actual_options = actual_objects.delete(object)

      if actual_options
        walk_object(user, host, object, expected_options, actual_options)
      else
        @driver.grant(user, host, object, expected_options)
        update!
      end
    end
  end

  actual_objects.each do |object, options|
    options ||= {}
    @driver.revoke(user, host, object, options)
    update!
  end
end
walk_options(user, host, expected_options, actual_options) click to toggle source
# File lib/gratan/client.rb, line 126
def walk_options(user, host, expected_options, actual_options)
  if expected_options.has_key?(:identified)
    walk_identified(user, host, expected_options[:identified], actual_options[:identified])
  end

  walk_required(user, host, expected_options[:required], actual_options[:required])
end
walk_privs(user, host, object, expected_privs, actual_privs) click to toggle source
# File lib/gratan/client.rb, line 189
def walk_privs(user, host, object, expected_privs, actual_privs)
  expected_privs = normalize_privs(expected_privs)
  actual_privs = normalize_privs(actual_privs)

  revoke_privs = actual_privs - expected_privs
  grant_privs = expected_privs - actual_privs

  unless revoke_privs.empty?
    if revoke_privs.length == 1 and revoke_privs[0] == 'USAGE' and not grant_privs.empty?
      # nothing to do
    else
      @driver.revoke(user, host, object, :privs => revoke_privs)
      update!
    end
  end

  unless grant_privs.empty?
    @driver.grant(user, host, object, :privs => grant_privs)
    update!
  end
end
walk_required(user, host, expected_required, actual_required) click to toggle source
# File lib/gratan/client.rb, line 145
def walk_required(user, host, expected_required, actual_required)
  if expected_required != actual_required
    @driver.set_require(user, host, expected_required)
    update!
  end
end
walk_user(user, host, expected_attrs, actual_attrs) click to toggle source
# File lib/gratan/client.rb, line 121
def walk_user(user, host, expected_attrs, actual_attrs)
  walk_options(user, host, expected_attrs[:options], actual_attrs[:options])
  walk_objects(user, host, expected_attrs[:objects], actual_attrs[:objects])
end
walk_with_option(user, host, object, expected_with_option, actual_with_option) click to toggle source
# File lib/gratan/client.rb, line 179
def walk_with_option(user, host, object, expected_with_option, actual_with_option)
  expected_with_option = (expected_with_option || '').upcase
  actual_with_option = (actual_with_option || '').upcase

  if expected_with_option != actual_with_option
    @driver.update_with_option(user, host, object, expected_with_option)
    update!
  end
end