diff --git a/src/spoc/podman.py b/src/spoc/podman.py index 3b2ca81..7fbdcf0 100644 --- a/src/spoc/podman.py +++ b/src/spoc/podman.py @@ -13,6 +13,16 @@ def run(cmd, **kwargs): def out(cmd, **kwargs): return run(cmd, stdout=subprocess.PIPE, text=True, **kwargs).stdout.rstrip() +def get_subuidgid(): + def get_first_sub(kind): + lines = out(['su', '-', 'spoc', '-c', f'podman unshare cat /proc/self/{kind}_map']) + for line in lines.splitlines(): + columns = line.split() + if columns[0] == '1': + return int(columns[1]) + return 0 + return (get_first_sub('uid'), get_first_sub('gid')) + def get_apps(): apps = {} data = json.loads(out(['podman', 'pod', 'ps', '--format', 'json'])) @@ -41,13 +51,17 @@ def get_pod_status(app_name=None): return out(['podman', 'pod', 'ps', '--filter', app_filter]) def create_volume(app_name, vol_name): - run(['podman', 'volume', 'create', '--label', f'spoc.app={app_name}', vol_name]) + subuid, subgid = get_subuidgid() + run(['podman', 'volume', 'create', + '--opt', f'o=uid={subuid},gid={subgid}', + '--label', f'spoc.app={app_name}', vol_name]) def remove_volume(vol_name): run(['podman', 'volume', 'rm', vol_name]) def create_pod(app_name, app_version): run(['podman', 'pod', 'create', '--name', app_name, + '--subuidname', 'spoc', '--subgidname', 'spoc', '--label', f'spoc.app={app_name}', '--label', f'spoc.version={app_version}']) def remove_pod(app_name): @@ -56,7 +70,7 @@ def remove_pod(app_name): def create_container(app_name, cnt_name, image, **kwargs): cmd = ['podman', 'container', 'create', '--name', cnt_name, '--pod', app_name, - '--subgidname', 'spoc', '--subgidname', 'spoc', + '--subuidname', 'spoc', '--subgidname', 'spoc', '--restart', 'unless-stopped'] env_file = kwargs.get('env_file') if env_file: diff --git a/tests/test_podman.py b/tests/test_podman.py index e1cb482..711d6c2 100644 --- a/tests/test_podman.py +++ b/tests/test_podman.py @@ -22,6 +22,23 @@ def test_out(run): env=podman.ENV, arg1=123, arg2=True) assert output == 'RESULT' +@patch('spoc.podman.out', return_value=' 0 1000 1\n 1 100000 65536') +def test_get_subuidgid(out): + subuidgid = podman.get_subuidgid() + + assert subuidgid == (100000, 100000) + out.assert_has_calls([ + call(['su', '-', 'spoc', '-c', 'podman unshare cat /proc/self/uid_map']), + call(['su', '-', 'spoc', '-c', 'podman unshare cat /proc/self/gid_map']), + ]) + +@patch('spoc.podman.out', return_value='') +def test_get_subuidgid_no_id(out): + subuidgid = podman.get_subuidgid() + + assert subuidgid == (0, 0) + assert out.call_count == 2 + @patch('spoc.podman.out') def test_get_apps(out): with open(os.path.join(TEST_DATA_DIR, 'podman_pod_ps.json'), encoding='utf-8') as f: @@ -78,10 +95,14 @@ def test_get_pod_status_all(out): assert status == 'RESULT' @patch('spoc.podman.run') -def test_create_volume(run): +@patch('spoc.podman.get_subuidgid', return_value=(100000, 100000)) +def test_create_volume(get_subuidgid, run): podman.create_volume('someapp', 'someapp-vol') - expected_cmd = ['podman', 'volume', 'create', '--label', 'spoc.app=someapp', 'someapp-vol'] + expected_cmd = ['podman', 'volume', 'create', + '--opt', 'o=uid=100000,gid=100000', + '--label', 'spoc.app=someapp', 'someapp-vol'] + get_subuidgid.assert_called_once() run.assert_called_once_with(expected_cmd) @patch('spoc.podman.run') @@ -96,6 +117,7 @@ def test_create_pod(run): podman.create_pod('someapp', '0.1') expected_cmd = ['podman', 'pod', 'create', '--name', 'someapp', + '--subuidname', 'spoc', '--subgidname', 'spoc', '--label', 'spoc.app=someapp', '--label', 'spoc.version=0.1'] run.assert_called_once_with(expected_cmd) @@ -117,7 +139,7 @@ def test_create_container(run): hosts={'cnt2', 'cnt3', 'cnt'}) expected_cmd = ['podman', 'container', 'create', '--name', 'someapp-cnt', '--pod', 'someapp', - '--subgidname', 'spoc', '--subgidname', 'spoc', + '--subuidname', 'spoc', '--subgidname', 'spoc', '--restart', 'unless-stopped', '--env-file', '/var/lib/spoc/someapp.env', '--requires', 'someapp-cnt2,someapp-cnt3', '--volume', 'someapp-mnt:/mnt', '--volume', 'someapp-srv:/srv', '--add-host', 'cnt:127.0.0.1', @@ -130,7 +152,7 @@ def test_create_container_minimal(run): podman.create_container('someapp', 'someapp-cnt', 'example.com/someapp:0.23.6-210515') expected_cmd = ['podman', 'container', 'create', '--name', 'someapp-cnt', '--pod', 'someapp', - '--subgidname', 'spoc', '--subgidname', 'spoc', + '--subuidname', 'spoc', '--subgidname', 'spoc', '--restart', 'unless-stopped', 'example.com/someapp:0.23.6-210515'] run.assert_called_once_with(expected_cmd)