Skip to content

Commit 6c45f01

Browse files
ashishaggkeshavpeswani
authored andcommitted
Adding init and close methods in authenticator trait (#54)
1 parent 804218d commit 6c45f01

File tree

7 files changed

+90
-162
lines changed

7 files changed

+90
-162
lines changed

commons/src/main/scala/com/expedia/www/haystack/collector/commons/config/ConfigurationLoader.scala

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,55 +20,104 @@ package com.expedia.www.haystack.collector.commons.config
2020
import java.io.File
2121
import java.util.Properties
2222

23-
import com.typesafe.config.{Config, ConfigFactory}
23+
import com.typesafe.config.{Config, ConfigFactory, ConfigRenderOptions, ConfigValueType}
2424
import org.apache.kafka.clients.producer.ProducerConfig
2525
import org.apache.kafka.clients.producer.ProducerConfig.{KEY_SERIALIZER_CLASS_CONFIG, VALUE_SERIALIZER_CLASS_CONFIG}
2626
import org.apache.kafka.common.serialization.ByteArraySerializer
27+
import org.slf4j.LoggerFactory
2728

2829
import scala.collection.JavaConversions._
30+
import scala.collection.JavaConverters._
2931

3032
object ConfigurationLoader {
3133

32-
private val ENV_NAME_PREFIX = "HAYSTACK_PROP_"
34+
private val LOGGER = LoggerFactory.getLogger(ConfigurationLoader.getClass)
35+
36+
private[haystack] val ENV_NAME_PREFIX = "HAYSTACK_PROP_"
3337

3438
/**
3539
* Load and return the configuration
3640
* if overrides_config_path env variable exists, then we load that config file and use base conf as fallback,
3741
* else we load the config from env variables(prefixed with haystack) and use base conf as fallback
3842
*
43+
* @param resourceName name of the resource file to be loaded. Default value is `config/base.conf`
44+
* @param envNamePrefix env variable prefix to override config values. Default is `HAYSTACK_PROP_`
45+
*
46+
* @return an instance of com.typesafe.Config
3947
*/
40-
lazy val loadAppConfig: Config = {
41-
val baseConfig = ConfigFactory.load("config/base.conf")
42-
43-
sys.env.get("HAYSTACK_OVERRIDES_CONFIG_PATH") match {
44-
case Some(path) => ConfigFactory.parseFile(new File(path)).withFallback(baseConfig)
45-
case _ => loadFromEnvVars().withFallback(baseConfig)
48+
def loadConfigFileWithEnvOverrides(resourceName : String = "config/base.conf",
49+
envNamePrefix : String = ENV_NAME_PREFIX) : Config = {
50+
51+
require(resourceName != null && resourceName.length > 0 , "resourceName is required")
52+
require(envNamePrefix != null && envNamePrefix.length > 0 , "envNamePrefix is required")
53+
54+
val baseConfig = ConfigFactory.load(resourceName)
55+
56+
val keysWithArrayValues = baseConfig.entrySet()
57+
.asScala
58+
.filter(_.getValue.valueType() == ConfigValueType.LIST)
59+
.map(_.getKey)
60+
.toSet
61+
62+
val config = sys.env.get("HAYSTACK_OVERRIDES_CONFIG_PATH") match {
63+
case Some(overrideConfigPath) =>
64+
val overrideConfig = ConfigFactory.parseFile(new File(overrideConfigPath))
65+
ConfigFactory
66+
.parseMap(parsePropertiesFromMap(sys.env, keysWithArrayValues, envNamePrefix).asJava)
67+
.withFallback(overrideConfig)
68+
.withFallback(baseConfig)
69+
.resolve()
70+
case _ => ConfigFactory
71+
.parseMap(parsePropertiesFromMap(sys.env, keysWithArrayValues, envNamePrefix).asJava)
72+
.withFallback(baseConfig)
73+
.resolve()
4674
}
75+
76+
// In key-value pairs that contain 'password' in the key, replace the value with asterisks
77+
LOGGER.info(config.root()
78+
.render(ConfigRenderOptions.defaults().setOriginComments(false))
79+
.replaceAll("(?i)(\\\".*password\\\"\\s*:\\s*)\\\".+\\\"", "$1********"))
80+
81+
config
4782
}
4883

4984
/**
50-
* @return new config object with haystack specific environment variables
85+
* @return new config object with haystack specific environment variables
5186
*/
52-
private def loadFromEnvVars(): Config = {
53-
val envMap = sys.env.filter {
54-
case (envName, _) => isHaystackEnvVar(envName)
87+
private[haystack] def parsePropertiesFromMap(envVars: Map[String, String],
88+
keysWithArrayValues: Set[String],
89+
envNamePrefix: String): Map[String, Object] = {
90+
envVars.filter {
91+
case (envName, _) => envName.startsWith(envNamePrefix)
5592
} map {
56-
case (envName, envValue) => (transformEnvVarName(envName), envValue)
93+
case (envName, envValue) =>
94+
val key = transformEnvVarName(envName, envNamePrefix)
95+
if (keysWithArrayValues.contains(key)) (key, transformEnvVarArrayValue(envValue)) else (key, envValue)
5796
}
58-
59-
ConfigFactory.parseMap(envMap)
6097
}
6198

62-
private def isHaystackEnvVar(env: String): Boolean = env.startsWith(ENV_NAME_PREFIX)
63-
6499
/**
65100
* converts the env variable to HOCON format
66101
* for e.g. env variable HAYSTACK_KAFKA_STREAMS_NUM_STREAM_THREADS gets converted to kafka.streams.num.stream.threads
67102
* @param env environment variable name
68-
* @return
103+
* @return variable name that complies with hocon key
69104
*/
70-
private def transformEnvVarName(env: String): String = {
71-
env.replaceFirst(ENV_NAME_PREFIX, "").toLowerCase.replace("_", ".")
105+
private def transformEnvVarName(env: String, envNamePrefix: String): String = {
106+
env.replaceFirst(envNamePrefix, "").toLowerCase.replace("_", ".")
107+
}
108+
109+
/**
110+
* converts the env variable value to iterable object if it starts and ends with '[' and ']' respectively.
111+
* @param env environment variable value
112+
* @return string or iterable object
113+
*/
114+
private def transformEnvVarArrayValue(env: String): java.util.List[String] = {
115+
if (env.startsWith("[") && env.endsWith("]")) {
116+
import scala.collection.JavaConverters._
117+
env.substring(1, env.length - 1).split(',').filter(str => (str != null) && str.nonEmpty).toList.asJava
118+
} else {
119+
throw new RuntimeException("config key is of array type, so it should start and end with '[', ']' respectively")
120+
}
72121
}
73122

74123
def kafkaProducerConfig(config: Config): KafkaProduceConfiguration = {
@@ -97,6 +146,5 @@ object ConfigurationLoader {
97146
def extractorConfiguration(config: Config): ExtractorConfiguration = {
98147
val extractor = config.getConfig("extractor")
99148
ExtractorConfiguration(outputFormat = if (extractor.hasPath("output.format")) Format.withName(extractor.getString("output.format")) else Format.PROTO)
100-
101149
}
102150
}

http/src/main/scala/com/expedia/www/haystack/http/span/collector/ProjectConfiguration.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,21 @@ import scala.reflect.ClassTag
2626
case class HttpConfiguration(host: String = "127.0.0.1", port: Int = 8080, authenticator: Authenticator = NoopAuthenticator)
2727

2828
object ProjectConfiguration {
29-
val config: Config = ConfigurationLoader.loadAppConfig
29+
val config: Config = ConfigurationLoader.loadConfigFileWithEnvOverrides()
3030

3131
def kafkaProducerConfig(): KafkaProduceConfiguration = ConfigurationLoader.kafkaProducerConfig(config)
3232
def extractorConfig(): ExtractorConfiguration = ConfigurationLoader.extractorConfiguration(config)
3333

34-
def httpConfig(): HttpConfiguration = {
34+
lazy val httpConfig: HttpConfiguration = {
3535
val authenticator = if(config.hasPath("http.authenticator")) {
3636
toInstance[Authenticator](config.getString("http.authenticator"))
3737
} else {
3838
NoopAuthenticator
3939
}
4040

41+
// initialize the
42+
authenticator.init(config)
43+
4144
HttpConfiguration(config.getString("http.host"), config.getInt("http.port"), authenticator)
4245
}
4346

http/src/main/scala/com/expedia/www/haystack/http/span/collector/WebServer.scala

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import org.slf4j.LoggerFactory
3535
import scala.concurrent.duration._
3636
import scala.concurrent.{Await, Future}
3737
import scala.sys._
38+
import scala.util.Try
3839

3940
object WebServer extends App with MetricsSupport {
4041
val LOGGER = LoggerFactory.getLogger(WebServer.getClass)
@@ -45,7 +46,7 @@ object WebServer extends App with MetricsSupport {
4546
ProjectConfiguration.extractorConfig(),
4647
LoggerFactory.getLogger(classOf[ProtoSpanExtractor]))
4748

48-
private val http = ProjectConfiguration.httpConfig()
49+
private val http = ProjectConfiguration.httpConfig
4950

5051
// setup actor system
5152
implicit val system = ActorSystem("span-collector", ProjectConfiguration.config)
@@ -123,7 +124,8 @@ object WebServer extends App with MetricsSupport {
123124
}
124125

125126
def close(): Unit = {
126-
kafkaSink.close()
127+
Try(kafkaSink.close())
128+
Try(http.authenticator.close())
127129
materializer.shutdown()
128130
system.terminate()
129131
jmxReporter.close()

http/src/main/scala/com/expedia/www/haystack/http/span/collector/WebServer.scala.orig

Lines changed: 0 additions & 135 deletions
This file was deleted.

http/src/main/scala/com/expedia/www/haystack/http/span/collector/authenticator/Authenticator.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,13 @@
1717

1818
package com.expedia.www.haystack.http.span.collector.authenticator
1919

20+
import java.io.Closeable
21+
2022
import akka.http.scaladsl.model.HttpRequest
23+
import com.typesafe.config.Config
2124

22-
trait Authenticator {
25+
trait Authenticator extends Closeable {
2326
def apply(req: HttpRequest): Boolean
27+
28+
def init(config: Config): Unit
2429
}

http/src/main/scala/com/expedia/www/haystack/http/span/collector/authenticator/NoopAuthenticator.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@
1818
package com.expedia.www.haystack.http.span.collector.authenticator
1919

2020
import akka.http.scaladsl.model.HttpRequest
21+
import com.typesafe.config.Config
2122

2223
object NoopAuthenticator extends Authenticator {
2324
override def apply(req: HttpRequest): Boolean = true
25+
26+
override def init(config: Config): Unit = ()
27+
28+
override def close(): Unit = ()
2429
}

kinesis/src/main/scala/com/expedia/www/haystack/kinesis/span/collector/config/ProjectConfiguration.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import scala.concurrent.duration._
2828

2929
object ProjectConfiguration {
3030

31-
private val config = ConfigurationLoader.loadAppConfig
31+
private val config = ConfigurationLoader.loadConfigFileWithEnvOverrides()
3232

3333
def healthStatusFile(): Option[String] = if(config.hasPath("health.status.path")) Some(config.getString("health.status.path")) else None
3434

0 commit comments

Comments
 (0)