From b6f0229f7d5b9d31183f7c5a69036a278dfb6d07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emirhan=20Durmu=C5=9F?= Date: Tue, 30 Sep 2025 12:12:24 +0300 Subject: [PATCH] microservice start/stop, missing edgeguardfrequency spec added to create/update fog endpoints, a new config/gps enpoint for agents, ecn viewer upgraded, minor fixes. --- docs/swagger.yaml | 93 +++++++++++++++++++ package-lock.json | 12 +-- package.json | 4 +- src/controllers/agent-controller.js | 7 ++ src/controllers/microservices-controller.js | 14 ++- src/data/managers/microservice-manager.js | 11 ++- ....0.3.sql => db_migration_mysql_v1.0.4.sql} | 2 + ..._v1.0.3.sql => db_migration_pg_v1.0.4.sql} | 4 +- ...0.3.sql => db_migration_sqlite_v1.0.4.sql} | 4 +- src/data/models/microservice.js | 5 + src/data/providers/database-provider.js | 12 +-- src/helpers/error-messages.js | 1 + src/jobs/stopped-app-status-job.js | 50 ++++++++-- src/routes/agent.js | 29 ++++++ src/routes/microservices.js | 70 ++++++++++++++ src/schemas/agent.js | 12 ++- src/services/agent-service.js | 70 ++++++++------ src/services/iofog-service.js | 15 ++- src/services/microservices-service.js | 48 +++++++++- 19 files changed, 408 insertions(+), 55 deletions(-) rename src/data/migrations/mysql/{db_migration_mysql_v1.0.3.sql => db_migration_mysql_v1.0.4.sql} (99%) rename src/data/migrations/postgres/{db_migration_pg_v1.0.3.sql => db_migration_pg_v1.0.4.sql} (99%) rename src/data/migrations/sqlite/{db_migration_sqlite_v1.0.3.sql => db_migration_sqlite_v1.0.4.sql} (99%) diff --git a/docs/swagger.yaml b/docs/swagger.yaml index c36985ad..c7b83042 100755 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1000,6 +1000,34 @@ paths: description: Not Authorized "500": description: Internal Server Error + /agent/config/gps: + patch: + tags: + - Agent + summary: Updates an ioFog node GPS configuration + operationId: updateIOFogNodeGps + security: + - authToken: [] + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/IOFogNodeGpsRequest" + required: true + responses: + "204": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + "400": + description: Bad Request + "401": + description: Not Authorized + "500": + description: Internal Server Error /agent/config/changes: get: tags: @@ -2609,6 +2637,64 @@ paths: description: Not Found "500": description: Internal Server Error + "/microservices/{uuid}/start": + patch: + tags: + - Microservices + summary: Starts a microservice + operationId: startMicroservice + parameters: + - in: path + name: uuid + description: Microservice Uuid + required: true + schema: + type: string + security: + - authToken: [] + responses: + "204": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error + "/microservices/{uuid}/stop": + patch: + tags: + - Microservices + summary: Stops a microservice + operationId: stopMicroservice + parameters: + - in: path + name: uuid + description: Microservice Uuid + required: true + schema: + type: string + security: + - authToken: [] + responses: + "204": + description: Success + headers: + X-Timestamp: + description: FogController server timestamp + schema: + type: number + "401": + description: Not Authorized + "404": + description: Not Found + "500": + description: Internal Server Error "/microservices/{uuid}/exec": post: tags: @@ -5786,6 +5872,13 @@ components: type: number dockerPruningFrequency: type: number + IOFogNodeGpsRequest: + type: object + properties: + latitude: + type: number + longitude: + type: number AgentStatus: type: object properties: diff --git a/package-lock.json b/package-lock.json index 172b64ab..0ab344b8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "@datasance/iofogcontroller", - "version": "3.5.5", + "version": "3.5.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@datasance/iofogcontroller", - "version": "3.5.5", + "version": "3.5.6", "hasInstallScript": true, "license": "EPL-2.0", "dependencies": { - "@datasance/ecn-viewer": "1.2.0", + "@datasance/ecn-viewer": "1.2.1", "@kubernetes/client-node": "^0.22.3", "@msgpack/msgpack": "^3.1.2", "@opentelemetry/api": "^1.9.0", @@ -426,9 +426,9 @@ } }, "node_modules/@datasance/ecn-viewer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@datasance/ecn-viewer/-/ecn-viewer-1.2.0.tgz", - "integrity": "sha512-n/2FHs2xTtG+ePxMdwHrK1B61wkJ2tN2K5F8OYS5t3THa7/pSzRYKx76xZ3b5sazWSiwQaNt+C7cU5tT67G3CA==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@datasance/ecn-viewer/-/ecn-viewer-1.2.1.tgz", + "integrity": "sha512-l7st68cRAJIHfKQq3NOY6kV5dCU6kTHS/CnZCnxnC2i4GJcjVgWNgCo9IdS+gLkgVPL9Xh68VnndW/fYyd057Q==" }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", diff --git a/package.json b/package.json index fcf826da..c84a8064 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@datasance/iofogcontroller", - "version": "3.5.5", + "version": "3.5.6", "description": "ioFog Controller project for Datasance PoT @ datasance.com \\nCopyright (c) 2023 Datasance Teknoloji A.S.", "main": "./src/main.js", "author": "Emirhan Durmus", @@ -55,7 +55,7 @@ "iofog-controller": "src/main.js" }, "dependencies": { - "@datasance/ecn-viewer": "1.2.0", + "@datasance/ecn-viewer": "1.2.1", "@kubernetes/client-node": "^0.22.3", "@msgpack/msgpack": "^3.1.2", "@opentelemetry/api": "^1.9.0", diff --git a/src/controllers/agent-controller.js b/src/controllers/agent-controller.js index c77ca4ca..404be92c 100644 --- a/src/controllers/agent-controller.js +++ b/src/controllers/agent-controller.js @@ -36,6 +36,12 @@ const updateAgentConfigEndPoint = async function (req, fog) { return AgentService.updateAgentConfig(updateData, fog) } +const updateAgentGpsEndPoint = async function (req, fog) { + const updateData = req.body + + return AgentService.updateAgentGpsEndPoint(updateData, fog) +} + const getAgentConfigChangesEndPoint = async function (req, fog) { return AgentService.getAgentConfigChanges(fog) } @@ -123,6 +129,7 @@ module.exports = { agentDeprovisionEndPoint: AuthDecorator.checkFogToken(agentDeprovisionEndPoint), getAgentConfigEndPoint: AuthDecorator.checkFogToken(getAgentConfigEndPoint), updateAgentConfigEndPoint: AuthDecorator.checkFogToken(updateAgentConfigEndPoint), + updateAgentGpsEndPoint: AuthDecorator.checkFogToken(updateAgentGpsEndPoint), getAgentConfigChangesEndPoint: AuthDecorator.checkFogToken(getAgentConfigChangesEndPoint), updateAgentStatusEndPoint: AuthDecorator.checkFogToken(updateAgentStatusEndPoint), getAgentMicroservicesEndPoint: AuthDecorator.checkFogToken(getAgentMicroservicesEndPoint), diff --git a/src/controllers/microservices-controller.js b/src/controllers/microservices-controller.js index 302a0fc8..7fa9b677 100644 --- a/src/controllers/microservices-controller.js +++ b/src/controllers/microservices-controller.js @@ -241,6 +241,16 @@ const deleteSystemMicroserviceExecEndPoint = async function (req) { return MicroservicesService.deleteSystemExecEndPoint(uuid, false) } +const startMicroserviceEndPoint = async function (req) { + const uuid = req.params.uuid + return MicroservicesService.startMicroserviceEndPoint(uuid, false) +} + +const stopMicroserviceEndPoint = async function (req) { + const uuid = req.params.uuid + return MicroservicesService.stopMicroserviceEndPoint(uuid, false) +} + module.exports = { createMicroserviceOnFogEndPoint: (createMicroserviceOnFogEndPoint), getMicroserviceEndPoint: (getMicroserviceEndPoint), @@ -278,5 +288,7 @@ module.exports = { createMicroserviceExecEndPoint: (createMicroserviceExecEndPoint), deleteMicroserviceExecEndPoint: (deleteMicroserviceExecEndPoint), createSystemMicroserviceExecEndPoint: (createSystemMicroserviceExecEndPoint), - deleteSystemMicroserviceExecEndPoint: (deleteSystemMicroserviceExecEndPoint) + deleteSystemMicroserviceExecEndPoint: (deleteSystemMicroserviceExecEndPoint), + startMicroserviceEndPoint: (startMicroserviceEndPoint), + stopMicroserviceEndPoint: (stopMicroserviceEndPoint) } diff --git a/src/data/managers/microservice-manager.js b/src/data/managers/microservice-manager.js index 573aa7d2..6c50fcd0 100644 --- a/src/data/managers/microservice-manager.js +++ b/src/data/managers/microservice-manager.js @@ -31,6 +31,7 @@ const Application = models.Application const Routing = models.Routing const Registry = models.Registry const MicroserviceStatus = models.MicroserviceStatus +const MicroserviceExecStatus = models.MicroserviceExecStatus const MicroserviceHealthCheck = models.MicroserviceHealthCheck const Op = require('sequelize').Op @@ -272,7 +273,10 @@ class MicroserviceManager extends BaseManager { [Op.or]: [ { - '$application.is_activated$': true + [Op.and]: [ + { '$application.is_activated$': true }, + { isActivated: true } + ] }, { '$catalogItem.category$': { [Op.eq]: 'SYSTEM' }, @@ -418,6 +422,11 @@ class MicroserviceManager extends BaseManager { model: MicroserviceStatus, as: 'microserviceStatus', required: false + }, + { + model: MicroserviceExecStatus, + as: 'microserviceExecStatus', + required: false } ], where: where diff --git a/src/data/migrations/mysql/db_migration_mysql_v1.0.3.sql b/src/data/migrations/mysql/db_migration_mysql_v1.0.4.sql similarity index 99% rename from src/data/migrations/mysql/db_migration_mysql_v1.0.3.sql rename to src/data/migrations/mysql/db_migration_mysql_v1.0.4.sql index 4126b2ae..1be5d278 100644 --- a/src/data/migrations/mysql/db_migration_mysql_v1.0.3.sql +++ b/src/data/migrations/mysql/db_migration_mysql_v1.0.4.sql @@ -810,4 +810,6 @@ CREATE INDEX idx_microservice_health_check_microservice_uuid ON MicroserviceHeal ALTER TABLE MicroserviceStatuses ADD COLUMN health_status TEXT; +ALTER TABLE Microservices ADD COLUMN is_activated BOOLEAN DEFAULT true; + COMMIT; \ No newline at end of file diff --git a/src/data/migrations/postgres/db_migration_pg_v1.0.3.sql b/src/data/migrations/postgres/db_migration_pg_v1.0.4.sql similarity index 99% rename from src/data/migrations/postgres/db_migration_pg_v1.0.3.sql rename to src/data/migrations/postgres/db_migration_pg_v1.0.4.sql index 9aeb5f6c..3181cc00 100644 --- a/src/data/migrations/postgres/db_migration_pg_v1.0.3.sql +++ b/src/data/migrations/postgres/db_migration_pg_v1.0.4.sql @@ -808,4 +808,6 @@ CREATE TABLE IF NOT EXISTS "MicroserviceHealthChecks" ( CREATE INDEX idx_microservice_health_check_microservice_uuid ON "MicroserviceHealthChecks" (microservice_uuid); -ALTER TABLE "MicroserviceStatuses" ADD COLUMN health_status TEXT; \ No newline at end of file +ALTER TABLE "MicroserviceStatuses" ADD COLUMN health_status TEXT; + +ALTER TABLE "Microservices" ADD COLUMN is_activated BOOLEAN DEFAULT true; \ No newline at end of file diff --git a/src/data/migrations/sqlite/db_migration_sqlite_v1.0.3.sql b/src/data/migrations/sqlite/db_migration_sqlite_v1.0.4.sql similarity index 99% rename from src/data/migrations/sqlite/db_migration_sqlite_v1.0.3.sql rename to src/data/migrations/sqlite/db_migration_sqlite_v1.0.4.sql index 0781f38d..13d21c08 100644 --- a/src/data/migrations/sqlite/db_migration_sqlite_v1.0.3.sql +++ b/src/data/migrations/sqlite/db_migration_sqlite_v1.0.4.sql @@ -795,4 +795,6 @@ CREATE TABLE IF NOT EXISTS MicroserviceHealthChecks ( CREATE INDEX idx_microservice_health_check_microservice_uuid ON MicroserviceHealthChecks (microservice_uuid); -ALTER TABLE MicroserviceStatuses ADD COLUMN health_status TEXT; \ No newline at end of file +ALTER TABLE MicroserviceStatuses ADD COLUMN health_status TEXT; + +ALTER TABLE Microservices ADD COLUMN is_activated BOOLEAN DEFAULT true; \ No newline at end of file diff --git a/src/data/models/microservice.js b/src/data/models/microservice.js index 261ffdab..e1128093 100644 --- a/src/data/models/microservice.js +++ b/src/data/models/microservice.js @@ -108,6 +108,11 @@ module.exports = (sequelize, DataTypes) => { type: DataTypes.BOOLEAN, field: 'delete_with_cleanup', defaultValue: false + }, + isActivated: { + type: DataTypes.BOOLEAN, + field: 'is_activated', + defaultValue: true } }, { tableName: 'Microservices', diff --git a/src/data/providers/database-provider.js b/src/data/providers/database-provider.js index a3206db1..fd6b899c 100644 --- a/src/data/providers/database-provider.js +++ b/src/data/providers/database-provider.js @@ -251,8 +251,8 @@ class DatabaseProvider { // SQLite migration async runMigrationSQLite (dbName) { - const migrationSqlPath = path.resolve(__dirname, '../migrations/sqlite/db_migration_sqlite_v1.0.3.sql') - const migrationVersion = '1.0.3' + const migrationSqlPath = path.resolve(__dirname, '../migrations/sqlite/db_migration_sqlite_v1.0.4.sql') + const migrationVersion = '1.0.4' if (!fs.existsSync(migrationSqlPath)) { logger.error(`Migration file not found: ${migrationSqlPath}`) @@ -324,8 +324,8 @@ class DatabaseProvider { // MySQL migration async runMigrationMySQL (db) { - const migrationSqlPath = path.resolve(__dirname, '../migrations/mysql/db_migration_mysql_v1.0.3.sql') - const migrationVersion = '1.0.3' + const migrationSqlPath = path.resolve(__dirname, '../migrations/mysql/db_migration_mysql_v1.0.4.sql') + const migrationVersion = '1.0.4' if (!fs.existsSync(migrationSqlPath)) { logger.error(`Migration file not found: ${migrationSqlPath}`) @@ -385,8 +385,8 @@ class DatabaseProvider { // PostgreSQL migration async runMigrationPostgres (db) { - const migrationSqlPath = path.resolve(__dirname, '../migrations/postgres/db_migration_pg_v1.0.3.sql') - const migrationVersion = '1.0.3' + const migrationSqlPath = path.resolve(__dirname, '../migrations/postgres/db_migration_pg_v1.0.4.sql') + const migrationVersion = '1.0.4' if (!fs.existsSync(migrationSqlPath)) { logger.error(`Migration file not found: ${migrationSqlPath}`) diff --git a/src/helpers/error-messages.js b/src/helpers/error-messages.js index 1e373ab6..9966ca14 100644 --- a/src/helpers/error-messages.js +++ b/src/helpers/error-messages.js @@ -57,6 +57,7 @@ module.exports = { 'List of invalid microservices:\n', INVALID_MICROSERVICE_CONFIG: 'Can\'t create network microservice without appropriate configuration.', INVALID_MICROSERVICE_USER: 'Invalid microservice user or UUID', + APPLICATION_NOT_ACTIVATED: 'Application {} is not activated', ROUTE_NOT_FOUND: 'Route not found', IMAGE_SNAPSHOT_WITHOUT_FOG: 'Can not run image snapshot for microservice without ioFog.', IMAGE_SNAPSHOT_NOT_AVAILABLE: 'Image snapshot is not available for this microservice.', diff --git a/src/jobs/stopped-app-status-job.js b/src/jobs/stopped-app-status-job.js index c31b77e8..05a5403d 100644 --- a/src/jobs/stopped-app-status-job.js +++ b/src/jobs/stopped-app-status-job.js @@ -26,7 +26,12 @@ const scheduleTime = Config.get('settings.fogStatusUpdateInterval') * 1000 async function run () { try { const _updateStoppedApplicationMicroserviceStatus = TransactionDecorator.generateTransaction(updateStoppedApplicationMicroserviceStatus) + const _updateStoppedMicroserviceStatus = TransactionDecorator.generateTransaction(updateStoppedMicroserviceStatus) + + // Handle microservices from deactivated applications await _updateStoppedApplicationMicroserviceStatus() + // Handle individually deactivated microservices + await _updateStoppedMicroserviceStatus() } catch (error) { console.error(error) } finally { @@ -35,18 +40,51 @@ async function run () { } async function updateStoppedApplicationMicroserviceStatus (transaction) { - const stoppedMicroservices = await ApplicationManager.findApplicationMicroservices({ isActivated: false }, transaction) + // Get all deactivated applications + const stoppedApplications = await ApplicationManager.findAllWithAttributes({ isActivated: false }, ['id'], transaction) + + if (stoppedApplications.length === 0) { + return + } + + // Get all microservices from these applications + const applicationIds = stoppedApplications.map(app => app.id) + const { Op } = require('sequelize') + const stoppedMicroservices = await MicroserviceManager.findAllWithStatuses({ applicationId: { [Op.in]: applicationIds } }, transaction) + + await _updateMicroserviceStatusStopped(stoppedMicroservices, transaction) +} + +async function updateStoppedMicroserviceStatus (transaction) { + // Get all individually deactivated microservices (where microservice isActivated = false but parent application is still active) + const { Op } = require('sequelize') + + // First get all active applications + const activeApplications = await ApplicationManager.findAllWithAttributes({ isActivated: true }, ['id'], transaction) + if (activeApplications.length === 0) { + return + } + + // Then get microservices that are individually deactivated but belong to active applications + const activeApplicationIds = activeApplications.map(app => app.id) + const stoppedMicroservices = await MicroserviceManager.findAllWithStatuses({ + isActivated: false, + applicationId: { [Op.in]: activeApplicationIds } + }, transaction) + + if (stoppedMicroservices.length === 0) { + return + } + await _updateMicroserviceStatusStopped(stoppedMicroservices, transaction) } async function _updateMicroserviceStatusStopped (stoppedMicroservices, transaction) { - const microserviceUuids = stoppedMicroservices.map((microservice) => microservice.uuid) - const microservices = await MicroserviceManager.findAllWithStatuses({ uuid: microserviceUuids }, transaction) - const microserviceStatusIds = microservices + const microserviceStatusIds = stoppedMicroservices .filter((microservice) => microservice.microserviceStatus && (microservice.microserviceStatus.status === microserviceState.DELETED || microservice.microserviceStatus.status === microserviceState.DELETING)) .map((microservice) => microservice.microserviceStatus.id) - const microserviceExecStatusIds = microservices + const microserviceExecStatusIds = stoppedMicroservices .filter((microservice) => microservice.microserviceStatus && (microservice.microserviceStatus.status === microserviceState.DELETED || @@ -56,7 +94,7 @@ async function _updateMicroserviceStatusStopped (stoppedMicroservices, transacti .map((microservice) => microservice.microserviceExecStatus.id) await MicroserviceStatusManager.update({ id: microserviceStatusIds }, { status: microserviceState.STOPPED }, transaction) await MicroserviceExecStatusManager.update({ id: microserviceExecStatusIds }, { execSesssionId: '', status: microserviceExecState.INACTIVE }, transaction) - return microservices + return stoppedMicroservices } module.exports = { diff --git a/src/routes/agent.js b/src/routes/agent.js index ccfa6a7b..8aca966f 100644 --- a/src/routes/agent.js +++ b/src/routes/agent.js @@ -133,6 +133,35 @@ module.exports = [ logger.apiRes({ req: req, res: res, responseObject: responseObject }) } }, + { + method: 'patch', + path: '/api/v3/agent/config/gps', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + const errorCodes = [ + { + code: constants.HTTP_CODE_UNAUTHORIZED, + errors: [Errors.AuthenticationError] + }, + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.ValidationError] + } + ] + + const updateAgentGpsEndPoint = ResponseDecorator.handleErrors(AgentController.updateAgentGpsEndPoint, + successCode, errorCodes) + const responseObject = await updateAgentGpsEndPoint(req) + + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, res: res, responseObject: responseObject }) + } + }, { method: 'get', path: '/api/v3/agent/config/changes', diff --git a/src/routes/microservices.js b/src/routes/microservices.js index 38c3d083..8927db9b 100644 --- a/src/routes/microservices.js +++ b/src/routes/microservices.js @@ -1290,6 +1290,76 @@ module.exports = [ }) } }, + { + method: 'patch', + path: '/api/v3/microservices/:uuid/start', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + 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] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const startMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.startMicroserviceEndPoint, + successCode, errorCodes) + const responseObject = await startMicroserviceEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, + { + method: 'patch', + path: '/api/v3/microservices/:uuid/stop', + middleware: async (req, res) => { + logger.apiReq(req) + + const successCode = constants.HTTP_CODE_NO_CONTENT + 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] + } + ] + + await keycloak.protect(['SRE', 'Developer'])(req, res, async () => { + const stopMicroserviceEndPoint = ResponseDecorator.handleErrors(MicroservicesController.stopMicroserviceEndPoint, + successCode, errorCodes) + const responseObject = await stopMicroserviceEndPoint(req) + const user = req.kauth.grant.access_token.content.preferred_username + res + .status(responseObject.code) + .send(responseObject.body) + + logger.apiRes({ req: req, user: user, res: res, responseObject: responseObject }) + }) + } + }, { method: 'ws', path: '/api/v3/microservices/exec/:microserviceUuid', diff --git a/src/schemas/agent.js b/src/schemas/agent.js index c41bde55..40bd2046 100644 --- a/src/schemas/agent.js +++ b/src/schemas/agent.js @@ -66,6 +66,16 @@ const updateAgentConfig = { 'additionalProperties': true } +const updateAgentGps = { + 'id': '/updateAgentGps', + 'type': 'object', + 'properties': { + 'latitude': { 'type': 'number', 'minimum': -90, 'maximum': 90 }, + 'longitude': { 'type': 'number', 'minimum': -180, 'maximum': 180 } + }, + 'additionalProperties': true +} + const updateAgentStatus = { 'id': '/updateAgentStatus', 'type': 'object', @@ -174,7 +184,7 @@ const updateUsbInfo = { } module.exports = { - mainSchemas: [agentProvision, agentDeprovision, updateAgentConfig, updateAgentStatus, updateAgentStrace, + mainSchemas: [agentProvision, agentDeprovision, updateAgentConfig, updateAgentGps, updateAgentStatus, updateAgentStrace, updateHardwareInfo, updateUsbInfo], innerSchemas: [straceData, microserviceStatus] } diff --git a/src/services/agent-service.js b/src/services/agent-service.js index b8bf4512..5e2208dd 100644 --- a/src/services/agent-service.js +++ b/src/services/agent-service.js @@ -139,37 +139,40 @@ const getAgentConfig = async function (fog, transaction) { // Get local agent certificate from secrets const localAgentSecret = await SecretManager.getSecret(`${fog.uuid}-local-agent`, transaction) - // fog is the result of FogManager.FindOne() in the checkFogToken middleware - return { - networkInterface: fog.networkInterface, - dockerUrl: fog.dockerUrl, - diskLimit: fog.diskLimit, - diskDirectory: fog.diskDirectory, - memoryLimit: fog.memoryLimit, - cpuLimit: fog.cpuLimit, - logLimit: fog.logLimit, - logDirectory: fog.logDirectory, - logFileCount: fog.logFileCount, - gpsMode: fog.gpsMode, - gpsDevice: fog.gpsDevice, - gpsScanFrequency: fog.gpsScanFrequency, - edgeGuardFrequency: fog.edgeGuardFrequency, - statusFrequency: fog.statusFrequency, - changeFrequency: fog.changeFrequency, - deviceScanFrequency: fog.deviceScanFrequency, - watchdogEnabled: fog.watchdogEnabled, - latitude: fog.latitude, - longitude: fog.longitude, - logLevel: fog.logLevel, - availableDiskThreshold: fog.availableDiskThreshold, - dockerPruningFrequency: fog.dockerPruningFrequency, - routerHost: router.host === fog.host ? 'localhost' : router.host, + const fogData = await FogManager.findOne({ + uuid: fog.uuid + }, transaction) + const resp = { + networkInterface: fogData.networkInterface, + dockerUrl: fogData.dockerUrl, + diskLimit: fogData.diskLimit, + diskDirectory: fogData.diskDirectory, + memoryLimit: fogData.memoryLimit, + cpuLimit: fogData.cpuLimit, + logLimit: fogData.logLimit, + logDirectory: fogData.logDirectory, + logFileCount: fogData.logFileCount, + gpsMode: fogData.gpsMode, + gpsDevice: fogData.gpsDevice, + gpsScanFrequency: fogData.gpsScanFrequency, + edgeGuardFrequency: fogData.edgeGuardFrequency, + statusFrequency: fogData.statusFrequency, + changeFrequency: fogData.changeFrequency, + deviceScanFrequency: fogData.deviceScanFrequency, + watchdogEnabled: fogData.watchdogEnabled, + latitude: fogData.latitude, + longitude: fogData.longitude, + logLevel: fogData.logLevel, + availableDiskThreshold: fogData.availableDiskThreshold, + dockerPruningFrequency: fogData.dockerPruningFrequency, + routerHost: router.host === fogData.host ? 'localhost' : router.host, routerPort: router.messagingPort, - timeZone: fog.timeZone, + timeZone: fogData.timeZone, caCert: localAgentSecret ? localAgentSecret.data['ca.crt'] : null, tlsCert: localAgentSecret ? localAgentSecret.data['tls.crt'] : null, tlsKey: localAgentSecret ? localAgentSecret.data['tls.key'] : null } + return resp } const updateAgentConfig = async function (updateData, fog, transaction) { @@ -207,6 +210,20 @@ const updateAgentConfig = async function (updateData, fog, transaction) { }, update, transaction) } +const updateAgentGpsEndPoint = async function (updateData, fog, transaction) { + await Validator.validate(updateData, Validator.schemas.updateAgentGps) + + let update = { + latitude: updateData.latitude, + longitude: updateData.longitude + } + update = AppHelper.deleteUndefinedFields(update) + + await FogManager.update({ + uuid: fog.uuid + }, update, transaction) +} + const getAgentConfigChanges = async function (ioFog, transaction) { const changeTrackings = await ChangeTrackingService.getByIoFogUuid(ioFog.uuid, transaction) const res = { ...CHANGE_TRACKING_DEFAULT } @@ -782,6 +799,7 @@ module.exports = { agentDeprovision: TransactionDecorator.generateTransaction(agentDeprovision), getAgentConfig: TransactionDecorator.generateTransaction(getAgentConfig), updateAgentConfig: TransactionDecorator.generateTransaction(updateAgentConfig), + updateAgentGpsEndPoint: TransactionDecorator.generateTransaction(updateAgentGpsEndPoint), getAgentConfigChanges: TransactionDecorator.generateTransaction(getAgentConfigChanges), resetAgentConfigChanges: TransactionDecorator.generateTransaction(resetAgentConfigChanges), updateAgentStatus: TransactionDecorator.generateTransaction(updateAgentStatus), diff --git a/src/services/iofog-service.js b/src/services/iofog-service.js index b088420a..50229608 100644 --- a/src/services/iofog-service.js +++ b/src/services/iofog-service.js @@ -267,6 +267,7 @@ async function createFogEndPoint (fogData, isCLI, transaction) { abstractedHardwareEnabled: fogData.abstractedHardwareEnabled, fogTypeId: fogData.fogType, logLevel: fogData.logLevel, + edgeGuardFrequency: fogData.edgeGuardFrequency, dockerPruningFrequency: fogData.dockerPruningFrequency, availableDiskThreshold: fogData.availableDiskThreshold, isSystem: fogData.isSystem, @@ -275,11 +276,15 @@ async function createFogEndPoint (fogData, isCLI, transaction) { timeZone: fogData.timeZone } - if ((fogData.latitude || fogData.longitude) && fogData.gpsMode !== 'dynamic') { + if ((fogData.latitude || fogData.longitude) && (fogData.gpsMode !== 'dynamic' && fogData.gpsMode !== 'off')) { createFogData.gpsMode = 'manual' } else if (fogData.gpsMode === 'dynamic' && fogData.gpsDevice) { createFogData.gpsMode = fogData.gpsMode createFogData.gpsDevice = fogData.gpsDevice + } else if (!(fogData.latitude || fogData.longitude) && fogData.gpsMode === 'auto') { + createFogData.gpsMode = 'auto' + } else if (fogData.gpsMode === 'off') { + createFogData.gpsMode = 'off' } else { createFogData.gpsMode = undefined } @@ -293,6 +298,7 @@ async function createFogEndPoint (fogData, isCLI, transaction) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_ROUTER_MODE, fogData.routerMode)) } + // TODO: handle multiple system fogs a.k.a multi-remote-controller and multi interior routers if (fogData.isSystem && !!(await FogManager.findOne({ isSystem: true }, transaction))) { throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.DUPLICATE_SYSTEM_FOG)) } @@ -436,16 +442,21 @@ async function updateFogEndPoint (fogData, isCLI, transaction) { fogTypeId: fogData.fogType, logLevel: fogData.logLevel, dockerPruningFrequency: fogData.dockerPruningFrequency, + edgeGuardFrequency: fogData.edgeGuardFrequency, host: fogData.host, availableDiskThreshold: fogData.availableDiskThreshold, timeZone: fogData.timeZone } - if ((fogData.latitude || fogData.longitude) && fogData.gpsMode !== 'dynamic') { + if ((fogData.latitude || fogData.longitude) && (fogData.gpsMode !== 'dynamic' && fogData.gpsMode !== 'off')) { updateFogData.gpsMode = 'manual' } else if (fogData.gpsMode === 'dynamic' && fogData.gpsDevice) { updateFogData.gpsMode = fogData.gpsMode updateFogData.gpsDevice = fogData.gpsDevice + } else if (!(fogData.latitude || fogData.longitude) && fogData.gpsMode === 'auto') { + updateFogData.gpsMode = 'auto' + } else if (fogData.gpsMode === 'off') { + updateFogData.gpsMode = 'off' } else { updateFogData.gpsMode = undefined } diff --git a/src/services/microservices-service.js b/src/services/microservices-service.js index 539ef74f..be84fcda 100644 --- a/src/services/microservices-service.js +++ b/src/services/microservices-service.js @@ -951,7 +951,7 @@ async function updateMicroserviceEndPoint (microserviceUuid, microserviceData, i if (microserviceDataUpdate.capDrop) { await _updateCapDrop(microserviceDataUpdate.capDrop, microserviceUuid, transaction) } - // TODO: Implement moveServiceToNewFog + const existingService = await ServiceManager.findOne({ type: `microservice`, resource: microservice.uuid }, transaction) if (microserviceDataUpdate.iofogUuid && microserviceDataUpdate.iofogUuid !== microservice.iofogUuid && existingService) { await ServiceServices.moveMicroserviceTcpBridgeToNewFog(existingService, microserviceDataUpdate.iofogUuid, microservice.iofogUuid, transaction) @@ -2339,6 +2339,48 @@ async function deleteSystemExecEndPoint (microserviceUuid, isCLI, transaction) { } } +async function startMicroserviceEndPoint (microserviceUuid, isCLI, transaction) { + const microservice = await MicroserviceManager.findOneWithCategory({ uuid: microserviceUuid }, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + if (microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + + // Check if the parent application is activated + const application = await ApplicationManager.findOne({ id: microservice.applicationId }, transaction) + if (!application || !application.isActivated) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.APPLICATION_NOT_ACTIVATED, application ? application.name : 'Unknown')) + } + + await MicroserviceManager.update({ uuid: microservice.uuid }, { isActivated: true }, transaction) + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceList, transaction) + + return { + uuid: microservice.uuid, + isActivated: true + } +} + +async function stopMicroserviceEndPoint (microserviceUuid, isCLI, transaction) { + const microservice = await MicroserviceManager.findOneWithCategory({ uuid: microserviceUuid }, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + if (microservice.catalogItem && microservice.catalogItem.category === 'SYSTEM') { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.SYSTEM_MICROSERVICE_UPDATE, microserviceUuid)) + } + + await MicroserviceManager.update({ uuid: microservice.uuid }, { isActivated: false }, transaction) + await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceList, transaction) + + return { + uuid: microservice.uuid, + isActivated: false + } +} + module.exports = { createMicroserviceEndPoint: TransactionDecorator.generateTransaction(createMicroserviceEndPoint), createPortMappingEndPoint: TransactionDecorator.generateTransaction(createPortMappingEndPoint), @@ -2380,5 +2422,7 @@ module.exports = { createExecEndPoint: TransactionDecorator.generateTransaction(createExecEndPoint), deleteExecEndPoint: TransactionDecorator.generateTransaction(deleteExecEndPoint), createSystemExecEndPoint: TransactionDecorator.generateTransaction(createSystemExecEndPoint), - deleteSystemExecEndPoint: TransactionDecorator.generateTransaction(deleteSystemExecEndPoint) + deleteSystemExecEndPoint: TransactionDecorator.generateTransaction(deleteSystemExecEndPoint), + startMicroserviceEndPoint: TransactionDecorator.generateTransaction(startMicroserviceEndPoint), + stopMicroserviceEndPoint: TransactionDecorator.generateTransaction(stopMicroserviceEndPoint) }