Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign upGitHub is where the world builds software
Millions of developers and companies build, ship, and maintain their software on GitHub — the largest and most advanced development platform in the world.
Detect rootless mode #1484
Detect rootless mode #1484
Conversation
|
It's my first attempt to write a python patch ever. I apologize if I did it wrong. |
|
Build failing on coverage, I am afraid it is beyond my knowledge to fix this :/. |
|
Now I am facing a coverage error: https://asottile.visualstudio.com/asottile/_build/results?buildId=3831&view=logs&j=291e3f77-befc-520d-9779-f5b46c027190&t=037f03e6-93d9-56f0-123e-44a4e724aa8c&l=73 I am clueless on how to fix it:
|
|
I spent some time trying to understand how to write a test to make the build pass. I just don't understand how to get it. Too much to learn in one go. May I request some help (again)? So far I understand that it should probably take the for of a block like that in Then I assume we have to mock the content of Then I got lost in the pytest documentation.
|
|
@themr0c you can use a built-in fixture called The simple one case test would be _ROOTLESS_DOCKER_OUTPUT = """
some garbage
rootless: true
even more garbage
"""
def test_rootless_docker(monkeypatch):
"""Verify that only rootless docker/podman doesn't add args."""
monkeypatch.setattr('subprocess.check_output', lambda cmd, text: _ROOTLESS_DOCKER_OUTPUT)
assert docker.get_docker_user() == () |
|
And going further this would create tests for 4 test cases (rootless docker, rootless podman, non-rootless docker and non-rootless podman): import os
import pytest
_CURRENT_UID = os.getuid()
_CURRENT_GID = os.getgid()
@pytest.mark.parametrize(
('docker_sys_info', 'expected_args'),
(
pytest.param(
"""
some garbage
rootless: true
even more garbage
""",
(),
id='rootless podman',
),
pytest.param(
"""
some garbage
rootless: false
even more garbage
""",
(_CURRENT_UID, _CURRENT_GID),
id='non-rootless podman',
),
pytest.param(
"""
some garbage
rootless
even more garbage
""",
(),
id='rootless docker',
),
pytest.param(
"""
some garbage
nothing good
even more garbage
""",
(_CURRENT_UID, _CURRENT_GID),
id='non-rootless docker',
),
),
)
def test_rootless_docker(docker_sys_info, expected_args, monkeypatch):
"""Verify that only rootless docker/podman doesn't add args."""
monkeypatch.setattr('subprocess.check_output', lambda cmd, text: docker_sys_info)
assert docker.get_docker_user() == expected_args |
|
before going further please address my comments here in particular:
additionally:
|
|
I am completely illiterate in writing python code, it seems I chose a task too complex for me as first attempt to contribute :/.
|
|
@themr0c you literally can just |
in-process cache is fine -- there's currently no precedent for across-process caching in pre-commit, though I plan to do that eventually for virtualenv invalidation |
you're doing great! we'll help you through it :)
in this case it would just be importing
the helper is
taking the test above, you'd use with mock.patch.object(docker, 'cmd_output', return_value=(0, ..., '')):
...instead of the |
|
Side note: We have a videocall scheduled with @themr0c for early next week (this week it didn't work out) and we'll go trough the requested changes together. |
|
|
||
| @functools.lru_cache(maxsize=1) | ||
| def docker_is_rootless() -> bool: | ||
| return _docker_is_rootless() |
hroncok
Jun 15, 2020
Note: We had to do this to be able to test this with multiple mocked outputs. The cached version made that very hard.
Note: We had to do this to be able to test this with multiple mocked outputs. The cached version made that very hard.
webknjaz
Jun 15, 2020
could also be something like docker_is_rootless = functools.lru_cache(maxsize=1)(_docker_is_rootless)
could also be something like docker_is_rootless = functools.lru_cache(maxsize=1)(_docker_is_rootless)
hroncok
Jun 15, 2020
•
Could. Not sure if more or less readable.
Could. Not sure if more or less readable.
asottile
Jun 15, 2020
Member
there's an example of testing the inner part of an lru_cache function in pre_commit/languages/node.py (tests/languages/node_test.py) -- basically, access the __wrapped__ attribute of the cached object in the tests, then you don't need this indirection
there's an example of testing the inner part of an lru_cache function in pre_commit/languages/node.py (tests/languages/node_test.py) -- basically, access the __wrapped__ attribute of the cached object in the tests, then you don't need this indirection
|
|
||
|
|
||
| @functools.lru_cache(maxsize=1) | ||
| def docker_is_rootless() -> bool: |
webknjaz
Jun 15, 2020
This should probably have a PEP257-compliant docstring
This should probably have a PEP257-compliant docstring
hroncok
Jun 15, 2020
•
To be fair, none of the other functions in this file have docstrings.
To be fair, none of the other functions in this file have docstrings.
webknjaz
Jun 15, 2020
Fair enough. I guess it's just my personal habit kicking in before the linters :)
Fair enough. I guess it's just my personal habit kicking in before the linters :)
|
I think you could also add a test calling |
This seems a bit too far fetched to me. Isn't it rather testing lru_cache? Also, technically, other test might already called this. |
It's a regression test: when somebody decides to remove the decorator, the test should explode. It's up to you, of course, to skip adding it. |
| # rootless docker has "rootless" | ||
| # rootless podman has "rootless: true" | ||
| if line.strip().startswith('rootless'): | ||
| if 'false' not in line: |
hroncok
Jun 15, 2020
Note: Technically, the case where false is present is not tested. However in practice, we haven't found a case like this, this check is present as precaution.
Note: Technically, the case where false is present is not tested. However in practice, we haven't found a case like this, this check is present as precaution.
webknjaz
Jun 15, 2020
This would be useful as a code comment
This would be useful as a code comment
themr0c
Jun 15, 2020
Author
Is that what is causing the coverage error?
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------
pre_commit/languages/docker.py 72 2 10 2 95% 89->91, 91, 101->102, 102
Is that what is causing the coverage error?
Name Stmts Miss Branch BrPart Cover Missing
----------------------------------------------------------------------------
pre_commit/languages/docker.py 72 2 10 2 95% 89->91, 91, 101->102, 102
webknjaz
Jun 15, 2020
This is weird. If tests include example outputs containing rootless, they should also hit this condition check.
This is weird. If tests include example outputs containing rootless, they should also hit this condition check.
webknjaz
Jul 3, 2020
@asottile any ideas about this?
@asottile any ideas about this?
asottile
Jul 3, 2020
•
Member
there's no test for rootless: false so line 91 is never hit -- perhaps simpler would be if line.strip() == 'rootless: true': ...
there's no test for rootless: false so line 91 is never hit -- perhaps simpler would be if line.strip() == 'rootless: true': ...
webknjaz
Jul 3, 2020
That would probably not catch docker's behavior. Better to just improve the test matrix
That would probably not catch docker's behavior. Better to just improve the test matrix
|
@themr0c I think this PR's title/description should be updated. |
| Insecure Registries: | ||
| 127.0.0.0/8 | ||
| Live Restore Enabled: true | ||
| ''' # noqa |
webknjaz
Jul 3, 2020
it's usually better to specify specific violation codes instead of ignoring everything
it's usually better to specify specific violation codes instead of ignoring everything
This comment has been minimized.
This comment has been minimized.
|
A more durable detection for podman can be: if b'podman' in subprocess.check_output(ver_cmd):
...
|
This comment has been minimized.
This comment has been minimized.
|
For better readability and avoiding obscure bugs, it is recommended to use try/except block on a single statement and not on entire code blocks. A.k.a, in this case we as the programmers expect an exception from exactly one statement. try:
podman = subprocess.check_output(...)
except AttributeError:
return ()
...Also the try/except behavior will assume that we use rootless, even if this is not the case (a.k.a using python version that doesn't have check_output), |
|
let me know if you'd like me to finish this one, I've finally gotten around to setting up podman to reproduce this |
|
Depends on @themr0c. I am available to meet again and address the review comments, but if they prefer you to handle it, I don't mind. |
|
I may be able to take a look at this as I use CentOS and have both docker and podman installed. Are the review comments still current? |
|
looks like |
|
The user option is not necessary, podman does the correct mapping.
On the contrary, specifying user and userns can lead to trouble:
One of the main advantages of podman is to get rid of the -u option that you need when you run docker if you don't want to see your workspaces filled by files owned by root... So why insist on keeping some unnecessary complexity? |
|
the default mapping gives too much permission and can create undeletable files outside: $ podman run --rm -ti -v $PWD:/z:rw ubuntu:focal bash -c 'mkdir -p /z/1/2/3 && chown -R nobody /z/1'
$ rm -rf 1
rm: cannot remove '1/2/3': Permission denied |
|
Now I undertand better. |
fix #1243 - the -u option is not necessary on podman