Skip to content

Commit

Permalink
Add all, external, and label to Image.prune()
Browse files Browse the repository at this point in the history
Param all is now supported by prune.
Param external is now supported by prune.
Filter by label, which was already supported, is now
documented and tested.

Resolves: #312

Signed-off-by: Nicola Sella <[email protected]>
  • Loading branch information
inknos committed Aug 1, 2024
1 parent c5bde04 commit d99076a
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 4 deletions.
23 changes: 19 additions & 4 deletions podman/domain/images_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,24 +135,39 @@ def load(self, data: bytes) -> Generator[Image, None, None]:
yield self.get(item)

def prune(
self, filters: Optional[Mapping[str, Any]] = None
self,
all: Optional[bool] = False, # pylint: disable=redefined-builtin
external: Optional[bool] = False,
filters: Optional[Mapping[str, Any]] = None,
) -> Dict[Literal["ImagesDeleted", "SpaceReclaimed"], Any]:
"""Delete unused images.
The Untagged keys will always be "".
Args:
all: Remove all images not in use by containers, not just dangling ones.
external: Remove images even when they are used by external containers
(e.g, by build containers).
filters: Qualify Images to prune. Available filters:
- dangling (bool): when true, only delete unused and untagged images.
- label: (dict): filter by label.
Examples:
filters={"label": {"key": "value"}}
filters={"label!": {"key": "value"}}
- until (str): Delete images older than this timestamp.
Raises:
APIError: when service returns an error
"""
response = self.client.post(
"/images/prune", params={"filters": api.prepare_filters(filters)}
)

params = {
"all": all,
"external": external,
"filters": api.prepare_filters(filters),
}

response = self.client.post("/images/prune", params=params)
response.raise_for_status()

deleted: List[Dict[str, str]] = []
Expand Down
58 changes: 58 additions & 0 deletions podman/tests/unit/test_imagesmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,64 @@ def test_prune_filters(self, mock):
self.assertEqual(len(untagged), 2)
self.assertEqual(len("".join(untagged)), 0)

@requests_mock.Mocker()
def test_prune_filters_label(self, mock):
"""Unit test filters param label for Images prune()."""
mock.post(
tests.LIBPOD_URL
+ "/images/prune?filters=%7B%22label%22%3A+%5B%22%7B%27license%27%3A+%27Apache-2.0%27%7D%22%5D%7D",
json=[
{
"Id": "326dd9d7add24646a325e8eaa82125294027db2332e49c5828d96312c5d773ab",
"Size": 1024,
},
],
)

report = self.client.images.prune(filters={"label": {"license": "Apache-2.0"}})
self.assertIn("ImagesDeleted", report)
self.assertIn("SpaceReclaimed", report)

self.assertEqual(report["SpaceReclaimed"], 1024)

deleted = [r["Deleted"] for r in report["ImagesDeleted"] if "Deleted" in r]
self.assertEqual(len(deleted), 1)
self.assertIn("326dd9d7add24646a325e8eaa82125294027db2332e49c5828d96312c5d773ab", deleted)
self.assertGreater(len("".join(deleted)), 0)

untagged = [r["Untagged"] for r in report["ImagesDeleted"] if "Untagged" in r]
self.assertEqual(len(untagged), 1)
self.assertEqual(len("".join(untagged)), 0)

@requests_mock.Mocker()
def test_prune_filters_not_label(self, mock):
"""Unit test filters param NOT-label for Images prune()."""
mock.post(
tests.LIBPOD_URL
+ "/images/prune?filters=%7B%22label%21%22%3A+%5B%22%7B%27license%27%3A+%27Apache-2.0%27%7D%22%5D%7D",
json=[
{
"Id": "c4b16966ecd94ffa910eab4e630e24f259bf34a87e924cd4b1434f267b0e354e",
"Size": 1024,
},
],
)

report = self.client.images.prune(filters={"label!": {"license": "Apache-2.0"}})
self.assertIn("ImagesDeleted", report)
self.assertIn("SpaceReclaimed", report)

self.assertEqual(report["SpaceReclaimed"], 1024)

deleted = [r["Deleted"] for r in report["ImagesDeleted"] if "Deleted" in r]
self.assertEqual(len(deleted), 1)
self.assertIn("c4b16966ecd94ffa910eab4e630e24f259bf34a87e924cd4b1434f267b0e354e", deleted)
self.assertGreater(len("".join(deleted)), 0)

untagged = [r["Untagged"] for r in report["ImagesDeleted"] if "Untagged" in r]
self.assertEqual(len(untagged), 1)
self.assertEqual(len("".join(untagged)), 0)

@requests_mock.Mocker()
def test_prune_failure(self, mock):
"""Unit test to report error carried in response body."""
Expand Down

0 comments on commit d99076a

Please sign in to comment.