Skip to content

Commit

Permalink
Merge pull request #42 from AMeng/jenkins-folders-plugin
Browse files Browse the repository at this point in the history
Capture Jenkins job names correctly when the folder plugin is being used
  • Loading branch information
tomaslin committed Dec 23, 2015
2 parents da333d1 + fb3093d commit cc0125a
Show file tree
Hide file tree
Showing 10 changed files with 537 additions and 72 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.netflix.spinnaker.igor.jenkins.client.model.JobConfig
import com.netflix.spinnaker.igor.jenkins.client.model.QueuedJob
import groovy.transform.InheritConstructors
import groovy.util.logging.Slf4j
import javax.servlet.http.HttpServletRequest
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.PathVariable
Expand All @@ -31,6 +32,7 @@ import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.servlet.HandlerMapping
import org.yaml.snakeyaml.Yaml

import java.util.concurrent.ExecutorService
Expand All @@ -47,8 +49,10 @@ class BuildController {
@Autowired
ObjectMapper objectMapper

@RequestMapping(value = '/jobs/{master}/{job}/{buildNumber}')
Map getJobStatus(@PathVariable String master, @PathVariable String job, @PathVariable Integer buildNumber) {
@RequestMapping(value = '/builds/status/{buildNumber}/{master}/**')
Map getJobStatus(@PathVariable String master, @PathVariable Integer buildNumber, HttpServletRequest request) {
def job = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).split('/').drop(5).join('/')
if (!masters.map.containsKey(master)) {
throw new MasterNotFoundException()
}
Expand All @@ -69,27 +73,30 @@ class BuildController {
result
}

@RequestMapping(value = '/jobs/{master}/queue/{item}')
@RequestMapping(value = '/builds/queue/{master}/{item}')
QueuedJob getQueueLocation(@PathVariable String master, @PathVariable int item){
if (!masters.map.containsKey(master)) {
throw new MasterNotFoundException()
}
masters.map[master].getQueuedItem(item)
}

@RequestMapping(value = '/jobs/{master}/{job}/builds')
List<Build> getBuilds(@PathVariable String master, @PathVariable String job) {
@RequestMapping(value = '/builds/all/{master}/**')
List<Build> getBuilds(@PathVariable String master, HttpServletRequest request) {
def job = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).split('/').drop(4).join('/')
if (!masters.map.containsKey(master)) {
throw new MasterNotFoundException()
}
def lists = masters.map[master].getBuilds(job)
masters.map[master].getBuilds(job).list
}

@RequestMapping(value = '/masters/{master}/jobs/{job:.+}', method = RequestMethod.PUT)
@RequestMapping(value = '/masters/{name}/jobs/**', method = RequestMethod.PUT)
String build(
@PathVariable("master") String master,
@PathVariable String job, @RequestParam Map<String, String> requestParams) {
@PathVariable("name") String master,
@RequestParam Map<String, String> requestParams, HttpServletRequest request) {
def job = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).split('/').drop(4).join('/')
if (!masters.map.containsKey(master)) {
throw new MasterNotFoundException()
}
Expand Down Expand Up @@ -125,10 +132,12 @@ class BuildController {
queuedLocation.split('/')[-1]
}

@RequestMapping(value = '/jobs/{master}/{job}/{buildNumber}/properties/{fileName:.+}')
@RequestMapping(value = '/builds/properties/{buildNumber}/{fileName}/{master}/**')
Map<String, Object> getProperties(
@PathVariable String master,
@PathVariable String job, @PathVariable Integer buildNumber, @PathVariable String fileName) {
@PathVariable Integer buildNumber, @PathVariable String fileName, HttpServletRequest request) {
def job = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).split('/').drop(6).join('/')
if (!masters.map.containsKey(master)) {
throw new MasterNotFoundException()
}
Expand Down Expand Up @@ -168,4 +177,77 @@ class BuildController {
@ResponseStatus(value = HttpStatus.SERVICE_UNAVAILABLE, reason = "Failed to determine job from queued item!")
@InheritConstructors
static class QueuedJobDeterminationError extends RuntimeException {}

// LEGACY ENDPOINTS:

@RequestMapping(value = '/jobs/{master}/{job}/{buildNumber}')
Map getJobStatusLegacy(@PathVariable String master, @PathVariable String job, @PathVariable Integer buildNumber) {
if (!masters.map.containsKey(master)) {
throw new MasterNotFoundException()
}
Map result = objectMapper.convertValue(masters.map[master].getBuild(job, buildNumber), Map)
try {
Map scm = objectMapper.convertValue(masters.map[master].getGitDetails(job, buildNumber), Map)
if (scm?.action?.lastBuiltRevision?.branch?.name) {
result.scm = scm?.action.lastBuiltRevision
result.scm = result.scm.branch.collect {
it.branch = it.name.split('/').last()
it
}

}
}catch(Exception e){
log.error("could not get scm results for $master / $job / $buildNumber")
}
result
}

@RequestMapping(value = '/jobs/{master}/queue/{item}')
QueuedJob getQueueLocationLegacy(@PathVariable String master, @PathVariable int item){
if (!masters.map.containsKey(master)) {
throw new MasterNotFoundException()
}
masters.map[master].getQueuedItem(item)
}

@RequestMapping(value = '/jobs/{master}/{job}/builds')
List<Build> getBuildsLegacy(@PathVariable String master, @PathVariable String job) {
if (!masters.map.containsKey(master)) {
throw new MasterNotFoundException()
}
masters.map[master].getBuilds(job).list
}

@RequestMapping(value = '/jobs/{master}/{job}/{buildNumber}/properties/{fileName:.+}')
Map<String, Object> getPropertiesLegacy(
@PathVariable String master,
@PathVariable String job, @PathVariable Integer buildNumber, @PathVariable String fileName) {
if (!masters.map.containsKey(master)) {
throw new MasterNotFoundException()
}
Map<String, Object> map = [:]
try {
def jenkinsService = masters.map[master]
String path = jenkinsService.getBuild(job, buildNumber).artifacts.find {
it.fileName == fileName
}?.relativePath

def propertyStream = jenkinsService.getPropertyFile(job, buildNumber, path).body.in()

if (fileName.endsWith('.yml') || fileName.endsWith('.yaml')) {
Yaml yml = new Yaml()
map = yml.load(propertyStream)
} else if (fileName.endsWith('.json')) {
map = objectMapper.readValue(propertyStream, Map)
} else {
Properties properties = new Properties()
properties.load(propertyStream)
map = map << properties
}
} catch (e) {
log.error("Unable to get properties `${job}`", e)
}
map
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.netflix.spinnaker.igor.jenkins.client.model.Build
import com.netflix.spinnaker.igor.jenkins.client.model.JobConfig
import groovy.transform.InheritConstructors
import groovy.util.logging.Slf4j
import javax.servlet.http.HttpServletRequest
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.PathVariable
Expand All @@ -30,6 +31,7 @@ import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.servlet.HandlerMapping

import javax.ws.rs.QueryParam

Expand Down Expand Up @@ -73,11 +75,31 @@ class InfoController {
throw new MasterNotFoundException("Master '${master}' does not exist")
}

jenkinsService.jobs.list.collect { it.name }
def jobList = []
def recursiveGetJobs

recursiveGetJobs = { list, prefix="" ->
if (prefix) {
prefix = prefix + "/job/"
}
list.each {
if (it.list == null || it.list.empty) {
jobList << prefix + it.name
} else {
recursiveGetJobs(it.list, prefix + it.name)
}
}
}
recursiveGetJobs(jenkinsService.jobs.list)

return jobList
}

@RequestMapping(value = '/jobs/{master}/{job:.+}')
JobConfig getJobConfig(@PathVariable String master, @PathVariable String job) {
@RequestMapping(value = '/jobs/{master}/**')
JobConfig getJobConfig(@PathVariable String master, HttpServletRequest request) {
def job = (String) request.getAttribute(
HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).split('/').drop(3).join('/')

log.info('Getting the job config for {} at {}', job, master)

def jenkinsService = masters.map[master]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import com.netflix.spinnaker.igor.jenkins.client.model.ProjectsList
import com.netflix.spinnaker.igor.jenkins.client.model.QueuedJob
import com.netflix.spinnaker.igor.jenkins.client.model.ScmDetails
import retrofit.client.Response
import retrofit.http.EncodedPath
import retrofit.http.GET
import retrofit.http.POST
import retrofit.http.Path
Expand All @@ -36,44 +37,48 @@ import retrofit.http.Streaming
*/
@SuppressWarnings('LineLength')
interface JenkinsClient {
/*
* Jobs created with the Jenkins Folders plugin are nested.
* Some queries look for jobs within folders with a depth of 10.
*/

@GET('/api/xml?tree=jobs[name,lastBuild[actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url]]&exclude=/*/*/*/action[not(totalCount)]')
@GET('/api/xml?tree=jobs[name,lastBuild[actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url],jobs[name,lastBuild[actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url],jobs[name,lastBuild[actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url],jobs[name,lastBuild[actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url],jobs[name,lastBuild[actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url],jobs[name,lastBuild[actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url],jobs[name,lastBuild[actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url],jobs[name,lastBuild[actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url],jobs[name,lastBuild[actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url],jobs[name,lastBuild[actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url]]]]]]]]]]]&exclude=/*/*/*/action[not(totalCount)]')
ProjectsList getProjects()

@GET('/api/xml?tree=jobs[name]')
@GET('/api/xml?tree=jobs[name,jobs[name,jobs[name,jobs[name,jobs[name,jobs[name,jobs[name,jobs[name,jobs[name,jobs[name]]]]]]]]]]')
JobList getJobs()

@GET('/job/{jobName}/api/xml?exclude=/*/build/action[not(totalCount)]&tree=builds[number,url,duration,timestamp,result,building,url,fullDisplayName,actions[failCount,skipCount,totalCount]]')
BuildsList getBuilds(@Path('jobName') String jobName)
BuildsList getBuilds(@EncodedPath('jobName') String jobName)

@GET('/job/{jobName}/api/xml?tree=name,url,actions[processes[name]],downstreamProjects[name,url],upstreamProjects[name,url]')
BuildDependencies getDependencies(@Path('jobName') String jobName)
BuildDependencies getDependencies(@EncodedPath('jobName') String jobName)

@GET('/job/{jobName}/{buildNumber}/api/xml?exclude=/*/action[not(totalCount)]&tree=actions[failCount,skipCount,totalCount,urlName],duration,number,timestamp,result,building,url,fullDisplayName,artifacts[displayPath,fileName,relativePath]')
Build getBuild(@Path('jobName') String jobName, @Path('buildNumber') Integer buildNumber)
Build getBuild(@EncodedPath('jobName') String jobName, @Path('buildNumber') Integer buildNumber)

@GET('/job/{jobName}/{buildNumber}/api/xml?exclude=/*/action[not(lastBuiltRevision)]&tree=actions[remoteUrl,lastBuiltRevision[branch[name,SHA1]]]')
ScmDetails getGitDetails(@Path('jobName') String jobName, @Path('buildNumber') Integer buildNumber)
ScmDetails getGitDetails(@EncodedPath('jobName') String jobName, @Path('buildNumber') Integer buildNumber)

@GET('/job/{jobName}/lastCompletedBuild/api/xml')
Build getLatestBuild(@Path('jobName') String jobName)
Build getLatestBuild(@EncodedPath('jobName') String jobName)

@GET('/queue/item/{itemNumber}/api/xml')
QueuedJob getQueuedItem(@Path('itemNumber') Integer item)

@POST('/job/{jobName}/build')
Response build(@Path('jobName') String jobName)
Response build(@EncodedPath('jobName') String jobName)

@POST('/job/{jobName}/buildWithParameters')
Response buildWithParameters(@Path('jobName') String jobName, @QueryMap Map<String, String> queryParams)
Response buildWithParameters(@EncodedPath('jobName') String jobName, @QueryMap Map<String, String> queryParams)

@GET('/job/{jobName}/api/xml?exclude=/*/action&exclude=/*/build&exclude=/*/property[not(parameterDefinition)]')
JobConfig getJobConfig(@Path('jobName') String jobName)
JobConfig getJobConfig(@EncodedPath('jobName') String jobName)

@Streaming
@GET('/job/{jobName}/{buildNumber}/artifact/{fileName}')
Response getPropertyFile(
@Path('jobName') String jobName,
@EncodedPath('jobName') String jobName,
@Path('buildNumber') Integer buildNumber, @Path(value = 'fileName', encode = false) String fileName)

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.netflix.spinnaker.igor.jenkins.client.model
import groovy.transform.CompileStatic
import org.simpleframework.xml.Attribute
import org.simpleframework.xml.Element
import org.simpleframework.xml.ElementList
import org.simpleframework.xml.Root

/**
Expand All @@ -27,6 +28,9 @@ import org.simpleframework.xml.Root
@CompileStatic
@Root(name='job')
class Project {
@ElementList(inline = true, name = "job", required=false)
List<Project> list

@Element String name
@Element(required = false)
Build lastBuild
Expand Down
Loading

0 comments on commit cc0125a

Please sign in to comment.