mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
Merge pull request #930 from tonioo/feature/map_files_upgrade
Upgrade mode.
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
"""Test map files generation."""
|
||||
|
||||
import os
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.management import call_command
|
||||
from django.test import TestCase
|
||||
|
||||
from modoboa.core.utils import parse_map_file
|
||||
from modoboa.lib.test_utils import MapFilesTestCaseMixin
|
||||
|
||||
|
||||
@@ -14,3 +19,38 @@ class MapFilesTestCase(MapFilesTestCaseMixin, TestCase):
|
||||
"sql-maintain.cf", "sql-sender-login-mailboxes.cf",
|
||||
"sql-sender-login-mailboxes-extra.cf", "sql-sender-login-aliases.cf"
|
||||
]
|
||||
|
||||
def test_map_upgrade(self):
|
||||
"""Check that map content is used."""
|
||||
dburl = "postgres://user:password@localhost/testdb"
|
||||
call_command(
|
||||
"generate_postfix_maps",
|
||||
"--dburl", dburl, "--destdir", self.workdir)
|
||||
|
||||
# Now upgrade files. Map credentials should be preserved.
|
||||
call_command("generate_postfix_maps", "--destdir", self.workdir)
|
||||
for f in self.MAP_FILES:
|
||||
mapcontent = parse_map_file(os.path.join(self.workdir, f))
|
||||
self.assertEqual(mapcontent["user"], "user")
|
||||
self.assertEqual(mapcontent["password"], "password")
|
||||
self.assertEqual(mapcontent["dbname"], "testdb")
|
||||
|
||||
# Now force overwrite, credentials should be different
|
||||
call_command(
|
||||
"generate_postfix_maps", "--destdir", self.workdir,
|
||||
"--force-overwrite")
|
||||
dbsettings = settings.DATABASES["default"]
|
||||
for f in self.MAP_FILES:
|
||||
mapcontent = parse_map_file(os.path.join(self.workdir, f))
|
||||
self.assertEqual(mapcontent["user"], dbsettings["USER"])
|
||||
self.assertEqual(mapcontent["password"], dbsettings["PASSWORD"])
|
||||
self.assertEqual(mapcontent["dbname"], dbsettings["NAME"])
|
||||
|
||||
# Now modify a file manually
|
||||
path = os.path.join(self.workdir, "sql-domains.cf")
|
||||
with open(path, "a") as fp:
|
||||
fp.write("pouet")
|
||||
call_command("generate_postfix_maps", "--destdir", self.workdir)
|
||||
with open(path) as fp:
|
||||
content = fp.read()
|
||||
self.assertIn("pouet", content)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""Management command to generate/update postfix map files."""
|
||||
|
||||
import copy
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -11,6 +13,7 @@ from django.utils import timezone
|
||||
import dj_database_url
|
||||
|
||||
from ... import signals
|
||||
from ... import utils
|
||||
|
||||
MAP_FILE_TEMPLATE = """# This file was generated on {{ date }} by running:
|
||||
# {{ commandline }}
|
||||
@@ -30,6 +33,23 @@ class Command(BaseCommand):
|
||||
parser.add_argument(
|
||||
"--destdir", default=".",
|
||||
help="Directory where files will be created")
|
||||
parser.add_argument(
|
||||
"--force-overwrite", action="store_true", default=False,
|
||||
help="Force overwrite of existing map files")
|
||||
|
||||
def __load_checksums(self, destdir):
|
||||
"""Load existing checksums if possible."""
|
||||
self.__checksums_file = os.path.join(
|
||||
destdir, "modoboa-postfix-maps.chk")
|
||||
self.__checksums = {}
|
||||
if not os.path.exists(self.__checksums_file):
|
||||
return
|
||||
with open(self.__checksums_file) as fp:
|
||||
for line in fp:
|
||||
fname, dbtype, checksum = line.split(":")
|
||||
self.__checksums[fname.strip()] = {
|
||||
"dbtype": dbtype, "checksum": checksum.strip()
|
||||
}
|
||||
|
||||
def __register_map_files(self):
|
||||
"""Load specified applications."""
|
||||
@@ -39,6 +59,18 @@ class Command(BaseCommand):
|
||||
mapfiles += response[1]
|
||||
return mapfiles
|
||||
|
||||
def __check_file(self, path):
|
||||
"""Check if map file has been modified."""
|
||||
fname = os.path.basename(path)
|
||||
condition = (
|
||||
not self.__checksums or
|
||||
fname not in self.__checksums)
|
||||
if condition:
|
||||
return True
|
||||
with open(path) as fp:
|
||||
checksum = hashlib.md5(fp.read()).hexdigest()
|
||||
return checksum == self.__checksums[fname]["checksum"]
|
||||
|
||||
def get_template(self, dbtype):
|
||||
"""Return map file template."""
|
||||
tplcontent = MAP_FILE_TEMPLATE
|
||||
@@ -80,8 +112,23 @@ query = {{ query|safe }}
|
||||
}
|
||||
return context
|
||||
|
||||
def __render_map_file(self, mapobject, destdir, context):
|
||||
def __render_map_file(
|
||||
self, mapobject, destdir, context, force_overwrite=False):
|
||||
"""Render a map file."""
|
||||
fullpath = os.path.join(destdir, mapobject.filename)
|
||||
if os.path.exists(fullpath) and not force_overwrite:
|
||||
if not self.__check_file(fullpath):
|
||||
print(
|
||||
"Cannot upgrade '{}' map because it has been modified."
|
||||
.format(mapobject.filename))
|
||||
return self.__checksums[mapobject.filename]
|
||||
mapcontent = utils.parse_map_file(fullpath)
|
||||
context = copy.deepcopy(context)
|
||||
context["dbtype"] = self.__checksums[mapobject.filename]["dbtype"]
|
||||
context["dbuser"] = mapcontent["user"]
|
||||
context["dbpass"] = mapcontent["password"]
|
||||
context["dbname"] = mapcontent["dbname"]
|
||||
context["dbhost"] = mapcontent["hosts"]
|
||||
content = self.get_template(context["dbtype"]).render(
|
||||
Context(
|
||||
dict(context.items(),
|
||||
@@ -90,7 +137,8 @@ query = {{ query|safe }}
|
||||
)
|
||||
fullpath = os.path.join(destdir, mapobject.filename)
|
||||
with open(fullpath, "w") as fp:
|
||||
fp.write("{}\n".format(content))
|
||||
fp.write(content)
|
||||
return hashlib.md5(content).hexdigest()
|
||||
|
||||
def handle(self, *args, **options):
|
||||
"""Command entry point."""
|
||||
@@ -100,6 +148,15 @@ query = {{ query|safe }}
|
||||
os.mkdir(destdir)
|
||||
except OSError:
|
||||
pass
|
||||
self.__load_checksums(destdir)
|
||||
context = self.get_template_context(options)
|
||||
checksums = {}
|
||||
for mapobject in mapfiles:
|
||||
self.__render_map_file(mapobject, destdir, context)
|
||||
checksum = self.__render_map_file(
|
||||
mapobject, destdir, context,
|
||||
force_overwrite=options["force_overwrite"])
|
||||
checksums[mapobject.filename] = checksum
|
||||
with open(self.__checksums_file, "w") as fp:
|
||||
for fname, checksum in checksums.items():
|
||||
fp.write("{}:{}:{}\n".format(
|
||||
fname, context["dbtype"], checksum))
|
||||
|
||||
@@ -11,6 +11,18 @@ from modoboa.lib.api_client import ModoAPIClient
|
||||
from . import models
|
||||
|
||||
|
||||
def parse_map_file(path):
|
||||
"""Parse a postfix map file and return values."""
|
||||
content = {}
|
||||
with open(path) as fp:
|
||||
for line in fp:
|
||||
if not line or line.startswith("#"):
|
||||
continue
|
||||
name, value = line.split("=", 1)
|
||||
content[name.strip()] = value.strip()
|
||||
return content
|
||||
|
||||
|
||||
def check_for_updates(request):
|
||||
"""Check if a new version of Modoboa is available."""
|
||||
local_config = models.LocalConfig.objects.first()
|
||||
|
||||
Reference in New Issue
Block a user