def test_quay_token_missing(get): """ Test missing token in env but defined in config. """ get.return_value.json.return_value = QUAY_API_DATA container.check_repos(CONF, {}) get.assert_called_once_with( 'https://quay.io/api/v1/repository/repos/testrepo/tag/?onlyActiveTags=true&limit=100&page=1', headers={} )
def test_quay_token(get): """ Test that token is passed from config. """ get.return_value.json.return_value = QUAY_API_DATA container.check_repos(CONF, {}) get.assert_called_once_with( 'https://quay.io/api/v1/repository/repos/testrepo/tag/?onlyActiveTags=true&limit=100&page=1', headers={"Authorization": "Bearer TOKEN"} )
def test_quay_latest(get): """ Test that data for a single tag from the quay.io API is handled correctly. """ get.return_value.json.return_value = QUAY_API_DATA result = container.check_repos(CONF, {}) get.assert_called_once_with( 'https://quay.io/api/v1/repository/repos/testrepo/tag/?onlyActiveTags=true&limit=100&page=1', headers={} ) assert result == { 'quay.io/repos/testrepo': { 'latest': { 'action': 'added', 'repo': 'quay.io/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': QUAY_API_DATA['tags'][0]['manifest_digest'], 'old_digest': None, 'created': format_ts(QUAY_API_DATA['tags'][0]['start_ts']), 'labels': {}, 'os': '', 'arch': '' } } }
def test_check_repos_raises(inspect_tag): """ Test that an error inspecting the repo results in the correct output. """ result = container.check_repos(CONF, {}) inspect_tag.assert_called_once_with('example.com/repos/testrepo') assert result == {}
def test_quay_multitag_multipage(get): """ Test that multiple pages of data containing multiple tags from the quay.io API are handled correctly. """ get.return_value.json.side_effect = [QUAY_API_DATA_MULTITAG, QUAY_API_DATA] result = container.check_repos(CONF, {}) calls = [ call( 'https://quay.io/api/v1/repository/repos/testrepo/tag/?onlyActiveTags=true&limit=100&page=1', headers={} ), call( 'https://quay.io/api/v1/repository/repos/testrepo/tag/?onlyActiveTags=true&limit=100&page=2', headers={} ) ] get.assert_has_calls(calls, any_order=True) assert result == { 'quay.io/repos/testrepo': { 'stage': { 'action': 'added', 'repo': 'quay.io/repos/testrepo', 'reponame': 'testrepo', 'tag': 'stage', 'digest': QUAY_API_DATA_MULTITAG['tags'][0]['manifest_digest'], 'old_digest': None, 'created': format_ts(QUAY_API_DATA_MULTITAG['tags'][0]['start_ts']), 'labels': {}, 'os': '', 'arch': '' }, 'prod': { 'action': 'added', 'repo': 'quay.io/repos/testrepo', 'reponame': 'testrepo', 'tag': 'prod', 'digest': QUAY_API_DATA_MULTITAG['tags'][1]['manifest_digest'], 'old_digest': None, 'created': format_ts(QUAY_API_DATA_MULTITAG['tags'][1]['start_ts']), 'labels': {}, 'os': '', 'arch': '' }, 'latest': { 'action': 'added', 'repo': 'quay.io/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': QUAY_API_DATA['tags'][0]['manifest_digest'], 'old_digest': None, 'created': format_ts(QUAY_API_DATA['tags'][0]['start_ts']), 'labels': {}, 'os': '', 'arch': '' } } }
def test_check_repos_removed_previously(inspect_tag): """ Test that a previously removed tag doesn't stay in the results. """ old_data = { 'example.com/repos/testrepo': { 'latest': { 'action': 'added', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': INSPECT_DATA_1['Digest'], 'old_digest': None, 'created': format_time(INSPECT_DATA_1['Created']), 'labels': INSPECT_DATA_1['Labels'], 'os': INSPECT_DATA_1['Os'], 'arch': INSPECT_DATA_1['Architecture'], }, 'stage': { 'action': 'removed', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'stage', 'digest': None, 'old_digest': INSPECT_DATA_2['Digest'], 'created': None, 'labels': {}, 'os': None, 'arch': None, } } } result = container.check_repos(CONF, old_data) inspect_tag.assert_called_once_with('example.com/repos/testrepo') assert result == { 'example.com/repos/testrepo': { 'latest': { 'action': 'unchanged', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': INSPECT_DATA_1['Digest'], 'old_digest': None, 'created': format_time(INSPECT_DATA_1['Created']), 'labels': INSPECT_DATA_1['Labels'], 'os': INSPECT_DATA_1['Os'], 'arch': INSPECT_DATA_1['Architecture'], }, } }
def test_check_repos_ghost(run): """ Test that a tag that appeared in the RepoTags list, but no longer exists, and isn't present in the data from the previous run, results in the correct output. """ run.side_effect = [ Mock(returncode=0, stdout=json.dumps(INSPECT_DATA_1)), Mock(returncode=1, stderr='FATA[0001] Error reading manifest stage in example.com/repos/testrepo:' 'manifest unknown: manifest unknown\n') ] old_data = { 'example.com/repos/testrepo': { 'latest': { 'action': 'added', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': INSPECT_DATA_1['Digest'], 'old_digest': None, 'created': format_time(INSPECT_DATA_1['Created']), 'labels': INSPECT_DATA_1['Labels'], 'os': INSPECT_DATA_1['Os'], 'arch': INSPECT_DATA_1['Architecture'], }, } } result = container.check_repos(CONF, old_data) assert run.call_count == 2 assert result == { 'example.com/repos/testrepo': { 'latest': { 'action': 'unchanged', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': INSPECT_DATA_1['Digest'], 'old_digest': None, 'created': format_time(INSPECT_DATA_1['Created']), 'labels': INSPECT_DATA_1['Labels'], 'os': INSPECT_DATA_1['Os'], 'arch': INSPECT_DATA_1['Architecture'], }, } }
def test_quay_error_unchanged(get): """ Test that a (temporary) error when querying the quay.io API leaves the data unchanged. """ get.return_value.raise_for_status.side_effect = RuntimeError('request error') old_data = { 'quay.io/repos/testrepo': { 'latest': { 'action': 'added', 'repo': 'quay.io/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': QUAY_API_DATA['tags'][0]['manifest_digest'], 'old_digest': None, 'created': format_ts(QUAY_API_DATA['tags'][0]['start_ts']), 'labels': {}, 'os': '', 'arch': '' } } } result = container.check_repos(CONF, old_data) get.assert_called_once_with( 'https://quay.io/api/v1/repository/repos/testrepo/tag/?onlyActiveTags=true&limit=100&page=1', headers={} ) assert result == { 'quay.io/repos/testrepo': { 'ignore': True, 'latest': { 'action': 'added', 'repo': 'quay.io/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': QUAY_API_DATA['tags'][0]['manifest_digest'], 'old_digest': None, 'created': format_ts(QUAY_API_DATA['tags'][0]['start_ts']), 'labels': {}, 'os': '', 'arch': '' } } }
def main(): args = get_args() if args.quiet: logging.basicConfig(level=logging.ERROR) else: logging.basicConfig(level=logging.INFO) conf = utils.load_config(args.config) data = utils.load_data(args.data) new_data = container.check_repos(conf, data) if args.verbose: pprint.pprint(new_data) try: messaging.send_container_updates(conf, new_data) except: log.error( 'Could not send all messages, container state will not be updated. ' 'May result in duplicate messages.') raise else: utils.save_data(args.data, new_data)
def test_check_repos_readded(inspect_tag): """ Test that a repo that is readded immediately after it was removed results in a "added" message, and not an "updated" message. """ old_data = { 'example.com/repos/testrepo': { 'latest': { 'action': 'removed', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': None, 'old_digest': 'a1b2c3', 'created': None, 'labels': {}, 'os': None, 'arch': None, } } } result = container.check_repos(CONF, old_data) inspect_tag.assert_called_once_with('example.com/repos/testrepo') assert result == { 'example.com/repos/testrepo': { 'latest': { 'action': 'added', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': INSPECT_DATA_1['Digest'], 'old_digest': None, 'created': format_time(INSPECT_DATA_1['Created']), 'labels': INSPECT_DATA_1['Labels'], 'os': INSPECT_DATA_1['Os'], 'arch': INSPECT_DATA_1['Architecture'], } } }
def test_check_repos_unchanged_ignore(inspect_tag): """ Test that the 'ignore' flag is correctly ignored. """ old_data = { 'example.com/repos/testrepo': { 'ignore': True, 'latest': { 'action': 'added', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': INSPECT_DATA_1['Digest'], 'old_digest': None, 'created': format_time(INSPECT_DATA_1['Created']), 'labels': INSPECT_DATA_1['Labels'], 'os': INSPECT_DATA_1['Os'], 'arch': INSPECT_DATA_1['Architecture'], } } } result = container.check_repos(CONF, old_data) inspect_tag.assert_called_once_with('example.com/repos/testrepo') assert result == { 'example.com/repos/testrepo': { 'latest': { 'action': 'unchanged', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': INSPECT_DATA_1['Digest'], 'old_digest': None, 'created': format_time(INSPECT_DATA_1['Created']), 'labels': INSPECT_DATA_1['Labels'], 'os': INSPECT_DATA_1['Os'], 'arch': INSPECT_DATA_1['Architecture'], } } }
def test_check_repos_added(inspect_tag): """ Test that a new repo results in the correct output. """ result = container.check_repos(CONF, {}) inspect_tag.assert_called_once_with('example.com/repos/testrepo') assert result == { 'example.com/repos/testrepo': { 'latest': { 'action': 'added', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': INSPECT_DATA_1['Digest'], 'old_digest': None, 'created': format_time(INSPECT_DATA_1['Created']), 'labels': INSPECT_DATA_1['Labels'], 'os': INSPECT_DATA_1['Os'], 'arch': INSPECT_DATA_1['Architecture'], } } }
def test_check_repos_removed_error(run): """ Test that a tag that throws an error after initial inspection results in the correct output. """ run.side_effect = [ Mock(returncode=0, stdout=json.dumps(INSPECT_DATA_1)), Mock(returncode=1, stderr='FATA[0001] Error reading manifest stage in example.com/repos/testrepo:' 'some other error\n') ] old_data = { 'example.com/repos/testrepo': { 'latest': { 'action': 'added', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': INSPECT_DATA_1['Digest'], 'old_digest': None, 'created': format_time(INSPECT_DATA_1['Created']), 'labels': INSPECT_DATA_1['Labels'], 'os': INSPECT_DATA_1['Os'], 'arch': INSPECT_DATA_1['Architecture'], }, 'stage': { 'action': 'added', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'stage', 'digest': INSPECT_DATA_2['Digest'], 'old_digest': None, 'created': format_time(INSPECT_DATA_2['Created']), 'labels': INSPECT_DATA_2['Labels'], 'os': INSPECT_DATA_2['Os'], 'arch': INSPECT_DATA_2['Architecture'], } } } result = container.check_repos(CONF, old_data) assert run.call_count == 2 assert result == { 'example.com/repos/testrepo': { 'latest': { 'action': 'unchanged', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'latest', 'digest': INSPECT_DATA_1['Digest'], 'old_digest': None, 'created': format_time(INSPECT_DATA_1['Created']), 'labels': INSPECT_DATA_1['Labels'], 'os': INSPECT_DATA_1['Os'], 'arch': INSPECT_DATA_1['Architecture'], }, 'stage': { 'action': 'removed', 'repo': 'example.com/repos/testrepo', 'reponame': 'testrepo', 'tag': 'stage', 'digest': None, 'old_digest': INSPECT_DATA_2['Digest'], 'created': None, 'labels': {}, 'os': None, 'arch': None, } } }