Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions podman/domain/containers_create.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@
from contextlib import suppress
from typing import Any, Union
from collections.abc import MutableMapping
import requests

from podman import api
from podman.domain.containers import Container
from podman.domain.images import Image
from podman.domain.pods import Pod
from podman.domain.secrets import Secret
from podman.errors import ImageNotFound

logger = logging.getLogger("podman.containers")

Expand Down Expand Up @@ -361,7 +361,6 @@ def create(
A Container object.

Raises:
ImageNotFound: when Image not found by Podman service
APIError: when Podman service reports an error
"""
if isinstance(image, Image):
Expand All @@ -379,7 +378,18 @@ def create(
headers={"content-type": "application/json"},
data=payload,
)
response.raise_for_status(not_found=ImageNotFound)
if response.status_code == requests.codes.not_found:
self.podman_client.images.pull(
image,
auth_config=kwargs.get("auth_config"),
platform=kwargs.get("platform"),
policy=kwargs.get("policy", "missing"),
)
response = self.client.post(
"/containers/create",
headers={"content-type": "application/json"},
data=payload,
)
Comment on lines +381 to +392
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was curious to see what podman cli does when you call create, so I tried. when you do podman-remote create you get a pull call no matter if the image is cached or not

POST /v5.7.0/libpod/images/pull
POST /v5.7.0/libpod/containers/create

therefore I would simply remove the if and try to pull every time you create

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct me if I'm wrong, but podman cli default pull policy should be "missing", thus, it should not pull if the image is cached:

tollsimy@thinkpad-redhat:~$ podman pull quay.io/fedora/fedora:43
Trying to pull quay.io/fedora/fedora:43...
Getting image source signatures
Copying blob 5f0ebf85c063 skipped: already exists  
Copying config ad291a240e done   | 
Writing manifest to image destination
ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b
tollsimy@thinkpad-redhat:~$ podman create --log-level=debug quay.io/fedora/fedora:43
INFO[0000] podman filtering at log level debug          
DEBU[0000] Called create.PersistentPreRunE(podman create --log-level=debug quay.io/fedora/fedora:43) 
INFO[0000] Setting parallel job count to 67             
DEBU[0000] Using conmon: "/usr/bin/conmon"              
INFO[0000] Using sqlite as database backend             
DEBU[0000] Using graph driver overlay                   
DEBU[0000] Using graph root /var/home/tollsimy/.local/share/containers/storage 
DEBU[0000] Using run root /run/user/1000/containers     
DEBU[0000] Using static dir /var/home/tollsimy/.local/share/containers/storage/libpod 
DEBU[0000] Using tmp dir /run/user/1000/libpod/tmp      
DEBU[0000] Using volume path /var/home/tollsimy/.local/share/containers/storage/volumes 
DEBU[0000] Using transient store: false                 
DEBU[0000] [graphdriver] trying provided driver "overlay" 
DEBU[0000] Cached value indicated that overlay is supported 
DEBU[0000] Cached value indicated that overlay is supported 
DEBU[0000] Cached value indicated that metacopy is not being used 
DEBU[0000] Cached value indicated that native-diff is usable 
DEBU[0000] backingFs=btrfs, projectQuotaSupported=false, useNativeDiff=true, usingMetacopy=false 
DEBU[0000] Initializing event backend journald          
DEBU[0000] Configured OCI runtime crun-vm initialization failed: no valid executable found for OCI runtime crun-vm: invalid argument 
DEBU[0000] Configured OCI runtime crun-wasm initialization failed: no valid executable found for OCI runtime crun-wasm: invalid argument 
DEBU[0000] Configured OCI runtime kata initialization failed: no valid executable found for OCI runtime kata: invalid argument 
DEBU[0000] Configured OCI runtime runsc initialization failed: no valid executable found for OCI runtime runsc: invalid argument 
DEBU[0000] Configured OCI runtime ocijail initialization failed: no valid executable found for OCI runtime ocijail: invalid argument 
DEBU[0000] Configured OCI runtime runc initialization failed: no valid executable found for OCI runtime runc: invalid argument 
DEBU[0000] Configured OCI runtime runj initialization failed: no valid executable found for OCI runtime runj: invalid argument 
DEBU[0000] Configured OCI runtime youki initialization failed: no valid executable found for OCI runtime youki: invalid argument 
DEBU[0000] Configured OCI runtime krun initialization failed: no valid executable found for OCI runtime krun: invalid argument 
DEBU[0000] Using OCI runtime "/usr/bin/crun"            
DEBU[0000] Pulling image quay.io/fedora/fedora:43 (policy: missing) 
DEBU[0000] Looking up image "quay.io/fedora/fedora:43" in local containers storage 
DEBU[0000] Normalized platform linux/amd64 to {amd64 linux  [] } 
DEBU[0000] Trying "quay.io/fedora/fedora:43" ...        
DEBU[0000] parsed reference into "[overlay@/var/home/tollsimy/.local/share/containers/storage+/run/user/1000/containers]@ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b" 
DEBU[0000] Found image "quay.io/fedora/fedora:43" as "quay.io/fedora/fedora:43" in local containers storage 
DEBU[0000] Found image "quay.io/fedora/fedora:43" as "quay.io/fedora/fedora:43" in local containers storage ([overlay@/var/home/tollsimy/.local/share/containers/storage+/run/user/1000/containers]@ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b) 
DEBU[0000] exporting opaque data as blob "sha256:ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b" 
DEBU[0000] Looking up image "quay.io/fedora/fedora:43" in local containers storage 
DEBU[0000] Normalized platform linux/amd64 to {amd64 linux  [] } 
DEBU[0000] Trying "quay.io/fedora/fedora:43" ...        
DEBU[0000] parsed reference into "[overlay@/var/home/tollsimy/.local/share/containers/storage+/run/user/1000/containers]@ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b" 
DEBU[0000] Found image "quay.io/fedora/fedora:43" as "quay.io/fedora/fedora:43" in local containers storage 
DEBU[0000] Found image "quay.io/fedora/fedora:43" as "quay.io/fedora/fedora:43" in local containers storage ([overlay@/var/home/tollsimy/.local/share/containers/storage+/run/user/1000/containers]@ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b) 
DEBU[0000] exporting opaque data as blob "sha256:ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b" 
DEBU[0000] Inspecting image ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b 
DEBU[0000] exporting opaque data as blob "sha256:ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b" 
DEBU[0000] Inspecting image ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b 
DEBU[0000] Inspecting image ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b 
DEBU[0000] using systemd mode: false                    
DEBU[0000] No hostname set; container's hostname will default to runtime default 
DEBU[0000] Loading seccomp profile from "/usr/share/containers/seccomp.json" 
DEBU[0000] Allocated lock 108 for container 12410d146b11430b5bfee809b0e435a237bd429808c916c30d808e9e89959f22 
DEBU[0000] exporting opaque data as blob "sha256:ad291a240ef9da8feda7517b3757f4bb9b1af017df724110e96472f34fa5103b" 
DEBU[0000] Cached value indicated that idmapped mounts for overlay are not supported 
DEBU[0000] Check for idmapped mounts support            
DEBU[0000] Created container "12410d146b11430b5bfee809b0e435a237bd429808c916c30d808e9e89959f22" 
DEBU[0000] Container "12410d146b11430b5bfee809b0e435a237bd429808c916c30d808e9e89959f22" has work directory "/var/home/tollsimy/.local/share/containers/storage/overlay-containers/12410d146b11430b5bfee809b0e435a237bd429808c916c30d808e9e89959f22/userdata" 
DEBU[0000] Container "12410d146b11430b5bfee809b0e435a237bd429808c916c30d808e9e89959f22" has run directory "/run/user/1000/containers/overlay-containers/12410d146b11430b5bfee809b0e435a237bd429808c916c30d808e9e89959f22/userdata" 
12410d146b11430b5bfee809b0e435a237bd429808c916c30d808e9e89959f22
DEBU[0000] Called create.PersistentPostRunE(podman create --log-level=debug quay.io/fedora/fedora:43) 
DEBU[0000] Shutting down engines                        
INFO[0000] Received shutdown.Stop(), terminating!        PID=9333

I guess podman-remote needs to query remote container storage so that's why you see the POST libpod/images/pull request.


container_id = response.json()["Id"]

Expand Down
14 changes: 2 additions & 12 deletions podman/domain/containers_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from podman.domain.containers import Container
from podman.domain.images import Image
from podman.errors import ContainerError, ImageNotFound
from podman.errors import ContainerError

logger = logging.getLogger("podman.containers")

Expand Down Expand Up @@ -62,7 +62,6 @@ def run(

Raises:
ContainerError: when Container exists with a non-zero code
ImageNotFound: when Image not found by Podman service
APIError: when Podman service reports an error
"""
if isinstance(image, Image):
Expand All @@ -72,16 +71,7 @@ def run(
if isinstance(command, str):
command = [command]

try:
container = self.create(image=image_id, command=command, **kwargs) # type: ignore[attr-defined]
except ImageNotFound:
self.podman_client.images.pull( # type: ignore[attr-defined]
image_id,
auth_config=kwargs.get("auth_config"),
platform=kwargs.get("platform"),
policy=kwargs.get("policy", "missing"),
)
container = self.create(image=image_id, command=command, **kwargs) # type: ignore[attr-defined]
container = self.create(image=image_id, command=command, **kwargs) # type: ignore[attr-defined]

container.start()
container.reload()
Expand Down
107 changes: 98 additions & 9 deletions podman/tests/unit/test_containersmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from podman.domain.containers import Container
from podman.domain.containers_create import CreateMixin
from podman.domain.containers_manager import ContainersManager
from podman.errors import ImageNotFound, NotFound
from podman.errors import NotFound

FIRST_CONTAINER = {
"Id": "87e1325c82424e49a00abdd4de08009eb76c7de8d228426a9b8af9318ced5ecd",
Expand Down Expand Up @@ -338,17 +338,45 @@ def test_create(self, mock):

@requests_mock.Mocker()
def test_create_404(self, mock):
# mock the first POST to return 404,
# then the second POST (after pulling the image) to return 201
mock.post(
tests.LIBPOD_URL + "/containers/create",
status_code=404,
json={
"cause": "Image not found",
"message": "Image not found",
"response": 404,
},
[
{
"status_code": 404,
"json": {
"cause": "Image not found",
"message": "Image not found",
"response": 404,
},
},
{
"status_code": 201,
"json": {
"Id": "87e1325c82424e49a00abdd4de08009eb76c7de8d228426a9b8af9318ced5ecd",
"Warnings": [],
},
},
],
)
self.client.images.pull = MagicMock()
mock.get(
tests.LIBPOD_URL + f"/containers/{FIRST_CONTAINER['Id']}/json",
json=FIRST_CONTAINER,
)
actual = self.client.containers.create("fedora", "/usr/bin/ls", cpu_count=9999)
self.client.images.pull.assert_called_once_with(
"fedora",
auth_config=None,
platform=None,
policy="missing",
)
with self.assertRaises(ImageNotFound):
self.client.containers.create("fedora", "/usr/bin/ls", cpu_count=9999)
self.assertIsInstance(actual, Container)
self.assertEqual(actual.id, FIRST_CONTAINER['Id'])
# 2 POSTs for create
# 1 GET for container json
self.assertEqual(mock.call_count, 3)

@requests_mock.Mocker()
def test_create_parse_host_port(self, mock):
Expand Down Expand Up @@ -644,6 +672,67 @@ def test_run(self, mock):
self.assertEqual(next(actual), b"This is a unittest - line 1")
self.assertEqual(next(actual), b"This is a unittest - line 2")

@requests_mock.Mocker()
def test_run_404(self, mock):
# mock the first POST to return 404,
# then the second POST (after pulling the image) to return 201
mock.post(
tests.LIBPOD_URL + "/containers/create",
[
{
"status_code": 404,
"json": {
"cause": "Image not found",
"message": "Image not found",
"response": 404,
},
},
{
"status_code": 201,
"json": {
"Id": "87e1325c82424e49a00abdd4de08009eb76c7de8d228426a9b8af9318ced5ecd",
"Warnings": [],
},
},
],
)
self.client.images.pull = MagicMock()
mock.post(
tests.LIBPOD_URL
+ "/containers/87e1325c82424e49a00abdd4de08009eb76c7de8d228426a9b8af9318ced5ecd/start",
status_code=204,
)
mock.get(
tests.LIBPOD_URL + f"/containers/{FIRST_CONTAINER['Id']}/json",
json=FIRST_CONTAINER,
)

mock_logs = (
b"This is a unittest - line 1",
b"This is a unittest - line 2",
)

with patch.multiple(Container, logs=DEFAULT, wait=DEFAULT, autospec=True) as mock_container:
mock_container["wait"].return_value = 0
mock_container["logs"].return_value = iter(mock_logs)

actual = self.client.containers.run("fedora", "/usr/bin/ls")
self.client.images.pull.assert_called_once_with(
"fedora",
auth_config=None,
platform=None,
policy="missing",
)
self.assertIsInstance(actual, bytes)
self.assertEqual(actual, b"This is a unittest - line 1This is a unittest - line 2")
# 2 POSTs for create
# 1 POST for start
# 1 GET for container json
# 1 GET for reload
for r in mock.request_history:
print(r)
self.assertEqual(mock.call_count, 5)


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