Skip to content

Commit 9d48125

Browse files
Merge pull request #536 from dklimpel/fix_push_manifest
Set X-Registry-Auth header on manifest push and bump to new API
2 parents e46c204 + d02e7e5 commit 9d48125

File tree

5 files changed

+67
-5
lines changed

5 files changed

+67
-5
lines changed

podman/api/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from podman.api.cached_property import cached_property
44
from podman.api.client import APIClient
55
from podman.api.api_versions import VERSION, COMPATIBLE_VERSION
6-
from podman.api.http_utils import prepare_body, prepare_filters
6+
from podman.api.http_utils import encode_auth_header, prepare_body, prepare_filters
77
from podman.api.parse_utils import (
88
decode_header,
99
frames,
@@ -27,6 +27,7 @@
2727
'cached_property',
2828
'create_tar',
2929
'decode_header',
30+
'encode_auth_header',
3031
'frames',
3132
'parse_repository',
3233
'prepare_body',

podman/domain/images_manager.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
from podman import api
1515
from podman.api.parse_utils import parse_repository
16-
from podman.api.http_utils import encode_auth_header
1716
from podman.domain.images import Image
1817
from podman.domain.images_build import BuildMixin
1918
from podman.domain.json_stream import json_stream
@@ -264,7 +263,7 @@ def push(
264263

265264
headers = {
266265
# A base64url-encoded auth configuration
267-
"X-Registry-Auth": encode_auth_header(auth_config) if auth_config else ""
266+
"X-Registry-Auth": api.encode_auth_header(auth_config) if auth_config else ""
268267
}
269268

270269
params = {
@@ -362,7 +361,7 @@ def pull(
362361

363362
headers = {
364363
# A base64url-encoded auth configuration
365-
"X-Registry-Auth": encode_auth_header(auth_config) if auth_config else ""
364+
"X-Registry-Auth": api.encode_auth_header(auth_config) if auth_config else ""
366365
}
367366

368367
params = {

podman/domain/manifests.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,22 +97,40 @@ def push(
9797
self,
9898
destination: str,
9999
all: Optional[bool] = None, # pylint: disable=redefined-builtin
100+
**kwargs,
100101
) -> None:
101102
"""Push a manifest list or image index to a registry.
102103
103104
Args:
104105
destination: Target for push.
105106
all: Push all images.
106107
108+
Keyword Args:
109+
auth_config (Mapping[str, str]: Override configured credentials. Must include
110+
username and password keys.
111+
107112
Raises:
108113
NotFound: when the Manifest could not be found
109114
APIError: when service reports an error
110115
"""
116+
auth_config: Optional[dict[str, str]] = kwargs.get("auth_config")
117+
118+
headers = {
119+
# A base64url-encoded auth configuration
120+
"X-Registry-Auth": api.encode_auth_header(auth_config) if auth_config else ""
121+
}
122+
111123
params = {
112124
"all": all,
113125
"destination": destination,
114126
}
115-
response = self.client.post(f"/manifests/{self.quoted_name}/push", params=params)
127+
128+
destination_quoted = urllib.parse.quote_plus(destination)
129+
response = self.client.post(
130+
f"/manifests/{self.quoted_name}/registry/{destination_quoted}",
131+
params=params,
132+
headers=headers,
133+
)
116134
response.raise_for_status()
117135

118136
def remove(self, digest: str) -> None:

podman/tests/unit/test_api_utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,15 @@ def test_prepare_body_dict_empty_string(self) -> None:
164164

165165
self.assertDictEqual(payload, actual_dict)
166166

167+
def test_encode_auth_header(self):
168+
auth_config = {
169+
"username": "user",
170+
"password": "pass",
171+
}
172+
expected = b"eyJ1c2VybmFtZSI6ICJ1c2VyIiwgInBhc3N3b3JkIjogInBhc3MifQ=="
173+
actual = api.encode_auth_header(auth_config)
174+
self.assertEqual(expected, actual)
175+
167176

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

podman/tests/unit/test_manifests.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
import unittest
22

3+
import requests_mock
4+
35
from podman import PodmanClient, tests
46
from podman.domain.manifests import Manifest, ManifestsManager
57

8+
FIRST_MANIFEST = {
9+
"Id": "326dd9d7add24646a389e8eaa82125294027db2332e49c5828d96312c5d773ab",
10+
"names": "quay.io/fedora:latest",
11+
}
12+
613

714
class ManifestTestCase(unittest.TestCase):
815
def setUp(self) -> None:
@@ -23,6 +30,34 @@ def test_name(self):
2330
manifest = Manifest()
2431
self.assertIsNone(manifest.name)
2532

33+
@requests_mock.Mocker()
34+
def test_push(self, mock):
35+
adapter = mock.post(
36+
tests.LIBPOD_URL + "/manifests/quay.io%2Ffedora%3Alatest/registry/quay.io%2Ffedora%3Av1"
37+
)
38+
39+
manifest = Manifest(attrs=FIRST_MANIFEST, client=self.client.api)
40+
manifest.push(destination="quay.io/fedora:v1")
41+
42+
self.assertTrue(adapter.called_once)
43+
44+
@requests_mock.Mocker()
45+
def test_push_with_auth(self, mock):
46+
adapter = mock.post(
47+
tests.LIBPOD_URL
48+
+ "/manifests/quay.io%2Ffedora%3Alatest/registry/quay.io%2Ffedora%3Av1",
49+
request_headers={
50+
"X-Registry-Auth": b"eyJ1c2VybmFtZSI6ICJ1c2VyIiwgInBhc3N3b3JkIjogInBhc3MifQ=="
51+
},
52+
)
53+
54+
manifest = Manifest(attrs=FIRST_MANIFEST, client=self.client.api)
55+
manifest.push(
56+
destination="quay.io/fedora:v1", auth_config={"username": "user", "password": "pass"}
57+
)
58+
59+
self.assertTrue(adapter.called_once)
60+
2661

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

0 commit comments

Comments
 (0)