Objective
Recently, the concept of Jenkins as code has been mentioned in many articles, and the eight principles of continuous delivery have all been put into version management, and we are recently ready to put some of our company's job configurations on Jenkins into git because https://github.com/ Jenkinsci/job-dsl-plugin's support is groovy. I didn't know groovy, so I looked for some online groovy scripts to change and implemented the template with go, and I practiced go.
All use to file introduction
A total of 3 files, only one template, if there are multiple scenarios can write more than a few templates
bogon:jenkins-dsl hongzhi.wang$ tree.├── config.yaml #配置文件├── generate_dsl.go #go 源码文件└── springboot.tpl #go template 文件
Configuration file
This time the preparation of the template through the Yaml file rendering, configuration file as follows, through YAML to configure the file is clear.
- 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
This is mainly used in the custom function, the ProcessNodes
other is the normal use of the template, there is our Jenkins is the main parameter pull code, and then through the Ansible-playbook implementation of the application deployment.
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 \ $WOR Kspacefor 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 source
This source code mainly according to the configuration file, the Department directory has been removed and the application groovy script is deleted, and pushed to the remote branch, groovy script name can not have -
, because groovy's script name will be parsed into the Java class name, -
Cannot be placed inside the class name of the Java language. So you can only turn the application name -
into _
.
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 ' ' Pyt Honversion 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 strin G ' 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 err OR) {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 to 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":d epartment. Name, "Cnname":d epartment. 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)
The reason for this is that to pass in a custom function, the return value of Parsefile is two that cannot be used directly with Funcs. This will be explained in the stack overflow answer.
Results of the script run
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
The rest of the work
After the script pushes the code to Gitlab, Gitlab triggers a jenkins-seed-job through Webhook, and the job runs the groovy script, and the groovy script remembers to delete the existing one. We are a department corresponding to a jenkins-seed-job, a webhook. If the config.yaml is too big, you can put some department configuration into another source repository.
steps { dsl { ignoreExisting(false) removeAction("DELETE") removeViewAction("IGNORE") lookupStrategy("JENKINS_ROOT") } }
Summarize
This time it was mostly about putting Jenkins's job management into version control and practicing go. Here is mainly to make a note o ( ̄︶ ̄) o.
Jenkins as code and go language learning