hydra-notify: move BuildFinished processing to an Event
[?]
Aug 12, 2021, 4:03 PM
T3OHZDYPABJBSN2G2BMQ47RL5PI5WVMAYQJVPT6TCPEFVGRLPEFQCDependencies
- [2]
OEQQ6CVTSet notificationpendingsince for dependent builds - [3]
QESASHY7Merge pull request #995 from DeterminateSystems/declarative-jobsets-plugin - [4]
LQEYBAELhydra-notify: move StepFinished processing to an Event - [5]
3AKZKWCRRunCommand: Test - [6]
FCTX433OAdd buildStarted plugin hook - [7]
GE7LFZHQhydra-notify: move buildFinished query in to the function impl - [8]
PXTSKX4GAdd buildQueued plugin hook - [9]
7VHPMFAGUse /usr/bin/env to find perl - [10]
NTEDD7T4Provide a plugin hook for when build steps finish - [11]
4ZCUCACYhydra-notify: Fix processing notifications - [12]
TNS4QKM4Declarative jobsets: move event handling to a plugin - [13]
P4SME2BCAbstract over postgres' LISTEN/NOTIFY - [14]
FN3QFV6Vhydra-notify: Create a helper for running each plugin on an event - [15]
AWPYSGP6hydra-notify: Don't do an unnecessary fetch of Jobsets - [16]
CQZQE32VImprove handling of Perl's block eval errors - [17]
32KJOERMTurn hydra-notify into a daemon - [18]
IE2PRAQUhydra-queue-runner: Send build notifications - [19]
EKHD4I44Event: init structure and parse existing messages - [20]
6WRGCITDEnable declarative projects. - [21]
GQGQEMMAEvent.pm: add a new_event helper to parse and construct an Event - [22]
OPSWSU4Lhydra-notify: move BuildStarted processing to an Event
Change contents
- replacement in src/lib/Hydra/Event/BuildFinished.pm at line 21
my ($self, $build_id, $dependencies) = @_;return bless { "build_id" => $build_id, "dependencies" => $dependencies }, $self;my ($self, $build_id, $dependent_ids) = @_;return bless {"build_id" => $build_id,"dependent_ids" => $dependent_ids,"build" => undef,"dependents" => [],}, $self;}sub load {my ($self, $db) = @_;if (!defined($self->{"build"})) {$self->{"build"} = $db->resultset('Builds')->find($self->{"build_id"})or die "build $self->{'build_id'} does not exist\n";foreach my $id (@{$self->{"dependent_ids"}}) {my $dep = $db->resultset('Builds')->find($id)or die "dependent build $id does not exist\n";push @{$self->{"dependents"}}, $dep;}}}sub execute {my ($self, $db, $plugin) = @_;$self->load($db);$plugin->buildFinished($self->{"build"}, $self->{"dependents"});# Mark the build and all dependents as having their notifications "finished".## Otherwise, the dependent builds will remain with notificationpendingsince set# until hydra-notify is started, as buildFinished is never emitted for them.foreach my $b ($self->{"build"}, @{$self->{"dependents"}}) {if ($b->finished && defined($b->notificationpendingsince)) {$b->update({ notificationpendingsince => undef })}}return 1; - edit in src/script/hydra-notify at line 5
use Hydra::Event::BuildFinished;use Hydra::Helper::AddBuilds;use Hydra::Helper::Nix; - edit in src/script/hydra-notify at line 10
use Hydra::Helper::Nix;use Hydra::Helper::AddBuilds; - edit in src/script/hydra-notify at line 47
sub buildFinished {my ($buildId, @deps) = @_; - edit in src/script/hydra-notify at line 48[5.32]→[5.32:135](∅→∅),[5.5892]→[5.1973:1974](∅→∅),[5.1823]→[5.3581:3601](∅→∅),[5.1974]→[5.3581:3601](∅→∅),[5.5892]→[5.3581:3601](∅→∅),[5.3581]→[5.3581:3601](∅→∅),[5.3601]→[5.1975:2004](∅→∅),[5.2004]→[5.3630:3771](∅→∅),[5.3630]→[5.3630:3771](∅→∅),[5.3771]→[5.3823:3860](∅→∅),[5.3860]→[5.858:1033](∅→∅),[5.1033]→[5.4000:4016](∅→∅),[5.4000]→[5.4000:4016](∅→∅),[5.4016]→[5.2005:2006](∅→∅),[5.2006]→[2.0:416](∅→∅),[2.416]→[5.4016:4018](∅→∅),[5.2065]→[5.4016:4018](∅→∅),[5.4016]→[5.4016:4018](∅→∅),[5.2128]→[5.473:474](∅→∅),[5.473]→[5.473:474](∅→∅)
my $build = $db->resultset('Builds')->find($buildId)or die "build $buildId does not exist\n";my @dependents;foreach my $id (@deps) {my $dep = $db->resultset('Builds')->find($id)or die "build $id does not exist\n";push @dependents, $dep;}foreach my $plugin (@plugins) {eval {$plugin->buildFinished($build, [@dependents]);1;} or do {print STDERR "error with $plugin->buildFinished: $@\n";}}# We have to iterate through all dependents as well, and if they are finished# to mark their notificationpendingsince.# Otherwise, the dependent builds will remain with notificationpendingsince set# until hydra-notify is started, as buildFinished is never emitted for them.foreach my $b ($build, @dependents) {$b->update({ notificationpendingsince => undef }) if $b->finished;}} - replacement in src/script/hydra-notify at line 52
my $buildId = $build->id;print STDERR "sending notifications for build ${\$buildId}...\n";buildFinished($build->id);print STDERR "sending notifications for build $build->id...\n";my $event = Hydra::Event::BuildFinished->new($build->id);runPluginsForEvent($event); - edit in src/script/hydra-notify at line 68
#print STDERR "got '$channelName' from $pid: $payload\n";my @payload = split /\t/, $payload; - replacement in src/script/hydra-notify at line 70[5.235]→[4.741:828](∅→∅),[4.828]→[5.451:585](∅→∅),[5.286]→[5.451:585](∅→∅),[5.585]→[5.334:439](∅→∅),[5.334]→[5.334:439](∅→∅),[5.439]→[5.168:233](∅→∅),[5.755]→[5.755:769](∅→∅)
if ($channelName eq "build_started" || $channelName eq "step_finished" ) {my $event = Hydra::Event::new_event($channelName, $message->{"payload"});runPluginsForEvent($event);} elsif ($channelName eq "build_finished") {my $buildId = int($payload[0]);buildFinished($buildId, @payload[1..$#payload]);}my $event = Hydra::Event::new_event($channelName, $message->{"payload"});runPluginsForEvent($event); - file addition: BuildFinished.t[5.587]
use strict;use Setup;my %ctx = test_init();require Hydra::Schema;require Hydra::Model::DB;use Hydra::Event;use Hydra::Event::BuildFinished;use Test2::V0;use Test2::Tools::Exception;use Test2::Tools::Mock qw(mock_obj);my $db = Hydra::Model::DB->new;hydra_setup($db);subtest "Parsing" => sub {like(dies { Hydra::Event::parse_payload("build_finished", "") },qr/at least one argument/,"empty payload");like(dies { Hydra::Event::parse_payload("build_finished", "abc123") },qr/should be integers/,"build ID should be an integer");like(dies { Hydra::Event::parse_payload("build_finished", "123\tabc123") },qr/should be integers/,"dependent ID should be an integer");is(Hydra::Event::parse_payload("build_finished", "123"),Hydra::Event::BuildFinished->new(123, []),"no dependent builds");is(Hydra::Event::parse_payload("build_finished", "123\t456"),Hydra::Event::BuildFinished->new(123, [456]),"one dependent build");is(Hydra::Event::parse_payload("build_finished", "123\t456\t789\t012\t345"),Hydra::Event::BuildFinished->new(123, [456, 789, 12, 345]),"four dependent builds");};my $project = $db->resultset('Projects')->create({name => "tests", displayname => "", owner => "root"});my $jobset = createBaseJobset("basic", "basic.nix", $ctx{jobsdir});ok(evalSucceeds($jobset), "Evaluating jobs/basic.nix should exit with return code 0");is(nrQueuedBuildsForJobset($jobset), 3, "Evaluating jobs/basic.nix should result in 3 builds");subtest "load" => sub {my ($build, $dependent_a, $dependent_b) = $db->resultset('Builds')->search({ },{ limit => 3 })->all;my $event = Hydra::Event::BuildFinished->new($build->id, [$dependent_a->id, $dependent_b->id]);$event->load($db);is($event->{"build"}->id, $build->id, "The build record matches.");is($event->{"dependents"}[0]->id, $dependent_a->id, "The dependent_a record matches.");is($event->{"dependents"}[1]->id, $dependent_b->id, "The dependent_b record matches.");# Create a fake "plugin" with a buildFinished sub, the sub sets this# global passedBuild and passedDependents variables for verifying.my $passedBuild;my $passedDependents;my $plugin = {};my $mock = mock_obj $plugin => (add => ["buildFinished" => sub {my ($self, $build, $dependents) = @_;$passedBuild = $build;$passedDependents = $dependents;}]);$event->execute($db, $plugin);is($passedBuild->id, $build->id, "The plugin's buildFinished hook is called with a matching build");is($passedDependents->[0]->id, $dependent_a->id, "The plugin's buildFinished hook is called with a matching dependent_a");is($passedDependents->[1]->id, $dependent_b->id, "The plugin's buildFinished hook is called with a matching dependent_b");};done_testing; - edit in t/Event.t at line 3
use Hydra::Event::BuildFinished; - edit in t/Event.t at line 12
};subtest "Payload type: build_finished" => sub {like(dies { Hydra::Event::parse_payload("build_finished", "") },qr/at least one argument/,"empty payload");like(dies { Hydra::Event::parse_payload("build_finished", "abc123") },qr/should be integers/,"build ID should be an integer");like(dies { Hydra::Event::parse_payload("build_finished", "123\tabc123") },qr/should be integers/,"dependent ID should be an integer");is(Hydra::Event::parse_payload("build_finished", "123"),Hydra::Event::BuildFinished->new(123, []),"no dependent builds");is(Hydra::Event::parse_payload("build_finished", "123\t456"),Hydra::Event::BuildFinished->new(123, [456]),"one dependent build");is(Hydra::Event::parse_payload("build_finished", "123\t456\t789\t012\t345"),Hydra::Event::BuildFinished->new(123, [456, 789, 12, 345]),"four dependent builds"); - edit in t/Event.t at line 14