Skip to content

Commit

Permalink
Merge pull request #181 from ZuluPro/pgpasswd
Browse files Browse the repository at this point in the history
Fix postgresql passwd
  • Loading branch information
ZuluPro authored Jul 26, 2016
2 parents b7c63a8 + 55355e0 commit 040f5d1
Show file tree
Hide file tree
Showing 3 changed files with 190 additions and 14 deletions.
24 changes: 11 additions & 13 deletions dbbackup/db/postgresql.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ class PgDumpConnector(BaseCommandDBConnector):
single_transaction = True
drop = True

def run_command(self, *args, **kwargs):
if self.settings.get('PASSWORD'):
env = kwargs.get('env', {})
env['PGPASSWORD'] = self.settings['PASSWORD']
kwargs['env'] = env
return super(PgDumpConnector, self).run_command(*args, **kwargs)

def _create_dump(self):
cmd = '{} {}'.format(self.dump_cmd, self.settings['NAME'])
if self.settings.get('HOST'):
Expand All @@ -20,10 +27,7 @@ def _create_dump(self):
cmd += ' --port={}'.format(self.settings['PORT'])
if self.settings.get('USER'):
cmd += ' --user={}'.format(self.settings['USER'])
if self.settings.get('PASSWORD'):
cmd += ' --password={}'.format(self.settings['PASSWORD'])
else:
cmd += ' --no-password'
cmd += ' --no-password'
for table in self.exclude:
cmd += ' --exclude-table={}'.format(table)
if self.drop:
Expand All @@ -40,10 +44,7 @@ def _restore_dump(self, dump):
cmd += ' --port={}'.format(self.settings['PORT'])
if self.settings.get('USER'):
cmd += ' --user={}'.format(self.settings['USER'])
if self.settings.get('PASSWORD'):
cmd += ' --password={}'.format(self.settings['PASSWORD'])
else:
cmd += ' --no-password'
cmd += ' --no-password'
if self.single_transaction:
cmd += ' --single-transaction'
cmd = '{} {} {}'.format(self.restore_prefix, cmd, self.restore_suffix)
Expand All @@ -62,17 +63,14 @@ def _enable_postgis(self):
cmd = '{} -c "CREATE EXTENSION IF NOT EXISTS postgis;"'.format(
self.psql_cmd)
cmd += ' --user={}'.format(self.settings['ADMIN_USER'])
if self.settings.get('ADMIN_PASSWORD'):
cmd += ' --password={}'.format(self.settings['ADMIN_PASSWORD'])
else:
cmd += ' --no-password'
cmd += ' --no-password'
if self.settings.get('HOST'):
cmd += ' --host={}'.format(self.settings['HOST'])
if self.settings.get('PORT'):
cmd += ' --port={}'.format(self.settings['PORT'])
return self.run_command(cmd)

def _restore_dump(self, dump):
if self.settings.get('ADMINUSER'):
if self.settings.get('ADMIN_USER'):
self._enable_postgis()
return super(PgDumpGisConnector, self)._restore_dump(dump)
174 changes: 173 additions & 1 deletion dbbackup/tests/test_connectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from dbbackup.db import exceptions
from dbbackup.db.sqlite import SqliteConnector, SqliteCPConnector
from dbbackup.db.mysql import MysqlDumpConnector
from dbbackup.db.postgresql import PgDumpConnector
from dbbackup.db.postgresql import PgDumpConnector, PgDumpGisConnector
from dbbackup.db.mongodb import MongoDumpConnector


Expand Down Expand Up @@ -139,6 +139,65 @@ def test_create_dump(self, mock_dump_cmd):
# Test cmd
self.assertTrue(mock_dump_cmd.called)

def test_create_dump_host(self, mock_dump_cmd):
connector = PgDumpConnector()
# Without
connector.settings.pop('HOST', None)
connector.create_dump()
self.assertNotIn(' --host=', mock_dump_cmd.call_args[0][0])
# With
connector.settings['HOST'] = 'foo'
connector.create_dump()
self.assertIn(' --host=foo', mock_dump_cmd.call_args[0][0])

def test_create_dump_port(self, mock_dump_cmd):
connector = PgDumpConnector()
# Without
connector.settings.pop('PORT', None)
connector.create_dump()
self.assertNotIn(' --port=', mock_dump_cmd.call_args[0][0])
# With
connector.settings['PORT'] = 42
connector.create_dump()
self.assertIn(' --port=42', mock_dump_cmd.call_args[0][0])

def test_create_dump_user(self, mock_dump_cmd):
connector = PgDumpConnector()
# Without
connector.settings.pop('USER', None)
connector.create_dump()
self.assertNotIn(' --user=', mock_dump_cmd.call_args[0][0])
# With
connector.settings['USER'] = 'foo'
connector.create_dump()
self.assertIn(' --user=foo', mock_dump_cmd.call_args[0][0])

def test_create_dump_exclude(self, mock_dump_cmd):
connector = PgDumpConnector()
# Without
connector.create_dump()
self.assertNotIn(' --exclude_table=', mock_dump_cmd.call_args[0][0])
# With
connector.exclude = ('foo',)
connector.create_dump()
self.assertNotIn(' --exclude_table=foo', mock_dump_cmd.call_args[0][0])
# With serveral
connector.exclude = ('foo', 'bar')
connector.create_dump()
self.assertIn(' --exclude-table=foo', mock_dump_cmd.call_args[0][0])
self.assertIn(' --exclude-table=bar', mock_dump_cmd.call_args[0][0])

def test_create_dump_drop(self, mock_dump_cmd):
connector = PgDumpConnector()
# Without
connector.drop = False
connector.create_dump()
self.assertNotIn(' --clean', mock_dump_cmd.call_args[0][0])
# With
connector.drop = True
connector.create_dump()
self.assertIn(' --clean', mock_dump_cmd.call_args[0][0])

