Skip to content

Commit 599b17a

Browse files
Merge pull request #593 from Riushda/issue/add_export_and_import_commands
Add support for export and import commands
2 parents 4d3a079 + 382d30a commit 599b17a

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed

podman/domain/volumes.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from typing import Any, Literal, Optional, Union
55

66
import requests
7+
import pathlib
78

89
from podman import api
910
from podman.domain.manager import Manager, PodmanResource
@@ -173,3 +174,49 @@ def remove(self, name: Union[Volume, str], force: Optional[bool] = None) -> None
173174
name = name.name
174175
response = self.client.delete(f"/volumes/{name}", params={"force": force})
175176
response.raise_for_status()
177+
178+
def export_archive(self, name: Union[Volume, str]) -> bytes:
179+
"""Export a podman volume, returns the exported archive as bytes.
180+
181+
Args:
182+
name: Identifier for Volume to be exported.
183+
184+
Raises:
185+
APIError: when service reports an error
186+
"""
187+
if isinstance(name, Volume):
188+
name = name.name
189+
response = self.client.get(f"/volumes/{name}/export")
190+
response.raise_for_status()
191+
return response._content
192+
193+
def import_archive(
194+
self, name: Union[Volume, str], data: Optional[bytes] = None, path: Optional[str] = None
195+
):
196+
"""Import a podman volume from tar.
197+
The podman volume archive must be provided either as bytes or as a path to the archive.
198+
199+
Args:
200+
name: Identifier for Volume to be imported.
201+
data: Uncompressed tar archive as bytes.
202+
path: Path to uncompressed tar archive.
203+
204+
Raises:
205+
APIError: when service reports an error
206+
"""
207+
if isinstance(name, Volume):
208+
name = name.name
209+
210+
if data is None and path is None:
211+
raise RuntimeError("Either data or path must be provided !")
212+
elif data is not None and path is not None:
213+
raise RuntimeError("Data and path must not be set at the same time !")
214+
215+
if data is None:
216+
file = pathlib.Path(path)
217+
if not file.exists():
218+
raise RuntimeError(f"Archive {path} does not exist !")
219+
data = file.read_bytes()
220+
221+
response = self.client.post(f"/volumes/{name}/import", data=data)
222+
response.raise_for_status()

podman/tests/integration/test_volumes.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from podman import PodmanClient
55
from podman.errors import NotFound
66
from podman.tests.integration import base
7+
from podman.tests.utils import PODMAN_VERSION
78

89

910
class VolumesIntegrationTest(base.IntegrationTest):
@@ -35,6 +36,13 @@ def test_volume_crud(self):
3536
names = [i.name for i in report]
3637
self.assertIn(volume_name, names)
3738

39+
if PODMAN_VERSION >= (5, 6, 0):
40+
with self.subTest("Export"):
41+
archive_bytes = self.client.volumes.export_archive(volume_name)
42+
43+
with self.subTest("Import"):
44+
self.client.volumes.import_archive(volume_name, archive_bytes)
45+
3846
with self.subTest("Remove"):
3947
self.client.volumes.remove(volume_name, force=True)
4048
with self.assertRaises(NotFound):

podman/tests/unit/test_volumesmanager.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,28 @@ def test_prune(self, mock):
137137
actual, {"VolumesDeleted": ["dbase", "source"], "SpaceReclaimed": 2048}
138138
)
139139

140+
@requests_mock.Mocker()
141+
def test_import(self, mock):
142+
mock.post(
143+
tests.LIBPOD_URL + "/volumes/dbase/import",
144+
status_code=requests.codes.no_content,
145+
)
146+
actual = self.client.volumes.import_archive("dbase", data=b'mocked_archive')
147+
self.assertIsInstance(actual, type(None))
148+
149+
with self.assertRaises(RuntimeError):
150+
# The archive does not exist
151+
self.client.volumes.import_archive("dbase", path="/path/to/archive.tar")
152+
153+
@requests_mock.Mocker()
154+
def test_export(self, mock):
155+
mock.get(
156+
tests.LIBPOD_URL + "/volumes/dbase/export",
157+
content=b'exported_archive',
158+
)
159+
actual = self.client.volumes.export_archive("dbase")
160+
self.assertIsInstance(actual, bytes)
161+
140162

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

0 commit comments

Comments
 (0)