From 55e93f663ac9874513b139b80814c0e6701e3c09 Mon Sep 17 00:00:00 2001 From: Sergio Betanzos Date: Mon, 11 May 2020 15:46:30 +0200 Subject: [PATCH] F #4089: Dockerhub-tags table for firecracker (#4691) --- .../OpenNebulaJSON/MarketPlaceAppJSON.rb | 4 +- src/sunstone/models/SunstoneServer.rb | 49 ++++++++ src/sunstone/public/app/opennebula/action.js | 23 ++++ .../public/app/opennebula/marketplaceapp.js | 5 +- .../app/tabs/marketplaceapps-tab/actions.js | 9 +- .../form-panels/docker-tags.js | 119 ++++++++++++++++++ .../marketplaceapps-tab/form-panels/export.js | 65 ++++++---- .../form-panels/export/wizard.hbs | 6 +- src/sunstone/sunstone-server.rb | 7 ++ 9 files changed, 261 insertions(+), 26 deletions(-) create mode 100644 src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/docker-tags.js diff --git a/src/sunstone/models/OpenNebulaJSON/MarketPlaceAppJSON.rb b/src/sunstone/models/OpenNebulaJSON/MarketPlaceAppJSON.rb index a310430ac4..5725906117 100644 --- a/src/sunstone/models/OpenNebulaJSON/MarketPlaceAppJSON.rb +++ b/src/sunstone/models/OpenNebulaJSON/MarketPlaceAppJSON.rb @@ -75,10 +75,12 @@ module OpenNebulaJSON dsid = params['dsid'] ? params['dsid'].to_i : params['dsid'] name = params['name'] vmtemplate_name = params['vmtemplate_name'] + tag ="tag=#{params['tag']}" if !params['tag'].empty? rc = super({ :dsid => dsid, :name => name, - :vmtemplate_name => vmtemplate_name + :vmtemplate_name => vmtemplate_name, + :url_args => tag }) if OpenNebula.is_error?(rc) diff --git a/src/sunstone/models/SunstoneServer.rb b/src/sunstone/models/SunstoneServer.rb index fea607b407..9b2b5309f5 100644 --- a/src/sunstone/models/SunstoneServer.rb +++ b/src/sunstone/models/SunstoneServer.rb @@ -24,6 +24,8 @@ require 'OpenNebulaAddons' require 'OpenNebulaJSON/JSONUtils' #include JSONUtils +require 'net/http' + class SunstoneServer < CloudServer # Secs to sleep between checks to see if image upload to repo is finished @@ -476,6 +478,53 @@ class SunstoneServer < CloudServer return [200, rc.to_json] end + def get_docker_tags(app_id) + # Get MarketPlaceApp + marketapp = retrieve_resource("marketplaceapp", app_id) + if OpenNebula.is_error?(marketapp) + return [404, marketapp.to_json] + end + + # Get MarketPlace + market_id = marketapp["MARKETPLACE_ID"] + market = retrieve_resource("marketplace", market_id) + if OpenNebula.is_error?(market) + return [404, market.to_json] + end + + # Check market_mad + # TODO Change message + return [400, "Invalid MARKET_MAD"] if market["MARKET_MAD"] != "dockerhub" + + # Get dockerhub tags + url = "https://hub.docker.com/v2/repositories/library/#{marketapp["NAME"]}/tags/?page_size=100" + tags_names = [] + loop do + uri = URI(url) + req = Net::HTTP::Get.new(uri.request_uri) + + req['User-Agent'] = "OpenNebula" + + opts = { :use_ssl => true } + + rc = Net::HTTP.start(uri.hostname, uri.port, nil, nil, opts) do |http| + http.request(req) + end + + return [rc.code.to_i, rc.msg] unless rc.is_a? Net::HTTPSuccess + + body = JSON.parse(rc.body) + (tags_names << body["results"].map { |values| { + name: values["name"], + last_updated: values["last_updated"] + } }).flatten! + break if body["next"].nil? || body["next"].empty? + url = body["next"] + end + + return [200, tags_names.to_json] + end + private diff --git a/src/sunstone/public/app/opennebula/action.js b/src/sunstone/public/app/opennebula/action.js index da1a12a06f..06d1b79359 100644 --- a/src/sunstone/public/app/opennebula/action.js +++ b/src/sunstone/public/app/opennebula/action.js @@ -462,6 +462,29 @@ define(function(require) { return ""+id; }, + + "getAppTags": function(params, resource){ + var callback = params.success; + var callbackError = params.error; + var data = params.data; + + var method = "getAppTags"; + var request = OpenNebulaHelper.request(resource, method, data); + + var url = resource.toLowerCase() + "/" + params.data.id + "/tags"; + $.ajax({ + url: url, + type: "GET", + dataType: "json", + success: function(response) { + return callback ? callback(request, response) : null; + }, + error: function(response) { + return callbackError ? + callbackError(request, OpenNebulaError(response)) : null; + } + }); + }, "get_all_cache": function() { return listCache; }, diff --git a/src/sunstone/public/app/opennebula/marketplaceapp.js b/src/sunstone/public/app/opennebula/marketplaceapp.js index 507d75b1d9..0e32e3aed3 100644 --- a/src/sunstone/public/app/opennebula/marketplaceapp.js +++ b/src/sunstone/public/app/opennebula/marketplaceapp.js @@ -121,7 +121,10 @@ define(function(require) { }, "unlock" : function(params) { OpenNebulaAction.simple_action(params, RESOURCE, "unlock"); - } + }, + "tags" : function(params) { + OpenNebulaAction.getAppTags(params, RESOURCE) + }, } return MarketPlaceApp; diff --git a/src/sunstone/public/app/tabs/marketplaceapps-tab/actions.js b/src/sunstone/public/app/tabs/marketplaceapps-tab/actions.js index 99c22b3482..476f88cf26 100644 --- a/src/sunstone/public/app/tabs/marketplaceapps-tab/actions.js +++ b/src/sunstone/public/app/tabs/marketplaceapps-tab/actions.js @@ -59,7 +59,14 @@ define(function(require) { EXPORT_DIALOG_ID, "export", function(formPanelInstance, context) { - formPanelInstance.setResourceId(context, resourceId, type); + OpenNebulaResource.show({ + data: { id: resourceId }, + success: function(_, app_json) { + formPanelInstance.setDockerTags(resourceId, app_json); + formPanelInstance.setResourceId(context, app_json, type); + }, + error: Notifier.onError + }); } ); } diff --git a/src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/docker-tags.js b/src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/docker-tags.js new file mode 100644 index 0000000000..bc08c99faa --- /dev/null +++ b/src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/docker-tags.js @@ -0,0 +1,119 @@ +/* -------------------------------------------------------------------------- */ +/* Copyright 2002-2020, OpenNebula Project, OpenNebula Systems */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); you may */ +/* not use this file except in compliance with the License. You may obtain */ +/* a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* -------------------------------------------------------------------------- */ + +define(function(require) { + /* + DEPENDENCIES + */ + + var Sunstone = require('sunstone'); + var OpenNebula = require('opennebula'); + var TabDataTable = require('utils/tab-datatable'); + var Locale = require('utils/locale'); + var Humanize = require('utils/humanize'); + + /* + CONSTANTS + */ + + var RESOURCE = "MarketPlaceApp"; + var TAB_ID = require('../tabId'); + + /* + CONSTRUCTOR + */ + + function Table(dataTableId, conf) { + this.conf = conf || {}; + this.tabId = TAB_ID; + this.dataTableId = dataTableId; + this.resource = RESOURCE; + this.appId = conf.appId; + + this.dataTableOptions = { + bAutoWidth: false, + bSortClasses : false, + bDeferRender: true, + aoColumnDefs: [ + { sType: "string", aTargets: [0, 1], aDataSort: [0, 1] }, + { sType: "date", aTargets: [2], aDataSort: [2] }, + ], + aLengthMenu: [ [5, 10, 20, -1], [5, 10, 20, "All"] ], + iDisplayLength: 5, + }; + + this.columns = [ + Locale.tr("Name"), + Locale.tr("Last updated") + ]; + + this.selectOptions = { + "id_index": 0, + "name_index": 0, + "select_resource": Locale.tr("Please select a dockerhub tag from the list (default latest)"), + "you_selected": Locale.tr("You selected the following dockerhub tags:") + } + + TabDataTable.call(this); + } + + Table.prototype = Object.create(TabDataTable.prototype); + Table.prototype.constructor = Table; + Table.prototype.elementArray = _elementArray; + Table.prototype.updateFn = _updateFn; + + return Table; + + /* + FUNCTION DEFINITIONS + */ + + function _elementArray(element) { + var lastUpdated = ""; + if (element.last_updated) { + var date = new Date(element.last_updated); + lastUpdated = date.toLocaleDateString(); + } + + return [ element.name, element.name, lastUpdated ]; + } + + function _updateFn() { + var that = this; + $("#refresh_button_exportMarketPlaceAppFormdocketagsTable").addClass("fa-spin"); + + var success_func = function (_, resource_list) { + var list_array = []; + $.each(resource_list, function() { + list_array.push(that.elementArray(this)); + }); + + that.updateView(null, list_array, true); + Sunstone.enableFormPanelSubmit(TAB_ID); + $("#refresh_button_exportMarketPlaceAppFormdocketagsTable").removeClass("fa-spin"); + } + + OpenNebula[this.resource].tags({ + data: { id: this.appId }, + success: success_func, + error: function(request, error_json, container) { + success_func(request, []); + Notifier.onError(request, error_json, container); + } + }); + + } +}); diff --git a/src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/export.js b/src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/export.js index 13785f34a0..dc0cf8ed5c 100644 --- a/src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/export.js +++ b/src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/export.js @@ -22,14 +22,10 @@ define(function(require) { var BaseFormPanel = require("utils/form-panels/form-panel"); var Sunstone = require("sunstone"); var Locale = require("utils/locale"); - var Notifier = require("utils/notifier"); var Tips = require("utils/tips"); var DataStoresTable = require("tabs/datastores-tab/datatable"); + var DockerTagsTable = require("./docker-tags"); var DataStore = require("opennebula/datastore"); - var OpenNebulaMarketPlaceApp = require("opennebula/marketplaceapp"); - var Config = require("sunstone-config"); - var OpenNebula = require("opennebula"); - var TemplateUtils = require("utils/template-utils"); /* TEMPLATES @@ -63,6 +59,14 @@ define(function(require) { return r; } + function _getDockerTagsTable(formPanelId, resourceId){ + return (formPanelId) + ? new DockerTagsTable( + formPanelId + "docketagsTable", + { appId: resourceId, select: true } + ) : null; + } + function FormPanel() { this.formPanelId = FORM_PANEL_ID; this.tabId = TAB_ID; @@ -85,6 +89,7 @@ define(function(require) { FormPanel.prototype.htmlWizard = _htmlWizard; FormPanel.prototype.submitWizard = _submitWizard; FormPanel.prototype.setResourceId = _setResourceId; + FormPanel.prototype.setDockerTags = _setDockerTags; FormPanel.prototype.onShow = _onShow; FormPanel.prototype.setup = _setup; @@ -104,14 +109,17 @@ define(function(require) { function _onShow(context) { var placeDataStore = "#placeDatatableDatastore"; Sunstone.disableFormPanelSubmit(TAB_ID); - if(this.type === "VMTEMPLATE" ){ + + if (this.type === "VMTEMPLATE") { this.datastoresTable.dataTable.parents(placeDataStore).remove(); - }else{ + } + else { if(!($(placeDataStore).length)){ $("#exportMarketPlaceAppFormWizard").append(this.datastoresTable.dataTableHTML); this.datastoresTable = getDataStore(FORM_PANEL_ID); this.datastoresTable.initialize(); } + this.datastoresTable.resetResourceTableSelect(); } $("#NAME", context).focus(); @@ -132,22 +140,31 @@ define(function(require) { }); } - function _setResourceId(context, resourceId, type) { + function _setResourceId(context, appJson, type) { + this.type = type; + + $("input#NAME", context).val(appJson.MARKETPLACEAPP.NAME).trigger("input"); + if (appJson.MARKETPLACEAPP.TEMPLATE.VMTEMPLATE64 != undefined){ + $(".vmname", context).show(); + } + } + + function _setDockerTags(resourceId, appJson) { this.resourceId = resourceId; - this.type = type; - OpenNebula.MarketPlaceApp.show({ - data : { - id: resourceId - }, - success: function(request,app_json){ - $("input#NAME", context).val(app_json.MARKETPLACEAPP.NAME).trigger("input"); - if (app_json.MARKETPLACEAPP.TEMPLATE.VMTEMPLATE64 != undefined){ - $(".vmname", context).show(); - } - Sunstone.enableFormPanelSubmit(TAB_ID); - }, - error: Notifier.onError - }); + + if (appJson.MARKETPLACEAPP && + appJson.MARKETPLACEAPP.MARKETPLACE != undefined && + String(appJson.MARKETPLACEAPP.MARKETPLACE).toLowerCase() === "dockerhub" + ) { + this.dockertagsTable = _getDockerTagsTable(FORM_PANEL_ID, resourceId); + $("#placeDatatableDockerTags").show().append(this.dockertagsTable.dataTableHTML); + this.dockertagsTable.initialize(); + this.dockertagsTable.resetResourceTableSelect(); + } + else { + $("#placeDatatableDockerTags").hide() + Sunstone.enableFormPanelSubmit(TAB_ID); + } } function _submitWizard(context) { @@ -157,6 +174,10 @@ define(function(require) { "dsid" : this.datastoresTable.idInput().val() }; + if (this.dockertagsTable) { + $.extend(marketPlaceAppObj, { "tag": this.dockertagsTable.idInput().val() }); + } + Sunstone.runAction("MarketPlaceApp.export", [this.resourceId], marketPlaceAppObj); return false; } diff --git a/src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/export/wizard.hbs b/src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/export/wizard.hbs index 81b09933ae..9d7b2db815 100644 --- a/src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/export/wizard.hbs +++ b/src/sunstone/public/app/tabs/marketplaceapps-tab/form-panels/export/wizard.hbs @@ -38,6 +38,10 @@
{{tr "Select the Datastore to store the resource"}} - {{{datastoresTableHTML}}} + {{{ datastoresTableHTML }}} +
+ diff --git a/src/sunstone/sunstone-server.rb b/src/sunstone/sunstone-server.rb index 424ba11f6c..72599d92a3 100755 --- a/src/sunstone/sunstone-server.rb +++ b/src/sunstone/sunstone-server.rb @@ -1014,6 +1014,13 @@ get '/marketplaceapp/:id/download' do end end +############################################################################## +# Retrive DockerHub tags +############################################################################## +get '/marketplaceapp/:id/tags' do + @SunstoneServer.get_docker_tags(params[:id]) +end + ############################################################################## # Create a new Resource ##############################################################################