def _create_build_config_and_build(self, build_request): # TODO: test this method more thoroughly build_json = build_request.render() api_version = build_json['apiVersion'] if api_version != self.os_conf.get_openshift_api_version(): raise OsbsValidationException("BuildConfig template has incorrect apiVersion (%s)" % api_version) build_config_name = build_json['metadata']['name'] # check if a build already exists for this config; if so then raise running_builds = self._get_running_builds_for_build_config(build_config_name) rb_len = len(running_builds) if rb_len > 0: if rb_len == 1: rb = running_builds[0] msg = 'Build %s for %s in state %s, can\'t proceed.' % \ (rb.get_build_name(), build_config_name, rb.status) else: msg = self._panic_msg_for_more_running_builds(build_config_name, running_builds) raise OsbsException(msg) try: # see if there's already a build config existing_bc = self.os.get_build_config(build_config_name) except OsbsException: # doesn't exist existing_bc = None build = None if existing_bc is not None: utils.buildconfig_update(existing_bc, build_json) logger.debug('build config for %s already exists, updating...', build_config_name) self.os.update_build_config(build_config_name, json.dumps(existing_bc)) else: # if it doesn't exist, then create it logger.debug('build config for %s doesn\'t exist, creating...', build_config_name) bc = self.os.create_build_config(json.dumps(build_json)).json() # if there's an "ImageChangeTrigger" on the BuildConfig and "From" is of type # "ImageStreamTag", the build will be scheduled automatically # see https://github.com/projectatomic/osbs-client/issues/205 if build_request.is_auto_instantiated(): prev_version = bc['status']['lastVersion'] build_id = self.os.wait_for_new_build_config_instance(build_config_name, prev_version) build = BuildResponse(self.os.get_build(build_id).json()) if build is None: response = self.os.start_build(build_config_name) build = BuildResponse(response.json()) return build
def _create_build_config_and_build(self, build_request): build = None build_json = build_request.render() api_version = build_json['apiVersion'] if api_version != self.os_conf.get_openshift_api_version(): raise OsbsValidationException( 'BuildConfig template has incorrect apiVersion (%s)' % api_version) build_config_name = build_json['metadata']['name'] logger.debug('build config to be named "%s"', build_config_name) existing_bc = self._get_existing_build_config(build_json) if existing_bc is not None: self._verify_labels_match(build_json, existing_bc) # Existing build config may have a different name if matched by # git-repo-name and git-branch labels. Continue using existing # build config name. build_config_name = existing_bc['metadata']['name'] logger.debug('existing build config name to be used "%s"', build_config_name) self._verify_no_running_builds(build_config_name) utils.buildconfig_update(existing_bc, build_json) # Reset name change that may have occurred during # update above, since renaming is not supported. existing_bc['metadata']['name'] = build_config_name logger.debug('build config for %s already exists, updating...', build_config_name) self.os.update_build_config(build_config_name, json.dumps(existing_bc)) else: # if it doesn't exist, then create it logger.debug('build config for %s doesn\'t exist, creating...', build_config_name) bc = self.os.create_build_config(json.dumps(build_json)).json() # if there's an "ImageChangeTrigger" on the BuildConfig and "From" is of type # "ImageStreamTag", the build will be scheduled automatically # see https://github.com/projectatomic/osbs-client/issues/205 if build_request.is_auto_instantiated(): prev_version = bc['status']['lastVersion'] build_id = self.os.wait_for_new_build_config_instance( build_config_name, prev_version) build = BuildResponse(self.os.get_build(build_id).json()) if build is None: response = self.os.start_build(build_config_name) build = BuildResponse(response.json()) return build
def _create_build_config_and_build(self, build_request): build_json = build_request.render() api_version = build_json['apiVersion'] if api_version != self.os_conf.get_openshift_api_version(): raise OsbsValidationException('BuildConfig template has incorrect apiVersion (%s)' % api_version) build_config_name = build_json['metadata']['name'] logger.debug('build config to be named "%s"', build_config_name) existing_bc = self._get_existing_build_config(build_json) image_stream, image_stream_tag_name = \ self._get_image_stream_info_for_build_request(build_request) # Remove triggers in BuildConfig to avoid accidental # auto instance of Build. If defined, triggers will # be added to BuildConfig after ImageStreamTag object # is properly configured. triggers = build_json['spec'].pop('triggers', None) if existing_bc: build_config_name = existing_bc['metadata']['name'] existing_bc = self._update_build_config_when_exist(build_json) else: logger.debug("build config for %s doesn't exist, creating...", build_config_name) existing_bc = self.os.create_build_config(json.dumps(build_json)).json() if image_stream: changed_ist = self.ensure_image_stream_tag(image_stream, image_stream_tag_name, scheduled=True) logger.debug('Changed parent ImageStreamTag? %s', changed_ist) if triggers: existing_bc = self._update_build_config_with_triggers(build_json, triggers) if image_stream and triggers: prev_version = existing_bc['status']['lastVersion'] build_id = self.os.wait_for_new_build_config_instance( build_config_name, prev_version) build = BuildResponse(self.os.get_build(build_id).json()) else: response = self.os.start_build(build_config_name) build = BuildResponse(response.json()) return build
def cancel_build(self, build_id): response = self.get_build(build_id) br = BuildResponse(response.json()) br.cancelled = True url = self._build_url(OCP_BUILD_API_V1, "builds/%s/" % build_id) return self._put(url, data=json.dumps(br.json), headers={"Content-Type": "application/json"})
def _create_scratch_build(self, build_request): logger.debug(build_request) build_json = build_request.render() build_json['kind'] = 'Build' build_json['spec']['serviceAccount'] = 'builder' build_json['metadata']['labels']['scratch'] = 'true' if build_request.low_priority_node_selector: build_json['spec'][ 'nodeSelector'] = build_request.low_priority_node_selector builder_img = build_json['spec']['strategy']['customStrategy']['from'] kind = builder_img['kind'] if kind == 'ImageStreamTag': # Only BuildConfigs get to specify an ImageStreamTag. When # creating Builds directly we need to specify a # DockerImage. response = self.get_image_stream_tag(builder_img['name']) ref = response.json()['image']['dockerImageReference'] builder_img['kind'] = 'DockerImage' builder_img['name'] = ref output_image_name = build_json['spec']['output']['to']['name'] # Reuse random string and timestamp values. build_config_name = 'scratch-%s-%s' % tuple( output_image_name.rsplit('-', 2)[-2:]) logger.debug('starting scratch build %s', build_config_name) build_json['metadata']['name'] = build_config_name return BuildResponse(self.os.create_build(build_json).json())
def list_builds(self, field_selector=None, koji_task_id=None, running=None, labels=None): """ List builds with matching fields :param field_selector: str, field selector for Builds :param koji_task_id: str, only list builds for Koji Task ID :return: BuildResponse list """ if running: running_fs = ",".join(["status!={status}".format(status=status.capitalize()) for status in BUILD_FINISHED_STATES]) if not field_selector: field_selector = running_fs else: field_selector = ','.join([field_selector, running_fs]) response = self.os.list_builds(field_selector=field_selector, koji_task_id=koji_task_id, labels=labels) serialized_response = response.json() build_list = [] for build in serialized_response["items"]: build_list.append(BuildResponse(build)) return build_list
def _create_build_directly(self, build_request, unique=None): logger.debug(build_request) build_json = build_request.render() build_json['kind'] = 'Build' build_json['spec']['serviceAccount'] = 'builder' builder_img = build_json['spec']['strategy']['customStrategy']['from'] kind = builder_img['kind'] if kind == 'ImageStreamTag': # Only BuildConfigs get to specify an ImageStreamTag. When # creating Builds directly we need to specify a # DockerImage. response = self.get_image_stream_tag(builder_img['name']) ref = response.json()['image']['dockerImageReference'] builder_img['kind'] = 'DockerImage' builder_img['name'] = ref if unique: unique_labels = {} for u in unique: unique_labels[u] = build_json['metadata']['labels'][u] running_builds = self.list_builds(running=True, labels=unique_labels) if running_builds: raise RuntimeError('Matching build(s) already running: {0}' .format(', '.join(x.get_build_name() for x in running_builds))) return BuildResponse(self.os.create_build(build_json).json())
def _create_scratch_build(self, build_request): logger.debug(build_request) build_json = build_request.render() build_json['kind'] = 'Build' if 'spec' not in build_json.keys(): build_json['spec'] = {} build_json['spec']['serviceAccount'] = 'builder' build_json['metadata']['labels']['scratch'] = 'true' if build_request.low_priority_node_selector: build_json['spec'][ 'nodeSelector'] = build_request.low_priority_node_selector builder_img = build_json['spec']['strategy']['customStrategy']['from'] kind = builder_img['kind'] if kind == 'ImageStreamTag': # Only BuildConfigs get to specify an ImageStreamTag. When # creating Builds directly we need to specify a # DockerImage. response = self.get_image_stream_tag(builder_img['name']) ref = response.json()['image']['dockerImageReference'] builder_img['kind'] = 'DockerImage' builder_img['name'] = ref build_config_name = 'scratch-%s' % datetime.datetime.now().strftime( '%Y%m%d%H%M%S') logger.debug('starting scratch build %s', build_config_name) build_json['metadata']['name'] = build_config_name return BuildResponse(self.os.create_build(build_json).json())
def cmd_list_builds(args, osbs): kwargs = {} if args.running: kwargs['running'] = args.running if args.from_json: with open(args.from_json) as fp: builds = [BuildResponse(build) for build in json.load(fp)] else: builds = osbs.list_builds(**kwargs) if args.output == 'json': json_output = [] for build in builds: json_output.append(build.json) print_json_nicely(json_output) elif args.output == 'text': if args.columns: cols_to_display = args.columns.split(",") else: cols_to_display = CLI_LIST_BUILDS_DEFAULT_COLS data = [{ "base_image": "BASE IMAGE NAME", "base_image_id": "BASE IMAGE ID", "commit": "COMMIT", "image": "IMAGE NAME", "unique_image": "UNIQUE IMAGE NAME", "image_id": "IMAGE ID", "koji_build_id": "KOJI BUILD ID", "name": "BUILD ID", "status": "STATUS", "time_created": "TIME CREATED", }] for build in sorted(builds, key=lambda x: x.get_time_created_in_seconds()): unique_image = build.get_image_tag() try: image = strip_registry_from_image( build.get_repositories()["primary"][0]) except (TypeError, KeyError, IndexError): image = "" # "" or unique_image? failed builds don't have that ^ if args.FILTER and args.FILTER not in image: continue if args.running and not build.is_in_progress(): continue b = { "base_image": build.get_base_image_name() or '', "base_image_id": build.get_base_image_id() or '', "commit": build.get_commit_id(), "image": image, "unique_image": unique_image, "image_id": build.get_image_id() or '', "koji_build_id": build.get_koji_build_id() or '', "name": build.get_build_name(), "status": build.status, "time_created": build.get_time_created(), } data.append(b) tp = TablePrinter(data, cols_to_display) tp.render()
def cancel_build(self, build_id): response = self.get_build(build_id) br = BuildResponse(response.json()) br.status = BUILD_CANCELLED_STATE url = self._build_url("builds/%s/" % build_id) return self._put(url, data=json.dumps(br.json), headers={"Content-Type": "application/json"})
def __init__(self, v1_image_id=None, unset_annotations=False): annotations = {'meta': 'test'} if v1_image_id: annotations['v1-image-id'] = v1_image_id if unset_annotations: annotations = None self.build = BuildResponse({'metadata': {'annotations': annotations}})
def _get_running_builds_for_build_config(self, build_config_id): all_builds_for_bc = self.os.list_builds(build_config_id=build_config_id).json()['items'] running = [] for b in all_builds_for_bc: br = BuildResponse(b) if br.is_pending() or br.is_running(): running.append(br) return running
def test_get_koji_build_id(self): koji_build_id = '123' build_response = BuildResponse({ 'metadata': { 'labels': { 'koji-build-id': koji_build_id, }, }, }) assert build_response.get_koji_build_id() == koji_build_id
def test_build_cancel(self): build_response = BuildResponse({'status': {'phase': 'Running'}}) assert not build_response.cancelled build_response.cancelled = True assert build_response.cancelled assert 'cancelled' in build_response.json['status'] assert build_response.json['status']['cancelled'] build_response.cancelled = False assert not build_response.cancelled assert 'cancelled' in build_response.json['status'] assert not build_response.json['status'].get('cancelled')
def get_docker_build_logs(self, build_id, decode_logs=True, build_json=None): """ get logs provided by "docker build" :param build_id: str :param decode_logs: bool, docker by default output logs in simple json structure: { "stream": "line" } if this arg is set to True, it decodes logs to human readable form :param build_json: dict, to save one get-build query :return: str """ if not build_json: build = self.os.get_build(build_id) build_response = BuildResponse(build.json()) else: build_response = BuildResponse(build_json) if build_response.is_finished(): logs = build_response.get_logs(decode_logs=decode_logs) return logs logger.warning("build haven't finished yet")
def create_build_from_buildrequest(self, build_request): """ render provided build_request and submit build from it :param build_request: instance of build.build_request.BuildRequest :return: instance of build.build_response.BuildResponse """ build_request.set_openshift_required_version(self.os_conf.get_openshift_required_version()) build = build_request.render() response = self.os.create_build(json.dumps(build)) build_response = BuildResponse(response.json()) return build_response
def _create_scratch_build(self, build_request): logger.debug(build_request) build_json = build_request.render() build_json['kind'] = 'Build' if 'spec' not in build_json.keys(): build_json['spec'] = {} build_json['spec']['serviceAccount'] = 'builder' build_json['metadata']['labels']['scratch'] = 'true' build_config_name = 'scratch-%s' % datetime.datetime.now().strftime( '%Y%m%d%H%M%S') logger.debug('starting scratch build %s', build_config_name) build_json['metadata']['name'] = build_config_name return BuildResponse(self.os.create_build(build_json).json())
def make_build_response(name, status, annotations=None, labels=None): build_response = { 'metadata': { 'name': name, 'annotations': annotations or {}, 'labels': labels or {}, }, 'status': { 'phase': status } } return BuildResponse(build_response)
def test_state_checkers(self): build_response = BuildResponse({ 'status': { 'phase': 'Complete' } }) build_response.status = 'complete' assert build_response.is_finished() assert build_response.is_succeeded() assert not build_response.is_failed() assert not build_response.is_cancelled() assert not build_response.is_running() assert not build_response.is_pending() assert not build_response.is_in_progress() build_response.status = 'failed' assert build_response.is_failed() assert build_response.is_finished() assert not build_response.is_succeeded() assert not build_response.is_cancelled() assert not build_response.is_running() assert not build_response.is_pending() assert not build_response.is_in_progress() build_response.status = 'cancelled' assert build_response.is_cancelled() assert build_response.is_failed() assert build_response.is_finished() assert not build_response.is_succeeded() assert not build_response.is_running() assert not build_response.is_pending() assert not build_response.is_in_progress() build_response.status = 'running' assert build_response.is_running() assert build_response.is_in_progress() assert not build_response.is_cancelled() assert not build_response.is_failed() assert not build_response.is_finished() assert not build_response.is_succeeded() assert not build_response.is_pending() build_response.status = 'pending' assert build_response.is_pending() assert build_response.is_in_progress() assert not build_response.is_running() assert not build_response.is_cancelled() assert not build_response.is_failed() assert not build_response.is_finished() assert not build_response.is_succeeded()
def list_builds(self, field_selector=None): """ List builds with matching fields :param field_selector: str, field selector for Builds :return: BuildResponse list """ response = self.os.list_builds(field_selector=field_selector) serialized_response = response.json() build_list = [] for build in serialized_response["items"]: build_list.append(BuildResponse(build)) return build_list
def list_builds(self, field_selector=None, koji_task_id=None): """ List builds with matching fields :param field_selector: str, field selector for Builds :param koji_task_id: str, only list builds for Koji Task ID :return: BuildResponse list """ response = self.os.list_builds(field_selector=field_selector, koji_task_id=koji_task_id) serialized_response = response.json() build_list = [] for build in serialized_response["items"]: build_list.append(BuildResponse(build)) return build_list
def test_error_message(self, plugin, message, expected_error_message): plugins_metadata = json.dumps({ 'errors': { plugin: message, }, }) if not plugin: plugins_metadata = '' build_response = BuildResponse({ 'metadata': { 'annotations': { 'plugins-metadata': plugins_metadata } } }) assert build_response.get_error_message() == expected_error_message
def test_get_logs(self): msg = "This is an error message" error = json.dumps({ 'errorDetail': { 'code': 1, 'message': msg, 'error': msg, }, }) build_response = BuildResponse({ 'metadata': { 'annotations': { 'logs': error, }, }, }) assert msg in build_response.get_logs()
def _create_scratch_build(self, build_request): logger.debug(build_request) build_json = build_request.render() build_json['kind'] = 'Build' build_json['spec']['serviceAccount'] = 'builder' build_json['metadata']['labels']['scratch'] = 'true' builder_img = build_json['spec']['strategy']['customStrategy']['from'] kind = builder_img['kind'] if kind == 'ImageStreamTag': # Only BuildConfigs get to specify an ImageStreamTag. When # creating Builds directly we need to specify a # DockerImage. response = self.get_image_stream_tag(builder_img['name']) ref = response.json()['image']['dockerImageReference'] builder_img['kind'] = 'DockerImage' builder_img['name'] = ref return BuildResponse(self.os.create_build(build_json).json())
def logs(self, build_id, follow=False, build_json=None, wait_if_missing=False): """ provide logs from build :param build_id: str :param follow: bool, fetch logs as they come? :param build_json: dict, to save one get-build query :param wait_if_missing: bool, if build doesn't exist, wait :return: None, str or iterator """ # does build exist? try: build_json = build_json or self.get_build(build_id).json() except OsbsResponseException as ex: if ex.status_code == 404: if not wait_if_missing: raise OsbsException("Build '%s' doesn't exist." % build_id) else: raise if follow or wait_if_missing: build_json = self.wait_for_build_to_get_scheduled(build_id) br = BuildResponse(build_json) # When build is in new or pending state, openshift responds with 500 if br.is_pending(): return buildlogs_url = self._build_url("builds/%s/log/" % build_id, follow=(1 if follow else 0)) response = self._get(buildlogs_url, stream=follow, headers={'Connection': 'close'}) check_response(response) if follow: return response.iter_lines() return response.content
def test_error_message(self, osbs, pod, plugin, message, expected_error_message): class MockOsbsPod: def __init__(self, error): self.error = error def get_pod_for_build(self, build_id): return self def get_failure_reason(self): if self.error: return self.error raise OsbsException if pod or osbs: mock_pod = MockOsbsPod(pod) else: mock_pod = None if plugin: plugins_metadata = json.dumps({ 'errors': { plugin: message, }, }) build = { 'metadata': { 'annotations': { 'plugins-metadata': plugins_metadata, }, }, } else: build = { 'metadata': { 'annotations': {}, }, } build_response = BuildResponse(build, mock_pod) assert build_response.get_error_message() == expected_error_message
def cancel_build(self, build_id): response = self.os.cancel_build(build_id) build_response = BuildResponse(response.json()) return build_response
def wait_for_build_to_get_scheduled(self, build_id): response = self.os.wait_for_build_to_get_scheduled(build_id) build_response = BuildResponse(response) return build_response
def wait_for_build_to_finish(self, build_id): response = self.os.wait_for_build_to_finish(build_id) build_response = BuildResponse(response) return build_response
def get_build(self, build_id): response = self.os.get_build(build_id) build_response = BuildResponse(response.json()) return build_response