diff --git a/gate-core/src/main/groovy/com/netflix/spinnaker/gate/retrofit/UpstreamBadRequest.java b/gate-core/src/main/groovy/com/netflix/spinnaker/gate/retrofit/UpstreamBadRequest.java index ddfac276b7..a417a2a633 100644 --- a/gate-core/src/main/groovy/com/netflix/spinnaker/gate/retrofit/UpstreamBadRequest.java +++ b/gate-core/src/main/groovy/com/netflix/spinnaker/gate/retrofit/UpstreamBadRequest.java @@ -18,6 +18,7 @@ import com.netflix.hystrix.exception.HystrixBadRequestException; import retrofit.RetrofitError; +import java.util.Collection; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; import static retrofit.RetrofitError.Kind.HTTP; @@ -48,4 +49,12 @@ public static Exception classifyError(RetrofitError error) { } } + public static Exception classifyError(RetrofitError error, Collection supportedHttpStatuses) { + if (error.getKind() == HTTP && supportedHttpStatuses.contains(error.getResponse().getStatus())) { + return new UpstreamBadRequest(error); + } else { + return error; + } + } + } diff --git a/gate-core/src/main/groovy/com/netflix/spinnaker/gate/services/commands/AbstractHystrixCommand.groovy b/gate-core/src/main/groovy/com/netflix/spinnaker/gate/services/commands/AbstractHystrixCommand.groovy index 5e8f82052a..a2d74454eb 100644 --- a/gate-core/src/main/groovy/com/netflix/spinnaker/gate/services/commands/AbstractHystrixCommand.groovy +++ b/gate-core/src/main/groovy/com/netflix/spinnaker/gate/services/commands/AbstractHystrixCommand.groovy @@ -18,11 +18,15 @@ package com.netflix.spinnaker.gate.services.commands import com.netflix.hystrix.HystrixCommand import com.netflix.hystrix.HystrixCommandKey +import com.netflix.spinnaker.gate.retrofit.UpstreamBadRequest import groovy.transform.CompileStatic import groovy.util.logging.Slf4j +import org.springframework.http.HttpStatus +import retrofit.RetrofitError import static HystrixFactory.createHystrixCommandPropertiesSetter import static HystrixFactory.toGroupKey +import static retrofit.RetrofitError.Kind.HTTP @Slf4j @CompileStatic @@ -49,7 +53,11 @@ abstract class AbstractHystrixCommand extends HystrixCommand { @Override protected T run() throws Exception { - return work() + try { + return work() + } catch (RetrofitError error) { + throw UpstreamBadRequest.classifyError(error, HttpStatus.values().findAll { it.is4xxClientError() }*.value() as Collection) + } } protected T getFallback() { diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/config/GateWebConfig.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/config/GateWebConfig.groovy index 3d896c9aac..c9522c8675 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/config/GateWebConfig.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/config/GateWebConfig.groovy @@ -16,16 +16,27 @@ package com.netflix.spinnaker.gate.config +import com.netflix.hystrix.exception.HystrixRuntimeException import com.netflix.spectator.api.Registry +import com.netflix.spinnaker.gate.retrofit.UpstreamBadRequest import com.netflix.spinnaker.kork.web.interceptors.MetricsInterceptor import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Bean import org.springframework.context.annotation.ComponentScan import org.springframework.context.annotation.Configuration +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 org.springframework.web.filter.ShallowEtagHeaderFilter import org.springframework.web.servlet.config.annotation.InterceptorRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter +import retrofit.RetrofitError + import javax.servlet.Filter +import javax.servlet.http.HttpServletRequest +import javax.servlet.http.HttpServletResponse @Configuration @ComponentScan @@ -46,4 +57,36 @@ public class GateWebConfig extends WebMvcConfigurerAdapter { Filter eTagFilter() { new ShallowEtagHeaderFilter() } + + @Bean + UpstreamBadRequestExceptionHandler upstreamBadRequestExceptionHandler() { + return new UpstreamBadRequestExceptionHandler() + } + + @ControllerAdvice + static class UpstreamBadRequestExceptionHandler { + @ResponseBody + @ExceptionHandler(UpstreamBadRequest) + public Map handleUpstreamBadRequest( + HttpServletRequest request, + HttpServletResponse response, + UpstreamBadRequest exception + ) { + response.setStatus(exception.status) + + def failureCause = exception.cause + if (failureCause instanceof RetrofitError) { + failureCause = failureCause.cause ?: failureCause + } + + return [ + failureCause: failureCause.toString(), + error: HttpStatus.valueOf(exception.status).reasonPhrase, + message: exception.message, + status: exception.status, + url: exception.url, + timestamp: System.currentTimeMillis() + ] + } + } }