refactor(endpoints/api/tag): refactor code for v22

this decouples the database models from the api

[TESTING->locally with docker compose]

Issue: https://coreosdev.atlassian.net/browse/QUAY-632

- [ ] It works!
- [ ] Comments provide sufficient explanations for the next contributor
- [ ] Tests cover changes and corner cases
- [ ] Follows Quay syntax patterns and format
This commit is contained in:
Charlton Austin 2017-07-06 14:50:30 -04:00
parent a3afd37c41
commit fc4b3642d3
7 changed files with 483 additions and 67 deletions

View file

@ -1,9 +1,12 @@
import pytest
from endpoints.api.tag_models_interface import RepositoryTagHistory, Tag
from mock import Mock
from data.model import DataModelException, InvalidImageException
from endpoints.api.tag_models_interface import RepositoryTagHistory, Tag, Repository
from mock import Mock, call
from data import model
from endpoints.api.tag_models_pre_oci import pre_oci_model
from util.morecollections import AttrDict
EMPTY_REPOSITORY = 'empty_repository'
EMPTY_NAMESPACE = 'empty_namespace'
@ -101,3 +104,221 @@ def test_list_repository_tag_history(expected, namespace_name, repository_name,
size, specific_tag)
assert pre_oci_model.list_repository_tag_history(namespace_name, repository_name, page, size,
specific_tag) == expected
def get_repo_image_mock(monkeypatch, return_value):
def return_return_value(namespace_name, repository_name, image_id):
return return_value
monkeypatch.setattr(model.image, 'get_repo_image', return_return_value)
def test_get_repo_not_exists(get_monkeypatch):
namespace_name = 'namespace_name'
repository_name = 'repository_name'
image_id = 'image_id'
get_repo_image_mock(get_monkeypatch, None)
repo = pre_oci_model.get_repo(namespace_name, repository_name, image_id)
assert repo is None
def test_get_repo_exists(get_monkeypatch):
namespace_name = 'namespace_name'
repository_name = 'repository_name'
image_id = 'image_id'
mock = Mock()
mock.namespace_user = namespace_name
mock.name = repository_name
mock.repository = mock
get_repo_image_mock(get_monkeypatch, mock)
repo = pre_oci_model.get_repo(namespace_name, repository_name, image_id)
assert repo is not None
assert repo.repository_name == repository_name
assert repo.namespace_name == namespace_name
def get_repository_mock(monkeypatch, return_value):
def return_return_value(namespace_name, repository_name, kind_filter=None):
return return_value
monkeypatch.setattr(model.repository, 'get_repository', return_return_value)
def get_repo_tag_image_mock(monkeypatch, return_value):
def return_return_value(repo, tag_name, include_storage=False):
return return_value
monkeypatch.setattr(model.tag, 'get_repo_tag_image', return_return_value)
def test_get_repo_tag_image_with_repo_and_repo_tag(get_monkeypatch):
mock_storage = Mock()
mock_image = Mock()
mock_image.docker_image_id = 'some docker image id'
mock_image.created = 1235
mock_image.comment = 'some comment'
mock_image.command = 'some command'
mock_image.storage = mock_storage
mock_image.ancestors = []
get_repository_mock(get_monkeypatch, mock_image)
get_repo_tag_image_mock(get_monkeypatch, mock_image)
image = pre_oci_model.get_repo_tag_image(Repository('namespace_name', 'repository_name'), 'tag_name')
assert image is not None
assert image.docker_image_id == 'some docker image id'
def test_get_repo_tag_image_without_repo(get_monkeypatch):
get_repository_mock(get_monkeypatch, None)
image = pre_oci_model.get_repo_tag_image(Repository('namespace_name', 'repository_name'), 'tag_name')
assert image is None
def test_get_repo_tag_image_without_repo_tag_image(get_monkeypatch):
mock = Mock()
mock.docker_image_id = 'some docker image id'
get_repository_mock(get_monkeypatch, mock)
def raise_exception(repo, tag_name, include_storage=False):
raise DataModelException()
get_monkeypatch.setattr(model.tag, 'get_repo_tag_image', raise_exception)
image = pre_oci_model.get_repo_tag_image(Repository('namespace_name', 'repository_name'), 'tag_name')
assert image is None
def test_create_or_update_tag(get_monkeypatch):
mock = Mock()
get_monkeypatch.setattr(model.tag, 'create_or_update_tag', mock)
pre_oci_model.create_or_update_tag('namespace_name', 'repository_name', 'tag_name', 'docker_image_id')
assert mock.call_count == 1
assert mock.call_args == call('namespace_name', 'repository_name', 'tag_name', 'docker_image_id')
def test_delete_tag(get_monkeypatch):
mock = Mock()
get_monkeypatch.setattr(model.tag, 'delete_tag', mock)
pre_oci_model.delete_tag('namespace_name', 'repository_name', 'tag_name')
assert mock.call_count == 1
assert mock.call_args == call('namespace_name', 'repository_name', 'tag_name')
def test_get_parent_images_with_exception(get_monkeypatch):
mock = Mock(side_effect=InvalidImageException)
get_monkeypatch.setattr(model.image, 'get_image_by_id', mock)
images = pre_oci_model.get_parent_images('namespace_name', 'repository_name', 'tag_name')
assert images == []
def test_get_parent_images_empty_parent_images(get_monkeypatch):
get_image_by_id_mock = Mock()
get_monkeypatch.setattr(model.image, 'get_image_by_id', get_image_by_id_mock)
get_parent_images_mock = Mock(return_value=[])
get_monkeypatch.setattr(model.image, 'get_parent_images', get_parent_images_mock)
images = pre_oci_model.get_parent_images('namespace_name', 'repository_name', 'tag_name')
assert images == []
def compare_images(parent_image, attr_dict):
for field in parent_image._fields:
assert getattr(parent_image, field) == getattr(attr_dict, field)
def test_get_parent_images(get_monkeypatch):
get_image_by_id_mock = Mock()
get_monkeypatch.setattr(model.image, 'get_image_by_id', get_image_by_id_mock)
def fake_list():
return []
image_one = AttrDict(
{'docker_image_id': 'docker_image_id', 'created': 'created_one', 'comment': 'comment_one', 'command': 'command_one',
'storage': AttrDict({'image_size': 'image_size_one', 'uploading': 'uploading_one'}), 'ancestors': 'one/two/three',
'ancestor_id_list': fake_list})
image_two = AttrDict(
{'docker_image_id': 'docker_image_id_two', 'created': 'created', 'comment': 'comment', 'command': 'command',
'storage': AttrDict({'image_size': 'image_size', 'uploading': 'uploading'}), 'ancestors': 'four/five/six',
'ancestor_id_list': fake_list})
get_parent_images_mock = Mock(return_value=[image_one, image_two])
get_monkeypatch.setattr(model.image, 'get_parent_images', get_parent_images_mock)
get_monkeypatch.setattr(model.image, 'get_image_by_id', Mock())
images = pre_oci_model.get_parent_images('namespace_name', 'repository_name', 'tag_name')
image_one.ancestor_id_list = []
image_one.storage_image_size = 'image_size_one'
image_one.storage_uploading = 'uploading_one'
image_one.ancestor_length = 13
image_two.ancestor_id_list = []
image_two.storage_uploading = 'uploading'
image_two.storage_image_size = 'image_size'
image_two.ancestor_length = 13
compare_images(images[0], image_one)
compare_images(images[1], image_two)
def test_list_repository_tags(get_monkeypatch):
mock = Mock(return_value=[])
get_monkeypatch.setattr(model.tag, 'list_repository_tags', mock)
pre_oci_model.list_repository_tags('namespace_name', 'repository_name')
mock.assert_called_once_with('namespace_name', 'repository_name')
def test_get_repository(get_monkeypatch):
mock = Mock()
get_monkeypatch.setattr(model.repository, 'get_repository', mock)
pre_oci_model.get_repository('namespace_name', 'repository_name')
mock.assert_called_once_with('namespace_name', 'repository_name')
def test_tag_to_manifest(get_monkeypatch):
repo_mock = Mock()
restore_tag_mock = Mock(return_value=None)
get_repository_mock = Mock(return_value=repo_mock)
get_monkeypatch.setattr(model.tag, 'restore_tag_to_manifest', restore_tag_mock)
get_monkeypatch.setattr(model.repository, 'get_repository', get_repository_mock)
pre_oci_model.restore_tag_to_manifest(Repository('namespace', 'repository'), 'tag_name', 'manifest_digest')
get_repository_mock.assert_called_once_with('namespace', 'repository')
restore_tag_mock.assert_called_once_with(repo_mock, 'tag_name', 'manifest_digest')
def test__tag_to_image(get_monkeypatch):
repo_mock = Mock()
restore_tag_mock = Mock(return_value=None)
get_repository_mock = Mock(return_value=repo_mock)
get_monkeypatch.setattr(model.tag, 'restore_tag_to_image', restore_tag_mock)
get_monkeypatch.setattr(model.repository, 'get_repository', get_repository_mock)
pre_oci_model.restore_tag_to_image(Repository('namespace', 'repository'), 'tag_name', 'image_id')
get_repository_mock.assert_called_once_with('namespace', 'repository')
restore_tag_mock.assert_called_once_with(repo_mock, 'tag_name', 'image_id')

