Plain is headed towards 1.0! Subscribe for development updates →

 1import os
 2import subprocess
 3
 4
 5class PostgresBackupClient:
 6    def __init__(self, connection):
 7        self.connection = connection
 8
 9    def get_env(self):
10        settings_dict = self.connection.settings_dict
11        options = settings_dict.get("OPTIONS", {})
12        env = {}
13        if options.get("passfile"):
14            env["PGPASSFILE"] = str(options.get("passfile"))
15        if settings_dict.get("PASSWORD"):
16            env["PGPASSWORD"] = str(settings_dict.get("PASSWORD"))
17        if options.get("service"):
18            env["PGSERVICE"] = str(options.get("service"))
19        if options.get("sslmode"):
20            env["PGSSLMODE"] = str(options.get("sslmode"))
21        if options.get("sslrootcert"):
22            env["PGSSLROOTCERT"] = str(options.get("sslrootcert"))
23        if options.get("sslcert"):
24            env["PGSSLCERT"] = str(options.get("sslcert"))
25        if options.get("sslkey"):
26            env["PGSSLKEY"] = str(options.get("sslkey"))
27        return env
28
29    def create_backup(self, backup_path, *, pg_dump="pg_dump"):
30        settings_dict = self.connection.settings_dict
31
32        args = pg_dump.split()
33        options = settings_dict.get("OPTIONS", {})
34
35        host = settings_dict.get("HOST")
36        port = settings_dict.get("PORT")
37        dbname = settings_dict.get("NAME")
38        user = settings_dict.get("USER")
39        service = options.get("service")
40
41        if not dbname and not service:
42            # Connect to the default 'postgres' db.
43            dbname = "postgres"
44        if user:
45            args += ["-U", user]
46        if host:
47            args += ["-h", host]
48        if port:
49            args += ["-p", str(port)]
50
51        args += ["-Fc"]
52        # args += ["-f", backup_path]
53
54        if dbname:
55            args += [dbname]
56
57        # Using stdin/stdout let's us use executables from within a docker container too
58        args += ["|", "gzip", ">", str(backup_path)]
59
60        cmd = " ".join(args)
61
62        subprocess.run(
63            cmd, env={**os.environ, **self.get_env()}, check=True, shell=True
64        )
65
66    def restore_backup(self, backup_path, *, pg_restore="pg_restore"):
67        settings_dict = self.connection.settings_dict
68
69        args = pg_restore.split()
70        options = settings_dict.get("OPTIONS", {})
71
72        host = settings_dict.get("HOST")
73        port = settings_dict.get("PORT")
74        dbname = settings_dict.get("NAME")
75        user = settings_dict.get("USER")
76        service = options.get("service")
77
78        if not dbname and not service:
79            # Connect to the default 'postgres' db.
80            dbname = "postgres"
81        if user:
82            args += ["-U", user]
83        if host:
84            args += ["-h", host]
85        if port:
86            args += ["-p", str(port)]
87
88        args += ["--clean"]  # Drop existing tables
89        args += ["-d", dbname]
90
91        # Using stdin/stdout let's us use executables from within a docker container too
92        args = ["gunzip", "<", str(backup_path), "|"] + args
93
94        cmd = " ".join(args)
95
96        subprocess.run(
97            cmd, env={**os.environ, **self.get_env()}, check=True, shell=True
98        )