Commit 149e10da authored by Leigh B Stoller's avatar Leigh B Stoller
Browse files

Clean up and reorg a few things, add some help popovers and text.

The control flow is kinda confusing, cause of the desire to hold off on
converting the geni-lib script (reducing the time we are updating from the
origin repo in the first please wait modal). Not really happy with how it
turned out.

I also need to change the code that does the initial fetch of a new repo,
and then a delete. The only reason for this is so we do not leave dangling
repos on our file system if the user wanders away before submitting the
form. Need a better way to deal with this, many options available, but
probably need to do something soon since the overhead (second fetch) on
larger repos will be annoying.
parent dbe8d149
......@@ -11,7 +11,7 @@ $(function () {
function InitRepoPicker(uuid, change_callback)
{
var callback = function(json) {
console.info(json);
console.info("InitRepoPicker", json);
if (json.code) {
console.info(json.value);
......@@ -54,7 +54,7 @@ $(function () {
function GetRepoSource(uuid, refspec, caller_callback)
{
var callback = function(json) {
console.info(json);
console.info("GetRepoSource", json);
if (json.code) {
sup.HideWaitWait();
......@@ -86,6 +86,7 @@ $(function () {
$('#repoinfo-panel .commit-hash').text(blob.hash);
$('#repoinfo-panel .commit-author').html(blob.author);
$('#repoinfo-panel .commit-refspec').html(blob.refspec);
$('#repoinfo-panel .commit-size').html(blob.size);
$('#repoinfo-panel .commit-date')
.html(moment(blob.date).format("lll"));
......@@ -120,7 +121,7 @@ $(function () {
refspec = "refs/heads/master";
}
var callback = function(json) {
console.info(json);
console.info("GetCommitInfo", json);
if (json.code) {
console.info("GetCommitInfo", json.value);
......@@ -144,7 +145,7 @@ $(function () {
function UpdateRepo(uuid, caller_callback)
{
var callback = function(json) {
console.info(json);
console.info("UpdateRepo", json);
if (json.code) {
sup.HideWaitWait();
......
......@@ -572,7 +572,7 @@ $(function ()
}
}
// Handler for all paths to rspec change (file upload, jacks, edit).
function changeRspec(newRspec, callback)
function changeRspec(newRspec, repoupdate_callback)
{
if (pythonRe.test(newRspec) || tclRe.test(newRspec)) {
//
......@@ -580,11 +580,11 @@ $(function ()
// the server to be "run", which returns XML.
//
if (newRspec != $('#profile_script_textarea').val()) {
checkScript(newRspec, callback);
checkScript(newRspec, repoupdate_callback);
}
return;
}
NewRspecHandler(newRspec, callback);
NewRspecHandler(newRspec);
}
//
......@@ -815,7 +815,7 @@ $(function ()
* use the original tour section. Once we get confirmation, we can
* continue with the update.
*/
function NewRspecHandler(newrspec, finish_callback)
function NewRspecHandler(newrspec)
{
newrspec = $.trim(newrspec);
var oldrspec = $.trim($('#profile_rspec_textarea').val());
......@@ -845,9 +845,6 @@ $(function ()
SyncSteps();
if (!fromrepo)
ProfileModified();
if (finish_callback !== undefined) {
finish_callback(true);
}
if (gotscript) {
$('#profile_instructions').prop("readonly", true);
$('#profile_description').prop("readonly", true);
......@@ -1137,7 +1134,7 @@ $(function ()
//
// Pass a geni-lib script to the server to run (convert to XML).
//
function checkScript(script, rspechandler_callback)
function checkScript(script, repoupdate_callback)
{
// Save for later.
$('#profile_script_textarea').val(script);
......@@ -1155,11 +1152,15 @@ $(function ()
}
if (json.value.rspec != "") {
gotscript = 1;
NewRspecHandler(json.value.rspec, rspechandler_callback);
NewRspecHandler(json.value.rspec);
// Force this; the script is obviously different, but the
// the XML might be exactly same. Still want to save it.
if (!fromrepo)
if (!fromrepo || window.ACTION == "create") {
ProfileModified();
}
if (repoupdate_callback !== undefined) {
repoupdate_callback();
}
// Show the XML source button.
$('#show_xml_modal_button').removeClass("hidden");
}
......@@ -1171,12 +1172,18 @@ $(function ()
* If this is a modification to an existing profile, we still
* have the project name in the same variable.
*/
var args = {
"script" : script,
"pid" : $('#profile_pid').val(),
};
if (repoupdate_callback !== undefined) {
// Pass along uuid as a flag to update repo.
args["repoupdate"] = version_uuid;
}
WaitWait("We are converting your geni-lib script to an rspec");
var xmlthing = sup.CallServerMethod(ajaxurl,
"manage_profile",
"CheckScript",
{"script" : script,
"pid" : $('#profile_pid').val()});
"CheckScript", args);
xmlthing.done(callback);
}
......@@ -1204,7 +1211,7 @@ $(function ()
sup.HideModal('#git-repo-modal');
var callback = function(json) {
console.info(json);
console.info("HandleGitRepoChange", json);
if (json.code) {
sup.HideWaitWait();
......@@ -1217,15 +1224,7 @@ $(function ()
// Add the url to the form.
$('#quickvm_create_profile_form #repourl').val(repourl);
sup.HideWaitWait(function() {
/*
* The point of this callback is to process the script/rspec
* before trying to mark the page as "modified".
*/
changeRspec(json.value.script, function(changed) {
if (changed) {
ProfileModified();
}
});
changeRspec(json.value.script);
});
}
WaitWait("We are attempting to clone your repository. " +
......@@ -1248,27 +1247,35 @@ $(function ()
console.info("HandleGitRepoUpdate", blob);
if (blob) {
/*
* The point of this callback is to process the script/rspec
* before trying to change the profile to use the new source.
* The processing is going to catch script/rspec errors, so
* we want to wait till that is done before telling the backend
* to record the new source in the profile descriptor.
* If the source was an rspec, we updated the profile
* to match the current repo right away. But to make things
* nicer for script based profiles, we wait until the
* script is converted to an rspec. Cause of workflow, we
* end up doing this later so that the user sees a short
* delay when hitting the update button for a script based
* profile.
*/
changeRspec(blob.source, function(changed) {
console.info("changerspec callback", changed, repohash);
if (changed) {
if (blob.hash != repohash) {
/*
* If the commit hash for HEAD has not changed, we
* do not need to do this.
*/
UpdateProileFromMaster(blob.hash);
}
}
if (!pythonRe.test(blob.source)) {
NewRspecHandler(blob.source);
// Mark as HEAD in the page.
repohash = blob.hash;
// Reset the list of tags and branches whenever we
// successfully update our clone.
SetupRepo();
return;
}
/*
* Else we wait till the script converted, the call back
* is invoked after CheckScript() finishes. The server
* side the profile update, no ww can finish things up.
*/
changeRspec(blob.source, function() {
// Mark as HEAD in the page.
repohash = blob.hash;
// Reset the list of tags and branches whenever we
// successfully update our clone.
SetupRepo();
});
// Reset the list of tags and branches whenever we successfully
// update our clone.
SetupRepo();
}
};
gitrepo.UpdateRepo(version_uuid, callback);
......@@ -1292,37 +1299,12 @@ $(function ()
{
var callback = function (source, hash) {
if (source) {
console.info(source);
changeRspec(source);
}
};
gitrepo.GetRepoSource(version_uuid, which, callback);
}
/*
* Force an update to repo-based profile with new script and/or rspec.
*/
function UpdateProileFromMaster(newhash)
{
var update_callback = function(json) {
if (json.code) {
console.info("UpdateProileFromMaster", json.value);
alert("Could not update profile from new master");
}
// Mark as HEAD in the page so we do not update again.
repohash = newhash;
};
var args = {"uuid" : version_uuid,
"rspec" : $('#profile_rspec_textarea').val()};
if ($('#profile_script_textarea').val() != "") {
args["script"] = $('#profile_script_textarea').val();
}
var xmlthing = sup.CallServerMethod(ajaxurl,
"manage_profile",
"UpdateFromMaster", args);
xmlthing.done(update_callback);
}
/*
* Convert from an NS file. The server will do the conversion and spit
* back a genilib script.
......
<?php
#
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
# Copyright (c) 2000-2017 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -330,6 +330,17 @@ function Do_CheckScript()
$this_idx = $this_user->uid_idx();
$this_uid = $this_user->uid();
if (isset($ajax_args["repoupdate"])) {
$profile = Profile::Lookup($ajax_args["repoupdate"]);
if (!$profile) {
SPITAJAX_ERROR(1, "Unknown profile uuid");
return;
}
if ($this_idx != $profile->creator_idx() && !ISADMIN()) {
SPITAJAX_ERROR(1, "Not enough permission");
return;
}
}
if (!isset($ajax_args["script"])) {
SPITAJAX_ERROR(1, "Missing script");
return;
......@@ -398,7 +409,10 @@ function Do_CheckScript()
$blob["paramdefs"] = $paramdefs;
}
}
SPITAJAX_RESPONSE($blob);
if (!isset($profile) ||
UpdateMaster($profile, $ajax_args["script"], $blob["rspec"]) == 0) {
SPITAJAX_RESPONSE($blob);
}
}
unlink($infname);
unlink($outfname);
......@@ -783,6 +797,10 @@ function Do_UpdateRepository()
SPITAJAX_ERROR(1, "Unknown profile uuid");
return;
}
if ($this_idx != $profile->creator_idx() && !ISADMIN()) {
SPITAJAX_ERROR(1, "Not enough permission");
return;
}
$webtask = WebTask::CreateAnonymous();
if (!$webtask) {
SPITAJAX_ERROR(-1, "Internal webtask Error");
......@@ -815,13 +833,22 @@ function Do_UpdateRepository()
"hash" => $webtask->TaskValue("hash"));
$webtask->Delete();
unlink($outfname);
#
# If the source is an rspec, update the profile now.
#
if (! preg_match("/^import/m", $blob["source"])) {
if (UpdateMaster($profile, null, $source)) {
return;
}
}
SPITAJAX_RESPONSE($blob);
}
#
# Save new master into the profile (no profile versioning on repos).
#
function Do_UpdateMaster()
function UpdateMaster($profile, $script, $rspec)
{
global $this_user;
global $ajax_args;
......@@ -829,29 +856,10 @@ function Do_UpdateMaster()
$this_idx = $this_user->uid_idx();
$this_uid = $this_user->uid();
if (!isset($ajax_args["uuid"])) {
SPITAJAX_ERROR(1, "Missing profile uuid");
return;
}
$profile = Profile::Lookup($ajax_args["uuid"]);
if (!$profile) {
SPITAJAX_ERROR(1, "Unknown profile uuid");
return;
}
if (!isset($ajax_args["rspec"]) || $ajax_args["rspec"] == "") {
SPITAJAX_ERROR(1, "Missing rspec uuid");
return;
}
$rspec = $ajax_args["rspec"];
# Script is optional of course.
if (isset($ajax_args["script"]) && $ajax_args["script"] != "") {
$script = $ajax_args["script"];
}
$webtask = WebTask::CreateAnonymous();
if (!$webtask) {
SPITAJAX_ERROR(-1, "Internal webtask Error");
return;
return -1;
}
$fname = tempnam("/tmp", "updateprofile");
$fp = fopen($fname, "w");
......@@ -866,11 +874,9 @@ function Do_UpdateMaster()
fwrite($fp, "<attribute name='rspec'>");
fwrite($fp, " <value>" . htmlspecialchars($rspec) . "</value>");
fwrite($fp, "</attribute>\n");
if (isset($script)) {
if ($script) {
fwrite($fp, "<attribute name='script'>");
fwrite($fp, " <value>" .
htmlspecialchars($formfields["profile_script"]) .
"</value>");
fwrite($fp, " <value>" . htmlspecialchars($script) . "</value>");
fwrite($fp, "</attribute>\n");
}
fwrite($fp, "</profile>\n");
......@@ -894,11 +900,10 @@ function Do_UpdateMaster()
$webtask->Delete();
unlink($fname);
SPITAJAX_ERROR(1, $error);
return;
return -1;
}
$webtask->Delete();
unlink($fname);
SPITAJAX_RESPONSE($blob);
return 0;
}
#
......
<?php
#
# Copyright (c) 2000-2016 University of Utah and the Flux Group.
# Copyright (c) 2000-2017 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -109,8 +109,6 @@ $routing = array("myprofiles" =>
"Do_ConvertClassic",
"UpdateRepository" =>
"Do_UpdateRepository",
"UpdateFromMaster" =>
"Do_UpdateMaster",
"GetRepository" =>
"Do_GetRepository",
"GetRepoSource" =>
......
......@@ -132,6 +132,10 @@
<td>Author:</td>
<td class="commit-author"></td>
</tr>
<tr>
<td>Est. Size:</td>
<td class="commit-size"></td>
</tr>
<tr>
<td>Log:</td>
<td class="commit-log">
......@@ -330,6 +334,17 @@
type='button'
style='margin-left: 10px;'>
Update</button>
<a href='#' class='btn btn-xs'
data-toggle='popover'
data-html='true'
data-delay='{"hide":500}'
data-content='Fetch from your repository, updating
your profile to reflect the current HEAD
of your master branch. Branches and tags
are updated as well (see below).'>
<span class='glyphicon glyphicon-question-sign'
style='margin-bottom: 4px;'></span>
</a>
</span>
</div>
</div>
......@@ -705,10 +720,13 @@
data-keyboard='false' data-backdrop='static'>
<div class='modal-dialog'>
<div class='modal-content'>
<div class='modal-header text-center'>
<big>Create a Repository-based Profile</big>
</div>
<div class='modal-body'>
<p>
Provide a git url to a public repo, and we will create your
profile from the repository. More info coming real soon!
Provide a git url to a public repository, and we will create your
profile from that repository.
</p>
<div class="row">
<div class='col-sm-8 col-sm-offset-2'>
......@@ -733,6 +751,58 @@
type='button'>Cancel
</button>
</center>
<div>
<hr>
<p>
Detailed info coming soon, but here are some of the most
important points to know about <em>repository-based
profiles</em>:
<ul>
<li>Your repository must be public, and the URL
should resemble
<a target="_blank"
href="https://github.com/lbstoller/my-profile.git">
https://github.com/lbstoller/my-profile.git</a>.
</li>
<li>Your repository must contain a file called
"profile.py" (a geni-lib script) <b>or</b> "profile.rspec"
(an rspec). Your topology will be loaded from that
file. Please place the source file in the toplevel directory
(see the aforementioned example repository).
</li>
<li>
When you instantiate your profile, we will clone
your repository to each of your experimental nodes in
the <code>/local/repository</code> directory, and set it
to match whatever branch or tag you have choosen to
instantiate. You will not be able to push to your
repository of course, until you install the necessary
credentials on your nodes.
</li>
<li>
<code>Execute</code> services run <em>after</em> the
nodes have cloned your repository, so you may refer to
the clone (<code>/local/repository</code>) from your
services.
</li>
<li>
You will be able to instantiate from any branch (HEAD) or
tag in the repository.
</li>
<li>
If you change your repository, simply come back here,
select your profile, and use the <code>Update</code> button;
we will fetch from your repository and update the profile to
reflect the changes.
</li>
<li>
Place anything you like in your repository, with the caveat
that a giant repository (including, say, the linux source
code), will take a long time to clone to each of your nodes.
</li>
</ul>
</p>
</div>
</div>
</div>
</div>
......
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