diff --git a/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js b/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js index 0f5ed1813c..34f96fa01d 100644 --- a/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js +++ b/src/fireedge/src/client/components/Tables/MarketplaceApps/actions.js @@ -16,12 +16,15 @@ /* eslint-disable jsdoc/require-jsdoc */ import { useMemo } from 'react' import { useHistory } from 'react-router-dom' -import { AddSquare, CloudDownload } from 'iconoir-react' +import { AddSquare, CloudDownload, DownloadCircledOutline } from 'iconoir-react' import { useViews } from 'client/features/Auth' import { useGeneralApi } from 'client/features/General' import { Translate } from 'client/components/HOC' -import { useExportAppMutation } from 'client/features/OneApi/marketplaceApp' +import { + useExportAppMutation, + useDownloadAppMutation, +} from 'client/features/OneApi/marketplaceApp' import { ExportForm } from 'client/components/Forms/MarketplaceApp' import { createActions } from 'client/components/Tables/Enhanced/Utils' @@ -51,6 +54,7 @@ const Actions = () => { const { view, getResourceView } = useViews() const { enqueueSuccess } = useGeneralApi() const [exportApp] = useExportAppMutation() + const [downloadApp] = useDownloadAppMutation() const marketplaceAppActions = useMemo( () => @@ -86,6 +90,18 @@ const Actions = () => { }, ], }, + { + accessor: MARKETPLACE_APP_ACTIONS.DOWNLOAD, + tooltip: T.DownloadApp, + selected: { min: 1 }, + icon: DownloadCircledOutline, + action: async (apps) => { + const urls = await Promise.all( + apps.map(({ id }) => downloadApp(id).unwrap()) + ) + urls.forEach((url) => window.open(url, '_blank')) + }, + }, ], }), [view] diff --git a/src/fireedge/src/client/constants/marketplaceApp.js b/src/fireedge/src/client/constants/marketplaceApp.js index 04314a8d69..79fda7fdb0 100644 --- a/src/fireedge/src/client/constants/marketplaceApp.js +++ b/src/fireedge/src/client/constants/marketplaceApp.js @@ -53,4 +53,5 @@ export const MARKETPLACE_APP_ACTIONS = { CREATE_DIALOG: 'create_dialog', RENAME: ACTIONS.RENAME, EXPORT: 'export', + DOWNLOAD: 'download', } diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js index dcfadf376e..adf6d03f55 100644 --- a/src/fireedge/src/client/constants/translates.js +++ b/src/fireedge/src/client/constants/translates.js @@ -670,6 +670,7 @@ module.exports = { automatically added when the App is exported`, ImportIntoDatastore: 'Import into Datastore', DownloadAppToOpenNebula: 'Download App to OpenNebula', + DownloadApp: 'Download App', ExportAppNameConcept: 'Name that the resource will get for description purposes', ExportTemplateNameConcept: ` diff --git a/src/fireedge/src/client/features/OneApi/marketplaceApp.js b/src/fireedge/src/client/features/OneApi/marketplaceApp.js index 31db9d2194..8d9ac74da1 100644 --- a/src/fireedge/src/client/features/OneApi/marketplaceApp.js +++ b/src/fireedge/src/client/features/OneApi/marketplaceApp.js @@ -24,6 +24,7 @@ import { ONE_RESOURCES, ONE_RESOURCES_POOL, } from 'client/features/OneApi' +import { requestConfig } from 'client/utils' import { FilterFlag, Permission, MarketplaceApp } from 'client/constants' const { APP } = ONE_RESOURCES @@ -305,6 +306,31 @@ const marketAppApi = oneApi.injectEndpoints({ }, invalidatesTags: [APP_POOL], }), + downloadApp: builder.mutation({ + /** + * Download a MarketPlaceApp. + * + * @param {string} id - Marketplace app id + * @param {object} configBaseQueryApi - ConfigBaseQueryApi + * @param {function():object} configBaseQueryApi.getState - Get current state + * @returns {object} Marketplace URL download + * @throws Fails when response isn't code 200 + */ + queryFn: (id, { getState }) => { + try { + const state = getState() + const token = state.auth.jwt + const name = ExtraActions.MARKETAPP_DOWNLOAD + + const command = { name, ...ExtraCommands[name] } + const { url, params } = requestConfig({ id, token }, command) + + return { data: `/fireedge${url}?token=${params.token}` } + } catch (error) { + return { error } + } + }, + }), exportApp: builder.mutation({ /** * Exports the marketplace app to the OpenNebula cloud. @@ -354,6 +380,7 @@ export const { useUnlockAppMutation, useImportAppMutation, useExportAppMutation, + useDownloadAppMutation, } = marketAppApi export default marketAppApi diff --git a/src/fireedge/src/server/routes/api/2fa/basepath.js b/src/fireedge/src/server/routes/api/2fa/basepath.js deleted file mode 100644 index f8cff1b5db..0000000000 --- a/src/fireedge/src/server/routes/api/2fa/basepath.js +++ /dev/null @@ -1,17 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2021, 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. * - * ------------------------------------------------------------------------- */ - -module.exports = 'tfa' diff --git a/src/fireedge/src/server/routes/api/2fa/functions.js b/src/fireedge/src/server/routes/api/2fa/functions.js index 0b65ac34cd..0b5b08eb64 100644 --- a/src/fireedge/src/server/routes/api/2fa/functions.js +++ b/src/fireedge/src/server/routes/api/2fa/functions.js @@ -43,19 +43,19 @@ const twoFactorAuthIssuer = appConfig.TWO_FACTOR_AUTH_ISSUER || default2FAIssuer /** * Get information for opennebula authenticated user. * - * @param {Function} connect - xmlrpc function + * @param {Function} oneConnect - xmlrpc function * @param {Function} next - express stepper * @param {Function} callback - run if have user information */ const getUserInfoAuthenticated = ( - connect = defaultEmptyFunction, + oneConnect = defaultEmptyFunction, next = defaultEmptyFunction, callback = defaultEmptyFunction ) => { - connect( - Actions.USER_INFO, - getDefaultParamsOfOpennebulaCommand(Actions.USER_INFO, GET), - (err, value) => { + oneConnect({ + action: Actions.USER_INFO, + parameters: getDefaultParamsOfOpennebulaCommand(Actions.USER_INFO, GET), + callback: (err, value) => { responseOpennebula( () => undefined, err, @@ -69,8 +69,8 @@ const getUserInfoAuthenticated = ( }, next ) - } - ) + }, + }) } /** @@ -105,9 +105,9 @@ const setup = ( const sunstone = user.USER.TEMPLATE.SUNSTONE const secret = sunstone[default2FAOpennebulaTmpVar] if (check2Fa(secret, token)) { - oneConnect( - Actions.USER_UPDATE, - [ + oneConnect({ + action: Actions.USER_UPDATE, + parameters: [ parseInt(user.USER.ID, 10), generateNewResourceTemplate( user.USER.TEMPLATE.SUNSTONE || {}, @@ -116,7 +116,7 @@ const setup = ( ), 1, ], - (error, value) => { + callback: (error, value) => { responseOpennebula( () => undefined, error, @@ -129,8 +129,8 @@ const setup = ( }, next ) - } - ) + }, + }) } else { res.locals.httpCode = httpResponse(unauthorized) next() @@ -171,9 +171,9 @@ const qr = ( const oneConnect = oneConnection() getUserInfoAuthenticated(oneConnect, next, (user) => { if (user && user.USER && user.USER.ID && user.USER.TEMPLATE) { - oneConnect( - Actions.USER_UPDATE, - [ + oneConnect({ + action: Actions.USER_UPDATE, + parameters: [ parseInt(user.USER.ID, 10), generateNewResourceTemplate( user.USER.TEMPLATE.SUNSTONE || {}, @@ -182,7 +182,7 @@ const qr = ( ), 1, ], - (error, value) => { + callback: (error, value) => { responseOpennebula( () => undefined, error, @@ -199,8 +199,8 @@ const qr = ( }, next ) - } - ) + }, + }) } else { next() } @@ -237,9 +237,9 @@ const del = ( user.USER.TEMPLATE && user.USER.TEMPLATE.SUNSTONE ) { - oneConnect( - Actions.USER_UPDATE, - [ + oneConnect({ + action: Actions.USER_UPDATE, + parameters: [ parseInt(user.USER.ID, 10), generateNewResourceTemplate(user.USER.TEMPLATE.SUNSTONE || {}, {}, [ default2FAOpennebulaTmpVar, @@ -247,7 +247,7 @@ const del = ( ]), 1, ], - (err, value) => { + callback: (err, value) => { responseOpennebula( () => undefined, err, @@ -260,8 +260,8 @@ const del = ( }, next ) - } - ) + }, + }) } }) } diff --git a/src/fireedge/src/server/routes/api/2fa/routes.js b/src/fireedge/src/server/routes/api/2fa/routes.js index 58725c9ca8..23cb4396e2 100644 --- a/src/fireedge/src/server/routes/api/2fa/routes.js +++ b/src/fireedge/src/server/routes/api/2fa/routes.js @@ -18,10 +18,9 @@ const { httpMethod, from: fromData, } = require('server/utils/constants/defaults') -const TFA = require('server/routes/api/2fa/basepath') const { POST, DELETE, GET } = httpMethod -const basepath = `/${TFA}` +const basepath = '/tfa' const TFA_SETUP = 'tfa.setup' const TFA_QR = 'tfa.qr' const TFA_DELETE = 'tfa.delete' diff --git a/src/fireedge/src/server/routes/api/auth/basepath.js b/src/fireedge/src/server/routes/api/auth/basepath.js deleted file mode 100644 index a3c0715deb..0000000000 --- a/src/fireedge/src/server/routes/api/auth/basepath.js +++ /dev/null @@ -1,17 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2021, 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. * - * ------------------------------------------------------------------------- */ - -module.exports = 'auth' diff --git a/src/fireedge/src/server/routes/api/auth/functions.js b/src/fireedge/src/server/routes/api/auth/functions.js index 3bcd08ff06..c109b9cedd 100644 --- a/src/fireedge/src/server/routes/api/auth/functions.js +++ b/src/fireedge/src/server/routes/api/auth/functions.js @@ -111,20 +111,19 @@ const auth = ( next() } - oneConnect( - Actions.USER_INFO, - getDefaultParamsOfOpennebulaCommand(Actions.USER_INFO, GET), - (err, value) => { + oneConnect({ + action: Actions.USER_INFO, + parameters: getDefaultParamsOfOpennebulaCommand(Actions.USER_INFO, GET), + callback: (err, value) => { loginUser(err, value, success, error) }, - false - ) + fillHookResource: false, + }) } else { next() } } -const authApi = { +module.exports = { auth, } -module.exports = authApi diff --git a/src/fireedge/src/server/routes/api/auth/routes.js b/src/fireedge/src/server/routes/api/auth/routes.js index d4a9aad4fe..bb5c9224ad 100644 --- a/src/fireedge/src/server/routes/api/auth/routes.js +++ b/src/fireedge/src/server/routes/api/auth/routes.js @@ -19,11 +19,9 @@ const { from: fromData, } = require('server/utils/constants/defaults') -const AUTH = require('server/routes/api/auth/basepath') - const { POST } = httpMethod const { postBody } = fromData -const basepath = `/${AUTH}` +const basepath = '/auth' const AUTHENTICATION = 'authentication' const Actions = { diff --git a/src/fireedge/src/server/routes/api/auth/utils.js b/src/fireedge/src/server/routes/api/auth/utils.js index 40d3d305fd..5696a6df96 100644 --- a/src/fireedge/src/server/routes/api/auth/utils.js +++ b/src/fireedge/src/server/routes/api/auth/utils.js @@ -357,10 +357,13 @@ const getCreatedTokenOpennebula = (username = '') => { const setZones = () => { if (global && !global.zones) { const oneConnect = connectOpennebula() - oneConnect( - ActionZones.ZONEPOOL_INFO, - getDefaultParamsOfOpennebulaCommand(ActionZones.ZONEPOOL_INFO, GET), - (err, value) => { + oneConnect({ + action: ActionZones.ZONEPOOL_INFO, + parameters: getDefaultParamsOfOpennebulaCommand( + ActionZones.ZONEPOOL_INFO, + GET + ), + callback: (err, value) => { // res, err, value, response, next responseOpennebula( () => undefined, @@ -394,8 +397,8 @@ const setZones = () => { next ) }, - false - ) + fillHookResource: false, + }) } } @@ -516,10 +519,13 @@ const getServerAdminAndWrapUser = (userData = {}) => { `${serverAdminData.username}:${serverAdminData.username}`, tokenWithServerAdmin.token ) - oneConnect( - ActionUsers.USER_INFO, - getDefaultParamsOfOpennebulaCommand(ActionUsers.USER_INFO, GET), - (err, value) => { + oneConnect({ + action: ActionUsers.USER_INFO, + parameters: getDefaultParamsOfOpennebulaCommand( + ActionUsers.USER_INFO, + GET + ), + callback: (err, value) => { responseOpennebula( updaterResponse, err, @@ -529,8 +535,8 @@ const getServerAdminAndWrapUser = (userData = {}) => { next ) }, - false - ) + fillHookResource: false, + }) } } } diff --git a/src/fireedge/src/server/routes/api/files/basepath.js b/src/fireedge/src/server/routes/api/files/basepath.js deleted file mode 100644 index d18b89945e..0000000000 --- a/src/fireedge/src/server/routes/api/files/basepath.js +++ /dev/null @@ -1,17 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2021, 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. * - * ------------------------------------------------------------------------- */ - -module.exports = 'files' diff --git a/src/fireedge/src/server/routes/api/files/functions.js b/src/fireedge/src/server/routes/api/files/functions.js index 8836c6908c..2620be9960 100644 --- a/src/fireedge/src/server/routes/api/files/functions.js +++ b/src/fireedge/src/server/routes/api/files/functions.js @@ -57,10 +57,10 @@ const checkUserAdmin = ( typeof success === 'function' && typeof error === 'function' ) { - oneConnection( - ActionUser.USER_INFO, - [parseInt(id, 10)], - (err, value) => { + oneConnection({ + action: ActionUser.USER_INFO, + parameters: [parseInt(id, 10)], + callback: (err, value) => { if ( !err && value && @@ -83,8 +83,8 @@ const checkUserAdmin = ( error(err) } }, - false - ) + fillHookResource: false, + }) } else { error() } @@ -255,14 +255,12 @@ const getDefaultFilesforApps = ( * @param {Function} next - express stepper * @param {string} params - data response http * @param {object} userData - user of http request - * @param {Function} oneConnection - one connection XMLRPC */ const list = ( res = {}, next = defaultEmptyFunction, params = {}, - userData = {}, - oneConnection = defaultEmptyFunction + userData = {} ) => { const { user, password, id } = userData const { app } = params diff --git a/src/fireedge/src/server/routes/api/files/routes.js b/src/fireedge/src/server/routes/api/files/routes.js index 8f065ec1ef..dfeed6a2d2 100644 --- a/src/fireedge/src/server/routes/api/files/routes.js +++ b/src/fireedge/src/server/routes/api/files/routes.js @@ -18,10 +18,9 @@ const { from: fromData, httpMethod, } = require('server/utils/constants/defaults') -const FILES = require('server/routes/api/files/basepath') const { GET, POST, PUT, DELETE } = httpMethod -const basepath = `/${FILES}` +const basepath = '/files' const { query } = fromData const FILE_SHOW = 'file.show' diff --git a/src/fireedge/src/server/routes/api/index.js b/src/fireedge/src/server/routes/api/index.js index f45d720730..aa69e06670 100644 --- a/src/fireedge/src/server/routes/api/index.js +++ b/src/fireedge/src/server/routes/api/index.js @@ -17,10 +17,13 @@ const multer = require('multer') const { messageTerminal } = require('server/utils/general') const { getRequestParameters, getRequestFiles } = require('server/utils/server') -const { defaultConfigErrorMessage } = require('server/utils/constants/defaults') +const { + defaultConfigErrorMessage, + defaultTmpPath, +} = require('server/utils/constants/defaults') const { writeInLogger } = require('server/utils/logger') -const upload = multer({ dest: '/tmp' }) +const upload = multer({ dest: defaultTmpPath }) const routes = [ '2fa', diff --git a/src/fireedge/src/server/routes/api/marketapp/functions.js b/src/fireedge/src/server/routes/api/marketapp/functions.js index a66c4a5e99..b0b8b586cf 100644 --- a/src/fireedge/src/server/routes/api/marketapp/functions.js +++ b/src/fireedge/src/server/routes/api/marketapp/functions.js @@ -14,9 +14,13 @@ * limitations under the License. * * ------------------------------------------------------------------------- */ +const btoa = require('btoa') +const { exec } = require('child_process') const { sprintf } = require('sprintf-js') const { request: axios } = require('axios') +const { messageTerminal } = require('server/utils/general') const { defaults, httpCodes } = require('server/utils/constants') +const { validateAuth } = require('server/utils/jwt') const { Actions: ActionsMarketApp, } = require('server/utils/constants/commands/marketapp') @@ -25,9 +29,11 @@ const { } = require('server/utils/constants/commands/market') const { httpResponse, executeCommand } = require('server/utils/server') const { getSunstoneConfig } = require('server/utils/yml') +const { writeInLogger } = require('server/utils/logger') const { defaultEmptyFunction, defaultCommandMarketApp, dockerUrl } = defaults -const { ok, internalServerError, badRequest, notFound } = httpCodes +const { ok, internalServerError, badRequest, notFound, unauthorized } = + httpCodes const httpBadRequest = httpResponse(badRequest, '', '') const httpNotFoundRequest = httpResponse(notFound, '', '') @@ -109,7 +115,83 @@ const exportApp = ( } /** - * Import the marketplace VM or VM TEMPLATE to the OpenNebula cloud. + * Exports the marketplace app to the OpenNebula cloud. + * + * @param {object} res - http response + * @param {Function} next - express stepper + * @param {object} params - params of http request + * @param {number} params.id - app id + * @param {object} userData - user of http request + * @param {Function} oneConnection - function of xmlrpc + */ +const downloadApp = ( + res = {}, + next = defaultEmptyFunction, + params = {}, + userData = {}, + oneConnection = defaultEmptyFunction +) => { + const { id, token } = params + if (!(Number.isInteger(parseInt(id, 10)) && token)) { + responseHttp(res, next, httpNotFoundRequest) + + return + } + + const userDataFromJWT = + validateAuth({ + headers: { authorization: token }, + }) || {} + const { aud, jti } = userDataFromJWT + + if (!(aud && jti)) { + responseHttp(res, next, httpResponse(unauthorized, '', '')) + + return + } + + const oneConnect = oneConnection(aud, jti) + const callbackNotfound = () => responseHttp(res, next, httpNotFoundRequest) + const market = ({ MARKETPLACE_ID, SOURCE }) => { + Number.isInteger(parseInt(MARKETPLACE_ID, 10)) && + getMarket({ + oneConnect, + id: MARKETPLACE_ID, + success: (MARKET) => { + const drvMessage = `${MARKET}` + const drvMessageBase64 = btoa(drvMessage) + const downloadCmd = `DRV_ACTION=${drvMessageBase64}; ${global.paths.DOWNLOADER} ${SOURCE} -` + const filename = `one-marketplaceapp-${id}` + res.setHeader('Content-Type', 'application/octet-stream') + res.setHeader('Cache-Control', 'no-transform') + res.setHeader( + 'Content-Disposition', + `attachment; filename=${filename}` + ) + const execChild = exec( + downloadCmd, + { maxBuffer: 1024 ** 3 }, + (error) => { + error && + writeInLogger(error) && + messageTerminal({ + color: 'red', + message: 'error download marketapp: %s', + error, + }) + } + ) + execChild.stdout.pipe(res) + }, + error: callbackNotfound, + parseXML: false, + }) + } + getMarketApp({ oneConnect, id, success: market, error: callbackNotfound }) +} + +/** + * Import the marketplace VM or VM TEPLATE to the OpenNebula cloud. * * @param {object} res - http response * @param {Function} next - express stepper @@ -178,58 +260,65 @@ const getTagsDocker = ( /** * Get market APP information. * - * @param {Function} oneConnection - ONE connection - * @param {number} id - ID market app - * @param {Function} success - callback when have data - * @param {Function} error - error callback + * @param {object} config - config + * @param {Function} config.oneConnect - ONE connection + * @param {number} config.id - ID market app + * @param {Function} config.success - callback when have data + * @param {Function} config.error - error callback + * @returns {undefined} one connect */ -const getMarketApp = ( - oneConnection = defaultEmptyFunction, +const getMarketApp = ({ + oneConnect = defaultEmptyFunction, id, success = defaultEmptyFunction, - error = defaultEmptyFunction -) => { - oneConnection( - ActionsMarketApp.MARKETAPP_INFO, - [parseInt(id, 10)], - (err = undefined, marketApp = {}) => { + error = defaultEmptyFunction, +}) => + oneConnect({ + action: ActionsMarketApp.MARKETAPP_INFO, + parameters: [parseInt(id, 10)], + callback: (err = undefined, marketApp = {}) => { if (err || !(marketApp && marketApp.MARKETPLACEAPP)) { error() return } success(marketApp && marketApp.MARKETPLACEAPP) - } - ) -} + }, + }) /** * Get market information. * - * @param {Function} oneConnection - ONE connection - * @param {number} id - ID market - * @param {Function} success - callback when have data - * @param {Function} error - error callback + * @param {object} config - config + * @param {Function} config.oneConnect - ONE connection + * @param {number} config.id - ID market + * @param {Function} config.success - callback when have data + * @param {Function} config.error - error callback + * @param {boolean} config.parseXML - parse XML data + * @returns {undefined} one connect */ -const getMarket = ( - oneConnection = defaultEmptyFunction, +const getMarket = ({ + oneConnect = defaultEmptyFunction, id, success = defaultEmptyFunction, - error = defaultEmptyFunction -) => { - oneConnection( - ActionsMarket.MARKET_INFO, - [parseInt(id, 10)], - (err = undefined, market = {}) => { - if (err || !(market && market.MARKETPLACE)) { + error = defaultEmptyFunction, + parseXML = true, +}) => + oneConnect({ + action: ActionsMarket.MARKET_INFO, + parameters: [parseInt(id, 10)], + callback: (err = undefined, market = {}) => { + if (err || (parseXML && !(market && market.MARKETPLACE))) { error() return } - success(market && market.MARKETPLACE) - } - ) -} + + success(parseXML ? market && market.MARKETPLACE : market) + }, + fillHookResource: false, + parseXML, + }) /** * Get Docker Hub Tags. @@ -254,16 +343,16 @@ const getDockerTags = ( const { id, page } = params const { user, password } = userData if (id && user && password) { - const connect = oneConnection(user, password) + const oneConnect = oneConnection(user, password) const callbackNotfound = () => responseHttp(res, next, httpNotFoundRequest) const callbackBadRequest = () => responseHttp(res, next, httpBadRequest) const market = ({ MARKETPLACE_ID, NAME: MARKETAPP_NAME }) => { - Number.isInteger(parseInt(MARKETPLACE_ID)) && - getMarket( - connect, - MARKETPLACE_ID, - ({ MARKET_MAD }) => { + Number.isInteger(parseInt(MARKETPLACE_ID, 10)) && + getMarket({ + oneConnect, + id: MARKETPLACE_ID, + success: ({ MARKET_MAD }) => { if (MARKET_MAD !== 'dockerhub') { return callbackBadRequest() } @@ -278,10 +367,10 @@ const getDockerTags = ( callbackNotfound ) }, - callbackNotfound - ) + error: callbackNotfound, + }) } - getMarketApp(connect, id, market, callbackNotfound) + getMarketApp({ oneConnect, id, success: market, error: callbackNotfound }) } else { responseHttp(res, next, httpNotFoundRequest) } @@ -289,6 +378,7 @@ const getDockerTags = ( const functionRoutes = { exportApp, + downloadApp, importMarket, getDockerTags, } diff --git a/src/fireedge/src/server/routes/api/marketapp/index.js b/src/fireedge/src/server/routes/api/marketapp/index.js index 9820486b4f..a8c0da8aba 100644 --- a/src/fireedge/src/server/routes/api/marketapp/index.js +++ b/src/fireedge/src/server/routes/api/marketapp/index.js @@ -17,11 +17,17 @@ const { Actions, Commands } = require('server/routes/api/marketapp/routes') const { exportApp, + downloadApp, importMarket, getDockerTags, } = require('server/routes/api/marketapp/functions') -const { MARKETAPP_EXPORT, MARKETAPP_IMPORT, MARKETAPP_DOCKERTAGS } = Actions +const { + MARKETAPP_EXPORT, + MARKETAPP_DOWNLOAD, + MARKETAPP_VMIMPORT, + MARKETAPP_DOCKERTAGS, +} = Actions module.exports = [ { @@ -29,7 +35,11 @@ module.exports = [ action: exportApp, }, { - ...Commands[MARKETAPP_IMPORT], + ...Commands[MARKETAPP_DOWNLOAD], + action: downloadApp, + }, + { + ...Commands[MARKETAPP_VMIMPORT], action: importMarket, }, { diff --git a/src/fireedge/src/server/routes/api/marketapp/routes.js b/src/fireedge/src/server/routes/api/marketapp/routes.js index e335c60992..c735762c3f 100644 --- a/src/fireedge/src/server/routes/api/marketapp/routes.js +++ b/src/fireedge/src/server/routes/api/marketapp/routes.js @@ -15,22 +15,23 @@ * ------------------------------------------------------------------------- */ const { - httpMethod, from: fromData, + httpMethod, } = require('../../../utils/constants/defaults') const { POST, GET } = httpMethod const { query, resource, postBody } = fromData const basepath = '/marketapp' - const MARKETAPP_EXPORT = 'marketapp.export' -const MARKETAPP_IMPORT = 'marketapp.import' +const MARKETAPP_DOWNLOAD = 'marketapp.download' +const MARKETAPP_VMIMPORT = 'marketapp.vmimport' const MARKETAPP_DOCKERTAGS = 'marketapp.dockertags' const Actions = { MARKETAPP_EXPORT, - MARKETAPP_IMPORT, + MARKETAPP_DOWNLOAD, + MARKETAPP_VMIMPORT, MARKETAPP_DOCKERTAGS, } @@ -68,8 +69,21 @@ module.exports = { }, }, }, - [MARKETAPP_IMPORT]: { - path: `${basepath}/import/:resource/:id`, + [MARKETAPP_DOWNLOAD]: { + path: `${basepath}/download/:id`, + httpMethod: GET, + auth: false, + params: { + id: { + from: resource, + }, + token: { + from: query, + }, + }, + }, + [MARKETAPP_VMIMPORT]: { + path: `${basepath}/vmimport/:vmId`, httpMethod: POST, auth: true, params: { diff --git a/src/fireedge/src/server/routes/api/oneflow/service/functions.js b/src/fireedge/src/server/routes/api/oneflow/service/functions.js index 15ed8a9126..34aefcb5c6 100644 --- a/src/fireedge/src/server/routes/api/oneflow/service/functions.js +++ b/src/fireedge/src/server/routes/api/oneflow/service/functions.js @@ -454,10 +454,10 @@ const serviceAddSchedAction = ( params.id, (node = {}, nodesLength, index) => { const oneConnect = oneConnection(user, password) - oneConnect( - ActionVM.VM_SCHED_ADD, - [node.deploy_id, schedTemplate], - (err, value) => { + oneConnect({ + action: ActionVM.VM_SCHED_ADD, + parameters: [node.deploy_id, schedTemplate], + callback: (err, value) => { if (!err && !isNaN(value)) { nodesUpdated.push(node.deploy_id) } @@ -465,8 +465,8 @@ const serviceAddSchedAction = ( success(next, res, nodesUpdated) } }, - false - ) + fillHookResource: false, + }) }, (data = '') => error(next, res, data) ) @@ -518,10 +518,10 @@ const serviceUpdateSchedAction = ( id, (node = {}, nodesLength, index) => { const oneConnect = oneConnection(user, password) - oneConnect( - ActionVM.VM_SCHED_UPDATE, - [node.deploy_id, parseInt(idSched, 10), schedTemplate], - (err, value) => { + oneConnect({ + action: ActionVM.VM_SCHED_UPDATE, + parameters: [node.deploy_id, parseInt(idSched, 10), schedTemplate], + callback: (err, value) => { if (!err && !isNaN(value)) { nodesUpdated.push(node.deploy_id) } @@ -529,8 +529,8 @@ const serviceUpdateSchedAction = ( success(next, res, nodesUpdated) } }, - false - ) + fillHookResource: false, + }) }, (data = '') => error(next, res, data) ) @@ -579,10 +579,10 @@ const serviceDeleteSchedAction = ( id, (node = {}, nodesLength, index) => { const oneConnect = oneConnection(user, password) - oneConnect( - ActionVM.VM_SCHED_DELETE, - [node.deploy_id, parseInt(idSched, 10)], - (err, value) => { + oneConnect({ + action: ActionVM.VM_SCHED_DELETE, + parameters: [node.deploy_id, parseInt(idSched, 10)], + callback: (err, value) => { if (!err && !isNaN(value)) { nodesUpdated.push(node.deploy_id) } @@ -590,8 +590,8 @@ const serviceDeleteSchedAction = ( success(next, res, nodesUpdated) } }, - false - ) + fillHookResource: false, + }) }, (data = '') => error(next, res, data) ) diff --git a/src/fireedge/src/server/routes/api/oneprovision/provision/functions.js b/src/fireedge/src/server/routes/api/oneprovision/provision/functions.js index 529aa27171..dbedc89b77 100644 --- a/src/fireedge/src/server/routes/api/oneprovision/provision/functions.js +++ b/src/fireedge/src/server/routes/api/oneprovision/provision/functions.js @@ -710,12 +710,16 @@ const deleteProvision = ( ) findFolder && removeFile(findFolder) } else { - const connect = oneConnection(user, password) - connect( - Actions.DOCUMENT_UPDATE, - [parseInt(params.id, 10), sprintf(defaultErrorTemplate, lastLine), 1], - defaultEmptyFunction - ) + const oneConnect = oneConnection(user, password) + oneConnect({ + action: Actions.DOCUMENT_UPDATE, + parameters: [ + parseInt(params.id, 10), + sprintf(defaultErrorTemplate, lastLine), + 1, + ], + callback: defaultEmptyFunction, + }) } } @@ -803,14 +807,12 @@ const hostCommand = ( * @param {string} userData.user - username * @param {string} userData.password - user password * @param {number} userData.id - user id - * @param {Function} oneConnection - function of xmlrpc */ const createProvision = ( res = {}, next = defaultEmptyFunction, params = {}, - userData = {}, - oneConnection = defaultEmptyFunction + userData = {} ) => { const basePath = `${global.paths.CPI}/provision` const relFile = `${basePath}/${relName}` diff --git a/src/fireedge/src/server/routes/api/sunstone/functions.js b/src/fireedge/src/server/routes/api/sunstone/functions.js index d6aa715288..c3b728bbce 100644 --- a/src/fireedge/src/server/routes/api/sunstone/functions.js +++ b/src/fireedge/src/server/routes/api/sunstone/functions.js @@ -32,21 +32,21 @@ const httpInternalError = httpResponse(internalServerError, '', '') /** * Get information of opennebula group. * - * @param {Function} connect - xmlrpc function + * @param {Function} oneConnect - xmlrpc function * @param {string} idGroup - id of group * @param {Function} callback - run function when have group information */ const getInfoGroup = ( - connect = defaultEmptyFunction, + oneConnect = defaultEmptyFunction, idGroup, callback = defaultEmptyFunction ) => { - connect( - ActionsGroup.GROUP_INFO, - [parseInt(idGroup, 10), false], + oneConnect({ + action: ActionsGroup.GROUP_INFO, + parameters: [parseInt(idGroup, 10), false], callback, - false - ) + fillHookResource: false, + }) } /** @@ -88,14 +88,14 @@ const getViews = ( global.paths.SUNSTONE_VIEWS && global.paths.SUNSTONE_PATH ) { - const connect = oneConnection(user, password) - connect( - ActionsUser.USER_INFO, - [-1, false], - (err = {}, dataUser = {}) => { + const oneConnect = oneConnection(user, password) + oneConnect({ + action: ActionsUser.USER_INFO, + parameters: [-1, false], + callback: (err = {}, dataUser = {}) => { if (dataUser && dataUser.USER && dataUser.USER.GID) { getInfoGroup( - connect, + oneConnect, dataUser.USER.GID, (err = {}, vmgroupData = {}) => { if (vmgroupData && vmgroupData.GROUP && vmgroupData.GROUP.NAME) { @@ -143,8 +143,8 @@ const getViews = ( responseHttp(res, next, httpInternalError) } }, - false - ) + fillHookResource: false, + }) } else { responseHttp(res, next, httpInternalError) } diff --git a/src/fireedge/src/server/routes/api/system/basepath.js b/src/fireedge/src/server/routes/api/system/basepath.js deleted file mode 100644 index b356f70bfd..0000000000 --- a/src/fireedge/src/server/routes/api/system/basepath.js +++ /dev/null @@ -1,17 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2021, 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. * - * ------------------------------------------------------------------------- */ - -module.exports = 'system' diff --git a/src/fireedge/src/server/routes/api/system/functions.js b/src/fireedge/src/server/routes/api/system/functions.js index 30eccb6295..74b28f1190 100644 --- a/src/fireedge/src/server/routes/api/system/functions.js +++ b/src/fireedge/src/server/routes/api/system/functions.js @@ -76,14 +76,17 @@ const getConfig = ( return } - const connect = oneConnection( + const oneConnect = oneConnection( `${username}:${username}`, tokenWithServerAdmin.token ) - connect( - ActionSystem.SYSTEM_CONFIG, - getDefaultParamsOfOpennebulaCommand(ActionSystem.SYSTEM_CONFIG, GET), - (err, value) => { + oneConnect({ + action: ActionSystem.SYSTEM_CONFIG, + parameters: getDefaultParamsOfOpennebulaCommand( + ActionSystem.SYSTEM_CONFIG, + GET + ), + callback: (err, value) => { if (err) { res.locals.httpCode = httpResponse(internalServerError, '', '') @@ -100,8 +103,8 @@ const getConfig = ( ) res.locals.httpCode = httpResponse(ok, filterData) next() - } - ) + }, + }) } module.exports = { diff --git a/src/fireedge/src/server/routes/api/system/routes.js b/src/fireedge/src/server/routes/api/system/routes.js index dba210ef37..fa59849bb9 100644 --- a/src/fireedge/src/server/routes/api/system/routes.js +++ b/src/fireedge/src/server/routes/api/system/routes.js @@ -15,9 +15,8 @@ * ------------------------------------------------------------------------- */ const { httpMethod } = require('server/utils/constants/defaults') -const SYSTEM = require('server/routes/api/system/basepath') -const basepath = `/${SYSTEM}` +const basepath = '/system' const { GET } = httpMethod const SYSTEM_CONFIG = 'system.config' diff --git a/src/fireedge/src/server/routes/api/vcenter/functions.js b/src/fireedge/src/server/routes/api/vcenter/functions.js index 7a722a3f1a..4e3f461e23 100644 --- a/src/fireedge/src/server/routes/api/vcenter/functions.js +++ b/src/fireedge/src/server/routes/api/vcenter/functions.js @@ -239,7 +239,6 @@ const importHost = (res = {}, next = defaultEmptyFunction, params = {}) => { * @param {Function} next - express stepper * @param {object} params - params of http request * @param {object} userData - user Data - * @param {Function} oneConnection - xmlrpc function * @param {'template'|'images'|'datastores'|'networks'} type - type resource */ const importVobject = ( @@ -247,7 +246,6 @@ const importVobject = ( next = defaultEmptyFunction, params = {}, userData = {}, - oneConnection = defaultEmptyFunction, type ) => { const httpReturn = (httpCode) => { diff --git a/src/fireedge/src/server/routes/api/vm/basepath.js b/src/fireedge/src/server/routes/api/vm/basepath.js deleted file mode 100644 index 11a938d650..0000000000 --- a/src/fireedge/src/server/routes/api/vm/basepath.js +++ /dev/null @@ -1,17 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2021, 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. * - * ------------------------------------------------------------------------- */ - -module.exports = 'vm' diff --git a/src/fireedge/src/server/routes/api/vm/routes.js b/src/fireedge/src/server/routes/api/vm/routes.js index 888f371b44..ae8b3f1977 100644 --- a/src/fireedge/src/server/routes/api/vm/routes.js +++ b/src/fireedge/src/server/routes/api/vm/routes.js @@ -18,9 +18,8 @@ const { httpMethod, from: fromData, } = require('server/utils/constants/defaults') -const VM = require('server/routes/api/vm/basepath') -const basepath = `/${VM}` +const basepath = '/vm' const { POST } = httpMethod const { resource, postBody } = fromData diff --git a/src/fireedge/src/server/routes/api/zendesk/basepath.js b/src/fireedge/src/server/routes/api/zendesk/basepath.js deleted file mode 100644 index 82af8ed713..0000000000 --- a/src/fireedge/src/server/routes/api/zendesk/basepath.js +++ /dev/null @@ -1,17 +0,0 @@ -/* ------------------------------------------------------------------------- * - * Copyright 2002-2021, 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. * - * ------------------------------------------------------------------------- */ - -module.exports = 'zendesk' diff --git a/src/fireedge/src/server/routes/api/zendesk/routes.js b/src/fireedge/src/server/routes/api/zendesk/routes.js index a40dfba47f..b46031d0cc 100644 --- a/src/fireedge/src/server/routes/api/zendesk/routes.js +++ b/src/fireedge/src/server/routes/api/zendesk/routes.js @@ -18,10 +18,9 @@ const { httpMethod, from: fromData, } = require('server/utils/constants/defaults') -const ZENDESK = require('server/routes/api/zendesk/basepath') const { POST, GET, PUT } = httpMethod -const basepath = `/${ZENDESK}` +const basepath = '/zendesk' const { resource, postBody } = fromData const ZENDESK_LOGIN = 'zendesk.login' diff --git a/src/fireedge/src/server/utils/constants/defaults.js b/src/fireedge/src/server/utils/constants/defaults.js index afb6c361a5..365df0e3b4 100644 --- a/src/fireedge/src/server/utils/constants/defaults.js +++ b/src/fireedge/src/server/utils/constants/defaults.js @@ -48,6 +48,7 @@ const defaults = { * @returns {undefined} undefined data */ defaultEmptyFunction: () => undefined, + defaultTmpPath: '/tmp', defaultErrorTemplate: 'ERROR_FIREEDGE="%1$s"', defaultSessionExpiration: 180, defaultSessionLimitExpiration: 30, @@ -91,6 +92,7 @@ const defaults = { query: 'QUERY', postBody: 'POST_BODY', }, + defaultDownloader: 'remotes/datastore/downloader.sh', defaultOpennebulaZones: [ { id: '0', diff --git a/src/fireedge/src/server/utils/index.worker.js b/src/fireedge/src/server/utils/index.worker.js index e07cd96aae..1c6f6a3639 100644 --- a/src/fireedge/src/server/utils/index.worker.js +++ b/src/fireedge/src/server/utils/index.worker.js @@ -50,10 +50,14 @@ onmessage = function (ev = {}) { if (globalState && user && password && rpc && command) { pass = false global.paths = globalState - const connect = opennebulaConnect(user, password, rpc) - connect(command, paramsCommand, (err, value) => { - pass = true - returnDataWorker({ err, value }) + const oneConnect = opennebulaConnect(user, password, rpc) + oneConnect({ + action: command, + parameters: paramsCommand, + callback: (err, value) => { + pass = true + returnDataWorker({ err, value }) + }, }) } } diff --git a/src/fireedge/src/server/utils/opennebula.js b/src/fireedge/src/server/utils/opennebula.js index ef5c2b3942..9309a50588 100644 --- a/src/fireedge/src/server/utils/opennebula.js +++ b/src/fireedge/src/server/utils/opennebula.js @@ -17,7 +17,7 @@ // eslint-disable-next-line node/no-deprecated-api const { parse } = require('url') const rpc = require('xmlrpc') -const parser = require('fast-xml-parser') +const { parse: xmlParse } = require('fast-xml-parser') const { Map } = require('immutable') const { sprintf } = require('sprintf-js') const { global } = require('window-or-global') @@ -63,7 +63,7 @@ const stringWrappedBrakets = /^\[.*\]$/g const brakets = /(^\[)|(\]$)/g /** - * Parse xml to JSON. + * Parse XML to JSON. * * @param {string} xml - xml data in string * @param {Function} callback - callback data @@ -71,7 +71,7 @@ const brakets = /(^\[)|(\]$)/g const xml2json = (xml = '', callback = defaultEmptyFunction) => { let rtn = [] try { - const jsonObj = parser.parse(xml, defaultConfigParseXML) + const jsonObj = xmlParse(xml, defaultConfigParseXML) rtn = [null, jsonObj] } catch (error) { rtn = [error] @@ -135,12 +135,13 @@ const opennebulaConnect = (username = '', password = '', zoneURL = '') => { xmlClient = rpc.createClient(zoneURL) } if (xmlClient && xmlClient.methodCall) { - rtn = ( + rtn = ({ action = '', parameters = [], - callback = () => undefined, - fillHookResource = true - ) => { + callback = defaultEmptyFunction, + fillHookResource = true, + parseXML = true, + }) => { if (action && parameters && Array.isArray(parameters) && callback) { // user config const appConfig = getFireedgeConfig() @@ -150,40 +151,41 @@ const opennebulaConnect = (username = '', password = '', zoneURL = '') => { `${namespace}.${action}`, xmlParameters, (err, value) => { - if (err && err.body) { - xml2json(err.body, (error, result) => { - if (error) { - callback(error, undefined) // error parse xml + const success = (data) => { + fillHookResource && + fillResourceforHookConnection(username, action, parameters) + callback(undefined, data) + } - return - } - if ( - result && - result.methodResponse && - result.methodResponse.fault && - result.methodResponse.fault.value && - result.methodResponse.fault.value.struct && - result.methodResponse.fault.value.struct.member && - Array.isArray( - result.methodResponse.fault.value.struct.member - ) - ) { - const errorData = - result.methodResponse.fault.value.struct.member.find( - (element) => element.value && element.value.string - ) - if (errorData) { - // success - fillHookResource && - fillResourceforHookConnection( - username, - action, - parameters + if (err && err.body) { + parseXML + ? xml2json(err.body, (error, result) => { + if (error) { + callback(error, undefined) // error parse xml + + return + } + if ( + result && + result.methodResponse && + result.methodResponse.fault && + result.methodResponse.fault.value && + result.methodResponse.fault.value.struct && + result.methodResponse.fault.value.struct.member && + Array.isArray( + result.methodResponse.fault.value.struct.member ) - callback(undefined, errorData.value.string) - } - } - }) + ) { + const errorData = + result.methodResponse.fault.value.struct.member.find( + (element) => element.value && element.value.string + ) + if (errorData) { + success(errorData.value.string) + } + } + }) + : success(err.body) return } else if (value && value[0] && value[1]) { @@ -194,26 +196,20 @@ const opennebulaConnect = (username = '', password = '', zoneURL = '') => { messageCall = value } if (typeof messageCall === 'string' && messageCall.length > 0) { - xml2json(messageCall, (error, result) => { - if (error) { - callback(error, undefined) // error parse xml + parseXML + ? xml2json(messageCall, (error, result) => { + if (error) { + callback(error, undefined) // error parse xml - return - } - // success - fillHookResource && - fillResourceforHookConnection( - username, - action, - parameters - ) - callback( - undefined, - error === null && !String(result) - ? JSON.stringify(messageCall) - : result - ) - }) + return + } + success( + error === null && !String(result) + ? JSON.stringify(messageCall) + : result + ) + }) + : success(messageCall) return } diff --git a/src/fireedge/src/server/utils/server.js b/src/fireedge/src/server/utils/server.js index 779df05322..2826cdce14 100644 --- a/src/fireedge/src/server/utils/server.js +++ b/src/fireedge/src/server/utils/server.js @@ -67,6 +67,7 @@ const { defaultSunstoneConfig, defaultProvisionPath, defaultProvisionConfig, + defaultDownloader, defaultEmptyFunction, } = defaults const { internalServerError } = httpCodes @@ -522,6 +523,9 @@ const genPathResources = () => { if (!global.paths.FIREEDGE_LOG) { global.paths.FIREEDGE_LOG = `${LOG_LOCATION}/${defaultLogFilename}` } + if (!global.paths.DOWNLOADER) { + global.paths.DOWNLOADER = `${VAR_LOCATION}/${defaultDownloader}` + } if (!global.paths.SUNSTONE_AUTH_PATH) { global.paths.SUNSTONE_AUTH_PATH = `${VAR_LOCATION}/.one/${defaultSunstoneAuth}` }