From c4e7be074120bedc331c9ee03322734aef01a4f7 Mon Sep 17 00:00:00 2001 From: tlangs Date: Tue, 23 Jan 2024 14:34:45 -0500 Subject: [PATCH 1/7] add action service accounts to project and bucket iam policies --- .../rawls/dataaccess/GoogleServicesDAO.scala | 6 +- .../dataaccess/HttpGoogleServicesDAO.scala | 21 +++- .../dsde/rawls/dataaccess/HttpSamDAO.scala | 18 ++++ .../dsde/rawls/dataaccess/SamDAO.scala | 7 ++ .../rawls/workspace/WorkspaceService.scala | 95 ++++++++++++++----- .../dataaccess/MockGoogleServicesDAO.scala | 6 +- .../dsde/rawls/mock/MockSamDAO.scala | 7 ++ project/Dependencies.scala | 2 +- 8 files changed, 131 insertions(+), 31 deletions(-) diff --git a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/GoogleServicesDAO.scala b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/GoogleServicesDAO.scala index 57fa426363..f2a5272d5d 100644 --- a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/GoogleServicesDAO.scala +++ b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/GoogleServicesDAO.scala @@ -35,7 +35,8 @@ abstract class GoogleServicesDAO(groupsPrefix: String) extends ErrorReportable { def updateBucketIam(bucketName: GcsBucketName, policyGroupsByAccessLevel: Map[WorkspaceAccessLevel, WorkbenchEmail], userProject: Option[GoogleProjectId] = None, - iamPolicyVersion: Int = 1 + iamPolicyVersion: Int = 1, + actionServiceAccountsByAction: Map[SamResourceAction, WorkbenchEmail] = Map.empty ): Future[Unit] // returns bucket and group information @@ -45,7 +46,8 @@ abstract class GoogleServicesDAO(groupsPrefix: String) extends ErrorReportable { bucketName: GcsBucketName, labels: Map[String, String], parentSpan: Span = null, - bucketLocation: Option[String] + bucketLocation: Option[String], + actionServiceAccountsByAction: Map[SamResourceAction, WorkbenchEmail] = Map.empty ): Future[GoogleWorkspaceInfo] def getGoogleProject(googleProject: GoogleProjectId): Future[Project] diff --git a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpGoogleServicesDAO.scala b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpGoogleServicesDAO.scala index bf4c994e58..db66142e93 100644 --- a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpGoogleServicesDAO.scala +++ b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpGoogleServicesDAO.scala @@ -130,7 +130,8 @@ class HttpGoogleServicesDAO(val clientSecrets: GoogleClientSecrets, override def updateBucketIam(bucketName: GcsBucketName, policyGroupsByAccessLevel: Map[WorkspaceAccessLevel, WorkbenchEmail], userProject: Option[GoogleProjectId], - iamPolicyVersion: Int = 1 + iamPolicyVersion: Int = 1, + actionServiceAccountsByAction: Map[SamResourceAction, WorkbenchEmail] = Map.empty ): Future[Unit] = { // default object ACLs are no longer used. bucket only policy is enabled on buckets to ensure that objects // do not have separate permissions that deviate from the bucket-level permissions. @@ -151,9 +152,19 @@ class HttpGoogleServicesDAO(val clientSecrets: GoogleClientSecrets, Read -> customTerraBucketReaderRole ) + val samActionToStorageRole = Map(SamWorkspaceActions.write -> customTerraBucketWriterRole, + SamWorkspaceActions.read -> customTerraBucketReaderRole + ) + val roleToIdentities = policyGroupsByAccessLevel .map { case (access, policyEmail) => Identity.group(policyEmail.value) -> workspaceAccessToStorageRole(access) } .+(Identity.serviceAccount(clientEmail) -> StorageRole.StorageAdmin) + .++( + actionServiceAccountsByAction + .map { case (action, serviceAccountEmail) => + Identity.serviceAccount(serviceAccountEmail.value) -> samActionToStorageRole(action) + } + ) .groupBy(_._2) .view .mapValues(_.keys) @@ -192,7 +203,8 @@ class HttpGoogleServicesDAO(val clientSecrets: GoogleClientSecrets, bucketName: GcsBucketName, labels: Map[String, String], parentSpan: Span = null, - bucketLocation: Option[String] + bucketLocation: Option[String], + actionServiceAccountsByAction: Map[SamResourceAction, WorkbenchEmail] = Map.empty ): Future[GoogleWorkspaceInfo] = { def insertInitialStorageLog: Future[Unit] = { implicit val service = GoogleInstrumentedService.Storage @@ -245,7 +257,10 @@ class HttpGoogleServicesDAO(val clientSecrets: GoogleClientSecrets, } ) // ACL = None because bucket IAM will be set separately in updateBucketIam updateBucketIamFuture = traceWithParent("updateBucketIam", parentSpan)(_ => - updateBucketIam(bucketName, policyGroupsByAccessLevel) + updateBucketIam(bucketName, + policyGroupsByAccessLevel, + actionServiceAccountsByAction = actionServiceAccountsByAction + ) ) insertInitialStorageLogFuture = traceWithParent("insertInitialStorageLog", parentSpan)(_ => insertInitialStorageLog diff --git a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpSamDAO.scala b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpSamDAO.scala index 56730c5c20..ad013cef1a 100644 --- a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpSamDAO.scala +++ b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/HttpSamDAO.scala @@ -335,6 +335,24 @@ class HttpSamDAO(baseSamServiceURL: String, serviceAccountCreds: Credential, tim callback.future.map(_.booleanValue()) } + override def getActionServiceAccount(googleProject: GoogleProjectId, + resourceTypeName: SamResourceTypeName, + resourceId: String, + action: SamResourceAction, + ctx: RawlsRequestContext + ): Future[WorkbenchEmail] = retry(when401or5xx) { () => + val callback = new SamApiCallback[java.lang.String]("getActionServiceAccount") + + googleApi(ctx).getActionServiceAccount(googleProject.value, + resourceTypeName.value, + resourceId, + action.value, + callback + ) + + callback.future.map(WorkbenchEmail) + } + override def getPolicy(resourceTypeName: SamResourceTypeName, resourceId: String, policyName: SamResourcePolicyName, diff --git a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/SamDAO.scala b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/SamDAO.scala index 27ac00aa54..71bc001baa 100644 --- a/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/SamDAO.scala +++ b/core/src/main/scala/org/broadinstitute/dsde/rawls/dataaccess/SamDAO.scala @@ -58,6 +58,13 @@ trait SamDAO { cts: RawlsRequestContext ): Future[Boolean] + def getActionServiceAccount(googleProject: GoogleProjectId, + resourceTypeName: SamResourceTypeName, + resourceId: String, + action: SamResourceAction, + ctx: RawlsRequestContext + ): Future[WorkbenchEmail] + def getPolicy(resourceTypeName: SamResourceTypeName, resourceId: String, policyName: SamResourcePolicyName, diff --git a/core/src/main/scala/org/broadinstitute/dsde/rawls/workspace/WorkspaceService.scala b/core/src/main/scala/org/broadinstitute/dsde/rawls/workspace/WorkspaceService.scala index 35934b2873..879d6c4cc9 100644 --- a/core/src/main/scala/org/broadinstitute/dsde/rawls/workspace/WorkspaceService.scala +++ b/core/src/main/scala/org/broadinstitute/dsde/rawls/workspace/WorkspaceService.scala @@ -3102,6 +3102,7 @@ class WorkspaceService(protected val ctx: RawlsRequestContext, def setupGoogleProjectIam(googleProjectId: GoogleProjectId, policyEmailsByName: Map[SamResourcePolicyName, WorkbenchEmail], billingProjectOwnerPolicyEmail: WorkbenchEmail, + actionServiceAccountsByAction: Map[SamResourceAction, WorkbenchEmail], parentContext: RawlsRequestContext = ctx ): Future[Unit] = traceWithParent("updateGoogleProjectIam", parentContext) { _ => @@ -3119,27 +3120,43 @@ class WorkspaceService(protected val ctx: RawlsRequestContext, // todo: update this line as part of https://broadworkbench.atlassian.net/browse/CA-1220 // This is done sequentially intentionally in order to avoid conflict exceptions as a result of concurrent IAM updates. - List( - billingProjectOwnerPolicyEmail -> Set(terraBillingProjectOwnerRole, - terraWorkspaceCanComputeRole, - terraWorkspaceNextflowRole - ), - policyEmailsByName(SamWorkspacePolicyNames.owner) -> Set(terraWorkspaceCanComputeRole, - terraWorkspaceNextflowRole - ), - policyEmailsByName(SamWorkspacePolicyNames.canCompute) -> Set(terraWorkspaceCanComputeRole, - terraWorkspaceNextflowRole + for { + _ <- List( + billingProjectOwnerPolicyEmail -> Set(terraBillingProjectOwnerRole, + terraWorkspaceCanComputeRole, + terraWorkspaceNextflowRole + ), + policyEmailsByName(SamWorkspacePolicyNames.owner) -> Set(terraWorkspaceCanComputeRole, + terraWorkspaceNextflowRole + ), + policyEmailsByName(SamWorkspacePolicyNames.canCompute) -> Set(terraWorkspaceCanComputeRole, + terraWorkspaceNextflowRole + ) ) - ) - .traverse_ { case (email, roles) => - googleIamDao.addRoles( - GoogleProject(googleProjectId.value), - email, - IamMemberTypes.Group, - roles, - retryIfGroupDoesNotExist = true + .traverse_ { case (email, roles) => + googleIamDao.addRoles( + GoogleProject(googleProjectId.value), + email, + IamMemberTypes.Group, + roles, + retryIfGroupDoesNotExist = true + ) + } + _ <- List( + actionServiceAccountsByAction(SamWorkspaceActions.compute) -> Set(terraWorkspaceCanComputeRole, + terraWorkspaceNextflowRole ) - } + ) + .traverse_ { case (email, roles) => + googleIamDao.addRoles( + GoogleProject(googleProjectId.value), + email, + IamMemberTypes.ServiceAccount, + roles + ) + } + } yield () + } /** @@ -3325,6 +3342,21 @@ class WorkspaceService(protected val ctx: RawlsRequestContext, } ) + private def createActionServiceAccountsInSam(workspaceId: String, + googleProjectId: GoogleProjectId, + actions: Set[SamResourceAction], + parentContext: RawlsRequestContext + ): Future[Map[SamResourceAction, WorkbenchEmail]] = + traceWithParent("createActionServiceAccountsInSam", parentContext)(_ => + Future + .traverse(actions) { action => + samDAO + .getActionServiceAccount(googleProjectId, SamResourceTypeNames.workspace, workspaceId, action, ctx) + .map(action -> _) + } + .map(_.toMap) + ) + private def createWorkspaceInDatabase(workspaceId: String, workspaceRequest: WorkspaceRequest, bucketName: String, @@ -3447,7 +3479,9 @@ class WorkspaceService(protected val ctx: RawlsRequestContext, syncPolicies(workspaceId, policyEmailsByName, workspaceRequest, parentContext) ).sequence_ } - (googleProjectId, googleProjectNumber) <- traceDBIOWithParent("setupGoogleProject", parentContext) { span => + (googleProjectId, googleProjectNumber, actionServiceAccountsByAction) <- traceDBIOWithParent("setupGoogleProject", + parentContext + ) { span => DBIO.from( for { (googleProjectId, googleProjectNumber) <- createGoogleProject(billingProject, @@ -3456,8 +3490,22 @@ class WorkspaceService(protected val ctx: RawlsRequestContext, ) _ <- setProjectBillingAccount(googleProjectId, billingProject, billingAccount, workspaceId, span) _ <- renameAndLabelProject(googleProjectId, workspaceId, workspaceName, span) - _ <- setupGoogleProjectIam(googleProjectId, policyEmailsByName, billingProjectOwnerPolicyEmail, span) - } yield (googleProjectId, googleProjectNumber) + actionsForServiceAccounts = Set(SamWorkspaceActions.read, + SamWorkspaceActions.write, + SamWorkspaceActions.compute + ) + actionServiceAccountsByAction <- createActionServiceAccountsInSam(workspaceId, + googleProjectId, + actionsForServiceAccounts, + parentContext + ) + _ <- setupGoogleProjectIam(googleProjectId, + policyEmailsByName, + billingProjectOwnerPolicyEmail, + actionServiceAccountsByAction, + span + ) + } yield (googleProjectId, googleProjectNumber, actionServiceAccountsByAction) ) } savedWorkspace <- traceDBIOWithParent("createWorkspaceInDatabase", parentContext)(span => @@ -3530,7 +3578,8 @@ class WorkspaceService(protected val ctx: RawlsRequestContext, GcsBucketName(bucketName), getLabels(workspaceRequest.authorizationDomain.getOrElse(Set.empty).toList), childContext.tracingSpan.orNull, - workspaceBucketLocation + workspaceBucketLocation, + actionServiceAccountsByAction ) ) ) diff --git a/core/src/test/scala/org/broadinstitute/dsde/rawls/dataaccess/MockGoogleServicesDAO.scala b/core/src/test/scala/org/broadinstitute/dsde/rawls/dataaccess/MockGoogleServicesDAO.scala index 09f6439fdc..d70f4d980c 100644 --- a/core/src/test/scala/org/broadinstitute/dsde/rawls/dataaccess/MockGoogleServicesDAO.scala +++ b/core/src/test/scala/org/broadinstitute/dsde/rawls/dataaccess/MockGoogleServicesDAO.scala @@ -91,7 +91,8 @@ class MockGoogleServicesDAO(groupsPrefix: String, override def updateBucketIam(bucketName: GcsBucketName, policyGroupsByAccessLevel: Map[WorkspaceAccessLevel, WorkbenchEmail], userProject: Option[GoogleProjectId], - iamPolicyVersion: Int = 1 + iamPolicyVersion: Int = 1, + actionServiceAccountsByAction: Map[SamResourceAction, WorkbenchEmail] = Map.empty ): Future[Unit] = Future.unit @@ -101,7 +102,8 @@ class MockGoogleServicesDAO(groupsPrefix: String, bucketName: GcsBucketName, labels: Map[String, String], parentSpan: Span = null, - bucketLocation: Option[String] + bucketLocation: Option[String], + actionServiceAccountsByAction: Map[SamResourceAction, WorkbenchEmail] = Map.empty ): Future[GoogleWorkspaceInfo] = { val googleWorkspaceInfo: GoogleWorkspaceInfo = GoogleWorkspaceInfo(bucketName.value, policyGroupsByAccessLevel) diff --git a/core/src/test/scala/org/broadinstitute/dsde/rawls/mock/MockSamDAO.scala b/core/src/test/scala/org/broadinstitute/dsde/rawls/mock/MockSamDAO.scala index f9cec1f6cf..691d19577e 100644 --- a/core/src/test/scala/org/broadinstitute/dsde/rawls/mock/MockSamDAO.scala +++ b/core/src/test/scala/org/broadinstitute/dsde/rawls/mock/MockSamDAO.scala @@ -65,6 +65,13 @@ class MockSamDAO(dataSource: SlickDataSource)(implicit executionContext: Executi cts: RawlsRequestContext ): Future[Boolean] = Future.successful(true) + override def getActionServiceAccount(googleProject: GoogleProjectId, + resourceTypeName: SamResourceTypeName, + resourceId: String, + action: SamResourceAction, + ctx: RawlsRequestContext + ): Future[WorkbenchEmail] = Future.successful(WorkbenchEmail("foo-action@neighborhood.horse")) + override def getPolicy(resourceTypeName: SamResourceTypeName, resourceId: String, policyName: SamResourcePolicyName, diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 6f15d34538..8d1f83f249 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -132,7 +132,7 @@ object Dependencies { val resourceBufferService = excludeJakarta("bio.terra" % "terra-resource-buffer-client" % "0.4.3-SNAPSHOT") val billingProfileManager = excludeJakarta("bio.terra" % "billing-profile-manager-client-javax" % "0.1.496-SNAPSHOT") val terraCommonLib = tclExclusions(excludeJakarta("bio.terra" % "terra-common-lib" % "0.0.95-SNAPSHOT" classifier "plain")) - val sam: ModuleID = excludeJakarta("org.broadinstitute.dsde.workbench" %% "sam-client" % "0.1-d606036") + val sam: ModuleID = excludeJakarta("org.broadinstitute.dsde.workbench" %% "sam-client" % "0.1-c4e5a5c-SNAP") val leonardo: ModuleID = "org.broadinstitute.dsde.workbench" % "leonardo-client_2.13" % "1.3.6-d0bf371" val opencensusScalaCode: ModuleID = "com.github.sebruck" %% "opencensus-scala-core" % "0.7.2" From 126461077be21b253cf129fc8ddb9c1262f980e6 Mon Sep 17 00:00:00 2001 From: tlangs Date: Wed, 24 Jan 2024 10:24:49 -0500 Subject: [PATCH 2/7] fix test --- .../dsde/rawls/webservice/WorkspaceApiServiceSpec.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/test/scala/org/broadinstitute/dsde/rawls/webservice/WorkspaceApiServiceSpec.scala b/core/src/test/scala/org/broadinstitute/dsde/rawls/webservice/WorkspaceApiServiceSpec.scala index 258ac6584d..70f76de246 100644 --- a/core/src/test/scala/org/broadinstitute/dsde/rawls/webservice/WorkspaceApiServiceSpec.scala +++ b/core/src/test/scala/org/broadinstitute/dsde/rawls/webservice/WorkspaceApiServiceSpec.scala @@ -1973,7 +1973,8 @@ class WorkspaceApiServiceSpec extends ApiServiceSpec { any[GcsBucketName], any[Map[String, String]], any[Span], - ArgumentMatchers.eq(newBucketLocation) + ArgumentMatchers.eq(newBucketLocation), + any[Map[SamResourceAction, WorkbenchEmail]] ) ) .thenReturn(Future.successful(mock[GoogleWorkspaceInfo])) @@ -2004,7 +2005,8 @@ class WorkspaceApiServiceSpec extends ApiServiceSpec { any[GcsBucketName], any[Map[String, String]], any[Span], - ArgumentMatchers.eq(newBucketLocation) + ArgumentMatchers.eq(newBucketLocation), + any[Map[SamResourceAction, WorkbenchEmail]] ) } From 1ba450f8559d5c501cf68fbc040efd1b54a3bf4e Mon Sep 17 00:00:00 2001 From: tlangs Date: Wed, 24 Jan 2024 11:03:16 -0500 Subject: [PATCH 3/7] fix tests (again...) --- project/Dependencies.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 8d1f83f249..eacfc003a8 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -132,7 +132,8 @@ object Dependencies { val resourceBufferService = excludeJakarta("bio.terra" % "terra-resource-buffer-client" % "0.4.3-SNAPSHOT") val billingProfileManager = excludeJakarta("bio.terra" % "billing-profile-manager-client-javax" % "0.1.496-SNAPSHOT") val terraCommonLib = tclExclusions(excludeJakarta("bio.terra" % "terra-common-lib" % "0.0.95-SNAPSHOT" classifier "plain")) - val sam: ModuleID = excludeJakarta("org.broadinstitute.dsde.workbench" %% "sam-client" % "0.1-c4e5a5c-SNAP") + val sam: ModuleID = excludeJakarta("org.broadinstitute.dsde.workbench" %% "sam-client" % "0.1-87e0451-SNAP") + val jaxrs: ModuleID = "org.apache.cxf" % "cxf-bundle-jaxrs" % "2.7.18" val leonardo: ModuleID = "org.broadinstitute.dsde.workbench" % "leonardo-client_2.13" % "1.3.6-d0bf371" val opencensusScalaCode: ModuleID = "com.github.sebruck" %% "opencensus-scala-core" % "0.7.2" @@ -265,6 +266,7 @@ object Dependencies { workbenchOpenTelemetryTests, terraCommonLib, sam, + jaxrs, leonardo ) From 6cedbc0e60bfbf94c54955620df08e158835abb0 Mon Sep 17 00:00:00 2001 From: tlangs Date: Wed, 24 Jan 2024 11:44:47 -0500 Subject: [PATCH 4/7] fix jar build --- project/Merging.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/project/Merging.scala b/project/Merging.scala index 8637c37556..ead7e50aa7 100644 --- a/project/Merging.scala +++ b/project/Merging.scala @@ -18,6 +18,8 @@ object Merging { case x if x.endsWith("kotlin_module") => MergeStrategy.first case x if x.endsWith("io.netty.versions.properties") => MergeStrategy.concat case x if x.endsWith("arrow-git.properties") => MergeStrategy.concat + case PathList("javax", "servlet", _ @_*) => MergeStrategy.first // This should be resolved in dependencies + case PathList("mozilla", _ @_*) => MergeStrategy.first // This should be resolved in dependencies case x => oldStrategy(x) } } From cb1d8d9773b4b375cc19a27f479ccd2e99bc36a6 Mon Sep 17 00:00:00 2001 From: tlangs Date: Mon, 12 Feb 2024 14:26:56 -0500 Subject: [PATCH 5/7] whoops --- .../broadinstitute/dsde/rawls/workspace/WorkspaceService.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/org/broadinstitute/dsde/rawls/workspace/WorkspaceService.scala b/core/src/main/scala/org/broadinstitute/dsde/rawls/workspace/WorkspaceService.scala index 18b889fc7c..f3a69be4c0 100644 --- a/core/src/main/scala/org/broadinstitute/dsde/rawls/workspace/WorkspaceService.scala +++ b/core/src/main/scala/org/broadinstitute/dsde/rawls/workspace/WorkspaceService.scala @@ -3327,7 +3327,7 @@ class WorkspaceService(protected val ctx: RawlsRequestContext, actions: Set[SamResourceAction], parentContext: RawlsRequestContext ): Future[Map[SamResourceAction, WorkbenchEmail]] = - traceWithParent("createActionServiceAccountsInSam", parentContext)(_ => + traceFutureWithParent("createActionServiceAccountsInSam", parentContext)(_ => Future .traverse(actions) { action => samDAO From 9d1a749bc1d127c0f278099c2bda611cd81d5cba Mon Sep 17 00:00:00 2001 From: tlangs Date: Tue, 13 Aug 2024 15:57:39 -0400 Subject: [PATCH 6/7] scalafmt... --- .../methods/MethodConfigurationUtils.scala | 6 +- .../MethodConfigurationUtilsSpec.scala | 55 ++++++++++++++----- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/core/src/main/scala/org/broadinstitute/dsde/rawls/methods/MethodConfigurationUtils.scala b/core/src/main/scala/org/broadinstitute/dsde/rawls/methods/MethodConfigurationUtils.scala index ea93de124b..f346adf542 100644 --- a/core/src/main/scala/org/broadinstitute/dsde/rawls/methods/MethodConfigurationUtils.scala +++ b/core/src/main/scala/org/broadinstitute/dsde/rawls/methods/MethodConfigurationUtils.scala @@ -21,7 +21,7 @@ object MethodConfigurationUtils { case Success(None) => throw new RawlsExceptionWithErrorReport( errorReport = ErrorReport(StatusCodes.NotFound, - s"Cannot get ${methodConfig.methodRepoMethod.methodUri} from method repo." + s"Cannot get ${methodConfig.methodRepoMethod.methodUri} from method repo." ) ) case Success(Some(wdl)) => @@ -34,8 +34,8 @@ object MethodConfigurationUtils { case Failure(throwable) => throw new RawlsExceptionWithErrorReport( errorReport = ErrorReport(StatusCodes.BadGateway, - s"Unable to query the method repo.", - methodRepoDAO.toErrorReport(throwable) + s"Unable to query the method repo.", + methodRepoDAO.toErrorReport(throwable) ) ) } diff --git a/core/src/test/scala/org/broadinstitute/dsde/rawls/methods/MethodConfigurationUtilsSpec.scala b/core/src/test/scala/org/broadinstitute/dsde/rawls/methods/MethodConfigurationUtilsSpec.scala index 41af3b0d20..63328729fb 100644 --- a/core/src/test/scala/org/broadinstitute/dsde/rawls/methods/MethodConfigurationUtilsSpec.scala +++ b/core/src/test/scala/org/broadinstitute/dsde/rawls/methods/MethodConfigurationUtilsSpec.scala @@ -4,7 +4,7 @@ import akka.actor.ActorSystem import akka.http.scaladsl.model.StatusCodes import akka.http.scaladsl.model.headers.OAuth2BearerToken import com.typesafe.config.ConfigFactory -import org.broadinstitute.dsde.rawls.{RawlsException, RawlsExceptionWithErrorReport, TestExecutionContext, jobexec} +import org.broadinstitute.dsde.rawls.{jobexec, RawlsException, RawlsExceptionWithErrorReport, TestExecutionContext} import org.broadinstitute.dsde.rawls.config.{MethodRepoConfig, WDLParserConfig} import org.broadinstitute.dsde.rawls.dataaccess.{HttpMethodRepoDAO, MockCromwellSwaggerClient} import org.broadinstitute.dsde.rawls.dataaccess.slick.TestDriverComponent @@ -12,7 +12,19 @@ import org.broadinstitute.dsde.rawls.jobexec.MethodConfigResolver import org.broadinstitute.dsde.rawls.jobexec.MethodConfigResolver.GatherInputsResult import org.broadinstitute.dsde.rawls.jobexec.wdlparsing.CachingWDLParser import org.broadinstitute.dsde.rawls.mock.RemoteServicesMockServer -import org.broadinstitute.dsde.rawls.model.{Agora, AgoraMethod, Dockstore, ErrorReport, ErrorReportSource, MethodConfiguration, RawlsRequestContext, RawlsUser, RawlsUserEmail, RawlsUserSubjectId, UserInfo} +import org.broadinstitute.dsde.rawls.model.{ + Agora, + AgoraMethod, + Dockstore, + ErrorReport, + ErrorReportSource, + MethodConfiguration, + RawlsRequestContext, + RawlsUser, + RawlsUserEmail, + RawlsUserSubjectId, + UserInfo +} import org.mockito.ArgumentMatchers.any import org.mockito.Mockito.when import org.scalatest.flatspec.AnyFlatSpec @@ -30,34 +42,43 @@ class MethodConfigurationUtilsSpec extends AnyFlatSpec with Matchers with TestDr val mockServer: RemoteServicesMockServer = RemoteServicesMockServer() val methodRepoDAO: HttpMethodRepoDAO = mock[HttpMethodRepoDAO] - val ctx: RawlsRequestContext = RawlsRequestContext(UserInfo(testData.userProjectOwner.userEmail, OAuth2BearerToken("foo"), 0, testData.userProjectOwner.userSubjectId)) + val ctx: RawlsRequestContext = RawlsRequestContext( + UserInfo(testData.userProjectOwner.userEmail, OAuth2BearerToken("foo"), 0, testData.userProjectOwner.userSubjectId) + ) val agoraMethodConf: MethodConfiguration = MethodConfiguration("dsde", - "no_input", - Some("Sample"), - None, - Map.empty, - Map.empty, - AgoraMethod("dsde", "no_input", 1)) + "no_input", + Some("Sample"), + None, + Map.empty, + Map.empty, + AgoraMethod("dsde", "no_input", 1) + ) behavior of "MethodConfigurationUtils" it should "return results when method when found" in { when(methodRepoDAO.getMethod(any(), any())).thenReturn(Future.successful(Option(meth1WDL))) - val future = MethodConfigurationUtils.gatherMethodConfigInputs(ctx, methodRepoDAO, agoraMethodConf, methodConfigResolver) + val future = + MethodConfigurationUtils.gatherMethodConfigInputs(ctx, methodRepoDAO, agoraMethodConf, methodConfigResolver) Await.ready(future, 30.seconds) val Success(result) = future.value.get // verify that it returns GatherInputsResult - result shouldBe a [GatherInputsResult] + result shouldBe a[GatherInputsResult] result.missingInputs shouldBe Set("meth1.method1.i1") } it should "return 404 if method is not found" in { when(methodRepoDAO.getMethod(any(), any())).thenReturn(Future.successful(None)) - val exception = intercept[RawlsExceptionWithErrorReport](Await.result(MethodConfigurationUtils.gatherMethodConfigInputs(ctx, methodRepoDAO, agoraMethodConf, methodConfigResolver), 30.seconds)) + val exception = intercept[RawlsExceptionWithErrorReport]( + Await.result( + MethodConfigurationUtils.gatherMethodConfigInputs(ctx, methodRepoDAO, agoraMethodConf, methodConfigResolver), + 30.seconds + ) + ) // assert that if method is not found it returns 404 exception shouldBe a[RawlsExceptionWithErrorReport] @@ -66,11 +87,17 @@ class MethodConfigurationUtilsSpec extends AnyFlatSpec with Matchers with TestDr } it should "return 502 when something unexpected happens" in { - when(methodRepoDAO.getMethod(any(), any())).thenReturn(Future.failed(new RawlsException("exception thrown for testing purposes"))) + when(methodRepoDAO.getMethod(any(), any())) + .thenReturn(Future.failed(new RawlsException("exception thrown for testing purposes"))) when(methodRepoDAO.errorReportSource).thenReturn(ErrorReportSource("agora")) when(methodRepoDAO.toErrorReport(any())).thenCallRealMethod() - val exception = intercept[RawlsExceptionWithErrorReport](Await.result(MethodConfigurationUtils.gatherMethodConfigInputs(ctx, methodRepoDAO, agoraMethodConf, methodConfigResolver), 30.seconds)) + val exception = intercept[RawlsExceptionWithErrorReport]( + Await.result( + MethodConfigurationUtils.gatherMethodConfigInputs(ctx, methodRepoDAO, agoraMethodConf, methodConfigResolver), + 30.seconds + ) + ) // assert that when Future fails, a 502 is returned with wrapped exception exception shouldBe a[RawlsExceptionWithErrorReport] From a2b9f2d3841788aece761d56c8ce19b8c98e02db Mon Sep 17 00:00:00 2001 From: tlangs Date: Tue, 13 Aug 2024 16:18:02 -0400 Subject: [PATCH 7/7] sam-client branch version --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index e62dc49890..55da50bae7 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -134,7 +134,7 @@ object Dependencies { val resourceBufferService = clientLibExclusions("bio.terra" % "terra-resource-buffer-client" % "0.198.42-SNAPSHOT") val billingProfileManager = clientLibExclusions("bio.terra" % "billing-profile-manager-client" % "0.1.565-SNAPSHOT") val terraCommonLib = tclExclusions(clientLibExclusions("bio.terra" % "terra-common-lib" % "0.1.23-SNAPSHOT" classifier "plain")) - val sam: ModuleID = clientLibExclusions("org.broadinstitute.dsde.workbench" %% "sam-client" % "v0.0.267") + val sam: ModuleID = clientLibExclusions("org.broadinstitute.dsde.workbench" %% "sam-client" % "v0.0.269-3af0af2-SNAP") val leonardo: ModuleID = "org.broadinstitute.dsde.workbench" % "leonardo-client_2.13" % "1.3.6-2e87300" // OpenTelemetry