#7312 Turn installutils.set_directive() into a context manager
Closed: fixed Opened by cheimes.

Related to #7288

The installutils.set_directive() reads the file, changes a line and writes the full file to disk again. That's a rather slow and tedious approach in case of 20, 25 installutils.set_directive() in a row, e.g. ipaserver/install/cainstance.py. We should turn the code into a proper context manager that reads and writes the file only one time. The code is also missing a chmod and should use atomic sync of file and directory inode.

interface

Here is the basic interface. I leave the rest to the implementer.

SENTINEL = object()
class DirectiveSetter(object):
    def __init__(self, filename, quotes=True, separator=''):
        self.filename = filename
        self.quotes = quotes
        self.separator = separator
        self.lines = None
        self.stat = None
    def __enter__(self):
        with open(self.filename) as f:
            self.stat = os.fstat(f.fileno())
            self.lines = list(f)
    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is None:
            # write to temporary file in same directory as self.filename
            # os.fchmod(...)
            # os.fchown(...)
            # flush_sync(...)
            # os.rename()
    def set(self, directive, value, quotes=SENTINEL, separator=SENTINEL):
        if quotes is SENTINEL:
            quotes = self.quotes
        if separator is SENTINEL:
            separator = self.separator
        set_directive_lines(...)

usage

with DirectiveSetter(paths.CA_CS_CFG_PATH, quotes=False, separator='=') as d:
    d.set('ca.publish.enable', 'true')
    d.set('ca.publish.ldappublish.enable', 'false')
    ...

or

with DirectiveSetter(filename) as ds:
    ds.setitems([
        ('key1', 'value1'),
        ('key2', 'value2'),
        ...
        ('key99', 'value99'),
    ])

Implemented as part of PR https://github.com/freeipa/freeipa/pull/1379

Metadata Update from @cheimes:
- Issue assigned to cheimes

master:

  • 2546ef6eb0c6321da810f42d0311b7053be43d62 Prevent set_directive from clobbering other keys
  • 1b04718b3c57eed2131db018b9d4e46c8d7f7345 pep8: reduce line lengths in CAInstance.__enable_crl_publish
  • c77f3a50d7a8d4b2f1e6fb7c95115a36a4ec6daa installutils: refactor set_directive
  • f688b5d8a7ff340a4f358e99bfe219167832359e Add tests for installutils.set_directive
  • f4001e1c53a263aa6c7f62385ed394631345a34c Add safe DirectiveSetter context manager

ipa-4-6:

  • fd316b94648c9df758e7bdbe741ac84248e6987c Prevent set_directive from clobbering other keys
  • 7a29a5dc9bcabcc1683dc80f1b613e742b7d7315 pep8: reduce line lengths in CAInstance.__enable_crl_publish
  • 241b83ded0a9e6368262e3ddce2beb602685017a installutils: refactor set_directive
  • 808b14351c1a39d287690f43be344b13c4944442 Add tests for installutils.set_directive
  • 342a141225cc51d31f78810f799511d6fe310864 Add safe DirectiveSetter context manager

ipa-4-5:

  • c60fcac09fe8a6050330f2c1e0a2662896e07708 Prevent set_directive from clobbering other keys
  • 929491d75d27ad365b1c22b7ce4803dfe8364b43 pep8: reduce line lengths in CAInstance.__enable_crl_publish
  • a1a58539c9b2ff33ad90391354db03b343fc9bd5 installutils: refactor set_directive
  • d3af8f69d7ed35a87f9547e4c9fa2ffc95df5591 Add tests for installutils.set_directive
  • a70ce13da6668e14ebc113fd00fea21eb460bf56 Add safe DirectiveSetter context manager
  • 1b8710109e4729b93fd2a4ea39969fc4ed1e336f Old pylint doesn't support bad python3 option

Metadata Update from @pvoborni:
- Issue close_status updated to: fixed
- Issue set to the milestone: FreeIPA 4.5.5
- Issue status updated to: Closed (was: Open)

Metadata