Set owner+group from control file

Add keys 'uid' and 'gid' to control file. The values may be UID/GIDs or
names. In either case, the value is checked against the passwd/group
database to ensure it exists.

Change-Id: I1730a126cf024755635043b1a0f70006ab22c046
This commit is contained in:
Alexis Lee 2014-10-17 11:39:21 +01:00 committed by Alexis Lee
parent ce14b9a4c3
commit bfef75d33f
12 changed files with 137 additions and 8 deletions

View File

@ -113,6 +113,10 @@ def write_file(path, obj):
else:
mode, uid, gid = 0o644, -1, -1
mode = obj.mode or mode
if obj.owner is not None:
uid = obj.owner
if obj.group is not None:
gid = obj.group
d = os.path.dirname(path)
os.path.exists(d) or os.makedirs(d)

View File

@ -13,6 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import grp
import pwd
import six
from os_apply_config import config_exception as exc
@ -22,6 +25,8 @@ class OacFile(object):
DEFAULTS = {
'allow_empty': True,
'mode': None,
'owner': None,
'group': None,
}
def __init__(self, body, **kwargs):
@ -79,7 +84,7 @@ class OacFile(object):
@mode.setter
def mode(self, v):
"""Returns the file mode.
"""Pass in the mode to set on the file.
EG 0644. Must be between 0 and 0777, the sticky bit is not supported.
"""
@ -88,3 +93,51 @@ class OacFile(object):
if not 0 <= v <= 0o777:
raise exc.ConfigException("mode '%#o' out of range" % v)
self._mode = v
@property
def owner(self):
"""The UID to set on the file, EG 'rabbitmq' or '501'."""
return self._owner
@owner.setter
def owner(self, v):
"""Pass in the UID to set on the file.
EG 'rabbitmq' or 501.
"""
try:
if type(v) is int:
user = pwd.getpwuid(v)
elif type(v) is str:
user = pwd.getpwnam(v)
else:
raise exc.ConfigException(
"owner '%s' must be a string or int" % v)
except KeyError:
raise exc.ConfigException(
"owner '%s' not found in passwd database" % v)
self._owner = user[2]
@property
def group(self):
"""The GID to set on the file, EG 'rabbitmq' or '501'."""
return self._group
@group.setter
def group(self, v):
"""Pass in the GID to set on the file.
EG 'rabbitmq' or 501.
"""
try:
if type(v) is int:
group = grp.getgrgid(v)
elif type(v) is str:
group = grp.getgrnam(v)
else:
raise exc.ConfigException(
"group '%s' must be a string or int" % v)
except KeyError:
raise exc.ConfigException(
"group '%s' not found in group database" % v)
self._group = group[2]

View File

@ -0,0 +1 @@
lorem gido

View File

@ -0,0 +1 @@
group: 0

View File

@ -0,0 +1 @@
namo gido

View File

@ -0,0 +1 @@
group: root

View File

@ -0,0 +1 @@
namo uido

View File

@ -0,0 +1 @@
owner: root

View File

@ -0,0 +1 @@
lorem uido

View File

@ -0,0 +1 @@
owner: 0

View File

@ -28,13 +28,6 @@ from os_apply_config import oac_file
# example template tree
TEMPLATES = os.path.join(os.path.dirname(__file__), 'templates')
TEMPLATE_PATHS = [
"/etc/glance/script.conf",
"/etc/keystone/keystone.conf",
"/etc/control/empty",
"/etc/control/allow_empty",
"/etc/control/mode",
]
# config for example tree
CONFIG = {
@ -68,6 +61,17 @@ OUTPUT = {
"/etc/control/mode": oac_file.OacFile(
"lorem modus\n").set('mode', 0o755),
}
TEMPLATE_PATHS = OUTPUT.keys()
# expected output for chown tests
# separated out to avoid needing to mock os.chown for most tests
CHOWN_TEMPLATES = os.path.join(os.path.dirname(__file__), 'chown_templates')
CHOWN_OUTPUT = {
"owner.uid": oac_file.OacFile("lorem uido\n").set('owner', 0),
"owner.name": oac_file.OacFile("namo uido\n").set('owner', 0),
"group.gid": oac_file.OacFile("lorem gido\n").set('group', 0),
"group.name": oac_file.OacFile("namo gido\n").set('group', 0),
}
def main_path():
@ -340,3 +344,14 @@ 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)
@mock.patch('os.chown')
def test_control_chown(self, chown_mock):
path = self.write_config(CONFIG)
tmpdir = tempfile.mkdtemp()
apply_config.install_config([path], CHOWN_TEMPLATES, tmpdir, False)
chown_mock.assert_has_calls([mock.call(mock.ANY, 0, -1), # uid
mock.call(mock.ANY, 0, -1), # username
mock.call(mock.ANY, -1, 0), # gid
mock.call(mock.ANY, -1, 0)], # groupname
any_order=True)

View File

@ -13,6 +13,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import grp
import pwd
import testtools
from os_apply_config import config_exception as exc
@ -39,3 +42,49 @@ class OacFileTestCase(testtools.TestCase):
for mode in [0, 0o777]:
oacf.mode = mode
def test_owner_positive(self):
oacf = oac_file.OacFile('')
users = pwd.getpwall()
for name in [user[0] for user in users]:
oacf.owner = name
for uid in [user[2] for user in users]:
oacf.owner = uid
def test_owner_negative(self):
oacf = oac_file.OacFile('')
try:
user = -1
oacf.owner = user
except exc.ConfigException as e:
self.assertIn(
"owner '%s' not found in passwd database" % user, str(e))
try:
user = "za"
oacf.owner = user
except exc.ConfigException as e:
self.assertIn(
"owner '%s' not found in passwd database" % user, str(e))
def test_group_positive(self):
oacf = oac_file.OacFile('')
groups = grp.getgrall()
for name in [group[0] for group in groups]:
oacf.group = name
for gid in [group[2] for group in groups]:
oacf.group = gid
def test_group_negative(self):
oacf = oac_file.OacFile('')
try:
group = -1
oacf.group = group
except exc.ConfigException as e:
self.assertIn(
"group '%s' not found in group database" % group, str(e))
try:
group = "za"
oacf.group = group
except exc.ConfigException as e:
self.assertIn(
"group '%s' not found in group database" % group, str(e))