postgresql_privs change fail to warn if role(s) does not exist (#52574)
* postgresql_privs change fail to warn if role does not exists * postgresql_privs change fail to warn if role does not exists: fix sanity * postgresql_privs change fail to warn if role does not exists: add changelog fragment * postgresql_privs change fail to warn if role does not exists: fixes * postgresql_privs change fail to warn if role does not exists: added fail_on_role param
This commit is contained in:
parent
23a6b88dd2
commit
cf05429b3c
2 changed files with 59 additions and 6 deletions
|
@ -0,0 +1,5 @@
|
|||
bugfixes:
|
||||
- postgresql_privs - change fail to warn if PostgreSQL role does not exist (https://github.com/ansible/ansible/issues/46168).
|
||||
|
||||
minor_changes:
|
||||
- postgresql_privs - add fail_on_role parameter to control the behavior (fail or warn) when target role does not exist.
|
|
@ -70,6 +70,13 @@ options:
|
|||
for the implicitly defined PUBLIC group.
|
||||
- 'Alias: I(role)'
|
||||
required: yes
|
||||
fail_on_role:
|
||||
version_added: "2.8"
|
||||
description:
|
||||
- If C(yes), fail when target role (for whom privs need to be granted) does not exist.
|
||||
Otherwise just warn and continue.
|
||||
default: yes
|
||||
type: bool
|
||||
session_role:
|
||||
version_added: "2.8"
|
||||
description: |
|
||||
|
@ -295,6 +302,19 @@ class Error(Exception):
|
|||
pass
|
||||
|
||||
|
||||
def role_exists(module, cursor, rolname):
|
||||
"""Check user exists or not"""
|
||||
query = "SELECT 1 FROM pg_roles WHERE rolname = '%s'" % rolname
|
||||
try:
|
||||
cursor.execute(query)
|
||||
return cursor.rowcount > 0
|
||||
|
||||
except Exception as e:
|
||||
module.fail_json(msg="Cannot execute SQL '%s': %s" % (query, to_native(e)))
|
||||
|
||||
return False
|
||||
|
||||
|
||||
# We don't have functools.partial in Python < 2.5
|
||||
def partial(f, *args, **kwargs):
|
||||
"""Partial function application"""
|
||||
|
@ -313,8 +333,9 @@ def partial(f, *args, **kwargs):
|
|||
class Connection(object):
|
||||
"""Wrapper around a psycopg2 connection with some convenience methods"""
|
||||
|
||||
def __init__(self, params):
|
||||
def __init__(self, params, module):
|
||||
self.database = params.database
|
||||
self.module = module
|
||||
# To use defaults values, keyword arguments must be absent, so
|
||||
# check which values are empty and don't include in the **kw
|
||||
# dictionary
|
||||
|
@ -466,7 +487,7 @@ class Connection(object):
|
|||
# Manipulating privileges
|
||||
|
||||
def manipulate_privs(self, obj_type, privs, objs, roles,
|
||||
state, grant_option, schema_qualifier=None):
|
||||
state, grant_option, schema_qualifier=None, fail_on_role=True):
|
||||
"""Manipulate database object privileges.
|
||||
|
||||
:param obj_type: Type of database object to grant/revoke
|
||||
|
@ -545,7 +566,21 @@ class Connection(object):
|
|||
if roles == 'PUBLIC':
|
||||
for_whom = 'PUBLIC'
|
||||
else:
|
||||
for_whom = ','.join(pg_quote_identifier(r, 'role') for r in roles)
|
||||
for_whom = []
|
||||
for r in roles:
|
||||
if not role_exists(self.module, self.cursor, r):
|
||||
if fail_on_role:
|
||||
self.module.fail_json(msg="Role '%s' does not exist" % r.strip())
|
||||
|
||||
else:
|
||||
self.module.warn("Role '%s' does not exist, pass it" % r.strip())
|
||||
else:
|
||||
for_whom.append(pg_quote_identifier(r, 'role'))
|
||||
|
||||
if not for_whom:
|
||||
return False
|
||||
|
||||
for_whom = ','.join(for_whom)
|
||||
|
||||
status_before = get_status(objs)
|
||||
|
||||
|
@ -685,11 +720,14 @@ def main():
|
|||
password=dict(default='', aliases=['login_password'], no_log=True),
|
||||
ssl_mode=dict(default="prefer",
|
||||
choices=['disable', 'allow', 'prefer', 'require', 'verify-ca', 'verify-full']),
|
||||
ssl_rootcert=dict(default=None)
|
||||
ssl_rootcert=dict(default=None),
|
||||
fail_on_role=dict(type='bool', default=True),
|
||||
),
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
fail_on_role = module.params['fail_on_role']
|
||||
|
||||
# Create type object as namespace for module params
|
||||
p = type('Params', (), module.params)
|
||||
# param "schema": default, allowed depends on param "type"
|
||||
|
@ -719,7 +757,7 @@ def main():
|
|||
if not psycopg2:
|
||||
module.fail_json(msg=missing_required_lib('psycopg2'), exception=PSYCOPG2_IMP_ERR)
|
||||
try:
|
||||
conn = Connection(p)
|
||||
conn = Connection(p, module)
|
||||
except psycopg2.Error as e:
|
||||
module.fail_json(msg='Could not connect to database: %s' % to_native(e), exception=traceback.format_exc())
|
||||
except TypeError as e:
|
||||
|
@ -776,6 +814,15 @@ def main():
|
|||
else:
|
||||
roles = p.roles.split(',')
|
||||
|
||||
if len(roles) == 1 and not role_exists(module, conn.cursor, roles[0]):
|
||||
module.exit_json(changed=False)
|
||||
|
||||
if fail_on_role:
|
||||
module.fail_json(msg="Role '%s' does not exist" % roles[0].strip())
|
||||
|
||||
else:
|
||||
module.warn("Role '%s' does not exist, nothing to do" % roles[0].strip())
|
||||
|
||||
changed = conn.manipulate_privs(
|
||||
obj_type=p.type,
|
||||
privs=privs,
|
||||
|
@ -783,7 +830,8 @@ def main():
|
|||
roles=roles,
|
||||
state=p.state,
|
||||
grant_option=p.grant_option,
|
||||
schema_qualifier=p.schema
|
||||
schema_qualifier=p.schema,
|
||||
fail_on_role=fail_on_role,
|
||||
)
|
||||
|
||||
except Error as e:
|
||||
|
|
Loading…
Reference in a new issue