Skip to content

Commit

Permalink
refactor(retrofit): use SpinnakerRetrofitErrorHandler as a step towar…
Browse files Browse the repository at this point in the history
…ds simplifying exception handling (#4529)

- add retry support to createBake() method in CreateBakeTask
- add retry support to bakeManifest() method in CreateBakeManifestTask
  • Loading branch information
kirangodishala authored Sep 19, 2023
1 parent 6dfe7dd commit 6ae6207
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 26 deletions.
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

0 comments on commit 6ae6207

Please sign in to comment.