diff --git a/README.md b/README.md index 1f4b885eb..b9ff96aad 100644 --- a/README.md +++ b/README.md @@ -530,7 +530,7 @@ $ iofog-controller catalog <*command*> <*options*>
"config": "string",
"catalogItemId": 0,
"flowId": 0,
- "ioFogNodeId": "string",
+ "iofogUuid": "string",
"rootHostAccess": true,
"logLimit": 0,
"volumeMappings": [
@@ -558,7 +558,7 @@ $ iofog-controller catalog <*command*> <*options*>
"name": "string",
"config": "string",
"rebuild": true,
- "ioFogNodeId": "string",
+ "iofogUuid": "string",
"rootHostAccess": true,
"logLimit": 0,
"volumeMappings": [
diff --git a/specs/swagger.yml b/specs/swagger.yml index 6e75fb688..47dd9b939 100644 --- a/specs/swagger.yml +++ b/specs/swagger.yml @@ -2907,7 +2907,7 @@ definitions: type: integer flowId: type: integer - ioFogNodeId: + iofogUuid: type: string rootHostAccess: type: boolean @@ -2934,7 +2934,7 @@ definitions: type: string rebuild: type: boolean - ioFogNodeId: + iofogUuid: type: string rootHostAccess: type: boolean diff --git a/src/cli/microservice.js b/src/cli/microservice.js index edabe1d2a..d33e3cce3 100644 --- a/src/cli/microservice.js +++ b/src/cli/microservice.js @@ -26,7 +26,7 @@ const JSON_SCHEMA_ADD = AppHelper.stringifyCliJsonSchema( config: "string", catalogItemId: 0, flowId: 0, - ioFogNodeId: "string", + iofogUuid: "string", rootHostAccess: true, logLimit: 0, volumeMappings: [ @@ -54,7 +54,7 @@ const JSON_SCHEMA_UPDATE = AppHelper.stringifyCliJsonSchema( name: "string", config: "string", rebuild: true, - ioFogNodeId: "string", + iofogUuid: "string", rootHostAccess: true, logLimit: 0, volumeMappings: [ @@ -370,7 +370,7 @@ const _updateMicroserviceObject = function (obj) { const microserviceObj = { name: obj.name, config: obj.config, - ioFogNodeId: obj.iofogId, + iofogUuid: obj.iofogId, rootHostAccess: AppHelper.validateBooleanCliOptions(obj.rootEnable, obj.rootDisable), logLimit: obj.logLimit, rebuild: obj.rebuild @@ -389,7 +389,7 @@ const _createMicroserviceObject = function (obj) { config: obj.config, catalogItemId: parseInt(obj.catalogId), flowId: parseInt(obj.flowId), - ioFogNodeId: obj.iofogId, + iofogUuid: obj.iofogId, rootHostAccess: AppHelper.validateBooleanCliOptions(obj.rootEnable, obj.rootDisable), logLimit: obj.logLimit, routes: obj.routes diff --git a/src/enums/microservice-state.js b/src/enums/microservice-state.js new file mode 100644 index 000000000..4661b0171 --- /dev/null +++ b/src/enums/microservice-state.js @@ -0,0 +1,9 @@ +const microserviceState = { + NOT_RUNNING: 'NOT_RUNNING', + RUNNING: 'RUNNING', + RESTARTING: 'RESTARTING', + STUCK_IN_RESTART: 'STUCK_IN_RESTART', +} + +module.exports = microserviceState; + diff --git a/src/schemas/microservice.js b/src/schemas/microservice.js index 05de1f720..29a1e6239 100644 --- a/src/schemas/microservice.js +++ b/src/schemas/microservice.js @@ -9,7 +9,7 @@ const microserviceCreate = { "config": {"type": "string"}, "catalogItemId": {"type": "integer"}, "flowId": {"type": "integer"}, - "ioFogNodeId": {"type": "string"}, + "iofogUuid": {"type": "string"}, "rootHostAccess": {"type": "boolean"}, "logSize": {"type": "integer"}, "imageSnapshot": {"type": "string"}, @@ -36,7 +36,7 @@ const microserviceUpdate = { }, "config": {"type": "string"}, "rebuild": {"type": "boolean"}, - "ioFogNodeId": {"type": "string"}, + "iofogUuid": {"type": "string"}, "rootHostAccess": {"type": "boolean"}, "logSize": {"type": "integer", "minimum" : 0}, "volumeMappings": { diff --git a/src/sequelize/managers/microservice-manager.js b/src/sequelize/managers/microservice-manager.js index 4b07377af..186e8640b 100644 --- a/src/sequelize/managers/microservice-manager.js +++ b/src/sequelize/managers/microservice-manager.js @@ -24,6 +24,7 @@ const Flow = models.Flow; const User = models.User; const Routing = models.Routing; const Registry = models.Registry; +const MicroserviceStatus = models.MicroserviceStatus; class MicroserviceManager extends BaseManager { getEntity() { @@ -194,6 +195,31 @@ class MicroserviceManager extends BaseManager { }, {transaction: transaction}) } + findOneWithStatus(where, transaction) { + return Microservice.findOne({ + include: [ + { + model: MicroserviceStatus, + as: 'microserviceStatus', + required: true + } + ], + where: where + }, {transaction: transaction}) + } + + findAllWithStatuses(transaction) { + return Microservice.findAll({ + include: [ + { + model: MicroserviceStatus, + as: 'microserviceStatus', + required: true + } + ] + }, {transaction: transaction}) + } + findMicroserviceOnGet(where, transaction) { return Microservice.findOne({ include: [ diff --git a/src/sequelize/managers/microservice-status-manager.js b/src/sequelize/managers/microservice-status-manager.js new file mode 100644 index 000000000..77ad85df4 --- /dev/null +++ b/src/sequelize/managers/microservice-status-manager.js @@ -0,0 +1,25 @@ +/* + * ******************************************************************************* + * * 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 BaseManager = require('../managers/base-manager') +const models = require('./../models'); +const MicroserviceStatus = models.MicroserviceStatus; + +class MicroserviceStatusManager extends BaseManager { + getEntity() { + return MicroserviceStatus; + } +} + +const instance = new MicroserviceStatusManager(); +module.exports = instance; \ No newline at end of file diff --git a/src/sequelize/migrations/20181001062956-create-microservice.js b/src/sequelize/migrations/20181001062956-create-microservice.js index a81ba6043..0d6117b37 100644 --- a/src/sequelize/migrations/20181001062956-create-microservice.js +++ b/src/sequelize/migrations/20181001062956-create-microservice.js @@ -44,7 +44,7 @@ module.exports = { type: Sequelize.TEXT, field: 'image_snapshot' }, - deleteWithCleanUp: { + deleteWithCleanup: { type: Sequelize.BOOLEAN, field: 'delete_with_cleanup' }, diff --git a/src/sequelize/migrations/20181031094923-drop_need_update_col_microservices.js.js b/src/sequelize/migrations/20181031094923-drop-need-update-col-microservices.js similarity index 100% rename from src/sequelize/migrations/20181031094923-drop_need_update_col_microservices.js.js rename to src/sequelize/migrations/20181031094923-drop-need-update-col-microservices.js diff --git a/src/sequelize/migrations/20181102105758-microservice-status-add-missing-time-cols.js b/src/sequelize/migrations/20181102105758-microservice-status-add-missing-time-cols.js new file mode 100644 index 000000000..0844e5fe6 --- /dev/null +++ b/src/sequelize/migrations/20181102105758-microservice-status-add-missing-time-cols.js @@ -0,0 +1,18 @@ +'use strict'; + +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.addColumn('MicroserviceStatuses', + 'operating_duration', + Sequelize.BIGINT + ) + .then(() => queryInterface.addColumn('MicroserviceStatuses', + 'start_time', + Sequelize.BIGINT)); + }, + + down: (queryInterface, Sequelize) => { + return queryInterface.removeColumn('MicroserviceStatuses', 'operating_duration') + .then(() => queryInterface.removeColumn('MicroserviceStatuses', 'start_time')); + } +}; diff --git a/src/sequelize/migrations/20181102163657-microservice-add-col-remove.js b/src/sequelize/migrations/20181102163657-microservice-add-col-remove.js new file mode 100644 index 000000000..6138b1178 --- /dev/null +++ b/src/sequelize/migrations/20181102163657-microservice-add-col-remove.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = { + up: (queryInterface, Sequelize) => { + return queryInterface.addColumn('Microservices', + 'delete', + Sequelize.BOOLEAN + ) + }, + + down: (queryInterface, Sequelize) => { + return queryInterface.removeColumn('Microservices', 'delete') + } +}; diff --git a/src/sequelize/models/microservice.js b/src/sequelize/models/microservice.js index df5d29245..5e64cb234 100644 --- a/src/sequelize/models/microservice.js +++ b/src/sequelize/models/microservice.js @@ -46,7 +46,12 @@ module.exports = (sequelize, DataTypes) => { field: 'image_snapshot', defaultValue: "" }, - deleteWithCleanUp: { + delete: { + type: DataTypes.BOOLEAN, + field: 'delete', + defaultValue: false + }, + deleteWithCleanup: { type: DataTypes.BOOLEAN, field: 'delete_with_cleanup', defaultValue: false @@ -112,7 +117,12 @@ module.exports = (sequelize, DataTypes) => { Microservice.hasMany(models.Routing, { foreignKey: 'source_microservice_uuid', as: 'routes' - }) + }); + + Microservice.hasOne(models.MicroserviceStatus, { + foreignKey: 'microservice_uuid', + as: 'microserviceStatus' + }); }; return Microservice; }; \ No newline at end of file diff --git a/src/sequelize/models/microservicestatus.js b/src/sequelize/models/microservicestatus.js index 5db6d080a..c886603a3 100644 --- a/src/sequelize/models/microservicestatus.js +++ b/src/sequelize/models/microservicestatus.js @@ -10,8 +10,19 @@ module.exports = (sequelize, DataTypes) => { }, status: { type: DataTypes.TEXT, + defaultValue: 'NOT_RUNNING', field: 'status' }, + operatingDuration: { + type: DataTypes.BIGINT, + defaultValue: 0, + field: 'operating_duration' + }, + startTime: { + type: DataTypes.BIGINT, + defaultValue: 0, + field: 'start_time' + }, cpuUsage: { type: DataTypes.FLOAT, defaultValue: 0.000, @@ -24,6 +35,7 @@ module.exports = (sequelize, DataTypes) => { }, containerId: { type: DataTypes.TEXT, + defaultValue: '', field: 'container_id' } }, { @@ -32,7 +44,6 @@ module.exports = (sequelize, DataTypes) => { underscored: true }); MicroserviceStatus.associate = function (models) { - MicroserviceStatus.belongsTo(models.Microservice, { foreignKey: { name: 'microserviceUuid', diff --git a/src/services/agent-service.js b/src/services/agent-service.js index 48ff590b7..ccc679ba3 100644 --- a/src/services/agent-service.js +++ b/src/services/agent-service.js @@ -21,6 +21,7 @@ const ChangeTrackingManager = require('../sequelize/managers/change-tracking-man const FogVersionCommandManager = require('../sequelize/managers/iofog-version-command-manager'); const StraceManager = require('../sequelize/managers/strace-manager'); const RegistryManager = require('../sequelize/managers/registry-manager'); +const MicroserviceStatusManager = require('../sequelize/managers/microservice-status-manager') const Validator = require('../schemas'); const Errors = require('../helpers/errors'); const AppHelper = require('../helpers/app-helper'); @@ -174,7 +175,7 @@ const getAgentConfigChanges = async function (fog, transaction) { const updateAgentStatus = async function (agentStatus, fog, transaction) { await Validator.validate(agentStatus, Validator.schemas.updateAgentStatus); - let update = { + let fogStatus = { daemonStatus: agentStatus.daemonStatus, daemonOperatingDuration: agentStatus.daemonOperatingDuration, daemonLastStart: agentStatus.daemonLastStart, @@ -184,7 +185,6 @@ const updateAgentStatus = async function (agentStatus, fog, transaction) { memoryViolation: agentStatus.memoryViolation, diskViolation: agentStatus.diskViolation, cpuViolation: agentStatus.cpuViolation, - microserviceStatus: agentStatus.microserviceStatus, repositoryCount: agentStatus.repositoryCount, repositoryStatus: agentStatus.repositoryStatus, systemTime: agentStatus.systemTime, @@ -199,11 +199,34 @@ const updateAgentStatus = async function (agentStatus, fog, transaction) { isReadyToUpgrade: agentStatus.isReadyToUpgrade, isReadyToRollback: agentStatus.isReadyToRollback }; - update = AppHelper.deleteUndefinedFields(update); + + fogStatus = AppHelper.deleteUndefinedFields(fogStatus); await FogManager.update({ uuid: fog.uuid - }, update, transaction); + }, fogStatus, transaction); + + await _updateMicroserviceStatuses(JSON.parse(agentStatus.microserviceStatus), transaction); + await MicroserviceService.deleteNotRunningMicroservices(transaction); +}; + + +const _updateMicroserviceStatuses = async function (microserviceStatus, transaction) { + for (status of microserviceStatus) { + let microserviceStatus = { + containerId: status.containerId, + status: status.status, + startTime: status.startTime, + operatingDuration: status.operatingDuration, + cpuUsage: status.cpuUsage, + memoryUsage: status.memoryUsage + }; + microserviceStatus = AppHelper.deleteUndefinedFields(microserviceStatus); + + await MicroserviceStatusManager.update({ + microserviceUuid: status.id + }, microserviceStatus, transaction); + } }; const getAgentMicroservices = async function (fog, transaction) { @@ -230,7 +253,8 @@ const getAgentMicroservices = async function (fog, transaction) { portMappings: microservice.ports, volumeMappings: microservice.volumeMappings, imageSnapshot: microservice.imageSnapshot, - deleteWithCleanUp: microservice.deleteWithCleanUp, + delete: microservice.delete, + deleteWithCleanup: microservice.deleteWithCleanup, routes: routes }; diff --git a/src/services/microservices-service.js b/src/services/microservices-service.js index ab94b91ee..17cabb45c 100644 --- a/src/services/microservices-service.js +++ b/src/services/microservices-service.js @@ -13,7 +13,9 @@ const TransactionDecorator = require('../decorators/transaction-decorator'); const MicroserviceManager = require('../sequelize/managers/microservice-manager'); +const MicroserviceStatusManager = require('../sequelize/managers/microservice-status-manager'); const MicroservicePortManager = require('../sequelize/managers/microservice-port-manager'); +const MicroserviceStates = require('../enums/microservice-state'); const VolumeMappingManager = require('../sequelize/managers/volume-mapping-manager'); const ConnectorManager = require('../sequelize/managers/connector-manager'); const ConnectorPortManager = require('../sequelize/managers/connector-port-manager'); @@ -34,7 +36,7 @@ const _listMicroservices = async function (flowId, user, isCLI, transaction) { if (!isCLI) { await FlowService.getFlow(flowId, user, isCLI, transaction); } - const where = isCLI ? {} : {flowId: flowId}; + const where = isCLI ? {delete: false} : {flowId: flowId, delete: false}; const microservices = await MicroserviceManager.findAllExcludeFields(where, transaction); return { @@ -48,7 +50,7 @@ const _getMicroservice = async function (microserviceUuid, user, isCLI, transact } const microservice = await MicroserviceManager.findOneExcludeFields({ - uuid: microserviceUuid + uuid: microserviceUuid, delete: false }, transaction); if (!microservice) { @@ -75,10 +77,12 @@ const _createMicroserviceOnFog = async function (microserviceData, user, isCLI, await _createRoutes(microserviceData.routes, microservice.uuid, user, transaction); } - if (microserviceData.ioFogNodeId) { - await _updateChangeTracking(false, microservice.uuid, microserviceData.ioFogNodeId, user, isCLI, transaction); + if (microserviceData.iofogUuid) { + await _updateChangeTracking(microserviceData.iofogUuid, transaction); } + await _createMicroserviceStatus(microservice.uuid, transaction); + return { uuid: microservice.uuid } @@ -92,7 +96,7 @@ const _createMicroservice = async function (microserviceData, user, isCLI, trans config: microserviceData.config, catalogItemId: microserviceData.catalogItemId, flowId: microserviceData.flowId, - iofogUuid: microserviceData.ioFogNodeId, + iofogUuid: microserviceData.iofogUuid, rootHostAccess: microserviceData.rootHostAccess, logSize: microserviceData.logLimit, updatedBy: user.id @@ -114,17 +118,10 @@ const _createMicroservice = async function (microserviceData, user, isCLI, trans return await MicroserviceManager.create(newMicroservice, transaction); }; -const _createMicroservicePorts = async function (ports, microserviceUuid, transaction) { - const microservicePortToCreate = { - portInternal: ports.internal, - portExternal: ports.external, - publicMode: ports.publicMode, - microserviceUuid: microserviceUuid - }; - - const microservicePortDataCreate = AppHelper.deleteUndefinedFields(microservicePortToCreate); - - await MicroservicePortManager.create(microservicePortDataCreate, transaction); +const _createMicroserviceStatus = function (uuid, transaction) { + return MicroserviceStatusManager.create({ + microserviceUuid: uuid + }, transaction); }; const _createVolumeMappings = async function (volumeMappings, microserviceUuid, transaction) { @@ -149,7 +146,7 @@ const _updateMicroservice = async function (microserviceUuid, microserviceData, name: microserviceData.name, config: microserviceData.config, rebuild: microserviceData.rebuild, - iofogUuid: microserviceData.ioFogNodeId, + iofogUuid: microserviceData.iofogUuid, rootHostAccess: microserviceData.rootHostAccess, logSize: microserviceData.logLimit, volumeMappings: microserviceData.volumeMappings, @@ -180,13 +177,13 @@ const _updateMicroservice = async function (microserviceUuid, microserviceData, await _updateVolumeMappings(microserviceDataUpdate.volumeMappings, microserviceUuid, transaction); } - if (microserviceDataUpdate.ioFogNodeId) { + if (microserviceDataUpdate.iofogUuid) { await _deleteRoutes(microserviceData.routes, microserviceUuid, transaction); await _createRoutes(microserviceData.routes, microserviceUuid, user, transaction); - await _updateChangeTracking(false, microserviceUuid, microserviceDataUpdate.ioFogNodeId, user, isCLI, transaction) + await _updateChangeTracking(microserviceDataUpdate.iofogUuid, transaction) } - await _updateChangeTracking(microserviceData.config ? true : false, microserviceUuid, microservice.ioFogNodeId, user, isCLI, transaction); + await _updateChangeTracking(microservice.iofogUuid, transaction); }; const _updateVolumeMappings = async function (volumeMappings, microserviceUuid, transaction) { @@ -197,42 +194,65 @@ const _updateVolumeMappings = async function (volumeMappings, microserviceUuid, } }; -const _updateChangeTracking = async function (configUpdated, microserviceUuid, fogNodeUuid, user, isCLI, transaction) { +const _updateChangeTracking = async function (iofogUuid, transaction) { const trackingData = { containerList: true, - containerConfig: configUpdated, - iofogUuid: fogNodeUuid + containerConfig: true, + routing: true }; - await ChangeTrackingManager.update({iofogUuid: fogNodeUuid}, trackingData, transaction); + await ChangeTrackingManager.update({iofogUuid: iofogUuid}, trackingData, transaction); }; const _deleteMicroservice = async function (microserviceUuid, deleteWithCleanUp, user, isCLI, transaction) { - if (deleteWithCleanUp) { - return await MicroserviceManager.update({ + + const where = isCLI + ? + { + uuid: microserviceUuid, + updatedBy: user.id + } + : + { + uuid: microserviceUuid + }; + + + const microservice = await MicroserviceManager.findOneWithStatus(where, transaction); + + if (microservice.microserviceStatus.status === MicroserviceStates.NOT_RUNNING) { + const affectedRows = await MicroserviceManager.delete({ + uuid: microserviceUuid + }, transaction); + if (affectedRows === 0) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)); + } + } else { + await MicroserviceManager.update({ uuid: microserviceUuid }, { - deleteWithCleanUp: deleteWithCleanUp + delete: true, + deleteWithCleanUp: !!deleteWithCleanUp }, transaction); } - const microservice = await MicroserviceManager.findOne({ - uuid: microserviceUuid, - updatedBy: user.id - }, transaction); - - const affectedRows = await MicroserviceManager.delete({ - uuid: microserviceUuid - }, transaction); - if (affectedRows === 0) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)); - } - - await _updateChangeTracking(false, microserviceUuid, microservice.ioFogNodeId, user, isCLI, transaction) + await _updateChangeTracking(microservice.iofogUuid, transaction) }; +const _deleteNotRunningMicroservices = async function (transaction) { + const microservices = await MicroserviceManager.findAllWithStatuses(transaction); + microservices + .filter(microservice => !!microservice.delete) + .filter(microservice => microservice.microserviceStatus.status === MicroserviceStates.NOT_RUNNING) + .forEach(microservice => { + MicroserviceManager.delete({ + uuid: microservice.uuid + }, transaction); + }); +} + const _checkForDuplicateName = async function (name, item, userId, transaction) { if (name) { const where = item.id @@ -811,5 +831,6 @@ module.exports = { getMicroservicePortMappingListWithTransaction: TransactionDecorator.generateTransaction(_getPortMappingList), deletePortMappingWithTransaction: TransactionDecorator.generateTransaction(_deletePortMapping), getPhysicalConections: getPhysicalConections, - getListMicroservices: _listMicroservices + listMicroservices: _listMicroservices, + deleteNotRunningMicroservices: _deleteNotRunningMicroservices };