Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 024723a

Browse filesBrowse files
author
vshepard
committed
tests 2.8.2
1 parent 09da114 commit 024723a
Copy full SHA for 024723a

16 files changed

+1135
-44
lines changed

‎.env

Copy file name to clipboard
+26Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
TARGET_OS=ubuntu
2+
TARGET_OS_VERSION=22.04
3+
PG_PRODUCT=enterprise
4+
PG_REPO=postgrespro
5+
PG_SRCDIR=./postgrespro
6+
PG_PRODUCT_SUFFIX=-ent
7+
PG_VERSION=15
8+
PG_VERSION_SUFFIX=-15
9+
PTRACK=ON
10+
PG_PROBACKUP_PTRACK=ON
11+
PGPROBACKUPBIN=/home/vshepard/pbckp/ent-15/bin/pg_probackup
12+
PG_CONFIG=/home/vshepard/pbckp/ent-15/bin/pg_config
13+
PGPROBACKUPBIN3=/home/vshepard/workspace/work/pg_probackup/dev-ee-probackup/pg_probackup3/builddir/src/pg_probackup3
14+
LANG=C.UTF-8
15+
LC_ALL=C
16+
17+
PG_PROBACKUP_S3_HOST=10.5.52.86
18+
PG_PROBACKUP_S3_PORT=9000
19+
PG_PROBACKUP_S3_REGION=us-east-1
20+
PG_PROBACKUP_S3_BUCKET_NAME=test1
21+
PG_PROBACKUP_S3_ACCESS_KEY=minioadmin
22+
PG_PROBACKUP_S3_SECRET_ACCESS_KEY=minioadmin
23+
PG_PROBACKUP_S3_HTTPS=OFF
24+
PG_PROBACKUP_S3_TEST=minio
25+
PG_PROBACKUP_S3_BUFFER_SIZE=64
26+
PG_PROBACKUP_S3_RETRIES=10

‎tests/archive_test.py

Copy file name to clipboardExpand all lines: tests/archive_test.py
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import subprocess
88
from sys import exit
99
from time import sleep
10+
from pathlib import PurePath
1011
from testgres import ProcessType
1112

1213

‎tests/auth_test.py

