podman-py icon indicating copy to clipboard operation
podman-py copied to clipboard

TypeError when trying to get container instance status

Open JacobCallahan opened this issue 1 year ago • 10 comments

When trying to get the status of a container instance running on a remote podman host, I get the following error.

Out[5]: <Container: 859c3172e0>

In [6]: container_inst.id
Out[6]: '859c3172e0de5e27934ba7bcbd190d0949227bfe973ded77658d25f30a1585c2'

In [7]: container_inst.status
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[7], line 1
----> 1 container_inst.status

File ~/Programming/broker/.venv/lib64/python3.12/site-packages/podman/domain/containers.py:54, in Container.status(self)
     52 """Literal["running", "stopped", "exited", "unknown"]: Returns status of container."""
     53 with suppress(KeyError):
---> 54     return self.attrs["State"]["Status"]
     55 return "unknown"

TypeError: string indices must be integers, not 'str'

When checking the value of attrs for the instance, I see that status isn't nested under state.

In [1]: self.attrs                                                                                                                                                                            
Out[1]:                                                                                                                                                                                       
{'AutoRemove': False,                                                                                                                                                                         
 'Command': ['/bin/sh', '-c', '/tmp/startup.sh'],
...
 'StartedAt': 1728929152,
 'State': 'running',
 'Status': ''}

There is also a possibility that this is due to a difference in major podman versions.

Local Podman: podman-5.2.3-1.fc40.x86_64 Local podman-py: 5.0.0 Remote Podman: podman-4.9.4-4

JacobCallahan avatar Oct 14 '24 18:10 JacobCallahan

@JacobCallahan thanks for the report, I think it could be due to Podman 4 on the host and Podman 5 on the client. Do you have the chance to try with the same major versions?

inknos avatar Oct 16 '24 12:10 inknos

@inknos unfortunately, I don't have a remote podman host now, but could potentially set one up on Friday or next week.

JacobCallahan avatar Oct 16 '24 20:10 JacobCallahan

@JacobCallahan , I tried to reproduce it and it works on my system with podman 5 in the host and podman 4 in the server. I think trying with podman 5 in both is not necessary.

>>> url = "ssh://[email protected]/run/user/1000/podman/podman.sock"
>>> from podman import PodmanClient
>>> client = PodmanClient(base_url=url, identity="/home/nsella/.ssh/id_rsa")
>>> client.containers.list()
[<Container: fd7dec82e8>, ... ]
>>> c = client.containers.get("fd7dec82e8")
>>> c.id
'fd7dec82e85daac28395403fc130411c6ee72e4b4d73e4a9ecf56dceeed42390'
>>> c.status
'running'

Host: podman version 5.3.0-dev-27d73b0cd Server: podman version 4.9.4-rhel PodmanPy: upstream and 5.0.0


Please provide more information on your issue. do my steps reproduce the issue for you? if not, could you provide reproducing steps?

inknos avatar Oct 17 '24 09:10 inknos

@inknos so i found something interesting while testing your steps. Your steps worked fine, when acting on a container object from get, but not on the container objects returned from list.

In [6]: url = "tcp://infra-podman-ipv4.our.lab.com:2375"

In [7]: client = PodmanClient(base_url=url)

In [8]: client.containers.list()
Out[8]: 
[<Container: abb2858b55>,
 <Container: f4539e8307>,
 <Container: 8c106a4a5a>,
 <Container: 6b9a4e97a2>,
 <Container: 43850b849c>,
 <Container: 7f5afe54bf>,
 <Container: e27ea6295c>,
 <Container: c13ed5f868>,
 <Container: 859c3172e0>,
 <Container: acefe65b89>]

In [9]: c = client.containers.get("abb2858b55")

In [10]: c.id
Out[10]: 'abb2858b558831055a74c9b79f4db1bf0e60b7c3f47a144ee05fe9c59edebeae'

In [11]: c.status
Out[11]: 'running'

In [12]: for c in client.containers.list():
    ...:     print(c.id)
    ...:     print(c.status)
    ...: 
abb2858b558831055a74c9b79f4db1bf0e60b7c3f47a144ee05fe9c59edebeae
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[12], line 3
      1 for c in client.containers.list():
      2     print(c.id)
----> 3     print(c.status)

File ~/Programming/broker/.venv/lib64/python3.12/site-packages/podman/domain/containers.py:54, in Container.status(self)
     52 """Literal["running", "stopped", "exited", "unknown"]: Returns status of container."""
     53 with suppress(KeyError):
---> 54     return self.attrs["State"]["Status"]
     55 return "unknown"

TypeError: string indices must be integers, not 'str'

In [13]: containers = client.containers.list()

In [14]: containers[0].id
Out[14]: 'abb2858b558831055a74c9b79f4db1bf0e60b7c3f47a144ee05fe9c59edebeae'

In [15]: containers[0].status
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[15], line 1
----> 1 containers[0].status

File ~/Programming/broker/.venv/lib64/python3.12/site-packages/podman/domain/containers.py:54, in Container.status(self)
     52 """Literal["running", "stopped", "exited", "unknown"]: Returns status of container."""
     53 with suppress(KeyError):
---> 54     return self.attrs["State"]["Status"]
     55 return "unknown"

TypeError: string indices must be integers, not 'str'

JacobCallahan avatar Oct 17 '24 13:10 JacobCallahan

right, so the issue is that the container status need to be reloaded in order to access it from the list. note that the inspect call is expensive, that's why you need to run reload explicitly.

clist = client.containers.list()
for c in clist:
    c.reload()
print(clist[0].status)

with this you should get the status.

I would not touch too much the reload function but something I would consider is handling such exceptions with a try like

@property
def status(self):
    """Literal["running", "stopped", "exited", "unknown"]: Returns status of container."""
    with suppress(KeyError):
        try:
            return self.attrs["State"]["Status"]
        except TypeError as e:
            # stderr or log something useful here
            # probably even raise e again since I am not sure TypeError would isolate this issue
    return "unknown"

inknos avatar Oct 17 '24 13:10 inknos

cc: @jwhonce TL;DR: we are once again discussing status property exceptions when it's invoked after a container.list call

inknos avatar Oct 17 '24 13:10 inknos

I would propose two approaches

  1. containers.list calls c.reload() before return - https://github.com/containers/podman-py/commit/2c6806ba808d7f4dfb71080c67606a275c26cecc

  2. c.status return "unknown" if TypeError - https://github.com/containers/podman-py/commit/fa92dd71d9a42f0e2801607fb895a52b3b17a58f

inknos avatar Oct 17 '24 16:10 inknos

The expense has to be paid by someone, to be fair.

As a user, I would expect the Container objects from list to be the same as those from get. Having to get the initial list of container objects, then iterating over those objects just to pull the full information again doesn't seem ideal.

I've worked around this issue for my team's use with a try/except in the meantime.

    try:
        info["status"] = container_inst.status
    except TypeError:
        info["status"] = container_inst.attrs["State"]

edit: If the intention isn't to have the container object be complete when returning from list, then you could just return a list of ids and make sure users call get manually. This would certainly speed things up from that perspective..

JacobCallahan avatar Oct 17 '24 19:10 JacobCallahan

@inknos I would rather see us implement the sparse keyword for list(). It is currently ignored.

jwhonce avatar Oct 18 '24 17:10 jwhonce