Merge "Set owner+group from control file"
This commit is contained in:
commit
312d741eaf
@ -119,6 +119,10 @@ def write_file(path, obj):
|
|||||||
else:
|
else:
|
||||||
mode, uid, gid = 0o644, -1, -1
|
mode, uid, gid = 0o644, -1, -1
|
||||||
mode = obj.mode or mode
|
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)
|
d = os.path.dirname(path)
|
||||||
os.path.exists(d) or os.makedirs(d)
|
os.path.exists(d) or os.makedirs(d)
|
||||||
|
@ -13,6 +13,9 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import grp
|
||||||
|
import pwd
|
||||||
|
|
||||||
import six
|
import six
|
||||||
|
|
||||||
from os_apply_config import config_exception as exc
|
from os_apply_config import config_exception as exc
|
||||||
@ -22,6 +25,8 @@ class OacFile(object):
|
|||||||
DEFAULTS = {
|
DEFAULTS = {
|
||||||
'allow_empty': True,
|
'allow_empty': True,
|
||||||
'mode': None,
|
'mode': None,
|
||||||
|
'owner': None,
|
||||||
|
'group': None,
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, body, **kwargs):
|
def __init__(self, body, **kwargs):
|
||||||
@ -79,7 +84,7 @@ class OacFile(object):
|
|||||||
|
|
||||||
@mode.setter
|
@mode.setter
|
||||||
def mode(self, v):
|
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.
|
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:
|
if not 0 <= v <= 0o777:
|
||||||
raise exc.ConfigException("mode '%#o' out of range" % v)
|
raise exc.ConfigException("mode '%#o' out of range" % v)
|
||||||
self._mode = 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
|
# example template tree
|
||||||
TEMPLATES = os.path.join(os.path.dirname(__file__), 'templates')
|
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 for example tree
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
@ -69,6 +62,17 @@ OUTPUT = {
|
|||||||
"/etc/control/mode": oac_file.OacFile(
|
"/etc/control/mode": oac_file.OacFile(
|
||||||
"lorem modus\n").set('mode', 0o755),
|
"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():
|
def main_path():
|
||||||
@ -364,3 +368,14 @@ class OSConfigApplierTestCase(testtools.TestCase):
|
|||||||
target_file = os.path.join(tmpdir, template[1:])
|
target_file = os.path.join(tmpdir, template[1:])
|
||||||
apply_config.install_config([path], TEMPLATES, tmpdir, False)
|
apply_config.install_config([path], TEMPLATES, tmpdir, False)
|
||||||
self.assertEqual(0o100755, os.stat(target_file).st_mode)
|
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
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import grp
|
||||||
|
import pwd
|
||||||
|
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from os_apply_config import config_exception as exc
|
from os_apply_config import config_exception as exc
|
||||||
@ -39,3 +42,49 @@ class OacFileTestCase(testtools.TestCase):
|
|||||||
|
|
||||||
for mode in [0, 0o777]:
|
for mode in [0, 0o777]:
|
||||||
oacf.mode = mode
|
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