def _update_index_image_pull_spec( output_pull_spec, request_id, arches, from_index=None, overwrite_from_index=False, overwrite_from_index_token=None, resolved_prebuild_from_index=None, add_or_rm=False, ): """ Update the request with the modified index image. This function was created so that code didn't need to be duplicated for the ``add`` and ``rm`` request types. :param str output_pull_spec: pull spec of the index image generated by IIB :param int request_id: the ID of the IIB build request :param set arches: the set of arches that were built as part of this request :param str from_index: the pull specification of the container image containing the index that the index image build was based from. :param bool overwrite_from_index: if True, overwrite the input ``from_index`` with the built index image. :param str overwrite_from_index_token: the token used for overwriting the input ``from_index`` image. :param str resolved_prebuild_from_index: resolved index image before starting the build. :param bool add_or_rm: true if the request is an ``Add`` or ``Rm`` request. defaults to false :raises IIBError: if the manifest list couldn't be created and pushed """ conf = get_worker_config() if from_index and overwrite_from_index: _overwrite_from_index( request_id, output_pull_spec, from_index, resolved_prebuild_from_index, overwrite_from_index_token, ) index_image = from_index elif conf['iib_index_image_output_registry']: index_image = output_pull_spec.replace( conf['iib_registry'], conf['iib_index_image_output_registry'], 1) log.info( 'Changed the index_image pull specification from %s to %s', output_pull_spec, index_image, ) else: index_image = output_pull_spec payload = {'arches': list(arches), 'index_image': index_image} if add_or_rm: with set_registry_token(overwrite_from_index_token, index_image): index_image_resolved = get_resolved_image(index_image) payload['index_image_resolved'] = index_image_resolved update_request(request_id, payload, exc_msg='Failed setting the index image on the request')
def _update_index_image_build_state(request_id, prebuild_info): """ Update the build request state with pre-determined build information. :param int request_id: the ID of the IIB build request :param dict prebuild_info: the information relevant to the build operation. The key ``arches`` is required and must be set to the list of arches to build for. The key ``binary_image_resolved`` is required and must be set to the image digest pull spec of the binary image. The key ``bundle_mapping`` is optional. When provided, its value must be a dict mapping an operator to a list of bundle images. The key ``from_index_resolved`` is optional. When provided it must be set to the image digest pull spec of the from index image. """ arches_str = ', '.join(sorted(prebuild_info['arches'])) payload = { 'binary_image_resolved': prebuild_info['binary_image_resolved'], 'state': 'in_progress', 'state_reason': f'Building the index image for the following arches: {arches_str}', } bundle_mapping = prebuild_info.get('bundle_mapping') if bundle_mapping: payload['bundle_mapping'] = bundle_mapping from_index_resolved = prebuild_info.get('from_index_resolved') if from_index_resolved: payload['from_index_resolved'] = from_index_resolved exc_msg = 'Failed setting the resolved images on the request' update_request(request_id, payload, exc_msg)
def _finish_request_post_build( output_pull_spec, request_id, arches, from_index=None, overwrite_from_index=False, overwrite_from_index_token=None, ): """ Finish the request after the manifest list has been pushed. This function was created so that code didn't need to be duplicated for the ``add`` and ``rm`` request types. :param str output_pull_spec: pull spec of the index image generated by IIB :param int request_id: the ID of the IIB build request :param set arches: the set of arches that were built as part of this request :param str from_index: the pull specification of the container image containing the index that the index image build was based from. :param bool overwrite_from_index: if True, overwrite the input ``from_index`` with the built index image. :param str overwrite_from_index_token: the token used for overwriting the input ``from_index`` image. :raises IIBError: if the manifest list couldn't be created and pushed """ conf = get_worker_config() if from_index and overwrite_from_index: log.info( f'Ovewriting the index image {from_index} with {output_pull_spec}') index_image = from_index exc_msg = f'Failed to overwrite the input from_index container image of {index_image}' args = [f'docker://{output_pull_spec}', f'docker://{index_image}'] _skopeo_copy(*args, copy_all=True, dest_token=overwrite_from_index_token, exc_msg=exc_msg) elif conf['iib_index_image_output_registry']: index_image = output_pull_spec.replace( conf['iib_registry'], conf['iib_index_image_output_registry'], 1) log.info( 'Changed the index_image pull specification from %s to %s', output_pull_spec, index_image, ) else: index_image = output_pull_spec payload = { 'arches': list(arches), 'index_image': index_image, 'state': 'complete', 'state_reason': 'The request completed successfully', } update_request(request_id, payload, exc_msg='Failed setting the index image on the request')
def test_update_request(mock_session): mock_session.patch.return_value.ok = True mock_session.patch.return_value.json.return_value = '{"id": 3}' api_utils.update_request(3, {'index_image': 'index-image:latest'}) mock_session.patch.assert_called_once_with( 'http://iib-api:8080/api/v1/builds/3', json={'index_image': 'index-image:latest'}, timeout=30, )
def handle_regenerate_bundle_request(from_bundle_image, organization, request_id): """ Coordinate the work needed to regenerate the operator bundle image. :param str from_bundle_image: the pull specification of the bundle image to be regenerated. :param str organization: the name of the organization the bundle should be regenerated for. :param int request_id: the ID of the IIB build request :raises IIBError: if the regenerate bundle image build fails. """ _cleanup() set_request_state(request_id, 'in_progress', 'Resolving from_bundle_image') from_bundle_image_resolved = _get_resolved_image(from_bundle_image) arches = _get_image_arches(from_bundle_image_resolved) if not arches: raise IIBError( f'No arches were found in the resolved from_bundle_image {from_bundle_image_resolved}' ) arches_str = ', '.join(sorted(arches)) log.debug( 'Set to regenerate the bundle image for the following arches: %s', arches_str) payload = { 'from_bundle_image_resolved': from_bundle_image_resolved, 'state': 'in_progress', 'state_reason': f'Regenerating the bundle image for the following arches: {arches_str}', } exc_msg = 'Failed setting the resolved "from_bundle_image" on the request' update_request(request_id, payload, exc_msg=exc_msg) # Pull the from_bundle_image to ensure steps later on don't fail due to registry timeouts podman_pull(from_bundle_image_resolved) with tempfile.TemporaryDirectory(prefix='iib-') as temp_dir: manifests_path = os.path.join(temp_dir, 'manifests') _copy_files_from_image(from_bundle_image_resolved, '/manifests', manifests_path) metadata_path = os.path.join(temp_dir, 'metadata') _copy_files_from_image(from_bundle_image_resolved, '/metadata', metadata_path) labels = _adjust_operator_bundle(manifests_path, metadata_path, organization) with open(os.path.join(temp_dir, 'Dockerfile'), 'w') as dockerfile: dockerfile.write( textwrap.dedent(f"""\ FROM {from_bundle_image_resolved} COPY ./manifests /manifests COPY ./metadata /metadata """)) for name, value in labels.items(): dockerfile.write(f'LABEL {name}={value}\n') for arch in sorted(arches): _build_image(temp_dir, 'Dockerfile', request_id, arch) _push_image(request_id, arch) set_request_state(request_id, 'in_progress', 'Creating the manifest list') output_pull_spec = _create_and_push_manifest_list(request_id, arches) conf = get_worker_config() if conf['iib_index_image_output_registry']: old_output_pull_spec = output_pull_spec output_pull_spec = output_pull_spec.replace( conf['iib_registry'], conf['iib_index_image_output_registry'], 1) log.info( 'Changed the bundle_image pull specification from %s to %s', old_output_pull_spec, output_pull_spec, ) payload = { 'arches': list(arches), 'bundle_image': output_pull_spec, 'state': 'complete', 'state_reason': 'The request completed successfully', } update_request(request_id, payload, exc_msg='Failed setting the bundle image on the request')
def _prepare_request_for_build(binary_image, request_id, from_index=None, add_arches=None, bundles=None): """ Prepare the request for the index image build. All information that was retrieved and/or calculated for the next steps in the build are returned as a dictionary. This function was created so that code didn't need to be duplicated for the ``add`` and ``rm`` request types. :param str binary_image: the pull specification of the container image where the opm binary gets copied from. :param int request_id: the ID of the IIB build request :param str from_index: the pull specification of the container image containing the index that the index image build will be based from. :param list add_arches: the list of arches to build in addition to the arches ``from_index`` is currently built for; if ``from_index`` is ``None``, then this is used as the list of arches to build the index image for :param list bundles: the list of bundles to create the bundle mapping on the request :return: a dictionary with the keys: arches, binary_image_resolved, and from_index_resolved. :raises IIBError: if the container image resolution fails or the architectures couldn't be detected. """ if bundles is None: bundles = [] set_request_state(request_id, 'in_progress', 'Resolving the container images') if add_arches: arches = set(add_arches) else: arches = set() binary_image_resolved = _get_resolved_image(binary_image) binary_image_arches = _get_image_arches(binary_image_resolved) if from_index: from_index_resolved = _get_resolved_image(from_index) from_index_arches = _get_image_arches(from_index_resolved) arches = arches | from_index_arches else: from_index_resolved = None if not arches: raise IIBError('No arches were provided to build the index image') arches_str = ', '.join(sorted(arches)) log.debug('Set to build the index image for the following arches: %s', arches_str) if not arches.issubset(binary_image_arches): raise IIBError( 'The binary image is not available for the following arches: {}'. format(', '.join(sorted(arches - binary_image_arches)))) bundle_mapping = {} for bundle in bundles: operator = get_image_label( bundle, 'operators.operatorframework.io.bundle.package.v1') if operator: bundle_mapping.setdefault(operator, []).append(bundle) payload = { 'binary_image_resolved': binary_image_resolved, 'state': 'in_progress', 'state_reason': f'Building the index image for the following arches: {arches_str}', } if bundle_mapping: payload['bundle_mapping'] = bundle_mapping if from_index_resolved: payload['from_index_resolved'] = from_index_resolved exc_msg = 'Failed setting the resolved images on the request' update_request(request_id, payload, exc_msg) return { 'arches': arches, 'binary_image_resolved': binary_image_resolved, 'from_index_resolved': from_index_resolved, }
def test_update_request_not_ok(mock_session, exc_msg, expected): mock_session.patch.return_value.ok = False with pytest.raises(IIBError, match=expected): api_utils.update_request(3, {'index_image': 'index-image:latest'}, exc_msg=exc_msg)
def test_update_request_connection_failed(mock_session): mock_session.patch.side_effect = requests.ConnectionError() with pytest.raises(IIBError, match='The connection failed.+'): api_utils.update_request(3, {'index_image': 'index-image:latest'})