diff options
-rw-r--r-- | modules/orionode/lib/cf/apps.js | 76 | ||||
-rw-r--r-- | modules/orionode/lib/cf/manifests.js | 242 | ||||
-rw-r--r-- | modules/orionode/lib/cf/plans.js | 5 | ||||
-rw-r--r-- | modules/orionode/lib/cf/services.js | 186 | ||||
-rw-r--r-- | modules/orionode/package.json | 3 |
5 files changed, 376 insertions, 136 deletions
diff --git a/modules/orionode/lib/cf/apps.js b/modules/orionode/lib/cf/apps.js index 45695e4..61e6ef4 100644 --- a/modules/orionode/lib/cf/apps.js +++ b/modules/orionode/lib/cf/apps.js @@ -248,7 +248,7 @@ function putapps(req, res){ return; } for(var key in instrumentationJSON){ - var value = instrumentationJSON.key; + var value = instrumentationJSON[key]; for(var j = 0; j < manifest.applications.length ; j++){ if(key === "memory" && !updateMemory(manifest.applications[j],value)){ continue; @@ -361,30 +361,30 @@ function startApp(userId, userTimeout ,appTarget){ return collectCFRespond() .then(function(result){ if(!result.data[0] && result.attemptsLeft > 0){ - return promiseWhile(result.attemptsLeft); - }else if(result.data[0]){ - var instancesNo = Object.keys(result.data).length; - var runningInstanceNo = 0; - var flappingInstanceNo = 0; - for (var key in result.data) { - if (result.data.hasOwnProperty(key)) { - if(result.data[key].state === "RUNNING"){ - runningInstanceNo++; - }else if(result.data[key].state === "FLAPPING"){ - flappingInstanceNo++; - } + return promiseWhile(result.attemptsLeft); + }else if(result.data[0] && result.attemptsLeft > 0){ + var instancesNo = Object.keys(result.data).length; + var runningInstanceNo = 0; + var flappingInstanceNo = 0; + for (var key in result.data) { + if (result.data.hasOwnProperty(key)) { + if(result.data[key].state === "RUNNING"){ + runningInstanceNo++; + }else if(result.data[key].state === "FLAPPING"){ + flappingInstanceNo++; } } - if (runningInstanceNo === instancesNo) { - return "RUNNING"; - } - if (flappingInstanceNo > 0 ) { - return "FLAPPING"; - } - return promiseWhile(result.attemptsLeft); - }else if(result.attemptsLeft === 0 ){ - return "TIMEOUT"; } + if (runningInstanceNo === instancesNo) { + return "RUNNING"; + } + if (flappingInstanceNo > 0 ) { + return "FLAPPING"; + } + return promiseWhile(result.attemptsLeft); + }else if(result.attemptsLeft === 0 ){ + return "TIMEOUT"; + } }); }); } @@ -504,7 +504,7 @@ function bindRoute(req, res, appTarget){ } } else { /* client has not requested a specific domain, get the first available */ - appCache.appDomain = domains.get(0); + appCache.appDomain = domainArray[0]; } }) /* find out whether the declared host can be reused */ @@ -616,7 +616,7 @@ function uploadBits(req, res, appTarget){ } }); } -function bindServices(req, res, appTarget){ // TODO need test case!! +function bindServices(req, res, appTarget){ if(appCache.manifest.applications[0].services){ return target.cfRequest("GET", req.user.username, null, appTarget.Url + "/v2/services", {"inline-relations-depth":"1"}) .then(function(result){ @@ -626,7 +626,7 @@ function bindServices(req, res, appTarget){ // TODO need test case!! if(version === 2){ return new Promise(function(fulfill,reject){ async.each(manifestService, function(service, cb) { - return getServiceGuid(req.user.username, service) + return getServiceGuid(req.user.username, service, appTarget) .then(function(serviceInstanceGUID){ if(!serviceInstanceGUID){ /* no service instance bound to the application, create one if possible */ @@ -655,32 +655,32 @@ function bindServices(req, res, appTarget){ // TODO need test case!! } return serviceInstanceGUID; }).then(function(serviceInstanceGUID){ - return bindService(req.user.username, serviceInstanceGUID); + return bindService(req.user.username, serviceInstanceGUID, appTarget); }).then(function(){ - cb(); + return cb(); }); }, function(err) { if(err){ return reject(err); } - fulfill(); + return fulfill(); }); }); } if(version === 6){ return new Promise(function(fulfill,reject){ async.each(manifestService, function(service, cb) { - return getServiceGuid(req.user.username, service) + return getServiceGuid(req.user.username, service, appTarget) .then(function(serviceInstanceGUID){ - return bindService(req.user.username, serviceInstanceGUID); + return bindService(req.user.username, serviceInstanceGUID, appTarget); }).then(function(){ - cb(); + return cb(); }); }, function(err) { if(err){ return reject(err); } - fulfill(); + return fulfill(); }); }); } @@ -690,7 +690,7 @@ function bindServices(req, res, appTarget){ // TODO need test case!! } function getServiceGuid(userId, service, appTarget){ return target.cfRequest("GET", userId, null, appTarget.Url + "/v2/spaces/" + appTarget.Space.metadata.guid + "/service_instances" - , {"inline-relations-depth":"1","return_user_provided_service_instances":"true","q":"name:"+service.label}) + , {"inline-relations-depth":"1","return_user_provided_service_instances":"true","q":"name:"+service}) .then(function(serviceJson){ var serviceResources = serviceJson.resources; var serviceInstanceGUID; @@ -723,7 +723,7 @@ function bindService(userId, serviceGuid, appTarget){ function createRoute(req, res, appTarget){ var body = { "space_guid": appTarget.Space.metadata.guid, - "host":appCache.manifest.applications[0].host || slugify(appCache.appName), + "host":appCache.manifest.applications[0].host, "domain_guid":appCache.appDomain.Guid }; return target.cfRequest("POST", req.user.username, null, appTarget.Url + "/v2/routes", {"inline-relations-depth":"1"}, JSON.stringify(body)); @@ -761,14 +761,6 @@ function normalizeMemoryMeasure(memory){ /* return default memory value, i.e. 1024 MB */ return 1024; } -function slugify(inputString){ - return inputString.toString().toLowerCase() - .replace(/\s+/g, "-") // Replace spaces with - - .replace(/[^\w\-]+/g, "") // Remove all non-word chars - .replace(/\-\-+/g, "-") // Replace multiple - with single - - .replace(/^-+/, "") // Trim - from start of text - .replace(/-+$/, ""); // Trim - from end of text -} function archiveTarget (filePath){ var ramdomName = crypto.randomBytes(5).toString("hex") + Date.now(); var resultFilePath = path.join(xfer.getUploadDir(), ramdomName + ".war"); diff --git a/modules/orionode/lib/cf/manifests.js b/modules/orionode/lib/cf/manifests.js index 3bc5ebc..e0a6ae5 100644 --- a/modules/orionode/lib/cf/manifests.js +++ b/modules/orionode/lib/cf/manifests.js @@ -15,6 +15,8 @@ var api = require("../api"), writeError = api.writeError; var fs = require("fs"); var path = require("path"); var yaml = require("js-yaml"); +var yamlAstParser = require("yaml-ast-parser"); +var tasks = require("../tasks"); module.exports.router = function() { @@ -43,17 +45,245 @@ function retrieveManifestFile(req, res, manifestAbsuluteLocation){ if(filePath.indexOf("manifest.yml") === -1){ filePath += "manifest.yml"; } - try { - var manifest = yaml.safeLoad(fs.readFileSync(filePath, "utf8")); - } catch (err) { - writeError(404, res, err.message); - } - fulfill(manifest); + fs.readFile(filePath, "utf8", function(err, fileContent){ + if(err){ + writeError(404, res, err.message); + } + fulfill(fileContent); + }); + }).then(function(fileContent){ + var manifest = yaml.safeLoad(fileContent); + var manifestAST = yamlAstParser.load(fileContent); + transformManifest(manifest); + // TODO when meet the case where need to do symbolResolver (as in JAVA) then implement. + analizeManifest(manifest, res, manifestAST, fileContent); + setDefaultManifestProperties(req,manifest); + return manifest; }); } +function setDefaultManifestProperties(req,manifest){ + function getDefaultName(rawProjectName){ + var nameParts = rawProjectName.split(" --- ", 2); + return nameParts.length > 1 ? nameParts[1] : nameParts[0]; + } + function getDefaultHost(rawProjectName){ + return slugify(rawProjectName); + } + var rawContentLocationData = req.params[0].split("/"); + var rawDefaultProjectName = rawContentLocationData[rawContentLocationData.length - 2]; + rawDefaultProjectName = rawDefaultProjectName.replace(/\|/, " --- "); + var MUST_HAVE_PROPERTITIES ={ + name : getDefaultName(rawDefaultProjectName), + host : getDefaultHost(rawDefaultProjectName), + memory : "512M", + instances : "1", + path: "." + }; + Object.keys(MUST_HAVE_PROPERTITIES).forEach(function(key){ + if(!manifest.applications[0].hasOwnProperty(key)){ + manifest.applications[0][key] = MUST_HAVE_PROPERTITIES[key]; + } + }); +} +function transformManifest(manifest){ + if(!manifest.applications){ + return; // Do nothing + } + var globals = []; + var APPLICATION_PROPERTIES = ["name", "memory", "host", "buildpack", "command", // //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$//$NON-NLS-4$ //$NON-NLS-5$ + "domain", "instances", "path", "timeout", "no-route", "services"]; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ + Object.keys(manifest).forEach(function(key){ + if(APPLICATION_PROPERTIES.indexOf(key) !== -1){ + globals.push(key); + } + }); + if(!globals){ + return; // nothing to do + } + manifest.applications.forEach(function(application){ + for( var k = 0; k < globals.length ; k++){ + if(!application.hasOwnProperty(globals[k])){ + application[globals[k]] = manifest[globals[k]]; + } + } + }); +} +function analizeManifest(manifest, res, manifestAST, fileContent){ + if(!manifest.applications){ + return; // Do nothing + } + var fileContentArray = fileContent.split("\r\n") ; + var lineNumbers = []; + var valueWithParent = []; + var currentLineFirstCharAt = 0; + for(var i = 0; i < fileContentArray.length ; i++){ + var lineLength = fileContentArray[i].length; + lineNumbers.push([currentLineFirstCharAt, currentLineFirstCharAt + lineLength ]); + currentLineFirstCharAt = currentLineFirstCharAt + lineLength + 2; + } + nullCheckOfManifest(manifestAST); + specificFieldCheck(valueWithParent); + + function nullCheckOfManifest(manifestAST){ + if(!manifestAST){ + sendInvalidResult("Empty Node", -1, res); + } + if(manifestAST.hasOwnProperty("mappings")){ + manifestAST.mappings.forEach(function(each){ + nullCheckOfManifest(each); + }); + }else if(manifestAST.hasOwnProperty("items")){ + manifestAST.items.forEach(function(each){ + nullCheckOfManifest(each); + }); + }else if(manifestAST.hasOwnProperty("value")){ + if(!manifestAST.value){ // null case + // find null + sendInvalidResult("Empty Propety",getLineNumber(manifestAST.startPosition, manifestAST.endPosition, lineNumbers),res); + } + if(typeof manifestAST.value === "string"){ + // record the node with real yaml values here. to save from another after null check. aka, do both together. + valueWithParent.push(manifestAST); + } + nullCheckOfManifest(manifestAST.value); + } + } + function specificFieldCheck(valueWithParent){ + for(var m = 0; m < valueWithParent.length; m++){ + var realParentNode = {nodeKey : "", node:{}}; + findclosestKeyName(valueWithParent[m], realParentNode); + switch(realParentNode.nodeKey){ + case "services": + if(isStringProperty(realParentNode.node)){ + sendInvalidResult("Invalid services declaration. Expected a list of service names.", getLineNumber(realParentNode.node.startPosition, realParentNode.node.endPosition, lineNumbers), res); + } + break; + case "buildpack": + if(!isStringProperty(realParentNode.node)){ + sendInvalidResult("Invalid \"buildpack\" value. Expected a string literal.", getLineNumber(realParentNode.node.startPosition, realParentNode.node.endPosition, lineNumbers), res); + } + break; + case "command": + if(!isStringProperty(realParentNode.node)){ + sendInvalidResult("Invalid \"command\" value. Expected a string literal.", getLineNumber(realParentNode.node.startPosition, realParentNode.node.endPosition, lineNumbers), res); + } + break; + case "domain": + if(!isStringProperty(realParentNode.node)){ + sendInvalidResult("Invalid \"domain\" value. Expected a string literal.", getLineNumber(realParentNode.node.startPosition, realParentNode.node.endPosition, lineNumbers), res); + } + break; + case "host": + if(!isStringProperty(realParentNode.node)){ + sendInvalidResult("Invalid \"host\" value. Expected a string literal.", getLineNumber(realParentNode.node.startPosition, realParentNode.node.endPosition, lineNumbers), res); + } + break; + case "path": + if(!isStringProperty(realParentNode.node)){ + sendInvalidResult("Invalid \"path\" value. Expected a string literal.", getLineNumber(realParentNode.node.startPosition, realParentNode.node.endPosition, lineNumbers), res); + } + break; + case "memory": + if(!isStringProperty(realParentNode.node)){ + sendInvalidResult("Invalid \"memory\" value. Expected a memory limit.", getLineNumber(realParentNode.node.startPosition, realParentNode.node.endPosition, lineNumbers), res); + } + if(!isValidMemoryProperty(valueWithParent[m].value)){ + sendInvalidResult("Invalid \"memory\" limit; Supported measurement units are M/MB, G/GB.", getLineNumber(valueWithParent[m].startPosition, valueWithParent[m].endPosition, lineNumbers), res); + } + break; + case "instances": + if(!isStringProperty(realParentNode.node)){ + sendInvalidResult("Invalid \"instances\" value. Expected a non-negative integer value.", getLineNumber(realParentNode.node.startPosition, realParentNode.node.endPosition, lineNumbers), res); + } + if(!isValidNonNegativeProperty(valueWithParent[m].value)){ + sendInvalidResult("Invalid \"instances\" value. Expected a non-negative integer value.", getLineNumber(valueWithParent[m].startPosition, valueWithParent[m].endPosition, lineNumbers), res); + } + break; + case "timeout": + if(!isStringProperty(realParentNode.node)){ + sendInvalidResult("Invalid \"timeout\" value. Expected a non-negative integer value.", getLineNumber(realParentNode.node.startPosition, realParentNode.node.endPosition, lineNumbers), res); + } + if(!isValidNonNegativeProperty(valueWithParent[m].value)){ + sendInvalidResult("Invalid \"timeout\" value. Expected a non-negative integer value.", getLineNumber(valueWithParent[m].startPosition, valueWithParent[m].endPosition, lineNumbers), res); + } + break; + case "no-route": + if(!isStringProperty(realParentNode.node)){ + sendInvalidResult("Invalid \"no-route\" value. Expected a string literal \"true\".", getLineNumber(realParentNode.node.startPosition, realParentNode.node.endPosition, lineNumbers), res); + } + if(valueWithParent[m].value !== "true"){ + sendInvalidResult("Invalid \"no-route\" value. Expected a string literal \"true\".", getLineNumber(valueWithParent[m].startPosition, valueWithParent[m].endPosition, lineNumbers), res); + } + break; + } + } + } + function isStringProperty(parentNode){ + if(parentNode.value.hasOwnProperty("value") && typeof parentNode.value.value === "string"){ + return true; + } + return false; + } + function isValidNonNegativeProperty(value){ + if(/^[1-9][0-9]*/.test(value)){ + return true; + } + return false; + } + function isValidMemoryProperty(value){ + if(/^[1-9][0-9]*(M|MB|G|GB|m|mb|g|gb)/.test(value)){ + return true; + } + return false; + } + function findclosestKeyName(valueNode, realParentNode){ + if(valueNode.hasOwnProperty("key")){ + realParentNode.nodeKey = valueNode.key.value; + realParentNode.node = valueNode; + return; + } + findclosestKeyName(valueNode.parent, realParentNode); + } + function getLineNumber(start, end, lineNumberArray){ + if(start && end){ + for(var j = 0; j < lineNumberArray.length ; j++){ + if(start >= lineNumberArray[j][0] && end <= lineNumberArray[j][1]){ + return j + 1; + } + } + } + return -1; + } + function sendInvalidResult(message, lineNumber, res){ + var task = new tasks.Task(res, false, false, 0, false); + var resultJson = { + "Severity":"Warning", + "Message": message + }; + if(lineNumber !== -1){ + resultJson.Line = lineNumber; + } + task.done({ + HttpCode: 400, + Code: 0, + DetailedMessage: message, + JsonData: resultJson, + Message: message, + Severity: "Error" + }); + } +} function retrieveProjectFilePath(req){ var projectPath = req.params[0]; return path.join(req.user.workspaceDir, projectPath); } +function slugify(inputString){ + return inputString.toString().toLowerCase() + .replace(/\s+/g, "-") // Replace spaces with - + .replace(/[^\w\-]+/g, "") // Remove all non-word chars + .replace(/\-\-+/g, "-") // Replace multiple - with single - + .replace(/^-+/, "") // Trim - from start of text + .replace(/-+$/, ""); // Trim - from end of text +} };
\ No newline at end of file diff --git a/modules/orionode/lib/cf/plans.js b/modules/orionode/lib/cf/plans.js index b171f95..2e53e3c 100644 --- a/modules/orionode/lib/cf/plans.js +++ b/modules/orionode/lib/cf/plans.js @@ -14,6 +14,7 @@ var bodyParser = require("body-parser"); var fs = require("fs"); var path = require("path"); var manifests = require("./manifests"); +var apps = require("./apps"); module.exports.router = function() { @@ -36,7 +37,7 @@ function planJson(type, manifest , planner , wizard){ function getplans(req, res){ var filePath = manifests.retrieveProjectFilePath(req); Promise.resolve(manifests.retrieveManifestFile(req)) - .then(function(manifests){ + .then(function(manifest){ var children = []; function generatePlansforManifest(manifest,children){ function generateGenericPlan(manifest){ @@ -54,7 +55,7 @@ function getplans(req, res){ children.push(nodePlan); } } - generatePlansforManifest(manifests,children); + generatePlansforManifest(manifest,children); var result = {"Children": children}; res.status(200).json(result); }); diff --git a/modules/orionode/lib/cf/services.js b/modules/orionode/lib/cf/services.js index 193c1f9..6a9fa1e 100644 --- a/modules/orionode/lib/cf/services.js +++ b/modules/orionode/lib/cf/services.js @@ -16,96 +16,112 @@ var tasks = require("../tasks"); var async = require("async"); module.exports.router = function() { - + return express.Router() - .use(bodyParser.json()) - .get("*", getService); - -function getService(req, res){ - var task = new tasks.Task(res,false,false,0,false); - var targetRequest = JSON.parse(req.query.Target); - var targetURL = targetRequest.Url; - var spaceGuid = targetRequest.SpaceId; - new Promise(function(fulfill, reject) { - var serviceUrl = "/v2/spaces/" + spaceGuid + "/service_instances"; - var fullService = []; - function promiseWhile(doRequest, value) { - return Promise.resolve(value).then(doRequest).then(function(result) { - return collectServiceInfo(result) - .then(function(){ - if(result && result.next_url){ - serviceUrl = result.next_url; - return promiseWhile(doRequest, serviceUrl); - } - return null; - }); - }); - } - function doRequest(serviceUrl){ - return target.cfRequest("GET", req.user.username, task, targetURL + serviceUrl,{"inline-relations-depth":"1","return_user_provided_service_instances":"true"}); - } - function collectServiceInfo(result){ - var serviceResources = result && result.resources; - if(serviceResources){ - return new Promise(function(fulfill , reject){ - async.each(serviceResources, function(resource, cb) { - var isBindable = true; - if(resource.entity.service_plan){ - var serviceEntity = resource.entity.service_plan.entity; - var serviceGuid = serviceEntity.service_guid; - return getCFService(req.user.username, targetURL,serviceGuid) - .then(function(singleServiceresult){ - isBindable = singleServiceresult.entity.bindable; - }).then(function(){ - if(isBindable){ - var ServiceJson = { - "Name": resource.entity.name, - "Type": "Service" - }; - fullService.push(ServiceJson); + .use(bodyParser.json()) + .get("*", getService); + + function getService(req, res) { + var task = new tasks.Task(res, false, false, 0, false); + var targetRequest = JSON.parse(req.query.Target); + var targetURL = targetRequest.Url; + var spaceGuid = targetRequest.SpaceId; + if (spaceGuid && targetURL) { + new Promise(function(fulfill, reject) { + var serviceUrl = "/v2/spaces/" + spaceGuid + "/service_instances"; + var fullService = []; + function promiseWhile(doRequest, value) { + return Promise.resolve(value).then(doRequest).then(function(result) { + return collectServiceInfo(result) + .then(function() { + if (result && result.next_url) { + serviceUrl = result.next_url; + return promiseWhile(doRequest, serviceUrl); + } + return null; + }); + }); + } + + function doRequest(serviceUrl) { + return target.cfRequest("GET", req.user.username, task, targetURL + serviceUrl, { + "inline-relations-depth": "1", + "return_user_provided_service_instances": "true" + }); + } + + function collectServiceInfo(result) { + var serviceResources = result && result.resources; + if (serviceResources) { + return new Promise(function(fulfill, reject) { + async.each(serviceResources, function(resource, cb) { + var isBindable = true; + if (resource.entity.service_plan) { + var serviceEntity = resource.entity.service_plan.entity; + var serviceGuid = serviceEntity.service_guid; + return getCFService(req.user.username, targetURL, serviceGuid) + .then(function(singleServiceresult) { + isBindable = singleServiceresult.entity.bindable; + }).then(function() { + if (isBindable) { + var ServiceJson = { + "Name": resource.entity.name, + "Type": "Service" + }; + fullService.push(ServiceJson); + } + cb(); + }); + } + }, function(err) { + if (err) { + return reject(err); } - cb(); + fulfill(); }); - } - }, function(err) { - if(err){ - return reject(err); - } - fulfill(); + }); + } + return Promise.resolve(); + } + return promiseWhile(doRequest, serviceUrl) + .then(function() { + fulfill(fullService); }); + }).then(function(fullService) { + var resp = { + "Children": fullService + }; + task.done({ + HttpCode: 200, + Code: 0, + DetailedMessage: "OK", + JsonData: resp, + Message: "OK", + Severity: "Ok" + }); + }).catch(function(err) { + task.done({ + HttpCode: 401, + Code: 0, + JsonData: {}, + DetailedMessage: err, + Message: err, + Severity: "Error" }); - } - return Promise.resolve(); + }); + }else{ + task.done({ + HttpCode: 400, + Code: 0, + JsonData: {}, + DetailedMessage: "No-Target", + Message: "No-Target", + Severity: "Error" + }); } - return promiseWhile(doRequest,serviceUrl) - .then(function(){ - fulfill(fullService); - }); - }).then(function(fullService){ - var resp = { - "Children" : fullService - }; - task.done({ - HttpCode: 200, - Code: 0, - DetailedMessage: "OK", - JsonData: resp, - Message: "OK", - Severity: "Ok" - }); - }).catch(function(err){ - task.done({ - HttpCode: 401, - Code: 0, - JsonData: {}, - DetailedMessage: err, - Message: err, - Severity: "Error" - }); - }); -} + } -function getCFService(userId, targetURL,serviceGuid){ - return target.cfRequest("GET", userId, null , targetURL + "/v2/services/" + serviceGuid); -} + function getCFService(userId, targetURL, serviceGuid) { + return target.cfRequest("GET", userId, null, targetURL + "/v2/services/" + serviceGuid); + } };
\ No newline at end of file diff --git a/modules/orionode/package.json b/modules/orionode/package.json index 9416ca6..db381ae 100644 --- a/modules/orionode/package.json +++ b/modules/orionode/package.json @@ -41,7 +41,8 @@ "socket.io": "~1.0.4", "term.js": "0.0.3", "tiny-worker": "^1.1.4", - "unzip2": "^0.2.5" + "unzip2": "^0.2.5", + "yaml-ast-parser": "0.0.28" }, "experimentalDependencies": { "pty.js": "~0.3.0" |