Commit 645dccd7 authored by Leigh Stoller's avatar Leigh Stoller

The Web UI part of repository-based profiles.

parent 92d64446
......@@ -131,6 +131,9 @@ class Instance
function servername() { return $this->field('servername'); }
function aggregate_urn(){ return $this->field('aggregate_urn'); }
function private_key() { return $this->field('privkey'); }
function repourl() { return $this->field('repourl'); }
function reporef() { return $this->field('reporef'); }
function repohash() { return $this->field('repohash'); }
function isopenstack() { return $this->field('isopenstack'); }
function openstack_utilization() {
return $this->field('openstack_utilization');
......
......@@ -103,11 +103,22 @@ function Do_GetParameters()
SPITAJAX_ERROR(1, "Not enough permission to instantiate profile");
return;
}
if (!$profile->isParameterized()) {
#
# For a repo-based profile, the paramdefs will be passed in.
# Note that different refspecs might be parameterized, but others not.
#
if ($profile->repourl() &&
isset($ajax_args["paramdefs"]) && $ajax_args["paramdefs"] != "") {
$paramdefs = $ajax_args["paramdefs"];
}
elseif (!$profile->isParameterized()) {
SPITAJAX_ERROR(1, "Not a parameterized profile");
return;
}
list ($formfrag, $defaults) = $profile->GenerateFormFragment();
else {
$paramdefs = $profile->paramdefs();
}
list ($formfrag, $defaults) = $profile->GenerateFormFragment($paramdefs);
SPITAJAX_RESPONSE(array("formfrag" => htmlentities($formfrag),
"defaults" => $defaults));
}
......@@ -820,8 +831,29 @@ function Do_Submit()
# we could get an rspec.
#
if ($profile->isParameterized() && $this_user &&
isset($formfields["pp_rspec"]) && $formfields["pp_rspec"] != "") {
$args["rspec"] = $formfields["pp_rspec"];
isset($formfields["rspec"]) && $formfields["rspec"] != "") {
$args["rspec"] = $formfields["rspec"];
}
#
# For a repo-based profile, we might get an rspec and/or a script.
# Need to deal with the paramdefs block too.
#
if ($profile->repourl() && $this_user) {
if (isset($formfields["script"]) && $formfields["script"] != "") {
$args["script"] = $formfields["script"];
}
if (isset($formfields["rspec"]) && $formfields["rspec"] != "") {
$args["rspec"] = $formfields["rspec"];
}
# If the user is instantiating the profile version, we will not
# get these.
if (isset($formfields["reporef"]) && $formfields["reporef"] != "") {
$args["reporef"] = $formfields["reporef"];
}
if (isset($formfields["repohash"]) && $formfields["repohash"] != "") {
$args["repohash"] = $formfields["repohash"];
}
}
$aggregate_urn = "";
......
......@@ -58,6 +58,7 @@ $optargs = OptionalPageArguments("create", PAGEARG_STRING,
"asguest", PAGEARG_BOOLEAN,
"default", PAGEARG_STRING,
"from", PAGEARG_STRING,
"refspec", PAGEARG_STRING,
"formfields", PAGEARG_ARRAY);
if ($ISAPT && !$this_user) {
......@@ -317,6 +318,7 @@ function SPITFORM($formfields, $newuser, $errors)
global $TBBASE, $APTMAIL, $ISAPT, $ISCLOUD, $ISPNET, $PORTAL_NAME;
global $profile_array, $this_user, $profilename, $profile;
global $projlist, $skipfirststep, $TBMAINSITE;
global $refspec;
$showabout = ($ISAPT && !$this_user ? 1 : 0);
$registered = (isset($this_user) ? "true" : "false");
......@@ -400,6 +402,15 @@ function SPITFORM($formfields, $newuser, $errors)
echo " window.SKIPFIRSTSTEP = " . ($skipfirststep ? "true" : "false") . ";\n";
echo " window.PORTAL_NAME = '$PORTAL_NAME';\n";
echo " window.USERNAME = '" . $formfields["username"] . "';\n";
if (isset($profile) && $profile->repourl()) {
echo " window.FROMREPO = true;\n";
if (isset($refspec)) {
echo " window.REFSPEC = '$refspec';\n";
}
}
else {
echo " window.FROMREPO = false;\n";
}
echo "</script>\n";
echo "<script src='js/lib/jquery-2.0.3.min.js'></script>\n";
......@@ -411,8 +422,10 @@ function SPITFORM($formfields, $newuser, $errors)
REQUIRE_FORMHELPERS();
REQUIRE_FILESTYLE();
REQUIRE_MARKED();
REQUIRE_MOMENT();
REQUIRE_JACKS();
REQUIRE_JQUERY_STEPS();
AddLibrary("js/gitrepo.js");
SPITREQUIRE("js/instantiate-new.js");
}
......
......@@ -63,6 +63,10 @@ $(function () {
if (_.has(item.dataset, "colsize")) {
colsize = item.dataset['colsize'];;
}
// Override wide setting per field
if (_.has(item.dataset, "wide")) {
wide = item.dataset['wide'];;
}
/*
* Wrap in a div we can name. We assume the form
......
......@@ -412,7 +412,9 @@ $(function ()
disabled: true,
versions: [],
withpublishing: false,
genilib_editor: true
genilib_editor: true,
canrepo: false,
fromrepo: false
});
manage_html = aptforms.FormatFormFieldsHorizontal(manage_html,
{"wide": false });
......@@ -464,7 +466,9 @@ $(function ()
disabled: true,
versions: [],
withpublishing: false,
genilib_editor: true
genilib_editor: true,
canrepo: false,
fromrepo: false
});
manage_html = aptforms.FormatFormFieldsHorizontal(manage_html,
{'wide': false });
......
$(function () {
window.gitrepo = (function () {
var repoString = APT_OPTIONS.fetchTemplate('gitrepo-picker');
var repoTemplate = _.template(repoString);
var branchlist = null;
var taglist = null;
/*
* Get the branches and tags for a profile, and draw the picker.
*/
function InitRepoPicker(uuid, change_callback)
{
var callback = function(json) {
console.info(json);
if (json.code) {
console.info(json.value);
return;
}
branchlist = json.value.branchlist;
taglist = json.value.taglist;
ShowRepoPicker(uuid, change_callback);
}
// Visible cue that something is happening
$('#gitpicker-div table').css("opacity", 0.4);
var xmlthing = sup.CallServerMethod(null,
"manage_profile",
"GetBranchList",
{"uuid" : uuid});
xmlthing.done(callback);
}
function ShowRepoPicker(uuid, change_callback)
{
var html = repoTemplate({"branches" : branchlist,
"tags" : taglist,
"uuid" : uuid});
$('#gitpicker-div').removeClass("hidden");
$('#gitpicker-div table').css("opacity", '');
$('#gitrepo-picker').html(html);
if (change_callback !== undefined) {
$('.branch-button').click(function (event) {
event.preventDefault();
change_callback($(this).data("which"));
});
}
}
/*
* Get source code for a branch or tag and send it back to caller.
* Also update the info panel as on the manage/show page.
*/
function GetRepoSource(uuid, refspec, caller_callback)
{
var callback = function(json) {
console.info(json);
if (json.code) {
sup.HideWaitWait();
sup.SpitOops(json.value);
caller_callback(null);
return;
}
sup.HideWaitWait(function() {
caller_callback(json.value.script, json.value.hash);
});
GetCommitInfo(uuid, refspec);
}
sup.ShowWaitWait("We are getting the source code from the " +
"repository. Patience please.");
var xmlthing = sup.CallServerMethod(null,
"manage_profile",
"GetRepoSource",
{"uuid" : uuid,
"refspec" : refspec});
xmlthing.done(callback);
}
/*
* The manage/show page both have a panel for the currently
* loaded commit. Update that panel when we get new source.
*/
function UpdateInfoPanel(blob)
{
$('#repoinfo-panel .commit-hash').text(blob.hash);
$('#repoinfo-panel .commit-author').html(blob.author);
$('#repoinfo-panel .commit-refspec').html(blob.refspec);
$('#repoinfo-panel .commit-date')
.html(moment(blob.date).format("lll"));
var log = blob.log;
if (log.length <= 20) {
$('#repoinfo-panel .commit-log-start').html(log);
$('#repoinfo-panel .commit-log .log').addClass("hidden");
}
else {
$('#repoinfo-panel .commit-log-start').html(log.substr(0,20));
$('#repoinfo-panel .commit-log .log').popover('destroy');
// DUMB! destroy is async.
setTimeout(function () {
$('#repoinfo-panel .commit-log .log').popover({
trigger: 'hover',
placement: 'auto',
container: 'body',
content: "<pre>" + log + "</pre>",
});}, 200);
$('#repoinfo-panel .commit-log .log').removeClass("hidden");
}
$('#repoinfo-panel .panel-body').css("opacity", '');
$('#repoinfo-panel').removeClass("hidden");
}
/*
* Ask for commit info, then update the info panel.
*/
function GetCommitInfo(uuid, refspec)
{
if (refspec === undefined) {
refspec = "refs/heads/master";
}
var callback = function(json) {
console.info(json);
if (json.code) {
console.info("GetCommitInfo", json.value);
return;
}
json.value.refspec = refspec;
UpdateInfoPanel(json.value);
}
// Visible cue that something is happening
$('#repoinfo-panel .panel-body').css("opacity", 0.4);
var args = {"uuid" : uuid, "refspec" : refspec}
var xmlthing = sup.CallServerMethod(null,
"manage_profile",
"GetCommitInfo", args);
xmlthing.done(callback);
}
/*
* Update from repo, possibly getting a new script or rspec.
*/
function UpdateRepo(uuid, caller_callback)
{
var callback = function(json) {
console.info(json);
if (json.code) {
sup.HideWaitWait();
sup.SpitOops(json.value);
caller_callback(null);
return;
}
sup.HideWaitWait(function() {
caller_callback(json.value);
});
}
sup.ShowWaitWait("We are attempting to pull from your repository. "+
"Patience please.");
var xmlthing = sup.CallServerMethod(null,
"manage_profile",
"UpdateRepository",
{uuid : uuid});
xmlthing.done(callback);
}
// Exports from this module for use elsewhere
return {
InitRepoPicker: InitRepoPicker,
GetRepoSource: GetRepoSource,
UpdateRepo: UpdateRepo,
GetCommitInfo: GetCommitInfo,
};
})();
});
......@@ -31,6 +31,7 @@ $(function ()
var amValueToKey = {};
var showpicker = 0;
var portal = null;
var fromrepo = false;
var registered = false;
var JACKS_NS = "http://www.protogeni.net/resources/rspec/ext/jacks/1";
var jacks = {
......@@ -59,6 +60,7 @@ $(function ()
multisite = window.MULTISITE;
portal = window.PORTAL;
ajaxurl = window.AJAXURL;
fromrepo = window.FROMREPO;
doconstraints = window.DOCONSTRAINTS;
showpicker = window.SHOWPICKER;
......@@ -1172,7 +1174,86 @@ $(function ()
var $xmlthing = sup.CallServerMethod(ajaxurl,
"instantiate", "GetProfile",
{"uuid" : profile});
$xmlthing.done(callback);
/*
* If a repo-based and we got a specific branch/tag, we have to
* get the source for that, since it will be different then what
* is stored in the profile descriptor.
*/
if (fromrepo && window.REFSPEC !== undefined) {
var which = window.REFSPEC;
$xmlthing.done(function(json) {
gitrepo.GetRepoSource(profile, which, function(source, hash) {
var pythonRe = /^import/m;
$('#repohash').val(hash);
$('#reporef').val(which);
if (pythonRe.test(source)) {
ConvertScript(source, function(rspec, paramdefs) {
// Need to pass these along at submit.
$('#rspec_textarea').val(rspec);
$('#script_textarea').val(source);
json.value.rspec = rspec;
json.value.isscript = true;
//
// We can get a parameterized profile, or not.
//
if (paramdefs === undefined) {
json.value.ispprofile = false;
}
else {
$('#paramdefs').val(paramdefs);
json.value.ispprofile = true;
}
callback(json);
});
}
else {
// New rspec, proceed
json.value.rspec = source;
// Need to pass this along at submit.
$('#rspec_textarea').val(source);
callback(json);
}
});
});
}
else {
$xmlthing.done(callback);
}
}
//
// Pass a geni-lib script to the server to run (convert to XML).
// We use this on repo-based profiles, where we have to get the
// source code from the repo, and convert to an rspec.
//
function ConvertScript(script, continuation)
{
var callback = function(json) {
sup.HideWaitWait();
if (json.code) {
sup.SpitOops("oops",
"<pre><code>" +
$('<div/>').text(json.value).html() +
"</code></pre>");
return;
}
if (json.value.rspec != "") {
continuation(json.value.rspec, json.value.paramdefs);
}
}
sup.ShowWaitWait("We are converting the geni-lib script to an rspec. " +
"Patience please.");
var xmlthing = sup.CallServerMethod(null,
"manage_profile",
"CheckScript",
{"script" : script,
"getparams" : true});
xmlthing.done(callback);
}
/*
......@@ -1182,7 +1263,7 @@ $(function ()
// If not a registered user, we do not get an rspec back, since
// the user is not allowed to change the configuration.
if (newRspec) {
$('#pp_rspec_textarea').val(newRspec);
$('#rspec_textarea').val(newRspec);
selected_rspec = newRspec;
CreateAggregateSelectors(newRspec);
}
......
......@@ -24,6 +24,7 @@ $(function ()
var doconstraints = 0;
var amValueToKey = {};
var showpicker = 0;
var fromrepo = false;
var portal = null;
var registered = false;
var JACKS_NS = "http://www.protogeni.net/resources/rspec/ext/jacks/1";
......@@ -53,6 +54,7 @@ $(function ()
multisite = window.MULTISITE;
portal = window.PORTAL;
ajaxurl = window.AJAXURL;
fromrepo = window.FROMREPO;
doconstraints = window.DOCONSTRAINTS;
showpicker = window.SHOWPICKER;
......@@ -860,7 +862,7 @@ $(function ()
var xml = $(xmlDoc);
/*
* We now use the desciption from inside the rspec, unless there
* We now use the description from inside the rspec, unless there
* is none, in which case look to see if the we got one in the
* rpc reply, which we will until all profiles converted over to
* new format rspecs.
......@@ -882,7 +884,28 @@ $(function ()
var $xmlthing = sup.CallServerMethod(ajaxurl,
"instantiate", "GetProfile",
{"uuid" : profile});
$xmlthing.done(callback);
/*
* If a repo-based and we got a specific branch/tag, we have to
* get the source for that, since it will be different then what
* is stored in the profile descriptor.
*/
console.info("fee", fromrepo, window.BRANCH, window.TAG);
if (fromrepo &&
(window.BRANCH !== undefined || window.TAG !== undefined)) {
var which =
(window.BRANCH !== undefined ? window.BRANCH : window.TAG);
$xmlthing.done(function(json) {
gitrepo.GetRepoSource(uuid, which, function(source) {
console.info("foo");
callback(json);
});
});
}
else {
$xmlthing.done(callback);
}
}
/*
......@@ -892,7 +915,7 @@ $(function ()
// If not a registered user, we do not get an rspec back, since
// the user is not allowed to change the configuration.
if (newRspec) {
$('#pp_rspec_textarea').val(newRspec);
$('#rspec_textarea').val(newRspec);
selected_rspec = newRspec;
CreateAggregateSelectors(newRspec);
}
......
This diff is collapsed.
......@@ -654,6 +654,16 @@ $(function () {
// This clears any errors before new submit. Needs more thought.
GenerateModalBody(formfields, null);
//
// XXX: Look for paramdefs/script in the main form and pass along.
// This is for repo-based profiles.
//
if ($('#paramdefs').val() !== undefined) {
formfields["paramdefs"] = $('#paramdefs').val();
formfields["script"] = $('#script_textarea').val();
}
console.info("formfields", formfields);
// Not in checkform mode, this will take time.
if (!checkonly) {
sup.ShowModal("#waitwait-modal");
......@@ -714,9 +724,16 @@ $(function () {
ShowEditor();
}
}
var blob = {"uuid" : uuid};
//
// XXX: Look for paramdefs/script in the form and pass that along.
// This is for repo-based profiles.
//
if ($('#paramdefs').val() !== undefined) {
blob["paramdefs"] = $('#paramdefs').val();
}
var xmlthing = sup.CallServerMethod(null, "instantiate",
"GetParameters",
{"uuid" : uuid});
"GetParameters", blob);
xmlthing.done(callback);
}
......
......@@ -3,14 +3,19 @@ window.sup = (function () {
function ShowModal(which)
{
// console.log('Showing modal ' + which);
$( which ).modal('show');
$(which).modal('show');
}
function HideModal(which)
function HideModal(which, continuation)
{
// console.log('Hide modal ' + which);
$( which ).modal('hide');
var callback = function() {
$(which).off('hidden.bs.modal', callback);
continuation();
};
if (continuation !== undefined) {
$(which).on('hidden.bs.modal', callback);
}
$(which).modal('hide');
}
function ShowWaitWait(message)
......@@ -23,11 +28,15 @@ function ShowWaitWait(message)
ShowModal('#waitwait-modal-withmessage');
}
}
function HideWaitWait()
function HideWaitWait(continuation)
{
$('#waitwait-modal-withmessage-message').html("");
HideModal('#waitwait-modal-withmessage');
HideModal('#waitwait-modal');
if ($('#waitwait-modal').is(':visible')) {
HideModal('#waitwait-modal', continuation);
}
else {
$('#waitwait-modal-withmessage-message').html("");
HideModal('#waitwait-modal-withmessage', continuation);
}
}
function CallServerMethod(url, route, method, args, callback)
......
......@@ -19,6 +19,7 @@ $(function ()
var profile_version = '';
var version_uuid = null;
var gotscript = 0;
var fromrepo = 0;
var ajaxurl = "";
var amlist = null;
var isppprofile = false;
......@@ -54,6 +55,10 @@ $(function ()
if (_.has(fields, "profile_version")) {
profile_version = fields['profile_version'];
}
if (_.has(fields, "profile_repourl") &&
fields["profile_repourl"] != "") {
fromrepo = 1;
}
// Generate the templates.
var show_html = showTemplate({
......@@ -65,6 +70,7 @@ $(function ()
canedit: window.CANEDIT,
disabled: window.DISABLED,
withpublishing: window.WITHPUBLISHING,
fromrepo: fromrepo
});
show_html = aptforms.FormatFormFieldsHorizontal(show_html,
{"wide" : true});
......@@ -80,6 +86,11 @@ $(function ()
$('#oops_div').html(oopsString);
$('#share_div').html(shareTemplate({formfields: fields}))
// Fireoff repo stuff now.
if (fromrepo) {
SetupRepo();
}
// This activates the popover subsystem.
$('[data-toggle="popover"]').popover({
trigger: 'hover',
......@@ -135,7 +146,8 @@ $(function ()
// $('#rspec_modal_download_button')
// .attr("href", href + "&rspec=true");
}
if ($(this).attr("id") == "show_source_modal_button" && isScript) {
if ($(this).attr("id") == "show_source_modal_button" &&
isScript && !fromrepo) {
openEditor();
}
else
......@@ -165,7 +177,7 @@ $(function ()
$('#modal_profile_rspec_div').prepend(elt);
}, {
value: source,
lineNumbers: false,
lineNumbers: true,
smartIndent: true,
autofocus: false,
readOnly: true,
......@@ -275,5 +287,68 @@ $(function ()
window.location.href = 'genilib-editor.php?profile=' + profile_name + '&project=' + profile_pid + '&version=' + profile_version;
}
function SetupRepo()
{
gitrepo.InitRepoPicker(version_uuid,
function(which) {
SelectRepoTarget(which);
});
gitrepo.GetCommitInfo(version_uuid);
}
/*
* User has clicked on a branch/tag. We need to get that branch/tag
* source code and update the page.
*/
function SelectRepoTarget(which)
{
var callback = function (source, hash) {
console.info(source);
// Need to put the source into correct hidden textarea.
// But if its a script, we have to convert it first.
if (pythonRe.test(source)) {
$('#profile_script_textarea').val(source);
ConvertScript(source);
}
else {
$('#profile_rspec_textarea').val(source);
ExtractFromRspec();
}
};
gitrepo.GetRepoSource(version_uuid, which, callback);
}
//
// Pass a geni-lib script to the server to run (convert to XML).
//
function ConvertScript(script)
{
// Save for later.
$('#profile_script_textarea').val(script);
var callback = function(json) {
sup.HideWaitWait();
//console.info(json.value);
if (json.code) {
sup.SpitOops("oops",
"<pre><code>" +
$('<div/>').text(json.value).html() +
"</code></pre>");
return;
}
if (json.value.rspec != "") {
$('#profile_rspec_textarea').val(json.value.rspec);
ExtractFromRspec();
}
}
sup.ShowWaitWait("We are converting the geni-lib script");
var xmlthing = sup.CallServerMethod(ajaxurl,
"manage_profile",
"CheckScript",
{"script" : script});
xmlthing.done(callback);
}
$(document).ready(initialize);
});
......@@ -99,6 +99,9 @@ $(function ()
lockdown_code: lockdown_code,
// The status panel starts out collapsed.
status_panel_show: (instanceStatus == "ready" ? false : true),
repourl: window.APT_OPTIONS.repourl,
reporef: window.APT_OPTIONS.reporef,
repohash: window.APT_OPTIONS.repohash,
};
var status_html = statusTemplate(template_args);
$('#status-body').html(status_html);
......@@ -265,8 +268,11 @@ $(function ()
return;
}
// This is considered the home page, for now.
window.location.replace('instantiate.php?default=' +
profile_uuid);