@vroomlabs/gsdk-deploy
Version:
Google Cloud deployment script for kubernetes clusters using Global Load Balancer
212 lines (185 loc) • 8.14 kB
JavaScript
;
/******************************************************************************
* MIT License
* Copyright (c) 2017 https://github.com/vroomlabs
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Created by rogerk on 7/2/17.
******************************************************************************/Object.defineProperty(exports,'__esModule',{value:true});exports.Configuration=undefined;var _createClass=function(){function defineProperties(target,props){for(var i=0;i<props.length;i++){var descriptor=props[i];descriptor.enumerable=descriptor.enumerable||false;descriptor.configurable=true;if('value'in descriptor)descriptor.writable=true;Object.defineProperty(target,descriptor.key,descriptor)}}return function(Constructor,protoProps,staticProps){if(protoProps)defineProperties(Constructor.prototype,protoProps);if(staticProps)defineProperties(Constructor,staticProps);return Constructor}}();
var _fs=require('fs');var fs=_interopRequireWildcard(_fs);
var _path=require('path');var path=_interopRequireWildcard(_path);
var _yamljs=require('yamljs');var YAML=_interopRequireWildcard(_yamljs);
var _logger=require('../util/logger');
var _templateArg=require('../util/templateArg');
var _deployConfig=require('./deployConfig');function _interopRequireWildcard(obj){if(obj&&obj.__esModule){return obj}else{var newObj={};if(obj!=null){for(var key in obj){if(Object.prototype.hasOwnProperty.call(obj,key))newObj[key]=obj[key]}}newObj.default=obj;return newObj}}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError('Cannot call a class as a function')}}var
Configuration=exports.Configuration=function(){
/**
* @param {Arguments} args
*/
function Configuration(args){_classCallCheck(this,Configuration);
if(!fs.existsSync(args.config)){
throw new Error('The configuration file was not found at:\n'+args.config);
}
if(!args.branch){
throw new Error('Specify a deployment/branch name with -branch=[NAME]');
}
/** @type {DeployConfig} */
this.current=this._readConfig(args.config,args.branch);
this._stateFile=path.join(args.artifacts,'deployConfig.json');
this.save();
this.current.assertValid();
this.name=this.current.name;
this.project=this.current['google-project'];
this.current.host=this.current.host.toLowerCase();
this.artifacts=args.artifacts;
this.clusters={};
process.env.GCLOUD_PROJECT=this.project;
process.env.SERVICE_NAME=this.name;
process.env.HOSTNAME=this.current.host;
process.env.NODE_PORT=this.getNodePort();
}
/**
* @private
* @param {string} fileName
* @param {string} deployName
* @returns {DeployConfig}
*/_createClass(Configuration,[{key:'_readConfig',value:function _readConfig(
fileName,deployName){
var cfg=void 0;
var rawCfg=fs.readFileSync(fileName).toString();
if(fileName.match(/\.json$/)){
cfg=JSON.parse(rawCfg);
}else
if(fileName.match(/\.ya?ml$/)){
cfg=YAML.parse(rawCfg);
}else
{
throw new Error('Unknown configuration type, supported types are (json|yaml).');
}
if(!cfg.hasOwnProperty(deployName)){
throw new Error('The config file does not have a deployment called "'+deployName+'".');
}
var current=Object.assign({},cfg[deployName]);
while(current.extend||current.extends){
var baseType=current.extend||current.extends;
delete current.extend;
delete current.extends;
if(!cfg.hasOwnProperty(baseType)){
throw new Error('The deployment extends unknown deployment called "'+baseType+'".');
}
current=Object.assign({},cfg[baseType],current);
}
current=Object.assign(new _deployConfig.DeployConfig,current);
current.path=path.resolve(fileName);
return current;
}},{key:'save',value:function save()
{
fs.writeFileSync(this._stateFile,JSON.stringify(this,null,2));
}
/**
* @returns {Array} returns an array of {name:string, value: string} objects
*/},{key:'getEnvironment',value:function getEnvironment()
{
var hasport=false;
var env=this.current.env.
filter(function(i){return typeof item==='string'||i.name}).
map(function(item){
if(typeof item==='string'){
var m=item.match(/^([\w_-]+)(=(.*))?$/);
item={name:m[1]};
if(typeof m[3]==='string')
item.value=m[3];
}
item.name=item.name.toUpperCase();
if(!item.hasOwnProperty('value')){
item.value='$'+item.name.toUpperCase();
}
hasport=hasport||item.name==='NODE_PORT';
return item;
});
if(!hasport){
env.push({name:'NODE_PORT',value:this.current.port.toString()});
}
env=(0,_templateArg.replaceInObject)(env);
return env;
}
/**
* @returns {number} Returns the specified nodePort or generates one from name
*/},{key:'getNodePort',value:function getNodePort()
{
if(!(this.current.nodePort>0)){
// Compute a nodePort base on hash of the service name
var crypto=require('crypto');
var md5sum=crypto.createHash('md5');
md5sum.update(this.name);
var digits=md5sum.digest('hex');
var digit=parseInt(digits.substr(0,8))^parseInt(digits.substr(8,8))^
parseInt(digits.substr(16,8))^parseInt(digits.substr(24,8));
this.current.nodePort=30000+digit%2768;
}
if(this.current.nodePort<30000||this.current.nodePort>32767){
throw new Error('The node port '+this.current.nodePort+' is invalid.');
}
return this.current.nodePort;
}
/**
* Returns the fully-qualified api source files
* @returns {string[]}
*/},{key:'getApiFiles',value:function getApiFiles()
{var _this=this;
var apiConfig=[].concat(this.current['api-config']||[]);
var basePath=path.dirname(this.current.path);
apiConfig=apiConfig.map(function(pth){return path.resolve(basePath,pth)});var _loop=function _loop(
ix){
var fname=apiConfig[ix];
var fType=fname.match(/.ya?ml$/i)?YAML:fname.match(/.json$/i)?JSON:null;
var cfg=fType?fType.parse(fs.readFileSync(fname).toString()):{};
var prop=null;
if(cfg.type==='google.api.Service'&&cfg.name){prop='name'}else
if(cfg.swagger&&cfg.swagger.toString().match(/^\d/)){prop='host'}else
{return'continue'}
// Replace the endpoint proper name with the value from endpointFormat
var correctedName=(0,_templateArg.replaceInText)(_this.current.endpointFormat);;
var newPath=path.join(_this.artifacts,path.basename(fname));
cfg[prop]=correctedName;
_logger.logger.verbose('replaced service endpoint name',{'new':newPath,old:fname,value:cfg[prop]});
// ROK - look for endpoints collection and if it matches the last 2 segments, replace it, i.e. cloud.goog, or vroomapi.com match
(cfg.endpoints||[]).
forEach(function(ep){
if(ep.name&&ep.name.split('.').slice(-2).join('.')===correctedName.split('.').slice(-2).join('.')){
ep.name=correctedName;
}
});
fs.writeFileSync(newPath,fType.stringify(cfg,null,2));
apiConfig[ix]=newPath};for(var ix=0;ix<apiConfig.length&&this.current.endpointFormat;ix++){var _ret=_loop(ix);if(_ret==='continue')continue;
}
return apiConfig;
}
/**
* @returns {string} - The version-specific tag that should be used
*/},{key:'getDockerTagFormat',value:function getDockerTagFormat()
{
var tagFormat=this.current.tagFormat;
if(!tagFormat&&process.env.CIRCLE_SHA1&&process.env.CIRCLE_BUILD_NUM){
tagFormat='$BRANCH-$CIRCLE_SHA1-$CIRCLE_BUILD_NUM';
}else
if(!tagFormat){
tagFormat+='$BRANCH-$BUILD_TIME';
}
return tagFormat;
}}]);return Configuration}();