All new accounts created on Gitlab now require administrator approval. If you invite any collaborators, please let Flux staff know so they can approve the accounts.

Commit 645dccd7 authored by Leigh B Stoller's avatar Leigh B 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);
}
......
......@@ -2,7 +2,7 @@ $(function ()
{
'use strict';
var templates = APT_OPTIONS.fetchTemplateList(['manage-profile', 'waitwait-modal', 'renderer-modal', 'showtopo-modal', 'oops-modal', 'rspectextview-modal', 'guest-instantiate', 'publish-modal', 'instantiate-modal', 'share-modal']);
var templates = APT_OPTIONS.fetchTemplateList(['manage-profile', 'waitwait-modal', 'renderer-modal', 'showtopo-modal', 'oops-modal', 'rspectextview-modal', 'guest-instantiate', 'publish-modal', 'instantiate-modal', 'share-modal', 'gitrepo-picker']);
var manageString = templates['manage-profile'];
var waitwaitString = templates['waitwait-modal'];
var rendererString = templates['renderer-modal'];
......@@ -13,7 +13,7 @@ $(function ()
var publishString = templates['publish-modal'];
var instantiateString = templates['instantiate-modal'];
var shareString = templates['share-modal'];
var gitrepoString = templates['gitrepo-picker'];
var profile_uuid = null;
var profile_name = '';
......@@ -23,6 +23,8 @@ $(function ()
var snapping = 0;
var gotrspec = 0;
var gotscript = 0;
var fromrepo = 0;
var repohash = null;
var ajaxurl = "";
var amlist = null;
var modified = false;
......@@ -41,6 +43,7 @@ $(function ()
var guestInstTemplate = _.template(guestInstantiateString);
var InstTemplate = _.template(instantiateString);
var shareTemplate = _.template(shareString);
var gitrepoTemplate = _.template(gitrepoString);
var stepsInitialized = false;
var pythonRe = /^import/m;
......@@ -75,6 +78,12 @@ $(function ()
if (_.has(fields, "profile_script") && fields["profile_script"] != "") {
gotscript = 1;
}
// Ditto a repourl
if (_.has(fields, "profile_repourl") &&
fields["profile_repourl"] != "") {
fromrepo = 1;
repohash = fields["profile_repohash"];
}
// If this is an existing profile, stash the name/project
if (_.has(fields, "profile_name")) {
......@@ -127,7 +136,9 @@ $(function ()
disabled: window.DISABLED,
versions: versions,
withpublishing: window.WITHPUBLISHING,
genilib_editor: false
genilib_editor: false,
canrepo: window.CANREPO,
fromrepo: fromrepo
});
manage_html = aptforms.FormatFormFieldsHorizontal(manage_html,
{"wide" : true});
......@@ -154,6 +165,11 @@ $(function ()
var rspectext_html = rspectextTemplate({});
$('#rspectext_div').html(rspectext_html);
$('#share_div').html(shareTemplate({formfields: fields}))
// Fireoff repo stuff now.
if (fromrepo) {
SetupRepo();
}
//
// Fix for filestyle problem; not a real class I guess, it
......@@ -214,21 +230,6 @@ $(function ()
reader.readAsText(this.files[0]);
});
// Handler for all paths to rspec change (file upload, jacks, edit).
function changeRspec(newRspec)
{
if (pythonRe.test(newRspec) || tclRe.test(newRspec)) {
//
// A geni-lib script. We are going to pass the script to
// the server to be "run", which returns XML.
//
if (newRspec != $('#profile_script_textarea').val()) {
checkScript(newRspec);
}
return;
}
NewRspecHandler(newRspec);
}
$('#edit_topo_modal_button').click(function (event) {
event.preventDefault();
editor.show($('#profile_rspec_textarea').val(), changeRspec);
......@@ -242,7 +243,7 @@ $(function ()
var source = $.trim($('#profile_script_textarea').val());
var type = "source";
if (source.length > 0 && window.ACTION === 'edit') {
if (source.length > 0 && window.ACTION === 'edit' && !fromrepo) {
openEditor();
} else {
if (source.length === 0) {
......@@ -261,11 +262,20 @@ $(function ()
else {
sup.ClearDownloadOnClick($('#rspec_modal_download_button'));
// $('#rspec_modal_download_button').addClass("hidden");
}
$('#rspec_modal_upload_span').removeClass("hidden");
$('#rspec_modal_editbuttons').removeClass("hidden");
$('#rspec_modal_viewbuttons').addClass("hidden");
$('#modal_profile_rspec_textarea').prop("readonly", false);
}
if (!fromrepo) {
$('#rspec_modal_upload_span').removeClass("hidden");
$('#rspec_modal_editbuttons').removeClass("hidden");
$('#rspec_modal_viewbuttons').addClass("hidden");
$('#modal_profile_rspec_textarea').prop("readonly", false);
}
else {
// No editing repo-based profiles
$('#rspec_modal_upload_span').addClass("hidden");
$('#rspec_modal_editbuttons').addClass("hidden");
$('#rspec_modal_viewbuttons').removeClass("hidden");
$('#modal_profile_rspec_textarea').prop("readonly", true);
}
$('#modal_profile_rspec_textarea').val(source);
$('#rspec_modal').modal({'backdrop':'static','keyboard':false});
$('#rspec_modal').modal('show');
......@@ -309,7 +319,7 @@ $(function ()
$('#rspec_modal').on('shown.bs.modal', function() {
var source = $('#modal_profile_rspec_textarea').val();
var mode = "text/xml";
var readonly = window.CLONING ||
var readonly = window.CLONING || fromrepo ||
$('#modal_profile_rspec_textarea').prop("readonly");