Skip to content

Commit 3f15058

Browse files
authored
Merge branch 'master' into 1.2.0-backup-retention
2 parents 65b49c6 + 341904f commit 3f15058

25 files changed

+377
-143
lines changed

README.rst

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@ Features
3131
archiving method (*optional*)
3232
- `AWS S3 <https://aws.amazon.com/s3/>`__ Secure Multipart backup uploads (*optional*)
3333
- `Google Cloud Storage <https://cloud.google.com/storage/>`__ Secure backup uploads (*optional*)
34+
- Rsync (over SSH) secure backup uploads (*optional*)
3435
- `Nagios NSCA <https://sourceforge.net/p/nagios/nsca>`__ push
3536
notification support (*optional*)
3637
- Modular backup, archiving, upload and notification components
3738
- Rotation of backups by time or count
39+
- Support for MongoDB Authentication and SSL database connections
3840
- Multi-threaded, single executable
3941
- Auto-scales to number of available CPUs by default
4042

@@ -78,6 +80,7 @@ To build an CentOS/RedHat RPM of the tool *(recommended)*:
7880
::
7981

8082
$ cd /path/to/mongodb_consistent_backup
83+
$ sudo yum install -y rpm-build
8184
$ make rpm
8285

8386
To build and install from source *(to default '/usr/local/bin/mongodb-consistent-backup')*:
@@ -149,25 +152,40 @@ The backups are `mongorestore <https://docs.mongodb.com/manual/reference/program
149152
...
150153
$ mongorestore --host mongod12.example.com --port 27017 -u admin -p 123456 --oplogReplay --dir /var/lib/mongodb-consistent-backup/default/20170424_0000/rs0/dump
151154

152-
Run as Docker Container (Experimental)
155+
Run as Docker Container
153156
~~~~~~~~~~~~~~~~~~~~~~~
154157

155-
Note: you need to use persistent volumes to store backups and/or config files long-term when using Docker. Data in Docker containers is destroyed when the container is deleted. See `scripts/docker-persistent.sh <scripts/docker-persistent.sh>`__ and `scripts/docker-persistent.example.conf <scripts/docker-persistent.example.conf>`__ as an example/demo of how to implement persistence.
158+
To persist logs, configs and backup data 3 directories should be mapped to be inside the Docker containter.
159+
160+
The 'docker run' command -v/--volume flags in the examples below map container paths to paths on your Docker host. The example below assumes there is a path on the Docker host named *'/data/mongobackup'* with *'data'*, *'conf'* and *'logs'* subdirectories mapped to inside the container. Replace any instance of *'/data/mongobackup'* below to a different path if necessary.
161+
162+
*Note: store a copy of your mongodb-consistent-backup.conf in the 'conf' directory and pass it's container path as the --config= flag if you wish to use config files.*
156163

157164
**Via Docker Hub**
158165

159166
::
160167

161-
$ docker run -i timvaillancourt/mongodb_consistent_backup <mongodb_consistent_backup-flags>
168+
$ mkdir -p /data/mongobackup/{conf,data,logs}
169+
$ cp -f /path/to/mongodb-consistent-backup.conf /data/mongobackup/conf
170+
$ docker run -it \
171+
-v "/data/mongobackup/conf:/conf:Z" \
172+
-v "/data/mongobackup/data:/var/lib/mongodb-consistent-backup:Z" \
173+
-v "/data/mongobackup/logs:/var/log/mongodb-consistent-backup:Z" \
174+
timvaillancourt/mongodb_consistent_backup:latest --config=/conf/mongodb-consistent-backup.conf
162175

163176
**Build and Run Docker Image**
164177

165178
::
166179

167180
$ cd /path/to/mongodb_consistent_backup
168181
$ make docker
169-
$ docker run -t mongodb_consistent_backup <mongodb_consistent_backup-flags>
170-
182+
$ mkdir -p /data/mongobackup/{conf,data,logs}
183+
$ cp -f /path/to/mongodb-consistent-backup.conf /data/mongobackup/conf
184+
$ docker run -it \
185+
-v "/data/mongobackup/conf:/conf:Z" \
186+
-v "/data/mongobackup/data:/var/lib/mongodb-consistent-backup:Z" \
187+
-v "/data/mongobackup/logs:/var/log/mongodb-consistent-backup:Z" \
188+
mongodb_consistent_backup --config=/conf/mongodb-consistent-backup.conf
171189

172190
ZBackup Archiving (Optional)
173191
~~~~~~~

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.1.0
1+
1.2.0

conf/mongodb-consistent-backup.example.conf

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ production:
1818
# mongodump:
1919
# binary: [path] (default: /usr/bin/mongodump)
2020
# compression: [auto|none|gzip] (default: auto - enable gzip if supported)
21-
# threads: [1-16] (default: auto-generated - shards/cpu)
2221
#rotate:
2322
# max_backups: [1+]
2423
# max_days: [0.1+]
24+
# threads: [1-16] (default: auto-generated, shards/cpu)
2525
#replication:
2626
# max_lag_secs: [1+] (default: 10)
2727
# min_priority: [0-999] (default: 0)
@@ -33,18 +33,19 @@ production:
3333
# wait_secs: [1+] (default: 300)
3434
# ping_secs: [1+] (default: 3)
3535
#oplog:
36-
# compression: [none|gzip] (default: gzip - if gzip is used by backup stage)
36+
# compression: [none|gzip] (default: gzip, if used by backup stage)
3737
# flush:
38-
# max_docs: 100
39-
# max_secs: 1
40-
# resolver_threads: [1+] (default: 2 per CPU)
38+
# max_docs: [1+] (default: 100)
39+
# max_secs: [1+] (default: 1)
40+
# resolver:
41+
# threads: [1+] (default: 2 per CPU)
4142
# tailer:
4243
# enabled: true
4344
# status_interval: 30
4445
archive:
4546
method: tar
4647
# tar:
47-
# compression: [none|gzip] (default: gzip - none if backup is already compressed)
48+
# compression: [none|gzip] (default: gzip, none if backup already compressed)
4849
# threads: [1+] (default: 1 per CPU)
4950
# zbackup:
5051
# binary: [path] (default: /usr/bin/zbackup)
@@ -62,21 +63,26 @@ production:
6263
upload:
6364
method: none
6465
# remove_uploaded: [true|false] (default: false)
66+
# retries: [1+] (default: 5)
67+
# threads: [1+] (default: 4)
6568
# gs:
6669
# project_id: [Google Cloud Project ID]
67-
# access_key: [Google Cloud Storage Access Key]
68-
# secret_key: [Google Cloud Storage Secret Key]
70+
# access_key: [Google Cloud Storage Interoperability API Access Key]
71+
# secret_key: [Google Cloud Storage Interoperability API Secret Key]
6972
# bucket_name: [Google Cloud Storage Bucket Name]
7073
# bucket_prefix: [prefix] (default: /)
71-
# threads: [1+] (default: 1 per CPU)
74+
# rsync:
75+
# path: [Rsync Destination Path]
76+
# user: [SSH Username]
77+
# host: [SSH Hostname/IP]
78+
# port: [SSH Port Number] (default: 22)
79+
# delete: [true|false] (default: false)
7280
# s3:
7381
# region: [AWS S3 Region] (default: us-east-1)
7482
# access_key: [AWS S3 Access Key]
7583
# secret_key: [AWS S3 Secret Key]
7684
# bucket_name: [AWS S3 Bucket Name]
7785
# bucket_prefix: [prefix] (default: /)
78-
# threads: [1+] (default: 1 per CPU)
7986
# chunk_size_mb: [1+] (default: 50)
8087
# secure: [true|false] (default: true)
81-
# retries: [1+] (default: 5)
8288
# acl: [acl_str] (default: none)

mongodb_consistent_backup/Backup/Mongodump/Mongodump.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import os
22
import logging
3-
import sys
43

54
from math import floor
65
from subprocess import check_output
@@ -57,22 +56,21 @@ def parse_mongodump_version(self):
5756
raise OperationError("Could not parse mongodump --version output!")
5857

5958
def choose_compression(self):
60-
if self.can_gzip():
59+
if self.can_compress():
6160
if self.compression() == 'auto':
6261
logging.info("Mongodump binary supports gzip compression, auto-enabling gzip compression")
6362
self.compression('gzip')
6463
elif self.compression() == 'gzip':
6564
raise OperationError("mongodump gzip compression requested on binary that does not support gzip!")
6665

67-
def can_gzip(self):
66+
def can_compress(self):
6867
if os.path.isfile(self.binary) and os.access(self.binary, os.X_OK):
69-
logging.debug("Mongodump binary supports gzip")
68+
logging.debug("Mongodump binary supports gzip compression")
7069
if tuple("3.2.0".split(".")) <= tuple(self.version.split(".")):
7170
return True
7271
return False
7372
else:
74-
logging.fatal("Cannot find or execute the mongodump binary file %s!" % self.binary)
75-
sys.exit(1)
73+
raise OperationError("Cannot find or execute the mongodump binary file %s!" % self.binary)
7674

7775
def summary(self):
7876
return self._summary

mongodb_consistent_backup/Common/Config.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,13 @@ def check_required(self):
107107
try:
108108
self._get(key)
109109
except:
110-
raise mongodb_consistent_backup.Errors.OperationError('Field "%s" must be set via command-line or config file!' % key)
110+
raise mongodb_consistent_backup.Errors.OperationError(
111+
'Field "%s" (config file field: "%s.%s") must be set via command-line or config file!' % (
112+
key,
113+
self._config.environment,
114+
key
115+
)
116+
)
111117

112118
def parse(self):
113119
self._config.parse(self.cmdline)

mongodb_consistent_backup/Common/DB.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ def __init__(self, uri, config, do_replset=False, read_pref='primaryPreferred',
4040
self.ssl_client_cert_file = self.config.ssl.client_cert_file
4141
self.read_pref_tags = self.config.replication.read_pref_tags
4242

43+
self.username = self.config.username
44+
self.password = self.config.password
45+
self.authdb = self.config.authdb
46+
self.ssl_ca_file = self.config.ssl.ca_file
47+
self.ssl_crl_file = self.config.ssl.crl_file
48+
self.ssl_client_cert_file = self.config.ssl.client_cert_file
49+
4350
self.replset = None
4451
self._conn = None
4552
self._is_master = None

mongodb_consistent_backup/Common/Util.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import socket
22

33
from dateutil import parser
4+
from select import select
45

56
from mongodb_consistent_backup.Errors import OperationError
67

@@ -31,3 +32,23 @@ def validate_hostname(hostname):
3132
socket.getaddrinfo(hostname, None)
3233
except socket.error, e:
3334
raise OperationError("Could not resolve host '%s', error: %s" % (hostname, e))
35+
36+
37+
def wait_popen(process, stderr_callback, stdout_callback):
38+
try:
39+
while not process.returncode:
40+
poll = select([process.stderr.fileno(), process.stdout.fileno()], [], [])
41+
if len(poll) >= 1:
42+
for fd in poll[0]:
43+
if process.stderr and fd == process.stderr.fileno():
44+
stderr_callback(process.stderr.readline().rstrip())
45+
if process.stdout and fd == process.stdout.fileno():
46+
stdout_callback(process.stdout.readline().rstrip())
47+
if process.poll() is not None:
48+
break
49+
stderr, stdout = process.communicate()
50+
stderr_callback(stderr.rstrip())
51+
stdout_callback(stdout.rstrip())
52+
except Exception, e:
53+
raise e
54+
return True

mongodb_consistent_backup/Common/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
from Lock import Lock # NOQA
55
from MongoUri import MongoUri # NOQA
66
from Timer import Timer # NOQA
7-
from Util import config_to_string, is_datetime, parse_method, validate_hostname # NOQA
7+
from Util import config_to_string, is_datetime, parse_method, validate_hostname, wait_popen # NOQA

mongodb_consistent_backup/Pipeline/Stage.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ def close(self):
6464
def is_compressed(self):
6565
if self.has_task() and hasattr(self._task, "is_compressed"):
6666
return self._task.is_compressed()
67+
return False
6768

6869
def compression(self, task=None):
6970
if self.has_task() and hasattr(self._task, "compression"):

mongodb_consistent_backup/Pipeline/Task.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ def compression(self, method=None):
3939
return parse_method(self.compression_method)
4040

4141
def is_compressed(self):
42-
if self.compression() != 'none':
42+
if self.compression() == 'auto' and hasattr(self, "can_compress"):
43+
return self.can_compress()
44+
elif self.compression() != 'none':
4345
return True
4446
return False
4547

0 commit comments

Comments
 (0)