Skip to content

Commit

Permalink
Merge pull request #49 from ajordens/hystrix-no-fallbacks
Browse files Browse the repository at this point in the history
Introduced Hystrix around the calls to jenkins shards
  • Loading branch information
ajordens committed Nov 30, 2015
2 parents 0a3aa17 + 89082eb commit 354f27d
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 13 deletions.
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ buildscript {
allprojects {
apply plugin: 'spinnaker.project'
apply plugin: 'groovy'

spinnaker {
dependenciesVersion = "0.19.0"
dependenciesVersion = "0.20.0"
}
test {
testLogging {
Expand Down
3 changes: 2 additions & 1 deletion igor-web/igor-web.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ dependencies {
compile spinnaker.dependency("rxJava")
compile spinnaker.dependency("retrofit")
compile spinnaker.dependency("eurekaClient")
compile spinnaker.dependency("korkHystrix")

compile 'org.yaml:snakeyaml:1.15'
compile 'com.squareup.retrofit:converter-simplexml:1.5.1'
Expand Down Expand Up @@ -70,7 +71,7 @@ startScripts {
. /etc/default/spinnaker
set +a
fi
DEFAULT_JVM_OPTS='''.stripIndent())
DEFAULT_JVM_OPTS='''.stripIndent())
unixScript.text = unixScript.text.replace('CLASSPATH=$APP_HOME', 'CLASSPATH=$APP_HOME/config:$APP_HOME')
windowsScript.text = windowsScript.text.replace('set CLASSPATH=', 'set CLASSPATH=%APP_HOME%\\config;')
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,20 @@

package com.netflix.spinnaker.igor.config

import com.netflix.hystrix.exception.HystrixRuntimeException
import groovy.transform.CompileStatic
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseBody
import org.springframework.web.bind.annotation.ResponseStatus
import retrofit.RetrofitError

import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.http.MediaType
import org.springframework.web.servlet.HandlerExceptionResolver
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter
import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver

/**
* Converts validation errors into REST Messages
Expand All @@ -36,4 +41,32 @@ class IgorConfig extends WebMvcConfigurerAdapter {
ExecutorService executorService() {
Executors.newCachedThreadPool()
}

@Bean
HystrixRuntimeExceptionHandler hystrixRuntimeExceptionHandler() {
return new HystrixRuntimeExceptionHandler()
}

@ControllerAdvice
static class HystrixRuntimeExceptionHandler {
@ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
@ResponseBody
@ExceptionHandler(HystrixRuntimeException)
public Map handleHystrix(HystrixRuntimeException exception) {
def failureCause = exception.cause
if (failureCause instanceof RetrofitError) {
failureCause = failureCause.cause ?: failureCause
}

return [
fallbackException: exception.fallbackException.toString(),
failureType: exception.failureType,
failureCause: failureCause.toString(),
error: "Hystrix Failure",
message: exception.message,
status: HttpStatus.TOO_MANY_REQUESTS.value(),
timestamp: System.currentTimeMillis()
]
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@ package com.netflix.spinnaker.igor.config

import com.netflix.spinnaker.igor.jenkins.client.JenkinsClient
import com.netflix.spinnaker.igor.jenkins.client.JenkinsMasters
import com.squareup.okhttp.Authenticator
import com.netflix.spinnaker.igor.jenkins.service.JenkinsService
import com.squareup.okhttp.Credentials
import com.squareup.okhttp.OkHttpClient
import com.squareup.okhttp.Request
import com.squareup.okhttp.Response
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import org.springframework.context.annotation.Bean
Expand All @@ -47,11 +44,15 @@ class JenkinsConfig {
JenkinsMasters jenkinsMasters(@Valid JenkinsProperties jenkinsProperties) {
new JenkinsMasters(map: jenkinsProperties?.masters?.collectEntries { JenkinsProperties.JenkinsHost host ->
log.info "bootstrapping ${host.address} as ${host.name}"
[(host.name): jenkinsClient(host.address, host.username, host.password)]
[(host.name): jenkinsService(host.name, jenkinsClient(host.address, host.username, host.password))]
})
}

JenkinsClient jenkinsClient(String address, String username, String password) {
static JenkinsService jenkinsService(String jenkinsHostId, JenkinsClient jenkinsClient) {
return new JenkinsService(jenkinsHostId, jenkinsClient)
}

static JenkinsClient jenkinsClient(String address, String username, String password) {
new RestAdapter.Builder()
.setEndpoint(Endpoints.newFixedEndpoint(address))
.setRequestInterceptor(new BasicAuthRequestInterceptor(username, password))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@

package com.netflix.spinnaker.igor.jenkins.client

import com.netflix.spinnaker.igor.jenkins.service.JenkinsService

/**
* Wrapper class for a collection of jenkins clients
*/
class JenkinsMasters {

Map<String, JenkinsClient> map
Map<String, JenkinsService> map

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
/*
* Copyright 2015 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/


package com.netflix.spinnaker.igor.jenkins.service

import com.netflix.spinnaker.hystrix.SimpleHystrixCommand
import com.netflix.spinnaker.igor.jenkins.client.JenkinsClient
import com.netflix.spinnaker.igor.jenkins.client.model.Build
import com.netflix.spinnaker.igor.jenkins.client.model.BuildDependencies
import com.netflix.spinnaker.igor.jenkins.client.model.BuildsList
import com.netflix.spinnaker.igor.jenkins.client.model.JobConfig
import com.netflix.spinnaker.igor.jenkins.client.model.JobList
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

class JenkinsService {
final String groupKey
final JenkinsClient jenkinsClient

JenkinsService(String jenkinsHostId, JenkinsClient jenkinsClient) {
this.groupKey = "jenkins-${jenkinsHostId}"
this.jenkinsClient = jenkinsClient
}

ProjectsList getProjects() {
new SimpleHystrixCommand<ProjectsList>(
groupKey, "getProjects", {
return jenkinsClient.getProjects()
}).execute()
}

JobList getJobs() {
new SimpleHystrixCommand<JobList>(
groupKey, "getJobs", {
return jenkinsClient.getJobs()
}).execute()
}

BuildsList getBuilds(String jobName) {
new SimpleHystrixCommand<BuildsList>(
groupKey, "getBuilds", {
return jenkinsClient.getBuilds(jobName)
}).execute()
}

BuildDependencies getDependencies(String jobName) {
new SimpleHystrixCommand<BuildDependencies>(
groupKey, "getDependencies", {
return jenkinsClient.getDependencies(jobName)
}).execute()
}

Build getBuild(String jobName, Integer buildNumber) {
new SimpleHystrixCommand<Build>(
groupKey, "getBuild", {
return jenkinsClient.getBuild(jobName, buildNumber)
}).execute()
}

ScmDetails getGitDetails(String jobName, Integer buildNumber) {
new SimpleHystrixCommand<ScmDetails>(
groupKey, "getGitDetails", {
return jenkinsClient.getGitDetails(jobName, buildNumber)
}).execute()
}

Build getLatestBuild(String jobName) {
new SimpleHystrixCommand<Build>(
groupKey, "getLatestBuild", {
return jenkinsClient.getLatestBuild(jobName)
}).execute()
}

QueuedJob getQueuedItem(Integer item) {
new SimpleHystrixCommand<QueuedJob>(
groupKey, "getQueuedItem", {
return jenkinsClient.getQueuedItem(item)
}).execute()
}

Response build(String jobName) {
new SimpleHystrixCommand<Response>(
groupKey, "build", {
return jenkinsClient.build(jobName)
}).execute()
}

Response buildWithParameters(String jobName, Map<String, String> queryParams) {
new SimpleHystrixCommand<Response>(
groupKey, "buildWithParameters", {
return jenkinsClient.buildWithParameters(jobName, queryParams)
}).execute()
}

JobConfig getJobConfig(String jobName) {
new SimpleHystrixCommand<JobConfig>(
groupKey, "getJobConfig", {
return jenkinsClient.getJobConfig(jobName)
}).execute()
}

Response getPropertyFile(String jobName, Integer buildNumber, String fileName) {
new SimpleHystrixCommand<Response>(
groupKey, "getPropertyFile", {
return jenkinsClient.getPropertyFile(jobName, buildNumber, fileName)
}).execute()

}
}

0 comments on commit 354f27d

Please sign in to comment.