diff --git a/gate-core/gate-core.gradle b/gate-core/gate-core.gradle index 7ad8a916ad..5c450f938f 100644 --- a/gate-core/gate-core.gradle +++ b/gate-core/gate-core.gradle @@ -23,6 +23,7 @@ dependencies { implementation "io.spinnaker.kork:kork-artifacts" implementation "io.spinnaker.kork:kork-plugins" + implementation "io.spinnaker.kork:kork-retrofit" implementation "com.jakewharton.retrofit:retrofit1-okhttp3-client:1.1.0" implementation "com.squareup.retrofit:converter-jackson" implementation "com.squareup.okhttp:okhttp" diff --git a/gate-core/src/main/java/com/netflix/spinnaker/gate/retrofit/UpstreamBadRequest.java b/gate-core/src/main/java/com/netflix/spinnaker/gate/retrofit/UpstreamBadRequest.java deleted file mode 100644 index e0b38769b0..0000000000 --- a/gate-core/src/main/java/com/netflix/spinnaker/gate/retrofit/UpstreamBadRequest.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.gate.retrofit; - -import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; -import static retrofit.RetrofitError.Kind.HTTP; - -import com.netflix.spinnaker.kork.exceptions.SpinnakerException; -import java.util.Collection; -import retrofit.RetrofitError; - -public class UpstreamBadRequest extends SpinnakerException { - - private final int status; - private final String url; - private final Object error; - - private UpstreamBadRequest(RetrofitError cause) { - super(cause.getMessage(), cause); - status = cause.getResponse().getStatus(); - url = cause.getUrl(); - error = cause.getBody(); - } - - public int getStatus() { - return status; - } - - public String getUrl() { - return url; - } - - public Object getError() { - return error; - } - - public static RuntimeException classifyError(RetrofitError error) { - if (error.getKind() == HTTP - && error.getResponse().getStatus() < INTERNAL_SERVER_ERROR.value()) { - return new UpstreamBadRequest(error); - } else { - return error; - } - } - - public static RuntimeException 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/java/com/netflix/spinnaker/gate/services/PermissionService.java b/gate-core/src/main/java/com/netflix/spinnaker/gate/services/PermissionService.java index 91c32e9f65..02a6b90b82 100644 --- a/gate-core/src/main/java/com/netflix/spinnaker/gate/services/PermissionService.java +++ b/gate-core/src/main/java/com/netflix/spinnaker/gate/services/PermissionService.java @@ -17,18 +17,20 @@ package com.netflix.spinnaker.gate.services; + import com.netflix.spinnaker.fiat.model.UserPermission; import com.netflix.spinnaker.fiat.model.resources.Role; import com.netflix.spinnaker.fiat.model.resources.ServiceAccount; import com.netflix.spinnaker.fiat.shared.FiatPermissionEvaluator; import com.netflix.spinnaker.fiat.shared.FiatService; import com.netflix.spinnaker.fiat.shared.FiatStatus; -import com.netflix.spinnaker.gate.retrofit.UpstreamBadRequest; import com.netflix.spinnaker.gate.security.SpinnakerUser; import com.netflix.spinnaker.gate.services.internal.ExtendedFiatService; import com.netflix.spinnaker.kork.core.RetrySupport; import com.netflix.spinnaker.kork.exceptions.SpinnakerException; import com.netflix.spinnaker.kork.exceptions.SystemException; +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException; +import com.netflix.spinnaker.kork.retrofit.exceptions.UpstreamBadRequest; import com.netflix.spinnaker.security.AuthenticatedRequest; import com.netflix.spinnaker.security.User; import java.time.Duration; @@ -83,7 +85,7 @@ public void login(final String userId) { permissionEvaluator.invalidatePermission(userId); return null; }); - } catch (RetrofitError e) { + } catch (SpinnakerServerException e) { throw UpstreamBadRequest.classifyError(e); } } @@ -98,7 +100,7 @@ public void loginWithRoles(final String userId, final Collection roles) permissionEvaluator.invalidatePermission(userId); return null; }); - } catch (RetrofitError e) { + } catch (SpinnakerServerException e) { throw UpstreamBadRequest.classifyError(e); } } @@ -109,7 +111,7 @@ public void logout(String userId) { try { getFiatServiceForLogin().logoutUser(userId); permissionEvaluator.invalidatePermission(userId); - } catch (RetrofitError e) { + } catch (SpinnakerServerException e) { throw UpstreamBadRequest.classifyError(e); } } @@ -119,7 +121,7 @@ public void sync() { if (fiatStatus.isEnabled()) { try { getFiatServiceForLogin().sync(List.of()); - } catch (RetrofitError e) { + } catch (SpinnakerServerException e) { throw UpstreamBadRequest.classifyError(e); } } @@ -133,7 +135,7 @@ public Set getRoles(String userId) { var permission = permissionEvaluator.getPermission(userId); var roles = permission != null ? permission.getRoles() : null; return roles != null ? roles : Set.of(); - } catch (RetrofitError e) { + } catch (SpinnakerServerException e) { throw UpstreamBadRequest.classifyError(e); } } @@ -141,13 +143,19 @@ public Set getRoles(String userId) { List lookupServiceAccounts(String userId) { try { return extendedFiatService.getUserServiceAccounts(userId); - } catch (RetrofitError re) { - var response = re.getResponse(); - if (response != null && response.getStatus() == HttpStatus.NOT_FOUND.value()) { - return List.of(); + } catch (SpinnakerServerException e) { + RetrofitError re = e.getRetrofitError(); + boolean shouldRetry = false; + + if (re != null) { + var response = re.getResponse(); + if (response != null && response.getStatus() == HttpStatus.NOT_FOUND.value()) { + return List.of(); + } + shouldRetry = + response == null || HttpStatus.valueOf(response.getStatus()).is5xxServerError(); } - boolean shouldRetry = - response == null || HttpStatus.valueOf(response.getStatus()).is5xxServerError(); + throw new SystemException("getUserServiceAccounts failed", re).setRetryable(shouldRetry); } } @@ -217,8 +225,8 @@ public List getServiceAccounts(@SpinnakerUser User user) { return permission.getServiceAccounts().stream() .map(ServiceAccount.View::getName) .collect(Collectors.toList()); - } catch (RetrofitError re) { - throw UpstreamBadRequest.classifyError(re); + } catch (SpinnakerServerException e) { + throw UpstreamBadRequest.classifyError(e); } } diff --git a/gate-core/src/test/groovy/com/netflix/spinnaker/gate/services/PermissionServiceSpec.groovy b/gate-core/src/test/groovy/com/netflix/spinnaker/gate/services/PermissionServiceSpec.groovy index bc93465fc5..71b15c7767 100644 --- a/gate-core/src/test/groovy/com/netflix/spinnaker/gate/services/PermissionServiceSpec.groovy +++ b/gate-core/src/test/groovy/com/netflix/spinnaker/gate/services/PermissionServiceSpec.groovy @@ -25,6 +25,7 @@ import com.netflix.spinnaker.fiat.shared.FiatPermissionEvaluator import com.netflix.spinnaker.fiat.shared.FiatStatus import com.netflix.spinnaker.gate.services.internal.ExtendedFiatService import com.netflix.spinnaker.kork.exceptions.SpinnakerException +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException; import com.netflix.spinnaker.security.User import retrofit.RetrofitError import retrofit.client.Response @@ -83,29 +84,30 @@ class PermissionServiceSpec extends Specification { unexpectedError() || true } - private RetrofitError conversionError(int code) { - RetrofitError.conversionError( + private static SpinnakerServerException conversionError(int code) { + new SpinnakerServerException(RetrofitError.conversionError( 'http://foo', new Response('http://foo', code, 'you are bad', [], null), null, null, - new ConversionException('boom')) + new ConversionException('boom'))) } - private RetrofitError networkError() { - RetrofitError.networkError('http://foo', new IOException()) + private static SpinnakerServerException networkError() { + new SpinnakerServerException( + RetrofitError.networkError('http://foo', new IOException())) } - private RetrofitError unexpectedError() { - RetrofitError.unexpectedError('http://foo', new Throwable()) + private static SpinnakerServerException unexpectedError() { + new SpinnakerServerException(RetrofitError.unexpectedError('http://foo', new Throwable())) } - private RetrofitError httpRetrofitError(int code) { - RetrofitError.httpError( + private static SpinnakerServerException httpRetrofitError(int code) { + new SpinnakerServerException(RetrofitError.httpError( 'http://foo', new Response('http://foo', code, 'you are bad', [], null), null, - null) + null)) } diff --git a/gate-iap/gate-iap.gradle b/gate-iap/gate-iap.gradle index a2fe1bd8d2..1d1c94b24c 100644 --- a/gate-iap/gate-iap.gradle +++ b/gate-iap/gate-iap.gradle @@ -2,6 +2,8 @@ dependencies { implementation project(":gate-core") implementation 'com.nimbusds:nimbus-jose-jwt' implementation "com.github.ben-manes.caffeine:guava" + implementation "io.spinnaker.kork:kork-exceptions" + implementation "io.spinnaker.kork:kork-retrofit" implementation "io.spinnaker.kork:kork-security" implementation "io.spinnaker.fiat:fiat-api:$fiatVersion" implementation "io.spinnaker.fiat:fiat-core:$fiatVersion" diff --git a/gate-iap/src/main/java/com/netflix/spinnaker/gate/security/iap/IapAuthenticationFilter.java b/gate-iap/src/main/java/com/netflix/spinnaker/gate/security/iap/IapAuthenticationFilter.java index 8aa587b9a7..b6abbb409b 100644 --- a/gate-iap/src/main/java/com/netflix/spinnaker/gate/security/iap/IapAuthenticationFilter.java +++ b/gate-iap/src/main/java/com/netflix/spinnaker/gate/security/iap/IapAuthenticationFilter.java @@ -21,6 +21,7 @@ import com.netflix.spinnaker.gate.security.iap.IapSsoConfig.IapSecurityConfigProperties; import com.netflix.spinnaker.gate.services.PermissionService; import com.netflix.spinnaker.gate.services.internal.Front50Service; +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException; import com.netflix.spinnaker.security.User; import com.nimbusds.jose.JWSHeader; import com.nimbusds.jose.JWSVerifier; @@ -50,7 +51,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; import org.springframework.web.filter.OncePerRequestFilter; -import retrofit.RetrofitError; /** * * This filter verifies the request header from Cloud IAP containing a JWT token, after the user @@ -140,8 +140,8 @@ private boolean isServiceAccount(String email) { } } return false; - } catch (RetrofitError re) { - log.warn("Could not get list of service accounts.", re); + } catch (SpinnakerServerException e) { + log.warn("Could not get list of service accounts.", e); } return false; } diff --git a/gate-oauth2/gate-oauth2.gradle b/gate-oauth2/gate-oauth2.gradle index 91403e7ea9..87f969b7fe 100644 --- a/gate-oauth2/gate-oauth2.gradle +++ b/gate-oauth2/gate-oauth2.gradle @@ -3,6 +3,7 @@ dependencies { implementation "com.netflix.spectator:spectator-api" implementation "io.spinnaker.fiat:fiat-api:$fiatVersion" implementation "io.spinnaker.kork:kork-exceptions" + implementation "io.spinnaker.kork:kork-retrofit" implementation "io.spinnaker.kork:kork-security" implementation "org.codehaus.groovy:groovy-json" implementation "org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure" diff --git a/gate-oauth2/src/main/groovy/com/netflix/spinnaker/gate/security/oauth2/SpinnakerUserInfoTokenServices.groovy b/gate-oauth2/src/main/groovy/com/netflix/spinnaker/gate/security/oauth2/SpinnakerUserInfoTokenServices.groovy index 966cfe5152..21713ad99f 100644 --- a/gate-oauth2/src/main/groovy/com/netflix/spinnaker/gate/security/oauth2/SpinnakerUserInfoTokenServices.groovy +++ b/gate-oauth2/src/main/groovy/com/netflix/spinnaker/gate/security/oauth2/SpinnakerUserInfoTokenServices.groovy @@ -24,6 +24,7 @@ import com.netflix.spinnaker.gate.services.CredentialsService import com.netflix.spinnaker.gate.services.PermissionService import com.netflix.spinnaker.gate.services.internal.Front50Service import com.netflix.spinnaker.kork.core.RetrySupport +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import com.netflix.spinnaker.security.User import groovy.json.JsonSlurper import groovy.util.logging.Slf4j @@ -39,7 +40,6 @@ import org.springframework.security.oauth2.provider.OAuth2Authentication import org.springframework.security.oauth2.provider.OAuth2Request import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken -import retrofit.RetrofitError import java.util.function.BiFunction import java.util.regex.Pattern @@ -188,8 +188,8 @@ class SpinnakerUserInfoTokenServices implements ResourceServerTokenServices { try { def serviceAccounts = front50Service.getServiceAccounts() return serviceAccounts.find { email.equalsIgnoreCase(it.name) } - } catch (RetrofitError re) { - log.warn("Could not get list of service accounts.", re) + } catch (SpinnakerServerException e) { + log.warn("Could not get list of service accounts.", e) } return false } diff --git a/gate-plugins/gate-plugins.gradle b/gate-plugins/gate-plugins.gradle index 36154c4ff3..c61dc9f413 100644 --- a/gate-plugins/gate-plugins.gradle +++ b/gate-plugins/gate-plugins.gradle @@ -24,7 +24,9 @@ dependencies { implementation "com.google.guava:guava" implementation "io.spinnaker.fiat:fiat-core:$fiatVersion" implementation "io.spinnaker.fiat:fiat-api:$fiatVersion" + implementation "io.spinnaker.kork:kork-hubble" implementation "io.spinnaker.kork:kork-plugins" + implementation "io.spinnaker.kork:kork-retrofit" implementation "io.spinnaker.kork:kork-web" implementation "io.swagger:swagger-annotations" diff --git a/gate-plugins/src/main/kotlin/com/netflix/spinnaker/gate/plugins/web/installed/PluginsInstalledController.kt b/gate-plugins/src/main/kotlin/com/netflix/spinnaker/gate/plugins/web/installed/PluginsInstalledController.kt index 67049bf9a3..d217762996 100644 --- a/gate-plugins/src/main/kotlin/com/netflix/spinnaker/gate/plugins/web/installed/PluginsInstalledController.kt +++ b/gate-plugins/src/main/kotlin/com/netflix/spinnaker/gate/plugins/web/installed/PluginsInstalledController.kt @@ -13,6 +13,7 @@ import com.netflix.spinnaker.gate.services.internal.SwabbieService import com.netflix.spinnaker.kork.plugins.SpinnakerPluginDescriptor import com.netflix.spinnaker.kork.plugins.SpinnakerPluginManager import com.netflix.spinnaker.kork.plugins.update.SpinnakerUpdateManager +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import io.swagger.annotations.ApiOperation import java.util.stream.Collectors import org.pf4j.PluginWrapper @@ -22,7 +23,6 @@ import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMethod import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.RestController -import retrofit.RetrofitError @RestController @RequestMapping("/plugins/installed") @@ -122,8 +122,8 @@ class PluginsInstalledController( fun callService(call: () -> List): List { return try { call() - } catch (e: RetrofitError) { - log.warn("Unable to retrieve installed plugins from '${e.response?.url}' due to '${e.response?.status}'") + } catch (e: SpinnakerServerException) { + log.warn("Unable to retrieve installed plugins from '${e.retrofitError?.response?.url}' due to '${e.retrofitError?.response?.status}'") return emptyList() } } diff --git a/gate-web/gate-web.gradle b/gate-web/gate-web.gradle index 18d3615d94..8d1d477f48 100644 --- a/gate-web/gate-web.gradle +++ b/gate-web/gate-web.gradle @@ -32,6 +32,7 @@ dependencies { implementation "io.spinnaker.kork:kork-core" implementation "io.spinnaker.kork:kork-config" implementation "io.spinnaker.kork:kork-plugins" + implementation "io.spinnaker.kork:kork-retrofit" implementation "io.spinnaker.kork:kork-web" implementation "com.netflix.frigga:frigga" implementation "redis.clients:jedis" 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 4acd511ec0..1f79d9d61f 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 @@ -21,12 +21,13 @@ import com.netflix.spinnaker.gate.filters.ContentCachingFilter import com.netflix.spinnaker.gate.interceptors.RequestContextInterceptor import com.netflix.spinnaker.gate.interceptors.ResponseHeaderInterceptor import com.netflix.spinnaker.gate.interceptors.ResponseHeaderInterceptorConfigurationProperties -import com.netflix.spinnaker.gate.retrofit.UpstreamBadRequest +import com.netflix.spinnaker.kork.retrofit.exceptions.UpstreamBadRequest import com.netflix.spinnaker.kork.dynamicconfig.DynamicConfigService -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import com.netflix.spinnaker.kork.web.interceptors.MetricsInterceptor import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Value +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.context.ApplicationContext import org.springframework.context.annotation.Bean @@ -40,7 +41,6 @@ import org.springframework.web.servlet.config.annotation.ContentNegotiationConfi import org.springframework.web.servlet.config.annotation.InterceptorRegistry import org.springframework.web.servlet.config.annotation.WebMvcConfigurer import org.springframework.web.servlet.handler.HandlerMappingIntrospector -import retrofit.RetrofitError import javax.servlet.Filter import javax.servlet.http.HttpServletResponse @@ -105,9 +105,9 @@ public class GateWebConfig implements WebMvcConfigurer { def message = exception.message def failureCause = exception.cause - if (failureCause instanceof RetrofitError) { + if (failureCause instanceof SpinnakerServerException) { try { - def retrofitBody = failureCause.getBodyAs(Map) as Map + def retrofitBody = failureCause.retrofitError.getBodyAs(Map) as Map message = retrofitBody.error ?: message } catch (Exception ignored) { // unable to extract error from response diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/BakeController.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/BakeController.groovy index 1d7aaedf83..6ea5cf83bd 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/BakeController.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/BakeController.groovy @@ -17,6 +17,7 @@ package com.netflix.spinnaker.gate.controllers import com.netflix.spinnaker.gate.services.BakeService +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import io.swagger.annotations.ApiOperation import org.springframework.beans.factory.annotation.Autowired import org.springframework.http.HttpStatus @@ -57,7 +58,13 @@ class BakeController { @ExceptionHandler @ResponseStatus(HttpStatus.NOT_FOUND) Map handleBakeOptionsException(Exception e) { - def errorMsg = e instanceof RetrofitError && e.getUrl().contains("/logs/") ? "logs.not.found" : "bake.options.not.found" + def errorMsg = "bake.options.not.found" + if (e instanceof SpinnakerServerException) { + RetrofitError re = e.getRetrofitError(); + if (re != null && re.getUrl().concat("/logs/")) { + errorMsg = "logs.not.found" + } + } return [error: errorMsg, status: HttpStatus.NOT_FOUND, message: e.message] } diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CanaryController.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CanaryController.groovy index 4ed19ccdd8..4b3c51b6f1 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CanaryController.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/CanaryController.groovy @@ -18,7 +18,7 @@ package com.netflix.spinnaker.gate.controllers import javax.servlet.http.HttpServletRequest import javax.servlet.http.HttpServletResponse -import com.netflix.spinnaker.gate.retrofit.UpstreamBadRequest +import com.netflix.spinnaker.kork.retrofit.exceptions.UpstreamBadRequest import com.netflix.spinnaker.gate.services.CanaryService import groovy.transform.CompileStatic import org.springframework.beans.factory.annotation.Autowired diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/ManagedController.java b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/ManagedController.java index bd43971857..b7de21fa9b 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/ManagedController.java +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/ManagedController.java @@ -14,6 +14,8 @@ import com.netflix.spinnaker.gate.model.manageddelivery.RetryVerificationRequest; import com.netflix.spinnaker.gate.services.NotificationService; import com.netflix.spinnaker.gate.services.internal.KeelService; +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException; +import com.netflix.spinnaker.kork.retrofit.exceptions.UpstreamBadRequest; import com.netflix.spinnaker.kork.web.interceptors.Criticality; import groovy.util.logging.Slf4j; import io.github.resilience4j.retry.RetryConfig; @@ -45,7 +47,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import retrofit.RetrofitError; import retrofit.client.Header; import retrofit.client.Response; @@ -91,7 +92,7 @@ private void configureRetry() { RetryConfig.custom() .maxAttempts(5) .waitDuration(Duration.ofSeconds(30)) - .retryExceptions(RetrofitError.class) + .retryExceptions(SpinnakerServerException.class) .build()); } @@ -217,21 +218,18 @@ DeliveryConfig deleteManifest(@PathVariable("name") String name) { path = "/delivery-configs/validate", consumes = {APPLICATION_JSON_VALUE, APPLICATION_YAML_VALUE}, produces = {APPLICATION_JSON_VALUE, APPLICATION_YAML_VALUE}) - ResponseEntity validateManifest(@RequestBody DeliveryConfig manifest) { + ResponseEntity validateManifest(@RequestBody DeliveryConfig manifest) { try { return ResponseEntity.ok(keelService.validateManifest(manifest)); - } catch (RetrofitError e) { - if (e.getResponse().getStatus() == 400) { - try { - return ResponseEntity.badRequest() - .body(objectMapper.readValue(e.getResponse().getBody().in(), Map.class)); - } catch (Exception ex) { - log.error("Error parsing error response from keel", ex); - return ResponseEntity.badRequest().body(Collections.emptyMap()); + } catch (SpinnakerServerException e) { + Throwable cause = e.getCause(); + if (cause instanceof UpstreamBadRequest) { + UpstreamBadRequest upstreamBadRequest = (UpstreamBadRequest) cause; + if (upstreamBadRequest.getStatus() == 400) { + return ResponseEntity.badRequest().body(upstreamBadRequest.getError()); } - } else { - throw e; } + throw e; } } diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/PagerDutyController.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/PagerDutyController.groovy index fb528e39f9..817c242b9c 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/PagerDutyController.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/PagerDutyController.groovy @@ -17,6 +17,7 @@ package com.netflix.spinnaker.gate.controllers import com.netflix.spinnaker.gate.services.PagerDutyService +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import com.netflix.spinnaker.security.AuthenticatedRequest import groovy.transform.CompileStatic import groovy.util.logging.Slf4j @@ -26,7 +27,6 @@ import org.springframework.scheduling.annotation.Scheduled import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController -import retrofit.RetrofitError import java.util.concurrent.atomic.AtomicReference @@ -80,7 +80,7 @@ class PagerDutyController { log.info("Fetched {} PagerDuty services", services?.size()) pagerDutyServicesCache.set(services) } catch (e) { - if (e instanceof RetrofitError && e.response?.status == 429) { + if (e instanceof SpinnakerServerException && e.retrofitError?.response?.status == 429) { log.warn("Unable to refresh PagerDuty service list (throttled!)") } else { log.error("Unable to refresh PagerDuty service list", e) @@ -92,7 +92,7 @@ class PagerDutyController { log.info("Fetched {} PagerDuty onCall", onCalls?.size()) pagerDutyOnCallCache.set(onCalls) } catch (e) { - if (e instanceof RetrofitError && e.response?.status == 429) { + if (e instanceof SpinnakerServerException && e.retrofitError?.response?.status == 429) { log.warn("Unable to refresh PagerDuty onCall list (throttled!)") } else { log.error("Unable to refresh PagerDuty onCall list", e) diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/PipelineController.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/PipelineController.groovy index 9fced36317..3de8c33c5a 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/PipelineController.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/PipelineController.groovy @@ -22,6 +22,7 @@ import com.netflix.spinnaker.gate.services.PipelineService import com.netflix.spinnaker.gate.services.TaskService import com.netflix.spinnaker.gate.services.internal.Front50Service import com.netflix.spinnaker.kork.exceptions.HasAdditionalAttributes +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import com.netflix.spinnaker.kork.web.exceptions.InvalidRequestException import com.netflix.spinnaker.kork.web.exceptions.NotFoundException import com.netflix.spinnaker.security.AuthenticatedRequest @@ -136,8 +137,8 @@ class PipelineController { Map getPipeline(@PathVariable("id") String id) { try { pipelineService.getPipeline(id) - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { throw new NotFoundException("Pipeline not found (id: ${id})") } } @@ -287,8 +288,8 @@ class PipelineController { @RequestParam("expression") String pipelineExpression) { try { pipelineService.evaluateExpressionForExecution(id, pipelineExpression) - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { throw new NotFoundException("Pipeline not found (id: ${id})") } } @@ -300,8 +301,8 @@ class PipelineController { @RequestBody String pipelineExpression) { try { pipelineService.evaluateExpressionForExecution(id, pipelineExpression) - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { throw new NotFoundException("Pipeline not found (id: ${id})") } } @@ -314,8 +315,8 @@ class PipelineController { @RequestParam("expression") String pipelineExpression) { try { pipelineService.evaluateExpressionForExecutionAtStage(id, stageId, pipelineExpression) - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { throw new NotFoundException("Pipeline not found (id: ${id})", e) } } @@ -350,8 +351,8 @@ class PipelineController { @RequestBody List> expressions) { try { return pipelineService.evaluateVariables(executionId, requisiteStageRefIds, spelVersionOverride, expressions) - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { throw new NotFoundException("Pipeline not found (id: ${executionId})") } } @@ -361,12 +362,12 @@ class PipelineController { try { def body = call() new ResponseEntity(body, HttpStatus.OK) - } catch (RetrofitError re) { - if (re.response?.status == HttpStatus.BAD_REQUEST.value() && requestBody.type == "templatedPipeline") { + } catch (SpinnakerServerException e) { + RetrofitError re = e.getRetrofitError() + if (re?.response?.status == HttpStatus.BAD_REQUEST.value() && requestBody.type == "templatedPipeline") { throw new PipelineException((HashMap) re.getBodyAs(HashMap.class)) - } else { - throw re } + throw e } } diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/SlackController.java b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/SlackController.java index 8c984fde13..9ae5c3eddc 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/SlackController.java +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/controllers/SlackController.java @@ -20,6 +20,7 @@ import com.netflix.spinnaker.gate.config.SlackConfigProperties; import com.netflix.spinnaker.gate.services.SlackService; import com.netflix.spinnaker.kork.core.RetrySupport; +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException; import io.swagger.annotations.ApiOperation; import java.time.Duration; import java.util.ArrayList; @@ -128,9 +129,10 @@ private SlackService.SlackChannelsResult getChannels(String token, String cursor } private Optional getRetryDelayMs(Exception e) { - if (e instanceof RetrofitError) { - RetrofitError re = (RetrofitError) e; - if (re.getKind() == RetrofitError.Kind.HTTP) { + if (e instanceof SpinnakerServerException) { + SpinnakerServerException se = (SpinnakerServerException) e; + RetrofitError re = se.getRetrofitError(); + if (re != null && re.getKind() == RetrofitError.Kind.HTTP) { if (re.getResponse() != null && re.getResponse().getStatus() == 429) { return re.getResponse().getHeaders().stream() // slack rate limit responses may include a `Retry-After` header indicating the number diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/health/DownstreamServicesHealthIndicator.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/health/DownstreamServicesHealthIndicator.groovy index 39aeea3475..132299c6dc 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/health/DownstreamServicesHealthIndicator.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/health/DownstreamServicesHealthIndicator.groovy @@ -22,6 +22,7 @@ import com.netflix.spinnaker.gate.config.Service import com.netflix.spinnaker.gate.config.ServiceConfiguration import com.netflix.spinnaker.gate.services.internal.HealthCheckableService import com.netflix.spinnaker.kork.client.ServiceClientProvider +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import com.netflix.spinnaker.security.AuthenticatedRequest import groovy.util.logging.Slf4j import org.springframework.beans.factory.annotation.Autowired @@ -96,8 +97,8 @@ class DownstreamServicesHealthIndicator extends AbstractHealthIndicator { healthCheckableServices.each { String name, HealthCheckableService service -> try { AuthenticatedRequest.allowAnonymous { service.health() } - } catch (RetrofitError e) { - serviceHealths[name] = "${e.message} (url: ${e.url})".toString() + } catch (SpinnakerServerException e) { + serviceHealths[name] = "${e.retrofitError.message} (url: ${e.retrofitError.url})".toString() log.error('Exception received during health check of service: {}, {}', name, serviceHealths[name]) } } diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ApplicationService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ApplicationService.groovy index 599dd5f66a..4658874986 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ApplicationService.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ApplicationService.groovy @@ -21,6 +21,7 @@ import com.netflix.spinnaker.gate.config.ServiceConfiguration import com.netflix.spinnaker.gate.services.internal.ClouddriverService import com.netflix.spinnaker.gate.services.internal.ClouddriverServiceSelector import com.netflix.spinnaker.gate.services.internal.Front50Service +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import com.netflix.spinnaker.security.AuthenticatedRequest import groovy.transform.CompileDynamic import groovy.transform.CompileStatic @@ -245,8 +246,8 @@ class ApplicationService { AuthenticatedRequest.propagate({ try { return AuthenticatedRequest.allowAnonymous { front50.getAllApplicationsUnrestricted() } - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { return [] } else { throw e @@ -284,12 +285,12 @@ class ApplicationService { metadata ?: [:] } catch (ConversionException ignored) { return [:] - } catch (RetrofitError e) { - if ((e.cause instanceof ConversionException) || e.response.status == 404) { + } catch (SpinnakerServerException e) { + RetrofitError re = e.getRetrofitError() + if ((re.cause instanceof ConversionException) || re.response.status == 404) { return [:] - } else { - throw e } + throw e } }, false, principal).call() as Map } catch (Exception e) { @@ -320,8 +321,8 @@ class ApplicationService { AuthenticatedRequest.propagate({ try { return AuthenticatedRequest.allowAnonymous { clouddriver.getAllApplicationsUnrestricted(expandClusterNames) } - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { return [] } else { throw e @@ -353,12 +354,11 @@ class ApplicationService { AuthenticatedRequest.propagate({ try { return clouddriver.getApplication(name) - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { return [:] - } else { - throw e } + throw e } }, false, principal).call() as Map } catch (Exception e) { diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/BuildService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/BuildService.groovy index dd427c2a36..e97d8e9205 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/BuildService.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/BuildService.groovy @@ -20,11 +20,11 @@ package com.netflix.spinnaker.gate.services import com.netflix.spinnaker.gate.services.internal.GoogleCloudBuildTrigger import com.netflix.spinnaker.gate.services.internal.IgorService +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import groovy.transform.CompileStatic import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component import org.springframework.web.util.UriUtils -import retrofit.RetrofitError @CompileStatic @Component @@ -62,8 +62,8 @@ class BuildService { } try { igorService.getJobsForBuildMaster(controller) - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { throw new BuildMasterNotFound("Build master '${controller}' not found") } throw e @@ -76,8 +76,8 @@ class BuildService { } try { igorService.getGoogleCloudBuildTriggers(account) - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { throw new GCBAccountNotFound("Account '${account}' not found") } @@ -91,8 +91,8 @@ class BuildService { } try { igorService.getJobConfig(controller, encode(job)) - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { throw new BuildMasterNotFound("Job not found (controller: '${controller}', job: '${job}')") } @@ -106,8 +106,8 @@ class BuildService { } try { igorService.getBuilds(controller, encode(job)) - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { throw new BuildMasterNotFound("Builds not found (controller: '${controller}', job: '${job}')") } @@ -121,8 +121,8 @@ class BuildService { } try { igorService.getBuild(controller, encode(job), number) - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { throw new BuildMasterNotFound("Build not found (controller: '${controller}', job: '${job}', build: ${number})") } diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CanaryService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CanaryService.groovy index e4bee91e6d..eac5fb2ea9 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CanaryService.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CanaryService.groovy @@ -17,12 +17,12 @@ package com.netflix.spinnaker.gate.services import com.netflix.spinnaker.gate.services.internal.MineService +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import groovy.transform.CompileStatic import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty import org.springframework.stereotype.Component -import retrofit.RetrofitError -import static com.netflix.spinnaker.gate.retrofit.UpstreamBadRequest.classifyError +import static com.netflix.spinnaker.kork.retrofit.exceptions.UpstreamBadRequest.classifyError @Component @CompileStatic @@ -35,7 +35,7 @@ class CanaryService { void generateCanaryResult(String canaryId, int duration, String durationUnit) { try { mineService?.generateCanaryResult(canaryId, duration, durationUnit, "") - } catch (RetrofitError error) { + } catch (SpinnakerServerException error) { throw classifyError(error) } } @@ -43,7 +43,7 @@ class CanaryService { List getCanaryAnalysisHistory(String canaryDeploymentId) { try { mineService ? mineService.getCanaryAnalysisHistory(canaryDeploymentId) : [] - } catch (RetrofitError error) { + } catch (SpinnakerServerException error) { throw classifyError(error) } } @@ -51,7 +51,7 @@ class CanaryService { Map endCanary(String canaryId, String result, String reason) { try { mineService ? mineService.endCanary(canaryId, result, reason, "") : [:] - } catch (RetrofitError error) { + } catch (SpinnakerServerException error) { throw classifyError(error) } } @@ -59,7 +59,7 @@ class CanaryService { Map showCanary(String canaryId) { try { mineService ? mineService.showCanary(canaryId) : [:] - } catch (RetrofitError error) { + } catch (SpinnakerServerException error) { throw classifyError(error) } } @@ -67,7 +67,7 @@ class CanaryService { List getCanaryConfigNames(String application) { try { mineService ? mineService.getCanaryConfigNames(application) : [] - } catch (RetrofitError error) { + } catch (SpinnakerServerException error) { throw classifyError(error) } } @@ -75,9 +75,8 @@ class CanaryService { List canaryConfigsForApplication(String applicationName) { try { mineService ? mineService.canaryConfigsForApplication(applicationName) : [] - } catch (RetrofitError error) { + } catch (SpinnakerServerException error) { throw classifyError(error) } } - } diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CleanupService.java b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CleanupService.java index 2eb1d6e980..abc256e762 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CleanupService.java +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CleanupService.java @@ -17,6 +17,7 @@ package com.netflix.spinnaker.gate.services; import com.netflix.spinnaker.gate.services.internal.SwabbieService; +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException; import java.util.Collections; import java.util.List; import java.util.Map; @@ -33,32 +34,36 @@ public class CleanupService { public Map optOut(String namespace, String resourceId) { try { return swabbieService.optOut(namespace, resourceId, ""); - } catch (RetrofitError e) { - if (e.getResponse().getStatus() == 404) { + } catch (SpinnakerServerException e) { + RetrofitError re = e.getRetrofitError(); + if (re != null && re.getResponse().getStatus() == 404) { return Collections.emptyMap(); - } else { - throw e; } + throw e; } } public Map get(String namespace, String resourceId) { try { return swabbieService.get(namespace, resourceId); - } catch (RetrofitError e) { - if (e.getResponse().getStatus() == 404) { + } catch (SpinnakerServerException e) { + RetrofitError re = e.getRetrofitError(); + if (re != null && re.getResponse().getStatus() == 404) { return Collections.emptyMap(); - } else { - throw e; } + throw e; } } public String restore(String namespace, String resourceId) { try { swabbieService.restore(namespace, resourceId, ""); - } catch (RetrofitError e) { - return Integer.toString(e.getResponse().getStatus()); + } catch (SpinnakerServerException e) { + RetrofitError re = e.getRetrofitError(); + if (re == null) { + return "500"; + } + return Integer.toString(re.getResponse().getStatus()); } return "200"; } diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ClusterService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ClusterService.groovy index 14b6a9bef4..47b36aaccf 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ClusterService.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ClusterService.groovy @@ -18,6 +18,7 @@ package com.netflix.spinnaker.gate.services import com.netflix.spinnaker.gate.services.internal.ClouddriverServiceSelector import com.netflix.spinnaker.kork.exceptions.SpinnakerException +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import groovy.transform.CompileStatic import groovy.transform.InheritConstructors import org.springframework.beans.factory.annotation.Autowired @@ -47,8 +48,8 @@ class ClusterService { Map getCluster(String app, String account, String clusterName, String selectorKey) { try { clouddriverServiceSelector.select().getCluster(app, account, clusterName)?.getAt(0) as Map - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { return [:] } else { throw e @@ -67,11 +68,16 @@ class ClusterService { Map getTargetServerGroup(String app, String account, String clusterName, String cloudProviderType, String scope, String target, Boolean onlyEnabled, Boolean validateOldest, String selectorKey) { try { return clouddriverServiceSelector.select().getTargetServerGroup(app, account, clusterName, cloudProviderType, scope, target, onlyEnabled, validateOldest) - } catch (RetrofitError re) { + } catch (SpinnakerServerException e) { + RetrofitError re = e.getRetrofitError(); + if (re == null) { + throw e; + } + if (re.kind == RetrofitError.Kind.HTTP && re.response?.status == 404) { throw new ServerGroupNotFound("unable to find $target in $cloudProviderType/$account/$scope/$clusterName") } - throw re + throw e } } diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CronService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CronService.groovy index 927fbdae0c..1803a89c25 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CronService.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/CronService.groovy @@ -17,11 +17,12 @@ package com.netflix.spinnaker.gate.services import com.netflix.spinnaker.gate.services.internal.EchoService +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException +import com.netflix.spinnaker.kork.retrofit.exceptions.UpstreamBadRequest import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component -import retrofit.RetrofitError @Component @CompileStatic @@ -38,10 +39,15 @@ class CronService { try { Map validationResult = echoService.validateCronExpression(cronExpression) return [ valid: true, description: validationResult.description ] - } catch (RetrofitError e) { - if (e.response?.status == 400) { - Map responseBody = e.getBodyAs(Map) as Map - return [ valid: false, message: responseBody.message ] + } catch (SpinnakerServerException e) { + Throwable cause = e.getCause() + if (!(cause instanceof UpstreamBadRequest)) { + throw e + } + + UpstreamBadRequest upstreamBadRequest = (UpstreamBadRequest) cause + if (upstreamBadRequest.status == 400 && upstreamBadRequest.error instanceof Map) { + return [ valid: false, message: ((Map)upstreamBadRequest.error).message ] } throw e } diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/JobService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/JobService.groovy index 496dd23655..c469598fb9 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/JobService.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/JobService.groovy @@ -19,10 +19,10 @@ package com.netflix.spinnaker.gate.services import com.netflix.spinnaker.gate.config.InsightConfiguration import com.netflix.spinnaker.gate.services.internal.ClouddriverServiceSelector import com.netflix.spinnaker.gate.services.internal.OrcaServiceSelector +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import groovy.transform.CompileStatic import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component -import retrofit.RetrofitError @CompileStatic @Component @@ -51,8 +51,8 @@ class JobService { return clouddriverServiceSelector.select().getJobDetails(applicationName, account, region, name, "") + [ "insightActions": insightConfiguration.job.collect { it.applyContext(context) } ] - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { return [:] } throw e diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/KayentaCanaryConfigService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/KayentaCanaryConfigService.groovy index 8aa3f375a0..a805bd959c 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/KayentaCanaryConfigService.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/KayentaCanaryConfigService.groovy @@ -17,14 +17,14 @@ package com.netflix.spinnaker.gate.services import com.netflix.spinnaker.gate.services.internal.KayentaService +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import groovy.transform.CompileStatic import groovy.util.logging.Slf4j import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression import org.springframework.stereotype.Component -import retrofit.RetrofitError -import static com.netflix.spinnaker.gate.retrofit.UpstreamBadRequest.classifyError +import static com.netflix.spinnaker.kork.retrofit.exceptions.UpstreamBadRequest.classifyError @CompileStatic @Component @@ -38,7 +38,7 @@ class KayentaCanaryConfigService implements CanaryConfigService { List getCanaryConfigs(String application, String configurationAccountName) { try { return kayentaService.getCanaryConfigs(application, configurationAccountName) - } catch (RetrofitError e) { + } catch (SpinnakerServerException e) { throw classifyError(e) } } @@ -46,7 +46,7 @@ class KayentaCanaryConfigService implements CanaryConfigService { Map getCanaryConfig(String id, String configurationAccountName) { try { return kayentaService.getCanaryConfig(id, configurationAccountName) - } catch (RetrofitError e) { + } catch (SpinnakerServerException e) { throw classifyError(e) } } @@ -54,7 +54,7 @@ class KayentaCanaryConfigService implements CanaryConfigService { Map createCanaryConfig(Map config, String configurationAccountName) { try { return kayentaService.createCanaryConfig(config, configurationAccountName) - } catch (RetrofitError e) { + } catch (SpinnakerServerException e) { throw classifyError(e) } } @@ -62,7 +62,7 @@ class KayentaCanaryConfigService implements CanaryConfigService { Map updateCanaryConfig(String id, Map config, String configurationAccountName) { try { return kayentaService.updateCanaryConfig(id, config, configurationAccountName) - } catch (RetrofitError e) { + } catch (SpinnakerServerException e) { throw classifyError(e) } } @@ -70,7 +70,7 @@ class KayentaCanaryConfigService implements CanaryConfigService { void deleteCanaryConfig(String id, String configurationAccountName) { try { kayentaService.deleteCanaryConfig(id, configurationAccountName) - } catch (RetrofitError e) { + } catch (SpinnakerServerException e) { throw classifyError(e) } } diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/LoadBalancerService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/LoadBalancerService.groovy index 95a3492f70..eadfbe87a3 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/LoadBalancerService.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/LoadBalancerService.groovy @@ -19,10 +19,10 @@ package com.netflix.spinnaker.gate.services import com.fasterxml.jackson.databind.ObjectMapper import com.netflix.spinnaker.gate.config.InsightConfiguration import com.netflix.spinnaker.gate.services.internal.ClouddriverServiceSelector +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import groovy.transform.CompileStatic import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component -import retrofit.RetrofitError @CompileStatic @Component @@ -64,8 +64,8 @@ class LoadBalancerService { return foo } return loadBalancerDetails - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { return [] } throw e diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ManifestService.java b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ManifestService.java index 60f6998972..74eb0386da 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ManifestService.java +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ManifestService.java @@ -18,6 +18,7 @@ import com.netflix.spinnaker.gate.services.internal.ClouddriverService; import com.netflix.spinnaker.kork.exceptions.SpinnakerException; +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; @@ -38,13 +39,15 @@ public ManifestService(ClouddriverService clouddriverService) { public Map getManifest(String account, String location, String name) { try { return clouddriverService.getManifest(account, location, name); - } catch (RetrofitError re) { - if (re.getKind() == RetrofitError.Kind.HTTP + } catch (SpinnakerServerException e) { + RetrofitError re = e.getRetrofitError(); + if (re != null + && re.getKind() == RetrofitError.Kind.HTTP && re.getResponse() != null && re.getResponse().getStatus() == 404) { throw new ManifestNotFound("Unable to find " + name + " in " + account + "/" + location); } - throw re; + throw e; } } diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/NotificationService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/NotificationService.groovy index 3c9f481adb..e2fd1ea60f 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/NotificationService.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/NotificationService.groovy @@ -22,6 +22,7 @@ import com.netflix.spinnaker.config.okhttp3.OkHttpClientProvider import com.netflix.spinnaker.gate.config.ServiceConfiguration import com.netflix.spinnaker.gate.services.internal.EchoService import com.netflix.spinnaker.gate.services.internal.Front50Service +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import com.netflix.spinnaker.kork.web.exceptions.InvalidRequestException import okhttp3.MediaType import okhttp3.OkHttpClient @@ -136,9 +137,10 @@ class NotificationService { HttpHeaders headers = new HttpHeaders() headers.putAll(response.headers().toMultimap()) return new ResponseEntity(body, headers, HttpStatus.valueOf(response.code())) - } catch (RetrofitError error) { + } catch (SpinnakerServerException error) { + RetrofitError re = error.getRetrofitError(); log.error("Error proxying notification callback to {}: $error", service) - if (error.getKind() == HTTP) { + if (re != null && re.getKind() == HTTP) { throw error } else { return new ResponseEntity(INTERNAL_SERVER_ERROR) diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ServerGroupService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ServerGroupService.groovy index 70f3a4d1c7..c6545b2c51 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ServerGroupService.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/ServerGroupService.groovy @@ -21,10 +21,10 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.netflix.frigga.Names import com.netflix.spinnaker.gate.config.InsightConfiguration import com.netflix.spinnaker.gate.services.internal.ClouddriverServiceSelector +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import groovy.transform.CompileStatic import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component -import retrofit.RetrofitError @CompileStatic @Component @@ -68,8 +68,8 @@ class ServerGroupService { return serverGroupDetails + [ "insightActions": insightConfiguration.serverGroup.findResults { it.applyContext(context) } ] - } catch (RetrofitError e) { - if (e.response?.status == 404) { + } catch (SpinnakerServerException e) { + if (e.retrofitError?.response?.status == 404) { return [:] } throw e diff --git a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/V2CanaryService.groovy b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/V2CanaryService.groovy index bd897d27b9..6297aaf9ea 100644 --- a/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/V2CanaryService.groovy +++ b/gate-web/src/main/groovy/com/netflix/spinnaker/gate/services/V2CanaryService.groovy @@ -16,14 +16,15 @@ package com.netflix.spinnaker.gate.services -import static com.netflix.spinnaker.gate.retrofit.UpstreamBadRequest.classifyError import com.netflix.spinnaker.gate.services.internal.KayentaService +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException +import com.netflix.spinnaker.kork.retrofit.exceptions.UpstreamBadRequest + import groovy.transform.CompileStatic import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression import org.springframework.stereotype.Component -import retrofit.RetrofitError @Component @CompileStatic @@ -36,24 +37,24 @@ class V2CanaryService { List getCredentials() { try { return kayentaService.getCredentials() - } catch (RetrofitError error) { - throw classifyError(error) + } catch (SpinnakerServerException error) { + throw UpstreamBadRequest.classifyError(error) } } List listMetricsServiceMetadata(String filter, String metricsAccountName) { try { return kayentaService.listMetricsServiceMetadata(filter, metricsAccountName) - } catch (RetrofitError error) { - throw classifyError(error) + } catch (SpinnakerServerException error) { + throw UpstreamBadRequest.classifyError(error) } } List listJudges() { try { return kayentaService.listJudges() - } catch (RetrofitError error) { - throw classifyError(error) + } catch (SpinnakerServerException error) { + throw UpstreamBadRequest.classifyError(error) } } @@ -68,8 +69,8 @@ class V2CanaryService { parentPipelineExecutionId, metricsAccountName, storageAccountName) - } catch (RetrofitError error) { - throw classifyError(error) + } catch (SpinnakerServerException error) { + throw UpstreamBadRequest.classifyError(error) } } @@ -88,32 +89,32 @@ class V2CanaryService { metricsAccountName, storageAccountName, configurationAccountName) - } catch (RetrofitError error) { - throw classifyError(error) + } catch (SpinnakerServerException error) { + throw UpstreamBadRequest.classifyError(error) } } Map getCanaryResults(String canaryExecutionId, String storageAccountName) { try { return kayentaService.getCanaryResult(canaryExecutionId, storageAccountName) - } catch (RetrofitError error) { - throw classifyError(error) + } catch (SpinnakerServerException error) { + throw UpstreamBadRequest.classifyError(error) } } List getCanaryResultsByApplication(String application, int limit, int page, String statuses, String storageAccountName) { try { return kayentaService.getCanaryResultsByApplication(application, limit, page, statuses, storageAccountName) - } catch (RetrofitError error) { - throw classifyError(error) + } catch (SpinnakerServerException error) { + throw UpstreamBadRequest.classifyError(error) } } List getMetricSetPairList(String metricSetPairListId, String storageAccountName) { try { return kayentaService.getMetricSetPairList(metricSetPairListId, storageAccountName) - } catch (RetrofitError error) { - throw classifyError(error) + } catch (SpinnakerServerException error) { + throw UpstreamBadRequest.classifyError(error) } } } diff --git a/gate-web/src/test/groovy/com/netflix/spinnaker/gate/controllers/GoogleCloudBuildControllerSpec.groovy b/gate-web/src/test/groovy/com/netflix/spinnaker/gate/controllers/GoogleCloudBuildControllerSpec.groovy index 84ca8ddcec..25aaa8b30c 100644 --- a/gate-web/src/test/groovy/com/netflix/spinnaker/gate/controllers/GoogleCloudBuildControllerSpec.groovy +++ b/gate-web/src/test/groovy/com/netflix/spinnaker/gate/controllers/GoogleCloudBuildControllerSpec.groovy @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper import com.netflix.spinnaker.gate.services.BuildService import com.netflix.spinnaker.gate.services.internal.GoogleCloudBuildTrigger import com.netflix.spinnaker.gate.services.internal.IgorService +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import com.squareup.okhttp.mockwebserver.MockWebServer import org.springframework.http.MediaType import org.springframework.mock.web.MockHttpServletResponse @@ -89,7 +90,9 @@ class GoogleCloudBuildControllerSpec extends Specification { def downstreamResponse = new Response("blah", 404, "Not found", [], null) given: 1 * igorService.getGoogleCloudBuildTriggers(ACCOUNT) >> {String account -> - throw RetrofitError.httpError("blah", downstreamResponse, null, null) + // retrofit errors are no longer thrown since the introduction of the + // exception handler for http clients + throw new SpinnakerServerException(RetrofitError.httpError("blah", downstreamResponse, null, null)) } when: diff --git a/gate-web/src/test/groovy/com/netflix/spinnaker/gate/health/DownstreamServicesHealthIndicatorSpec.groovy b/gate-web/src/test/groovy/com/netflix/spinnaker/gate/health/DownstreamServicesHealthIndicatorSpec.groovy index 5e225ffdd7..1d4620eb8d 100644 --- a/gate-web/src/test/groovy/com/netflix/spinnaker/gate/health/DownstreamServicesHealthIndicatorSpec.groovy +++ b/gate-web/src/test/groovy/com/netflix/spinnaker/gate/health/DownstreamServicesHealthIndicatorSpec.groovy @@ -18,6 +18,7 @@ package com.netflix.spinnaker.gate.health import com.netflix.spinnaker.gate.services.internal.HealthCheckableService +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import org.springframework.boot.actuate.health.Health import org.springframework.web.context.request.RequestContextHolder import org.springframework.web.context.request.ServletWebRequest @@ -58,7 +59,7 @@ class DownstreamServicesHealthIndicatorSpec extends Specification { then: 1 * healthCheckableService.health() >> { - throw new RetrofitError("Unable to connect", "http://localhost", null, null, null, null, null) + throw new SpinnakerServerException(new RetrofitError("Unable to connect", "http://localhost", null, null, null, null, null)) } healthIndicator.failuresByService.get() == [ diff --git a/gate-web/src/test/groovy/com/netflix/spinnaker/gate/services/CronServiceSpec.groovy b/gate-web/src/test/groovy/com/netflix/spinnaker/gate/services/CronServiceSpec.groovy index c54ea72455..676d9e76e9 100644 --- a/gate-web/src/test/groovy/com/netflix/spinnaker/gate/services/CronServiceSpec.groovy +++ b/gate-web/src/test/groovy/com/netflix/spinnaker/gate/services/CronServiceSpec.groovy @@ -19,6 +19,7 @@ package com.netflix.spinnaker.gate.services import com.fasterxml.jackson.databind.ObjectMapper import com.google.gson.Gson import com.netflix.spinnaker.gate.services.internal.EchoService +import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerServerException import retrofit.RetrofitError import retrofit.client.Response import retrofit.converter.GsonConverter @@ -33,7 +34,7 @@ class CronServiceSpec extends Specification { void "should return validation message when scheduler service fails with 400 status"() { given: def body = new TypedByteArray(null, OBJECT_MAPPER.writeValueAsBytes([message: "Invalid Cron expression!!!"])) - def error = RetrofitError.httpError("", new Response("", 400, "", [], body), new GsonConverter(new Gson()), Map) + def error = new SpinnakerServerException(RetrofitError.httpError("", new Response("", 400, "", [], body), new GsonConverter(new Gson()), Map)) def service = new CronService( echoService: Mock(EchoService) { 1 * validateCronExpression("blah") >> { throw error } @@ -48,7 +49,7 @@ class CronServiceSpec extends Specification { void "should propagate Retrofit error when status code is #code"() { given: def body = new TypedByteArray(null, OBJECT_MAPPER.writeValueAsBytes([message: "Invalid Cron expression!!!"])) - def error = RetrofitError.httpError("", new Response("", code, "", [], body), new GsonConverter(new Gson()), Map) + def error = new SpinnakerServerException(RetrofitError.httpError("", new Response("", code, "", [], body), new GsonConverter(new Gson()), Map)) def service = new CronService( echoService: Mock(EchoService) { 1 * validateCronExpression("blah") >> { throw error } @@ -59,7 +60,7 @@ class CronServiceSpec extends Specification { service.validateCronExpression("blah") then: - thrown RetrofitError + thrown SpinnakerServerException where: code << [401, 402, 403, 404, 500]