diff --git a/src/config/constants.js b/src/config/constants.js index 21dc6e958..1535e6d3a 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:FogStatusUpdateIntervalSeconds': 120, 'Settings:FogStatusFrequencySeconds': 60, diff --git a/src/controllers/kubelet-controller.js b/src/controllers/kubelet-controller.js new file mode 100644 index 000000000..63a0ab7bf --- /dev/null +++ b/src/controllers/kubelet-controller.js @@ -0,0 +1,110 @@ +/* + * ******************************************************************************* + * * 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) { + const createPodData = req.body + const fogNodeUuid = req.params.nodeName + + return await KubeletService.kubeletCreatePod(createPodData, fogNodeUuid) +} + +const kubeletUploadPodEndPoint = async function(req) { + const uploadPodData = req.body + const fogNodeUuid = req.params.nodeName + + return await KubeletService.kubeletUploadPod(uploadPodData, fogNodeUuid) +} + +const kubeletDeletePodEndPoint = async function(req) { + const fogNodeUuid = req.params.nodeName + + return await KubeletService.kubeletDeletePod(fogNodeUuid) +} + +const kubeletGetPodEndPoint = async function(req) { + const namespace = req.params.namespace + const name = req.params.name + const fogNodeUuid = req.params.nodeName + + return await KubeletService.kubeletGetPod(namespace, name, fogNodeUuid) +} + +const kubeletGetContainerLogsEndPoint = async function(req) { + const namespace = req.params.namespace + const podName = req.params.podName + const containerName = req.params.containerName + const tail = req.params.tail + const fogNodeUuid = req.params.nodeName + + return await KubeletService.kubeletGetContainerLogs(namespace, podName, containerName, tail, fogNodeUuid) +} + +const kubeletGetPodStatusEndPoint = async function(req) { + const namespace = req.params.namespace + const name = req.params.name + const fogNodeUuid = req.params.nodeName + + return await KubeletService.kubeletGetPodStatus(namespace, name, fogNodeUuid) +} + +const kubeletGetPodsEndPoint = async function(req) { + const fogNodeUuid = req.params.nodeName + + return await KubeletService.kubeletGetPods(createPodData, fogNodeUuid) +} + +const kubeletGetCapacityEndPoint = async function(req) { + const fogNodeUuid = req.params.nodeName + + return await KubeletService.kubeletGetCapacity(createPodData, fogNodeUuid) +} + +const kubeletGetNodeConditionsEndPoint = async function(req) { + const fogNodeUuid = req.params.nodeName + + return await KubeletService.kubeletGetNodeConditions(createPodData, fogNodeUuid) +} + +const kubeletGetNodeAddressesEndPoint = async function(req) { + const fogNodeUuid = req.params.nodeName + + return await KubeletService.kubeletGetNodeAddresses(createPodData, fogNodeUuid) +} + +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: kubeletCreatePodEndPoint, + kubeletUploadPodEndPoint: kubeletUploadPodEndPoint, + kubeletDeletePodEndPoint: kubeletDeletePodEndPoint, + kubeletGetPodEndPoint: kubeletGetPodEndPoint, + kubeletGetContainerLogsEndPoint: kubeletGetContainerLogsEndPoint, + kubeletGetPodStatusEndPoint: kubeletGetPodStatusEndPoint, + kubeletGetPodsEndPoint: kubeletGetPodsEndPoint, + kubeletGetCapacityEndPoint: kubeletGetCapacityEndPoint, + kubeletGetNodeConditionsEndPoint: kubeletGetNodeConditionsEndPoint, + kubeletGetNodeAddressesEndPoint: kubeletGetNodeAddressesEndPoint, + kubeletGetVkTokenEndPoint: AuthDecorator.checkAuthToken(kubeletGetVkTokenEndPoint), + kubeletGetSchedulerTokenEndPoint: AuthDecorator.checkAuthToken(kubeletGetSchedulerTokenEndPoint), +} diff --git a/src/routes/kubelet.js b/src/routes/kubelet.js new file mode 100644 index 000000000..83455d7a6 --- /dev/null +++ b/src/routes/kubelet.js @@ -0,0 +1,346 @@ +/* + * ******************************************************************************* + * * 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 KubeletController = require('../controllers/kubelet-controller') +const ResponseDecorator = require('../decorators/response-decorator') +const constants = require('../helpers/constants') +const Errors = require('../helpers/errors') +const logger = require('../logger') + +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], + }, + ] + + 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/uploadPod', + middleware: async (req, res) => { + res + .status(constants.HTTP_CODE_SUCCESS) + .send(req.body) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_BAD_REQUEST, + errors: [Errors.AuthenticationError], + }, + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.AuthenticationError], + }, + ] + + const kubeletUploadPodEndPoint = ResponseDecorator + .handleErrors(KubeletController.kubeletUploadPodEndPoint, successCode, errorCodes) + const responseObject = await kubeletUploadPodEndPoint(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) => { + res + .status(constants.HTTP_CODE_SUCCESS) + .send(req.body) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.AuthenticationError], + }, + ] + + 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) => { + res + .status(constants.HTTP_CODE_SUCCESS) + .send(req.body) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.AuthenticationError], + }, + ] + + 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) => { + res + .status(constants.HTTP_CODE_SUCCESS) + .send(req.body) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.AuthenticationError], + }, + ] + + 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) => { + res + .status(constants.HTTP_CODE_SUCCESS) + .send(req.body) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.AuthenticationError], + }, + ] + + 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) => { + res + .status(constants.HTTP_CODE_SUCCESS) + .send(req.body) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.AuthenticationError], + }, + ] + + 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) => { + res + .status(constants.HTTP_CODE_SUCCESS) + .send(req.body) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.AuthenticationError], + }, + ] + 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/nodeConditions', + middleware: async (req, res) => { + res + .status(constants.HTTP_CODE_SUCCESS) + .send(req.body) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.AuthenticationError], + }, + ] + + 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) => { + res + .status(constants.HTTP_CODE_SUCCESS) + .send(req.body) + + const successCode = constants.HTTP_CODE_SUCCESS + const errorCodes = [ + { + code: constants.HTTP_CODE_NOT_FOUND, + errors: [Errors.AuthenticationError], + }, + ] + + 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) => { + res + .status(constants.HTTP_CODE_SUCCESS) + .send(req.body) + + 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) => { + res + .status(constants.HTTP_CODE_SUCCESS) + .send(req.body) + + 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/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..aeefca1cd --- /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/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..8c2623754 --- /dev/null +++ b/src/services/kubelet-service.js @@ -0,0 +1,73 @@ +/* + * ******************************************************************************* + * * 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 KubeletAccessTokenService = require('./kubelet-access-token-service') +const TransactionDecorator = require('../decorators/transaction-decorator') + +const kubeletCreatePod = async function(createPodData, fogNodeUuid, transaction) { + // TODO: to implement +} +const kubeletUploadPod = async function(uploadPodData, fogNodeUuid, transaction) { + // TODO: to implement +} +const kubeletDeletePod = async function(fogNodeUuid, transaction) { + // TODO: to implement +} +const kubeletGetPod = async function(namespace, name, fogNodeUuid, transaction) { + // TODO: to implement +} +const kubeletGetContainerLogs = async function(namespace, podName, containerName, tail, fogNodeUuid, transaction) { + // TODO: to implement +} +const kubeletGetPodStatus = async function(namespace, name, fogNodeUuid, transaction) { + // TODO: to implement +} +const kubeletGetPods = async function(fogNodeUuid, transaction) { + // TODO: to implement +} +const kubeletGetCapacity = async function(fogNodeUuid, transaction) { + // TODO: to implement +} +const kubeletGetNodeConditions = async function(fogNodeUuid, transaction) { + // TODO: to implement +} +const kubeletGetNodeAddresses = async function(fogNodeUuid, transaction) { + // TODO: to implement +} +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) { + // TODO: to implement +} + +module.exports = { + kubeletCreatePod: TransactionDecorator.generateFakeTransaction(kubeletCreatePod), + kubeletUploadPod: TransactionDecorator.generateFakeTransaction(kubeletUploadPod), + kubeletDeletePod: TransactionDecorator.generateFakeTransaction(kubeletDeletePod), + kubeletGetPod: TransactionDecorator.generateFakeTransaction(kubeletGetPod), + kubeletGetContainerLogs: TransactionDecorator.generateFakeTransaction(kubeletGetContainerLogs), + kubeletGetPodStatus: TransactionDecorator.generateFakeTransaction(kubeletGetPodStatus), + kubeletGetPods: TransactionDecorator.generateFakeTransaction(kubeletGetPods), + kubeletGetCapacity: TransactionDecorator.generateFakeTransaction(kubeletGetCapacity), + kubeletGetNodeConditions: TransactionDecorator.generateFakeTransaction(kubeletGetNodeConditions), + kubeletGetNodeAddresses: TransactionDecorator.generateFakeTransaction(kubeletGetNodeAddresses), + kubeletGetVkToken: TransactionDecorator.generateFakeTransaction(kubeletGetVkToken), + kubeletGetSchedulerToken: TransactionDecorator.generateFakeTransaction(kubeletGetSchedulerToken), +}