-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
closes #58
- Loading branch information
Showing
7 changed files
with
864 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
.. _item_postgres_db: | ||
|
||
####################### | ||
Postgres database items | ||
####################### | ||
|
||
Manages Postgres databases. | ||
|
||
.. code-block:: python | ||
postgres_dbs = { | ||
"mydatabase": { | ||
"owner": "me", | ||
}, | ||
} | ||
Attribute reference | ||
------------------- | ||
|
||
.. seealso:: | ||
|
||
:ref:`The list of generic builtin item attributes <builtin_item_attributes>` | ||
|
||
Optional attributes | ||
=================== | ||
|
||
``owner`` | ||
+++++++++ | ||
|
||
Name of the role which owns this database (defaults to ``"postgres"``). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
.. _item_postgres_role: | ||
|
||
################### | ||
Postgres role items | ||
################### | ||
|
||
Manages Postgres roles. | ||
|
||
.. code-block:: python | ||
postgres_roles = { | ||
"me": { | ||
"superuser": True, | ||
"password": "itsamemario", | ||
}, | ||
} | ||
Attribute reference | ||
------------------- | ||
|
||
.. seealso:: | ||
|
||
:ref:`The list of generic builtin item attributes <builtin_item_attributes>` | ||
|
||
Optional attributes | ||
=================== | ||
|
||
``superuser`` | ||
+++++++++++++ | ||
|
||
``True`` if the role should be given superuser privileges (defaults to ``False``). | ||
|
||
| | ||
``password`` | ||
++++++++++++ | ||
|
||
Plaintext password to set for this role (will be hashed using MD5). | ||
|
||
.. warning:: | ||
Please do not write any passwords into your bundles. This attribute is intended to be used with an external source of passwords and filled dynamically. If you don't have or want such an elaborate setup, specify passwords using the ``password_hash`` attribute instead. | ||
|
||
| | ||
``password_hash`` | ||
+++++++++++++++++ | ||
|
||
As an alternative to ``password``, this allows setting the raw hash as it will be stored in Postgres' internal database. Should start with "md5". |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
# -*- coding: utf-8 -*- | ||
from __future__ import unicode_literals | ||
|
||
from pipes import quote | ||
|
||
from bundlewrap.exceptions import BundleError | ||
from bundlewrap.items import Item, ItemStatus | ||
from bundlewrap.utils.text import bold, red | ||
from bundlewrap.utils.text import mark_for_translation as _ | ||
|
||
|
||
def create_db(node, name, owner): | ||
return node.run("sudo -u postgres createdb -wO {owner} {name}".format( | ||
name=name, | ||
owner=owner, | ||
)) | ||
|
||
|
||
def drop_db(node, name): | ||
return node.run("sudo -u postgres dropdb -w {}".format(quote(name))) | ||
|
||
|
||
def get_databases(node): | ||
output = node.run("echo '\\l' | sudo -u postgres psql -Anqt -F '|' | grep '|'").stdout | ||
result = {} | ||
for line in output.strip().split("\n"): | ||
db, owner = line.strip().split("|", 2)[:2] | ||
result[db] = { | ||
'owner': owner, | ||
} | ||
return result | ||
|
||
|
||
def set_owner(node, name, owner): | ||
return node.run( | ||
"echo 'ALTER DATABASE {name} OWNER TO {owner}' | " | ||
"sudo -u postgres psql -nqw".format( | ||
name=name, | ||
owner=owner, | ||
), | ||
) | ||
|
||
|
||
class PostgresDB(Item): | ||
""" | ||
A postgres database. | ||
""" | ||
BUNDLE_ATTRIBUTE_NAME = "postgres_dbs" | ||
ITEM_ATTRIBUTES = { | ||
'delete': False, | ||
'owner': "postgres", | ||
} | ||
ITEM_TYPE_NAME = "postgres_db" | ||
NEEDS_STATIC = [ | ||
"pkg_apt:", | ||
"pkg_pacman:", | ||
"pkg_yum:", | ||
"pkg_zypper:", | ||
"postgres_role:", | ||
] | ||
def __repr__(self): | ||
return "<PostgresDB name:{}>".format(self.name) | ||
|
||
def ask(self, status): | ||
if not status.info['exists'] and not self.attributes['delete']: | ||
return _("Doesn't exist. Do you want to create it?") | ||
if status.info['exists'] and self.attributes['delete']: | ||
return red(_("Will be deleted.")) | ||
if status.info['owner'] != self.attributes['owner']: | ||
return "{} {} → {}".format( | ||
bold(_("owner")), | ||
status.info['owner'], | ||
self.attributes['owner'], | ||
) | ||
|
||
def fix(self, status): | ||
if 'existence' in status.info['needs_fixing']: | ||
if self.attributes['delete']: | ||
drop_db(self.node, self.name) | ||
else: | ||
create_db(self.node, self.name, self.attributes['owner']) | ||
elif 'owner' in status.info['needs_fixing']: | ||
set_owner(self.node, self.name, self.attributes['owner']) | ||
|
||
def get_status(self): | ||
databases = get_databases(self.node) | ||
status_info = { | ||
'exists': self.name in databases, | ||
'needs_fixing': [], | ||
} | ||
status_info.update(databases[self.name]) | ||
if self.attributes['delete'] == status_info['exists']: | ||
status_info['needs_fixing'].append('existence') | ||
return ItemStatus(correct=False, info=status_info) | ||
elif ( | ||
not self.attributes['delete'] and | ||
self.attributes['owner'] != databases[self.name]['owner'] | ||
): | ||
status_info['needs_fixing'].append('owner') | ||
return ItemStatus(correct=False, info=status_info) | ||
return ItemStatus(correct=True, info=status_info) | ||
|
||
@classmethod | ||
def validate_attributes(cls, bundle, item_id, attributes): | ||
if not isinstance(attributes.get('delete', True), bool): | ||
raise BundleError(_( | ||
"expected boolean for 'delete' on {item} in bundle '{bundle}'" | ||
).format( | ||
bundle=bundle.name, | ||
item=item_id, | ||
)) |
Oops, something went wrong.