View file

@ -4,7 +4,7 @@ import pytest
from mock import patch, Mock, MagicMock, call
from data.model import DataModelException
from endpoints.api.tag_models_interface import RepositoryTagHistory, Tag
from endpoints.api.test.shared import conduct_api_call
from endpoints.test.shared import client_with_identity
@ -18,26 +18,36 @@ from test.fixtures import *
@pytest.fixture()
def get_repo_image():
def mock_callable(namespace, repository, image_id):
img = Mock(repository='fetched_repository') if image_id == 'image1' else None
mock = Mock(namespace_user='devtable')
mock.name = 'simple'
img = Mock(repository=mock, docker_image_id=12) if image_id == 'image1' else None
return img
with patch('endpoints.api.tag.model.image.get_repo_image', side_effect=mock_callable) as mk:
with patch('endpoints.api.tag_models_pre_oci.model.image.get_repo_image', side_effect=mock_callable) as mk:
yield mk
@pytest.fixture()
def get_repository():
with patch('endpoints.api.tag.model.image.get_repo_image', return_value='mock_repo') as mk:
with patch('endpoints.api.tag_models_pre_oci.model.image.get_repo_image', return_value='mock_repo') as mk:
yield mk
@pytest.fixture()
def get_repo_tag_image():
def mock_get_repo_tag_image(repository, tag):
tag_img = Mock(docker_image_id='mock_docker_image_id') if tag == 'existing-tag' else None
return tag_img
storage_mock = Mock(image_size=1234, uploading='uploading')
with patch('endpoints.api.tag.model.tag.get_repo_tag_image',
def fake_ancestor_id_list():
return []
if tag == 'existing-tag':
return Mock(docker_image_id='mock_docker_image_id', created=12345, comment='comment', command='command',
storage=storage_mock, ancestors=[], ancestor_id_list=fake_ancestor_id_list)
else:
raise DataModelException('Unable to find image for tag.')
with patch('endpoints.api.tag_models_pre_oci.model.tag.get_repo_tag_image',
side_effect=mock_get_repo_tag_image):
yield
@ -48,7 +58,7 @@ def restore_tag_to_manifest():
tag_img = Mock(docker_image_id='mock_docker_image_id') if tag == 'existing-tag' else None
return tag_img
with patch('endpoints.api.tag.model.tag.restore_tag_to_manifest',
with patch('endpoints.api.tag_models_pre_oci.model.tag.restore_tag_to_manifest',
side_effect=mock_restore_tag_to_manifest):
yield
@ -59,14 +69,14 @@ def restore_tag_to_image():
tag_img = Mock(docker_image_id='mock_docker_image_id') if tag == 'existing-tag' else None
return tag_img
with patch('endpoints.api.tag.model.tag.restore_tag_to_image',
with patch('endpoints.api.tag_models_pre_oci.model.tag.restore_tag_to_image',
side_effect=mock_restore_tag_to_image):
yield
@pytest.fixture()
def create_or_update_tag():
with patch('endpoints.api.tag.model.tag.create_or_update_tag') as mk:
with patch('endpoints.api.tag_models_pre_oci.model.tag.create_or_update_tag') as mk:
yield mk
@ -95,7 +105,7 @@ def list_repository_tag_history():
Tag(name='Second Tag', image='second image', reversion=True, lifetime_start_ts=10, lifetime_end_ts=100,
manifest_list=[], docker_image_id='second docker image id')], more=False)
with patch('endpoints.api.tag.pre_oci_model.list_repository_tag_history', side_effect=list_repository_tag_history):
with patch('endpoints.api.tag.model.list_repository_tag_history', side_effect=list_repository_tag_history):
yield
@ -104,7 +114,7 @@ def find_no_repo_tag_history():
def list_repository_tag_history(namespace_name, repository_name, page, size, specific_tag):
return None
with patch('endpoints.api.tag.pre_oci_model.list_repository_tag_history', side_effect=list_repository_tag_history):
with patch('endpoints.api.tag.model.list_repository_tag_history', side_effect=list_repository_tag_history):
yield
@ -178,7 +188,7 @@ def test_repo_tag_history_param_parse(specific_tag, page, limit, expected_specif
mock = MagicMock()
mock.return_value = RepositoryTagHistory(tags=[], more=False)
with patch('endpoints.api.tag.pre_oci_model.list_repository_tag_history', side_effect=mock):
with patch('endpoints.api.tag.model.list_repository_tag_history', side_effect=mock):
params = {'repository': 'devtable/simple', 'specificTag': specific_tag, 'page': page, 'limit': limit}
conduct_api_call(authd_client, ListRepositoryTags, 'get', params)