Copy file name to clipboard
+342-14Lines changed: 342 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,348 @@
1+
"""
2+
The Test suite check behavior of pg_probackup utility, if password is required for connection to PostgreSQL instance.
3+
- https://confluence.postgrespro.ru/pages/viewpage.action?pageId=16777522
4+
"""
5+
6+
import os
7+
import unittest
8+
import signal
9+
import time
10+
111
from .helpers.ptrack_helpers import ProbackupTest
12+
from testgres import StartNodeException
13+
14+
skip_test = False
15+
16+
try:
17+
from pexpect import *
18+
except ImportError:
19+
skip_test = True
20+
21+
22+
class SimpleAuthTest(ProbackupTest):
23+
24+
# @unittest.skip("skip")
25+
def test_backup_via_unprivileged_user(self):
26+
"""
27+
Make node, create unprivileged user, try to
28+
run a backups without EXECUTE rights on
29+
certain functions
30+
"""
31+
node = self.pg_node.make_simple('node',
32+
set_replication=True,
33+
ptrack_enable=self.ptrack)
34+
35+
self.pb.init()
36+
self.pb.add_instance('node', node)
37+
self.pb.set_archiving('node', node)
38+
node.slow_start()
39+
40+
if self.ptrack:
41+
node.safe_psql(
42+
"postgres",
43+
"CREATE EXTENSION ptrack")
44+
45+
node.safe_psql("postgres", "CREATE ROLE backup with LOGIN")
46+
47+
self.pb.backup_node('node', node, options=['-U', 'backup'],
48+
expect_error='due to missing grant on EXECUTE')
49+
if self.pg_config_version < 150000:
50+
self.assertMessage(contains=
51+
"ERROR: Query failed: ERROR: permission denied "
52+
"for function pg_start_backup")
53+
else:
54+
self.assertMessage(contains=
55+
"ERROR: Query failed: ERROR: permission denied "
56+
"for function pg_backup_start")
57+
58+
if self.pg_config_version < 150000:
59+
node.safe_psql(
60+
"postgres",
61+
"GRANT EXECUTE ON FUNCTION"
62+
" pg_start_backup(text, boolean, boolean) TO backup;")
63+
else:
64+
node.safe_psql(
65+
"postgres",
66+
"GRANT EXECUTE ON FUNCTION"
67+
" pg_backup_start(text, boolean) TO backup;")
68+
69+
node.safe_psql(
70+
'postgres',
71+
"GRANT EXECUTE ON FUNCTION pg_catalog.pg_switch_wal() TO backup")
72+
73+
self.pb.backup_node('node', node,
74+
options=['-U', 'backup'],
75+
expect_error='due to missing grant on EXECUTE')
76+
self.assertMessage(contains=
77+
"ERROR: Query failed: ERROR: permission denied for function "
78+
"pg_create_restore_point\nquery was: "
79+
"SELECT pg_catalog.pg_create_restore_point($1)")
80+
81+
node.safe_psql(
82+
"postgres",
83+
"GRANT EXECUTE ON FUNCTION"
84+
" pg_create_restore_point(text) TO backup;")
85+
86+
self.pb.backup_node('node', node,
87+
options=['-U', 'backup'],
88+
expect_error='due to missing grant on EXECUTE')
89+
if self.pg_config_version < 150000:
90+
self.assertMessage(contains=
91+
"ERROR: Query failed: ERROR: permission denied "
92+
"for function pg_stop_backup")
93+
else:
94+
self.assertMessage(contains=
95+
"ERROR: Query failed: ERROR: permission denied "
96+
"for function pg_backup_stop")
97+
98+
if self.pg_config_version < self.version_to_num('15.0'):
99+
node.safe_psql(
100+
"postgres",
101+
"GRANT EXECUTE ON FUNCTION pg_stop_backup() TO backup; "
102+
"GRANT EXECUTE ON FUNCTION pg_stop_backup(boolean, boolean) TO backup;")
103+
else:
104+
node.safe_psql(
105+
"postgres",
106+
"GRANT EXECUTE ON FUNCTION pg_backup_stop(boolean) TO backup;")
107+
108+
self.pb.backup_node('node', node, options=['-U', 'backup'])
109+
110+
node.safe_psql("postgres", "CREATE DATABASE test1")
111+
112+
self.pb.backup_node('node', node, options=['-U', 'backup'])
113+
114+
node.safe_psql(
115+
"test1", "create table t1 as select generate_series(0,100)")
116+
117+
node.stop()
118+
node.slow_start()
119+
120+
node.safe_psql(
121+
"postgres",
122+
"ALTER ROLE backup REPLICATION")
123+
124+
# FULL
125+
self.pb.backup_node('node', node, options=['-U', 'backup'])
126+
127+
# PTRACK
128+
if self.ptrack:
129+
self.pb.backup_node('node', node,
130+
backup_type='ptrack', options=['-U', 'backup'])
131+
132+
133+
class AuthTest(ProbackupTest):
134+
pb = None
135+
node = None
136+
137+
# TODO move to object scope, replace module_name
138+
@unittest.skipIf(skip_test, "Module pexpect isn't installed. You need to install it.")
139+
def setUp(self):
140+
141+
super().setUp()
142+
143+
self.node = self.pg_node.make_simple("node",
144+
set_replication=True,
145+
initdb_params=['--auth-host=md5'],
146+
pg_options={'archive_timeout': '5s'},
147+
)
148+
149+
self.modify_pg_hba(self.node)
150+
151+
self.pb.init()
152+
self.pb.add_instance(self.node.name, self.node)
153+
self.pb.set_archiving(self.node.name, self.node)
154+
try:
155+
self.node.slow_start()
156+
except StartNodeException:
157+
raise unittest.skip("Node hasn't started")
158+
159+
160+
version = self.pg_config_version
161+
if version < 150000:
162+
self.node.safe_psql(
163+
"postgres",
164+
"CREATE ROLE backup WITH LOGIN PASSWORD 'password'; "
165+
"GRANT USAGE ON SCHEMA pg_catalog TO backup; "
166+
"GRANT EXECUTE ON FUNCTION current_setting(text) TO backup; "
167+
"GRANT EXECUTE ON FUNCTION pg_is_in_recovery() TO backup; "
168+
"GRANT EXECUTE ON FUNCTION pg_start_backup(text, boolean, boolean) TO backup; "
169+
"GRANT EXECUTE ON FUNCTION pg_stop_backup() TO backup; "
170+
"GRANT EXECUTE ON FUNCTION pg_stop_backup(boolean, boolean) TO backup; "
171+
"GRANT EXECUTE ON FUNCTION pg_create_restore_point(text) TO backup; "
172+
"GRANT EXECUTE ON FUNCTION pg_switch_wal() TO backup; "
173+
"GRANT EXECUTE ON FUNCTION txid_current() TO backup; "
174+
"GRANT EXECUTE ON FUNCTION txid_current_snapshot() TO backup; "
175+
"GRANT EXECUTE ON FUNCTION txid_snapshot_xmax(txid_snapshot) TO backup;")
176+
else:
177+
self.node.safe_psql(
178+
"postgres",
179+
"CREATE ROLE backup WITH LOGIN PASSWORD 'password'; "
180+
"GRANT USAGE ON SCHEMA pg_catalog TO backup; "
181+
"GRANT EXECUTE ON FUNCTION current_setting(text) TO backup; "
182+
"GRANT EXECUTE ON FUNCTION pg_is_in_recovery() TO backup; "
183+
"GRANT EXECUTE ON FUNCTION pg_backup_start(text, boolean) TO backup; "
184+
"GRANT EXECUTE ON FUNCTION pg_backup_stop(boolean) TO backup; "
185+
"GRANT EXECUTE ON FUNCTION pg_create_restore_point(text) TO backup; "
186+
"GRANT EXECUTE ON FUNCTION pg_switch_wal() TO backup; "
187+
"GRANT EXECUTE ON FUNCTION txid_current() TO backup; "
188+
"GRANT EXECUTE ON FUNCTION txid_current_snapshot() TO backup; "
189+
"GRANT EXECUTE ON FUNCTION txid_snapshot_xmax(txid_snapshot) TO backup;")
190+
191+
if version >= 150000:
192+
home_dir = os.path.join(self.test_path, "home")
193+
os.makedirs(home_dir, exist_ok=True)
194+
self.test_env['HOME'] = home_dir
195+
self.pgpass_file = os.path.join(home_dir, '.pgpass')
196+
self.pgpass_file_lock = None
197+
else:
198+
# before PGv15 only true home dir were inspected.
199+
# Since we can't have separate file per test, we have to serialize
200+
# tests.
201+
self.pgpass_file = os.path.join(os.path.expanduser('~'), '.pgpass')
202+
self.pgpass_file_lock = self.pgpass_file + '~probackup_test_lock'
203+
# have to lock pgpass by creating file in exclusive mode
204+
for i in range(120):
205+
try:
206+
open(self.pgpass_file_lock, "x").close()
207+
except FileExistsError:
208+
time.sleep(1)
209+
else:
210+
break
211+
else:
212+
raise TimeoutError("can't create ~/.pgpass~probackup_test_lock for 120 seconds")
213+
214+
self.pb_cmd = ['backup',
215+
'--instance', self.node.name,
216+
'-h', '127.0.0.1',
217+
'-p', str(self.node.port),
218+
'-U', 'backup',
219+
'-d', 'postgres',
220+
'-b', 'FULL',
221+
'--no-sync'
222+
]
223+
224+
def tearDown(self):
225+
super().tearDown()
226+
if not self.pgpass_file_lock:
227+
return
228+
if hasattr(self, "pgpass_line") and os.path.exists(self.pgpass_file):
229+
with open(self.pgpass_file, 'r') as fl:
230+
lines = fl.readlines()
231+
if self.pgpass_line in lines:
232+
lines.remove(self.pgpass_line)
233+
if len(lines) == 0:
234+
os.remove(self.pgpass_file)
235+
else:
236+
with open(self.pgpass_file, 'w') as fl:
237+
fl.writelines(lines)
238+
os.remove(self.pgpass_file_lock)
239+
240+
def test_empty_password(self):
241+
""" Test case: PGPB_AUTH03 - zero password length """
242+
try:
243+
self.assertIn("ERROR: no password supplied",
244+
self.run_pb_with_auth('\0\r\n'))
245+
except (TIMEOUT, ExceptionPexpect) as e:
246+
self.fail(e.value)
247+
248+
def test_wrong_password(self):
249+
""" Test case: PGPB_AUTH04 - incorrect password """
250+
self.assertIn("password authentication failed",
251+
self.run_pb_with_auth('wrong_password\r\n'))
252+
253+
def test_right_password(self):
254+
""" Test case: PGPB_AUTH01 - correct password """
255+
self.assertIn("completed",
256+
self.run_pb_with_auth('password\r\n'))
257+
258+
def test_right_password_and_wrong_pgpass(self):
259+
""" Test case: PGPB_AUTH05 - correct password and incorrect .pgpass (-W)"""
260+
line = ":".join(['127.0.0.1', str(self.node.port), 'postgres', 'backup', 'wrong_password'])
261+
self.create_pgpass(self.pgpass_file, line)
262+
self.assertIn("completed",
263+
self.run_pb_with_auth('password\r\n', add_args=["-W"]))
264+
265+
def test_ctrl_c_event(self):
266+
""" Test case: PGPB_AUTH02 - send interrupt signal """
267+
try:
268+
self.run_pb_with_auth(kill=True)
269+
except TIMEOUT:
270+
self.fail("Error: CTRL+C event ignored")
271+
272+
def test_pgpassfile_env(self):
273+
""" Test case: PGPB_AUTH06 - set environment var PGPASSFILE """
274+
path = os.path.join(self.test_path, 'pgpass.conf')
275+
line = ":".join(['127.0.0.1', str(self.node.port), 'postgres', 'backup', 'password'])
276+
self.create_pgpass(path, line)
277+
self.test_env["PGPASSFILE"] = path
278+
self.assertEqual(
279+
"OK",
280+
self.pb.show(self.node.name, self.pb.run(self.pb_cmd + ['-w']))["status"],
281+
"ERROR: Full backup status is not valid."
282+
)
283+
284+
def test_pgpass(self):
285+
""" Test case: PGPB_AUTH07 - Create file .pgpass in home dir. """
286+
line = ":".join(['127.0.0.1', str(self.node.port), 'postgres', 'backup', 'password'])
287+
self.create_pgpass(self.pgpass_file, line)
288+
self.assertEqual(
289+
"OK",
290+
self.pb.show(self.node.name, self.pb.run(self.pb_cmd + ['-w']))["status"],
291+
"ERROR: Full backup status is not valid."
292+
)
293+
294+
def test_pgpassword(self):
295+
""" Test case: PGPB_AUTH08 - set environment var PGPASSWORD """
296+
self.test_env["PGPASSWORD"] = "password"
297+
self.assertEqual(
298+
"OK",
299+
self.pb.show(self.node.name, self.pb.run(self.pb_cmd + ['-w']))["status"],
300+
"ERROR: Full backup status is not valid."
301+
)
302+
303+
def test_pgpassword_and_wrong_pgpass(self):
304+
""" Test case: PGPB_AUTH09 - Check priority between PGPASSWORD and .pgpass file"""
305+
line = ":".join(['127.0.0.1', str(self.node.port), 'postgres', 'backup', 'wrong_password'])
306+
self.create_pgpass(self.pgpass_file, line)
307+
self.test_env["PGPASSWORD"] = "password"
308+
self.assertEqual(
309+
"OK",
310+
self.pb.show(self.node.name, self.pb.run(self.pb_cmd + ['-w']))["status"],
311+
"ERROR: Full backup status is not valid."
312+
)
2313

