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:
parent
ce14b9a4c3
commit
bfef75d33f
@ -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)
|
||||
|
@ -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]
|
||||
|
1
os_apply_config/tests/chown_templates/group.gid
Normal file
1
os_apply_config/tests/chown_templates/group.gid
Normal file
@ -0,0 +1 @@
|
||||
lorem gido
|
1
os_apply_config/tests/chown_templates/group.gid.oac
Normal file
1
os_apply_config/tests/chown_templates/group.gid.oac
Normal file
@ -0,0 +1 @@
|
||||
group: 0
|
1
os_apply_config/tests/chown_templates/group.name
Normal file
1
os_apply_config/tests/chown_templates/group.name
Normal file
@ -0,0 +1 @@
|
||||
namo gido
|
1
os_apply_config/tests/chown_templates/group.name.oac
Normal file
1
os_apply_config/tests/chown_templates/group.name.oac
Normal file
@ -0,0 +1 @@
|
||||
group: root
|
1
os_apply_config/tests/chown_templates/owner.name
Normal file
1
os_apply_config/tests/chown_templates/owner.name
Normal file
@ -0,0 +1 @@
|
||||
namo uido
|
1
os_apply_config/tests/chown_templates/owner.name.oac
Normal file
1
os_apply_config/tests/chown_templates/owner.name.oac
Normal file
@ -0,0 +1 @@
|
||||
owner: root
|
1
os_apply_config/tests/chown_templates/owner.uid
Normal file
1
os_apply_config/tests/chown_templates/owner.uid
Normal file
@ -0,0 +1 @@
|
||||
lorem uido
|
1
os_apply_config/tests/chown_templates/owner.uid.oac
Normal file
1
os_apply_config/tests/chown_templates/owner.uid.oac
Normal file
@ -0,0 +1 @@
|
||||
owner: 0
|
@ -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)
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user