hydra-notify: move BuildFinished processing to an Event

[?]
Aug 12, 2021, 4:03 PM
T3OHZDYPABJBSN2G2BMQ47RL5PI5WVMAYQJVPT6TCPEFVGRLPEFQC

Dependencies

  • [2] OEQQ6CVT Set notificationpendingsince for dependent builds
  • [3] QESASHY7 Merge pull request #995 from DeterminateSystems/declarative-jobsets-plugin
  • [4] LQEYBAEL hydra-notify: move StepFinished processing to an Event
  • [5] 6WRGCITD Enable declarative projects.
  • [6] NTEDD7T4 Provide a plugin hook for when build steps finish
  • [7] 4ZCUCACY hydra-notify: Fix processing notifications
  • [8] CQZQE32V Improve handling of Perl's block eval errors
  • [9] TNS4QKM4 Declarative jobsets: move event handling to a plugin
  • [10] EKHD4I44 Event: init structure and parse existing messages
  • [11] FN3QFV6V hydra-notify: Create a helper for running each plugin on an event
  • [12] OPSWSU4L hydra-notify: move BuildStarted processing to an Event
  • [13] PXTSKX4G Add buildQueued plugin hook
  • [14] 32KJOERM Turn hydra-notify into a daemon
  • [15] IE2PRAQU hydra-queue-runner: Send build notifications
  • [16] 7VHPMFAG Use /usr/bin/env to find perl
  • [17] 3AKZKWCR RunCommand: Test
  • [18] GE7LFZHQ hydra-notify: move buildFinished query in to the function impl
  • [19] AWPYSGP6 hydra-notify: Don't do an unnecessary fetch of Jobsets
  • [20] GQGQEMMA Event.pm: add a new_event helper to parse and construct an Event
  • [21] P4SME2BC Abstract over postgres' LISTEN/NOTIFY
  • [22] FCTX433O Add buildStarted plugin hook

Change contents

  • replacement in src/lib/Hydra/Event/BuildFinished.pm at line 21
    [5.563][5.563:696]()
    my ($self, $build_id, $dependencies) = @_;
    return bless { "build_id" => $build_id, "dependencies" => $dependencies }, $self;
    [5.563]
    [5.696]
    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
    [5.3053]
    [5.3053]
    use Hydra::Event::BuildFinished;
    use Hydra::Helper::AddBuilds;
    use Hydra::Helper::Nix;
  • edit in src/script/hydra-notify at line 10
    [5.1752][5.3072:3096](),[5.3072][5.3072:3096](),[5.3128][5.5636:5666]()
    use Hydra::Helper::Nix;
    use Hydra::Helper::AddBuilds;
  • edit in src/script/hydra-notify at line 47
    [5.1743][5.1743:1744](),[5.3822][5.1922:1942](),[5.1942][5.0:31]()
    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
    [5.2466][5.2466:2566](),[5.2566][5.136:167]()
    my $buildId = $build->id;
    print STDERR "sending notifications for build ${\$buildId}...\n";
    buildFinished($build->id);
    [5.2466]
    [5.2593]
    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
    [5.2149][5.108:219](),[5.108][5.108:219]()
    #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]);
    }
    [5.235]
    [5.1202]
    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
    [5.3001][5.3001:3034]()
    use Hydra::Event::BuildFinished;
  • edit in t/Event.t at line 12
    [5.463][5.463:466](),[5.3478][5.3478:3479](),[5.4960][5.4960:6009]()
    };
    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
    [5.6013][5.6013:6014]()