@patch('dbbackup.db.postgresql.PgDumpConnector.run_command',
return_value=(BytesIO(), BytesIO()))
def test_restore_dump(self, mock_dump_cmd, mock_restore_cmd):
Expand All @@ -148,6 +207,119 @@ def test_restore_dump(self, mock_dump_cmd, mock_restore_cmd):
# Test cmd
self.assertTrue(mock_restore_cmd.called)

def test_restore_dump_host(self, mock_dump_cmd):
connector = PgDumpConnector()
dump = connector.create_dump()
# Without
connector.settings.pop('HOST', None)
connector.restore_dump(dump)
self.assertNotIn(' --host=foo', mock_dump_cmd.call_args[0][0])
# With
connector.settings['HOST'] = 'foo'
connector.restore_dump(dump)
self.assertIn(' --host=foo', mock_dump_cmd.call_args[0][0])

def test_restore_dump_port(self, mock_dump_cmd):
connector = PgDumpConnector()
dump = connector.create_dump()
# Without
connector.settings.pop('PORT', None)
connector.restore_dump(dump)
self.assertNotIn(' --port=', mock_dump_cmd.call_args[0][0])
# With
connector.settings['PORT'] = 42
connector.restore_dump(dump)
self.assertIn(' --port=42', mock_dump_cmd.call_args[0][0])

def test_restore_dump_user(self, mock_dump_cmd):
connector = PgDumpConnector()
dump = connector.create_dump()
# Without
connector.settings.pop('USER', None)
connector.restore_dump(dump)
self.assertNotIn(' --user=', mock_dump_cmd.call_args[0][0])
# With
connector.settings['USER'] = 'foo'
connector.restore_dump(dump)
self.assertIn(' --user=foo', mock_dump_cmd.call_args[0][0])


@patch('dbbackup.db.postgresql.PgDumpGisConnector.run_command',
return_value=(BytesIO(b'foo'), BytesIO()))
class PgDumpGisConnectorTest(TestCase):
@patch('dbbackup.db.postgresql.PgDumpGisConnector.run_command',
return_value=(BytesIO(b'foo'), BytesIO()))
def test_restore_dump(self, mock_dump_cmd, mock_restore_cmd):
connector = PgDumpGisConnector()
dump = connector.create_dump()
# Without ADMINUSER
connector.settings.pop('ADMIN_USER', None)
connector.restore_dump(dump)
self.assertTrue(mock_restore_cmd.called)
# With
connector.settings['ADMIN_USER'] = 'foo'
connector.restore_dump(dump)
self.assertTrue(mock_restore_cmd.called)

def test_enable_postgis(self, mock_dump_cmd):
connector = PgDumpGisConnector()
connector.settings['ADMIN_USER'] = 'foo'
connector._enable_postgis()
self.assertIn('"CREATE EXTENSION IF NOT EXISTS postgis;"', mock_dump_cmd.call_args[0][0])
self.assertIn('--user=foo', mock_dump_cmd.call_args[0][0])

def test_enable_postgis_host(self, mock_dump_cmd):
connector = PgDumpGisConnector()
connector.settings['ADMIN_USER'] = 'foo'
# Without
connector.settings.pop('HOST', None)
connector._enable_postgis()
self.assertNotIn(' --host=', mock_dump_cmd.call_args[0][0])
# With
connector.settings['HOST'] = 'foo'
connector._enable_postgis()
self.assertIn(' --host=foo', mock_dump_cmd.call_args[0][0])

def test_enable_postgis_port(self, mock_dump_cmd):
connector = PgDumpGisConnector()
connector.settings['ADMIN_USER'] = 'foo'
# Without
connector.settings.pop('PORT', None)
connector._enable_postgis()
self.assertNotIn(' --port=', mock_dump_cmd.call_args[0][0])
# With
connector.settings['PORT'] = 42
connector._enable_postgis()
self.assertIn(' --port=42', mock_dump_cmd.call_args[0][0])


@patch('dbbackup.db.base.Popen', **{
'return_value.wait.return_value': True,
'return_value.poll.return_value': False,
})
class PgDumpConnectorRunCommandTest(TestCase):
def test_run_command(self, mock_popen):
connector = PgDumpConnector()
connector.create_dump()
self.assertEqual(mock_popen.call_args[0][0][0], 'pg_dump')

def test_run_command_with_password(self, mock_popen):
connector = PgDumpConnector()
connector.settings['PASSWORD'] = 'foo'
connector.create_dump()
self.assertEqual(mock_popen.call_args[0][0][0], 'pg_dump')
self.assertEqual(mock_popen.call_args[1]['env'], {'PGPASSWORD': 'foo'})

def test_run_command_with_password_and_other(self, mock_popen):
connector = PgDumpConnector(env={'foo': 'bar'})
connector.settings['PASSWORD'] = 'foo'
connector.create_dump()
self.assertEqual(mock_popen.call_args[0][0][0], 'pg_dump')
self.assertEqual(mock_popen.call_args[1]['env'], {
'foo': 'bar',
'PGPASSWORD': 'foo'
})


@patch('dbbackup.db.mongodb.MongoDumpConnector.run_command',
return_value=(BytesIO(b'foo'), BytesIO()))
Expand Down
6 changes: 6 additions & 0 deletions docs/databases.rst
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ Path to ``psql`` command used for administration tasks like enable PostGIS
for example, default is ``psql``.


PASSWORD
~~~~~~~~

If you fill this settings ``PGPASSWORD`` environment variable will be used
with every commands. For security reason, we advise to use ``.pgpass`` file.

ADMIN_USER
~~~~~~~~~~

Expand Down

0 comments on commit 040f5d1

Please sign in to comment.