Skip to content

Commit

Permalink
Merge pull request NixOS#1349 from NixOS/ca-no-new-col
Browse files Browse the repository at this point in the history
Allow building content-addressed derivations with hydra, minimally
  • Loading branch information
Ericson2314 committed Jan 26, 2024
2 parents 6ac4292 + b503280 commit 838648c
Show file tree
Hide file tree
Showing 12 changed files with 184 additions and 11 deletions.
7 changes: 7 additions & 0 deletions doc/manual/src/projects.md
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,10 @@ analogous:
| `String value` | `gitea_status_repo` | *Name of the `Git checkout` input* |
| `String value` | `gitea_http_url` | *Public URL of `gitea`*, optional |

Content-addressed derivations
-----------------------------

Hydra can to a certain extent use the [`ca-derivations` experimental Nix feature](https://github.com/NixOS/rfcs/pull/62).
To use it, make sure that the Nix version you use is at least as recent as the one used in hydra's flake.

Be warned that this support is still highly experimental, and anything beyond the basic functionality might be broken at that point.
29 changes: 29 additions & 0 deletions src/lib/Hydra/Controller/Root.pm
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use Net::Prometheus;
use Types::Standard qw/StrMatch/;

use constant NARINFO_REGEX => qr{^([a-z0-9]{32})\.narinfo$};
# e.g.: https://hydra.example.com/realisations/sha256:a62128132508a3a32eef651d6467695944763602f226ac630543e947d9feb140!out.doi
use constant REALISATIONS_REGEX => qr{^(sha256:[a-z0-9]{64}![a-z]+)\.doi$};

# Put this controller at top-level.
__PACKAGE__->config->{namespace} = '';
Expand Down Expand Up @@ -355,6 +357,33 @@ sub nix_cache_info :Path('nix-cache-info') :Args(0) {
}


sub realisations :Path('realisations') :Args(StrMatch[REALISATIONS_REGEX]) {
my ($self, $c, $realisation) = @_;

if (!isLocalStore) {
notFound($c, "There is no binary cache here.");
}

else {
my ($rawDrvOutput) = $realisation =~ REALISATIONS_REGEX;
my $rawRealisation = queryRawRealisation($rawDrvOutput);

if (!$rawRealisation) {
$c->response->status(404);
$c->response->content_type('text/plain');
$c->stash->{plain}->{data} = "does not exist\n";
$c->forward('Hydra::View::Plain');
setCacheHeaders($c, 60 * 60);
return;
}

$c->response->content_type('text/plain');
$c->stash->{plain}->{data} = $rawRealisation;
$c->forward('Hydra::View::Plain');
}
}


sub narinfo :Path :Args(StrMatch[NARINFO_REGEX]) {
my ($self, $c, $narinfo) = @_;

Expand Down
8 changes: 4 additions & 4 deletions src/lib/Hydra/Schema/Result/BuildOutputs.pm
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ __PACKAGE__->table("buildoutputs");
=head2 path
data_type: 'text'
is_nullable: 0
is_nullable: 1
=cut

Expand All @@ -59,7 +59,7 @@ __PACKAGE__->add_columns(
"name",
{ data_type => "text", is_nullable => 0 },
"path",
{ data_type => "text", is_nullable => 0 },
{ data_type => "text", is_nullable => 1 },
);

=head1 PRIMARY KEY
Expand Down Expand Up @@ -94,8 +94,8 @@ __PACKAGE__->belongs_to(
);


# Created by DBIx::Class::Schema::Loader v0.07049 @ 2021-08-26 12:02:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:gU+kZ6A0ISKpaXGRGve8mg
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2022-06-30 12:02:32
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Jsabm3YTcI7YvCuNdKP5Ng

my %hint = (
columns => [
Expand Down
8 changes: 4 additions & 4 deletions src/lib/Hydra/Schema/Result/BuildStepOutputs.pm
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ __PACKAGE__->table("buildstepoutputs");
=head2 path
data_type: 'text'
is_nullable: 0
is_nullable: 1
=cut

Expand All @@ -67,7 +67,7 @@ __PACKAGE__->add_columns(
"name",
{ data_type => "text", is_nullable => 0 },
"path",
{ data_type => "text", is_nullable => 0 },
{ data_type => "text", is_nullable => 1 },
);

=head1 PRIMARY KEY
Expand Down Expand Up @@ -119,8 +119,8 @@ __PACKAGE__->belongs_to(
);


# Created by DBIx::Class::Schema::Loader v0.07049 @ 2021-08-26 12:02:36
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:gxp8rOjpRVen4YbIjomHTw
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2022-06-30 12:02:32
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:Bad70CRTt7zb2GGuRoQ++Q


# You can replace this text with custom code or comments, and it will be preserved on regeneration
Expand Down
4 changes: 2 additions & 2 deletions src/sql/hydra.sql
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ create trigger BuildBumped after update on Builds for each row
create table BuildOutputs (
build integer not null,
name text not null,
path text not null,
path text,
primary key (build, name),
foreign key (build) references Builds(id) on delete cascade
);
Expand Down Expand Up @@ -303,7 +303,7 @@ create table BuildStepOutputs (
build integer not null,
stepnr integer not null,
name text not null,
path text not null,
path text,
primary key (build, stepnr, name),
foreign key (build) references Builds(id) on delete cascade,
foreign key (build, stepnr) references BuildSteps(build, stepnr) on delete cascade
Expand Down
4 changes: 4 additions & 0 deletions src/sql/upgrade-84.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- CA derivations do not have statically known output paths. The values
-- are only filled in after the build runs.
ALTER TABLE BuildStepOutputs ALTER COLUMN path DROP NOT NULL;
ALTER TABLE BuildOutputs ALTER COLUMN path DROP NOT NULL;
61 changes: 61 additions & 0 deletions t/content-addressed/basic.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use feature 'unicode_strings';
use strict;
use warnings;
use Setup;

my %ctx = test_init(
nix_config => qq|
experimental-features = ca-derivations
|,
);

require Hydra::Schema;
require Hydra::Model::DB;

use JSON::MaybeXS;

use HTTP::Request::Common;
use Test2::V0;
require Catalyst::Test;
Catalyst::Test->import('Hydra');

my $db = Hydra::Model::DB->new;
hydra_setup($db);

my $project = $db->resultset('Projects')->create({name => "tests", displayname => "", owner => "root"});

my $jobset = createBaseJobset("content-addressed", "content-addressed.nix", $ctx{jobsdir});

ok(evalSucceeds($jobset), "Evaluating jobs/content-addressed.nix should exit with return code 0");
is(nrQueuedBuildsForJobset($jobset), 5, "Evaluating jobs/content-addressed.nix should result in 4 builds");

for my $build (queuedBuildsForJobset($jobset)) {
ok(runBuild($build), "Build '".$build->job."' from jobs/content-addressed.nix should exit with code 0");
my $newbuild = $db->resultset('Builds')->find($build->id);
is($newbuild->finished, 1, "Build '".$build->job."' from jobs/content-addressed.nix should be finished.");
my $expected = $build->job eq "fails" ? 1 : $build->job =~ /with_failed/ ? 6 : 0;
is($newbuild->buildstatus, $expected, "Build '".$build->job."' from jobs/content-addressed.nix should have buildstatus $expected.");

my $response = request("/build/".$build->id);
ok($response->is_success, "The 'build' page for build '".$build->job."' should load properly");

if ($newbuild->buildstatus == 0) {
my $buildOutputs = $newbuild->buildoutputs;
for my $output ($newbuild->buildoutputs) {
# XXX: This hardcodes /nix/store/.
# It's fine because in practice the nix store for the tests will be of
# the form `/some/thing/nix/store/`, but it would be cleaner if there
# was a way to query Nix for its store dir?
like(
$output->path, qr|/nix/store/|,
"Output '".$output->name."' of build '".$build->job."' should be a valid store path"
);
}
}

}

isnt(<$ctx{deststoredir}/realisations/*>, "", "The destination store should have the realisations of the built derivations registered");

done_testing;

28 changes: 28 additions & 0 deletions t/content-addressed/without-experimental-feature.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use feature 'unicode_strings';
use strict;
use warnings;
use Setup;

my %ctx = test_init();

require Hydra::Schema;
require Hydra::Model::DB;

use JSON::MaybeXS;

use HTTP::Request::Common;
use Test2::V0;
require Catalyst::Test;
Catalyst::Test->import('Hydra');

my $db = Hydra::Model::DB->new;
hydra_setup($db);

my $project = $db->resultset('Projects')->create({name => "tests", displayname => "", owner => "root"});

my $jobset = createBaseJobset("content-addressed", "content-addressed.nix", $ctx{jobsdir});

ok(evalSucceeds($jobset), "Evaluating jobs/content-addressed.nix without the experimental feature should exit with return code 0");
is(nrQueuedBuildsForJobset($jobset), 0, "Evaluating jobs/content-addressed.nix without the experimental Nix feature should result in 0 build");

done_testing;
5 changes: 5 additions & 0 deletions t/jobs/config.nix.in
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ rec {
system = builtins.currentSystem;
PATH = path;
} // args);
mkContentAddressedDerivation = args: mkDerivation ({
__contentAddressed = true;
outputHashMode = "recursive";
outputHashAlgo = "sha256";
} // args);
}
35 changes: 35 additions & 0 deletions t/jobs/content-addressed.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
let cfg = import ./config.nix; in
rec {
empty_dir =
cfg.mkContentAddressedDerivation {
name = "empty-dir";
builder = ./empty-dir-builder.sh;
};

fails =
cfg.mkContentAddressedDerivation {
name = "fails";
builder = ./fail.sh;
};

succeed_with_failed =
cfg.mkContentAddressedDerivation {
name = "succeed-with-failed";
builder = ./succeed-with-failed.sh;
};

caDependingOnCA =
cfg.mkContentAddressedDerivation {
name = "ca-depending-on-ca";
builder = ./dir-with-file-builder.sh;
FOO = empty_dir;
};

nonCaDependingOnCA =
cfg.mkDerivation {
name = "non-ca-depending-on-ca";
builder = ./dir-with-file-builder.sh;
FOO = empty_dir;
};
}

4 changes: 4 additions & 0 deletions t/jobs/dir-with-file-builder.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#! /bin/sh

mkdir $out
echo foo > $out/a-file
2 changes: 1 addition & 1 deletion t/queue-runner/notifications.t
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ my $binarycachedir = File::Temp->newdir();

my $ctx = test_context(
nix_config => qq|
experimental-features = nix-command
experimental-features = nix-command ca-derivations
substituters = file://${binarycachedir}?trusted=1
|,
hydra_config => q|
Expand Down

0 comments on commit 838648c

Please sign in to comment.