mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-09-26 09:44:03 +00:00
88441f6ec3
A build tree can live on an NFS re-export. The kernel refuses locks on one -- "Clients are not allowed to get file locks or delegations from a reexport server" -- so every flock() there answers errno 524, and a build that takes one dies before it starts. buildrpms.pl's per-target lock and BuildUtils.pm's take_build_lock, which builddebs.pl calls for the Ubuntu core build, are both atomic mkdir claims now. Each records its owner and names it when it refuses. A directory is not released by a filehandle closing, which is how both locks were freed before. buildrpms.pl releases from END, and again in abort_builds because that handler re-raises the signal with DEFAULT and END blocks do not run then -- a killed build would otherwise strand the lock for every later one. BuildUtils returns a small object whose DESTROY releases it, preserving the caller's "hold the returned value" contract. Both releases are guarded by owning pid: both scripts fork, and the flock they replace could not be released by a child. builddebs_lock.t closed the returned value to prove the lock is released, which is "Not a GLOB reference" against the new contract. It now lets the value go out of scope. What it asserts is unchanged: a second build of the same checkout is refused, and the next one succeeds once the first releases. Signed-off-by: Daniel Hilst <392820+dhilst@users.noreply.github.com>
55 lines
2.3 KiB
Perl
55 lines
2.3 KiB
Perl
#!/usr/bin/env perl
|
|
# The deb build lock (VersatusHPC/xcat-core#52).
|
|
#
|
|
# builddebs.pl builds in-place in its own checkout -- it rewrites debian/changelog and
|
|
# debian/control and runs dpkg-buildpackage inside the package directories -- so two
|
|
# builds of the SAME checkout would corrupt each other and must fail fast, while two
|
|
# builds of DIFFERENT checkouts share nothing and must run concurrently. The historic
|
|
# host-global lock got that backwards and made the devel and stable CD lanes collide.
|
|
#
|
|
# This drives the real lock. The lock is a function, so it is called directly.
|
|
use strict;
|
|
use warnings;
|
|
|
|
use File::Temp qw(tempdir);
|
|
use FindBin;
|
|
use lib "$FindBin::Bin/../lib";
|
|
use lib "$FindBin::Bin/../../build-utils/lib";
|
|
use Test::More;
|
|
|
|
use XCAT::BuildUtils qw(lock_id_for take_build_lock);
|
|
|
|
my $lockdir = tempdir(CLEANUP => 1);
|
|
|
|
is( lock_id_for('/opt/builds/devel/xcat-core'),
|
|
lock_id_for('/opt/builds/devel/xcat-core'),
|
|
'one checkout always maps to one lock id' );
|
|
isnt( lock_id_for('/opt/builds/devel/xcat-core'),
|
|
lock_id_for('/opt/builds/stable/xcat-core'),
|
|
'two checkouts map to different lock ids' );
|
|
like( lock_id_for('/any/path'), qr/\A[0-9a-f]{12}\z/,
|
|
'the id is filesystem-safe, so it can name a file' );
|
|
is( length lock_id_for(''), 12, 'an empty path still yields an id rather than dying' );
|
|
|
|
# Same checkout: the second build must be refused.
|
|
my $devel = '/opt/builds/devel/xcat-core';
|
|
my $first = take_build_lock($devel, $lockdir);
|
|
ok( $first, 'the first build of a checkout takes the lock' );
|
|
|
|
my $second = eval { take_build_lock($devel, $lockdir) };
|
|
ok( !$second, 'a second build of the SAME checkout is refused' );
|
|
like( $@, qr/already holds/, 'and says which checkout is already building' );
|
|
|
|
# Different checkout: must not be blocked by the first.
|
|
my $stable = eval { take_build_lock('/opt/builds/stable/xcat-core', $lockdir) };
|
|
ok( $stable, 'a build of a DIFFERENT checkout runs concurrently' );
|
|
|
|
# Releasing lets the next build in. The lock is a directory now, not an flock on a filehandle --
|
|
# an NFS re-export refuses locks outright (errno 524) -- so it is freed when the returned object
|
|
# goes out of scope, not when a handle is closed.
|
|
undef $first;
|
|
my $again = eval { take_build_lock($devel, $lockdir) };
|
|
ok( $again, 'the lock is released when the returned value goes out of scope' );
|
|
|
|
done_testing();
|