原文:http://blog.csdn.net/jinzhencs/article/details/51461776前端
場景:在咱們工做中,有時須要生成一些文件,可能它不是一種標準的格式,好比JSON。java
目的:配置一個模板,根據前端傳入的值動態生成配置文件,而且支持循環判斷node
請無視YamlTest.Java和YamlTemplate.ftldocker
package com.ming.freemark.demo; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import freemarker.template.Configuration; import freemarker.template.Template; import freemarker.template.TemplateException; public class YamlTestList { //模板配置對象 private Configuration cfg; //Yaml目錄 private static String yamlPath = "resources\\yaml"; /** * 初始化配置 */ public void init() { cfg = new Configuration(); File yamlFile = null; try { yamlFile = new File(yamlPath); cfg.setDirectoryForTemplateLoading(yamlFile); } catch (IOException e) { e.printStackTrace(); } } public void process(Map<String, Object> map){ try { Template template = cfg.getTemplate("YamlTemplate2.ftl"); template.process(map, new FileWriter(new File(yamlPath + "\\test.yaml"))); } catch (IOException | TemplateException e) { e.printStackTrace(); } } public static void main(String[] args) { YamlTestList test = new YamlTestList(); test.init(); //生成一個pod的yaml文件 Map<String,Object> podMap = new HashMap<String,Object>(); podMap.put("kind", "Pod"); podMap.put("apiVersion", "v1"); podMap.put("metadataName", "test-pod"); podMap.put("lablesName", "test-pod"); podMap.put("lablesVersion", "1.0"); //此處是一個循環 一個pod包含多個微服務組件 podMap.put("containerName", "tomcat"); podMap.put("imageName", "tomcatImage"); //組裝一個container裏面的port列表 //一個docker container裏面可能有多個組件,好比一個container部署了兩個tomcat,則此處是port的列表 List<Map<String,Object>> portList = new ArrayList<>(); Map<String,Object> portMap1 = new HashMap<String,Object>(); portMap1.put("containerPort", 8080); Map<String,Object> portMap2 = new HashMap<String,Object>(); portMap2.put("containerPort", 8081); portList.add(portMap1); portList.add(portMap2); podMap.put("portList", portList); test.process(podMap); // myMap[key]?default(""),這裏是判斷值是否爲null,若是爲null,設置默認值爲空,freemarker不支持null,若是值爲null,會拋出異常報錯。 } }
{ "kind": "${kind}", "apiVersion": "${apiVersion}", "metadata": { "name": "${metadataName}", "labels": { "name": "${lablesName}", "version": "${lablesVersion}" } }, "spec": { "containers": [ { "name": "${containerName}", "image": "${imageName}", "ports":[ <#list portList as portInfo> { "containerPort":${portInfo.containerPort?c} }<#if portInfo_has_next>,</#if> </#list> ] } ] } }
test.yaml
以前是空白的,運行程序後自動生成一份文件(k8s的編排文件)json
說明:此處其實生成的是.json文件。打錯字了。其實無所謂,本身把ftl裏面的改成yaml格式便可。api
花了2小時把這個玩意大致搞定了。
遇到的坑:
1.如何遍歷list
以前我按本身想的寫的是<#list ${portList} as portInfo>,一直報錯,後來修修改改發現原來不須要,直接portList便可。tomcat
2.加入判斷 list.hasNext來決定是否加逗號。
以前直接每一個後面加逗號,而後就成這樣了app
"ports":[ { "containerPort":8080 }, { "containerPort":8081 }, ]
因此須要加上一個判斷。不過freemark直接支持dom
<#if portInfo_has_next>,</#if>
仍是很給力.tcp
通過完整的測試部署以後的一份ftl及Demo示例,解決了list循環嵌套且list的是map的問題
{ "kind": "ReplicationController", "apiVersion": "v1", "metadata": { "name": "${appName}-rc", "labels": { "name": "${appName}-rc", "version": "v1" } }, "spec": { "replicas": ${replicas}, "selector": { "name": "${appName}-pod" }, "template": { "metadata": { "name": "${appName}-pod", "labels": { "name": "${appName}-pod", "version": "v1" } }, "spec": { "containers": [ <#list containerList as containerInfo>{ "name": "${containerInfo.microServiceName}", "image": "${containerInfo.imageUrl}", "ports": [ <#list containerInfo.portList as portInfo>{ "containerPort": ${portInfo.port?c} }<#if portInfo_has_next>,</#if> </#list> ], "resources": { "limits":{ "cpu": "${containerInfo.cpu}", "memory": "${containerInfo.memory}" } } } </#list>] } } } }
public static String layoutData(String name,int exposePort){ // 編排基本信息 JSONObject layoutInfoJSON = new JSONObject(); layoutInfoJSON.put("name", name + "layout"); layoutInfoJSON.put("version", "v1.0"); layoutInfoJSON.put("dmsAppName", name); layoutInfoJSON.put("dmsAppId", "0a2f9r8t7y6j7hg9"); //dms的應用Id layoutInfoJSON.put("replicas", 2); //副本數 layoutInfoJSON.put("masterId", "beijing1"); //北京一區 layoutInfoJSON.put("basicCpuScale", 1); //pod基礎設置:6核cpu layoutInfoJSON.put("basicMemScale", 2); //pod基礎設置:12G內存 //構建微服務信息list //微服務1 JSONArray msArray = new JSONArray(); JSONObject msJsonObjectOne = new JSONObject(); msJsonObjectOne.put("name", "tomcat"); msJsonObjectOne.put("version", "1.0"); msJsonObjectOne.put("type", 1); //微服務類型:1:service、2:job msJsonObjectOne.put("domain", "www.test.com"); msJsonObjectOne.put("imageUrl", "tomcat-1"); msJsonObjectOne.put("memRatio", 1); msJsonObjectOne.put("cpuRatio", 1); //微服務1中的ports 端口列表 JSONArray portArray = new JSONArray(); JSONObject portJsonObjectOne = new JSONObject(); portJsonObjectOne.put("port", 8080); //容器自己的端口 portJsonObjectOne.put("protocol", "tcp"); //端口協議類型 portJsonObjectOne.put("exposePort", exposePort); //容器端口 - 外網暴露端口 注意端口占用 portArray.add(portJsonObjectOne); msJsonObjectOne.put("ports", portArray); //微服務1中的evns 環境變量列表 JSONArray evnArray = new JSONArray(); JSONObject evnJsonObjectOne = new JSONObject(); evnJsonObjectOne.put("key", "evn_key1"); evnJsonObjectOne.put("value", "evn_value1"); evnJsonObjectOne.put("option", "evn_option1"); evnArray.add(evnJsonObjectOne); msJsonObjectOne.put("evns", evnArray); msArray.add(msJsonObjectOne); //把微服務放入編排信息中 layoutInfoJSON.put("microServiceInfoList", msArray); return JSONObject.toJSONString(layoutInfoJSON); }
{ "kind": "ReplicationController", "apiVersion": "v1", "metadata": { "name": "k8stest6-rc", "labels": { "name": "k8stest6-rc", "version": "v1" } }, "spec": { "replicas": 2, "template": { "metadata": { "name": "k8stest6-pod", "labels": { "name": "k8stest6-pod", "version": "v1" } }, "spec": { "containers": [ { "name": "tomcat", "image": "tomcat-1", "ports": [ { "containerPort": 8080 } ], "resources": { "limits":{ "cpu": "1", "memory": "2.0Gi" } } } ] } } } }
注意:要使用這個json最好 .trim().replaceAll(" ", "");
而且要注意不要有縮進,會被坑
2016-07-06:
{ "kind": "ReplicationController", "apiVersion": "v1", "metadata": { "name": "${stackName}", "labels": { "appName": "${appName}", "stackName": "${stackName}", <#list containerList as containerInfo> "${containerInfo.microServiceName}-major": "${containerInfo.major}", "${containerInfo.microServiceName}-version": "${containerInfo.version}" <#if containerInfo_has_next>,</#if></#list> } }, "spec": { "replicas": ${replicas}, "template": { "metadata": { "name": "${stackName}", "labels": { "appName": "${appName}", "stackName": "${stackName}", <#list containerList as containerInfo> "${containerInfo.microServiceName}-major": "${containerInfo.major}", "${containerInfo.microServiceName}-version": "${containerInfo.version}" <#if containerInfo_has_next>,</#if></#list> } }, "spec": { "volumes": [{ "name": "flumelog", "hostPath": { "path": "/var/logs/ulog/${appName}-${stackName}" } }], "containers": [ <#list containerList as containerInfo>{ "name": "${containerInfo.microServiceName}", "image": "${containerInfo.imageUrl}", "ports": [ <#list containerInfo.portList as portInfo>{ "containerPort": ${portInfo.targetPort?c} } <#if portInfo_has_next>,</#if> </#list> ], "env": [ <#list containerInfo.envList as envInfo>{ "name": "${envInfo.name}", "value": "${envInfo.value}" } <#if envInfo_has_next>,</#if> </#list> ], "volumeMounts": [ { "mountPath": "${containerInfo.logPath}", "name": "flumelog" } ], "resources": { "limits":{ <#if containerInfo.cpu??> "cpu": "${containerInfo.cpu}" </#if> <#if containerInfo.memory??> ,"memory": "${containerInfo.memory}" </#if> } } }<#if containerInfo_has_next>,</#if></#list> ] <#if nodeSelector??> //這裏是判斷是否爲null , "nodeSelector": { <#list nodeSelector as nodeSelector> "${nodeSelector.key}": "${nodeSelector.value}" <#if nodeSelector_has_next>,</#if> </#list> } </#if> } } } }