Add mode to metadata header

-- os-apply-config --
  mode: 0644
  -- end --

Mode must be an integer in [0, 0777]. Preferably this will be written as
three octal digits. The default is to use the mode of the target file if
it already exists, otherwise 0644.

Advances blueprint oac-header

Change-Id: I3abfee89d418474b3766a97cfc178c02252e2d02
This commit is contained in:
Alexis Lee 2014-04-17 11:25:08 +01:00
parent 0d39cd5e11
commit 638d62518e
4 changed files with 60 additions and 7 deletions

View File

@ -69,7 +69,8 @@ CONTROL_FILE_SUFFIX = ".oac"
class OacFile(object):
DEFAULTS = {
'allow_empty': True
'allow_empty': True,
'mode': None,
}
def __init__(self, body, **kwargs):
@ -120,6 +121,23 @@ class OacFile(object):
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,
@ -166,6 +184,8 @@ def write_file(path, obj):
mode, uid, gid = stat.st_mode, stat.st_uid, stat.st_gid
else:
mode, uid, gid = 0o644, -1, -1
mode = obj.mode or mode
d = os.path.dirname(path)
os.path.exists(d) or os.makedirs(d)
with tempfile.NamedTemporaryFile(dir=d, delete=False) as newfile:

View File

@ -0,0 +1 @@
lorem modus

View File

@ -0,0 +1 @@
mode: 0755

View File

@ -23,7 +23,7 @@ import mock
import testtools
from os_apply_config import apply_config
from os_apply_config import config_exception
from os_apply_config import config_exception as exc
# example template tree
TEMPLATES = os.path.join(os.path.dirname(__file__), 'templates')
@ -32,6 +32,7 @@ TEMPLATE_PATHS = [
"/etc/keystone/keystone.conf",
"/etc/control/empty",
"/etc/control/allow_empty",
"/etc/control/mode",
]
# config for example tree
@ -63,6 +64,8 @@ OUTPUT = {
"foo\n"),
"/etc/control/allow_empty": apply_config.OacFile(
"").set('allow_empty', False),
"/etc/control/mode": apply_config.OacFile(
"lorem modus\n").set('mode', 0o755),
}
@ -232,7 +235,7 @@ class OSConfigApplierTestCase(testtools.TestCase):
self.assertEqual("abc\n", apply_config.render_template(template(
"/etc/glance/script.conf"), {"x": "abc"}))
self.assertRaises(
config_exception.ConfigException,
exc.ConfigException,
apply_config.render_template,
template("/etc/glance/script.conf"), {})
@ -241,7 +244,7 @@ class OSConfigApplierTestCase(testtools.TestCase):
bt_path = os.path.join(tdir.path, 'bad_template')
with open(bt_path, 'w') as bt:
bt.write("{{#foo}}bar={{bar}}{{/bar}}")
e = self.assertRaises(config_exception.ConfigException,
e = self.assertRaises(exc.ConfigException,
apply_config.render_template,
bt_path, {'foo': [{'bar':
'abc'}]})
@ -263,7 +266,7 @@ class OSConfigApplierTestCase(testtools.TestCase):
def test_render_executable_failure(self):
self.assertRaises(
config_exception.ConfigException,
exc.ConfigException,
apply_config.render_executable,
template("/etc/glance/script.conf"), {})
@ -277,9 +280,9 @@ class OSConfigApplierTestCase(testtools.TestCase):
def test_strip_hash(self):
h = {'a': {'b': {'x': 'y'}}, "c": [1, 2, 3]}
self.assertEqual({'x': 'y'}, apply_config.strip_hash(h, 'a.b'))
self.assertRaises(config_exception.ConfigException,
self.assertRaises(exc.ConfigException,
apply_config.strip_hash, h, 'a.nonexistent')
self.assertRaises(config_exception.ConfigException,
self.assertRaises(exc.ConfigException,
apply_config.strip_hash, h, 'a.c')
def test_load_list_from_json(self):
@ -328,3 +331,31 @@ class OSConfigApplierTestCase(testtools.TestCase):
with mock.patch('os.path.isdir', lambda x: (x == default or
x == deprecated)):
self.assertEqual(default, apply_config.templates_dir())
def test_control_mode(self):
path = self.write_config(CONFIG)
tmpdir = tempfile.mkdtemp()
template = "/etc/control/mode"
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