diff --git a/.gitignore b/.gitignore index ea05b88e55..78473491aa 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ node_modules dist docs/src lib +tools/test-backend/config/mock.config.json src/index.scss src/plugins/.plugins.scss tools/.coverage-karma/ diff --git a/tools/package.json b/tools/package.json index 81efc3b2de..0696684649 100644 --- a/tools/package.json +++ b/tools/package.json @@ -7,6 +7,7 @@ "build": "gulp clean:dist && gulp", "lint": "gulp lint", "gate-check": "gulp lint && npm test", + "test-backend": "node test_server.js", "pretest": "npm run update-webdriver", "test": "karma start karma.conf.js --single-run", "update-webdriver": "webdriver-manager update", @@ -27,11 +28,14 @@ "wiredep": "^3.0.0" }, "devDependencies": { + "body-parser": "^1.15.2", "bower-files": "^3.14.1", "browser-sync": "^2.8.2", + "connect-nocache": "^1.1.0", "delete": "^0.3.2", "eslint": "^1.10.3", "eslint-plugin-angular": "^0.15.0", + "express": "^4.14.0", "fs-extra": "^0.30.0", "gulp-angular-filesort": "^1.1.1", "gulp-angular-gettext": "^2.1.0", @@ -43,6 +47,7 @@ "gulp-sort": "^2.0.0", "gulp-uglify": "^2.0.0", "gulp-util": "^3.0.7", + "http-proxy": "^1.15.1", "jasmine-core": "^2.4.1", "jsdoc": "^3.4.0", "karma": "^0.13.19", @@ -59,6 +64,7 @@ "protractor": "^3.0.0", "q": "^1.4.1", "request": "^2.69.0", - "shelljs": "^0.5.3" + "shelljs": "^0.5.3", + "transformer-proxy": "^0.3.3" } } diff --git a/tools/test-backend/api.js b/tools/test-backend/api.js new file mode 100644 index 0000000000..58eff78cfd --- /dev/null +++ b/tools/test-backend/api.js @@ -0,0 +1,33 @@ +'use strict'; + +var express = require('express'); +var bodyParser = require('body-parser'); + +// Mock APIs +var serviceInstances = require('./api/cnsis'); +var infoEndpoints = require('./api/info'); +var authEndpoints = require('./api/auth'); +var appEndpoints = require('./api/apps'); +var orgsEndpoints = require('./api/orgs'); + +exports.init = init; + +function init(app, config, proxy) { + + console.log('\x1b[32mTest Backend API Layer registering routes\x1b[0m'); + + /* Setup mock request for list instances */ + var mockApiRouter = express.Router(); + mockApiRouter.use(bodyParser.json()); + + + serviceInstances.init(mockApiRouter, config); + infoEndpoints.init(mockApiRouter, config); + authEndpoints.init(mockApiRouter, config, proxy); + appEndpoints.init(mockApiRouter, config, proxy); + orgsEndpoints.init(mockApiRouter, config, proxy); + + app.use('/', mockApiRouter); + + +} diff --git a/tools/test-backend/api/apps.js b/tools/test-backend/api/apps.js new file mode 100644 index 0000000000..4171362035 --- /dev/null +++ b/tools/test-backend/api/apps.js @@ -0,0 +1,331 @@ +'use strict'; + +var _ = require('lodash'); +var noCache = require('connect-nocache')(); +var utils = require('./utils'); + +exports.init = init; + +var appsTemplate = require('../data/apps.json').response; + +var cnsiApps = {}; + +function init(router, config, proxy) { + + var delay = config.apps.apiDelay || 0; + + router.get('/pp/v1/proxy/v2/apps', noCache, function (request, response) { + setTimeout(function () { + if (_.isUndefined(request.query.q)) { + response.json(mockAppsResponse(request, config)); + } else { + response.json(mockAppsResponseWithQ(request, config)); + + } + }, delay); + }); + + router.get('/pp/v1/proxy/v2/apps/:id/stats', noCache, function (request, response) { + return proxy.web(rewriteStatsRequest(request, config), response); + }); + +} + +function mockAppsResponse(request, config) { + + // How many apps to emulate per cluster + var minAppCount = config.apps.minCount; + var maxAppCount = config.apps.maxCount; + var fixedCount = config.apps.fixedCount; + var exactCounts = config.apps.exactCounts; + var appsPerPage = parseInt(request.query['results-per-page']); + var currentPage = parseInt(request.query['page']); + + // check if organizationGuid has been supplied + + var cnsiList = utils.getCnsiList(request); + + var response = {}; + _.each(cnsiList, function (cnsi, index) { + + + if (!_.has(cnsiApps, cnsi)) { + cnsiApps[cnsi] = {}; + if (fixedCount !== -1) { + cnsiApps[cnsi].appCount = fixedCount; + } else { + if (exactCounts && exactCounts[index]) { + cnsiApps[cnsi].appCount = exactCounts[index]; + } else { + cnsiApps[cnsi].appCount = Math.floor(Math.random() * maxAppCount) + minAppCount; + } + } + cnsiApps[cnsi] = determineAppsPerOrg(cnsiApps[cnsi], index, config); + } + + var cnsiResponse = _.clone(appsTemplate); + cnsiResponse.total_pages = Math.ceil(cnsiApps[cnsi].appCount / appsPerPage); + cnsiResponse.total_results = cnsiApps[cnsi].appCount; + + // Calculate how many apps should there be in the current page + cnsiResponse.resources = generateApps(cnsiApps[cnsi], currentPage, appsPerPage, config); + + if (currentPage < cnsiResponse.total_pages) { + if (currentPage !== 1) { + cnsiResponse.prev_url = generatePrevUrl(currentPage, appsPerPage); + } + cnsiResponse.next_url = generateNextUrl(currentPage, appsPerPage); + } else { + if (currentPage !== 1) { + cnsiResponse.prev_url = generatePrevUrl(currentPage, appsPerPage); + } + } + + if (request.headers['x-cnap-passthrough']) { + response = cnsiResponse; + } else { + response[cnsi] = cnsiResponse; + } + }); + + return response; +} + +function mockAppsResponseWithQ(request, config) { + + + var organizationGuid, spaceGuid; + + if (request.query.q.indexOf('space_guid') === 0) { + // SpaceGuid request + spaceGuid = request.query.q.replace('space_guid:', ''); + var index = spaceGuid.indexOf('_space'); + organizationGuid = spaceGuid.substr(0, index); + } else if (request.query.q.indexOf('organization_guid') === 0) { + organizationGuid = request.query.q.replace('organization_guid:', ''); + } + + var appsPerPage = parseInt(request.query['results-per-page']); + var currentPage = parseInt(request.query['page']); + + +// check if organizationGuid has been supplied + + var cnsiList = utils.getCnsiList(request); + + var response = {}; + _.each(cnsiList, function (cnsi, index) { + + var appCount = 0; + if (organizationGuid && _.isUndefined(cnsiApps[cnsi].orgs[organizationGuid])) { + appCount = 0; + + } else { + if (spaceGuid) { + + var space = cnsiApps[cnsi].orgs[organizationGuid][spaceGuid]; + if (_.isUndefined(space)) { + appCount = 0; + } else { + appCount = space.apps.length; + + } + } else { + appCount = cnsiApps[cnsi].orgs[organizationGuid].apps.length; + + } + } + + var cnsiResponse = _.clone(appsTemplate); + cnsiResponse.total_pages = Math.ceil(appCount / appsPerPage); + cnsiResponse.total_results = appCount; + + // Calculate how many apps should there be in the current page + cnsiResponse.resources = generateApps(cnsiApps[cnsi], currentPage, appsPerPage, config, organizationGuid, spaceGuid); + + if (currentPage < cnsiResponse.total_pages) { + if (currentPage !== 1) { + cnsiResponse.prev_url = generatePrevUrl(currentPage, appsPerPage, organizationGuid, spaceGuid); + } + cnsiResponse.next_url = generateNextUrl(currentPage, appsPerPage, organizationGuid, spaceGuid); + } else { + if (currentPage !== 1) { + cnsiResponse.prev_url = generatePrevUrl(currentPage, appsPerPage, organizationGuid, spaceGuid); + } + } + + if (request.headers['x-cnap-passthrough']) { + response = cnsiResponse; + } else { + response[cnsi] = cnsiResponse; + } + } + ) + ; + + return response; +} + +function generateApps(cnsiStruct, currentPage, appsPerPage, config, orgGuid, spaceGuid) { + + var appCount = 0; + + // Empty org/space + if (orgGuid && _.isUndefined(cnsiStruct.orgs[orgGuid])) { + return []; + } + // Empty org/space + if (spaceGuid && _.isUndefined(cnsiStruct.orgs[orgGuid][spaceGuid])) { + return []; + } + var appsArray = cnsiStruct.apps; + var totalResults = cnsiStruct.appCount; + if (spaceGuid) { + totalResults = cnsiStruct.orgs[orgGuid][spaceGuid].apps.length; + appsArray = cnsiStruct.orgs[orgGuid][spaceGuid].apps; + } else if (orgGuid) { + totalResults = cnsiStruct.orgs[orgGuid].apps.length; + appsArray = cnsiStruct.orgs[orgGuid].apps; + + } else { + totalResults = cnsiStruct.appCount; + } + + var offset = 0; + if (totalResults <= appsPerPage) { + // HCF is a smaller number of apps than appsPerPage + appCount = totalResults; + + } else { + + var appsServed = (currentPage - 1) * appsPerPage; + var remainingResults = totalResults - appsServed; + offset = appsServed; + if (remainingResults < appsPerPage) { + appCount = remainingResults; + } else { + appCount = appsPerPage; + } + } + + var resources = []; + for (var i = 0; i < appCount; i++) { + + var app = utils.clone(appsTemplate.resources[0]); + var guidVal = appsArray[offset + i]; + if (_.isUndefined(guidVal)) { + break; + } + app.entity.name = guidVal; + app.metadata.guid = guidVal; + + // If an hcf.app_id is not provided, set the state to stopped + if (_.isUndefined(config.hcf.app_id)) { + app.entity.state = 'STOPPED'; + } + resources.push(utils.clone(app)); + } + return resources; +} + +function generateNextUrl(pageNumber, appsPerPage, organizationGuid, spaceGuid) { + + var q = null; + if (spaceGuid) { + q = "q=organization_guid:" + organizationGuid + "&space_guid:" + spaceGuid; + } else if (organizationGuid) { + q = "q=organization_guid:" + organizationGuid; + + } + + if (q) { + return '/v2/apps?order-direction=asc' + q + '&page=' + (pageNumber + 1) + '&results-per-page=' + appsPerPage; + } + return '/v2/apps?order-direction=asc&page=' + (pageNumber + 1) + '&results-per-page=' + appsPerPage; +} + +function generatePrevUrl(pageNumber, appsPerPage, organizationGuid, spaceGuid) { + + var q = null; + if (spaceGuid) { + q = "q=organization_guid:" + organizationGuid + "&space_guid:" + spaceGuid; + } else if (organizationGuid) { + q = "q=organization_guid:" + organizationGuid; + + } + + if (q) { + return '/v2/apps?order-direction=asc' + q + '&page=' + (pageNumber - 1) + '&results-per-page=' + appsPerPage; + } + return '/v2/apps?order-direction=asc&page=' + (pageNumber - 1) + '&results-per-page=' + appsPerPage; +} + +function rewriteStatsRequest(request, config) { + request.url = request.url.replace(request.params.id, config.hcf.app_id); + request.headers['x-cnap-cnsi-list'] = config.hcf.cnsi; + return request; +} + +function determineAppsPerOrg(cnsi, index, config) { + + var appCount = cnsi.appCount; + var numberOfOrgs, numberOfSpaces; + + if (_.isArray(config.serviceInstances.orgs)) { + numberOfOrgs = config.serviceInstances.orgs[index].count; + numberOfSpaces = config.serviceInstances.orgs[index].spacesCount; + } else { + // All HCF instances have equal number of orgs and spaces + numberOfOrgs = config.serviceInstances.orgs.count; + numberOfSpaces = config.serviceInstances.orgs.spacesCount; + } + + // distributing apps across all spaces evenly + var numberOfAppsPerSpace = Math.floor(appCount / (numberOfOrgs * numberOfSpaces)); + + var lastSpaceApps = 0; + // check if there is a remainder for SpaceApps + var remainderForSpaces = appCount - Math.floor(numberOfAppsPerSpace * numberOfSpaces * numberOfOrgs); + if (remainderForSpaces === 0) { + // We are good + } else { + lastSpaceApps = remainderForSpaces; + } + + cnsi.orgs = {}; + cnsi.apps = []; + + + function isLast(index, total) { + return index === total - 1; + } + + var appIndex = 0; + for (var i = 0; i < numberOfOrgs; i++) { + + var orgGuid = "org_" + i; + cnsi.orgs[orgGuid] = {}; + cnsi.orgs[orgGuid].apps = []; + for (var j = 0; j < numberOfSpaces; j++) { + var spaceGuid = "org_" + i + "_space_" + j; + cnsi.orgs[orgGuid][spaceGuid] = {}; + cnsi.orgs[orgGuid][spaceGuid].apps = []; + for (var k = 0; k < numberOfAppsPerSpace; k++) { + var appName = "mock_hcf_" + index + "_app_" + appIndex++; + cnsi.orgs[orgGuid][spaceGuid].apps.push(appName); + cnsi.orgs[orgGuid].apps.push(appName); + cnsi.apps.push(appName); + } + // Add remainder app to teh last space + if (lastSpaceApps !== 0 && isLast(j, numberOfSpaces) && isLast(i, numberOfOrgs)) { + for (var g = 0; g < lastSpaceApps; g++) { + var appName = "mock_hcf_" + index + "_app_" + appIndex++; + cnsi.orgs[orgGuid][spaceGuid].apps.push(appName); + cnsi.orgs[orgGuid].apps.push(appName); + cnsi.apps.push(appName); + } + } + } + } + return cnsi; +} diff --git a/tools/test-backend/api/auth.js b/tools/test-backend/api/auth.js new file mode 100644 index 0000000000..a9f3368bfb --- /dev/null +++ b/tools/test-backend/api/auth.js @@ -0,0 +1,28 @@ +'use strict'; + +var _ = require('lodash'); +var noCache = require('connect-nocache')(); + +exports.init = init; + +function init(router, config, proxy) { + + router.get('/pp/v1/proxy/v2/config/feature_flags', noCache, function (request, response) { + return proxy.web(rewriteHeader(request, config), response); + }); + + router.get('/pp/v1/proxy/v2/users/:id/summary', noCache, function (request, response) { + return proxy.web(rewriteUserSummaryRequest(request, config), response); + }); + +} +function rewriteHeader(request, config) { + request.headers['x-cnap-cnsi-list'] = config.hcf.cnsi; + return request; +} + +function rewriteUserSummaryRequest(request, config) { + request = rewriteHeader(request, config); + request.url = request.url.replace(request.params.id, config.hcf.user_id); + return request; +} diff --git a/tools/test-backend/api/cnsis.js b/tools/test-backend/api/cnsis.js new file mode 100644 index 0000000000..e4487560c6 --- /dev/null +++ b/tools/test-backend/api/cnsis.js @@ -0,0 +1,57 @@ +'use strict'; + +var _ = require('lodash'); +var noCache = require('connect-nocache')(); + +exports.init = init; + +var responseTemplate = require('../data/cnsis.json').response; +var responseTemplateRegistered = require('../data/registered_cnsis.json').response; + +function init(router, config) { + + router.get('/pp/v1/cnsis', noCache, function (request, response) { + response.json(mockListServiceInstancesResponse(config)); + }); + + router.get('/pp/v1/cnsis/registered', noCache, function (request, response) { + response.json(mockListRegisteredServiceInstancesResponse(config)); + }); + +} + +function mockListServiceInstancesResponse(config) { + + var responseArray = []; + _.each(config.serviceInstances, function (instances, type) { + for (var i = 0; i < instances; i++) { + var obj = _.clone(responseTemplate[0]); + obj.name = 'mock_' + type + '_' + i; + obj.cnsi_type = type; + obj.guid = obj.guid + type + i; + responseArray.push(obj); + } + }); + + return responseArray; + +} + +function mockListRegisteredServiceInstancesResponse(config) { + + var responseArray = []; + _.each(config.serviceInstances, function (instances, type) { + for (var i = 0; i < instances; i++) { + var obj = _.clone(responseTemplateRegistered[0]); + obj.name = 'mock_' + type + '_' + i; + obj.guid = obj.guid + type +i; + obj.token_expiry = 1575066528; + obj.cnsi_type = type; + responseArray.push(obj); + + } + }); + + return responseArray; + +} diff --git a/tools/test-backend/api/info.js b/tools/test-backend/api/info.js new file mode 100644 index 0000000000..c0d570c57b --- /dev/null +++ b/tools/test-backend/api/info.js @@ -0,0 +1,79 @@ +'use strict'; + +var _ = require('lodash'); +var noCache = require('connect-nocache')(); +exports.init = init; + +var responseTemplate = require('../data/stackatoInfo.json').response; +var hcfInfoTemplate = require('../data/hcfInfo.json').response; +var hceInfoTemplate = require('../data/hceInfo.json').response; + +function init(router, config) { + + router.get('/pp/v1/stackato/info', noCache, function (request, response) { + response.json(mockStackatoInfo(config)); + }); + + router.get('/pp/v1/proxy/v2/info', noCache, function (request, response) { + response.json(mockHcfProxyInfo(request)); + }); + + router.get('/pp/v1/proxy/info', function (request, response) { + request.header('Cache-Control', noCache, 'private, no-cache, no-store, must-revalidate'); + response.json(mockHceProxyInfo(request)); + }); + +} + +function mockStackatoInfo(config) { + + var response = JSON.parse(JSON.stringify(responseTemplate)); + + _.each(config.serviceInstances, function (instances, type) { + + var endpoints = {}; + + for (var i = 0; i < instances; i++) { + var guid = Object.keys(responseTemplate.endpoints[type])[0]; + var endpoint = JSON.parse(JSON.stringify(response.endpoints[type][guid])); + endpoint.name = 'mock_' + type + '_' + i; + endpoint.guid = endpoint.guid + type + i; + endpoints[endpoint.guid] = endpoint; + } + response.endpoints[type] = endpoints; + }); + + return response; +} + +function mockHcfProxyInfo(request) { + + // TODO how many are disconnected + var cnsiList = request.headers['x-cnap-cnsi-list'].split(','); + + var guid = Object.keys(hcfInfoTemplate)[0]; + var endpoint = _.clone(hcfInfoTemplate[guid]); + + var responseObj = {}; + _.each(cnsiList, function (cnsi) { + responseObj[cnsi] = _.clone(endpoint); + }); + + return responseObj; +} + +function mockHceProxyInfo(request) { + + // TODO how many are disconnected + var cnsiList = request.headers['x-cnap-cnsi-list'].split(','); + + var guid = Object.keys(hceInfoTemplate)[0]; + var endpoint = _.clone(hceInfoTemplate[guid]); + + var responseObj = {}; + _.each(cnsiList, function (cnsi) { + responseObj[cnsi] = _.clone(endpoint); + }); + + return responseObj; +} diff --git a/tools/test-backend/api/orgs.js b/tools/test-backend/api/orgs.js new file mode 100644 index 0000000000..ffec3076c8 --- /dev/null +++ b/tools/test-backend/api/orgs.js @@ -0,0 +1,134 @@ +'use strict'; + +var _ = require('lodash'); +var noCache = require('connect-nocache')(); +var utils = require('./utils'); +exports.init = init; + +var appsTemplate = require('../data/organisations.json').response; +var spacesTemplate = require('../data/spaces.json').response; + +function init(router, config, proxy) { + + router.get('/pp/v1/proxy/v2/organizations', noCache, function (request, response) { + response.json(mockOrgsResponse(request, config)); + }); + router.get('/pp/v1/proxy/v2/organizations/:id/spaces', noCache, function (request, response) { + response.json(mockSpacesResponse(request, config)); + }); +} + +function mockOrgsResponse(request, config) { + + + var orgCount; + + var serviceInstance = config.serviceInstances.orgs; + if (_.isArray(config.serviceInstances.orgs)) { + var hcfIndex = utils.getCnsiIdFromHeader(request); + serviceInstance = config.serviceInstances.orgs[hcfIndex]; + } + + orgCount = serviceInstance.count; + + if (serviceInstance.addEmpty) { + orgCount += 1; + } + + var template = appsTemplate; + + var orgElement = template.resources[0]; + + var cnsiList = utils.getCnsiList(request); + var resultsPerPages = utils.getResultsPerPage(request); + + _.each(cnsiList, function (cnsi) { + var organisations = []; + for (var i = 0; i < orgCount; i++) { + organisations.push(createMockOrganisation(orgElement, cnsi, i)); + } + template.total_results = orgCount; + template.total_pages = Math.floor(orgCount / resultsPerPages); + template.resources = organisations; + }); + + return template; + +} + +function mockSpacesResponse(request, config) { + + // TODO passthrough + var spacesCount; + + var serviceInstance = config.serviceInstances.orgs; + if (_.isArray(config.serviceInstances.orgs)) { + var hcfIndex = utils.getCnsiIdFromHeader(request); + serviceInstance = config.serviceInstances.orgs[hcfIndex]; + } + + spacesCount = serviceInstance.spacesCount; + + if (serviceInstance.addEmpty) { + spacesCount += 1; + } + + var template = spacesTemplate; + + var spaceElement = template.resources[0]; + + var cnsiList = utils.getCnsiList(request); + var resultsPerPages = utils.getResultsPerPage(request); + + var orgId = request.params.id; + + _.each(cnsiList, function (cnsi) { + var spaces = []; + for (var i = 0; i < spacesCount; i++) { + spaces.push(createMockSpace(spaceElement, orgId, i)); + } + template.total_results = spacesCount; + template.total_pages = (spacesCount / resultsPerPages); + template.resources = spaces; + }); + + + return template; + +} + +function createMockOrganisation(template, cnsi, index) { + + var orgTemplate = utils.clone(template); + orgTemplate.metadata.guid = "org_" + index; + orgTemplate.entity.name = "org_" + index; + orgTemplate.entity.quota_definition_url = "/v2/quota_definitions/d32e33f7-6368-4684-9928-53b90f3097d9"; + orgTemplate.entity.spaces_url = "/v2/organizations/" + orgTemplate.guid + "/spaces"; + orgTemplate.entity.private_domains_url = "/v2/organizations/" + orgTemplate.guid + "/private_domains"; + orgTemplate.entity.users_url = "/v2/organizations/" + orgTemplate.guid + "/users_url"; + orgTemplate.entity.managers_url = "/v2/organizations/" + orgTemplate.guid + "/managers_url"; + orgTemplate.entity.billing_managers_url = "/v2/organizations/" + orgTemplate.guid + "/billing_managers_url"; + orgTemplate.entity.auditors_url = "/v2/organizations/" + orgTemplate.guid + "/auditors_url"; + orgTemplate.entity.app_events_url = "/v2/organizations/" + orgTemplate.guid + "/app_events_url"; + orgTemplate.entity.space_quota_definitions_url = "/v2/organizations/" + orgTemplate.guid + "/space_quota_definitions_url"; + + return orgTemplate; +} + +function createMockSpace(template, orgId, index) { + + var spaceTemplate = utils.clone(template); + spaceTemplate.metadata.guid = orgId + "_space_" + index; + spaceTemplate.entity.name = orgId + "_space_" + index; + spaceTemplate.entity.quota_definition_url = "/v2/quota_definitions/d32e33f7-6368-4684-9928-53b90f3097d9"; + spaceTemplate.entity.organization_url = "/v2/organizations/" + orgId; + spaceTemplate.entity.private_domains_url = "/v2/spaces/" + spaceTemplate.guid + "/private_domains"; + spaceTemplate.entity.users_url = "/v2/spaces/" + spaceTemplate.guid + "/users_url"; + spaceTemplate.entity.managers_url = "/v2/spaces/" + spaceTemplate.guid + "/managers_url"; + spaceTemplate.entity.billing_managers_url = "/v2/spaces/" + spaceTemplate.guid + "/billing_managers_url"; + spaceTemplate.entity.auditors_url = "/v2/spaces/" + spaceTemplate.guid + "/auditors_url"; + spaceTemplate.entity.app_events_url = "/v2/spaces/" + spaceTemplate.guid + "/app_events_url"; + spaceTemplate.entity.space_quota_definitions_url = "/v2/spaces/" + spaceTemplate.guid + "/space_quota_definitions_url"; + + return spaceTemplate; +} diff --git a/tools/test-backend/api/utils.js b/tools/test-backend/api/utils.js new file mode 100644 index 0000000000..c07d0f3eb4 --- /dev/null +++ b/tools/test-backend/api/utils.js @@ -0,0 +1,30 @@ +exports.getCnsiList = getCnsiList; +exports.getResultsPerPage = getResultsPerPage; +exports.clone = clone; +exports.getCnsiName = getCnsiName; +exports.getCnsiIdFromHeader = getCnsiIdFromHeader; + + +function getCnsiList(request) { + return request.headers['x-cnap-cnsi-list'].split(','); +} + +function getResultsPerPage(request) { + return parseInt(request.query['results-per-page']); +} + +function clone(object) { + return JSON.parse(JSON.stringify(object)); +} + +function getCnsiName(cnsiGuid) { + var id = cnsiGuid.split('hcf')[1]; + return "mock_hcf_" + id; +} + +function getCnsiIdFromHeader(request) { + + var cnsiList = request.headers['x-cnap-cnsi-list']; + var index = cnsiList.indexOf('hcf'); + return parseInt(cnsiList.substr(index+3, cnsiList.length)); +} diff --git a/tools/test-backend/config/mock.config.json.example b/tools/test-backend/config/mock.config.json.example new file mode 100644 index 0000000000..9c7447f784 --- /dev/null +++ b/tools/test-backend/config/mock.config.json.example @@ -0,0 +1,44 @@ +{ + "portal_proxy": { + "host": "localhost", + "port": 80, + "path": "pp" + }, + "serviceInstances": { + "hcf": 3, + "hce": 1, + "orgs":[ + { + "count": 2, + "spacesCount": 4 + }, + { + "count": 1, + "spacesCount": 1, + "addEmpty": true + }, + { + "count": 2, + "spacesCount": 1 + } + ] + }, + "hcf":{ + #CNSI ID of your HCF instance + "cnsi": "ff095d55-9831-409a-b6e5-3fb0d7fad509", + #User ID of the Admin user, for auth passthrough + "user_id": "bc44ee38-4074-48ce-ad3b-95e6e691bc98", + #ID of an online application for stats + "app_id": "8247ebc9-94bd-441c-909b-e9124efda3a1" + }, + "apps":{ + + # Specify the min/max number of application per cluster + "minCount": 50, + "maxCount": 150, + + # If you want to create fixed number of applicatons per cluster + "fixedCount": -1 + } +} + diff --git a/tools/test-backend/data/apps.json b/tools/test-backend/data/apps.json new file mode 100644 index 0000000000..e3bee97208 --- /dev/null +++ b/tools/test-backend/data/apps.json @@ -0,0 +1,59 @@ +{ + "url": "http://localhost:3100/pp/v1/proxy/v2/apps?page=1&results-per-page=48", + "response": { + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "7e083cbe-c4a7-4dd0-9322-b2c3ed0f77e5", + "url": "/v2/apps/7e083cbe-c4a7-4dd0-9322-b2c3ed0f77e5", + "created_at": "2016-08-26T19:04:33Z", + "updated_at": "2016-09-26T23:07:56Z" + }, + "entity": { + "name": "goenv", + "production": false, + "space_guid": "c8b885aa-588a-4357-a194-77e157b5a1fc", + "stack_guid": "6e4a8145-f2e4-42bf-b747-5c912d0c7775", + "buildpack": "https://github.com/cloudfoundry/go-buildpack#v1.3.1", + "detected_buildpack": "", + "environment_json": {}, + "memory": 64, + "instances": 1, + "disk_quota": 1024, + "state": "STARTED", + "version": "73ef0778-0924-4db1-898e-2e6ce5acbc8f", + "command": null, + "console": false, + "debug": null, + "staging_task_id": "fa72ed57d63d4e18b6538b1887bf31f9", + "package_state": "STAGED(N,0,0)", + "health_check_type": "port", + "health_check_timeout": null, + "staging_failed_reason": null, + "staging_failed_description": null, + "diego": true, + "docker_image": null, + "package_updated_at": "2016-08-26T19:04:35Z", + "detected_start_command": "bin/go-env", + "enable_ssh": true, + "docker_credentials_json": { + "redacted_message": "[PRIVATE DATA HIDDEN]" + }, + "ports": [ + 8080 + ], + "space_url": "/v2/spaces/c8b885aa-588a-4357-a194-77e157b5a1fc", + "stack_url": "/v2/stacks/6e4a8145-f2e4-42bf-b747-5c912d0c7775", + "routes_url": "/v2/apps/7e083cbe-c4a7-4dd0-9322-b2c3ed0f77e5/routes", + "events_url": "/v2/apps/7e083cbe-c4a7-4dd0-9322-b2c3ed0f77e5/events", + "service_bindings_url": "/v2/apps/7e083cbe-c4a7-4dd0-9322-b2c3ed0f77e5/service_bindings", + "route_mappings_url": "/v2/apps/7e083cbe-c4a7-4dd0-9322-b2c3ed0f77e5/route_mappings" + } + } + ] + } +} diff --git a/tools/test-backend/data/cnsis.json b/tools/test-backend/data/cnsis.json new file mode 100644 index 0000000000..1a1efb35e1 --- /dev/null +++ b/tools/test-backend/data/cnsis.json @@ -0,0 +1,24 @@ +{ + "url": "/pp/v1/cnsis", + "response": [ + { + "guid": "ff095d55-9831-409a-b6e5-3fb0d7fad509", + "name": "LOL", + "cnsi_type": "hcf", + "api_endpoint": { + "Scheme": "http", + "Opaque": "", + "User": null, + "Host": "api.hcf.hsc.stacktest.io", + "Path": "", + "RawPath": "", + "RawQuery": "", + "Fragment": "" + }, + "authorization_endpoint": "https://login.hcf.helion.lol", + "token_endpoint": "https://uaa.hcf.helion.lol", + "doppler_logging_endpoint": "wss://doppler.hcf.helion.lol:4443", + "skip_ssl_validation": true + } + ] +} diff --git a/tools/test-backend/data/hceInfo.json b/tools/test-backend/data/hceInfo.json new file mode 100644 index 0000000000..251ae506bc --- /dev/null +++ b/tools/test-backend/data/hceInfo.json @@ -0,0 +1,10 @@ +{ + "url": "/pp/v1/proxy/info", + "response": { + "f99021eb-fb8e-4819-813c-558c2311649b": { + "api_latest_version": 2, + "api_public_uri": "https://a2d908311822111e6ac740669ec078d9-2076574576.eu-central-1.elb.amazonaws.com:443/v2", + "auth_endpoint": "https://hce.identity.hscdemo.stacktest.io:443" + } + } +} diff --git a/tools/test-backend/data/hcfInfo.json b/tools/test-backend/data/hcfInfo.json new file mode 100644 index 0000000000..157391f783 --- /dev/null +++ b/tools/test-backend/data/hcfInfo.json @@ -0,0 +1,24 @@ +{ + "url": "http://localhost:3100/pp/v1/proxy/v2/info", + "response": { + "ff095d55-9831-409a-b6e5-3fb0d7fad509": { + "name": "", + "build": "", + "support": "http://support.cloudfoundry.com", + "version": 0, + "description": "Helion Cloud Foundry", + "authorization_endpoint": "https://login.hcf.helion.lol", + "token_endpoint": "https://uaa.hcf.helion.lol", + "min_cli_version": null, + "min_recommended_cli_version": null, + "api_version": "2.58.0", + "app_ssh_endpoint": "ssh.hcf.helion.lol:2222", + "app_ssh_host_key_fingerprint": "33:0e:b8:43:cf:16:13:f9:cb:e7:df:ce:54:a3:6e:6c", + "app_ssh_oauth_client": "ssh-proxy", + "routing_endpoint": "https://api.hcf.helion.lol/routing", + "logging_endpoint": "wss://loggregator.hcf.helion.lol:4443", + "doppler_logging_endpoint": "wss://doppler.hcf.helion.lol:4443", + "user": "ae257571-e323-4cd9-bd97-2b21223d9b36" + } + } +} diff --git a/tools/test-backend/data/organisations.json b/tools/test-backend/data/organisations.json new file mode 100644 index 0000000000..29d7f08a29 --- /dev/null +++ b/tools/test-backend/data/organisations.json @@ -0,0 +1,34 @@ +{ + "response": { + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "75237811-febb-4d30-a864-9d30507bcc26", + "url": "/v2/organizations/75237811-febb-4d30-a864-9d30507bcc26", + "created_at": "2016-09-30T04:04:53Z", + "updated_at": null + }, + "entity": { + "name": "dev", + "billing_enabled": false, + "quota_definition_guid": "d32e33f7-6368-4684-9928-53b90f3097d9", + "status": "active", + "quota_definition_url": "/v2/quota_definitions/d32e33f7-6368-4684-9928-53b90f3097d9", + "spaces_url": "/v2/organizations/75237811-febb-4d30-a864-9d30507bcc26/spaces", + "domains_url": "/v2/organizations/75237811-febb-4d30-a864-9d30507bcc26/domains", + "private_domains_url": "/v2/organizations/75237811-febb-4d30-a864-9d30507bcc26/private_domains", + "users_url": "/v2/organizations/75237811-febb-4d30-a864-9d30507bcc26/users", + "managers_url": "/v2/organizations/75237811-febb-4d30-a864-9d30507bcc26/managers", + "billing_managers_url": "/v2/organizations/75237811-febb-4d30-a864-9d30507bcc26/billing_managers", + "auditors_url": "/v2/organizations/75237811-febb-4d30-a864-9d30507bcc26/auditors", + "app_events_url": "/v2/organizations/75237811-febb-4d30-a864-9d30507bcc26/app_events", + "space_quota_definitions_url": "/v2/organizations/75237811-febb-4d30-a864-9d30507bcc26/space_quota_definitions" + } + } + ] + } +} diff --git a/tools/test-backend/data/registered_cnsis.json b/tools/test-backend/data/registered_cnsis.json new file mode 100644 index 0000000000..4217e4b1c8 --- /dev/null +++ b/tools/test-backend/data/registered_cnsis.json @@ -0,0 +1,23 @@ +{ + "url": "/pp/v1/cnsis/registered", + "response": [ + { + "guid": "ff095d55-9831-409a-b6e5-3fb0d7fad509", + "name": "HCF", + "cnsi_type": "hcf", + "api_endpoint": { + "Scheme": "https", + "Opaque": "", + "User": null, + "Host": "api.hcf.hsc.stacktest.io", + "Path": "/", + "RawPath": "", + "RawQuery": "", + "Fragment": "" + }, + "account": "63ddbbe1-a185-465d-ba10-63d5c01f1a99", + "token_expiry": 1475066528, + "skip_ssl_validation": true + } + ] +} diff --git a/tools/test-backend/data/spaces.json b/tools/test-backend/data/spaces.json new file mode 100644 index 0000000000..29310c75e3 --- /dev/null +++ b/tools/test-backend/data/spaces.json @@ -0,0 +1,35 @@ +{ + "response": { + "total_results": 1, + "total_pages": 1, + "prev_url": null, + "next_url": null, + "resources": [ + { + "metadata": { + "guid": "2e1df160-2ab8-498c-8be4-fda9b8a126c8", + "url": "/v2/spaces/2e1df160-2ab8-498c-8be4-fda9b8a126c8", + "created_at": "2016-09-30T04:05:13Z", + "updated_at": null + }, + "entity": { + "name": "dev", + "organization_guid": "75237811-febb-4d30-a864-9d30507bcc26", + "space_quota_definition_guid": null, + "allow_ssh": true, + "organization_url": "/v2/organizations/75237811-febb-4d30-a864-9d30507bcc26", + "developers_url": "/v2/spaces/2e1df160-2ab8-498c-8be4-fda9b8a126c8/developers", + "managers_url": "/v2/spaces/2e1df160-2ab8-498c-8be4-fda9b8a126c8/managers", + "auditors_url": "/v2/spaces/2e1df160-2ab8-498c-8be4-fda9b8a126c8/auditors", + "apps_url": "/v2/spaces/2e1df160-2ab8-498c-8be4-fda9b8a126c8/apps", + "routes_url": "/v2/spaces/2e1df160-2ab8-498c-8be4-fda9b8a126c8/routes", + "domains_url": "/v2/spaces/2e1df160-2ab8-498c-8be4-fda9b8a126c8/domains", + "service_instances_url": "/v2/spaces/2e1df160-2ab8-498c-8be4-fda9b8a126c8/service_instances", + "app_events_url": "/v2/spaces/2e1df160-2ab8-498c-8be4-fda9b8a126c8/app_events", + "events_url": "/v2/spaces/2e1df160-2ab8-498c-8be4-fda9b8a126c8/events", + "security_groups_url": "/v2/spaces/2e1df160-2ab8-498c-8be4-fda9b8a126c8/security_groups" + } + } + ] + } +} diff --git a/tools/test-backend/data/stackatoInfo.json b/tools/test-backend/data/stackatoInfo.json new file mode 100644 index 0000000000..756a93119b --- /dev/null +++ b/tools/test-backend/data/stackatoInfo.json @@ -0,0 +1,42 @@ +{ + "url": "http://localhost:3100/pp/v1/stackato/info", + "response": { + "version": { + "proxy_version": "dev", + "database_version": 20160511195737 + }, + "user": { + "guid": "63ddbbe1-a185-465d-ba10-63d5c01f1a99", + "name": "admin@cnap.local", + "admin": true + }, + "endpoints": { + "hce": { + "ff095d55-9831-409a-b6e5-3fb0d7fad50f": { + "guid": "ff095d55-9831-409a-b6e5-3fb0d7fad50f", + "name": "LOL", + "version": "", + "user": { + "guid": "ae257571-e323-4cd9-bd97-2b21223d9b36", + "name": "admin", + "admin": true + }, + "type": "" + } + }, + "hcf": { + "ff095d55-9831-409a-b6e5-3fb0d7fad509": { + "guid": "ff095d55-9831-409a-b6e5-3fb0d7fad509", + "name": "LOL", + "version": "", + "user": { + "guid": "ae257571-e323-4cd9-bd97-2b21223d9b36", + "name": "admin", + "admin": true + }, + "type": "" + } + } + } + } +} diff --git a/tools/test-backend/data/stats.json b/tools/test-backend/data/stats.json new file mode 100644 index 0000000000..0d36e60cda --- /dev/null +++ b/tools/test-backend/data/stats.json @@ -0,0 +1,39 @@ +{ + "url": "http://localhost:3100/pp/v1/proxy/v2/apps/b079c730-b2e2-42ae-ac57-f9715495d2f4/stats", + "response": { + "0": { + "state": "RUNNING", + "stats": { + "name": "ge-python-env", + "uris": [ + "ge-python-env.hcf.helion.lol" + ], + "host": "172.20.10.24", + "port": 61434, + "net_info": { + "address": "172.20.10.24", + "ports": [ + { + "container_port": 8080, + "host_port": 61434 + }, + { + "container_port": 2222, + "host_port": 61435 + } + ] + }, + "uptime": 574939, + "mem_quota": 33554432, + "disk_quota": 1073741824, + "fds_quota": 16384, + "usage": { + "time": "2016-09-27T14:28:38.821241031Z", + "cpu": 0.00013580323988798018, + "mem": 21004288, + "disk": 121556992 + } + } + } + } +} diff --git a/tools/test-backend/data/userCnsiModel.json b/tools/test-backend/data/userCnsiModel.json new file mode 100644 index 0000000000..8b5dda12a0 --- /dev/null +++ b/tools/test-backend/data/userCnsiModel.json @@ -0,0 +1,23 @@ +{ + "url": "http://localhost:3100/pp/v1/cnsis/registered", + "response": [ + { + "guid": "ef28c1bd-8d65-4d04-86b2-fe002e9e65de", + "name": "LOL", + "cnsi_type": "hcf", + "api_endpoint": { + "Scheme": "http", + "Opaque": "", + "User": null, + "Host": "api.hcf.helion.lol", + "Path": "", + "RawPath": "", + "RawQuery": "", + "Fragment": "" + }, + "account": "63ddbbe1-a185-465d-ba10-63d5c01f1a99", + "token_expiry": 1474987087, + "skip_ssl_validation": true + } + ] +} diff --git a/tools/test-backend/package.json b/tools/test-backend/package.json new file mode 100644 index 0000000000..462d7c97bc --- /dev/null +++ b/tools/test-backend/package.json @@ -0,0 +1,17 @@ +{ + "name": "mock-server", + "version": "1.0.0", + "description": "", + "scripts": {}, + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.15.2", + "connect-nocache": "^1.1.0", + "express": "^4.14.0", + "lodash": "^4.16.2", + "node-uuid": "^1.4.7", + "randomstring": "^1.1.5", + "transformer-proxy": "^0.3.3" + } +} diff --git a/tools/test_server.js b/tools/test_server.js new file mode 100644 index 0000000000..fb5b66d878 --- /dev/null +++ b/tools/test_server.js @@ -0,0 +1,84 @@ +'use strict'; + +var express = require('express'); +var http = require('http'); +var app = express(); +var path = require('path'); +var _ = require('lodash'); +var config; + +var unSupportedRequests = [ + '/pp/v1/auth', + '/pp/v1/version' +]; + +try { + // Need a JSON file named 'mock.config.json' + var configFileName = path.resolve(__dirname, './test-backend/config/mock.config.json'); + console.log(configFileName); + config = require(configFileName); +} catch (e) { + console.log(e) + throw 'Can not find the required mock.config.json configuration file'; +} + +var port = config.port || 4000; + +// Delay to simulate slower proxy API calls +var delay = config.delay || 0; + +var staticFiles = path.join(__dirname, '..', 'dist'); +app.use(express.static(staticFiles)); + +var httpProxy = require('http-proxy'); +var proxy = httpProxy.createServer({ + target: { + host: config.portal_proxy.host, + port: config.portal_proxy.port || 80 + } +}); + + +var mockApi = require('./test-backend/api'); +mockApi.init(app, config, proxy); + +if (delay > 0) { + app.use(function (req, res, next) { + // Only delay those calls that are proxied calls to HCF or HCE endpoints + setTimeout(next, delay); + }); +} +app.use(function (req, res, next) { + if (unSupportedRequested(req.url)) { + console.log('Forwarding to portal-proxy: ' + req.method + ' ' + req.url); + return proxy.web(req, res); + } else { + return next(); + } +}); + + +var server = http.createServer(app); + +server.on('upgrade', function (req, socket, head) { + proxy.ws(req, socket, head); +}); + + +server.listen(port, function () { + console.log('\x1b[32mStackato Console Test HTTP Server starts listening on %d ...\x1b[0m', port); +}); + + +function unSupportedRequested(url) { + + // Don't support authentication + var unSupported = false; + _.each(unSupportedRequests, function (request) { + if (url.indexOf(request) === 0) { + unSupported = true; + } + }); + + return unSupported; +}