Skip to content

Commit

Permalink
Fix socket timeout exception (#337)
Browse files Browse the repository at this point in the history
* Don't crash on null test executions

* Use 60s timeout on requests by default

* Add executeWithRetry

* Add ftl.http package

* Use TimeoutHttpRequestInitializer in mock code path
  • Loading branch information
bootstraponline authored Oct 9, 2018
1 parent cba181e commit 86a530f
Show file tree
Hide file tree
Showing 8 changed files with 61 additions and 17 deletions.
3 changes: 2 additions & 1 deletion test_runner/src/main/kotlin/ftl/android/AndroidCatalog.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ftl.android

import com.google.api.services.testing.model.AndroidDevice
import ftl.http.executeWithRetry
import ftl.gc.GcTesting

/**
Expand All @@ -9,7 +10,7 @@ import ftl.gc.GcTesting
*/
object AndroidCatalog {
private val androidDeviceCatalog by lazy {
GcTesting.get.testEnvironmentCatalog().get("android").execute().androidDeviceCatalog
GcTesting.get.testEnvironmentCatalog().get("android").executeWithRetry().androidDeviceCatalog
}

val androidModelIds by lazy { androidDeviceCatalog.models.map { it.id } }
Expand Down
28 changes: 16 additions & 12 deletions test_runner/src/main/kotlin/ftl/config/FtlConstants.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package ftl.config

import com.google.api.client.auth.oauth2.Credential
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport
import com.google.api.client.googleapis.testing.auth.oauth2.MockGoogleCredential
import com.google.api.client.googleapis.util.Utils
import com.google.api.client.http.HttpRequestInitializer
import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.client.json.JsonFactory
import ftl.http.TimeoutHttpRequestInitializer

object FtlConstants {
var useMock = false
Expand All @@ -18,7 +20,7 @@ object FtlConstants {
const val matrixIdsFile = "matrix_ids.json"
const val applicationName = "Flank"
const val GCS_PREFIX = "gs://"
val JSON_FACTORY: JacksonFactory by lazy { JacksonFactory.getDefaultInstance() }
val JSON_FACTORY: JsonFactory by lazy { Utils.getDefaultJsonFactory() }

val httpTransport: NetHttpTransport by lazy {
try {
Expand All @@ -28,20 +30,22 @@ object FtlConstants {
}
}

val credential: Credential by lazy {
val credential: HttpRequestInitializer by lazy {
try {
if (useMock) {
return@lazy MockGoogleCredential.Builder()
.setTransport(MockGoogleCredential.newMockHttpTransportWithSampleTokenResponse())
.build()
}

val defaultCredential = GoogleCredential.getApplicationDefault()
// Scope is required.
// https://developers.google.com/identity/protocols/googlescopes
// https://developers.google.com/identity/protocols/application-default-credentials
// https://cloud.google.com/sdk/gcloud/reference/alpha/compute/instances/set-scopes
return@lazy defaultCredential.createScoped(listOf("https://www.googleapis.com/auth/cloud-platform"))
val credential = if (useMock) {
MockGoogleCredential.Builder()
.setTransport(MockGoogleCredential.newMockHttpTransportWithSampleTokenResponse())
.build()
} else {
GoogleCredential.getApplicationDefault()
.createScoped(listOf("https://www.googleapis.com/auth/cloud-platform"))
}

return@lazy TimeoutHttpRequestInitializer(credential)
} catch (e: Exception) {
throw RuntimeException(e)
}
Expand Down
3 changes: 2 additions & 1 deletion test_runner/src/main/kotlin/ftl/gc/GcTestMatrix.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ftl.gc

import com.google.api.services.testing.model.TestMatrix
import ftl.args.IArgs
import ftl.http.executeWithRetry
import ftl.util.Utils.sleep
import java.time.Duration.ofHours

Expand Down Expand Up @@ -38,7 +39,7 @@ object GcTestMatrix {

while (failed < maxWait) {
try {
return getMatrix.execute()
return getMatrix.executeWithRetry()
} catch (e: Exception) {
sleep(1)
failed += 1
Expand Down
5 changes: 3 additions & 2 deletions test_runner/src/main/kotlin/ftl/gc/GcToolResults.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ftl.config.FtlConstants.JSON_FACTORY
import ftl.config.FtlConstants.applicationName
import ftl.config.FtlConstants.credential
import ftl.config.FtlConstants.httpTransport
import ftl.http.executeWithRetry

object GcToolResults {

Expand All @@ -29,7 +30,7 @@ object GcToolResults {
.histories()
.list(args.projectId)
.setFilterByName(args.resultsHistoryName)
.execute()
.executeWithRetry()
return result?.histories ?: emptyList()
}

Expand Down Expand Up @@ -64,7 +65,7 @@ object GcToolResults {
toolResultsStep.executionId,
toolResultsStep.stepId
)
.execute()
.executeWithRetry()
}

fun getDefaultBucket(projectId: String): String? {
Expand Down
21 changes: 21 additions & 0 deletions test_runner/src/main/kotlin/ftl/http/ExecuteWithRetry.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ftl.http

import com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest
import java.io.IOException

// Only use on calls that don't change state.
// Fetching status is safe to retry on timeout. Creating a matrix is not.
fun <T> AbstractGoogleJsonClientRequest<T>.executeWithRetry(): T {
var lastErr: IOException? = null

for (i in 1..10) {
try {
return this.execute()
} catch (err: IOException) {
lastErr = err
System.err.println("Request failed, retrying ${i}x $err")
}
}

throw IOException("Request failed", lastErr)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ftl.http

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential
import com.google.api.client.http.HttpRequest
import com.google.api.client.http.HttpRequestInitializer

class TimeoutHttpRequestInitializer(private val googleCredential: GoogleCredential) : HttpRequestInitializer {
override fun initialize(request: HttpRequest?) {
googleCredential.initialize(request)
// timeout in milliseconds. wait 60s instead of default 20s
request?.connectTimeout = 60 * 1000
request?.readTimeout = 60 * 1000
}
}
3 changes: 2 additions & 1 deletion test_runner/src/main/kotlin/ftl/ios/IosCatalog.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ftl.ios

import ftl.http.executeWithRetry
import ftl.gc.GcTesting

/**
Expand All @@ -14,7 +15,7 @@ object IosCatalog {

private val iosDeviceCatalog by lazy {
try {
GcTesting.get.testEnvironmentCatalog().get("ios").execute().iosDeviceCatalog
GcTesting.get.testEnvironmentCatalog().get("ios").executeWithRetry().iosDeviceCatalog
} catch (e: java.lang.Exception) {
throw java.lang.RuntimeException(
"""
Expand Down
1 change: 1 addition & 0 deletions test_runner/src/main/kotlin/ftl/json/SavedMatrix.kt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class SavedMatrix(matrix: TestMatrix) {
billableVirtualMinutes = 0
billablePhysicalMinutes = 0
outcome = success
if (matrix.testExecutions == null) return
matrix.testExecutions.forEach {
val step = GcToolResults.getResults(it.toolResultsStep)
if (step.testExecutionStep == null) return
Expand Down

0 comments on commit 86a530f

Please sign in to comment.