From 01c18c28e99c197536313c6e027d63d9b231682a Mon Sep 17 00:00:00 2001 From: baghbidi Date: Tue, 7 May 2019 19:06:09 -0700 Subject: [PATCH 01/17] Bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b1cec6abd..602a85e74 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iofogcontroller", - "version": "1.0.37", + "version": "1.0.38", "description": "ioFog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2018 Edgeworx, Inc.", "main": "./src/main.js", "author": "Saeid Baghbidi", From e5095ef6cdae7b2813ca527b63395d38d1f7cc87 Mon Sep 17 00:00:00 2001 From: baghbidi Date: Thu, 9 May 2019 19:31:51 -0700 Subject: [PATCH 02/17] Delete vritaul-kubelet service --- package.json | 4 - src/cli/config.js | 10 - src/config/constants.js | 1 - src/config/default.json | 3 - src/config/development.json | 3 - src/controllers/kubelet-controller.js | 120 ----- src/routes/kubelet.js | 434 ------------------ src/schemas/config.js | 1 - .../managers/kubelet-access-token-manager.js | 25 - ...90222135632-create-kubelet-access-token.js | 31 -- src/sequelize/models/kubeletaccesstoken.js | 34 -- src/services/iofog-service.js | 24 - src/services/kubelet-access-token-service.js | 48 -- src/services/kubelet-service.js | 421 ----------------- 14 files changed, 1159 deletions(-) delete mode 100644 src/controllers/kubelet-controller.js delete mode 100644 src/routes/kubelet.js delete mode 100644 src/sequelize/managers/kubelet-access-token-manager.js delete mode 100644 src/sequelize/migrations/20190222135632-create-kubelet-access-token.js delete mode 100644 src/sequelize/models/kubeletaccesstoken.js delete mode 100644 src/services/kubelet-access-token-service.js delete mode 100644 src/services/kubelet-service.js diff --git a/package.json b/package.json index 602a85e74..49975594a 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,6 @@ "dependencies": { "@sentry/node": "4.5.3", "body-parser": "1.18.3", - "child_process": "1.0.2", "command-line-args": "5.0.2", "command-line-usage": "5.0.5", "continuation-local-storage": "3.2.1", @@ -70,7 +69,6 @@ "ejs": "2.6.1", "express": "4.16.4", "formidable": "1.2.1", - "fs": "0.0.1-security", "ftp": "0.3.10", "helmet": "3.15.0", "jsonschema": "1.2.4", @@ -80,8 +78,6 @@ "newman": "4.3.1", "nodemailer": "5.1.1", "nodemailer-smtp-transport": "2.7.4", - "os": "0.1.1", - "path": "0.12.7", "portscanner": "2.2.0", "qs": "6.6.0", "request-promise": "^4.2.4", diff --git a/src/cli/config.js b/src/cli/config.js index c74cd193e..7a6f20f59 100644 --- a/src/cli/config.js +++ b/src/cli/config.js @@ -86,10 +86,6 @@ class Config extends BaseCLIHandler { name: 'off', alias: 'f', type: Boolean, description: 'Disable', group: [constants.CMD_DEV_MODE, constants.CMD_EMAIL_ACTIVATION], }, - { - name: 'kubelet', alias: 't', type: String, description: 'iofog-kubelet url', - group: constants.CMD_ADD, - }, ] this.commands = { [constants.CMD_ADD]: 'Add a new config value.', @@ -212,11 +208,6 @@ const _addConfigOption = async function(options) { config.set('Service:LogsFileSize', options.logSize * 1024) onSuccess() }) - - await updateConfig(options.kubelet, 'kubelet', 'Kubelet:Uri', (onSuccess) => { - config.set('Kubelet:Uri', options.kubelet) - onSuccess() - }) } const updateConfig = async function(newConfigValue, cliConfigName, configName, fn) { @@ -247,7 +238,6 @@ const _listConfigOptions = function() { 'Log files directory': config.get('Service:LogsDirectory'), 'Log files size': config.get('Service:LogsFileSize'), 'Dev mode': config.get('Server:DevMode'), - 'Kubelet Url': config.get('Kubelet:Uri'), } const result = Object.keys(configuration) diff --git a/src/config/constants.js b/src/config/constants.js index 9aaa097f1..1098a1c64 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -26,7 +26,6 @@ module.exports = { 'Settings:DefaultJobIntervalSeconds': 120, 'Settings:UserTokenExpirationIntervalSeconds': 3600, 'Settings:FogTokenExpirationIntervalSeconds': 3600, - 'Settings:KubeletTokenExpirationIntervalSeconds': 3600, 'Settings:SchedulerTokenExpirationIntervalSeconds': 3600, 'Settings:FogStatusUpdateIntervalSeconds': 120, 'Settings:FogStatusFrequencySeconds': 60, diff --git a/src/config/default.json b/src/config/default.json index 86087f320..d45cdddc9 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -2,9 +2,6 @@ "App": { "Name": "iofog-controller" }, - "Kubelet": { - "Uri": "http://localhost:1234" - }, "Server": { "Port": 54421, "DevMode": false diff --git a/src/config/development.json b/src/config/development.json index a2ae4f3c1..1a21cf099 100644 --- a/src/config/development.json +++ b/src/config/development.json @@ -2,9 +2,6 @@ "App": { "Name": "iofog-controller-dev" }, - "Kubelet": { - "Uri": "http://localhost:1234" - }, "Server": { "Port": 51121, "DevMode": true diff --git a/src/controllers/kubelet-controller.js b/src/controllers/kubelet-controller.js deleted file mode 100644 index 533d4cd39..000000000 --- a/src/controllers/kubelet-controller.js +++ /dev/null @@ -1,120 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2019 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const KubeletService = require('../services/kubelet-service') -const AuthDecorator = require('../decorators/authorization-decorator') - -const kubeletCreatePodEndPoint = async function(req, user) { - const createPodData = req.body - const fogNodeUuid = req.query.nodeName - - return await KubeletService.kubeletCreatePod(createPodData, fogNodeUuid, user) -} - -const kubeletUpdatePodEndPoint = async function(req, user) { - const uploadPodData = req.body - const fogNodeUuid = req.query.nodeName - - return await KubeletService.kubeletUpdatePod(uploadPodData, fogNodeUuid, user) -} - -const kubeletDeletePodEndPoint = async function(req, user) { - const fogNodeUuid = req.query.nodeName - const podData = req.body - - return await KubeletService.kubeletDeletePod(podData, fogNodeUuid, user) -} - -const kubeletGetPodEndPoint = async function(req, user) { - const namespace = req.query.namespace - const name = req.query.name - const fogNodeUuid = req.query.nodeName - - return await KubeletService.kubeletGetPod(namespace, name, fogNodeUuid, user) -} - -const kubeletGetContainerLogsEndPoint = async function(req, user) { - const namespace = req.query.namespace - const podName = req.query.podName - const containerName = req.query.containerName - const tail = req.query.tail - const fogNodeUuid = req.query.nodeName - - return await KubeletService.kubeletGetContainerLogs(namespace, podName, containerName, tail, fogNodeUuid) -} - -const kubeletGetPodStatusEndPoint = async function(req, user) { - const namespace = req.query.namespace - const name = req.query.name - const fogNodeUuid = req.query.nodeName - - return await KubeletService.kubeletGetPodStatus(namespace, name, fogNodeUuid, user) -} - -const kubeletGetPodsEndPoint = async function(req, user) { - const fogNodeUuid = req.query.nodeName - - return KubeletService.kubeletGetPods(fogNodeUuid, user) -} - -const kubeletGetCapacityEndPoint = async function(req, user) { - const fogNodeUuid = req.query.nodeName - - return await KubeletService.kubeletGetCapacity(fogNodeUuid, user) -} - -const kubeletGetAllocatableEndPoint = async function(req, user) { - const fogNodeUuid = req.query.nodeName - - return await KubeletService.kubeletGetAllocatable(fogNodeUuid, user) -} - -const kubeletGetNodeConditionsEndPoint = async function(req, user) { - const fogNodeUuid = req.query.nodeName - - return await KubeletService.kubeletGetNodeConditions(fogNodeUuid, user) -} - -const kubeletGetNodeAddressesEndPoint = async function(req, user) { - const fogNodeUuid = req.query.nodeName - - return await KubeletService.kubeletGetNodeAddresses(fogNodeUuid, user) -} - -const kubeletGetVkTokenEndPoint = async function(req, user) { - const userId = user.id - - return await KubeletService.kubeletGetVkToken(userId) -} - -const kubeletGetSchedulerTokenEndPoint = async function(req, user) { - const userId = user.id - - return await KubeletService.kubeletGetSchedulerToken(userId) -} - -module.exports = { - kubeletCreatePodEndPoint: AuthDecorator.checkAuthToken(kubeletCreatePodEndPoint), - kubeletUpdatePodEndPoint: AuthDecorator.checkAuthToken(kubeletUpdatePodEndPoint), - kubeletDeletePodEndPoint: AuthDecorator.checkAuthToken(kubeletDeletePodEndPoint), - kubeletGetPodEndPoint: AuthDecorator.checkAuthToken(kubeletGetPodEndPoint), - kubeletGetContainerLogsEndPoint: AuthDecorator.checkAuthToken(kubeletGetContainerLogsEndPoint), - kubeletGetPodStatusEndPoint: AuthDecorator.checkAuthToken(kubeletGetPodStatusEndPoint), - kubeletGetPodsEndPoint: AuthDecorator.checkAuthToken(kubeletGetPodsEndPoint), - kubeletGetCapacityEndPoint: AuthDecorator.checkAuthToken(kubeletGetCapacityEndPoint), - kubeletGetAllocatableEndPoint: AuthDecorator.checkAuthToken(kubeletGetAllocatableEndPoint), - kubeletGetNodeConditionsEndPoint: AuthDecorator.checkAuthToken(kubeletGetNodeConditionsEndPoint), - kubeletGetNodeAddressesEndPoint: AuthDecorator.checkAuthToken(kubeletGetNodeAddressesEndPoint), - kubeletGetVkTokenEndPoint: AuthDecorator.checkAuthToken(kubeletGetVkTokenEndPoint), - kubeletGetSchedulerTokenEndPoint: AuthDecorator.checkAuthToken(kubeletGetSchedulerTokenEndPoint), -} diff --git a/src/routes/kubelet.js b/src/routes/kubelet.js deleted file mode 100644 index d53da7f95..000000000 --- a/src/routes/kubelet.js +++ /dev/null @@ -1,434 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2019 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const constants = require('../helpers/constants') -const Errors = require('../helpers/errors') -const KubeletController = require('../controllers/kubelet-controller') -const logger = require('../logger') -const ResponseDecorator = require('../decorators/response-decorator') - -module.exports = [ - { - method: 'post', - path: '/api/v3/k8s/createPod', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError], - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError], - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError], - }, - ] - - const kubeletCreatePodEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletCreatePodEndPoint, successCode, errorCodes) - const responseObject = await kubeletCreatePodEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'put', - path: '/api/v3/k8s/updatePod', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError], - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError], - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError], - }, - ] - - const kubeletUpdatePodEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletUpdatePodEndPoint, successCode, errorCodes) - const responseObject = await kubeletUpdatePodEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'delete', - path: '/api/v3/k8s/deletePod', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError], - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError], - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError], - }, - ] - - const kubeletDeletePodEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletDeletePodEndPoint, successCode, errorCodes) - const responseObject = await kubeletDeletePodEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'get', - path: '/api/v3/k8s/getPod', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError], - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError], - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError], - }, - ] - - const kubeletGetPodEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetPodEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetPodEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'get', - path: '/api/v3/k8s/getContainerLogs', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError], - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError], - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError], - }, - ] - - const kubeletGetContainerLogsEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetContainerLogsEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetContainerLogsEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'get', - path: '/api/v3/k8s/getPodStatus', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError], - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError], - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError], - }, - ] - - const kubeletGetPodStatusEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetPodStatusEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetPodStatusEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'get', - path: '/api/v3/k8s/getPods', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError], - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError], - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError], - }, - ] - - const kubeletGetPodsEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetPodsEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetPodsEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'get', - path: '/api/v3/k8s/capacity', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError], - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError], - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError], - }, - ] - - const kubeletGetCapacityEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetCapacityEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetCapacityEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'get', - path: '/api/v3/k8s/allocatable', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError], - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError], - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError], - }, - ] - - const kubeletGetAllocatableEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetAllocatableEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetAllocatableEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'get', - path: '/api/v3/k8s/nodeConditions', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError], - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError], - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError], - }, - ] - - const kubeletGetNodeConditionsEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetNodeConditionsEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetNodeConditionsEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'get', - path: '/api/v3/k8s/nodeAddresses', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError], - }, - { - code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError], - }, - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError], - }, - ] - - const kubeletGetNodeAddressesEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetNodeAddressesEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetNodeAddressesEndPoint(req) - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'put', - path: '/api/v3/k8s/vk-token', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.AuthenticationError], - }, - ] - - const kubeletGetVkTokenEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetVkTokenEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetVkTokenEndPoint() - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, - { - method: 'get', - path: '/api/v3/k8s/scheduler-token', - middleware: async (req, res) => { - logger.apiReq(req) - - const successCode = constants.HTTP_CODE_SUCCESS - const errorCodes = [ - { - code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.AuthenticationError], - }, - ] - - const kubeletGetSchedulerTokenEndPoint = ResponseDecorator - .handleErrors(KubeletController.kubeletGetSchedulerTokenEndPoint, successCode, errorCodes) - const responseObject = await kubeletGetSchedulerTokenEndPoint() - - res - .status(responseObject.code) - .send(responseObject.body) - - logger.apiRes({ req: req, res: responseObject }) - }, - }, -] diff --git a/src/schemas/config.js b/src/schemas/config.js index 74c73aab7..34ff05854 100644 --- a/src/schemas/config.js +++ b/src/schemas/config.js @@ -27,7 +27,6 @@ const configUpdate = { 'emailService': { 'type': 'string' }, 'logDir': { 'type': 'string' }, 'logSize': { 'type': 'integer' }, - 'kubelet': { 'type': 'string' }, }, } diff --git a/src/sequelize/managers/kubelet-access-token-manager.js b/src/sequelize/managers/kubelet-access-token-manager.js deleted file mode 100644 index cbe2e32af..000000000 --- a/src/sequelize/managers/kubelet-access-token-manager.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2019 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const BaseManager = require('./base-manager') -const models = require('./../models') -const KubeletAccessToken = models.KubeletAccessToken - -class KubeletAccessTokenManager extends BaseManager { - getEntity() { - return KubeletAccessToken - } -} - -const instance = new KubeletAccessTokenManager() -module.exports = instance diff --git a/src/sequelize/migrations/20190222135632-create-kubelet-access-token.js b/src/sequelize/migrations/20190222135632-create-kubelet-access-token.js deleted file mode 100644 index 68f239e01..000000000 --- a/src/sequelize/migrations/20190222135632-create-kubelet-access-token.js +++ /dev/null @@ -1,31 +0,0 @@ -'use strict' -module.exports = { - up: (queryInterface, Sequelize) => { - return queryInterface.createTable('KubeletAccessTokens', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id', - }, - expirationTime: { - type: Sequelize.BIGINT, - field: 'expiration_time', - }, - token: { - type: Sequelize.TEXT, - field: 'token', - }, - userId: { - type: Sequelize.INTEGER, - field: 'user_id', - references: { model: 'Users', key: 'id' }, - onDelete: 'cascade', - }, - }) - }, - down: (queryInterface, Sequelize) => { - return queryInterface.dropTable('KubeletAccessTokens') - }, -} diff --git a/src/sequelize/models/kubeletaccesstoken.js b/src/sequelize/models/kubeletaccesstoken.js deleted file mode 100644 index 2ee528262..000000000 --- a/src/sequelize/models/kubeletaccesstoken.js +++ /dev/null @@ -1,34 +0,0 @@ -'use strict' -module.exports = (sequelize, DataTypes) => { - const KubeletAccessToken = sequelize.define('KubeletAccessToken', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false, - field: 'id', - }, - expirationTime: { - type: DataTypes.BIGINT, - field: 'expiration_time', - }, - token: { - type: DataTypes.TEXT, - field: 'token', - }, - }, { - timestamps: false, - underscored: true, - }) - KubeletAccessToken.associate = function(models) { - KubeletAccessToken.belongsTo(models.User, { - foreignKey: { - name: 'userId', - field: 'user_id', - }, - as: 'user', - onDelete: 'cascade', - }) - } - return KubeletAccessToken -} diff --git a/src/services/iofog-service.js b/src/services/iofog-service.js index 08c9acc0a..91b51695a 100644 --- a/src/services/iofog-service.js +++ b/src/services/iofog-service.js @@ -11,9 +11,6 @@ * */ -const config = require('../config') -const request = require('request-promise') - const TransactionDecorator = require('../decorators/transaction-decorator') const AppHelper = require('../helpers/app-helper') const FogManager = require('../sequelize/managers/iofog-manager') @@ -79,10 +76,6 @@ async function createFogEndPoint(fogData, user, isCLI, transaction) { await ChangeTrackingService.update(createFogData.uuid, ChangeTrackingService.events.microserviceCommon, transaction) - try { - await informKubelet(fog.uuid, 'POST') - } catch (e) {} - return res } @@ -163,10 +156,6 @@ async function deleteFogEndPoint(fogData, user, isCLI, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) } await _processDeleteCommand(fog, transaction) - - try { - await informKubelet(fog.uuid, 'DELETE') - } catch (e) {} } async function getFog(fogData, user, isCLI, transaction) { @@ -395,19 +384,6 @@ async function _deleteBluetoothMicroserviceByFog(fogData, transaction) { // decorated functions const createFogWithTracking = TrackingDecorator.trackEvent(createFogEndPoint, TrackingEventType.IOFOG_CREATED) -const informKubelet = function(iofogUuid, method) { - const kubeletUri = config.get('Kubelet:Uri') - const options = { - uri: kubeletUri + '/node', - qs: { - uuid: iofogUuid, - }, - method: method, - } - - return request(options) -} - module.exports = { createFogEndPoint: TransactionDecorator.generateTransaction(createFogWithTracking), updateFogEndPoint: TransactionDecorator.generateTransaction(updateFogEndPoint), diff --git a/src/services/kubelet-access-token-service.js b/src/services/kubelet-access-token-service.js deleted file mode 100644 index 532811d7f..000000000 --- a/src/services/kubelet-access-token-service.js +++ /dev/null @@ -1,48 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2019 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const AppHelper = require('../helpers/app-helper') -const KubeletAccessTokenManager = require('../sequelize/managers/kubelet-access-token-manager') - -const Config = require('../config') - -const generateAccessToken = async function(transaction) { - while (true) { - const newAccessToken = AppHelper.generateAccessToken() - const exists = await KubeletAccessTokenManager.findOne({ - token: newAccessToken, - }, transaction) - if (!exists) { - const accessTokenExpiryTime = Date.now() + Config.get('Settings:KubeletTokenExpirationIntervalSeconds') * 9999999 - return { - token: newAccessToken, - expirationTime: accessTokenExpiryTime, - } - } - } -} - -async function updateAccessToken(userId, newAccessToken, transaction) { - return KubeletAccessTokenManager.updateOrCreate({ - userId: userId, - }, { - userId: userId, - token: newAccessToken.token, - expirationTime: newAccessToken.expirationTime, - }, transaction) -} - -module.exports = { - generateAccessToken, - updateAccessToken, -} diff --git a/src/services/kubelet-service.js b/src/services/kubelet-service.js deleted file mode 100644 index d55154947..000000000 --- a/src/services/kubelet-service.js +++ /dev/null @@ -1,421 +0,0 @@ -/* - * ******************************************************************************* - * * Copyright (c) 2019 Edgeworx, Inc. - * * - * * This program and the accompanying materials are made available under the - * * terms of the Eclipse Public License v. 2.0 which is available at - * * http://www.eclipse.org/legal/epl-2.0 - * * - * * SPDX-License-Identifier: EPL-2.0 - * ******************************************************************************* - * - */ - -const moment = require('moment') - -const AppHelper = require('../helpers/app-helper') -const ErrorMessages = require('../helpers/error-messages') -const Errors = require('../helpers/errors') -const FlowService = require('./flow-service') -const FogManager = require('../sequelize/managers/iofog-manager') -const IOFogService = require('./iofog-service') -const KubeletAccessTokenService = require('./kubelet-access-token-service') -const MicroservicesService = require('./microservices-service') -const MicroserviceStatusManager = require('../sequelize/managers/microservice-status-manager') -const SchedulerAccessTokenService = require('./scheduler-access-token-service') -const TransactionDecorator = require('../decorators/transaction-decorator') - -const NODE_CAPACITY = 100 - -const processPodPayload = function (createPodData, fogNodeUuid) { - const msMetadata = JSON.parse(createPodData.metadata.annotations.microservices) - const flowDescription = { - metadata: createPodData, - node: fogNodeUuid, - } - - const flowData = { - name: createPodData.metadata.name, - isActivated: true, - description: Buffer.from(JSON.stringify(flowDescription)).toString('base64'), - } - - const microservices = microservicesTopologicalOrder(msMetadata) - - return { - flowData, - microservices, - } -} - -const kubeletCreatePod = async function (createPodData, fogNodeUuid, user, transaction) { - const podPayload = processPodPayload(createPodData, fogNodeUuid) - const { flowData, microservices } = podPayload - - const flows = await FlowService.getAllFlowsEndPoint(false, transaction) - let flow = flows.flows.find((flow) => flow.name === flowData.name) - if (!flow) { - flow = await FlowService.createFlowEndPoint(flowData, user, false, transaction) - } - - const existingMicroservices = await MicroservicesService.listMicroservicesEndPoint(flow.id, user, false, transaction) - - const microservicesIds = [] - for (const ms of microservices) { - const name = `${flowData.name}-${ms.name}` - const existingMicroservice = existingMicroservices.microservices.find((it) => it.name === name) - if (existingMicroservice) { - microservicesIds.push(existingMicroservice.uuid) - continue - } - - ms.routes = ms.routes || [] - ms.routes = ms.routes.map((route) => { - if (!route.startsWith('@')) { - return route - } - const routeId = route.substr(1) * 1 - const idx = microservices.findIndex((it) => it.originalIndex == routeId) - return microservicesIds[idx] - }) - - const microserviceData = { - name: name, - config: ms.config, - catalogItemId: ms['catalog-item-id'], - flowId: flow.id, - iofogUuid: fogNodeUuid, - rootHostAccess: ms['host-access'], - volumeMappings: ms['volume-mappings'] || [], - ports: ms.ports || [], - routes: ms.routes || [], - } - if (ms.env && ms.env.length > 0) { - microserviceData.env = ms.env - } - if (ms.cmd && ms.cmd.length > 0) { - microserviceData.cmd = ms.cmd - } - - microservice = await MicroservicesService.createMicroserviceEndPoint(microserviceData, user, false, transaction) - microservicesIds.push(microservice.uuid) - } -} - -const kubeletUpdatePod = async function (uploadPodData, fogNodeUuid, user, transaction) { - const podPayload = processPodPayload(createPodData, iofogUuid) - const { flowData, microservices } = podPayload - - const flows = await FlowService.getAllFlowsEndPoint(false, transaction) - const flow = flows.flows.find((flow) => flow.name === flowData.name) - if (!flow) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, flowData.name)) - } - - const existingMicroservices = await MicroservicesService.listMicroservicesEndPoint(flow.id, user, false, transaction) - const msDup = [].concat(microservices) - const toDelete = [] - existingMicroservices.forEach((ms) => { - const name = `${flowData.name}-${ms.name}` - const idx = msDup.findIndex((it) => it.name === name) - - if (!idx) { - toDelete.push(ms) - } else { - toUpdate.push(msDup[idx]) - msDup = msDup.splice(idx, 1) - } - }) - - msDup.map((ms) => { - const name = `${flowData.name}-${ms.name}` - - const microserviceData = { - name: name, - config: ms.config, - catalogItemId: ms['catalog-item-id'], - flowId: flow.id, - iofogUuid: fogNodeUuid, - rootHostAccess: ms['host-access'], - volumeMappings: ms['volume-mappings'] || [], - ports: ms.ports || [], - routes: ms.routes || [], - } - if (ms.env && ms.env.length > 0) { - microserviceData.env = ms.env - } - if (ms.cmd && ms.cmd.length > 0) { - microserviceData.cmd = ms.cmd - } - - return microserviceData - }) -} - -const kubeletDeletePod = async function (podData, fogNodeUuid, user, transaction) { - const flowName = podData.metadata.name - - const flows = await FlowService.getAllFlowsEndPoint(false, transaction) - const flow = flows.flows.find((flow) => flow.name === flowName) - if (!flow) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogNodeUuid)) - } - - const existingMicroservices = await MicroservicesService.listMicroservicesEndPoint(flow.id, user, false, transaction) - existingMicroservices.microservices.forEach(async (ms) => { - await MicroservicesService.deleteMicroserviceEndPoint(ms.uuid, { withCleanup: true }, user, false, transaction) - }) - - await FlowService.deleteFlowEndPoint(flow.id, user, false, transaction) -} - -const kubeletGetPod = async function (namespace, name, fogNodeUuid, user, transaction) { - const flow = await FlowService.getFlowByName(name, user, false, transaction) - - return JSON.parse(Buffer.from(flow.description, 'base64').toString('utf8')).metadata -} - -const kubeletGetContainerLogs = async function (namespace, podName, containerName, tail, fogNodeUuid, user, transaction) { - // Not supported yet -} - -const kubeletGetPodStatus = async function (namespace, name, fogNodeUuid, user, transaction) { - const fog = await FogManager.findOne({ uuid: fogNodeUuid }, transaction) - const changeFrequency = (fog && fog.changeFrequency) || 60 - - const flow = await FlowService.getFlowByName(name, user, false, transaction) - const microservices = await MicroservicesService.listMicroservicesEndPoint(flow.id, user, false, transaction) - const pod = JSON.parse(Buffer.from(flow.description, 'base64').toString('utf8')).metadata - - for (const ms of microservices.microservices) { - const status = await MicroserviceStatusManager.findOne({ microserviceUuid: ms.uuid }, transaction) - ms.status = status.dataValues - ms.status.alive = moment().diff(moment(ms.status.updated_at), 'seconds') <= (changeFrequency * 2) - } - - const phase = microservices.microservices.every((ms) => ms.status.status === 'RUNNING') ? 'Running' : 'Pending' - const alive = microservices.microservices.every((ms) => ms.status.alive) - const status = { - phase: phase, - startTime: (alive && phase === 'Running') ? moment(microservices.microservices[0].startTime).utc().toISOString() : null, - conditions: [ - { - Type: 'PodInitialized', - Status: 'True', - }, - { - Type: 'PodReady', - Status: (alive && phase === 'Running') ? 'True' : 'False', - }, - { - Type: 'PodScheduled', - Status: 'True', - }, - ], - containerStatuses: [], - } - - status.containerStatuses = pod.spec.containers.map((c) => { - const microservice = microservices.microservices.find((ms) => ms.name === `${name}-${c.name}`) - - const containerState = {} - if (!microservice.status.alive) { - containerState.waiting = { reason: 'NOT_RESPONSIVE' } - } else if (microservice.status.status === 'RUNNING') { - containerState.running = { startedAt: moment(microservice.status.startTime).utc().toISOString() } - } else { - containerState.waiting = { reason: microservice.status.status } - } - - return { - name: c.name, - imageID: microservice.uuid, - ready: alive && microservice.status.status === 'RUNNING', - restartCount: 0, - state: containerState, - containerId: microservice.status.containerId, - } - }) - - return status -} - -const kubeletGetPods = async function (fogNodeUuid, user, transaction) { - const flows = await FlowService.getAllFlowsEndPoint(false, transaction) - const pods = flows.flows - .filter((flow) => JSON.parse(Buffer.from(flow.description, 'base64').toString('utf8')).node === fogNodeUuid) - .map((flow) => JSON.parse(Buffer.from(flow.description, 'base64').toString('utf8')).metadata) - - return pods -} - -const kubeletGetCapacity = async function (fogNodeUuid, user, transaction) { - const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) - - return { - cpu: node.cpuLimit, - memory: `${(node.memoryLimit).toFixed(0)}Mi`, - pods: `${NODE_CAPACITY}`, - } -} - -const kubeletGetAllocatable = async function (fogNodeUuid, user, transaction) { - const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) - - const pods = await kubeletGetPods(fogNodeUuid, user, transaction) - const allocatablePods = NODE_CAPACITY - pods.length - - return { - cpu: node.cpuLimit - node.cpuUsage, - memory: `${(node.memoryLimit - node.memoryUsage).toFixed(0)}Mi`, - pods: allocatablePods < 0 ? 0 : allocatablePods, - } -} - -const kubeletGetNodeConditions = async function (fogNodeUuid, user, transaction) { - const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) - const now = moment().utc().toISOString() - const lastStatusTime = node.lastStatusTime ? moment(node.lastStatusTime).utc().toISOString() : null - return [ - { - type: 'Ready', - status: node.daemonStatus === 'RUNNING' ? 'True' : 'False', - lastHeartbeatTime: lastStatusTime, - lastTransitionTime: now, - reason: '', - message: node.daemonStatus, - }, - { - type: 'OutOfDisk', - status: node.diskUsage >= node.diskLimit ? 'True' : 'False', - lastHeartbeatTime: lastStatusTime, - lastTransitionTime: now, - reason: '', - message: `Usage: ${node.diskUsage}, Limit: ${node.diskLimit}`, - }, - { - type: 'MemoryPressure', - status: (node.memoryUsage / node.memoryLimit) >= 0.9 ? 'True' : 'False', - lastHeartbeatTime: lastStatusTime, - lastTransitionTime: now, - reason: '', - message: `Usage: ${node.memoryUsage}, Limit: ${node.memoryLimit}`, - }, - { - type: 'DiskPressure', - status: (node.diskUsage / node.diskLimit) >= 0.9 ? 'True' : 'False', - lastHeartbeatTime: lastStatusTime, - lastTransitionTime: now, - reason: '', - message: `Usage: ${node.diskUsage}, Limit: ${node.diskLimit}`, - }, - { - type: 'NetworkUnavailable', - status: 'False', - lastHeartbeatTime: lastStatusTime, - lastTransitionTime: now, - reason: '', - message: '', - }, - ] -} - -const kubeletGetNodeAddresses = async function (fogNodeUuid, user, transaction) { - const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) - if (!node.ipAddress || node.ipAddress === '0.0.0.0') { - return [] - } - - return [ - { - type: 'InternalIP', - address: node.ipAddress, - }, - ] -} - -const kubeletGetVkToken = async function (userId, transaction) { - const newAccessToken = await KubeletAccessTokenService.generateAccessToken(transaction) - await KubeletAccessTokenService.updateAccessToken(userId, newAccessToken, transaction) - - return { - userId: userId, - token: newAccessToken.token, - } -} - -const kubeletGetSchedulerToken = async function (transaction) { - const newAccessToken = await SchedulerAccessTokenService.generateAccessToken(transaction) - await SchedulerAccessTokenService.updateAccessToken(userId, newAccessToken, transaction) - - return { - userId: userId, - token: newAccessToken.token, - } -} - -const microservicesTopologicalOrder = function (msMetadata) { - const microservices = [] - const graph = [] - msMetadata.forEach((ms, i) => { - graph[i] = { - edges: [], - } - - if (!ms.routes) { - return - } - - ms.routes.forEach((route) => { - if (route.startsWith('@')) { - graph[i].edges.push(route.substr(1)) - } - }) - }) - - const stack = msMetadata.reduce((prev, ms, i) => { - if (graph[i].edges.length === 0) { - return prev.concat(i) - } - - return prev - }, []) - - while (stack.length > 0) { - const n = stack.pop() - microservices.push(n) - graph.forEach((node, i) => { - if (!node.edges.length) { - return - } - - node.edges = node.edges.filter((e) => e != n) - if (!node.edges.length) { - stack.push(i) - } - }) - } - - const hasCircuit = !!graph.filter((node) => !!node.edges.length).length - if (hasCircuit) { - throw new Error('Circular dependency!!!') - } - - return microservices.map((idx) => Object.assign({ originalIndex: idx }, msMetadata[idx])) -} - -module.exports = { - kubeletCreatePod: TransactionDecorator.generateFakeTransaction(kubeletCreatePod), - kubeletUpdatePod: TransactionDecorator.generateFakeTransaction(kubeletUpdatePod), - kubeletDeletePod: TransactionDecorator.generateFakeTransaction(kubeletDeletePod), - kubeletGetPod: TransactionDecorator.generateFakeTransaction(kubeletGetPod), - kubeletGetContainerLogs: TransactionDecorator.generateFakeTransaction(kubeletGetContainerLogs), - kubeletGetPodStatus: TransactionDecorator.generateFakeTransaction(kubeletGetPodStatus), - kubeletGetPods: TransactionDecorator.generateFakeTransaction(kubeletGetPods), - kubeletGetCapacity: TransactionDecorator.generateFakeTransaction(kubeletGetCapacity), - kubeletGetAllocatable: TransactionDecorator.generateFakeTransaction(kubeletGetAllocatable), - kubeletGetNodeConditions: TransactionDecorator.generateFakeTransaction(kubeletGetNodeConditions), - kubeletGetNodeAddresses: TransactionDecorator.generateFakeTransaction(kubeletGetNodeAddresses), - kubeletGetVkToken: TransactionDecorator.generateFakeTransaction(kubeletGetVkToken), - kubeletGetSchedulerToken: TransactionDecorator.generateFakeTransaction(kubeletGetSchedulerToken), -} From 7d7e507f3ee8a7a74c27f99c1ecf61cf7253033b Mon Sep 17 00:00:00 2001 From: baghbidi Date: Tue, 14 May 2019 16:54:49 -0700 Subject: [PATCH 03/17] Fixed re-install issue --- scripts/preuninstall.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/preuninstall.js b/scripts/preuninstall.js index 585b4e30e..4d1e0f582 100644 --- a/scripts/preuninstall.js +++ b/scripts/preuninstall.js @@ -11,6 +11,7 @@ * */ +const execSync = require('child_process').execSync const fs = require('fs') const version = require('../package').version const { backupDBs, backupConfigs, backupTrackingUuid, INSTALLATION_VARIABLES_FILE } = require('./util') @@ -25,6 +26,11 @@ function preuninstall() { backupDBs() backupConfigs() backupTrackingUuid() + + const options = { + stdio: [process.stdin, process.stdout, process.stderr], + } + execSync('iofog-controller stop', options) } module.exports = { From a2c6d8fd95b0aafefef5905778d03ff83841a655 Mon Sep 17 00:00:00 2001 From: baghbidi Date: Tue, 14 May 2019 16:55:05 -0700 Subject: [PATCH 04/17] Fixed update CMD --- package.json | 4 ++-- src/cli/microservice.js | 4 ++-- src/services/microservices-service.js | 1 - 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 49975594a..c25dfabd8 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "ftp": "0.3.10", "helmet": "3.15.0", "jsonschema": "1.2.4", - "moment": "2.24.0", + "moment": "2.20.1", "morgan": "1.9.1", "nconf": "0.10.0", "newman": "4.3.1", @@ -88,7 +88,7 @@ "sqlite3": "4.0.6", "string-format": "2.0.0", "umzug": "2.2.0", - "underscore": "1.9.1", + "underscore": "1.8.3", "winston": "3.1.0", "xss-clean": "0.1.1" }, diff --git a/src/cli/microservice.js b/src/cli/microservice.js index 71125f330..d40ec4218 100644 --- a/src/cli/microservice.js +++ b/src/cli/microservice.js @@ -490,7 +490,7 @@ const _updateMicroserviceObject = function(obj) { rootHostAccess: AppHelper.validateBooleanCliOptions(obj.rootEnable, obj.rootDisable), logSize: obj.logSize, rebuild: obj.rebuild, - arg: obj.cmd, + cmd: obj.cmd, env, } @@ -524,7 +524,7 @@ const _createMicroserviceObject = function(obj) { rootHostAccess: AppHelper.validateBooleanCliOptions(obj.rootEnable, obj.rootDisable), logSize: obj.logSize, routes: obj.routes, - arg: obj.cmd, + cmd: obj.cmd, env, } diff --git a/src/services/microservices-service.js b/src/services/microservices-service.js index f1bb53ac2..65ecee08b 100644 --- a/src/services/microservices-service.js +++ b/src/services/microservices-service.js @@ -1018,7 +1018,6 @@ async function _createPortMappingOverConnector(microservice, portMappingData, us } await MicroservicePublicModeManager.create(msPubModeData, transaction) - await _switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, true, transaction) const publicLink = _buildLink(connector.devMode ? 'http' : 'https', connector.publicIp, connectorPort.port2) return { publicLink: publicLink } From 05e41bd168da59a7e3d7a75bf30e32d792d00c21 Mon Sep 17 00:00:00 2001 From: Saeid Rezaei Baghbidi Date: Wed, 15 May 2019 13:33:03 -0700 Subject: [PATCH 05/17] Add `release/*` to triggers --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index a60b87d9c..e7729c5a2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -5,7 +5,7 @@ trigger: branches: include: - develop - - release + - release/* paths: exclude: - README.md From 0f46a8d51ff26f3aa39f282d0f00e6017c4caa84 Mon Sep 17 00:00:00 2001 From: baghbidi Date: Wed, 15 May 2019 13:49:43 -0700 Subject: [PATCH 06/17] Update `nconf` version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c25dfabd8..c8dc444fb 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "jsonschema": "1.2.4", "moment": "2.20.1", "morgan": "1.9.1", - "nconf": "0.10.0", + "nconf": "0.9.1", "newman": "4.3.1", "nodemailer": "5.1.1", "nodemailer-smtp-transport": "2.7.4", From 29fc4b9ea99290a9b8466e34f1797d6376861784 Mon Sep 17 00:00:00 2001 From: baghbidi Date: Wed, 15 May 2019 21:28:52 -0700 Subject: [PATCH 07/17] Fixed issue with logger causing high CPU usage --- package.json | 1 - src/cli/start.js | 34 +++++++++++++++++++--------------- src/logger/index.js | 27 +-------------------------- src/main.js | 6 ++++-- 4 files changed, 24 insertions(+), 44 deletions(-) diff --git a/package.json b/package.json index c8dc444fb..0ffb0d882 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,6 @@ "eslint": "^5.14.1", "eslint-config-google": "^0.12.0", "mocha": "5.2.0", - "newman": "4.3.1", "nyc": "13.3.0", "sinon": "7.2.3", "sinon-chai": "3.3.0" diff --git a/src/cli/start.js b/src/cli/start.js index 5d6e3f449..9604b9b53 100644 --- a/src/cli/start.js +++ b/src/cli/start.js @@ -31,7 +31,7 @@ class Start extends BaseCLIHandler { if (pid === 0) { this.initDB() daemon.start() - checkDaemon(daemon, configuration) + await checkDaemon(daemon, configuration) } else { logger.cliRes(`iofog-controller already running. PID: ${pid}`) } @@ -49,24 +49,28 @@ class Start extends BaseCLIHandler { } function checkDaemon(daemon, configuration) { - let iterationsCount = 0 - const check = () => { - iterationsCount++ - const pid = daemon.status() - if (pid === 0) { - return logger.error('Error: port is probably allocated, or ssl_key or ssl_cert or intermediate_cert ' + - 'is either missing or invalid.') - } + return new Promise((resolve, reject) => { + let iterationsCount = 0 + const check = () => { + iterationsCount++ + const pid = daemon.status() + if (pid === 0) { + logger.error('Error: port is probably allocated, or ssl_key or ssl_cert or intermediate_cert ' + + 'is either missing or invalid.') + return reject(new Error('Error starting ioFog-Controller')) + } - if (iterationsCount === 5) { - checkServerProtocol(configuration) - return logger.cliRes(`ioFog-Controller has started at pid: ${pid}`) + if (iterationsCount === 5) { + checkServerProtocol(configuration) + logger.cliRes(`ioFog-Controller has started at pid: ${pid}`) + return resolve() + } + + setTimeout(check, 1000) } setTimeout(check, 1000) - } - - setTimeout(check, 1000) + }) } function checkServerProtocol(configuration) { diff --git a/src/logger/index.js b/src/logger/index.js index 009e2706e..3e3ed1408 100644 --- a/src/logger/index.js +++ b/src/logger/index.js @@ -79,38 +79,13 @@ const logger = winston.createLogger({ prepareObjectLogs(), formattedJson() ), - filename: 'iofog-controller.0.log', + filename: 'iofog-controller.log.0', dirname: dirname, maxsize: maxsize, - rotationFormat: function() { - return getFormattedLogName() - }, }), ], }) -// logFileName pattern similar to agent -function getFormattedLogName() { - if (fs.existsSync(dirname)) { - const files = fs.readdirSync(dirname) - const latestFilePath = dirname + '/' + files[0] - - if (files.length === 0 || fs.statSync(latestFilePath).size <= maxsize) { - return '' - } - - files.reverse().forEach((file) => { - const path = dirname + '/' + file - if (fs.existsSync(path)) { - const strNumber = file.replace('iofog-controller.', '').replace('.log', '') - const number = parseInt(strNumber) + 1 - fs.renameSync(path, path.replace(strNumber, number)) - } - }) - } - return '' -} - logger.add(new winston.transports.Console({ level: 'info', format: winston.format((log) => { diff --git a/src/main.js b/src/main.js index 2c32f7c9c..c8eee0f33 100644 --- a/src/main.js +++ b/src/main.js @@ -20,10 +20,12 @@ if (!process.env.NODE_ENV) { const Cli = require('./cli') const daemon = require('./daemon') -function main() { +async function main() { const cli = new Cli() - cli.run(daemon) + await cli.run(daemon) } main() + .then(() => process.exit(0)) + .catch(() => process.exit(1)) From a0427e9d9bdaecec566b92705339d917e503cc86 Mon Sep 17 00:00:00 2001 From: Saeid Rezaei Baghbidi Date: Thu, 16 May 2019 15:15:25 -0700 Subject: [PATCH 08/17] Disable versioning --- azure-pipelines.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e7729c5a2..2fcf212a9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -59,14 +59,6 @@ jobs: echo "npm test" displayName: 'integration tests' - - script: | - git checkout package-lock.json - git config --global user.email "info@edgeworx.io" - git config --global user.name "Azure DevOps" - npm version prerelease --preid=rc-$(Build.BuildNumber) - displayName: 'npm version' - condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/heads/')) - - script: | npm pack displayName: 'npm pack' From 3e42a3bd86ccb9f1c398db28439ba4b410e403c0 Mon Sep 17 00:00:00 2001 From: baghbidi Date: Mon, 27 May 2019 21:10:37 -0700 Subject: [PATCH 09/17] Kubelet support --- package.json | 6 +- src/cli/config.js | 10 + src/config/constants.js | 1 + src/config/default.json | 3 + src/config/development.json | 3 + src/controllers/kubelet-controller.js | 120 +++++ src/logger/index.js | 2 +- src/routes/kubelet.js | 434 ++++++++++++++++++ src/schemas/config.js | 1 + .../managers/kubelet-access-token-manager.js | 25 + ...90222135632-create-kubelet-access-token.js | 31 ++ src/sequelize/models/kubeletaccesstoken.js | 34 ++ src/services/iofog-service.js | 24 + src/services/kubelet-access-token-service.js | 48 ++ src/services/kubelet-service.js | 421 +++++++++++++++++ 15 files changed, 1159 insertions(+), 4 deletions(-) create mode 100644 src/controllers/kubelet-controller.js create mode 100644 src/routes/kubelet.js create mode 100644 src/sequelize/managers/kubelet-access-token-manager.js create mode 100644 src/sequelize/migrations/20190222135632-create-kubelet-access-token.js create mode 100644 src/sequelize/models/kubeletaccesstoken.js create mode 100644 src/services/kubelet-access-token-service.js create mode 100644 src/services/kubelet-service.js diff --git a/package.json b/package.json index 0ffb0d882..387bb71ca 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "nodemailer-smtp-transport": "2.7.4", "portscanner": "2.2.0", "qs": "6.6.0", - "request-promise": "^4.2.4", + "request-promise": "4.2.4", "retry-as-promised": "3.1.0", "semver": "5.6.0", "sequelize": "4.42.0", @@ -98,8 +98,8 @@ "chai": "4.2.0", "chai-as-promised": "7.1.1", "chai-http": "4.2.1", - "eslint": "^5.14.1", - "eslint-config-google": "^0.12.0", + "eslint": "5.14.1", + "eslint-config-google": "0.12.0", "mocha": "5.2.0", "nyc": "13.3.0", "sinon": "7.2.3", diff --git a/src/cli/config.js b/src/cli/config.js index 7a6f20f59..c74cd193e 100644 --- a/src/cli/config.js +++ b/src/cli/config.js @@ -86,6 +86,10 @@ class Config extends BaseCLIHandler { name: 'off', alias: 'f', type: Boolean, description: 'Disable', group: [constants.CMD_DEV_MODE, constants.CMD_EMAIL_ACTIVATION], }, + { + name: 'kubelet', alias: 't', type: String, description: 'iofog-kubelet url', + group: constants.CMD_ADD, + }, ] this.commands = { [constants.CMD_ADD]: 'Add a new config value.', @@ -208,6 +212,11 @@ const _addConfigOption = async function(options) { config.set('Service:LogsFileSize', options.logSize * 1024) onSuccess() }) + + await updateConfig(options.kubelet, 'kubelet', 'Kubelet:Uri', (onSuccess) => { + config.set('Kubelet:Uri', options.kubelet) + onSuccess() + }) } const updateConfig = async function(newConfigValue, cliConfigName, configName, fn) { @@ -238,6 +247,7 @@ const _listConfigOptions = function() { 'Log files directory': config.get('Service:LogsDirectory'), 'Log files size': config.get('Service:LogsFileSize'), 'Dev mode': config.get('Server:DevMode'), + 'Kubelet Url': config.get('Kubelet:Uri'), } const result = Object.keys(configuration) diff --git a/src/config/constants.js b/src/config/constants.js index 1098a1c64..9aaa097f1 100644 --- a/src/config/constants.js +++ b/src/config/constants.js @@ -26,6 +26,7 @@ module.exports = { 'Settings:DefaultJobIntervalSeconds': 120, 'Settings:UserTokenExpirationIntervalSeconds': 3600, 'Settings:FogTokenExpirationIntervalSeconds': 3600, + 'Settings:KubeletTokenExpirationIntervalSeconds': 3600, 'Settings:SchedulerTokenExpirationIntervalSeconds': 3600, 'Settings:FogStatusUpdateIntervalSeconds': 120, 'Settings:FogStatusFrequencySeconds': 60, diff --git a/src/config/default.json b/src/config/default.json index d45cdddc9..86087f320 100644 --- a/src/config/default.json +++ b/src/config/default.json @@ -2,6 +2,9 @@ "App": { "Name": "iofog-controller" }, + "Kubelet": { + "Uri": "http://localhost:1234" + }, "Server": { "Port": 54421, "DevMode": false diff --git a/src/config/development.json b/src/config/development.json index 1a21cf099..a2ae4f3c1 100644 --- a/src/config/development.json +++ b/src/config/development.json @@ -2,6 +2,9 @@ "App": { "Name": "iofog-controller-dev" }, + "Kubelet": { + "Uri": "http://localhost:1234" + }, "Server": { "Port": 51121, "DevMode": true diff --git a/src/controllers/kubelet-controller.js b/src/controllers/kubelet-controller.js new file mode 100644 index 000000000..533d4cd39 --- /dev/null +++ b/src/controllers/kubelet-controller.js @@ -0,0 +1,120 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2019 Edgeworx, Inc. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const KubeletService = require('../services/kubelet-service') +const AuthDecorator = require('../decorators/authorization-decorator') + +const kubeletCreatePodEndPoint = async function(req, user) { + const createPodData = req.body + const fogNodeUuid = req.query.nodeName + + return await KubeletService.kubeletCreatePod(createPodData, fogNodeUuid, user) +} + +const kubeletUpdatePodEndPoint = async function(req, user) { + const uploadPodData = req.body + const fogNodeUuid = req.query.nodeName + + return await KubeletService.kubeletUpdatePod(uploadPodData, fogNodeUuid, user) +} + +const kubeletDeletePodEndPoint = async function(req, user) { + const fogNodeUuid = req.query.nodeName + const podData = req.body + + return await KubeletService.kubeletDeletePod(podData, fogNodeUuid, user) +} + +const kubeletGetPodEndPoint = async function(req, user) { + const namespace = req.query.namespace + const name = req.query.name + const fogNodeUuid = req.query.nodeName + + return await KubeletService.kubeletGetPod(namespace, name, fogNodeUuid, user) +} + +const kubeletGetContainerLogsEndPoint = async function(req, user) { + const namespace = req.query.namespace + const podName = req.query.podName + const containerName = req.query.containerName + const tail = req.query.tail + const fogNodeUuid = req.query.nodeName + + return await KubeletService.kubeletGetContainerLogs(namespace, podName, containerName, tail, fogNodeUuid) +} + +const kubeletGetPodStatusEndPoint = async function(req, user) { + const namespace = req.query.namespace + const name = req.query.name + const fogNodeUuid = req.query.nodeName + + return await KubeletService.kubeletGetPodStatus(namespace, name, fogNodeUuid, user) +} + +const kubeletGetPodsEndPoint = async function(req, user) { + const fogNodeUuid = req.query.nodeName + + return KubeletService.kubeletGetPods(fogNodeUuid, user) +} + +const kubeletGetCapacityEndPoint = async function(req, user) { + const fogNodeUuid = req.query.nodeName + + return await KubeletService.kubeletGetCapacity(fogNodeUuid, user) +} + +const kubeletGetAllocatableEndPoint = async function(req, user) { + const fogNodeUuid = req.query.nodeName + + return await KubeletService.kubeletGetAllocatable(fogNodeUuid, user) +} + +const kubeletGetNodeConditionsEndPoint = async function(req, user) { + const fogNodeUuid = req.query.nodeName + + return await KubeletService.kubeletGetNodeConditions(fogNodeUuid, user) +} + +const kubeletGetNodeAddressesEndPoint = async function(req, user) { + const fogNodeUuid = req.query.nodeName + + return await KubeletService.kubeletGetNodeAddresses(fogNodeUuid, user) +} + +const kubeletGetVkTokenEndPoint = async function(req, user) { + const userId = user.id + + return await KubeletService.kubeletGetVkToken(userId) +} + +const kubeletGetSchedulerTokenEndPoint = async function(req, user) { + const userId = user.id + + return await KubeletService.kubeletGetSchedulerToken(userId) +} + +module.exports = { + kubeletCreatePodEndPoint: AuthDecorator.checkAuthToken(kubeletCreatePodEndPoint), + kubeletUpdatePodEndPoint: AuthDecorator.checkAuthToken(kubeletUpdatePodEndPoint), + kubeletDeletePodEndPoint: AuthDecorator.checkAuthToken(kubeletDeletePodEndPoint), + kubeletGetPodEndPoint: AuthDecorator.checkAuthToken(kubeletGetPodEndPoint), + kubeletGetContainerLogsEndPoint: AuthDecorator.checkAuthToken(kubeletGetContainerLogsEndPoint), + kubeletGetPodStatusEndPoint: AuthDecorator.checkAuthToken(kubeletGetPodStatusEndPoint), + kubeletGetPodsEndPoint: AuthDecorator.checkAuthToken(kubeletGetPodsEndPoint), + kubeletGetCapacityEndPoint: AuthDecorator.checkAuthToken(kubeletGetCapacityEndPoint), + kubeletGetAllocatableEndPoint: AuthDecorator.checkAuthToken(kubeletGetAllocatableEndPoint), + kubeletGetNodeConditionsEndPoint: AuthDecorator.checkAuthToken(kubeletGetNodeConditionsEndPoint), + kubeletGetNodeAddressesEndPoint: AuthDecorator.checkAuthToken(kubeletGetNodeAddressesEndPoint), + kubeletGetVkTokenEndPoint: AuthDecorator.checkAuthToken(kubeletGetVkTokenEndPoint), + kubeletGetSchedulerTokenEndPoint: AuthDecorator.checkAuthToken(kubeletGetSchedulerTokenEndPoint), +} diff --git a/src/logger/index.js b/src/logger/index.js index 3e3ed1408..a3185661c 100644 --- a/src/logger/index.js +++ b/src/logger/index.js @@ -79,7 +79,7 @@ const logger = winston.createLogger({ prepareObjectLogs(), formattedJson() ), - filename: 'iofog-controller.log.0', + filename: 'iofog-controller.log', dirname: dirname, maxsize: maxsize, }), diff --git a/src/routes/kubelet.js b/src/routes/kubelet.js new file mode 100644 index 000000000..d53da7f95 --- /dev/null +++ b/src/routes/kubelet.js @@ -0,0 +1,434 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2019 Edgeworx, Inc. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const constants = require('../helpers/constants') +const Errors = require('../helpers/errors') +const KubeletController = require('../controllers/kubelet-controller') +const logger = require('../logger') +const ResponseDecorator = require('../decorators/response-decorator') + +module.exports = [ + { + method: 'post', + path: '/api/v3/k8s/createPod', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError], + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError], + }, + ] + + const kubeletCreatePodEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletCreatePodEndPoint, successCode, errorCodes) + const responseObject = await kubeletCreatePodEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'put', + path: '/api/v3/k8s/updatePod', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError], + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError], + }, + ] + + const kubeletUpdatePodEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletUpdatePodEndPoint, successCode, errorCodes) + const responseObject = await kubeletUpdatePodEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'delete', + path: '/api/v3/k8s/deletePod', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError], + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError], + }, + ] + + const kubeletDeletePodEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletDeletePodEndPoint, successCode, errorCodes) + const responseObject = await kubeletDeletePodEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'get', + path: '/api/v3/k8s/getPod', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError], + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError], + }, + ] + + const kubeletGetPodEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletGetPodEndPoint, successCode, errorCodes) + const responseObject = await kubeletGetPodEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'get', + path: '/api/v3/k8s/getContainerLogs', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError], + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError], + }, + ] + + const kubeletGetContainerLogsEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletGetContainerLogsEndPoint, successCode, errorCodes) + const responseObject = await kubeletGetContainerLogsEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'get', + path: '/api/v3/k8s/getPodStatus', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError], + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError], + }, + ] + + const kubeletGetPodStatusEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletGetPodStatusEndPoint, successCode, errorCodes) + const responseObject = await kubeletGetPodStatusEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'get', + path: '/api/v3/k8s/getPods', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError], + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError], + }, + ] + + const kubeletGetPodsEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletGetPodsEndPoint, successCode, errorCodes) + const responseObject = await kubeletGetPodsEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'get', + path: '/api/v3/k8s/capacity', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError], + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError], + }, + ] + + const kubeletGetCapacityEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletGetCapacityEndPoint, successCode, errorCodes) + const responseObject = await kubeletGetCapacityEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'get', + path: '/api/v3/k8s/allocatable', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError], + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError], + }, + ] + + const kubeletGetAllocatableEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletGetAllocatableEndPoint, successCode, errorCodes) + const responseObject = await kubeletGetAllocatableEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'get', + path: '/api/v3/k8s/nodeConditions', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError], + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError], + }, + ] + + const kubeletGetNodeConditionsEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletGetNodeConditionsEndPoint, successCode, errorCodes) + const responseObject = await kubeletGetNodeConditionsEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'get', + path: '/api/v3/k8s/nodeAddresses', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError], + }, + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError], + }, + ] + + const kubeletGetNodeAddressesEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletGetNodeAddressesEndPoint, successCode, errorCodes) + const responseObject = await kubeletGetNodeAddressesEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'put', + path: '/api/v3/k8s/vk-token', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.AuthenticationError], + }, + ] + + const kubeletGetVkTokenEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletGetVkTokenEndPoint, successCode, errorCodes) + const responseObject = await kubeletGetVkTokenEndPoint() + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, + { + method: 'get', + path: '/api/v3/k8s/scheduler-token', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.AuthenticationError], + }, + ] + + const kubeletGetSchedulerTokenEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletGetSchedulerTokenEndPoint, successCode, errorCodes) + const responseObject = await kubeletGetSchedulerTokenEndPoint() + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + }, + }, +] diff --git a/src/schemas/config.js b/src/schemas/config.js index 34ff05854..74c73aab7 100644 --- a/src/schemas/config.js +++ b/src/schemas/config.js @@ -27,6 +27,7 @@ const configUpdate = { 'emailService': { 'type': 'string' }, 'logDir': { 'type': 'string' }, 'logSize': { 'type': 'integer' }, + 'kubelet': { 'type': 'string' }, }, } diff --git a/src/sequelize/managers/kubelet-access-token-manager.js b/src/sequelize/managers/kubelet-access-token-manager.js new file mode 100644 index 000000000..cbe2e32af --- /dev/null +++ b/src/sequelize/managers/kubelet-access-token-manager.js @@ -0,0 +1,25 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2019 Edgeworx, Inc. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const BaseManager = require('./base-manager') +const models = require('./../models') +const KubeletAccessToken = models.KubeletAccessToken + +class KubeletAccessTokenManager extends BaseManager { + getEntity() { + return KubeletAccessToken + } +} + +const instance = new KubeletAccessTokenManager() +module.exports = instance diff --git a/src/sequelize/migrations/20190222135632-create-kubelet-access-token.js b/src/sequelize/migrations/20190222135632-create-kubelet-access-token.js new file mode 100644 index 000000000..68f239e01 --- /dev/null +++ b/src/sequelize/migrations/20190222135632-create-kubelet-access-token.js @@ -0,0 +1,31 @@ +'use strict' +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.createTable('KubeletAccessTokens', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id', + }, + expirationTime: { + type: Sequelize.BIGINT, + field: 'expiration_time', + }, + token: { + type: Sequelize.TEXT, + field: 'token', + }, + userId: { + type: Sequelize.INTEGER, + field: 'user_id', + references: { model: 'Users', key: 'id' }, + onDelete: 'cascade', + }, + }) + }, + down: (queryInterface, Sequelize) => { + return queryInterface.dropTable('KubeletAccessTokens') + }, +} diff --git a/src/sequelize/models/kubeletaccesstoken.js b/src/sequelize/models/kubeletaccesstoken.js new file mode 100644 index 000000000..2ee528262 --- /dev/null +++ b/src/sequelize/models/kubeletaccesstoken.js @@ -0,0 +1,34 @@ +'use strict' +module.exports = (sequelize, DataTypes) => { + const KubeletAccessToken = sequelize.define('KubeletAccessToken', { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + field: 'id', + }, + expirationTime: { + type: DataTypes.BIGINT, + field: 'expiration_time', + }, + token: { + type: DataTypes.TEXT, + field: 'token', + }, + }, { + timestamps: false, + underscored: true, + }) + KubeletAccessToken.associate = function(models) { + KubeletAccessToken.belongsTo(models.User, { + foreignKey: { + name: 'userId', + field: 'user_id', + }, + as: 'user', + onDelete: 'cascade', + }) + } + return KubeletAccessToken +} diff --git a/src/services/iofog-service.js b/src/services/iofog-service.js index 91b51695a..bcd27339d 100644 --- a/src/services/iofog-service.js +++ b/src/services/iofog-service.js @@ -11,6 +11,8 @@ * */ +const request = require('request-promise') + const TransactionDecorator = require('../decorators/transaction-decorator') const AppHelper = require('../helpers/app-helper') const FogManager = require('../sequelize/managers/iofog-manager') @@ -27,6 +29,7 @@ const MicroserviceManager = require('../sequelize/managers/microservice-manager' const FogStates = require('../enums/fog-state') const TrackingDecorator = require('../decorators/tracking-decorator') const TrackingEventType = require('../enums/tracking-event-type') +const config = require('../config') async function createFogEndPoint(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogCreate) @@ -76,6 +79,10 @@ async function createFogEndPoint(fogData, user, isCLI, transaction) { await ChangeTrackingService.update(createFogData.uuid, ChangeTrackingService.events.microserviceCommon, transaction) + try { + await informKubelet(fog.uuid, 'POST') + } catch (e) {} + return res } @@ -156,6 +163,10 @@ async function deleteFogEndPoint(fogData, user, isCLI, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogData.uuid)) } await _processDeleteCommand(fog, transaction) + + try { + await informKubelet(fog.uuid, 'DELETE') + } catch (e) {} } async function getFog(fogData, user, isCLI, transaction) { @@ -384,6 +395,19 @@ async function _deleteBluetoothMicroserviceByFog(fogData, transaction) { // decorated functions const createFogWithTracking = TrackingDecorator.trackEvent(createFogEndPoint, TrackingEventType.IOFOG_CREATED) +const informKubelet = function(iofogUuid, method) { + const kubeletUri = config.get('Kubelet:Uri') + const options = { + uri: kubeletUri + '/node', + qs: { + uuid: iofogUuid, + }, + method: method, + } + + return request(options) +} + module.exports = { createFogEndPoint: TransactionDecorator.generateTransaction(createFogWithTracking), updateFogEndPoint: TransactionDecorator.generateTransaction(updateFogEndPoint), diff --git a/src/services/kubelet-access-token-service.js b/src/services/kubelet-access-token-service.js new file mode 100644 index 000000000..532811d7f --- /dev/null +++ b/src/services/kubelet-access-token-service.js @@ -0,0 +1,48 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2019 Edgeworx, Inc. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const AppHelper = require('../helpers/app-helper') +const KubeletAccessTokenManager = require('../sequelize/managers/kubelet-access-token-manager') + +const Config = require('../config') + +const generateAccessToken = async function(transaction) { + while (true) { + const newAccessToken = AppHelper.generateAccessToken() + const exists = await KubeletAccessTokenManager.findOne({ + token: newAccessToken, + }, transaction) + if (!exists) { + const accessTokenExpiryTime = Date.now() + Config.get('Settings:KubeletTokenExpirationIntervalSeconds') * 9999999 + return { + token: newAccessToken, + expirationTime: accessTokenExpiryTime, + } + } + } +} + +async function updateAccessToken(userId, newAccessToken, transaction) { + return KubeletAccessTokenManager.updateOrCreate({ + userId: userId, + }, { + userId: userId, + token: newAccessToken.token, + expirationTime: newAccessToken.expirationTime, + }, transaction) +} + +module.exports = { + generateAccessToken, + updateAccessToken, +} diff --git a/src/services/kubelet-service.js b/src/services/kubelet-service.js new file mode 100644 index 000000000..d55154947 --- /dev/null +++ b/src/services/kubelet-service.js @@ -0,0 +1,421 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2019 Edgeworx, Inc. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const moment = require('moment') + +const AppHelper = require('../helpers/app-helper') +const ErrorMessages = require('../helpers/error-messages') +const Errors = require('../helpers/errors') +const FlowService = require('./flow-service') +const FogManager = require('../sequelize/managers/iofog-manager') +const IOFogService = require('./iofog-service') +const KubeletAccessTokenService = require('./kubelet-access-token-service') +const MicroservicesService = require('./microservices-service') +const MicroserviceStatusManager = require('../sequelize/managers/microservice-status-manager') +const SchedulerAccessTokenService = require('./scheduler-access-token-service') +const TransactionDecorator = require('../decorators/transaction-decorator') + +const NODE_CAPACITY = 100 + +const processPodPayload = function (createPodData, fogNodeUuid) { + const msMetadata = JSON.parse(createPodData.metadata.annotations.microservices) + const flowDescription = { + metadata: createPodData, + node: fogNodeUuid, + } + + const flowData = { + name: createPodData.metadata.name, + isActivated: true, + description: Buffer.from(JSON.stringify(flowDescription)).toString('base64'), + } + + const microservices = microservicesTopologicalOrder(msMetadata) + + return { + flowData, + microservices, + } +} + +const kubeletCreatePod = async function (createPodData, fogNodeUuid, user, transaction) { + const podPayload = processPodPayload(createPodData, fogNodeUuid) + const { flowData, microservices } = podPayload + + const flows = await FlowService.getAllFlowsEndPoint(false, transaction) + let flow = flows.flows.find((flow) => flow.name === flowData.name) + if (!flow) { + flow = await FlowService.createFlowEndPoint(flowData, user, false, transaction) + } + + const existingMicroservices = await MicroservicesService.listMicroservicesEndPoint(flow.id, user, false, transaction) + + const microservicesIds = [] + for (const ms of microservices) { + const name = `${flowData.name}-${ms.name}` + const existingMicroservice = existingMicroservices.microservices.find((it) => it.name === name) + if (existingMicroservice) { + microservicesIds.push(existingMicroservice.uuid) + continue + } + + ms.routes = ms.routes || [] + ms.routes = ms.routes.map((route) => { + if (!route.startsWith('@')) { + return route + } + const routeId = route.substr(1) * 1 + const idx = microservices.findIndex((it) => it.originalIndex == routeId) + return microservicesIds[idx] + }) + + const microserviceData = { + name: name, + config: ms.config, + catalogItemId: ms['catalog-item-id'], + flowId: flow.id, + iofogUuid: fogNodeUuid, + rootHostAccess: ms['host-access'], + volumeMappings: ms['volume-mappings'] || [], + ports: ms.ports || [], + routes: ms.routes || [], + } + if (ms.env && ms.env.length > 0) { + microserviceData.env = ms.env + } + if (ms.cmd && ms.cmd.length > 0) { + microserviceData.cmd = ms.cmd + } + + microservice = await MicroservicesService.createMicroserviceEndPoint(microserviceData, user, false, transaction) + microservicesIds.push(microservice.uuid) + } +} + +const kubeletUpdatePod = async function (uploadPodData, fogNodeUuid, user, transaction) { + const podPayload = processPodPayload(createPodData, iofogUuid) + const { flowData, microservices } = podPayload + + const flows = await FlowService.getAllFlowsEndPoint(false, transaction) + const flow = flows.flows.find((flow) => flow.name === flowData.name) + if (!flow) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, flowData.name)) + } + + const existingMicroservices = await MicroservicesService.listMicroservicesEndPoint(flow.id, user, false, transaction) + const msDup = [].concat(microservices) + const toDelete = [] + existingMicroservices.forEach((ms) => { + const name = `${flowData.name}-${ms.name}` + const idx = msDup.findIndex((it) => it.name === name) + + if (!idx) { + toDelete.push(ms) + } else { + toUpdate.push(msDup[idx]) + msDup = msDup.splice(idx, 1) + } + }) + + msDup.map((ms) => { + const name = `${flowData.name}-${ms.name}` + + const microserviceData = { + name: name, + config: ms.config, + catalogItemId: ms['catalog-item-id'], + flowId: flow.id, + iofogUuid: fogNodeUuid, + rootHostAccess: ms['host-access'], + volumeMappings: ms['volume-mappings'] || [], + ports: ms.ports || [], + routes: ms.routes || [], + } + if (ms.env && ms.env.length > 0) { + microserviceData.env = ms.env + } + if (ms.cmd && ms.cmd.length > 0) { + microserviceData.cmd = ms.cmd + } + + return microserviceData + }) +} + +const kubeletDeletePod = async function (podData, fogNodeUuid, user, transaction) { + const flowName = podData.metadata.name + + const flows = await FlowService.getAllFlowsEndPoint(false, transaction) + const flow = flows.flows.find((flow) => flow.name === flowName) + if (!flow) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_IOFOG_UUID, fogNodeUuid)) + } + + const existingMicroservices = await MicroservicesService.listMicroservicesEndPoint(flow.id, user, false, transaction) + existingMicroservices.microservices.forEach(async (ms) => { + await MicroservicesService.deleteMicroserviceEndPoint(ms.uuid, { withCleanup: true }, user, false, transaction) + }) + + await FlowService.deleteFlowEndPoint(flow.id, user, false, transaction) +} + +const kubeletGetPod = async function (namespace, name, fogNodeUuid, user, transaction) { + const flow = await FlowService.getFlowByName(name, user, false, transaction) + + return JSON.parse(Buffer.from(flow.description, 'base64').toString('utf8')).metadata +} + +const kubeletGetContainerLogs = async function (namespace, podName, containerName, tail, fogNodeUuid, user, transaction) { + // Not supported yet +} + +const kubeletGetPodStatus = async function (namespace, name, fogNodeUuid, user, transaction) { + const fog = await FogManager.findOne({ uuid: fogNodeUuid }, transaction) + const changeFrequency = (fog && fog.changeFrequency) || 60 + + const flow = await FlowService.getFlowByName(name, user, false, transaction) + const microservices = await MicroservicesService.listMicroservicesEndPoint(flow.id, user, false, transaction) + const pod = JSON.parse(Buffer.from(flow.description, 'base64').toString('utf8')).metadata + + for (const ms of microservices.microservices) { + const status = await MicroserviceStatusManager.findOne({ microserviceUuid: ms.uuid }, transaction) + ms.status = status.dataValues + ms.status.alive = moment().diff(moment(ms.status.updated_at), 'seconds') <= (changeFrequency * 2) + } + + const phase = microservices.microservices.every((ms) => ms.status.status === 'RUNNING') ? 'Running' : 'Pending' + const alive = microservices.microservices.every((ms) => ms.status.alive) + const status = { + phase: phase, + startTime: (alive && phase === 'Running') ? moment(microservices.microservices[0].startTime).utc().toISOString() : null, + conditions: [ + { + Type: 'PodInitialized', + Status: 'True', + }, + { + Type: 'PodReady', + Status: (alive && phase === 'Running') ? 'True' : 'False', + }, + { + Type: 'PodScheduled', + Status: 'True', + }, + ], + containerStatuses: [], + } + + status.containerStatuses = pod.spec.containers.map((c) => { + const microservice = microservices.microservices.find((ms) => ms.name === `${name}-${c.name}`) + + const containerState = {} + if (!microservice.status.alive) { + containerState.waiting = { reason: 'NOT_RESPONSIVE' } + } else if (microservice.status.status === 'RUNNING') { + containerState.running = { startedAt: moment(microservice.status.startTime).utc().toISOString() } + } else { + containerState.waiting = { reason: microservice.status.status } + } + + return { + name: c.name, + imageID: microservice.uuid, + ready: alive && microservice.status.status === 'RUNNING', + restartCount: 0, + state: containerState, + containerId: microservice.status.containerId, + } + }) + + return status +} + +const kubeletGetPods = async function (fogNodeUuid, user, transaction) { + const flows = await FlowService.getAllFlowsEndPoint(false, transaction) + const pods = flows.flows + .filter((flow) => JSON.parse(Buffer.from(flow.description, 'base64').toString('utf8')).node === fogNodeUuid) + .map((flow) => JSON.parse(Buffer.from(flow.description, 'base64').toString('utf8')).metadata) + + return pods +} + +const kubeletGetCapacity = async function (fogNodeUuid, user, transaction) { + const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) + + return { + cpu: node.cpuLimit, + memory: `${(node.memoryLimit).toFixed(0)}Mi`, + pods: `${NODE_CAPACITY}`, + } +} + +const kubeletGetAllocatable = async function (fogNodeUuid, user, transaction) { + const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) + + const pods = await kubeletGetPods(fogNodeUuid, user, transaction) + const allocatablePods = NODE_CAPACITY - pods.length + + return { + cpu: node.cpuLimit - node.cpuUsage, + memory: `${(node.memoryLimit - node.memoryUsage).toFixed(0)}Mi`, + pods: allocatablePods < 0 ? 0 : allocatablePods, + } +} + +const kubeletGetNodeConditions = async function (fogNodeUuid, user, transaction) { + const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) + const now = moment().utc().toISOString() + const lastStatusTime = node.lastStatusTime ? moment(node.lastStatusTime).utc().toISOString() : null + return [ + { + type: 'Ready', + status: node.daemonStatus === 'RUNNING' ? 'True' : 'False', + lastHeartbeatTime: lastStatusTime, + lastTransitionTime: now, + reason: '', + message: node.daemonStatus, + }, + { + type: 'OutOfDisk', + status: node.diskUsage >= node.diskLimit ? 'True' : 'False', + lastHeartbeatTime: lastStatusTime, + lastTransitionTime: now, + reason: '', + message: `Usage: ${node.diskUsage}, Limit: ${node.diskLimit}`, + }, + { + type: 'MemoryPressure', + status: (node.memoryUsage / node.memoryLimit) >= 0.9 ? 'True' : 'False', + lastHeartbeatTime: lastStatusTime, + lastTransitionTime: now, + reason: '', + message: `Usage: ${node.memoryUsage}, Limit: ${node.memoryLimit}`, + }, + { + type: 'DiskPressure', + status: (node.diskUsage / node.diskLimit) >= 0.9 ? 'True' : 'False', + lastHeartbeatTime: lastStatusTime, + lastTransitionTime: now, + reason: '', + message: `Usage: ${node.diskUsage}, Limit: ${node.diskLimit}`, + }, + { + type: 'NetworkUnavailable', + status: 'False', + lastHeartbeatTime: lastStatusTime, + lastTransitionTime: now, + reason: '', + message: '', + }, + ] +} + +const kubeletGetNodeAddresses = async function (fogNodeUuid, user, transaction) { + const node = await IOFogService.getFogEndPoint({ uuid: fogNodeUuid }, user, false, transaction) + if (!node.ipAddress || node.ipAddress === '0.0.0.0') { + return [] + } + + return [ + { + type: 'InternalIP', + address: node.ipAddress, + }, + ] +} + +const kubeletGetVkToken = async function (userId, transaction) { + const newAccessToken = await KubeletAccessTokenService.generateAccessToken(transaction) + await KubeletAccessTokenService.updateAccessToken(userId, newAccessToken, transaction) + + return { + userId: userId, + token: newAccessToken.token, + } +} + +const kubeletGetSchedulerToken = async function (transaction) { + const newAccessToken = await SchedulerAccessTokenService.generateAccessToken(transaction) + await SchedulerAccessTokenService.updateAccessToken(userId, newAccessToken, transaction) + + return { + userId: userId, + token: newAccessToken.token, + } +} + +const microservicesTopologicalOrder = function (msMetadata) { + const microservices = [] + const graph = [] + msMetadata.forEach((ms, i) => { + graph[i] = { + edges: [], + } + + if (!ms.routes) { + return + } + + ms.routes.forEach((route) => { + if (route.startsWith('@')) { + graph[i].edges.push(route.substr(1)) + } + }) + }) + + const stack = msMetadata.reduce((prev, ms, i) => { + if (graph[i].edges.length === 0) { + return prev.concat(i) + } + + return prev + }, []) + + while (stack.length > 0) { + const n = stack.pop() + microservices.push(n) + graph.forEach((node, i) => { + if (!node.edges.length) { + return + } + + node.edges = node.edges.filter((e) => e != n) + if (!node.edges.length) { + stack.push(i) + } + }) + } + + const hasCircuit = !!graph.filter((node) => !!node.edges.length).length + if (hasCircuit) { + throw new Error('Circular dependency!!!') + } + + return microservices.map((idx) => Object.assign({ originalIndex: idx }, msMetadata[idx])) +} + +module.exports = { + kubeletCreatePod: TransactionDecorator.generateFakeTransaction(kubeletCreatePod), + kubeletUpdatePod: TransactionDecorator.generateFakeTransaction(kubeletUpdatePod), + kubeletDeletePod: TransactionDecorator.generateFakeTransaction(kubeletDeletePod), + kubeletGetPod: TransactionDecorator.generateFakeTransaction(kubeletGetPod), + kubeletGetContainerLogs: TransactionDecorator.generateFakeTransaction(kubeletGetContainerLogs), + kubeletGetPodStatus: TransactionDecorator.generateFakeTransaction(kubeletGetPodStatus), + kubeletGetPods: TransactionDecorator.generateFakeTransaction(kubeletGetPods), + kubeletGetCapacity: TransactionDecorator.generateFakeTransaction(kubeletGetCapacity), + kubeletGetAllocatable: TransactionDecorator.generateFakeTransaction(kubeletGetAllocatable), + kubeletGetNodeConditions: TransactionDecorator.generateFakeTransaction(kubeletGetNodeConditions), + kubeletGetNodeAddresses: TransactionDecorator.generateFakeTransaction(kubeletGetNodeAddresses), + kubeletGetVkToken: TransactionDecorator.generateFakeTransaction(kubeletGetVkToken), + kubeletGetSchedulerToken: TransactionDecorator.generateFakeTransaction(kubeletGetSchedulerToken), +} From d9e90ddbcfc4fd3372d59c3f0591774349caef78 Mon Sep 17 00:00:00 2001 From: baghbidi Date: Mon, 27 May 2019 21:24:26 -0700 Subject: [PATCH 10/17] Version --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 9e4295795..6df9fff54 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iofogcontroller", - "version": "1.0.37", + "version": "1.0.38", "description": "ioFog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2018 Edgeworx, Inc.", "main": "./src/main.js", "author": "Saeid Baghbidi", @@ -86,7 +86,7 @@ "path": "0.12.7", "portscanner": "2.2.0", "qs": "6.6.0", - "request-promise": "^4.2.4", + "request-promise": "4.2.4", "retry-as-promised": "3.1.0", "semver": "5.6.0", "sequelize": "4.42.0", @@ -105,8 +105,8 @@ "chai": "4.2.0", "chai-as-promised": "7.1.1", "chai-http": "4.2.1", - "eslint": "^5.14.1", - "eslint-config-google": "^0.12.0", + "eslint": "5.14.1", + "eslint-config-google": "0.12.0", "mocha": "5.2.0", "nyc": "13.3.0", "sinon": "7.2.3", From 7ffe57edb75fb69fe8ff3d4e36fdeabb0811e84c Mon Sep 17 00:00:00 2001 From: baghbidi Date: Fri, 31 May 2019 20:16:26 -0700 Subject: [PATCH 11/17] Fixed microservice update bug --- src/services/microservices-service.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/services/microservices-service.js b/src/services/microservices-service.js index 65ecee08b..6aae66b16 100644 --- a/src/services/microservices-service.js +++ b/src/services/microservices-service.js @@ -162,14 +162,16 @@ async function updateMicroserviceEndPoint(microserviceUuid, microserviceData, us throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) } + const iofogUuid = microserviceDataUpdate.iofogUuid || microservice.iofogUuid + if (microserviceDataUpdate.name) { const userId = isCLI ? microservice.userId : user.id await _checkForDuplicateName(microserviceDataUpdate.name, { id: microserviceUuid }, userId, transaction) } // validate fog node - if (microserviceDataUpdate.iofogUuid) { - await IoFogService.getFog({ uuid: microserviceDataUpdate.iofogUuid }, user, isCLI, transaction) + if (iofogUuid) { + await IoFogService.getFog({ uuid: iofogUuid }, user, isCLI, transaction) } const updatedMicroservice = await MicroserviceManager.updateAndFind(query, microserviceDataUpdate, transaction) @@ -189,10 +191,11 @@ async function updateMicroserviceEndPoint(microserviceUuid, microserviceData, us if (microserviceDataUpdate.iofogUuid && microserviceDataUpdate.iofogUuid !== microservice.iofogUuid) { await _moveRoutesToNewFog(updatedMicroservice, microservice.iofogUuid, user, transaction) await _movePublicModesToNewFog(updatedMicroservice, microservice.iofogUuid, user, transaction) + await _updateChangeTracking(true, microservice.iofogUuid, transaction) } // update change tracking for new fog - await _updateChangeTracking(!!microserviceData.config, microserviceDataUpdate.iofogUuid, transaction) + await _updateChangeTracking(!!microserviceData.config, iofogUuid, transaction) } async function _moveRoutesToNewFog(microservice, oldFogUuid, user, transaction) { From 0a150bfdb0c797cc84827403bb5c9d64c77d041c Mon Sep 17 00:00:00 2001 From: Rashmi Modhwadia Date: Tue, 11 Jun 2019 15:00:39 +0530 Subject: [PATCH 12/17] review tags for release candidate --- azure-pipelines.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 0532cb1c2..801b5182f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -111,12 +111,9 @@ jobs: echo "##vso[task.setvariable variable=secondaryTag]$commit" echo "##vso[task.setvariable variable=secondaryTag;isOutput=true]$commit" - echo 'setting primary tag' - echo $(Build.SourceBranch) - if [[ $(Build.SourceBranchName) == 'develop' ]]; then - echo "##vso[task.setvariable variable=primaryTag;isOutput=true]dev-latest" - elif [[ $(Build.SourceBranch) == refs/heads/release* ]]; then - echo "##vso[task.setvariable variable=primaryTag;isOutput=true]rc-latest" + if [[ $(Build.SourceBranch) == refs/heads/release* ]]; then + echo "##vso[task.setvariable variable=primaryTag]rc-$(Build.SourceBranchName)" + echo "##vso[task.setvariable variable=primaryTag;isOutput=true]rc-$(Build.SourceBranchName)" #If branch starts with ref/tags, apply latest and version tag elif [[ $(Build.SourceBranch) == refs/tags* ]]; then primaryTag= echo $(primaryTag) | awk '{print substr($1,2);}' From ca5b549224ec5980555d6405d1721742e4454eba Mon Sep 17 00:00:00 2001 From: Pixcell Date: Wed, 12 Jun 2019 14:37:14 +1200 Subject: [PATCH 13/17] Add connector routes to the Controller API --- src/controllers/connector-controller.js | 40 +++++++ src/routes/connector.js | 132 ++++++++++++++++++++++++ src/routes/controller.js | 20 ++-- src/services/connector-service.js | 34 +++--- 4 files changed, 199 insertions(+), 27 deletions(-) create mode 100644 src/controllers/connector-controller.js create mode 100644 src/routes/connector.js diff --git a/src/controllers/connector-controller.js b/src/controllers/connector-controller.js new file mode 100644 index 000000000..12cea6c61 --- /dev/null +++ b/src/controllers/connector-controller.js @@ -0,0 +1,40 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2018 Edgeworx, Inc. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const ConnectorService = require('../services/connector-service') + +const addConnectorEndPoint = async function (req) { + const connectorData = req.body + return ConnectorService.createConnector(connectorData) +} + +const updateConnectorEndPoint = async function (req) { + const connectorData = req.body + return ConnectorService.updateConnector(connectorData) +} + +const deleteConnectorEndPoint = async function (req) { + const connectorData = req.body + return ConnectorService.deleteConnector(connectorData) +} + +const listConnectorEndPoint = async function (req) { + return ConnectorService.getConnectorList() +} + +module.exports = { + addConnectorEndPoint, + updateConnectorEndPoint, + deleteConnectorEndPoint, + listConnectorEndPoint +} diff --git a/src/routes/connector.js b/src/routes/connector.js new file mode 100644 index 000000000..b9c406edd --- /dev/null +++ b/src/routes/connector.js @@ -0,0 +1,132 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2018 Edgeworx, Inc. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ +const constants = require('../helpers/constants') +const Connector = require('../controllers/connector') +const ResponseDecorator = require('../decorators/response-decorator') +const logger = require('../logger') +const Errors = require('../helpers/errors') + +module.exports = [ + { + method: 'post', + path: '/api/v3/connector', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + const addConnectorEndPoint = ResponseDecorator.handleErrors(Connector.addConnectorEndPoint, successCode, errorCodes) + const responseObject = await addConnectorEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + } + }, + { + method: 'get', + path: '/api/v3/connector', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + } + ] + const listConnectorEndPoint = ResponseDecorator.handleErrors(Connector.listConnectorEndPoint, successCode, errorCodes) + const responseObject = await listConnectorEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + } + }, + { + method: 'put', + path: '/api/v3/connector', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + const updateConnectorEndPoint = ResponseDecorator.handleErrors(Connector.updateConnectorEndPoint, successCode, errorCodes) + const responseObject = await updateConnectorEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + } + }, + { + method: 'delete', + path: '/api/v3/connector', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.NotFoundError] + } + ] + const deleteConnectorEndPoint = ResponseDecorator.handleErrors(Connector.deleteConnectorEndPoint, successCode, errorCodes) + const responseObject = await deleteConnectorEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: responseObject }) + } + } +] diff --git a/src/routes/controller.js b/src/routes/controller.js index ff4e76067..02661a163 100644 --- a/src/routes/controller.js +++ b/src/routes/controller.js @@ -28,11 +28,11 @@ module.exports = [ const responseObject = await statusControllerEndPoint(req) res - .status(responseObject.code) - .send(responseObject.body) + .status(responseObject.code) + .send(responseObject.body) logger.apiRes({ req: req, res: responseObject }) - }, + } }, { method: 'get', @@ -46,11 +46,11 @@ module.exports = [ const responseObject = await emailActivationEndPoint(req) res - .status(responseObject.code) - .send(responseObject.body) + .status(responseObject.code) + .send(responseObject.body) logger.apiRes({ req: req, res: responseObject }) - }, + } }, { method: 'get', @@ -64,10 +64,10 @@ module.exports = [ const responseObject = await fogTypesEndPoint(req) res - .status(responseObject.code) - .send(responseObject.body) + .status(responseObject.code) + .send(responseObject.body) logger.apiRes({ req: req, res: responseObject }) - }, - }, + } + } ] diff --git a/src/services/connector-service.js b/src/services/connector-service.js index 5ee2968b9..0f313fc01 100644 --- a/src/services/connector-service.js +++ b/src/services/connector-service.js @@ -22,37 +22,37 @@ const Op = require('sequelize').Op const ConnectorPortManager = require('../sequelize/managers/connector-port-manager') const MicroserviceService = require('../services/microservices-service') -async function createConnector(connectorData, transaction) { +async function createConnector (connectorData, transaction) { await Validator.validate(connectorData, Validator.schemas.connectorCreate) _validateConnectorData(connectorData) const connector = await ConnectorManager.findOne({ [Op.or]: [ { - name: connectorData.name, + name: connectorData.name }, { - publicIp: connectorData.publicIp, + publicIp: connectorData.publicIp }, { - domain: connectorData.domain, - }, - ], + domain: connectorData.domain + } + ] }, transaction) if (connector) { throw new Errors.ValidationError(ErrorMessages.ALREADY_EXISTS) } - return await ConnectorManager.create(connectorData, transaction) + return ConnectorManager.create(connectorData, transaction) } -async function updateConnector(connectorData, transaction) { +async function updateConnector (connectorData, transaction) { await Validator.validate(connectorData, Validator.schemas.connectorUpdate) _validateConnectorData(connectorData) const queryConnectorData = { - publicIp: connectorData.publicIp, + publicIp: connectorData.publicIp } const connector = await ConnectorManager.findOne({ - publicIp: connectorData.publicIp, + publicIp: connectorData.publicIp }, transaction) if (!connector) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_CONNECTOR_IP, connectorData.publicIp)) @@ -64,10 +64,10 @@ async function updateConnector(connectorData, transaction) { await MicroserviceService.updatePortMappingOverConnector(updatedConnector, transaction) } -async function deleteConnector(connectorData, transaction) { +async function deleteConnector (connectorData, transaction) { await Validator.validate(connectorData, Validator.schemas.connectorDelete) const queryConnectorData = { - publicIp: connectorData.publicIp, + publicIp: connectorData.publicIp } const connector = await ConnectorManager.findOne(queryConnectorData, transaction) if (!connector) { @@ -77,14 +77,14 @@ async function deleteConnector(connectorData, transaction) { if (ports && ports.length > 0) { throw new Errors.ValidationError(ErrorMessages.CONNECTOR_IS_IN_USE) } - await ConnectorManager.delete(queryConnectorData, transaction) + return ConnectorManager.delete(queryConnectorData, transaction) } -async function getConnectorList(transaction) { - return await ConnectorManager.findAll({}, transaction) +async function getConnectorList (transaction) { + return ConnectorManager.findAll({}, transaction) } -function _validateConnectorData(connectorData) { +function _validateConnectorData (connectorData) { if (connectorData.domain) { const validDomain = AppHelper.isValidDomain(connectorData.domain) || AppHelper.isValidPublicIP(connectorData.domain) if (!validDomain) { @@ -101,5 +101,5 @@ module.exports = { createConnector: TransactionDecorator.generateTransaction(createConnector), updateConnector: TransactionDecorator.generateTransaction(updateConnector), deleteConnector: TransactionDecorator.generateTransaction(deleteConnector), - getConnectorList: TransactionDecorator.generateTransaction(getConnectorList), + getConnectorList: TransactionDecorator.generateTransaction(getConnectorList) } From 7446cf980cfcb29a1d97a00186231be6a8c3967d Mon Sep 17 00:00:00 2001 From: Pixcell Date: Wed, 12 Jun 2019 15:05:08 +1200 Subject: [PATCH 14/17] Change success return code of delete to 204 --- src/routes/connector.js | 2 +- src/services/connector-service.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/connector.js b/src/routes/connector.js index b9c406edd..eb1e75060 100644 --- a/src/routes/connector.js +++ b/src/routes/connector.js @@ -104,7 +104,7 @@ module.exports = [ middleware: async (req, res) => { logger.apiReq(req) - const successCode = constants.HTTP_CODE_SUCCESS + const successCode = constants.HTTP_CODE_NO_CONTENT const errorCodes = [ { code: constants.HTTP_CODE_UNAUTHORIZED, diff --git a/src/services/connector-service.js b/src/services/connector-service.js index 0f313fc01..787b785a8 100644 --- a/src/services/connector-service.js +++ b/src/services/connector-service.js @@ -77,7 +77,7 @@ async function deleteConnector (connectorData, transaction) { if (ports && ports.length > 0) { throw new Errors.ValidationError(ErrorMessages.CONNECTOR_IS_IN_USE) } - return ConnectorManager.delete(queryConnectorData, transaction) + await ConnectorManager.delete(queryConnectorData, transaction) } async function getConnectorList (transaction) { From 897f0efe52d56402358553e6f437a41847596bf4 Mon Sep 17 00:00:00 2001 From: Pixcell Date: Wed, 12 Jun 2019 15:35:40 +1200 Subject: [PATCH 15/17] Lint --- src/controllers/connector-controller.js | 2 +- src/routes/connector.js | 52 ++++++++++++------------- src/routes/controller.js | 20 +++++----- src/services/connector-service.js | 18 ++++----- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/controllers/connector-controller.js b/src/controllers/connector-controller.js index 12cea6c61..d96fa1f9c 100644 --- a/src/controllers/connector-controller.js +++ b/src/controllers/connector-controller.js @@ -36,5 +36,5 @@ module.exports = { addConnectorEndPoint, updateConnectorEndPoint, deleteConnectorEndPoint, - listConnectorEndPoint + listConnectorEndPoint, } diff --git a/src/routes/connector.js b/src/routes/connector.js index eb1e75060..45b815b7c 100644 --- a/src/routes/connector.js +++ b/src/routes/connector.js @@ -27,22 +27,22 @@ module.exports = [ const errorCodes = [ { code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] + errors: [Errors.AuthenticationError], }, { code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] - } + errors: [Errors.ValidationError], + }, ] const addConnectorEndPoint = ResponseDecorator.handleErrors(Connector.addConnectorEndPoint, successCode, errorCodes) const responseObject = await addConnectorEndPoint(req) res - .status(responseObject.code) - .send(responseObject.body) + .status(responseObject.code) + .send(responseObject.body) logger.apiRes({ req: req, res: responseObject }) - } + }, }, { method: 'get', @@ -54,18 +54,18 @@ module.exports = [ const errorCodes = [ { code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] - } + errors: [Errors.AuthenticationError], + }, ] const listConnectorEndPoint = ResponseDecorator.handleErrors(Connector.listConnectorEndPoint, successCode, errorCodes) const responseObject = await listConnectorEndPoint(req) res - .status(responseObject.code) - .send(responseObject.body) + .status(responseObject.code) + .send(responseObject.body) logger.apiRes({ req: req, res: responseObject }) - } + }, }, { method: 'put', @@ -77,26 +77,26 @@ module.exports = [ const errorCodes = [ { code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] + errors: [Errors.AuthenticationError], }, { code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] + errors: [Errors.ValidationError], }, { code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } + errors: [Errors.NotFoundError], + }, ] const updateConnectorEndPoint = ResponseDecorator.handleErrors(Connector.updateConnectorEndPoint, successCode, errorCodes) const responseObject = await updateConnectorEndPoint(req) res - .status(responseObject.code) - .send(responseObject.body) + .status(responseObject.code) + .send(responseObject.body) logger.apiRes({ req: req, res: responseObject }) - } + }, }, { method: 'delete', @@ -108,25 +108,25 @@ module.exports = [ const errorCodes = [ { code: constants.HTTP_CODE_UNAUTHORIZED, - errors: [Errors.AuthenticationError] + errors: [Errors.AuthenticationError], }, { code: constants.HTTP_CODE_BAD_REQUEST, - errors: [Errors.ValidationError] + errors: [Errors.ValidationError], }, { code: constants.HTTP_CODE_NOT_FOUND, - errors: [Errors.NotFoundError] - } + errors: [Errors.NotFoundError], + }, ] const deleteConnectorEndPoint = ResponseDecorator.handleErrors(Connector.deleteConnectorEndPoint, successCode, errorCodes) const responseObject = await deleteConnectorEndPoint(req) res - .status(responseObject.code) - .send(responseObject.body) + .status(responseObject.code) + .send(responseObject.body) logger.apiRes({ req: req, res: responseObject }) - } - } + }, + }, ] diff --git a/src/routes/controller.js b/src/routes/controller.js index 02661a163..ff4e76067 100644 --- a/src/routes/controller.js +++ b/src/routes/controller.js @@ -28,11 +28,11 @@ module.exports = [ const responseObject = await statusControllerEndPoint(req) res - .status(responseObject.code) - .send(responseObject.body) + .status(responseObject.code) + .send(responseObject.body) logger.apiRes({ req: req, res: responseObject }) - } + }, }, { method: 'get', @@ -46,11 +46,11 @@ module.exports = [ const responseObject = await emailActivationEndPoint(req) res - .status(responseObject.code) - .send(responseObject.body) + .status(responseObject.code) + .send(responseObject.body) logger.apiRes({ req: req, res: responseObject }) - } + }, }, { method: 'get', @@ -64,10 +64,10 @@ module.exports = [ const responseObject = await fogTypesEndPoint(req) res - .status(responseObject.code) - .send(responseObject.body) + .status(responseObject.code) + .send(responseObject.body) logger.apiRes({ req: req, res: responseObject }) - } - } + }, + }, ] diff --git a/src/services/connector-service.js b/src/services/connector-service.js index 787b785a8..e7e775ed7 100644 --- a/src/services/connector-service.js +++ b/src/services/connector-service.js @@ -28,15 +28,15 @@ async function createConnector (connectorData, transaction) { const connector = await ConnectorManager.findOne({ [Op.or]: [ { - name: connectorData.name + name: connectorData.name, }, { - publicIp: connectorData.publicIp + publicIp: connectorData.publicIp, }, { - domain: connectorData.domain - } - ] + domain: connectorData.domain, + }, + ], }, transaction) if (connector) { throw new Errors.ValidationError(ErrorMessages.ALREADY_EXISTS) @@ -48,11 +48,11 @@ async function updateConnector (connectorData, transaction) { await Validator.validate(connectorData, Validator.schemas.connectorUpdate) _validateConnectorData(connectorData) const queryConnectorData = { - publicIp: connectorData.publicIp + publicIp: connectorData.publicIp, } const connector = await ConnectorManager.findOne({ - publicIp: connectorData.publicIp + publicIp: connectorData.publicIp, }, transaction) if (!connector) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_CONNECTOR_IP, connectorData.publicIp)) @@ -67,7 +67,7 @@ async function updateConnector (connectorData, transaction) { async function deleteConnector (connectorData, transaction) { await Validator.validate(connectorData, Validator.schemas.connectorDelete) const queryConnectorData = { - publicIp: connectorData.publicIp + publicIp: connectorData.publicIp, } const connector = await ConnectorManager.findOne(queryConnectorData, transaction) if (!connector) { @@ -101,5 +101,5 @@ module.exports = { createConnector: TransactionDecorator.generateTransaction(createConnector), updateConnector: TransactionDecorator.generateTransaction(updateConnector), deleteConnector: TransactionDecorator.generateTransaction(deleteConnector), - getConnectorList: TransactionDecorator.generateTransaction(getConnectorList) + getConnectorList: TransactionDecorator.generateTransaction(getConnectorList), } From 1cb99563d8516012bc45fe19e15460e1af5d54a8 Mon Sep 17 00:00:00 2001 From: Pixcell Date: Wed, 12 Jun 2019 16:43:20 +1200 Subject: [PATCH 16/17] Add unit tests and postman tests --- package-lock.json | 2 +- package.json | 1 + scripts/cli-tests.js | 54 +-- src/controllers/connector-controller.js | 5 +- src/routes/connector.js | 2 +- src/services/connector-service.js | 1 + test/postman_collection.json | 361 ++++++++++++++++++++ test/src/services/connector-service.test.js | 10 +- 8 files changed, 401 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index 941bcee1c..fb7fdea01 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "iofogcontroller", - "version": "1.0.37", + "version": "1.0.38", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 6df9fff54..dc536e7e7 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "contributors": [ "Kilton Hopkins ", "Saeid Baghbidi", + "Alexandre de Wergifosse", "Pavel Kazlou", "Egor Krylovich", "Iryna Laryionava", diff --git a/scripts/cli-tests.js b/scripts/cli-tests.js index 53b185ad6..32cf110ff 100644 --- a/scripts/cli-tests.js +++ b/scripts/cli-tests.js @@ -53,7 +53,7 @@ const registryListFields = ['registries'] const tunnelListFields = ['tunnels'] -function testControllerSection() { +function testControllerSection () { console.log('\n=============================\nStarting controller section..') responseHasFields(testCommand('controller status'), controllerStatusFields) @@ -62,7 +62,7 @@ function testControllerSection() { hasSomeResponse(testCommand('controller version')) } -function testUserSection() { +function testUserSection () { console.log('\n=============================\nStarting user section..') responseHasFields(testCommand('user add -f John -l Doe -e user@domain.com -p \'#Bugs4Fun\''), userCreateFields) @@ -75,7 +75,7 @@ function testUserSection() { responseEquals(testCommand('user remove -e user@domain.com'), 'User removed successfully.') } -function testConfigSection() { +function testConfigSection () { console.log('\n=============================\nStarting config section..') // TODO backup config before this command @@ -86,7 +86,7 @@ function testConfigSection() { responseEquals(testCommand('config email-activation -f'), 'Email activation state updated successfully.') } -function testConnectorSection() { +function testConnectorSection () { console.log('\n=============================\nStarting connector section..') responseContains(testCommand('connector add -i 127.0.0.1 -n Connector1 -d iofog.test.org -c testCertPath' + @@ -97,7 +97,7 @@ function testConnectorSection() { responseIsArray(testCommand('connector list')) } -function testTunnelSection() { +function testTunnelSection () { console.log('\n=============================\nStarting tunnel section..') responseContains(testCommand('tunnel update -i testIoFogUuid -u testUsername -p testPassword -s 127.0.0.1 ' + @@ -105,7 +105,7 @@ function testTunnelSection() { responseHasFields(testCommand('tunnel list'), tunnelListFields) } -function testIoFogSection() { +function testIoFogSection () { console.log('\n=============================\nStarting iofog section..') const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e fogUser@domain.com' + @@ -135,7 +135,7 @@ function testIoFogSection() { } } -function testCatalogSection() { +function testCatalogSection () { console.log('\n=============================\nStarting catalog section..') const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e catalogUser@domain.com' + @@ -147,13 +147,13 @@ function testCatalogSection() { try { const catalogCreateResponse = responseHasFields(testCommand('catalog add -n testCatalogItem1 -d testDescription' + - ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' - + registryId + ' -I testInputType -F testInputFormat -O testOutputType -T testOutputFormat ' + + ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' + + registryId + ' -I testInputType -F testInputFormat -O testOutputType -T testOutputFormat ' + '-X \'{}\' -u ' + userId), catalogCreateFields) const catalogId = catalogCreateResponse.id responseEquals(testCommand('catalog update -i ' + catalogId + ' -n testCatalogItem2 -d testDescription' + - ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' - + registryId + ' -I testInputType -F testInputFormat -O testOutputType -T testOutputFormat -X \'{}\''), + ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' + + registryId + ' -I testInputType -F testInputFormat -O testOutputType -T testOutputFormat -X \'{}\''), 'Catalog item has been updated successfully.') responseHasFields(testCommand('catalog list'), catalogListFields) responseHasFields(testCommand('catalog info -i ' + catalogId), catalogCreateFields) @@ -166,7 +166,7 @@ function testCatalogSection() { } } -function testFlowSection() { +function testFlowSection () { console.log('\n=============================\nStarting flow section..') const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e flowUser@domain.com' + @@ -188,7 +188,7 @@ function testFlowSection() { } } -function testMicroserviceSection() { +function testMicroserviceSection () { console.log('\n=============================\nStarting microservice section..') const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e microserviceUser@domain.com' + @@ -200,8 +200,8 @@ function testMicroserviceSection() { const registryId = registryCreateResponse.id const catalogCreateResponse = responseHasFields(executeCommand('catalog add -n testCatalogItem1 -d testDescription' + - ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' - + registryId + ' -I testInputType -F testInputFormat -O testOutputType -T testOutputFormat ' + + ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' + + registryId + ' -I testInputType -F testInputFormat -O testOutputType -T testOutputFormat ' + '-X \'{}\' -u ' + userId), catalogCreateFields) const catalogId = catalogCreateResponse.id @@ -254,7 +254,7 @@ function testMicroserviceSection() { } } -function testRegistrySection() { +function testRegistrySection () { console.log('\n=============================\nStarting registry section..') const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e registryUser@domain.com' + @@ -275,7 +275,7 @@ function testRegistrySection() { } } -function testDiagnosticsSection() { +function testDiagnosticsSection () { console.log('\n=============================\nStarting diagnostics section..') const userCreateResponse = responseHasFields(executeCommand('user add -f John -l Doe -e diagnosticsUser@domain.com' + @@ -287,8 +287,8 @@ function testDiagnosticsSection() { const registryId = registryCreateResponse.id const catalogCreateResponse = responseHasFields(executeCommand('catalog add -n testCatalogItem1 -d testDescription' + - ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' - + registryId + ' -I testInputType -F testInputFormat -O testOutputType -T testOutputFormat ' + + ' -c testCategory -x testIntelImage -a testArmImage -p testPublisher -s 15 -r 15 -t testPicture -g ' + + registryId + ' -I testInputType -F testInputFormat -O testOutputType -T testOutputFormat ' + '-X \'{}\' -u ' + userId), catalogCreateFields) const catalogId = catalogCreateResponse.id @@ -333,27 +333,27 @@ function testDiagnosticsSection() { } } -function testCommand(command) { +function testCommand (command) { console.log('\n Testing command \'' + command + '\'') testsCounter++ return executeCommand(command) } -function executeCommand(command) { +function executeCommand (command) { let response = execSync('node ./src/main.js ' + command, options) response = response.toString() response = response.replace(/\r?\n?/g, '') // remove line breaks return response } -function hasSomeResponse(response) { +function hasSomeResponse (response) { if (response === undefined || response === null) { testsFailed++ console.log('\'hasSomeResponse\' test failed with response: ' + JSON.stringify(response)) } } -function responseIsArray(jsonResponse) { +function responseIsArray (jsonResponse) { try { const response = JSON.parse(jsonResponse) if (!Array.isArray(response)) { @@ -366,7 +366,7 @@ function responseIsArray(jsonResponse) { } } -function responseHasFields(jsonResponse, fields) { +function responseHasFields (jsonResponse, fields) { try { const response = JSON.parse(jsonResponse) for (const field of fields) { @@ -383,21 +383,21 @@ function responseHasFields(jsonResponse, fields) { } } -function responseEquals(response, expectedResponse) { +function responseEquals (response, expectedResponse) { if (response !== expectedResponse) { testsFailed++ console.log('\'responseEquals\' test failed with response: ' + JSON.stringify(response)) } } -function responseContains(response, expectedResponsePart) { +function responseContains (response, expectedResponsePart) { if (!response.includes(expectedResponsePart)) { testsFailed++ console.log('\'responseContains\' test failed with response: ' + JSON.stringify(response)) } } -function cliTest() { +function cliTest () { try { backupDBs() // create new DBs diff --git a/src/controllers/connector-controller.js b/src/controllers/connector-controller.js index d96fa1f9c..ad1d98672 100644 --- a/src/controllers/connector-controller.js +++ b/src/controllers/connector-controller.js @@ -29,7 +29,10 @@ const deleteConnectorEndPoint = async function (req) { } const listConnectorEndPoint = async function (req) { - return ConnectorService.getConnectorList() + const res = await ConnectorService.getConnectorList() + return { + connectors: res, + } } module.exports = { diff --git a/src/routes/connector.js b/src/routes/connector.js index 45b815b7c..cf8ed3f09 100644 --- a/src/routes/connector.js +++ b/src/routes/connector.js @@ -11,7 +11,7 @@ * */ const constants = require('../helpers/constants') -const Connector = require('../controllers/connector') +const Connector = require('../controllers/connector-controller') const ResponseDecorator = require('../decorators/response-decorator') const logger = require('../logger') const Errors = require('../helpers/errors') diff --git a/src/services/connector-service.js b/src/services/connector-service.js index e7e775ed7..b50062c7e 100644 --- a/src/services/connector-service.js +++ b/src/services/connector-service.js @@ -62,6 +62,7 @@ async function updateConnector (connectorData, transaction) { const updatedConnector = await ConnectorManager.findOne({ publicIp: connectorData.publicIp }, transaction) await MicroserviceService.updateRouteOverConnector(updatedConnector, transaction) await MicroserviceService.updatePortMappingOverConnector(updatedConnector, transaction) + return updatedConnector } async function deleteConnector (connectorData, transaction) { diff --git a/test/postman_collection.json b/test/postman_collection.json index 5ba9de1a8..d62b867a0 100644 --- a/test/postman_collection.json +++ b/test/postman_collection.json @@ -676,6 +676,367 @@ } ] }, + { + "name": "Connector", + "item": [ + { + "name": "Create user", + "event": [ + { + "listen": "test", + "script": { + "id": "954febdd-1a16-4db0-bdff-bd13fc50a451", + "exec": [ + "tests[\"Status code is 201\"] = responseCode.code === 201;", + "", + "var data = JSON.parse(responseBody);", + "", + "tests[\"Response validation passed\"] = data.hasOwnProperty('userId') && data.firstName && data.lastName && data.email && data.hasOwnProperty('emailActivated');" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"firstName\": \"John\",\n \"lastName\": \"Doe\",\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + }, + "url": { + "raw": "{{host}}/api/v3/user/signup", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "v3", + "user", + "signup" + ] + } + }, + "response": [] + }, + { + "name": "Login", + "event": [ + { + "listen": "test", + "script": { + "id": "4f7a9f52-12cc-49d0-9e2f-147b6f5cb6fa", + "exec": [ + "tests[\"Status code is 200\"] = responseCode.code === 200;", + "", + "var data = JSON.parse(responseBody);", + "", + "tests[\"Response validation passed\"] = data.accessToken;", + "", + "", + "postman.setGlobalVariable(\"user-token\", data.accessToken);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"user@domain.com\",\n \"password\": \"#Bugs4Fun\"\n}" + }, + "url": { + "raw": "{{host}}/api/v3/user/login", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "v3", + "user", + "login" + ] + } + }, + "response": [] + }, + { + "name": "Add connector", + "event": [ + { + "listen": "test", + "script": { + "id": "4f7a9f52-12cc-49d0-9e2f-147b6f5cb6fa", + "exec": [ + "tests[\"Status code is 200\"] = responseCode.code === 200;", + "", + "var data = JSON.parse(responseBody);", + "", + "tests[\"Response validation passed\"] = data.hasOwnProperty('id');", + "", + "postman.setGlobalVariable(\"connector-ip\", data.publicIp);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{user-token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"MyConnector\",\n \"domain\": \"ConnectorDomain\",\n \"publicIp\": \"1.2.3.4\", \"devMode\": true\n}" + }, + "url": { + "raw": "{{host}}/api/v3/connector", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "v3", + "connector" + ] + } + }, + "response": [] + }, + { + "name": "Get Connectors", + "event": [ + { + "listen": "test", + "script": { + "id": "4f7a9f52-12cc-49d0-9e2f-147b6f5cb6fa", + "exec": [ + "tests[\"Status code is 200\"] = responseCode.code === 200;", + "", + "var data = JSON.parse(responseBody);", + "", + "tests[\"Response validation passed\"] = data.hasOwnProperty('connectors') && data.connectors.length === 1" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{user-token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{host}}/api/v3/connector", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "v3", + "connector" + ] + } + }, + "response": [] + }, + { + "name": "Update Connector", + "event": [ + { + "listen": "test", + "script": { + "id": "4f7a9f52-12cc-49d0-9e2f-147b6f5cb6fa", + "exec": [ + "tests[\"Status code is 200\"] = responseCode.code === 200;", + "", + "var data = JSON.parse(responseBody);", + "", + "tests[\"Response validation passed\"] = data.hasOwnProperty('name') && data.name === 'updatedName';" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PUT", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{user-token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"updatedName\",\n \"publicIp\": \"{{connector-ip}}\" \n}" + }, + "url": { + "raw": "{{host}}/api/v3/connector", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "v3", + "connector" + ] + } + }, + "response": [] + }, + { + "name": "Delete Connector", + "event": [ + { + "listen": "test", + "script": { + "id": "4f7a9f52-12cc-49d0-9e2f-147b6f5cb6fa", + "exec": [ + "tests[\"Status code is 204\"] = responseCode.code === 204;" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Authorization", + "value": "{{user-token}}", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"publicIp\": \"{{connector-ip}}\"\n}" + }, + "url": { + "raw": "{{host}}/api/v3/connector", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "v3", + "connector" + ] + } + }, + "response": [] + }, + { + "name": "Delete user", + "event": [ + { + "listen": "test", + "script": { + "id": "954febdd-1a16-4db0-bdff-bd13fc50a451", + "exec": [ + "tests[\"Status code is 204\"] = responseCode.code === 204;" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Authorization", + "value": "{{user-token}}" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{host}}/api/v3/user/profile", + "host": [ + "{{host}}" + ], + "path": [ + "api", + "v3", + "user", + "profile" + ] + } + }, + "response": [] + } + ], + "description": "Connector collection", + "event": [ + { + "listen": "prerequest", + "script": { + "id": "9bbd4e2b-52b4-459e-b897-da7c1b23bb67", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "b7c4da7e-1045-4b10-9512-01aa7a49939f", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + }, { "name": "Agent", "item": [ diff --git a/test/src/services/connector-service.test.js b/test/src/services/connector-service.test.js index c58211864..af123deba 100644 --- a/test/src/services/connector-service.test.js +++ b/test/src/services/connector-service.test.js @@ -174,7 +174,7 @@ describe('Connector Service', () => { def('isValidDomainResponse', () => false) def('isValidPublicIpResponse', () => true) def('isValidPublicIpResponse2', () => true) - def('updateConnectorResponse', () => Promise.resolve()) + def('updateConnectorResponse', () => Promise.resolve(connectorData)) def('findOneConnectorResponse', () => Promise.resolve(connector)) def('updateRouteOverConnectorResponse', () => Promise.resolve()) def('updatePortMappingOverConnectorResponse', () => Promise.resolve()) @@ -218,7 +218,7 @@ describe('Connector Service', () => { def('isValidDomainResponse', () => error) it(`fails with ${error}`, () => { - return expect($subject).to.eventually.equal(undefined) + return expect($subject).to.eventually.equal(connector) }) }) @@ -232,7 +232,7 @@ describe('Connector Service', () => { def('isValidPublicIpResponse', () => error) it(`fails with ${error}`, () => { - return expect($subject).to.eventually.equal(undefined) + return expect($subject).to.eventually.equal(connector) }) }) @@ -246,7 +246,7 @@ describe('Connector Service', () => { def('isValidPublicIpResponse2', () => error) it(`fails with ${error}`, () => { - return expect($subject).to.eventually.equal(undefined) + return expect($subject).to.eventually.equal(connector) }) }) @@ -310,7 +310,7 @@ describe('Connector Service', () => { context('when MicroserviceService#updatePortMappingOverConnector() succeeds', () => { it('fulfills the promise', () => { - return expect($subject).to.eventually.equal(undefined) + return expect($subject).to.eventually.equal(connector) }) }) }) From 33e61649926e5d89366803f01096d73c9d79380c Mon Sep 17 00:00:00 2001 From: Rashmi Modhwadia Date: Wed, 12 Jun 2019 16:15:26 +0530 Subject: [PATCH 17/17] Update snapshot package version --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 801b5182f..cae6b029a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -93,7 +93,7 @@ jobs: git config --global user.name "Azure DevOps" PACKAGE_VERSION=$(cat package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[\",]//g' | tr -d '[[:space:]]') - npm --no-git-tag-version version $PACKAGE_VERSION-rc-$(Build.BuildNumber) + npm --no-git-tag-version version $PACKAGE_VERSION-b$(Build.BuildId) displayName: 'npm version' - script: |