Whole bunch of changes

Run sync within a SQL transaction, so that no partial sync happens

Don't abort on SQL errors, but print ERROR notice

Exit with code 1 when any ERRORs were logged

Silence the test suite, so that test runs are more clearly

Use PG queries instead of psql in test. This is more flexible
than parsing text outputs per Regexp.
This commit is contained in:
Lars Kanis
2018-03-13 16:21:20 +01:00
parent d9cb63ed98
commit 257b1a5e49
7 changed files with 164 additions and 46 deletions

View File

@ -1,2 +1,21 @@
require "pg_ldap_sync/application"
require "pg_ldap_sync/version"
module PgLdapSync
class LdapError < RuntimeError
end
class ApplicationExit < RuntimeError
attr_reader :exitcode
def initialize(exitcode)
@exitcode = exitcode
end
end
class InvalidConfig < ApplicationExit
end
class ErrorExit < ApplicationExit
end
end

View File

@ -1,16 +1,14 @@
#!/usr/bin/env ruby
require 'rubygems'
require 'net/ldap'
require 'optparse'
require 'yaml'
require 'logger'
require 'kwalify'
require 'pg'
require "pg_ldap_sync/logger"
module PgLdapSync
class Application
class LdapError < RuntimeError; end
attr_accessor :config_fname
attr_accessor :log
attr_accessor :test
@ -36,7 +34,7 @@ class Application
errors.each do |err|
log.fatal "error in #{fname}: [#{err.path}] #{err.message}"
end
exit(-1)
raise InvalidConfig, 78 # EX_CONFIG
end
end
@ -194,10 +192,21 @@ class Application
return roles
end
def try_sql(text)
begin
@pgconn.exec "SAVEPOINT try_sql;"
@pgconn.exec text
rescue PG::Error => err
@pgconn.exec "ROLLBACK TO try_sql;"
log.error{ "#{err} (#{err.class})" }
end
end
def pg_exec_modify(sql)
log.info{ "SQL: #{sql}" }
unless self.test
res = @pgconn.exec sql
try_sql sql
end
end
@ -298,36 +307,45 @@ class Application
# gather PGs users and groups
@pgconn = PG.connect @config[:pg_connection]
pg_users = uniq_names search_pg_users
pg_groups = uniq_names search_pg_groups
begin
@pgconn.transaction do
pg_users = uniq_names search_pg_users
pg_groups = uniq_names search_pg_groups
# compare LDAP to PG users and groups
mroles = match_roles(ldap_users, pg_users, :user)
mroles += match_roles(ldap_groups, pg_groups, :group)
# compare LDAP to PG users and groups
mroles = match_roles(ldap_users, pg_users, :user)
mroles += match_roles(ldap_groups, pg_groups, :group)
# compare LDAP to PG memberships
mmemberships = match_memberships(ldap_users+ldap_groups, pg_users+pg_groups)
# compare LDAP to PG memberships
mmemberships = match_memberships(ldap_users+ldap_groups, pg_users+pg_groups)
# drop/revoke roles/memberships first
sync_membership_to_pg(mmemberships, :revoke)
sync_roles_to_pg(mroles, :drop)
# create/grant roles/memberships
sync_roles_to_pg(mroles, :create)
sync_membership_to_pg(mmemberships, :grant)
# drop/revoke roles/memberships first
sync_membership_to_pg(mmemberships, :revoke)
sync_roles_to_pg(mroles, :drop)
# create/grant roles/memberships
sync_roles_to_pg(mroles, :create)
sync_membership_to_pg(mmemberships, :grant)
end
ensure
@pgconn.close
end
@pgconn.close
# Determine exitcode
if log.had_errors?
raise ErrorExit, 1
end
end
def self.run(argv)
s = self.new
s.config_fname = '/etc/pg_ldap_sync.yaml'
s.log = Logger.new(STDOUT)
s.log = Logger.new($stdout, @error_counters)
s.log.level = Logger::ERROR
OptionParser.new do |opts|
opts.version = VERSION
opts.banner = "Usage: #{$0} [options]"
opts.on("-v", "--[no-]verbose", "Increase verbose level"){ s.log.level-=1 }
opts.on("-v", "--[no-]verbose", "Increase verbose level"){|v| s.log.level += v ? -1 : 1 }
opts.on("-c", "--config FILE", "Config file [#{s.config_fname}]", &s.method(:config_fname=))
opts.on("-t", "--[no-]test", "Don't do any change in the database", &s.method(:test=))

View File

@ -0,0 +1,24 @@
require 'logger'
module PgLdapSync
class Logger < ::Logger
def initialize(io, counters)
super(io)
@counters = {}
end
def add(severity, *args)
@counters[severity] ||= 0
@counters[severity] += 1
super
end
def had_logged?(severity)
@counters[severity] && @counters[severity] > 0
end
def had_errors?
had_logged?(Logger::FATAL) || had_logged?(Logger::ERROR)
end
end
end