Skip to content

Commit ccf601a

Browse files
committed
add zone-specific config files
1 parent 0029221 commit ccf601a

3 files changed

Lines changed: 43 additions & 12 deletions

File tree

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ trim_trailing_whitespace = true
1212
[*.md]
1313
indent_style = space
1414
indent_size = 2
15-
insert_final_newline = false
15+
insert_final_newline = true
1616
trim_trailing_whitespace = false
1717

1818
[bindtool]

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ it will be searched for in the same locations,
3232
e.g. `bindtool --config config.json` will load `./config.json`, `/etc/bindtool/config.json`, or `<install-dir>/config.json`.
3333
The file must adhere to standard JSON format.
3434

35+
Zone-specific config files may be placed along side the config file with the name `bindtoool.<zone-name>.json`.
36+
If present, values in a zone-specific config file will override those in the config file when processing that zone.
37+
3538
#### Defaults
3639

3740
The `defaults` section specifies the default values for all of the arguments for the various record commands.

bindtool

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import re
8787
import subprocess
8888
import sys
8989
import unicodedata
90+
from collections import abc
9091

9192
import DNS
9293

@@ -137,7 +138,8 @@ class BindTool:
137138
if (self.args.debug):
138139
sys.excepthook = debug_hook
139140

140-
self.config, self.config_file_path = self._load_config(self.args.config_path, ('.', os.path.join('/etc', self.script_name), script_dir))
141+
zone_name = os.path.basename(self.args.zone_file_path)
142+
self.config, self.config_dir = self._load_config(self.args.config_path, ('.', os.path.join('/etc', self.script_name), script_dir), zone_name)
141143
self._config_defaults = {
142144
'defaults': {
143145
'soa': {
@@ -261,17 +263,43 @@ class BindTool:
261263
self.certificates = {}
262264
self.public_keys = {}
263265

264-
def _load_config(self, file_path, search_paths=[]):
266+
def _zone_config_file_path(self, config_file_path, zone_name):
267+
config_dir, config_filename = os.path.split(config_file_path)
268+
if ('.' in config_filename):
269+
name, extension = config_filename.rsplit('.', 1)
270+
return os.path.join(config_dir, f'{name}.{zone_name}.{extension}')
271+
return os.path.join(config_dir, f'{name}.{zone_name}')
272+
273+
def _merge_config(self, base, extra):
274+
if (base is None):
275+
base = collections.OrderedDict()
276+
for name, value in extra.items():
277+
if (isinstance(value, abc.Mapping)):
278+
base[name] = self._merge_config(base.get(name), value)
279+
else:
280+
base[name] = value
281+
return base
282+
283+
def _load_config_file(self, config_file_path):
284+
if (os.path.isfile(config_file_path)):
285+
try:
286+
with open(config_file_path) as config_file:
287+
return json.load(config_file, object_pairs_hook=collections.OrderedDict)
288+
except Exception as error:
289+
self._error('Error reading config file ', config_file_path, ': ', error, '\n')
290+
return {}
291+
292+
def _load_config(self, file_path, search_paths, zone_name):
265293
search_paths = [''] if (os.path.isabs(file_path)) else search_paths
266-
for search_path in search_paths:
294+
for search_path in reversed(search_paths):
267295
config_file_path = os.path.join(search_path, file_path)
268-
if (os.path.isfile(config_file_path)):
269-
try:
270-
with open(config_file_path) as config_file:
271-
return (json.load(config_file, object_pairs_hook=collections.OrderedDict), os.path.abspath(config_file_path))
272-
except Exception as error:
273-
self._error('Error reading config file ', config_file_path, ': ', error, '\n')
274-
return (collections.OrderedDict(), '')
296+
zone_config_file_path = self._zone_config_file_path(config_file_path, zone_name)
297+
if (os.path.isfile(config_file_path) or os.path.isfile(zone_config_file_path)):
298+
config = None
299+
config = self._merge_config(config, self._load_config_file(config_file_path))
300+
config = self._merge_config(config, self._load_config_file(zone_config_file_path))
301+
return (config, os.path.dirname(os.path.abspath(config_file_path)))
302+
return (config, '')
275303

276304
def _message(self, *args):
277305
message = ''
@@ -306,7 +334,7 @@ class BindTool:
306334

307335
def _directory(self, file_type):
308336
directory = self._config('directories', file_type, '')
309-
return os.path.normpath(os.path.join(os.path.dirname(self.config_file_path), directory)) if (directory) else directory
337+
return os.path.normpath(os.path.join(self.config_dir, directory)) if (directory) else directory
310338

311339
def _key_type_suffix(self, key_type):
312340
return self._config('key_type_suffixes', key_type, '')

0 commit comments

Comments
 (0)