From 51670cf6508803ab92654686cc4ce3dd7f833d27 Mon Sep 17 00:00:00 2001 From: Nick LaMuro Date: Mon, 13 Jan 2020 18:18:03 -0600 Subject: [PATCH] Add TravisBranchMonitor Worker that is in charge of monitoring Travis for build_failures, and will create BuildFailure records and send messages to gitter as needed. --- app/workers/travis_branch_monitor.rb | 94 ++++++++++++++++++++++ spec/workers/travis_branch_monitor_spec.rb | 69 ++++++++++++++++ 2 files changed, 163 insertions(+) create mode 100644 app/workers/travis_branch_monitor.rb create mode 100644 spec/workers/travis_branch_monitor_spec.rb diff --git a/app/workers/travis_branch_monitor.rb b/app/workers/travis_branch_monitor.rb new file mode 100644 index 00000000..8a0f3a98 --- /dev/null +++ b/app/workers/travis_branch_monitor.rb @@ -0,0 +1,94 @@ +require 'travis' + +class TravisBranchMonitor + include Sidekiq::Worker + sidekiq_options :queue => :miq_bot, :retry => false + + include Sidetiq::Schedulable + recurrence { hourly.minute_of_hour(0, 15, 30, 45) } + + include SidekiqWorkerMixin + + class << self + private + + # For this class, sometimes the repo needs to be mapped to a specific + # gitter room, so a hash is required. + # + # This override allows for doing this in the config + # + # travis_branch_monitor: + # included_repos: + # ManageIQ/manageiq-ui-classic: ManageIQ/ui + # ManageIQ/manageiq-gems-pending: ManageIQ/core + # ManageIQ/manageiq: + # ManageIQ/miq_bot: + # + # Which you are allowed to leave the value empty, and the key will be used + # where appropriate (not used in this class). + # + # The result from the above for this method will then be: + # + # [ + # [ + # "ManageIQ/manageiq-ui-classic", + # "ManageIQ/manageiq-gems-pending", + # "ManageIQ/manageiq", + # "ManageIQ/miq_bot" + # ], + # [] + # ] + # + def included_and_excluded_repos + super # just used for error handling... + + [ + settings.included_repos.try(:to_h).try(:stringify_keys).try(:keys), + settings.excluded_repos.try(:to_h).try(:stringify_keys).try(:keys) + ] + end + end + + def perform + if !first_unique_worker? + logger.info("#{self.class} is already running, skipping") + else + process_repos + end + end + + def process_repos + enabled_repos.each do |repo| + process_repo(repo) + end + end + + def process_repo(repo) + repo.regular_branch_names.each do |branch_record| + process_branch(repo, branch_record) + end + end + + def process_branch(repo, branch_record) + # If we already have a failure record, call notify with that record + return branch_record.notify_of_failure if branch_record.previously_failing? + + # otherwise, check if any builds exist with a failures, and if so, update + # the branch_record to add the `travis_build_failure_id`. + v3_client = TravisV3Client.new(:repo => Travis::Repository.find(repo.name)) + branch_builds = v3_client.repo_branch_builds(branch_record.name) + + if branch_builds.first.failed? + first_failure = find_first_recent_failure(branch_builds) + branch_record.update(:travis_build_failure_id => first_failure.id) + + branch_record.notify_of_failure + end + end + + private + + def find_first_recent_failure(builds) + builds.take_while(&:failed?).last + end +end diff --git a/spec/workers/travis_branch_monitor_spec.rb b/spec/workers/travis_branch_monitor_spec.rb new file mode 100644 index 00000000..bf41b985 --- /dev/null +++ b/spec/workers/travis_branch_monitor_spec.rb @@ -0,0 +1,69 @@ +describe TravisBranchMonitor do + include IncludedReposConfigMethods + + describe ".included_and_excluded_repos (private)" do + it "builds the list from a hash of only keys" do + stub_settings included_repos_keys_only + expected = %w[ManageIQ/manageiq ManageIQ/miq_bot] + + expect(described_class.send(:included_and_excluded_repos)).to eq([expected, nil]) + end + + it "builds the list from a hash of keys with values" do + stub_settings included_repos_keys_and_values + expected = %w[ManageIQ/manageiq-ui-classic ManageIQ/manageiq-gems-pending] + + expect(described_class.send(:included_and_excluded_repos)).to eq([expected, nil]) + end + + it "builds the list from a mixed hash with keys and some values" do + stub_settings included_repos_mixed_keys_with_some_values + expected = %w[ + ManageIQ/manageiq-ui-classic + ManageIQ/manageiq-gems-pending + ManageIQ/manageiq + ManageIQ/miq_bot + ] + + expect(described_class.send(:included_and_excluded_repos)).to eq([expected, nil]) + end + end + + describe "#find_first_recent_failure (private)" do + def passed(build_id) + build = Travis::Client::Build.new(nil, build_id) + allow(build).to receive(:inspect_info).and_return("Foo/foo##{build_id}") + build.tap { |b| b.update_attributes(:state => "passed") } + end + + def failed(build_id) + build = Travis::Client::Build.new(nil, build_id) + allow(build).to receive(:inspect_info).and_return("Foo/foo##{build_id}") + build.tap { |b| b.update_attributes(:state => "failed") } + end + + it "returns earliest failure" do + earliest_failure = failed(2) + builds = [ + failed(4), + failed(3), + earliest_failure, + passed(1) + ] + + expect(subject.send(:find_first_recent_failure, builds)).to eq(earliest_failure) + end + + it "returns nil if the first build has passed" do + builds = [ + passed(5), + failed(4), + failed(3), + failed(2), + passed(1) + ] + + expect(subject.send(:find_first_recent_failure, builds)).to eq(nil) + end + end +end