Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,15 @@

package org.apache.texera.service

import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.typesafe.scalalogging.LazyLogging
import io.dropwizard.configuration.{EnvironmentVariableSubstitutor, SubstitutingSourceProvider}
import io.dropwizard.core.Application
import io.dropwizard.core.setup.{Bootstrap, Environment}
import org.apache.texera.common.config.StorageConfig
import org.apache.texera.auth.{AuthFeatures, RequestLoggingFilter, RoleAnnotationEnforcer}
import org.apache.texera.dao.SqlServer
import org.apache.texera.auth.{
AuthFeatures,
RequestLoggingFilter,
RoleAnnotationEnforcer,
ServiceBootstrap
}
import org.apache.texera.service.activity.UserActivityEventListener
import org.apache.texera.service.resource.{
AccessControlResource,
Expand All @@ -33,25 +34,11 @@ import org.apache.texera.service.resource.{
LiteLLMProxyResource
}
import org.eclipse.jetty.server.session.SessionHandler
import java.nio.file.Path

class AccessControlService extends Application[AccessControlServiceConfiguration] with LazyLogging {
override def initialize(bootstrap: Bootstrap[AccessControlServiceConfiguration]): Unit = {
// enable environment variable substitution in YAML config
bootstrap.setConfigurationSourceProvider(
new SubstitutingSourceProvider(
bootstrap.getConfigurationSourceProvider,
new EnvironmentVariableSubstitutor(false)
)
)
// Register Scala module to Dropwizard default object mapper
bootstrap.getObjectMapper.registerModule(DefaultScalaModule)

SqlServer.initConnection(
StorageConfig.jdbcUrl,
StorageConfig.jdbcUsername,
StorageConfig.jdbcPassword
)
ServiceBootstrap.configure(bootstrap)
ServiceBootstrap.initDatabase()
}

override def run(
Expand Down Expand Up @@ -85,15 +72,10 @@ class AccessControlService extends Application[AccessControlServiceConfiguration
}
object AccessControlService {
def main(args: Array[String]): Unit = {
val accessControlPath = Path
.of(sys.env.getOrElse("TEXERA_HOME", "."))
.resolve("access-control-service")
.resolve("src")
.resolve("main")
.resolve("resources")
.resolve("access-control-service-web-config.yaml")
.toAbsolutePath
.toString
val accessControlPath = ServiceBootstrap.configFilePath(
"access-control-service",
"access-control-service-web-config.yaml"
)

// Start the Dropwizard application
new AccessControlService().run("server", accessControlPath)
Expand Down
1 change: 1 addition & 0 deletions common/auth/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ libraryDependencies ++= Seq(
"org.glassfish.jersey.core" % "jersey-server" % "3.0.12" % "provided", // for RoleAnnotationEnforcer's ResourceConfig overload and AuthFeatures' RolesAllowedDynamicFeature
"io.dropwizard" % "dropwizard-core" % "4.0.7" % "provided", // for AuthFeatures' Environment
"io.dropwizard" % "dropwizard-auth" % "4.0.7" % "provided", // for AuthFeatures' AuthDynamicFeature/AuthValueFactoryProvider
"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.18.6" % "provided", // for ServiceBootstrap's DefaultScalaModule
"org.scalatest" %% "scalatest" % "3.2.17" % Test,
"org.mockito" % "mockito-core" % "5.4.0" % Test // for mocking the Jersey environment in AuthFeaturesSpec
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.texera.auth

import com.fasterxml.jackson.module.scala.DefaultScalaModule
import io.dropwizard.configuration.{EnvironmentVariableSubstitutor, SubstitutingSourceProvider}
import io.dropwizard.core.Configuration
import io.dropwizard.core.setup.Bootstrap
import org.apache.texera.common.config.StorageConfig
import org.apache.texera.dao.SqlServer

import java.nio.file.Path

/** Shared Dropwizard service bootstrap steps, identical across every Texera
* service. Kept here so the services don't drift apart.
*/
object ServiceBootstrap {

/** Enable `${ENV_VAR}` substitution in the YAML config and register the Scala
* module on Dropwizard's default object mapper.
*/
def configure[T <: Configuration](bootstrap: Bootstrap[T]): Unit = {
// enable environment variable substitution in YAML config
bootstrap.setConfigurationSourceProvider(
new SubstitutingSourceProvider(
bootstrap.getConfigurationSourceProvider,
new EnvironmentVariableSubstitutor(false)
)
)
// register Scala module to Dropwizard default object mapper
bootstrap.getObjectMapper.registerModule(DefaultScalaModule)
}

/** Open the shared SQL connection pool using the storage configuration. */
def initDatabase(): Unit =
SqlServer.initConnection(
StorageConfig.jdbcUrl,
StorageConfig.jdbcUsername,
StorageConfig.jdbcPassword
)

/** Resolve `$TEXERA_HOME/<serviceDir>/src/main/resources/<configFileName>` to
* an absolute path string, the convention every service `main` uses.
*/
def configFilePath(serviceDir: String, configFileName: String): String =
Path
.of(sys.env.getOrElse("TEXERA_HOME", "."))
.resolve(serviceDir)
.resolve("src")
.resolve("main")
.resolve("resources")
.resolve(configFileName)
.toAbsolutePath
.toString
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.texera.auth

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import io.dropwizard.configuration.ConfigurationSourceProvider
import io.dropwizard.core.Configuration
import io.dropwizard.core.setup.Bootstrap
import org.mockito.ArgumentMatchers.{any, isA}
import org.mockito.Mockito.{mock, verify, when}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

import java.nio.file.Paths

class ServiceBootstrapSpec extends AnyFlatSpec with Matchers {

// Every service shares this bootstrap helper, so its behavior is verified once here
// rather than per service.
"ServiceBootstrap.configure" should "wrap the config source provider and register the Scala module" in {
val bootstrap = mock(classOf[Bootstrap[Configuration]])
val objectMapper = mock(classOf[ObjectMapper])
val sourceProvider = mock(classOf[ConfigurationSourceProvider])
when(bootstrap.getObjectMapper).thenReturn(objectMapper)
when(bootstrap.getConfigurationSourceProvider).thenReturn(sourceProvider)

ServiceBootstrap.configure(bootstrap)

verify(bootstrap).setConfigurationSourceProvider(any(classOf[ConfigurationSourceProvider]))
verify(objectMapper).registerModule(isA(classOf[DefaultScalaModule]))
}

"ServiceBootstrap.configFilePath" should "resolve the conventional resources path under the service dir" in {
val result = ServiceBootstrap.configFilePath("file-service", "file-service-web-config.yaml")

val expectedSuffix = Paths
.get("file-service", "src", "main", "resources", "file-service-web-config.yaml")
.toString
result should endWith(expectedSuffix)
Paths.get(result).isAbsolute shouldBe true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,34 +19,26 @@

package org.apache.texera.service

import com.fasterxml.jackson.module.scala.DefaultScalaModule
import io.dropwizard.configuration.{EnvironmentVariableSubstitutor, SubstitutingSourceProvider}
import io.dropwizard.core.Application
import io.dropwizard.core.setup.{Bootstrap, Environment}
import org.apache.texera.common.config.StorageConfig
import org.apache.texera.auth.{AuthFeatures, RequestLoggingFilter, RoleAnnotationEnforcer}
import org.apache.texera.dao.SqlServer
import org.apache.texera.auth.{
AuthFeatures,
RequestLoggingFilter,
RoleAnnotationEnforcer,
ServiceBootstrap
}
import org.apache.texera.service.resource.{
ComputingUnitAccessResource,
ComputingUnitManagingResource,
HealthCheckResource
}
import java.nio.file.Path

class ComputingUnitManagingService extends Application[ComputingUnitManagingServiceConfiguration] {

override def initialize(
bootstrap: Bootstrap[ComputingUnitManagingServiceConfiguration]
): Unit = {
// enable environment variable substitution in YAML config
bootstrap.setConfigurationSourceProvider(
new SubstitutingSourceProvider(
bootstrap.getConfigurationSourceProvider,
new EnvironmentVariableSubstitutor(false)
)
)
// register scala module to dropwizard default object mapper
bootstrap.getObjectMapper.registerModule(DefaultScalaModule)
ServiceBootstrap.configure(bootstrap)
}
override def run(
configuration: ComputingUnitManagingServiceConfiguration,
Expand All @@ -58,11 +50,7 @@ class ComputingUnitManagingService extends Application[ComputingUnitManagingServ

AuthFeatures.register(environment)

SqlServer.initConnection(
StorageConfig.jdbcUrl,
StorageConfig.jdbcUsername,
StorageConfig.jdbcPassword
)
ServiceBootstrap.initDatabase()

environment.jersey().register(new ComputingUnitManagingResource)
environment.jersey().register(new ComputingUnitAccessResource)
Expand All @@ -79,15 +67,10 @@ class ComputingUnitManagingService extends Application[ComputingUnitManagingServ

object ComputingUnitManagingService {
def main(args: Array[String]): Unit = {
val configFilePath = Path
.of(sys.env.getOrElse("TEXERA_HOME", "."))
.resolve("computing-unit-managing-service")
.resolve("src")
.resolve("main")
.resolve("resources")
.resolve("computing-unit-managing-service-config.yaml")
.toAbsolutePath
.toString
val configFilePath = ServiceBootstrap.configFilePath(
"computing-unit-managing-service",
"computing-unit-managing-service-config.yaml"
)

new ComputingUnitManagingService().run("server", configFilePath)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,37 +19,25 @@

package org.apache.texera.service

import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.typesafe.scalalogging.LazyLogging
import io.dropwizard.configuration.{EnvironmentVariableSubstitutor, SubstitutingSourceProvider}
import io.dropwizard.core.Application
import io.dropwizard.core.setup.{Bootstrap, Environment}
import org.apache.texera.auth.{AuthFeatures, RequestLoggingFilter, RoleAnnotationEnforcer}
import org.apache.texera.common.config.{DefaultsConfig, StorageConfig}
import org.apache.texera.auth.{
AuthFeatures,
RequestLoggingFilter,
RoleAnnotationEnforcer,
ServiceBootstrap
}
import org.apache.texera.common.config.DefaultsConfig
import org.apache.texera.dao.SqlServer
import org.apache.texera.service.resource.{ConfigResource, HealthCheckResource}
import org.eclipse.jetty.server.session.SessionHandler
import org.jooq.impl.DSL

import java.nio.file.Path

class ConfigService extends Application[ConfigServiceConfiguration] with LazyLogging {
override def initialize(bootstrap: Bootstrap[ConfigServiceConfiguration]): Unit = {
// enable environment variable substitution in YAML config
bootstrap.setConfigurationSourceProvider(
new SubstitutingSourceProvider(
bootstrap.getConfigurationSourceProvider,
new EnvironmentVariableSubstitutor(false)
)
)
// Register Scala module to Dropwizard default object mapper
bootstrap.getObjectMapper.registerModule(DefaultScalaModule)

SqlServer.initConnection(
StorageConfig.jdbcUrl,
StorageConfig.jdbcUsername,
StorageConfig.jdbcPassword
)
ServiceBootstrap.configure(bootstrap)
ServiceBootstrap.initDatabase()
}

override def run(configuration: ConfigServiceConfiguration, environment: Environment): Unit = {
Expand Down Expand Up @@ -104,15 +92,8 @@ class ConfigService extends Application[ConfigServiceConfiguration] with LazyLog

object ConfigService {
def main(args: Array[String]): Unit = {
val configFilePath = Path
.of(sys.env.getOrElse("TEXERA_HOME", "."))
.resolve("config-service")
.resolve("src")
.resolve("main")
.resolve("resources")
.resolve("config-service-web-config.yaml")
.toAbsolutePath
.toString
val configFilePath =
ServiceBootstrap.configFilePath("config-service", "config-service-web-config.yaml")

// Start the Dropwizard application
new ConfigService().run("server", configFilePath)
Expand Down
Loading
Loading