Skip to content

Commit

Permalink
[IA-4295] [IA-4327] Add Sam client to Leonardo, populate workspace ID (
Browse files Browse the repository at this point in the history
  • Loading branch information
rtitle authored Aug 28, 2024
1 parent 54c8a19 commit 0f2a5a0
Show file tree
Hide file tree
Showing 51 changed files with 1,045 additions and 491 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ final case class ListPersistentDiskResponse(id: DiskId,
size: DiskSize,
diskType: DiskType,
blockSize: BlockSize,
labels: LabelMap
labels: LabelMap,
workspaceId: Option[WorkspaceId]
)

final case class GetPersistentDiskResponse(id: DiskId,
Expand All @@ -57,7 +58,8 @@ final case class GetPersistentDiskResponse(id: DiskId,
diskType: DiskType,
blockSize: BlockSize,
labels: LabelMap,
formattedBy: Option[FormattedBy]
formattedBy: Option[FormattedBy],
workspaceId: Option[WorkspaceId]
)

final case class GetPersistentDiskV2Response(id: DiskId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ object DiskRoutesTestJsonCodec {
blockSize <- x.downField("blockSize").as[BlockSize]
labels <- x.downField("labels").as[LabelMap]
formattedBy <- x.downField("formattedBy").as[Option[FormattedBy]]
workspaceId <- x.downField("workspaceId").as[Option[WorkspaceId]]
} yield GetPersistentDiskResponse(id,
cloudContext,
zone,
Expand All @@ -64,7 +65,8 @@ object DiskRoutesTestJsonCodec {
diskType,
blockSize,
labels,
formattedBy
formattedBy,
workspaceId
)
}

Expand Down Expand Up @@ -115,6 +117,7 @@ object DiskRoutesTestJsonCodec {
diskType <- x.downField("diskType").as[DiskType]
blockSize <- x.downField("blockSize").as[BlockSize]
labels <- x.downField("labels").as[LabelMap]
workspaceId <- x.downField("workspaceId").as[Option[WorkspaceId]]
} yield ListPersistentDiskResponse(
id,
cloudContext,
Expand All @@ -125,7 +128,8 @@ object DiskRoutesTestJsonCodec {
size,
diskType,
blockSize,
labels
labels,
workspaceId
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import org.scalatest.flatspec.AnyFlatSpecLike
import org.scalatest.matchers.should.Matchers

import java.time.Instant
import java.util.UUID

class DiskRoutesTestJsonCodecSpec extends LeonardoTestSuite with Matchers with AnyFlatSpecLike {
it should "decode DataprocConfig properly" in {
it should "decode GetPersistentDiskResponse properly" in {
val inputString =
"""
|{
Expand Down Expand Up @@ -41,7 +42,8 @@ class DiskRoutesTestJsonCodecSpec extends LeonardoTestSuite with Matchers with A
| "creator": "[email protected]",
| "googleProject": "gpalloc-dev-master-tzprbkr",
| "serviceAccount": "b305pet-114763077412354570085@gpalloc-dev-master-tzprbkr.iam.gserviceaccount.com"
| }
| },
| "workspaceId": "5955382f-c8be-464b-b5b9-2c9260cd2661"
|}
|""".stripMargin

Expand Down Expand Up @@ -69,7 +71,8 @@ class DiskRoutesTestJsonCodecSpec extends LeonardoTestSuite with Matchers with A
"googleProject" -> "gpalloc-dev-master-tzprbkr",
"serviceAccount" -> "b305pet-114763077412354570085@gpalloc-dev-master-tzprbkr.iam.gserviceaccount.com"
),
None
None,
Some(WorkspaceId(UUID.fromString("5955382f-c8be-464b-b5b9-2c9260cd2661")))
)
res shouldBe (Right(expected))
}
Expand Down
17 changes: 0 additions & 17 deletions http/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -1116,23 +1116,6 @@ auth {
}
}

# Implement and specify a class that will provide appropriate service accounts
serviceAccounts {
providerClass = "org.broadinstitute.dsde.workbench.leonardo.auth.sam.PetClusterServiceAccountProvider"
providerConfig {
leoServiceAccountJsonFile = ${application.leoServiceAccountJsonFile}
leoServiceAccountEmail = ${application.leoServiceAccountEmail}
samServer = ${sam.server}
petKeyCacheExpiryTime = 24 hours
petKeyCacheMaxSize = 10000
providerTimeout = 30 seconds
}
kubeConfig {
leoServiceAccountJsonFile = ${application.leoServiceAccountJsonFile}
leoServiceAccountEmail = ${application.leoServiceAccountEmail}
}
}

pubsub {
#pubsubGoogleProject = "broad-dsde-dev"
#topicName = "leonardo-pubsub"
Expand Down
16 changes: 12 additions & 4 deletions http/src/main/resources/swagger/api-docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3295,6 +3295,10 @@ components:
labels:
type: object
description: The labels of each persistent disk in the response whose key is in includeLabels in the request. Of type Map[String,String]
workspaceId:
type: string
description: The id of the disk's workspace


GetPersistentDiskResponse:
description: ""
Expand Down Expand Up @@ -3354,6 +3358,10 @@ components:
- CUSTOM
- ALLOWED
description: App that formatted disk, missing if unformatted
workspaceId:
type: string
description: The id of the disk's workspace


GetPersistentDiskV2Response:
description: ""
Expand Down Expand Up @@ -4163,18 +4171,18 @@ components:
bucketNameToMount:
type: string
description: >
The GCS bucket name (without gs://) that will be mounted in the app if present.
The GCS bucket name (without gs://) that will be mounted in the app if present.
UpdateAppRequest:
description: A request to update the configuration of an app
type: object
properties:
autodeleteEnabled:
type: boolean
description: >
description: >
Optional: Whether to turn on autodelete. When autodeleteEnabled is set to true but there is no
autodeleteThreshold supplied in this request, the existing app's configuration will be used. If the
existing configuration does not have an autodeleteThreshold with a positive integer, this would result in
autodeleteThreshold supplied in this request, the existing app's configuration will be used. If the
existing configuration does not have an autodeleteThreshold with a positive integer, this would result in
an invalid autodelete configuration, and therefore this update request would be rejected.
autodeleteThreshold:
type: integer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ package org.broadinstitute.dsde.workbench.leonardo.auth
import cats.effect.{Async, Ref}
import cats.syntax.all._
import org.broadinstitute.dsde.workbench.leonardo.CloudProvider
import org.broadinstitute.dsde.workbench.leonardo.config.AzureHostingModeConfig
import org.broadinstitute.dsde.workbench.leonardo.model.ServiceAccountProviderConfig
import org.broadinstitute.dsde.workbench.leonardo.config.{ApplicationConfig, AzureHostingModeConfig}
import org.http4s.headers.Authorization
import org.http4s.{AuthScheme, Credentials}

Expand Down Expand Up @@ -55,12 +54,12 @@ abstract class CloudServiceAuthTokenProvider[F[_]](cloudProvider: CloudProvider)

object CloudAuthTokenProvider {
def apply[F[_]: Async](hostingModeConfig: AzureHostingModeConfig,
serviceAccountProviderConfig: ServiceAccountProviderConfig
applicationConfig: ApplicationConfig
): CloudAuthTokenProvider[F] =
if (hostingModeConfig.enabled) {
new AzureCloudAuthTokenProvider[F](hostingModeConfig)
} else {
new GcpCloudAuthTokenProvider[F](serviceAccountProviderConfig)
new GcpCloudAuthTokenProvider[F](applicationConfig)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import cats.syntax.all._
import com.google.api.services.storage.StorageScopes
import org.broadinstitute.dsde.workbench.google2.credentialResource
import org.broadinstitute.dsde.workbench.leonardo.CloudProvider
import org.broadinstitute.dsde.workbench.leonardo.model.ServiceAccountProviderConfig
import org.broadinstitute.dsde.workbench.leonardo.config.ApplicationConfig

import scala.jdk.CollectionConverters.SeqHasAsJava

Expand All @@ -14,7 +14,7 @@ import scala.jdk.CollectionConverters.SeqHasAsJava
* Gets a token using the service account json file and the scopes.
*/
class GcpCloudAuthTokenProvider[F[_]](config: ServiceAccountProviderConfig)(implicit F: Async[F])
class GcpCloudAuthTokenProvider[F[_]](config: ApplicationConfig)(implicit F: Async[F])
extends CloudServiceAuthTokenProvider[F](CloudProvider.Gcp) {
// TODO: The scopes were hardcoded in the previous implementation as well
// Perhaps we should consider adding them to the configuration.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,17 @@ import scalacache.Cache

import scala.concurrent.duration._

/**
* Deprecated. Functionality should be ported to SamService, which uses the generated Sam client.
*/
@Deprecated
class SamAuthProvider[F[_]: OpenTelemetryMetrics](
samDao: SamDAO[F],
config: SamAuthProviderConfig,
saProvider: ServiceAccountProvider[F],
authCache: Cache[F, AuthCacheKey, Boolean]
)(implicit F: Async[F], logger: StructuredLogger[F])
extends LeoAuthProvider[F]
with Http4sClientDsl[F] {
override def serviceAccountProvider: ServiceAccountProvider[F] = saProvider
override def hasPermission[R, A](samResource: R, action: A, userInfo: UserInfo)(implicit
sr: SamResourceAction[R, A],
ev: Ask[F, TraceId]
Expand Down Expand Up @@ -421,7 +423,6 @@ class SamAuthProvider[F[_]: OpenTelemetryMetrics](
case _ => F.raiseError(new RuntimeException("Could not obtain Leo auth token"))
}
} yield token

}

final case class SamAuthProviderConfig(authCacheEnabled: Boolean,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import org.broadinstitute.dsde.workbench.leonardo.dao.{GroupName, HttpSamDaoConf
import org.broadinstitute.dsde.workbench.leonardo.http.ConfigReader
import org.broadinstitute.dsde.workbench.leonardo.http.service.AppServiceConfig
import org.broadinstitute.dsde.workbench.leonardo.http.service.LeoAppServiceInterp.LeoKubernetesConfig
import org.broadinstitute.dsde.workbench.leonardo.model.ServiceAccountProviderConfig
import org.broadinstitute.dsde.workbench.leonardo.monitor.MonitorConfig.{DataprocMonitorConfig, GceMonitorConfig}
import org.broadinstitute.dsde.workbench.leonardo.monitor.{
CreateDiskTimeout,
Expand Down Expand Up @@ -381,14 +380,6 @@ object Config {
)
}

implicit private val serviceAccountProviderConfigValueReader: ValueReader[ServiceAccountProviderConfig] =
ValueReader.relative { config =>
ServiceAccountProviderConfig(
config.as[Path]("leoServiceAccountJsonFile"),
config.as[WorkbenchEmail]("leoServiceAccountEmail")
)
}

implicit private val httpSamDao2ConfigValueReader: ValueReader[HttpSamDaoConfig] = ValueReader.relative { config =>
HttpSamDaoConfig(
Uri.unsafeFromString(config.as[String]("samServer")),
Expand Down Expand Up @@ -515,8 +506,6 @@ object Config {
val samConfig = config.as[SamConfig]("sam")
val autoFreezeConfig = config.as[AutoFreezeConfig]("autoFreeze")
val autodeleteConfig = config.as[AutoDeleteConfig]("autodelete")
val serviceAccountProviderConfig = config.as[ServiceAccountProviderConfig]("serviceAccounts.providerConfig")
val kubeServiceAccountProviderConfig = config.as[ServiceAccountProviderConfig]("serviceAccounts.kubeConfig")
val contentSecurityPolicy = config.as[ContentSecurityPolicyConfig]("contentSecurityPolicy")
val refererConfig = config.as[RefererConfig]("refererConfig")
val vpcConfig = config.as[VPCConfig]("vpc")
Expand Down Expand Up @@ -796,7 +785,6 @@ object Config {
}

val leoKubernetesConfig = LeoKubernetesConfig(
kubeServiceAccountProviderConfig,
gkeClusterConfig,
gkeNodepoolConfig,
gkeIngressConfig,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ import scala.concurrent.duration._
import scala.jdk.CollectionConverters.SeqHasAsJava
import scala.util.control.NoStackTrace

/**
* Deprecated. Functionality should be ported to SamService, which uses the generated Sam client.
*/
@Deprecated
class HttpSamDAO[F[_]](httpClient: Client[F],
config: HttpSamDaoConfig,
petKeyCache: Cache[F, UserEmailAndProject, Option[Json]],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import org.broadinstitute.dsde.workbench.model.{TraceId, UserInfo, WorkbenchEmai
import org.broadinstitute.dsde.workbench.util.health.StatusCheckResponse
import org.http4s.headers.Authorization

/**
* Deprecated. Functionality should be ported to SamService, which uses the generated Sam client.
*/
@Deprecated
trait SamDAO[F[_]] {

/** Registers the Leo SA as a user in Sam. */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package org.broadinstitute.dsde.workbench.leonardo.dao.sam

import cats.effect.Async
import cats.mtl.Ask
import cats.syntax.all._
import okhttp3.Protocol
import org.broadinstitute.dsde.workbench.client.sam.ApiClient
import org.broadinstitute.dsde.workbench.client.sam.api.{AzureApi, GoogleApi, ResourcesApi}
import org.broadinstitute.dsde.workbench.leonardo.AppContext

import scala.concurrent.duration._
import scala.jdk.CollectionConverters._
import scala.jdk.DurationConverters._

/**
* Provides access to various Sam clients:
* - ResourcesApi is used for interacting with Sam resources and policies to enforce access control.
* - GoogleApi is used for Google-specific extensions for users, such as pet service accounts and proxy groups.
* - AzureApi is used for Azure-specific extensions for users, such as pet managed identities.
*/
trait SamApiClientProvider[F[_]] {
def resourcesApi(token: String)(implicit ev: Ask[F, AppContext]): F[ResourcesApi]
def googleApi(token: String)(implicit ev: Ask[F, AppContext]): F[GoogleApi]
def azureApi(token: String)(implicit ev: Ask[F, AppContext]): F[AzureApi]
}

class HttpSamApiClientProvider[F[_]](samUrl: String)(implicit F: Async[F]) extends SamApiClientProvider[F] {
private val okHttpClient = new ApiClient().getHttpClient
private val timeout = 30 seconds

private def getApiClient(token: String)(implicit ev: Ask[F, AppContext]): F[ApiClient] =
for {
ctx <- ev.ask
okHttpClientBuilder = okHttpClient.newBuilder
.readTimeout(timeout.toJava)
.protocols(Seq(Protocol.HTTP_1_1).asJava)
// TODO add otel interceptors
// See https://broadworkbench.atlassian.net/browse/IA-5052
apiClient = new ApiClient(okHttpClientBuilder.build()).setBasePath(samUrl)
_ = apiClient.setAccessToken(token)
} yield apiClient

override def resourcesApi(token: String)(implicit ev: Ask[F, AppContext]): F[ResourcesApi] =
getApiClient(token).map(api => new ResourcesApi(api))

override def googleApi(token: String)(implicit ev: Ask[F, AppContext]): F[GoogleApi] =
getApiClient(token).map(api => new GoogleApi(api))

override def azureApi(token: String)(implicit ev: Ask[F, AppContext]): F[AzureApi] =
getApiClient(token).map(api => new AzureApi(api))
}
Loading

0 comments on commit 0f2a5a0

Please sign in to comment.