@@ -9,6 +9,7 @@ import akka.http.scaladsl.model.headers.OAuth2BearerToken
99import akka .testkit .TestKit
1010import com .google .api .client .googleapis .testing .json .GoogleJsonResponseExceptionFactoryTesting
1111import com .google .api .client .testing .json .MockJsonFactory
12+ import com .typesafe .scalalogging .LazyLogging
1213import org .broadinstitute .dsde .workbench .google .GoogleStorageDAO
1314import org .broadinstitute .dsde .workbench .google .mock .{MockGoogleDataprocDAO , MockGoogleIamDAO , MockGoogleStorageDAO }
1415import org .broadinstitute .dsde .workbench .leonardo .CommonTestData
@@ -25,19 +26,21 @@ import org.broadinstitute.dsde.workbench.leonardo.monitor.NoopActor
2526import org .broadinstitute .dsde .workbench .leonardo .util .BucketHelper
2627import org .broadinstitute .dsde .workbench .model ._
2728import org .broadinstitute .dsde .workbench .model .google ._
29+ import org .broadinstitute .dsde .workbench .util .Retry
2830import org .mockito .ArgumentMatchers .{any , eq => mockitoEq }
2931import org .mockito .Mockito .{never , verify , _ }
3032import org .scalatest ._
3133import org .scalatest .concurrent .Eventually .eventually
3234import org .scalatest .concurrent .ScalaFutures
35+ import org .scalatest .time .{Minutes , Span }
3336import spray .json ._
3437
3538import scala .concurrent .duration ._
3639import scala .concurrent .{Await , ExecutionContext , Future }
3740
3841class LeonardoServiceSpec extends TestKit (ActorSystem (" leonardotest" )) with FlatSpecLike with Matchers
3942 with BeforeAndAfter with BeforeAndAfterAll with TestComponent with ScalaFutures
40- with OptionValues with CommonTestData with LeoComponent {
43+ with OptionValues with CommonTestData with LeoComponent with Retry with LazyLogging {
4144
4245 private var gdDAO : MockGoogleDataprocDAO = _
4346 private var computeDAO : MockGoogleComputeDAO = _
@@ -975,8 +978,9 @@ class LeonardoServiceSpec extends TestKit(ActorSystem("leonardotest")) with Flat
975978 new MockGoogleStorageDAO
976979 }
977980
978- // we meed to use a special version of the MockGoogleDataprocDAO to simulate an error during the call to resizeCluster
979- leo = new LeonardoService (dataprocConfig, clusterFilesConfig, clusterResourcesConfig, clusterDefaultsConfig, proxyConfig, swaggerConfig, autoFreezeConfig, mockGoogleDataprocDAO, computeDAO, new ErroredMockGoogleIamDAO , storageDAO, mockPetGoogleDAO, DbSingleton .ref, authProvider, serviceAccountProvider, whitelist, bucketHelper, contentSecurityPolicy)
981+ // we meed to use a special version of the MockGoogleIamDAO to simulate an error when adding IAM roles
982+ val iamDAO = new ErroredMockGoogleIamDAO
983+ leo = new LeonardoService (dataprocConfig, clusterFilesConfig, clusterResourcesConfig, clusterDefaultsConfig, proxyConfig, swaggerConfig, autoFreezeConfig, mockGoogleDataprocDAO, computeDAO, iamDAO, storageDAO, mockPetGoogleDAO, DbSingleton .ref, authProvider, serviceAccountProvider, whitelist, bucketHelper, contentSecurityPolicy)
980984
981985 // create the cluster
982986 val clusterCreateResponse =
@@ -985,6 +989,30 @@ class LeonardoServiceSpec extends TestKit(ActorSystem("leonardotest")) with Flat
985989 eventually {
986990 dbFutureValue { _.clusterQuery.getClusterStatus(clusterCreateResponse.id) } shouldBe Some (ClusterStatus .Error )
987991 }
992+
993+ // IAM call should not have been retried
994+ iamDAO.invocationCount shouldBe 1
995+ }
996+
997+ it should " retry 409 errors when adding dataproc worker role" in isolatedDbTest {
998+ val mockPetGoogleDAO : String => GoogleStorageDAO = _ => {
999+ new MockGoogleStorageDAO
1000+ }
1001+
1002+ // we meed to use a special version of the MockGoogleIamDAO to simulate a conflict when adding IAM roles
1003+ val iamDAO = new ErroredMockGoogleIamDAO (409 )
1004+ leo = new LeonardoService (dataprocConfig, clusterFilesConfig, clusterResourcesConfig, clusterDefaultsConfig, proxyConfig, swaggerConfig, autoFreezeConfig, mockGoogleDataprocDAO, computeDAO, iamDAO, storageDAO, mockPetGoogleDAO, DbSingleton .ref, authProvider, serviceAccountProvider, whitelist, bucketHelper, contentSecurityPolicy)
1005+
1006+ // create the cluster
1007+ val clusterCreateResponse =
1008+ leo.processClusterCreationRequest(userInfo, project, name1, testClusterRequest).futureValue
1009+
1010+ eventually(timeout(Span (5 , Minutes ))) {
1011+ dbFutureValue { _.clusterQuery.getClusterStatus(clusterCreateResponse.id) } shouldBe Some (ClusterStatus .Error )
1012+ }
1013+
1014+ // IAM call should have been retried exponentially
1015+ iamDAO.invocationCount shouldBe exponentialBackOffIntervals.size + 1
9881016 }
9891017
9901018 it should " update the autopause threshold for a cluster" in isolatedDbTest {
@@ -1158,10 +1186,12 @@ class LeonardoServiceSpec extends TestKit(ActorSystem("leonardotest")) with Flat
11581186 }
11591187 }
11601188
1161- private class ErroredMockGoogleIamDAO extends MockGoogleIamDAO {
1189+ private class ErroredMockGoogleIamDAO (statusCode : Int = 400 ) extends MockGoogleIamDAO {
1190+ var invocationCount = 0
11621191 override def addIamRolesForUser (iamProject : GoogleProject , email : WorkbenchEmail , rolesToAdd : Set [String ]): Future [Unit ] = {
1192+ invocationCount += 1
11631193 val jsonFactory = new MockJsonFactory
1164- val testException = GoogleJsonResponseExceptionFactoryTesting .newMock(jsonFactory, 400 , " oh no i have failed" )
1194+ val testException = GoogleJsonResponseExceptionFactoryTesting .newMock(jsonFactory, statusCode , " oh no i have failed" )
11651195
11661196 Future .failed(testException)
11671197 }
0 commit comments