Skip to content

Commit

Permalink
chg: Container.top to use stream_helper
Browse files Browse the repository at this point in the history
fixes: Container.top
- infinite blocking when stream=True
- inconsistent decoding to json

add: test for Container.top with stream=True to avoid regression

Signed-off-by: Raz Crimson <[email protected]>
  • Loading branch information
RazCrimson committed Mar 14, 2023
1 parent 5ca9517 commit 3d8eb1b
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 10 deletions.
16 changes: 6 additions & 10 deletions podman/domain/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from typing import Any, Dict, Iterable, Iterator, List, Mapping, Optional, Tuple, Union

import requests
from requests import Response

from podman import api
from podman.domain.images import Image
Expand Down Expand Up @@ -457,23 +456,20 @@ def top(self, **kwargs) -> Union[Iterator[Dict[str, Any]], Dict[str, Any]]:
NotFound: when the container no longer exists
APIError: when the service reports an error
"""
stream = kwargs.get("stream", False)

params = {
"stream": stream,
"ps_args": kwargs.get("ps_args"),
"stream": kwargs.get("stream", False),
}
response = self.client.get(f"/containers/{self.id}/top", params=params)
response = self.client.get(f"/containers/{self.id}/top", params=params, stream=stream)
response.raise_for_status()

if params["stream"]:
self._top_helper(response)
if stream:
return api.stream_helper(response, decode_to_json=True)

return response.json()

@staticmethod
def _top_helper(response: Response) -> Iterator[Dict[str, Any]]:
for line in response.iter_lines():
yield line

def unpause(self) -> None:
"""Unpause processes in container."""
response = self.client.post(f"/containers/{self.id}/unpause")
Expand Down
47 changes: 47 additions & 0 deletions podman/tests/unit/test_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,53 @@ def test_top(self, mock):
self.assertDictEqual(actual, body)
self.assertTrue(adapter.called_once)

@requests_mock.Mocker()
def test_top_with_streaming(self, mock):
stream = [
{
"Processes": [
[
'jhonce',
'2417',
'2274',
'0',
'Mar01',
'?',
'00:00:01',
(
'/usr/bin/ssh-agent /bin/sh -c exec -l /bin/bash -c'
' "/usr/bin/gnome-session"'
),
],
['jhonce', '5544', '3522', '0', 'Mar01', 'pts/1', '00:00:02', '-bash'],
['jhonce', '6140', '3522', '0', 'Mar01', 'pts/2', '00:00:00', '-bash'],
],
"Titles": ["UID", "PID", "PPID", "C", "STIME", "TTY", "TIME CMD"],
}
]

buffer = io.StringIO()
for entry in stream:
buffer.write(json.JSONEncoder().encode(entry))
buffer.write("\n")

adapter = mock.get(
tests.LIBPOD_URL
+ "/containers/87e1325c82424e49a00abdd4de08009eb76c7de8d228426a9b8af9318ced5ecd/top"
"?stream=True",
text=buffer.getvalue(),
)

container = Container(attrs=FIRST_CONTAINER, client=self.client.api)
top_stats = container.top(stream=True)

self.assertIsInstance(top_stats, Iterable)
for response, actual in zip(top_stats, stream):
self.assertIsInstance(response, dict)
self.assertDictEqual(response, actual)

self.assertTrue(adapter.called_once)


if __name__ == '__main__':
unittest.main()

0 comments on commit 3d8eb1b

Please sign in to comment.