def __do_test_error__(self, nr_errors, signal_error_threshold, expect_error_signal): ampel = self.__create_ampel__(signal_error_threshold) # requires a previous state - in this case all is OK before (green=True, yellow=False, red=False) ampel.on_update({ ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.HEALTHY) }) status = {("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.ERROR)} for i in range(nr_errors): ampel.signal.assert_called_once_with(red=False, yellow=False, green=True, flash=False) ampel.signal.reset_mock() ampel.on_update(status) if expect_error_signal: ampel.signal.assert_called_once_with(red=True, yellow=True, green=True, flash=False) else: ampel.signal.assert_called_once_with(red=False, yellow=False, green=True, flash=False)
def test_filter_ok_many(self): filter = NameFilter("job\.\w*") status = { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.HEALTHY), ("ci.sbb.ch", "job.b"): JobStatus(RequestStatus.NOT_FOUND) } self.assertEqual(status, filter.filter_status(status))
def test_2_success_1_not_found_is_green(self): self.__do_test__( { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.HEALTHY), ("ci.sbb.ch", "job.b"): JobStatus(RequestStatus.NOT_FOUND), ("ci.sbb.ch", "job.c"): JobStatus(RequestStatus.OK, Health.HEALTHY) }, red=False, yellow=False, green=True)
def test_2_success_1_error_is_all_on(self): self.__do_test__( { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.HEALTHY), ("ci.sbb.ch", "job.b"): JobStatus(RequestStatus.ERROR), ("ci.sbb.ch", "job.c"): JobStatus(RequestStatus.OK, Health.HEALTHY) }, red=True, yellow=True, green=True)
def test_is_buiding_error(self): self.__do_test__( { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.ERROR), ("ci.sbb.ch", "job.b"): JobStatus(RequestStatus.OK, Health.OTHER, False) }, red=True, yellow=True, green=True, flash=False)
def __status_from_color__(self, job): if "color" in job: color_status_building = job["color"].split( "_") if job["color"] else (None, ) if color_status_building[0] == "disabled": return JobStatus(request_status=RequestStatus.NOT_FOUND) elif color_status_building[0] in self.colors_to_result: return JobStatus( health=self.colors_to_result[color_status_building[0]], active=len(color_status_building) > 1 and color_status_building[1] == "anime") logger.warning('Missing attribute "color" in job description %s' % job) return JobStatus(health=Health.OTHER)
def test_success_failture__unstable_is_red(self): self.__do_test__( { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.HEALTHY), ("ci.sbb.ch", "job.b"): JobStatus(RequestStatus.OK, Health.SICK), ("ci.sbb.ch", "job.c"): JobStatus(RequestStatus.OK, Health.UNWELL) }, red=True, yellow=False, green=False)
def test_2_success_2_unstable_is_yellow(self): self.__do_test__( { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.HEALTHY), ("ci.sbb.ch", "job.b"): JobStatus(RequestStatus.OK, Health.UNWELL), ("ci.sbb.ch", "job.c"): JobStatus(RequestStatus.OK, Health.HEALTHY) }, red=False, yellow=True, green=False)
def test_filter_with_ampel_one_filtered(self): ampel = self.__create_ampel__( signal_error_threshold=1, build_filter_pattern="bla.*" ) # threshold is pointless here as no previosu status exists ampel.on_update({ ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.SICK), ("ci.sbb.ch", "bla.b"): JobStatus(RequestStatus.OK, Health.HEALTHY) }) ampel.signal.assert_called_once_with(red=False, yellow=False, green=True, flash=False)
def test_not_found_is_no_output(self): ampel = self.__create_ampel__(signal_error_threshold=0) ampel.on_update({ ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.NOT_FOUND) }) self.assertEqual(ampel.signal.call_count, 0)
def test_error_with_all(self): ampel = self.__create_ampel__(0) ampel.on_update({("ci.sbb.ch", 'all'): JobStatus(RequestStatus.ERROR)}) ampel.signal.assert_called_once_with(red=True, yellow=True, green=True, flash=False)
def test_error_no_previous_status_is_all_on(self): # first error (no previous status) is error self.__do_test__( {("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.ERROR)}, red=True, yellow=True, green=True)
def __convert_build__(self, job_name, jenkins_build_result): if "actions" in jenkins_build_result and len(jenkins_build_result["actions"]) > 0 and "causes" in \ jenkins_build_result["actions"][0] and len(jenkins_build_result["actions"][0]["causes"]) > 0: cause = jenkins_build_result["actions"][0]["causes"][0][ "shortDescription"] else: cause = None status = JobStatus( health=self.__convert_store_fill_job_result__( job_name, jenkins_build_result["result"]), active=jenkins_build_result["building"], timestamp=datetime.fromtimestamp( jenkins_build_result["timestamp"] / 1000.0), number=jenkins_build_result["number"], names=[ culprit["fullName"] for culprit in jenkins_build_result["culprits"] ] if "culprits" in jenkins_build_result else [], duration=jenkins_build_result["duration"] if "duration" in jenkins_build_result else None, fullDisplayName=jenkins_build_result["fullDisplayName"], url=jenkins_build_result["url"], builtOn=jenkins_build_result["builtOn"] if "builtOn" in jenkins_build_result else None, cause=cause) logger.debug("Converted Build result: %s", str(status)) return status
def __collect_alerts__(self): try: return self.__to_status__( self.__filter__(self.new_relic_client.open_alert_violations())) except HTTPError as e: # ignore... if (e.code == 404): # not found - its OK lets not crash logger.warning("No applications found in new relic: : %s" % e.msg) return {(self.name, "all"): JobStatus(RequestStatus.NOT_FOUND)} else: logger.exception("HTTP Error requesting status for job: %s" % e.msg) return {(self.name, "all"): JobStatus(RequestStatus.ERROR)} except URLError as e: logger.exception("URL Error requesting status for job %s" % e) return {(self.name, "all"): JobStatus(RequestStatus.ERROR)}
def test_success_is_green(self): self.__do_test__( { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.HEALTHY) }, red=False, yellow=False, green=True)
def test_filter_with_ampel(self): ampel = self.__create_ampel__( signal_error_threshold=1, build_filter_pattern="bla.*" ) # threshold is pointless here as no previosu status exists ampel.on_update({ ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.HEALTHY) }) self.assertFalse(ampel.signal.called)
def test_filter_nofilter(self): filter = NameFilter() status = { "another.a": { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.HEALTHY) } } self.assertEqual(status, filter.filter_status(status))
def __collect_health__(self): try: return {(self.name, app_name): self.__to_job_status__(app_health) for app_name, app_health in self.new_relic_client.applications_health().items()} except HTTPError as e: # ignore... if (e.code == 404): # not found - its OK lets not crash logger.warning("No applications found in new relic: : %s" % e.msg) return {(self.name, "all"): JobStatus(RequestStatus.NOT_FOUND)} else: logger.exception("HTTP Error requesting status for job: %s" % e.msg) return {(self.name, "all"): JobStatus(RequestStatus.ERROR)} except URLError as e: logger.exception("URL Error requesting status for job %s" % e) return {(self.name, "all"): JobStatus(RequestStatus.ERROR)}
def test_is_buiding_other(self): self.__do_test__( { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.OTHER, True) }, red=False, yellow=True, green=False, flash=True)
def test_is_buiding_unstable(self): self.__do_test__( { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.UNWELL, True) }, red=False, yellow=True, green=False, flash=True)
def test_is_buiding_failure(self): self.__do_test__( { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.SICK, True) }, red=True, yellow=False, green=False, flash=True)
def test_is_not_buiding_success(self): self.__do_test__( { ("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.HEALTHY, False) }, red=False, yellow=False, green=True, flash=False)
def __extract_job__status__(self, view): builds = {} for job in view["jobs"]: jobname = self.qualified_job_name(job["name"], job["url"]) status = None if "color" in job: color_status_building = job["color"].split("_") if job["color"] else (None,) if color_status_building[0] == "disabled": status = JobStatus(request_status=RequestStatus.NOT_FOUND) elif color_status_building[0] in self.colors_to_result: status = JobStatus( health= self.colors_to_result[color_status_building[0]], active = len(color_status_building) > 1 and color_status_building[1] == "anime") else: status = JobStatus(health=Health.OTHER) if status and "builds" in job: # requires depth 2 latest_build = self.__latest_build_in_view__(job) if "number" in latest_build: status.number = latest_build["number"] if "timestamp" in latest_build: status.timestamp = datetime.fromtimestamp(latest_build["timestamp"] / 1000) if "culprits" in latest_build: status.names = [culprit["fullName"] for culprit in latest_build["culprits"]] if status: builds[jobname] = status return builds
def collect_job(self, job_name): job_name, req_status, jenkins_build = self.__latest_build__(job_name) job_url = jenkins_build[ "url"] if jenkins_build and "url" in jenkins_build else None if req_status == RequestStatus.OK: return { self.qualified_job_name(job_name, job_url): self.__convert_build__(job_name, jenkins_build) } else: return { self.qualified_job_name(job_name, job_url): JobStatus(request_status=req_status) }
def __status_multibranch_job__(self, job): statusFromColor = self.status_from_color(job) if statusFromColor.request_status == RequestStatus.NOT_FOUND: return statusFromColor return JobStatus( health=statusFromColor.health, active=statusFromColor.active, timestamp=self.__get_timestamp_from_job__(job), number=self.__get_number_from_job__(job), names=self.__get_culprits_from_job__(job), duration=self.__get_duration_from_job__(job), fullDisplayName=self.__get_name_from_job__(job), url=job["url"] if "url" in job else None, builtOn=None, # not available on job cause=self.__get_cause_from_job__(job))
def __collect_view_recursive__(self, view_name, allready_visited): if view_name in allready_visited: # guard against infinite loops return {} allready_visited.add(view_name) view = self.__view__(view_name) if view: # add the builds to the existing ones (from recursion) builds = self.__extract_job__status__(view) if "views" in view: nested_views = self.__extract_nested_view_names__(view) for nested_view in nested_views: # recurse for all nested views builds.update( self.__collect_view_recursive__( nested_view, allready_visited)) return builds else: return { self.qualified_job_name(view_name, None): JobStatus(request_status=RequestStatus.ERROR) }
def __to_job_status__(self, alert): return JobStatus(request_status=RequestStatus.OK, health=self.__to_cimon_health__(alert["priority"]), timestamp=datetime.fromtimestamp(alert["opened_at"] / 1000.0), number=alert["id"])
def __to_job_status__(self, app_health): return JobStatus(request_status=RequestStatus.OK, health=self.__to_cimon_health__(app_health))
def test_failture_is_red(self): self.__do_test__( {("ci.sbb.ch", "job.a"): JobStatus(RequestStatus.OK, Health.SICK)}, red=True, yellow=False, green=False)
__author__ = 'florianseidl' import logging from cimon import JobStatus, RequestStatus, Health # for manual test purposes only, cycle through predefined output values logger = logging.getLogger(__name__) default_status = ( JobStatus(request_status=RequestStatus.OK, health=Health.SICK, active=False), JobStatus(request_status=RequestStatus.OK, health=Health.SICK, active=True), JobStatus(request_status=RequestStatus.OK, health=Health.UNWELL, active=False), JobStatus(request_status=RequestStatus.OK, health=Health.UNWELL, active=True), JobStatus(request_status=RequestStatus.OK, health=Health.HEALTHY, active=False), JobStatus(request_status=RequestStatus.OK, health=Health.HEALTHY, active=True), JobStatus(request_status=RequestStatus.OK, health=Health.OTHER, active=False), JobStatus(request_status=RequestStatus.OK,
def test_filter_filtered(self): filter = NameFilter("job\.\w*") status = {"another.a": JobStatus(RequestStatus.OK, Health.HEALTHY)} self.assertEqual({}, filter.filter_status(status))