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

can't specify a volume via a host path

Open leifliddy opened this issue 2 years ago • 6 comments

According to the documentation listed here https://github.com/containers/podman-py/blob/main/podman/domain/containers_create.py

            volumes (Dict[str, Dict[str, str]]): A dictionary to configure volumes mounted inside
                the container. The key is either the host path or a volume name, and the value is
                a dictionary with the keys:
                - bind: The path to mount the volume inside the container
                - mode: Either rw to mount the volume read/write, or ro to mount it read-only.
                For example:
                    {'/home/user1/': {'bind': '/mnt/vol2', 'mode': 'rw'},
                     '/var/www': {'bind': '/mnt/vol1', 'mode': 'ro'}}

...a dictionary is needed to configure volumes inside the container.

Ok, so I'm literally using the same example to create a container.

client.containers.create(image=podman_image_name, name=podman_container_name, volumes={'/home/user1/': {'bind': '/mnt/vol2', 'mode': 'rw'}}

However, I'm met with the following error:

  File "/home/user1/Desktop/podman-clamav-el6/./script-podman.py", line 167, in <module>
    ensure_container_exists_and_running()
  File "/home/user1/Desktop/podman-clamav-el6/./script-podman.py", line 119, in ensure_container_exists_and_running
    run_container()
  File "/home/user1/Desktop/podman-clamav-el6/./script-podman.py", line 139, in run_container
    client.containers.create(image=podman_image_name, name=podman_container_name, volumes={'/home/user1/': {'bind': '/mnt/vol2', 'mode': 'rw'}})    
  File "/usr/lib/python3.10/site-packages/podman/domain/containers_create.py", line 224, in create
    response.raise_for_status(not_found=ImageNotFound)
  File "/usr/lib/python3.10/site-packages/podman/api/client.py", line 65, in raise_for_status
    raise APIError(cause, response=self._response, explanation=message)
podman.errors.exceptions.APIError: 500 Server Error: Internal Server Error (error creating named volume "/home/user1/": error running volume create option: names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*: invalid argument)

It's trying to create a named volume vs using the host path --even though the documentation clearly states The key is either the host path or a volume name

This was MUCH easier with docker-py where you could just provide a list of mounts.

I'll dig through the code later, but in the meantime any help would be appreciated.

leifliddy avatar Dec 11 '21 16:12 leifliddy

I sorted out a solution by analyzing containers_create.py basically I needed to do this to use the mounts feature (still not sure why the volumes feature doesn't work)

   bind_volumes =  [
        {
            'type': 'bind',
            'source': output_rpm_dir_host,
            'target': output_rpm_dir_container            
        }
    ]
    
    client.containers.run(image=podman_image_name, name=podman_container_name, mounts=bind_volumes)    

It would be nice if there was some sort of documentation on how to use the mounts feature. Anyways, I hope this helps someone out....

leifliddy avatar Dec 11 '21 18:12 leifliddy

Where would you expect to see the documentation? Interested in opening a PR to add the docs there?

rhatdan avatar Dec 13 '21 20:12 rhatdan

If you look at https://github.com/containers/podman-py/blob/main/podman/domain/containers_create.py

mounts (List[Mount]): Specification for mounts to be added to the container. More
    powerful alternative to volumes. Each item in the list is expected to be a
    Mount object.
                
....  

volumes (Dict[str, Dict[str, str]]): A dictionary to configure volumes mounted inside
    the container. The key is either the host path or a volume name, and the value is
    a dictionary with the keys:
    - bind: The path to mount the volume inside the container
    - mode: Either rw to mount the volume read/write, or ro to mount it read-only.
    For example:
        {'/home/user1/': {'bind': '/mnt/vol2', 'mode': 'rw'},
         '/var/www': {'bind': '/mnt/vol1', 'mode': 'ro'}}

You'll see there's an example given for volumes --which is a bad example since providing a host path doesn't work. But for "the much more powerful" mounts, it just states Each item in the list is expected to be a Mount object
Does anyone know what a mount object is!? All I'm saying is that it would be nice to have an example listed there of how to construct a Mount object for that --or rather the data structure....it's not really an object is it? I could write up a PR for that when I have time...

leifliddy avatar Dec 13 '21 21:12 leifliddy

I don't know if you're using Fedora/CentOS, but I had trouble with bind mounts until I set the context of the host directory to container_file_t. I was able to get both the mounts= and the volumes= directives working. For a an existing podman volume syncthing_config and a backup directory (with the same contents as the volume) /var/backups/scotty/podman/syncthing_config:

In [1]: from podman import PodmanClient
In [2]: client = PodmanClient()
In [3]: client.containers.run(image='alpine:3.15',name='test',command=["top"], \
          volumes={'syncthing_config': {'bind': '/mnt', 'mode':'rw'}}, \
          mounts=[{'type':'bind','source':'/var/backups/scotty/podman/syncthing_config/','target':'/backups'}]

In another terminal:

$ podman exec -it test ls /mnt
cert.pem          config.xml.v35    https-cert.pem    index-v0.14.0.db
config.xml        csrftokens.txt    https-key.pem     key.pem
$ podman exec -it test ls /backup
cert.pem          config.xml.v35    https-cert.pem    index-v0.14.0.db
config.xml        csrftokens.txt    https-key.pem     key.pem

To set the correct selinux context on the host directory:

    $ sudo semanage fcontext -a -t container_file_t "/var/backups/scotty/podman(/.+)?" 
    $ sudo restorecon -Frv /var/backups/scotty/podman/

It would be useful if the :z and :Z modes are handled by podman-py as well as rw and ro for the bind mounts. That would make the selinux issues go away, I think. Also, the volumes= directive does not seem to work with bind mounts as advertised in the docs (as pointed out by the OP). It doesn't look like the volume type is supported, only bind looking at the source code:

In [4]: client.containers.run(image='alpine:3.15', name='test', command["top"], \ 
          volumes={'/var/backups/scotty/podman/syncthing_config': {'bind': '/mnt', 'mode': 'rw'}})
......
APIError: 500 Server Error: Internal Server Error (error creating named volume "/var/backups/scotty/podman/syncthing_config": error running volume create option: names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*: invalid argument)

firecat53 avatar Mar 20 '22 04:03 firecat53

I've just run into the same error. I have an existing application that uses the Docker API (which this API is clearly based on) and volumes don't work when the keys are the host paths. There's also a similar problem with the user argument which raises:

500 Server Error: Internal Server Error (decode(): json: cannot unmarshal number into Go struct field SpecGenerator.user of type string)

When you pass a UID, I imagine passing a username will work (although I haven't tested yet).

Kazade avatar Oct 15 '23 16:10 Kazade

I am also getting this error. Does anyone know a workaround? It's a blocker for my use case

import os
import podman

os.makedirs("test-output-folder", exist_ok=True)
with podman.PodmanClient(base_url="unix:/var/run/docker.sock") as client:
    client.containers.run(
        "ubuntu",
        command=["touch", "/path/file.txt"],
        volumes={
            os.path.join(os.getcwd(), "test-output-folder"): {
                "bind": "/path",
                "mode": "rw",
            }
        },
        remove=True,
    )

error is

podman.errors.exceptions.APIError: 500 Server Error: Internal Server Error (creating named "...": running volume create option: names must match [a-zA-Z0-9][a-zA-Z0-9_.-]*: invalid argument)

SebastianCallh avatar Dec 21 '23 08:12 SebastianCallh