docs: function (options, cb) {
  		options.soajs.log.debug("Deploying new Cluster");
  		
  		let uniqueName = `ht${options.params.soajs_project.toLowerCase()}${randomstring.generate({
  			length: 13,
  			charset: 'alphanumeric',
  			capitalization: 'lowercase'
  		})}`;
  		
  		let request = getConnector(options.infra.api);
  		
  		let name = '', createNewNetwork = false;
  		if (options.infra && options.infra._id && options.soajs && options.soajs.registry && options.soajs.registry.restriction && options.soajs.registry.restriction[options.infra._id]) {
  			if (Object.keys(options.soajs.registry.restriction[options.infra._id]).length > 0) {
  				options.params.region = Object.keys(options.soajs.registry.restriction[options.infra._id])[0];
  				if(options.soajs.registry.restriction[options.infra._id][options.params.region] && options.soajs.registry.restriction[options.infra._id][options.params.region].network) {
  					options.soajs.log.debug("Cluster network provided as input, using it to deploy ...");
  					name = options.soajs.registry.restriction[options.infra._id][options.params.region].network;
  				}
  			}
  		}
  		
  		if (!name) {
  			options.soajs.log.debug("No cluster network provided, creating a new network for the cluster ...");
  			//no create a name made from ht + deployment type + random string
  			name = uniqueName;
  			createNewNetwork = true;
  		}
  		
  		let oneDeployment = {};
  		
  		function prepareDeploymentConfiguration(mCb) {
  			options.soajs.log.debug("Preparing Deployment Entry in Project");
  			
  			if (!options.infra.deployments) {
  				options.infra.deployments = [];
  			}
  			
  			oneDeployment = {
  				id: uniqueName,
  				name: uniqueName,
  				technology: "kubernetes",
  				environments: [options.soajs.registry.code.toUpperCase()],
  				options: {}
  			};
  			
  			return mCb(null, true);
  		}
  		
  		/**
  		 * method used to create a google vpc network
  		 * @returns {*}
  		 */
  		function createVpcNetwork(mCb) {
  			let networksOptions = Object.assign({}, options);
  			networksOptions.params = {
  				name,
  				returnGlobalOperation: true
  			};
  			networks.add(networksOptions, (error, globalOperationResponse) => {
  				if (error) return cb(error);
  				
  				//assign network name to deployment entry
  				oneDeployment.options.network = name;
  				
  				//check if network is ready then update firewall rules
  				checkVpcNetwork(globalOperationResponse, mCb);
  			});
  			
  			function checkVpcNetwork(globalOperationResponse, mCb) {
  				
  				function globalOperations(miniCB) {
  					options.soajs.log.debug("Checking network Create Status");
  					//Ref https://cloud.google.com/compute/docs/reference/latest/globalOperations/get
  					let request = getConnector(options.infra.api);
  					delete request.projectId;
  					request.operation = globalOperationResponse.name;
  					v1Compute().globalOperations.get(request, (error, response) => {
  						if (error) {
  							return miniCB(error);
  						}
  						if (!response || response.status !== "DONE") {
  							setTimeout(function () {
  								globalOperations(miniCB);
  							}, (process.env.SOAJS_CLOOSTRO_TEST) ? 1 : 5000);
  						} else {
  							return miniCB(null, response);
  						}
  					});
  				}
  				
  				globalOperations(function (err) {
  					if (err) {
  						return mCb(err);
  					} else {
  						//Ref: https://cloud.google.com/compute/docs/reference/latest/firewalls/insert
  						let firewallRules = getFirewallRules(oneDeployment.options.network);
  						
  						let request = getConnector(options.infra.api);
  						async.eachSeries(firewallRules, (oneRule, vCb) => {
  							options.soajs.log.debug("Registering new firewall rule:", oneRule.name);
  							request.resource = oneRule;
  							v1Compute().firewalls.insert(request, vCb);
  						}, mCb);
  					}
  				});
  				
  			}
  		}
  		
  		/**
  		 * method used to use an existing google vpc network
  		 * @returns {*}
  		 */
  		function useVpcNetwork(mCb) {
  			//assign network name to deployment entry
  			oneDeployment.options.network = name;
  			
  			function patchFirewall() {
  				let firewallRules = getFirewallRules(oneDeployment.options.network);
  				
  				let request = getConnector(options.infra.api);
  				request.filter = `network eq .*${oneDeployment.options.network}`;
  				request.project = options.infra.api.project;
  				
  				//list firewalls
  				//Ref: https://cloud.google.com/compute/docs/reference/rest/v1/firewalls/list
  				v1Compute().firewalls.list(request, function (error, firewalls) {
  					if (error) return mCb(error);
  					
  					if (!firewalls.items) firewalls.items = [];
  					
  					async.eachSeries(firewallRules, (oneRule, vCb) => {
  						let foundFirewall = firewalls.items.find((oneEntry) => {
  							return oneEntry.name === oneRule.name
  						});
  						
  						if (foundFirewall) {
  							options.soajs.log.debug("Firewall rule:", oneRule.name, "already exists, skipping");
  							return vCb();
  						} else {
  							options.soajs.log.debug("Creating firewall rule:", oneRule.name);
  							request.resource = oneRule;
  							return v1Compute().firewalls.insert(request, vCb);
  						}
  					}, mCb);
  				});
  			}
  			
  			patchFirewall();
  		}
  		
  		function getFirewallRules(network) {
  			let firewallRules = [
  				{
  					//gcloud compute --project=ragheb-project firewall-rules create template-cluster-allow-icmp --description=Allows\ ICMP\ connections\ from\ any\ source\ to\ any\ instance\ on\ the\ network. --direction=INGRESS --priority=65534 --network=template-cluster --action=ALLOW --rules=icmp --source-ranges=0.0.0.0/0
  					"kind": "compute#firewall",
  					"name": network + "-allow-icmp",
  					"description": "Allow ICMP Connections",
  					"network": "projects/" + options.infra.api.project + "/global/networks/" + network,
  					"priority": 65534,
  					"sourceRanges": "0.0.0.0/0",
  					"allowed": [
  						{
  							"IPProtocol": "icmp"
  						}
  					]
  				},
  				{
  					//gcloud compute --project=ragheb-project firewall-rules create template-cluster-allow-ssh --description=Allows\ TCP\ connections\ from\ any\ source\ to\ any\ instance\ on\ the\ network\ using\ port\ 22. --direction=INGRESS --priority=65534 --network=template-cluster --action=ALLOW --rules=tcp:22 --source-ranges=0.0.0.0/0
  					"kind": "compute#firewall",
  					"name": network + "-allow-ssh",
  					"description": "Allow SSH Connections",
  					"network": "projects/" + options.infra.api.project + "/global/networks/" + network,
  					"priority": 65534,
  					"sourceRanges": "0.0.0.0/0",
  					"allowed": [
  						{
  							"IPProtocol": "tcp",
  							"ports": ["22"]
  						}
  					]
  				},
  				{
  					//gcloud compute --project=ragheb-project firewall-rules create template-cluster-allow-rdp --description=Allows\ RDP\ connections\ from\ any\ source\ to\ any\ instance\ on\ the\ network\ using\ port\ 3389. --direction=INGRESS --priority=65534 --network=template-cluster --action=ALLOW --rules=tcp:3389 --source-ranges=0.0.0.0/0
  					"kind": "compute#firewall",
  					"name": network + "-allow-rdp",
  					"description": "Allow RDP Connections",
  					"network": "projects/" + options.infra.api.project + "/global/networks/" + network,
  					"priority": 65534,
  					"sourceRanges": "0.0.0.0/0",
  					"allowed": [
  						{
  							"IPProtocol": "tcp",
  							"ports": ["3389"]
  						}
  					]
  				},
  				{
  					"kind": "compute#firewall",
  					"name": network + "-allow-http",
  					"description": "Allow HTTP Connections",
  					"network": "projects/" + options.infra.api.project + "/global/networks/" + network,
  					"priority": 65534,
  					"sourceRanges": "0.0.0.0/0",
  					"allowed": [
  						{
  							"IPProtocol": "tcp",
  							"ports": ["80"]
  						}
  					]
  				},
  				{
  					"kind": "compute#firewall",
  					"name": network + "-allow-https",
  					"description": "Allow HTTPS Connections",
  					"network": "projects/" + options.infra.api.project + "/global/networks/" + network,
  					"priority": 65534,
  					"sourceRanges": "0.0.0.0/0",
  					"allowed": [
  						{
  							"IPProtocol": "tcp",
  							"ports": ["443"]
  						}
  					]
  				},
  				{
  					//gcloud compute --project=ragheb-project firewall-rules create template-cluster-allow-internal --description=Allows\ connections\ from\ any\ source\ in\ the\ network\ IP\ range\ to\ any\ instance\ on\ the\ network\ using\ all\ protocols. --direction=INGRESS --priority=65534 --network=template-cluster --action=ALLOW --rules=all --source-ranges=10.128.0.0/9
  					"kind": "compute#firewall",
  					"name": network + "-allow-internal",
  					"description": "Allow All Internal Connections",
  					"network": "projects/" + options.infra.api.project + "/global/networks/" + network,
  					"priority": 65534,
  					"sourceRanges": "10.128.0.0/9",
  					"allowed": [
  						{
  							"IPProtocol": "tcp",
  							"ports": ["0-65535"]
  						}
  					]
  				}
  			];
  			
  			return firewallRules;
  		}
  		
  		/**
  		 * method used to get cluster version
  		 * @returns {*}
  		 */
  		function getClusterVersion(request, mCb) {
  			delete request.project;
  			v1Container().projects.zones.getServerconfig(request, function (err, response) {
  				if (err) {
  					return mCb(err);
  				}
  				let version;
  				if (response && response.validMasterVersions && Array.isArray(response.validMasterVersions)
  					&& response.validMasterVersions.length > 0) {
  					response.validMasterVersions.forEach(function (oneVersion) {
  						if (oneVersion.substring(0, 3) === "1.7") {
  							version = oneVersion;
  							options.soajs.log.debug("Initial Cluster version set to :", version);
  						}
  					});
  					
  				} else if (response && response.defaultClusterVersion && !version) {
  					version = response.defaultClusterVersion;
  					options.soajs.log.debug("Initial Cluster version set to default version :", version);
  				} else {
  					return mCb({"code": 410, "msg": "Invalid or no kubernetes cluster version found!"})
  				}
  				return mCb(null, version);
  			});
  		}
  		
  		/**
  		 * delete vpc Network after a certain timeout
  		 * @returns {*}
  		 */
  		function deleteNetwork() {
  			setTimeout(function () {
  				//cluster failed, delete network if it was not provided by the user
  				let networksOptions = Object.assign({}, options);
  				networksOptions.params = {name: oneDeployment.options.network};
  				networks.delete(networksOptions, (error) => {
  					if (error) {
  						options.soajs.log.error(error);
  					} else {
  						options.soajs.log.debug("VPC Network Deleted Successfully.");
  					}
  				});
  			}, (process.env.SOAJS_CLOOSTRO_TEST) ? 1 : 5 * 60 * 1000);
  		}
  		
  		function createTemplate(mCb) {
  			if (!options.params.template || !options.params.template.content) {
  				return mCb(new Error('Invalid GKE template provided'));
  			}
  			//Ref: https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.zones.clusters#Cluster
  			if (options.params.template && typeof (options.params.template.content) === 'string') {
  				options.params.template.content = JSON.parse(options.params.template.content);
  			}
  			let template = JSON.parse(JSON.stringify(options.params.template.content));
  			if (!template) {
  				return mCb(new Error("Invalid or Cluster Template detected to create the cluster from!"));
  			}
  			
  			template.cluster.name = uniqueName; //same name as network
  			template.cluster.zone = options.params.region;
  			// template.cluster.zoneLocation = data.options.region;
  			template.cluster.network = oneDeployment.options.network;
  			template.cluster.subnetwork = oneDeployment.options.network;
  			
  			if (typeof options.params.template.inputs === 'string') {
  				try {
  					options.params.template.inputs = JSON.parse(options.params.template.inputs);
  				} catch (e) {
  					return cb(new Error("Detected invalid template inputs schema !!!"));
  				}
  			}
  			mapTemplateInputsWithValues(options.params.template.inputs, options.params, template.cluster, () => {
  				
  				let request = getConnector(options.infra.api);
  				request.zone = options.params.region;
  				
  				options.soajs.log.debug("Retrieving cluster version to use from google");
  				getClusterVersion(request, function (err, version) {
  					if (err) {
  						if (createNewNetwork) {
  							options.soajs.log.debug("Deleting VPC network...");
  							deleteNetwork();
  						}
  						return mCb(err);
  					}
  					
  					template.cluster.initialClusterVersion = version;
  					delete request.project;
  					request.resource = template;
  					request.zone = [];
  					//Ref: https://cloud.google.com/kubernetes-engine/docs/reference/rest/v1/projects.zones.clusters/create
  					options.soajs.log.debug("Deploying new Cluster from Template:", oneDeployment.options.network);
  					driver.checkZoneRegion(options, options.params.region, false, (err, type) => {
  						if (err) {
  							return cb(err)
  						}
  						request.parent = `projects/${options.infra.api.project}/locations/${options.params.region}`
  						v1beta1container().projects[type].clusters.create(request, function (err, operation) {
  							if (err) {
  								if (createNewNetwork) {
  									options.soajs.log.debug("Deleting VPC network...");
  									deleteNetwork();
  								}
  								return mCb(err);
  							} else {
  								oneDeployment.options.nodePoolId = template.cluster.nodePools[0].name;
  								oneDeployment.options.zone = options.params.region;
  								oneDeployment.options.operationId = operation.name;
  								return mCb(null, true);
  							}
  						});
  					})
  				});
  			});
  		}
  		
  		let stages = [prepareDeploymentConfiguration];
  		
  		if (options.soajs.registry.pending && options.params.resume) {
  			options.soajs.log.warn(`Detected new call to deploy infra for the same environment ${options.soajs.registry.code}, skipping call...`);
  		} else {
  			if (createNewNetwork) {
  				stages.push(createVpcNetwork);
  			} else {
  				stages.push(useVpcNetwork);
  			}
  			
  			stages.push(createTemplate);
  		}
  		
  		async.series(stages, (error) => {
  			if (error) {
  				return cb(error);
  			}
  			return cb(null, oneDeployment);
  		});
  		
  		function mapTemplateInputsWithValues(inputs, params, template, mapCb) {
  			let templateInputs = {};
  			async.each(inputs, (oneInput, iCb) => {
  				if (oneInput.entries) {
  					mapTemplateInputsWithValues(oneInput.entries, params, template, iCb);
  				} else {
  					let paramValue;
  					if (params[oneInput.name]){
  						paramValue = params[oneInput.name].toString();
  					}
  					if (!paramValue) {
  						paramValue = oneInput.value.toString();
  					}
  					templateInputs[oneInput.name] = paramValue;
  					return iCb();
  				}
  			}, () => {
  				//loop in template recursively till you find a match, replace value of found matches
  				for (let property in templateInputs) {
  					traverse(template).forEach(function (x) {
  						if (this.key === property) {
  							this.update(templateInputs[property]);
  						}
  					});
  				}
  				return mapCb();
  			});
  		}
  	}