Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(retrofit): use SpinnakerRetrofitErrorHandler as a step towards simplifying exception handling #4529

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions orca-bakery/orca-bakery.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies {
implementation("com.fasterxml.jackson.datatype:jackson-datatype-guava")
implementation("io.spinnaker.fiat:fiat-core:$fiatVersion")
implementation("org.codehaus.groovy:groovy-datetime")
implementation("io.spinnaker.kork:kork-retrofit")

api("io.spinnaker.kork:kork-web")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.netflix.spinnaker.orca.bakery.config

import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerRetrofitErrorHandler
import com.netflix.spinnaker.orca.bakery.BakerySelector
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression
import org.springframework.boot.context.properties.EnableConfigurationProperties
Expand Down Expand Up @@ -80,6 +81,7 @@ class BakeryConfiguration {
.setClient(retrofitClient)
.setLogLevel(retrofitLogLevel)
.setLog(new RetrofitSlf4jLog(BakeryService))
.setErrorHandler(SpinnakerRetrofitErrorHandler.getInstance())
.build()
.create(BakeryService)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package com.netflix.spinnaker.orca.bakery.tasks
import com.fasterxml.jackson.databind.ObjectMapper
import com.netflix.spinnaker.kork.artifacts.model.Artifact
import com.netflix.spinnaker.kork.core.RetrySupport
import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerHttpException
import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus
import com.netflix.spinnaker.orca.api.pipeline.RetryableTask
import com.netflix.spinnaker.orca.api.pipeline.models.StageExecution
Expand All @@ -35,12 +36,14 @@ import com.netflix.spinnaker.orca.pipeline.util.PackageInfo
import com.netflix.spinnaker.orca.pipeline.util.PackageType
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import java.time.Duration
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import retrofit.RetrofitError


import static com.netflix.spinnaker.kork.web.selector.v2.SelectableService.*

@Component
Expand All @@ -61,7 +64,7 @@ class CreateBakeTask implements RetryableTask {
@Autowired(required = false)
Front50Service front50Service

RetrySupport retrySupport = new RetrySupport()
private final RetrySupport retrySupport = new RetrySupport()

private final Logger log = LoggerFactory.getLogger(getClass())

Expand All @@ -76,7 +79,8 @@ class CreateBakeTask implements RetryableTask {
try {
if (front50Service != null) {
String appName = stage.execution.application
Application application = retrySupport.retry({return front50Service.get(appName)}, 5, 2000, false)
Application application =
retrySupport.retry({ return front50Service.get(appName) }, 5, Duration.ofMillis(2000), false)
String user = application.email
if (user != null && user != "") {
stage.context.user = user
Expand All @@ -100,12 +104,16 @@ class CreateBakeTask implements RetryableTask {
bake.amiSuffix = stage.context.amiSuffix
}

def bakeStatus = bakery.service.createBake(stage.context.region as String, bake, rebake)
def bakeStatus = retrySupport.retry(
{ return bakery.service.createBake(stage.context.region as String, bake, rebake) },
5,
Duration.ofMillis(2000),
false)

def stageOutputs = [
status : bakeStatus,
bakePackageName: bake.packageName ?: "",
previouslyBaked: bakeStatus.state == BakeStatus.State.COMPLETED
status : bakeStatus,
bakePackageName: bake.packageName ?: "",
previouslyBaked: bakeStatus.state == BakeStatus.State.COMPLETED
] as Map<String, ? extends Object>

if (bake.buildInfoUrl) {
Expand All @@ -114,9 +122,9 @@ class CreateBakeTask implements RetryableTask {

if (bake.buildHost) {
stageOutputs << [
buildHost : bake.buildHost,
job : bake.job,
buildNumber: bake.buildNumber
buildHost : bake.buildHost,
job : bake.job,
buildNumber: bake.buildNumber
]

if (bake.commitHash) {
Expand All @@ -125,19 +133,13 @@ class CreateBakeTask implements RetryableTask {
}

TaskResult.builder(ExecutionStatus.SUCCEEDED).context(stageOutputs).build()
} catch (RetrofitError e) {
if (e.response?.status && e.response.status == 404) {
try {
def exceptionResult = mapper.readValue(e.response.body.in().text, Map)
def exceptionMessages = (exceptionResult.messages ?: []) as List<String>
if (exceptionMessages) {
throw new IllegalStateException(exceptionMessages[0])
}
} catch (IOException ignored) {
// do nothing
} catch (SpinnakerHttpException e) {
if (e.responseCode == 404) {
def exceptionMessages =
(e.responseBody?.messages ?: e.responseBody?.message ? [e.responseBody.message] : []) as List<String>
if (exceptionMessages) {
throw new IllegalStateException(exceptionMessages[0])
}

return TaskResult.ofStatus(ExecutionStatus.RUNNING)
}
throw e
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import com.netflix.spinnaker.kork.artifacts.model.Artifact;
import com.netflix.spinnaker.kork.artifacts.model.ExpectedArtifact;
import com.netflix.spinnaker.kork.core.RetrySupport;
import com.netflix.spinnaker.orca.api.pipeline.RetryableTask;
import com.netflix.spinnaker.orca.api.pipeline.TaskResult;
import com.netflix.spinnaker.orca.api.pipeline.models.ExecutionStatus;
Expand All @@ -30,6 +31,7 @@
import com.netflix.spinnaker.orca.bakery.api.manifests.kustomize.KustomizeBakeManifestRequest;
import com.netflix.spinnaker.orca.pipeline.util.ArtifactUtils;
import com.netflix.spinnaker.orca.pipeline.util.ContextParameterProcessor;
import java.time.Duration;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
Expand Down Expand Up @@ -62,6 +64,8 @@ public long getTimeout() {

private final ContextParameterProcessor contextParameterProcessor;

private final RetrySupport retrySupport = new RetrySupport();

@Autowired
public CreateBakeManifestTask(
ArtifactUtils artifactUtils,
Expand Down Expand Up @@ -146,7 +150,12 @@ public TaskResult execute(@Nonnull StageExecution stage) {
"Invalid template renderer " + context.getTemplateRenderer());
}

Artifact result = bakery.bakeManifest(request.getTemplateRenderer(), request);
Artifact result =
retrySupport.retry(
() -> bakery.bakeManifest(request.getTemplateRenderer(), request),
5,
Duration.ofMillis(2000),
false);

Map<String, Object> outputs = new HashMap<>();
outputs.put("artifacts", Collections.singleton(result));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.netflix.spinnaker.orca.bakery.api

import com.github.tomakehurst.wiremock.WireMockServer
import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerHttpException
import com.netflix.spinnaker.orca.bakery.config.BakeryConfiguration
import com.netflix.spinnaker.orca.jackson.OrcaObjectMapper
import retrofit.RequestInterceptor
Expand Down Expand Up @@ -105,14 +106,15 @@ class BakeryServiceSpec extends Specification {
.willReturn(
aResponse()
.withStatus(HTTP_NOT_FOUND)
.withBody("{\"message\": \"error\"}")
)
)

when:
bakery.lookupStatus(region, statusId)

then:
def ex = thrown(RetrofitError)
def ex = thrown(SpinnakerHttpException)
ex.response.status == HTTP_NOT_FOUND
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.netflix.spinnaker.orca.bakery.tasks

import com.netflix.spinnaker.kork.artifacts.model.Artifact
import com.netflix.spinnaker.kork.retrofit.exceptions.SpinnakerHttpException
import com.netflix.spinnaker.kork.web.selector.v2.SelectableService
import com.netflix.spinnaker.orca.api.pipeline.models.Trigger
import com.netflix.spinnaker.orca.bakery.BakerySelector
Expand All @@ -30,6 +31,7 @@ import com.netflix.spinnaker.orca.pipeline.util.ArtifactUtils
import com.netflix.spinnaker.orca.pipeline.util.PackageType
import retrofit.RetrofitError
import retrofit.client.Response
import retrofit.converter.JacksonConverter
import retrofit.mime.TypedString
import spock.lang.Shared
import spock.lang.Specification
Expand Down Expand Up @@ -245,9 +247,10 @@ class CreateBakeTaskSpec extends Specification {
def error404 = RetrofitError.httpError(
null,
new Response("", HTTP_NOT_FOUND, "Not Found", [], new TypedString("{ \"messages\": [\"Error Message\"]}")),
null,
new JacksonConverter(),
null
)
def httpError404 = new SpinnakerHttpException(error404)

def setup() {
task.mapper = mapper
Expand Down Expand Up @@ -332,8 +335,8 @@ class CreateBakeTaskSpec extends Specification {
task.execute(bakeStage)

then:
1 * bakery.createBake(bakeConfig.region, _ as BakeRequest, null) >> {
throw error404
5 * bakery.createBake(bakeConfig.region, _ as BakeRequest, null) >> {
throw httpError404
}
IllegalStateException e = thrown()
e.message == "Error Message"
Expand Down