314+
def run_pb_with_auth(self, password=None, add_args = [], kill=False):
315+
cmd = [*self.pb_cmd, *add_args, *self.backup_dir.pb_args]
316+
with spawn(self.probackup_path, cmd,
317+
encoding='utf-8', timeout=60, env=self.test_env) as probackup:
318+
result = probackup.expect(u"Password for user .*:", 10)
319+
if kill:
320+
probackup.kill(signal.SIGINT)
321+
elif result == 0:
322+
probackup.sendline(password)
323+
probackup.expect(EOF)
324+
return str(probackup.before)
325+
else:
326+
raise ExceptionPexpect("Other pexpect errors.")
3327

4-
class AuthorizationTest(ProbackupTest):
5-
"""
6-
Check connect to S3 via pre_start_checks() function
7-
calling pg_probackup init --s3
8328

9-
test that s3 keys allow to connect to all types of storages
10-
"""
329+
def modify_pg_hba(self, node):
330+
"""
331+
Description:
332+
Add trust authentication for user postgres. Need for add new role and set grant.
333+
:param node:
334+
:return None:
335+
"""
336+
hba_conf = os.path.join(node.data_dir, "pg_hba.conf")
337+
with open(hba_conf, 'r+') as fio:
338+
data = fio.read()
339+
fio.seek(0)
340+
fio.write('host\tall\t%s\t127.0.0.1/0\ttrust\n%s' % (self.username, data))
11341

12-
def test_s3_auth_test(self):
13-
console_output = self.pb.init(options=["--log-level-console=VERBOSE"])
14342

15-
self.assertNotIn(': 403', console_output) # Because we can have just '403' substring in timestamp
16-
self.assertMessage(console_output, contains='S3_pre_start_check successful')
17-
self.assertMessage(console_output, contains='HTTP response: 200')
18-
self.assertIn(
19-
f"INFO: Backup catalog '{self.backup_dir}' successfully initialized",
20-
console_output)
343+
def create_pgpass(self, path, line):
344+
self.pgpass_line = line+"\n"
345+
with open(path, 'a') as passfile:
346+
# host:port:db:username:password
347+
passfile.write(self.pgpass_line)
348+
os.chmod(path, 0o600)

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.