diff options
| -rw-r--r-- | ansible/module_utils/kolla_podman_worker.py | 92 | ||||
| -rw-r--r-- | tests/kolla_container_tests/test_podman_worker.py | 144 |
2 files changed, 191 insertions, 45 deletions
diff --git a/ansible/module_utils/kolla_podman_worker.py b/ansible/module_utils/kolla_podman_worker.py index 82653bb8d..a7644c351 100644 --- a/ansible/module_utils/kolla_podman_worker.py +++ b/ansible/module_utils/kolla_podman_worker.py @@ -192,6 +192,7 @@ class PodmanWorker(ContainerWorker): ) if src == 'devpts': mount_item = dict( + source=src, target=dest, type='devpts' ) @@ -385,50 +386,65 @@ class PodmanWorker(ContainerWorker): else: return string - raw_volumes, binds = self.generate_volumes() - raw_vols, current_binds = self.generate_volumes( - container_info['HostConfig'].get('Binds')) - - current_vols = [check_slash(vol) for vol in raw_vols if vol] - volumes = [check_slash(vol) for vol in raw_volumes if vol] + # NOTE(blanson): Podman automatically appends default flags + # such as rprivate, nosuid, nodev, rbind to all mounts. + # For special paths like /proc, /run, /sys, and /var/run, + # noexec is also added by default. We remove these defaults + # because they do not reflect a meaningful difference + # between the requested and current container configuration. + # Additionally, if neither 'ro' nor 'rw' is specified, + # we implicitly assume 'rw' (Podman's default behavior). + def normalize_mode(path, mode): + default_flags = {'rprivate', 'nosuid', 'nodev', 'rbind'} + special_paths_noexec = {'/proc', '/run', '/sys', '/var/run'} + + flags = set(mode.split(',')) if mode else set() + flags -= default_flags + + if any(path.startswith(p) for p in special_paths_noexec): + flags.discard('noexec') + if not (flags & {'ro', 'rw'}): + flags.add('rw') + return flags + + # NOTE(blanson): Convert a binds dict into a list of + # (src, dst, normalized_flags) tuples. Normalization ignores + # default Podman flags and noexec for special paths to allow + # consistent comparison. + def build_bind_list(binds_dict): + lst = [] + for src, info in (binds_dict or {}).items(): + src_path = check_slash(src) + dst_path = check_slash(info['bind']) + mode_flags = normalize_mode( + dst_path, + info['mode'], + ) + lst.append((src_path, dst_path, mode_flags)) + return lst - if not volumes: - volumes = list() - if not current_vols: - current_vols = list() - if not current_binds: - current_binds = list() + binds_input = container_info['HostConfig'].get('Binds') + raw_volumes, binds = self.generate_volumes() + raw_vols, current_binds = ( + [], {} + ) if not binds_input else self.generate_volumes(binds_input) - volumes.sort() - current_vols.sort() + volumes = [check_slash(v) for v in raw_volumes or [] if v] + current_vols = [check_slash(v) for v in raw_vols or [] if v] - if set(volumes).symmetric_difference(set(current_vols)): + if set(volumes) != set(current_vols): return True - new_binds = list() - new_current_binds = list() - if binds: - for k, v in binds.items(): - k = check_slash(k) - v['bind'] = check_slash(v['bind']) - new_binds.append( - "{}:{}:{}".format(k, v['bind'], v['mode'])) - - if current_binds: - for k, v in current_binds.items(): - k = check_slash(k) - v['bind'] = check_slash(v['bind']) - if 'ro' in v['mode']: - v['mode'] = 'ro' - else: - v['mode'] = 'rw' - new_current_binds.append( - "{}:{}:{}".format(k, v['bind'], v['mode'][0:2])) - - new_binds.sort() - new_current_binds.sort() + req_bind_list = [ + (src, dst, frozenset(flags)) + for src, dst, flags in build_bind_list(binds) + ] + cur_bind_list = [ + (src, dst, frozenset(flags)) + for src, dst, flags in build_bind_list(current_binds) + ] - if set(new_binds).symmetric_difference(set(new_current_binds)): + if set(req_bind_list) != set(cur_bind_list): return True def compare_dimensions(self, container_info): diff --git a/tests/kolla_container_tests/test_podman_worker.py b/tests/kolla_container_tests/test_podman_worker.py index 2d3333e04..b3bfcdce2 100644 --- a/tests/kolla_container_tests/test_podman_worker.py +++ b/tests/kolla_container_tests/test_podman_worker.py @@ -1290,19 +1290,149 @@ class TestAttrComp(base.BaseTestCase): def test_compare_volumes_neg(self): container_info = { 'Config': dict(Volumes=['/var/log/kolla/']), - 'HostConfig': dict(Binds=['kolla_logs:/var/log/kolla/:rw'])} - self.pw = get_PodmanWorker( - {'volumes': ['kolla_logs:/var/log/kolla/:rw']}) - + 'HostConfig': dict(Binds=[ + 'kolla_logs:/var/log/kolla/:rw,rprivate,nosuid,nodev,rbind' + ]) + } + self.pw = get_PodmanWorker({ + 'volumes': ['kolla_logs:/var/log/kolla/'] + }) self.assertFalse(self.pw.compare_volumes(container_info)) def test_compare_volumes_pos(self): container_info = { 'Config': dict(Volumes=['/var/log/kolla/']), - 'HostConfig': dict(Binds=['kolla_logs:/var/log/kolla/:rw'])} - self.pw = get_PodmanWorker( - {'volumes': ['/dev/:/dev/:rw']}) + 'HostConfig': dict(Binds=[ + 'kolla_logs:/var/log/kolla/:ro,rprivate,nosuid,nodev,rbind' + ]) + } + self.pw = get_PodmanWorker({ + 'volumes': ['kolla_logs:/var/log/kolla/'] + }) + self.assertTrue(self.pw.compare_volumes(container_info)) + + def test_compare_volumes_empty_add(self): + container_info = { + 'Config': dict(Volumes=[]), + 'HostConfig': dict(Binds=[]) + } + self.pw = get_PodmanWorker({ + 'volumes': ['kolla_logs:/var/log/kolla/'] + }) + self.assertTrue(self.pw.compare_volumes(container_info)) + + def test_compare_volumes_empty_del(self): + container_info = { + 'Config': dict(Volumes=['/var/log/kolla/']), + 'HostConfig': dict(Binds=[ + 'kolla_logs:/var/log/kolla/:ro,rprivate,nosuid,nodev,rbind' + ]) + } + self.pw = get_PodmanWorker({ + 'volumes': [] + }) + self.assertTrue(self.pw.compare_volumes(container_info)) + + def test_compare_volumes_noexec_default(self): + container_info = { + 'Config': dict(Volumes=['/proc/', '/run/libvirt', '/sys/']), + 'HostConfig': dict(Binds=[ + '/proc/:/proc/:rw,shared,rprivate,nosuid,nodev,noexec,rbind', + '/run/libvirt:/run/libvirt:rw,nosuid,nodev,noexec,rbind', + '/sys/:/sys/:rw,rprivate,nosuid,nodev,noexec,rbind', + ]) + } + self.pw = get_PodmanWorker({ + 'volumes': [ + '/proc/:/proc/:shared', + '/run/libvirt:/run/libvirt:rprivate', + '/sys/:/sys/:rprivate' + ] + }) + self.assertFalse(self.pw.compare_volumes(container_info)) + + def test_compare_volumes_shared_vs_rw(self): + container_info = { + 'Config': dict(Volumes=['/run/libvirt/']), + 'HostConfig': dict(Binds=[ + '/run/libvirt:/run/libvirt:rw,rprivate,nosuid,nodev,rbind' + ]) + } + self.pw = get_PodmanWorker({ + 'volumes': ['/run/libvirt:/run/libvirt:shared'] + }) + self.assertTrue(self.pw.compare_volumes(container_info)) + + def test_compare_volumes_implicit_rw_both_sides(self): + container_info = { + 'Config': dict(Volumes=['/dev/shm/']), # nosec + 'HostConfig': dict(Binds=[ + '/dev/shm:/dev/shm:rprivate,nosuid,nodev,rbind' # nosec + ]) + } + self.pw = get_PodmanWorker({ + 'volumes': ['/dev/shm:/dev/shm'] # nosec + }) + self.assertFalse(self.pw.compare_volumes(container_info)) + + def test_compare_volumes_explicit_rw_vs_implicit(self): + container_info = { + 'Config': dict(Volumes=['/data/']), + 'HostConfig': dict(Binds=[ + '/host/data:/data:rprivate,nosuid,nodev,rbind' + ]) + } + self.pw = get_PodmanWorker({ + 'volumes': ['/host/data:/data:rw'] + }) + self.assertFalse(self.pw.compare_volumes(container_info)) + def test_compare_volumes_var_run_noexec(self): + container_info = { + 'Config': dict(Volumes=['/var/run/libvirt/']), + 'HostConfig': dict(Binds=[ + '/var/run/libvirt:/var/run/libvirt:' + 'rw,rprivate,nosuid,nodev,noexec,rbind' + ]) + } + self.pw = get_PodmanWorker({ + 'volumes': ['/var/run/libvirt:/var/run/libvirt'] + }) + self.assertFalse(self.pw.compare_volumes(container_info)) + + def test_compare_volumes_multiple_binds(self): + container_info = { + 'Config': dict(Volumes=[ # nosec + '/var/log/kolla/', '/etc/kolla/', '/dev/shm/']), + 'HostConfig': dict(Binds=[ + 'kolla_logs:/var/log/kolla/:rw,rprivate,nosuid,nodev,rbind', + '/etc/kolla:/etc/kolla:ro,rprivate,nosuid,nodev,rbind', + '/dev/shm:/dev/shm:rprivate,nosuid,nodev,rbind' # nosec + ]) + } + self.pw = get_PodmanWorker({ + 'volumes': [ + 'kolla_logs:/var/log/kolla/', + '/etc/kolla:/etc/kolla:ro', + '/dev/shm:/dev/shm' # nosec + ] + }) + self.assertFalse(self.pw.compare_volumes(container_info)) + + def test_compare_volumes_multiple_binds_one_diff(self): + container_info = { + 'Config': dict(Volumes=['/var/log/kolla/', '/etc/kolla/']), + 'HostConfig': dict(Binds=[ + 'kolla_logs:/var/log/kolla/:rw,rprivate,nosuid,nodev,rbind', + '/etc/kolla:/etc/kolla:ro,rprivate,nosuid,nodev,rbind' + ]) + } + self.pw = get_PodmanWorker({ + 'volumes': [ + 'kolla_logs:/var/log/kolla/', + '/etc/kolla:/etc/kolla:rw' + ] + }) self.assertTrue(self.pw.compare_volumes(container_info)) def test_compare_environment_neg(self): |
