標籤:傳參數 deploy 應用 運行 bool ring 控制 dir item
前言
最近看jenkins as code這個概念在很多文章中提起,持續傳遞中八大原則也有把一切都放入版本管理,最近準備把我們公司用的一些jenkins上的job的配置也放到git中,由於https://github.com/jenkinsci/job-dsl-plugin的支援是groovy。我不懂groovy,所以找了一些網上的groovy指令碼改了改,並且通過go template實現這個模板,還能練習使用go。
所有用的到檔案簡介
一共用到3個檔案,只有一個模板,如果有多個情景可以多寫幾個模板
bogon:jenkins-dsl hongzhi.wang$ tree.├── config.yaml #設定檔├── generate_dsl.go #go 源碼檔案└── springboot.tpl #go template 檔案
設定檔
這次準備通過yaml檔案實現模板的渲染,設定檔如下,通過yaml來設定檔比較清晰。
- department_name: java cn_name: java組 apps: - app_name: app-1 nodes: - node1 - node2 - node3 ansible_playbook_name: deploy-springboot-jar.yml jvm_size: 2048m template_name: springboot.tpl - app_name: app-2 nodes: - node1 - node2 - node3 ansible_playbook_name: deploy-springboot-jar.yml jvm_size: 512m template_name: springboot.tpl- department_name: java2 cn_name: java2組 apps: - app_name: app-4 nodes: - node1 - node2 - node3 ansible_playbook_name: deploy-springboot-war.yml jvm_size: 2048m template_name: springboot.tpl
GO Template
這裡面主要是用到了自定函數ProcessNodes
,其他的都是模板的正常用法,還有就是我們的jenkins主要是傳參數拉代碼,然後通過ansible-playbook實現應用部署。
def app_name = ‘{{ .App.AppName }}‘def gitUrl = "[email protected]:{{ .Name }}/{{ .App.AppName }}.git"job("{{ .Name }}-${app_name}") {description()keepDependencies(false)parameters {choiceParam("Mode", ["release", "deploy"], "")choiceParam("app_name", ["${app_name}"], "")choiceParam("NODES", {{ ProcessNodes .App.Nodes }}, "")gitParam(‘VERSION‘) {sortMode(‘DESCENDING‘)tagFilter(‘*‘)}}scm {git {remote {url(gitUrl)credentials("git")}branch("\$VERSION")}}disabled(false)concurrentBuild(false)steps {maven{configure { node ->node / ‘mavenName‘ (‘maven‘)}goals(‘clean‘)}maven{configure { node ->node / ‘mavenName‘ (‘maven‘)}goals(‘install -Pproduct‘)}shell("""source /opt/py3/bin/activateif [ \$Mode == release ]thenSTART_TASK="copy app code"fiecho \$JOB_NAMEcd /ansible/echo \$WORKSPACEfor host in \$NODESdoansible-playbook --start-at-task="\$START_TASK" {{ .App.AnsiblePlaybookName }} --extra-vars "target_host=\$host workspace=\$WORKSPACE app_name=\$app_name version=\$VERSION jvm_size={{ .App.JvmSize }}"done""")}}listView("{{ .CnName }}") {jobs {regex(/{{ .Name }}-.+/)}columns {status()weather()name()lastSuccess()lastFailure()lastDuration()buildButton()}}
GO源碼
這個源碼主要按照設定檔,把已經去掉的部門目錄還有應用groovy指令碼給刪了,並且推送到遠程分支,groovy指令碼的名字裡面不能有-
,因為groovy的指令碼名字會解析成java的類名,-
是不能放在java語言的類名裡面了。所以只能把應用程式名稱字都的-
轉成_
。
package mainimport ( "io/ioutil" "github.com/ghodss/yaml" "fmt" "text/template" "os" "log" "strings" "os/exec" "bytes")type Department struct { Name string `json:"department_name"` CnName string `json:"cn_name"` Apps []App `json:"apps"`}type App struct { AppName string `json:"app_name"` Nodes []string `json:"nodes"` PythonVersion string `json:"python_version"` JvmSize string `json:"jvm_size"` TemplateName string `json:"template_name"` AnsiblePlaybookName string `json:"ansible_playbook_name"` DestPath string `json:"dest_path"` TomcatGitUrl string `json:"tomcat_git_url"` TomcatPort string `json:"tomcat_port"` TomcatAdminPort string `json:"tomcat_admin_port"` TomcatName string `json:"tomcat_name"` TomcatWebApp string `json:"tomcat_web_app"`}func In(i string,l []string) bool{ for _, item:= range l{ if item == i{ return true } } return false}func checkErr(err error) { if err != nil{ log.Fatal(err) }}func ProcessNodes(l []string) string { if len(l) == 1{ return fmt.Sprintf(`["%s"]`,strings.Join(l," ")) } return fmt.Sprintf(`["%s","%s"]`,l[0],strings.Join(l," "))}func AppNameToFileName(appname string) string { appname = strings.Replace(appname,"-","_",-1) return fmt.Sprintf("%s.groovy",appname)}func SysCommand(command string){ commandList := strings.Fields(command) var out bytes.Buffer cmd := exec.Command(commandList[0],commandList[1:]...) cmd.Stdout = &out cmd.Stderr = &out err := cmd.Run() if err != nil{ log.Printf("commd error: %v",command) } log.Println(out.String())}func main() { content,err := ioutil.ReadFile("config.yaml") checkErr(err) var d []Department err = yaml.Unmarshal(content, &d) checkErr(err) DepartmentNames := []string{} funcMap := template.FuncMap{ "ProcessNodes": ProcessNodes, } for _, department := range d{ err := os.MkdirAll(department.Name,0755) checkErr(err) DepartmentNames = append(DepartmentNames, department.Name) if AppFileNames := []string{}; department.Apps != nil{ for _, app := range department.Apps{ FileName := AppNameToFileName(app.AppName) AppFileNames = append(AppFileNames,FileName) data := map[string]interface{}{ "Name":department.Name, "CnName":department.CnName, "App":app, } templ,err := template.New(app.TemplateName).Funcs(funcMap).ParseFiles(app.TemplateName) checkErr(err) f, err := os.Create(fmt.Sprintf("%s/%s",department.Name, FileName)) defer f.Close() checkErr(err) err = templ.Execute(f, data) checkErr(err) } ExistFiles,err := ioutil.ReadDir(department.Name) checkErr(err) for _,v := range ExistFiles{ if ! In(v.Name(),AppFileNames){ log.Println("going to delete file: ",v.Name()) SysCommand(fmt.Sprintf("git rm %s/%s",department.Name,v.Name())) } } } } ExistDirs,err := ioutil.ReadDir("./") checkErr(err) for _,dir := range ExistDirs{ if dir.IsDir()&& dir.Name()!=".idea" && dir.Name()!=".git" && ! In(dir.Name(),DepartmentNames){ log.Println("going to delete dir: ",dir.Name()) log.Printf("git rm -r %s\n",dir.Name()) SysCommand(fmt.Sprintf("git rm -r %s",dir.Name())) checkErr(err) } } SysCommand("git add * ") SysCommand("git commit -m ‘update‘ ") //SysCommand("git push ") log.Println("finished")}
templ,err := template.New(app.TemplateName).Funcs(funcMap).ParseFiles(app.TemplateName)
這個之所以這樣是因為要傳入自定的函數,parsefile的傳回值是兩個不能直接用Funcs。這個就要詳見Stack Overflow上的答案了。
指令碼啟動並執行結果
bogon:jenkins-dsl hongzhi.wang$ go run generate_dsl.go bogon:jenkins-dsl hongzhi.wang$ tree.├── config.yaml├── generate_dsl.go├── java│ ├── app_1.groovy│ └── app_2.groovy├── java2│ └── app_4.groovy└── springboot.tpl
剩餘的工作
這個指令碼把程式碼推送到gitlab之後,gitlab通過webhook觸發一個jenkins-seed-job,這個job再運行groovy指令碼,這個groovy指令碼裡面記得把已經存在的刪了,我們是一個部門對應一個jenkins-seed-job,一個webhook。如果config.yaml太大了,可以把一些部門的配置放到另一個源碼庫裡。
steps { dsl { ignoreExisting(false) removeAction("DELETE") removeViewAction("IGNORE") lookupStrategy("JENKINS_ROOT") } }
總結
這次主要是把jenkins的job管理納入版本控制,並且練習一下go。這裡主要是做個筆記o( ̄︶ ̄)o。
jenkins as code 與go語言學習