diff --git a/os_apply_config/apply_config.py b/os_apply_config/apply_config.py index fa609ea..40c268a 100755 --- a/os_apply_config/apply_config.py +++ b/os_apply_config/apply_config.py @@ -22,11 +22,11 @@ import sys import tempfile from pystache import context -import six import yaml from os_apply_config import collect_config from os_apply_config import config_exception as exc +from os_apply_config import oac_file from os_apply_config import renderers from os_apply_config import value_types from os_apply_config import version @@ -67,78 +67,6 @@ OS_CONFIG_FILES_PATH_OLD = '/var/run/os-collect-config/os_config_files.json' CONTROL_FILE_SUFFIX = ".oac" -class OacFile(object): - DEFAULTS = { - 'allow_empty': True, - 'mode': None, - } - - def __init__(self, body, **kwargs): - super(OacFile, self).__init__() - self.body = body - - for k, v in six.iteritems(self.DEFAULTS): - setattr(self, '_' + k, v) - - for k, v in six.iteritems(kwargs): - if not hasattr(self, k): - raise exc.ConfigException( - "unrecognised file control key '%s'" % (k)) - setattr(self, k, v) - - def __eq__(self, other): - if type(other) is type(self): - return self.__dict__ == other.__dict__ - return False - - def __repr__(self): - a = ["OacFile(%s" % repr(self.body)] - for key, default in six.iteritems(self.DEFAULTS): - value = getattr(self, key) - if value != default: - a.append("%s=%s" % (key, repr(value))) - return ", ".join(a) + ")" - - def set(self, key, value): - """Allows setting attrs as an expression rather than a statement.""" - setattr(self, key, value) - return self - - @property - def allow_empty(self): - """Returns allow_empty. - - If True and body='', no file will be created and any existing - file will be deleted. - """ - return self._allow_empty - - @allow_empty.setter - def allow_empty(self, value): - if type(value) is not bool: - raise exc.ConfigException( - "allow_empty requires Boolean, got: '%s'" % value) - self._allow_empty = value - return self - - @property - def mode(self): - """The permissions to set on the file, EG 0755.""" - return self._mode - - @mode.setter - def mode(self, v): - """Returns the file mode. - - EG 0644. Must be between 0 and 0777, the sticky bit is not supported. - """ - if type(v) is not int: - raise exc.ConfigException("mode '%s' is not numeric" % v) - if not 0 <= v <= 0o777: - raise exc.ConfigException("mode '%#o' out of range" % v) - self._mode = v - - def install_config( config_path, template_root, output_path, validate, subhash=None, fallback_metadata=None): @@ -212,7 +140,7 @@ def build_tree(templates, config): if not isinstance(ctrl_dict, dict): raise exc.ConfigException( "header is not a dict: %s" % in_file) - res[out_file] = OacFile(body, **ctrl_dict) + res[out_file] = oac_file.OacFile(body, **ctrl_dict) except exc.ConfigException as e: e.args += in_file, raise diff --git a/os_apply_config/oac_file.py b/os_apply_config/oac_file.py new file mode 100644 index 0000000..17c54f3 --- /dev/null +++ b/os_apply_config/oac_file.py @@ -0,0 +1,90 @@ +# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import six + +from os_apply_config import config_exception as exc + + +class OacFile(object): + DEFAULTS = { + 'allow_empty': True, + 'mode': None, + } + + def __init__(self, body, **kwargs): + super(OacFile, self).__init__() + self.body = body + + for k, v in six.iteritems(self.DEFAULTS): + setattr(self, '_' + k, v) + + for k, v in six.iteritems(kwargs): + if not hasattr(self, k): + raise exc.ConfigException( + "unrecognised file control key '%s'" % (k)) + setattr(self, k, v) + + def __eq__(self, other): + if type(other) is type(self): + return self.__dict__ == other.__dict__ + return False + + def __repr__(self): + a = ["OacFile(%s" % repr(self.body)] + for key, default in six.iteritems(self.DEFAULTS): + value = getattr(self, key) + if value != default: + a.append("%s=%s" % (key, repr(value))) + return ", ".join(a) + ")" + + def set(self, key, value): + """Allows setting attrs as an expression rather than a statement.""" + setattr(self, key, value) + return self + + @property + def allow_empty(self): + """Returns allow_empty. + + If True and body='', no file will be created and any existing + file will be deleted. + """ + return self._allow_empty + + @allow_empty.setter + def allow_empty(self, value): + if type(value) is not bool: + raise exc.ConfigException( + "allow_empty requires Boolean, got: '%s'" % value) + self._allow_empty = value + return self + + @property + def mode(self): + """The permissions to set on the file, EG 0755.""" + return self._mode + + @mode.setter + def mode(self, v): + """Returns the file mode. + + EG 0644. Must be between 0 and 0777, the sticky bit is not supported. + """ + if type(v) is not int: + raise exc.ConfigException("mode '%s' is not numeric" % v) + if not 0 <= v <= 0o777: + raise exc.ConfigException("mode '%#o' out of range" % v) + self._mode = v diff --git a/os_apply_config/tests/test_apply_config.py b/os_apply_config/tests/test_apply_config.py index 16319b5..58f6323 100644 --- a/os_apply_config/tests/test_apply_config.py +++ b/os_apply_config/tests/test_apply_config.py @@ -24,6 +24,7 @@ import testtools from os_apply_config import apply_config from os_apply_config import config_exception as exc +from os_apply_config import oac_file # example template tree TEMPLATES = os.path.join(os.path.dirname(__file__), 'templates') @@ -56,15 +57,15 @@ CONFIG_SUBHASH = { # expected output for example tree OUTPUT = { - "/etc/glance/script.conf": apply_config.OacFile( + "/etc/glance/script.conf": oac_file.OacFile( "foo\n"), - "/etc/keystone/keystone.conf": apply_config.OacFile( + "/etc/keystone/keystone.conf": oac_file.OacFile( "[foo]\ndatabase = sqlite:///blah\n"), - "/etc/control/empty": apply_config.OacFile( + "/etc/control/empty": oac_file.OacFile( "foo\n"), - "/etc/control/allow_empty": apply_config.OacFile( + "/etc/control/allow_empty": oac_file.OacFile( "").set('allow_empty', False), - "/etc/control/mode": apply_config.OacFile( + "/etc/control/mode": oac_file.OacFile( "lorem modus\n").set('mode', 0o755), } @@ -339,23 +340,3 @@ class OSConfigApplierTestCase(testtools.TestCase): target_file = os.path.join(tmpdir, template[1:]) apply_config.install_config([path], TEMPLATES, tmpdir, False) self.assertEqual(0o100755, os.stat(target_file).st_mode) - - def test_control_mode_string(self): - oac_file = apply_config.OacFile('') - mode = '0644' - try: - oac_file.mode = mode - except exc.ConfigException as e: - self.assertIn("mode '%s' is not numeric" % mode, str(e)) - - def test_control_mode_range(self): - oac_file = apply_config.OacFile('') - for mode in [-1, 0o1000]: - try: - oac_file.mode = mode - except exc.ConfigException as e: - self.assertTrue("mode '%#o' out of range" % mode in str(e), - "mode: %#o" % mode) - - for mode in [0, 0o777]: - oac_file.mode = mode diff --git a/os_apply_config/tests/test_oac_file.py b/os_apply_config/tests/test_oac_file.py new file mode 100644 index 0000000..26b686f --- /dev/null +++ b/os_apply_config/tests/test_oac_file.py @@ -0,0 +1,41 @@ +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import testtools + +from os_apply_config import config_exception as exc +from os_apply_config import oac_file + + +class OacFileTestCase(testtools.TestCase): + def test_mode_string(self): + oacf = oac_file.OacFile('') + mode = '0644' + try: + oacf.mode = mode + except exc.ConfigException as e: + self.assertIn("mode '%s' is not numeric" % mode, str(e)) + + def test_mode_range(self): + oacf = oac_file.OacFile('') + for mode in [-1, 0o1000]: + try: + oacf.mode = mode + except exc.ConfigException as e: + self.assertTrue("mode '%#o' out of range" % mode in str(e), + "mode: %#o" % mode) + + for mode in [0, 0o777]: + oacf.mode = mode