Commit 89c6e499 authored by Leigh Stoller's avatar Leigh Stoller

Use a mysql process lock to prevent concurrent creation of new projects

and groups on the geni path. This seems to happen more then I thought it
might.
parent 18988234
......@@ -44,6 +44,7 @@ use File::Temp qw(tempfile);
use POSIX qw(strftime);
use Date::Parse;
use Time::Local;
use emdb;
use GeniHRN;
use GeniResponse;
use EmulabConstants;
......@@ -321,6 +322,7 @@ sub GetHoldingProject($$;$)
{
my ($urn, $creator, $noexports) = @_;
my ($project, $group);
my $lock_urn;
$noexports = 0 if (!defined($noexports));
require Project;
......@@ -412,10 +414,20 @@ sub GetHoldingProject($$;$)
"Cannot form a valid local project name from $project_urn");
}
}
$lock_urn = DBQuoteSpecial($project_urn);
#
# See if the project exists.
#
# We need to lock for concurrent attempt to create this user.
# Lets use a DB process lock, named by the URN.
#
my $lock_result = DBQueryWarn("select GET_LOCK($lock_urn, 120)");
if (!$lock_result ||
!$lock_result->numrows) {
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not SQL lock for a long time");
}
$project = Project->LookupNonLocal($project_urn);
if (!defined($project)) {
#
......@@ -438,6 +450,7 @@ sub GetHoldingProject($$;$)
}
}
if (!defined($project_id)) {
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Could not create a unique project name for $project_urn");
}
......@@ -448,6 +461,7 @@ sub GetHoldingProject($$;$)
#
my ($fh, $filename) = tempfile(UNLINK => 0);
if (!defined($fh)) {
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
print STDERR "Could not create temp file for $project_id\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
......@@ -470,6 +484,7 @@ sub GetHoldingProject($$;$)
print $fh "</project>\n";
close($fh);
if (! chmod(0755, $filename)) {
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
print STDERR "Could not chmod $filename\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
......@@ -480,6 +495,7 @@ sub GetHoldingProject($$;$)
my $output = GeniUtil::ExecQuiet("$WAP $NEWPROJECT -l $filename");
if ($?) {
GeniUtil::FlipToGeniUser();
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
print STDERR $output;
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Internal error creating project description");
......@@ -490,12 +506,14 @@ sub GetHoldingProject($$;$)
my $ecode = $?;
GeniUtil::FlipToGeniUser();
if ($ecode) {
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
print STDERR $output;
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Internal error creating project");
}
$project = Project->LookupNonLocal($project_urn);
if (!defined($project)) {
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
print STDERR "Cannot lookup new project for $project_id\n";
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Internal error locating project after creation");
......@@ -508,6 +526,7 @@ sub GetHoldingProject($$;$)
#
if (defined($group_id)) {
if (!Group->ValidGID($group_id)) {
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
return GeniResponse->Create(GENIRESPONSE_BADARGS, undef,
"Invalid local group name $group_id");
}
......@@ -522,7 +541,8 @@ sub GetHoldingProject($$;$)
#
my ($fh, $filename) = tempfile(UNLINK => 0);
if (!defined($fh)) {
print STDERR "Could not create temp file for group $group_id\n";
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
print STDERR "Could not create temp file for $group_id\n";
return GeniResponse->Create(GENIRESPONSE_ERROR);
}
print $fh "<group>\n";
......@@ -542,6 +562,7 @@ sub GetHoldingProject($$;$)
close($fh);
my $output = GeniUtil::ExecQuiet("$NEWGROUP $filename");
if ($?) {
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
print STDERR $output;
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Internal error creating group");
......@@ -550,6 +571,7 @@ sub GetHoldingProject($$;$)
$group = $project->LookupGroup($group_id);
if (!defined($group)) {
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
print STDERR "Cannot lookup new group for $group_id\n";
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Internal error locating group after creation");
......@@ -570,6 +592,7 @@ sub GetHoldingProject($$;$)
if (!defined($project->LookupUser($creator->emulab_user()))) {
system("$MODGROUPS -a $pid:$pid:$trust $uid");
if ($?) {
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Internal error adding new user to local project");
}
......@@ -578,12 +601,14 @@ sub GetHoldingProject($$;$)
!defined($group->LookupUser($creator->emulab_user()))) {
system("$MODGROUPS -a $pid:$gid:$trust $uid");
if ($?) {
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Internal error adding new user to local group");
}
}
$ENV{'EMULAB_REAL_USER'} = $creator->emulab_user()->uid_idx();
}
DBQueryWarn("select RELEASE_LOCK($lock_urn)");
done:
if (!$noexports) {
if (defined($creator) && $creator->IsLocal()) {
......@@ -608,10 +633,14 @@ sub CreateLocalUser($$)
#
my $safe_email = GeniUtil::escapeshellarg($email);
my $safe_urn = GeniUtil::escapeshellarg($urn);
#
# The script is going to lock for concurrent creation, and return
# non-negative value if the user already exists.
#
FlipToElabMan();
system("$CREATEGENIUSER $safe_urn $safe_email");
if ($?) {
if ($? && $? >> 8 < 0) {
FlipToGeniUser();
return GeniResponse->Create(GENIRESPONSE_ERROR, undef,
"Could not create local user")
......
#!/usr/bin/perl -w
#
# Copyright (c) 2008-2015 University of Utah and the Flux Group.
# Copyright (c) 2008-2017 University of Utah and the Flux Group.
#
# {{{GENIPUBLIC-LICENSE
#
......@@ -41,6 +41,7 @@ use Data::Dumper;
sub usage()
{
print STDERR "Usage: $0 [-n] [-p project] <urn> <email>\n";
print STDERR "Usage: $0 -r <urn>\n";
exit(1);
}
my $optlist = "nrp:";
......@@ -53,9 +54,11 @@ my $TB = "@prefix@";
my $TBACCT = "$TB/sbin/tbacct";
my $MKUSERCERT = "$TB/sbin/mkusercert";
my $MODGROUPS = "$TB/sbin/modgroups";
my $WAP = "$TB/sbin/withadminprivs";
use lib '@prefix@/lib';
use emutil;
use emdb;
use User;
use Project;
use GeniCertificate;
......@@ -113,9 +116,22 @@ my $usr_uid;
fatal("Invalid email")
if (! User->ValidEmail($email));
#
# Must not be a user with same nonlocal ID.
# We need to lock for concurrent attempt to create this user.
# Lets use a DB process lock, named by the URN.
#
my $safe_urn = DBQuoteSpecial($urn);
my $lock_result = DBQueryWarn("select GET_LOCK($safe_urn, 90)");
if (!$lock_result ||
!$lock_result->numrows) {
fatal("Could not get SQL lock for a long time!");
}
if (User->LookupNonLocal($urn)) {
fatal("We already have a user with that nonlocal ID (urn)");
print "Already have a user with that nonlocal ID (urn)\n";
DBQueryWarn("select RELEASE_LOCK($safe_urn)");
# Caller looks for non-zero status.
exit(1);
}
#
......@@ -195,8 +211,9 @@ if ($?) {
$project->GetProjectGroup()->DeleteMemberShip($user)
if (defined($project));
# Make sure the entire account is deleted. Leave DB record on failure
# so that we can try again later.
if (system("$TBACCT -f del $usr_uid") == 0) {
# so that we can try again later. We are invoked as elabman, so we
# cap use wap here (required for -f option).
if (system("$WAP $TBACCT -f del $usr_uid") == 0) {
$user->Delete();
}
fatal("Could not instantiate user account!")
......@@ -216,6 +233,8 @@ if ($?) {
}
fatal("Could not create local SSL certificate");
}
# Do not really need to do this, all locks released on exit.
DBQueryWarn("select RELEASE_LOCK($safe_urn)");
exit(0);
#
......
......@@ -409,10 +409,14 @@ function CreateNonLocalUser($urn, $email)
$safe_urn = escapeshellarg($urn);
$safe_email = escapeshellarg($email);
#
# The script is going to lock for concurrent creation, and return
# non-negative value if the user already exists.
#
$retval = SUEXEC("elabman", $TBOPSPID,
"webcreategeniuser -p CloudLab $safe_urn $safe_email",
SUEXEC_ACTION_CONTINUE);
if ($retval)
if ($retval < 0)
return -1;
return 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment