diff --git a/testing/src/scenario/mocking.py b/testing/src/scenario/mocking.py index 1ec9025f1..46cc617a2 100644 --- a/testing/src/scenario/mocking.py +++ b/testing/src/scenario/mocking.py @@ -779,6 +779,14 @@ def __init__( # wipe just in case if container_root.exists(): + if any(container_root.iterdir()): + logger.warning( + 'Container %r has a non-empty filesystem that will be wiped before this run.' + ' If you are trying to mock filesystem contents, use Mounts instead.' + ' If you are reusing a Context instance across multiple ctx.run() calls,' + ' note that the filesystem is cleared between runs.', + container_name, + ) # Path.rmdir will fail if root is nonempty shutil.rmtree(container_root) diff --git a/testing/tests/test_e2e/test_pebble.py b/testing/tests/test_e2e/test_pebble.py index 7dac587ad..1a7623209 100644 --- a/testing/tests/test_e2e/test_pebble.py +++ b/testing/tests/test_e2e/test_pebble.py @@ -977,3 +977,49 @@ def test_plan_accessed_twice_does_not_accumulate_list_fields(): assert layer1.services['svc-a'].before == ['another'] assert layer1.services['svc-a'].requires == ['dep'] assert layer1.log_targets['lt-a'].services == ['svc-a'] + + +def test_warning_on_non_empty_container(): + class MyCharm(CharmBase): + def __init__(self, framework: Framework): + super().__init__(framework) + self.framework.observe(self.on.start, self._on_start) + + def _on_start(self, _: object): + self.unit.get_container('mycontainer').push('/foo.txt', 'hello') + + ctx = Context( + MyCharm, + meta={'name': 'foo', 'containers': {'mycontainer': {}}}, + ) + container = Container(name='mycontainer', can_connect=True) + state = State(containers={container}) + + # First run populates the container root with a file. + ctx.run(ctx.on.start(), state) + + # Second run should warn that the container root is non-empty. + ctx.run(ctx.on.start(), state) + + assert any( + 'mycontainer' in line.message and 'non-empty' in line.message for line in ctx.juju_log + ) + + +def test_no_warning_on_empty_container(): + ctx = Context( + CharmBase, + meta={'name': 'foo', 'containers': {'mycontainer': {}}}, + ) + container = Container(name='mycontainer', can_connect=True) + state = State(containers={container}) + + # First run creates the container root. + ctx.run(ctx.on.start(), state) + + # Second run should not warn since the container root is empty. + ctx.run(ctx.on.start(), state) + + assert not any( + 'mycontainer' in line.message and 'non-empty' in line.message for line in ctx.juju_log + )