原文:http://blog.csdn.net/jinzhencs/article/details/51461776
场景:在我们工作中,有时需要生成一些文件,可能它不是一种标准的格式,比如JSON。
目的:配置一个模板,根据前端传入的值动态生成配置文件,并且支持循环判断
项目路径:
请无视YamlTest.和YamlTemplate.ftl
JAVA代码:
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(Mapmap){ 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 podMap = new HashMap (); 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
ftl模板文件:
{ "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>, ] } ] }}
最终效果:
test.yaml
之前是空白的,运行程序后自动生成一份文件(k8s的编排文件)说明:此处其实生成的是.json文件。打错字了。其实无所谓,自己把ftl里面的改为yaml格式即可。
遇到的问题
花了2小时把这个玩意大体搞定了。
遇到的坑: 1.如何遍历list 之前我按自己想的写的是<#list ${portList} as portInfo>,一直报错,后来修修改改发现原来不需要,直接portList即可。2.加入判断 list.hasNext来决定是否加逗号。
之前直接每个后面加逗号,然后就成这样了"ports":[ { "containerPort":8080 }, { "containerPort":8081 },]
所以需要加上一个判断。不过freemark直接支持
<#if portInfo_has_next>,
还是很给力.
2016-05-27 新增:
经过完整的测试部署之后的一份ftl及Demo示例,解决了list循环嵌套且list的是map的问题
ftl:
{ "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>, ], "resources": { "limits":{ "cpu": "${containerInfo.cpu}", "memory": "${containerInfo.memory}" } } } ] } } }}
测试代码:
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(" ", "");
并且要注意不要有缩进,会被坑 持续更新ing…..
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>, } }, "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>, } }, "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>, ], "env": [ <#list containerInfo.envList as envInfo>{ "name": "${envInfo.name}", "value": "${envInfo.value}" } <#if envInfo_has_next>, ], "volumeMounts": [ { "mountPath": "${containerInfo.logPath}", "name": "flumelog" } ], "resources": { "limits":{ <#if containerInfo.cpu??> "cpu": "${containerInfo.cpu}" <#if containerInfo.memory??> ,"memory": "${containerInfo.memory}" } } }<#if containerInfo_has_next>, ] <#if nodeSelector??> //这里是判断是否为null , "nodeSelector": { <#list nodeSelector as nodeSelector> "${nodeSelector.key}": "${nodeSelector.value}" <#if nodeSelector_has_next>, } } } }}