def test_init_no_docker_client(mock_quay_api_client, mock_quay_client): references = ["stage.quay.io/name/repo1:1", "stage.quay.io/name/repo2:2"] host = "stage.quay.io/" token = "some-token" untagger = setup_untagger(references, quay_password=None, token=token, host=host) mock_quay_client.assert_not_called() mock_quay_api_client.assert_called_once_with(token, host[:-1]) assert untagger._quay_client is None client = quay_client.QuayClient("user", "pass") untagger.set_quay_client(client) assert client == untagger._quay_client
def test_parse_image(mock_session): client = quay_client.QuayClient("user", "pass", "stage.quay.io") repo, ref = client._parse_and_validate_image_url("quay.io/name/image:1") assert repo == "name/image" assert ref == "1" repo, ref = client._parse_and_validate_image_url( "quay.io/name2/image2@sha256:dfgdfg8df5g") assert repo == "name2/image2" assert ref == "sha256:dfgdfg8df5g" with pytest.raises(ValueError, match="Neither tag nor digest were found in the image"): client._parse_and_validate_image_url("quay.io/name/image")
def test_get_manifest_v2s2_second_attempt(): v2s1_manifest = { "name": "hello-world", "tag": "latest", "architecture": "amd64", "fsLayers": [], "history": [], "schemaVersion": 1, "signatures": [], } v2s2_manifest = { "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 429, "digest": "sha256:6d5f4d65fg4d6f54g", "platform": { "architecture": "arm64", "os": "linux" }, } with requests_mock.Mocker() as m: m.get( "https://quay.io/v2/namespace/image/manifests/1", [ { "headers": { "Content-Type": "application/vnd.docker.distribution.manifest.v1+json" }, "json": v2s1_manifest, }, { "headers": { "Content-Type": "application/vnd.docker.distribution.manifest.v2+json" }, "json": v2s2_manifest, }, ], ) client = quay_client.QuayClient("user", "pass") ret_manifest = client.get_manifest("quay.io/namespace/image:1") assert m.call_count == 2 assert v2s2_manifest == ret_manifest
def test_get_repository_tags_raw(): with requests_mock.Mocker() as m: m.get( "https://quay.io/v2/namespace/image/tags/list", json={ "name": "namespace/image", "tags": ["1", "2", "3"] }, ) client = quay_client.QuayClient("user", "pass") data = client.get_repository_tags("namespace/image", "raw") assert data == json.dumps({ "name": "namespace/image", "tags": ["1", "2", "3"] }) assert m.call_count == 1
def test_get_repository_tags_pagination_cannot_parse_url(): with requests_mock.Mocker() as m: m.get( "https://quay.io/v2/namespace/image/tags/list", json={ "name": "namespace/image", "tags": ["1", "2", "3"] }, headers={ "Link": '/v2/namespace/image/tags/list/next-page-2; rel="next"' }, ) client = quay_client.QuayClient("user", "pass") with pytest.raises(ValueError, match="Could not extract next page URL.*"): client.get_repository_tags("namespace/image") assert m.call_count == 1
def test_authenticate_quay_success(mock_session, mock_quay_session): mock_response = mock.MagicMock() mock_response.json.return_value = {"token": "abcdef"} mock_get = mock.MagicMock() mock_get.return_value = mock_response mocked_session = mock.MagicMock() mocked_session.get = mock_get mock_session.return_value = mocked_session mocked_quay_session = mock.MagicMock() mock_quay_session.return_value = mocked_quay_session client = quay_client.QuayClient("user", "pass") header = { "Server": "nginx/1.12.1", "Date": "Tue, 02 Feb 2021 13:03:35 GMT", "Content-Type": "application/json", "Content-Length": "112", "Connection": "close", "Docker-Distribution-API-Version": "registry/2.0", "WWW-Authenticate": 'Bearer realm="https://quay.io/v2/auth",service="quay.io",' 'scope="repository:namespace/some-repo:pull"', } client._authenticate_quay(header) mocked_session.get.assert_called_once_with( "https://quay.io/v2/auth", auth=("user", "pass"), params={ "service": "quay.io", "scope": "repository:namespace/some-repo:pull" }, timeout=10, ) mocked_quay_session.set_auth_token.assert_called_once_with("abcdef")
def test_get_manifest_digest(): manifest = ( '{"mediaType": "application/vnd.docker.distribution.manifest.v2+json",' ' "size": 429, "digest": "sha256:6d5f4d65fg4d6f54g",' ' "platform": {"architecture": "arm64", "os": "linux"}}') with requests_mock.Mocker() as m: m.get( "https://quay.io/v2/namespace/image/manifests/1", text=manifest, headers={ "Content-Type": "application/vnd.docker.distribution.manifest.v2+json" }, ) client = quay_client.QuayClient("user", "pass") digest = client.get_manifest_digest("quay.io/namespace/image:1") assert m.call_count == 1 assert digest == "sha256:b9742c91f353022604e8ed4cf4ab1d688114fc4b133f0e11cbf7dd6272753ac8"
def test_get_repository_tags_pagination(): with requests_mock.Mocker() as m: m.get( "https://quay.io/v2/namespace/image/tags/list", json={ "name": "namespace/image", "tags": ["1", "2", "3"] }, headers={ "Link": '</v2/namespace/image/tags/list/next-page-2>; rel="next"' }, ) m.get( "https://quay.io/v2/namespace/image/tags/list/next-page-2", json={ "name": "namespace/image", "tags": ["4", "5", "6"] }, headers={ "Link": '</v2/namespace/image/tags/list/next-page-3>; rel="next"' }, ) m.get( "https://quay.io/v2/namespace/image/tags/list/next-page-3", json={ "name": "namespace/image", "tags": ["7", "8", "9"] }, ) client = quay_client.QuayClient("user", "pass") data = client.get_repository_tags("namespace/image") assert data == { "name": "namespace/image", "tags": ["1", "2", "3", "4", "5", "6", "7", "8", "9"], } assert m.call_count == 3
def test_get_manifest_list_raw_success(): ml = { "schemaVersion": 2, "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json", "manifests": [{ "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "size": 429, "digest": "sha256:6d5f4d65fg4d6f54g", "platform": { "architecture": "arm64", "os": "linux" }, }], } with requests_mock.Mocker() as m: m.get( "https://quay.io/v2/namespace/image/manifests/1", json=ml, headers={ "Content-Type": "application/vnd.docker.distribution.manifest.list.v2+json" }, ) client = quay_client.QuayClient("user", "pass") ret_ml = client.get_manifest( "quay.io/namespace/image:1", raw=True, media_type= "application/vnd.docker.distribution.manifest.list.v2+json", ) assert m.call_count == 1 assert json.dumps(ml) == ret_ml
def test_request_quay_authenticate_missing(mock_authenticate): with requests_mock.Mocker() as m: m.get( "https://quay.io/v2/get/data/1", [ { "headers": { "some-header": "value" }, "status_code": 401 }, { "text": "missing", "status_code": 404 }, ], ) client = quay_client.QuayClient("user", "pass") with pytest.raises(requests.HTTPError, match="404 Client Error.*"): client._request_quay("GET", "get/data/1") mock_authenticate.assert_called_once_with({"some-header": "value"})
def test_request_quay_authenticate_success(mock_authenticate): with requests_mock.Mocker() as m: m.get( "https://quay.io/v2/get/data/1", [ { "headers": { "some-header": "value" }, "status_code": 401 }, { "text": "data", "status_code": 200 }, ], ) client = quay_client.QuayClient("user", "pass") r = client._request_quay("GET", "get/data/1") assert r.text == "data" assert r.status_code == 200 mock_authenticate.assert_called_once_with({"some-header": "value"})