def own_concourse_config(): if not _running_on_ci(): raise RuntimeError( 'Can only determine own concourse config if running on CI infrastructure' ) pipeline_metadata = get_pipeline_metadata() config_set = ctx().cfg_factory().cfg_set( pipeline_metadata.current_config_set_name) return config_set.concourse()
def own_running_build_url(concourse_cfg=None, ): if not _running_on_ci(): raise RuntimeError( 'Can only determine own build url if running on CI infrastructure') if concourse_cfg is None: concourse_cfg = own_concourse_config() pipeline_metadata = get_pipeline_metadata() own_build = find_own_running_build(concourse_cfg) return ConcourseApiRoutesBase.running_build_url( concourse_cfg.external_url(), pipeline_metadata, own_build.build_number())
def own_running_build_url(cfg_factory=None): if not _running_on_ci(): raise RuntimeError( 'Can only determine own build url if running on CI infrastructure') pipeline_metadata = get_pipeline_metadata() own_build = find_own_running_build(cfg_factory=cfg_factory) cc_cfg = _current_concourse_config() return ConcourseApiRoutesBase.running_build_url( cc_cfg.external_url(), pipeline_metadata, own_build.build_number(), )
def get_pipeline_metadata(): if not _running_on_ci(): raise RuntimeError('Pipeline-metadata is only available if running on CI infrastructure') current_cfg_set_name = check_env('CONCOURSE_CURRENT_CFG') team_name = check_env('CONCOURSE_CURRENT_TEAM') pipeline_name = check_env('PIPELINE_NAME') job_name = check_env('BUILD_JOB_NAME') return PipelineMetaData( pipeline_name=pipeline_name, job_name=job_name, current_config_set_name=current_cfg_set_name, team_name=team_name, )
def find_own_running_build(): ''' Determines the current build job running on concourse by relying on the "meta" contract ( see steps/meta), which prints a JSON document containing a UUID. By iterating through all current build jobs (considering running jobs only), and comparing the UUID read via file system and the UUID from build log output, it is possible to tell whether or not a given build job is the one from which this function was invoked. ''' if not _running_on_ci(): raise RuntimeError( 'Can only find own running build if running on CI infrastructure.') meta_dir = os.path.join(os.path.abspath(check_env('CC_ROOT_DIR')), concourse.model.traits.meta.DIR_NAME) meta_info_file = os.path.join( meta_dir, concourse.steps.meta.jobmetadata_filename, ) with open(meta_info_file, 'r') as f: metadata_json = json.load(f) build_job_uuid = metadata_json['uuid'] pipeline_metadata = get_pipeline_metadata() client = ccc.concourse.client_from_env( team_name=pipeline_metadata.team_name, ) # only consider limited amount of jobs to avoid large number of requests in case we do not # find ourself (assumption: there are only few running jobs in parallel at a given time) consider_builds = 20 builds = client.job_builds(pipeline_metadata.pipeline_name, pipeline_metadata.job_name) builds = [ build for build in builds if build.status() is concourse.client.model.BuildStatus.RUNNING ][:consider_builds] # avoid parsing too much output. usually, there will be only one line (our JSON output) # sometimes (new image version is retrieved), there will be a few lines more. for build in builds: build_events = build.events() build_plan = build.plan() meta_task_id = build_plan.task_id( concourse.model.traits.meta.META_STEP_NAME) uuid_line = None try: for line in reversed(list( build_events.iter_buildlog(meta_task_id))): if line.strip(): # last non empty line uuid_line = line break except StopIteration: pass uuid_json = json.loads(uuid_line) if uuid_json['uuid'] == build_job_uuid: return build else: raise RuntimeError('Could not determine own Concourse job.')
def _current_concourse_config(): if not _running_on_ci(): raise RuntimeError( 'Can only determine own concourse config if running on CI') return ctx().cfg_set().concourse()
def find_own_running_build(cfg_factory=None): ''' Determines the current build job running on concourse by relying on the "meta" contract ( see steps/meta), which prints a JSON document containing a UUID. By iterating through all current build jobs (considering running jobs only), and comparing the UUID read via file system and the UUID from build log output, it is possible to tell whether or not a given build job is the one from which this function was invoked. ''' if not _running_on_ci(): raise RuntimeError( 'Can only find own running build if running on CI infrastructure.') meta_info_file = meta_info_file_from_env() with open(meta_info_file, 'r') as f: metadata_json = json.load(f) build_job_uuid = metadata_json['uuid'] if cfg_factory: current_cfg_set_name = check_env('CONCOURSE_CURRENT_CFG') cfg_set = cfg_factory.cfg_set(current_cfg_set_name) else: cfg_set = None pipeline_metadata = get_pipeline_metadata() client = ccc.concourse.client_from_env( team_name=pipeline_metadata.team_name, cfg_set=cfg_set, ) # only consider limited amount of jobs to avoid large number of requests in case we do not # find ourself (assumption: there are only few running jobs in parallel at a given time) consider_builds = 20 builds = client.job_builds(pipeline_metadata.pipeline_name, pipeline_metadata.job_name) builds = [ build for build in builds if build.status() is concourse.client.model.BuildStatus.RUNNING ][:consider_builds] # avoid parsing too much output. usually, there will be only one line (our JSON output) # sometimes (new image version is retrieved), there will be a few lines more. for build in builds: build_events = build.events() build_plan = build.plan() meta_task_id = build_plan.task_id( concourse.model.traits.meta.META_STEP_NAME) # we expect output to only contain valid JSON meta_output = ''.join(build_events.iter_buildlog(meta_task_id)).strip() try: uuid_json = json.loads(meta_output) except json.decoder.JSONDecodeError: logger.error(f'Error when parsing {meta_output=}') continue # ignore - we might still find "our" job if uuid_json['uuid'] == build_job_uuid: return build else: raise RuntimeError('Could not determine own Concourse job.')