def create_policy_from_artifact_and_teardown(request, pytestconfig): """ Implicitly tests ADD and DELETE operations for policies """ _logger.info("Loading Policy Bundle JSON from Artifact") with open( pytestconfig.rootdir + '/tests/functional/artifacts/bundle-with-all-rules-2020-08-20.json' ) as file: policy_bundle_all_rules = json.load(file) resp = http_post(['policies'], policy_bundle_all_rules, config=request.param) if resp.code != 200: raise RequestFailedError(resp.url, resp.code, resp.body) policy_id = resp.body.get('policyId') def delete_policies(): del_resp = http_del(['policies', policy_id], config=request.param) if del_resp.code != 200: raise RequestFailedError(del_resp.url, del_resp.code, del_resp.body) request.addfinalizer(delete_policies) return policy_bundle_all_rules, policy_id, request.param
def add_alpine_latest_image(request): """ Note: the test_subscriptions depends on this bit...because a subscription won't exist if there is no image added. For now, leave this as session scoped (we can make the subscription test create it's own images later) TODO: decouple test_subscriptions from this """ resp = http_post(['images'], {'tag': 'alpine:latest'}, config=request.param) if resp.code != 200: raise RequestFailedError(resp.url, resp.code, resp.body) image_id = get_image_id(resp) def remove_image_by_id(): remove_resp = http_del(['images', 'by_id', image_id], query={'force': True}, config=request.param) if remove_resp.code != 200: if not does_ft_account_exist(): # Because this is a session fixture, can't guarantee the order it runs against the account cleanup # Therefore, I've observed this finalizer running after the account is deleted. It's not the end of # the world, shouldn't be a failed test. If I make this fixture autouse=True, it has been generating an # extra matrix of tests which is worse than just letting the finalizer skip _logger.info( "{} account does not exist, ignoring for teardown".format( FT_ACCOUNT)) return raise RequestFailedError(remove_resp.url, remove_resp.code, remove_resp.body) request.addfinalizer(remove_image_by_id) return resp, request.param
def add_and_teardown_registry(request): registry_info = get_registry_info() registry_payload = { 'registry': registry_info['service_name'], 'registry_name': 'localhost', 'registry_pass': registry_info['pass'], 'registry_type': 'docker_v2', 'registry_user': registry_info['user'], 'registry_verify': False } _logger.info("Adding Registry. APIConf={}".format( str(request.param.__name__))) add_registry_resp = http_post(['registries'], registry_payload, config=request.param) if add_registry_resp.code != 200: raise RequestFailedError(add_registry_resp.url, add_registry_resp.code, add_registry_resp.body) def remove_registry(): _logger.info("Removing Registry. APIConf={}".format( str(request.param.__name__))) remove_resp = http_del( ['registries', quote(registry_info['service_name'])], config=request.param) if remove_resp.code != 200: raise RequestFailedError( remove_resp.url, remove_resp.code, '' if not hasattr(remove_resp, 'body') else remove_resp.body) request.addfinalizer(remove_registry) return add_registry_resp, request.param
def test_system_feeds_sync(self, api_conf): """ Should run fairly close to last to ensure test performance """ resp = http_post(['system', 'feeds'], None, {'sync': True}, config=api_conf) assert resp == APIResponse(200)
def test_add_user(self, api_conf): create_resp = http_post(['accounts', FT_ACCOUNT, 'users'], { 'username': '******', 'password': '******' }, config=api_conf) assert create_resp == APIResponse(200) delete_ft_account_user('creation_test', api_conf)
def create_ft_account_user(username, password, api_conf: callable): create_resp = http_post(['accounts', FT_ACCOUNT, 'users'], { 'username': username, 'password': password }, config=api_conf) if create_resp.code != 200: raise RequestFailedError(create_resp.url, create_resp.code, create_resp.body)
def test_add_credential(self, api_conf): """ Do an add-in-place (i.e. do not change the password as it is depended on throughout the other tests) """ resp = http_post(['user', 'credentials'], { 'type': 'password', 'value': api_conf()['ANCHORE_API_PASS'] }, config=api_conf) assert resp == APIResponse(200)
def add_subscription(api_conf: callable): added_subscription = None resp = http_post(['subscriptions'], ALPINE_LATEST_SUBSCRIPTION, config=api_conf) if resp.code == 500 and resp.body.get( 'message') == 'subscription already exists in DB': # Already exists resp = http_get(['subscriptions'], config=api_conf) subscription_list = resp.body for subscription in subscription_list: if subscription.get( 'subscription_type') == 'tag_update' and subscription.get( 'subscription_key') == 'docker.io/alpine:latest': added_subscription = subscription break elif resp.code != 200: raise RequestFailedError(resp.url, resp.code, resp.body) else: added_subscription = resp.body[0] return added_subscription
def test_add_repository(self, api_conf): resp = http_post(['repositories'], None, query={'repository': 'docker.io/alpine'}, config=api_conf) assert resp == APIResponse(200)
def create_functional_test_account_with_teardown(request): _logger = logging.getLogger('conftest') """ This fixture implicitly tests get_by_account_name, create, update state, and delete operations, but essentially, creates a functional_test account with a user ('ft_user' unless overridden by environment variables), and then deletes this account (blocking until deletion is complete) at the end of the test session """ def disable_and_delete_functional_test_account(): """ This method wil dynamically, and in a blocking fashion, handle account deletion, which requires that the functional_test account be disabled before deletion. If the functional_test account is currently enabled, it will disable and then delete the account, waiting for the deletion to complete. If the functional_test account is already disabled, it will delete the account, and wait for the deletion to complete. If the functional_test account is currently awaiting deletion, it will wait for the deletion to complete. If the functional_test account is not found, it will exit. """ def await_account_deletion(): """ This method is helpful for awaiting account deletion of the functional_test account, with a timeout governed by DELETE_ACCOUNT_TIMEOUT_SEC. It awaits in 5 second intervals. """ start_time_sec = time.time() result = 200 while result != 404: time.sleep(5) ft_get_account_resp = http_get(['accounts', FT_ACCOUNT]) _logger.info( "Waiting for functional_test account to fully delete. Time Elapsed={}sec" .format(int(time.time() - start_time_sec))) if not (ft_get_account_resp.code == 200 or ft_get_account_resp.code == 404): _logger.error(ft_get_account_resp) raise RequestFailedError(ft_get_account_resp.url, ft_get_account_resp.code, ft_get_account_resp.body) if time.time() - start_time_sec >= DELETE_ACCOUNT_TIMEOUT_SEC: raise TimeoutError( 'Timed out waiting for functional_test account to delete' ) result = ft_get_account_resp.code ft_account_resp = http_get(['accounts', FT_ACCOUNT]) if ft_account_resp.code == 404: _logger.info('functional_test account not found') return state = ft_account_resp.body.get('state') if state == 'enabled': _logger.info( 'functional_test account found, and enabled. Disabling') disable_account_resp = http_put(['accounts', FT_ACCOUNT, 'state'], {'state': 'disabled'}) if disable_account_resp.code != 200: raise RequestFailedError(disable_account_resp.url, disable_account_resp.code, disable_account_resp.body) elif state == 'deleting': _logger.info( 'functional_test account found, but is currently being deleted' ) await_account_deletion() return _logger.info('Deleting functional_test account') delete_resp = http_del(['accounts', FT_ACCOUNT]) if not (delete_resp.code == 200 or delete_resp.code == 404): raise RequestFailedError(delete_resp.url, delete_resp.code, delete_resp.body) await_account_deletion() # Delete the account if it exists already for some reason (sanity check) disable_and_delete_functional_test_account() _logger.info("Creating functional_test account") create_resp = http_post(['accounts'], { 'name': FT_ACCOUNT, 'email': '*****@*****.**' }) if create_resp.code != 200: raise RequestFailedError(create_resp.url, create_resp.code, create_resp.body) ft_user = get_ft_user() _logger.info("Creating functional_test user: {}".format( ft_user['username'])) create_user_resp = http_post(['accounts', FT_ACCOUNT, 'users'], ft_user) if create_user_resp.code != 200: raise RequestFailedError(create_user_resp.url, create_user_resp.code, create_user_resp.body) # # Need to reach out to rbac-manager to add the user to the full-control role # _logger.info("Giving user full control permissions of account") # # def get_rbac_api_conf(): # rbac_api_conf = copy.copy(get_api_conf()) # rbac_base_url = 'http://localhost:8229' if not os.getenv('ANCHORE_TEST_RBAC_BASE_URL') \ # else os.getenv('ANCHORE_TEST_RBAC_BASE_URL') # rbac_api_conf['ANCHORE_BASE_URL'] = rbac_base_url # return rbac_api_conf # # rbac_resp = http_post(['roles', 'full-control', 'members'], # {'username': ft_user['username'], 'for_account': FT_ACCOUNT}, # config=get_rbac_api_conf) # if rbac_resp.code != 200: # raise RequestFailedError(rbac_resp.url, rbac_resp.code, rbac_resp.body) request.addfinalizer(disable_and_delete_functional_test_account) return ft_user
def create_and_teardown_archive_rule(request): """ In order to interact with the archives API, a rule must be added first, which depends on there being an image added as well: 1. Add node:latest image (this isn't currently depended upon in other tests) 2. Add Archive Rule Note: This appears to only work for the root user ATM, so don't run w/ ft_user """ _logger.info("Adding alpine:edge Image for analysis") add_image_resp = http_post(['images'], {'tag': 'alpine:edge'}, config=request.param) if add_image_resp.code != 200: raise RequestFailedError(add_image_resp.url, add_image_resp.code, add_image_resp.body) wait_for_image_to_analyze(get_image_id(add_image_resp), request.param) archive_rule_json = { "analysis_age_days": 0, "created_at": "2020-08-25T17:15:16.865Z", "last_updated": "2020-08-25T17:15:16.865Z", "selector": { "registry": "docker.io", "repository": "alpine", "tag": "edge" }, "system_global": True, "tag_versions_newer": 0, "transition": "archive" } _logger.info('Adding Archive Rule') archive_rule_resp = http_post(['archives', 'rules'], archive_rule_json, config=request.param) if archive_rule_resp.code != 200: raise RequestFailedError(archive_rule_resp.url, archive_rule_resp.code, archive_rule_resp.body) archive_resp = http_post(['archives', 'images'], [get_image_digest(add_image_resp)], config=request.param) if archive_resp.code != 200: raise RequestFailedError(archive_resp.url, archive_resp.code, archive_resp.body) def teardown(): _logger.info('Removing alpine:edge image from anchore') remove_image_resp = http_del( ['images', 'by_id', get_image_id(add_image_resp)], query={'force': True}) if remove_image_resp.code != 200: raise RequestFailedError(remove_image_resp.url, remove_image_resp.code, remove_image_resp.body) _logger.info('Removing Archive Rule: rule_id={}'.format( archive_rule_resp.body['rule_id'])) remove_rule_resp = http_del( ['archives', 'rules', archive_rule_resp.body['rule_id']]) if remove_rule_resp.code != 200: raise RequestFailedError(remove_rule_resp.url, remove_rule_resp.code, remove_rule_resp.body) delete_archive_image_resp = http_del( ['archives', 'images', get_image_digest(add_image_resp)], config=request.param) if delete_archive_image_resp.code != 200: raise RequestFailedError(delete_archive_image_resp.url, delete_archive_image_resp.code, delete_archive_image_resp.body) request.addfinalizer(teardown) return add_image_resp, archive_rule_resp, archive_resp, request.param