Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Environment variables in HOCON files will not be resolved in some cases #397

Open
luedi opened this issue Dec 11, 2023 · 0 comments
Open

Comments

@luedi
Copy link

luedi commented Dec 11, 2023

I'm using the standard Kotlin application.conf behavior to use environment variables to override config values. So my HOCON file looks like this (simplified):

{
  prod = {
    host = "myprodhost:myport"
    host = ${?FIXED_PREFIX_MY_PROD_HOST}
  }
  dev = {
    host = "mydevhost:mydevport"
    host = ${?FIXED_PREFIX_MY_DEV_HOST}
  }
  anotherProperty = "stage_independent"
  anotherProperty = ${?FIXED_PREFIX_ANOTHER_PROPE>RTY}
}

Then I implemented a configuration class which is (very simplified) like this:

enum class Stage {
    DEV,
    PROD
}

// this interface is needed to be able to process the configuration
sealed interface Configuration

data class StageConfig(
    val host: String,
) : Configuration

object HostConfiguration {

    data class HostConfig(
        val stages: Map<Stage, StageConfig>,
        val anotherProperty: String,
    )

    private val hostConfig: HostConfig

    init {
        // by the way: how can i avoid this and read the nested configuration direct into HostConfig???
        data class InternalHostConfig(
            val prod: Configuration,
            val dev: Configuration,
            val anotherProperty: String,
        )

        val config = ConfigLoaderBuilder.default()
            .addPropertySource(PropertySource.resource("/host.conf"))
            .build()
            .loadConfigOrThrow<InternalHostConfig>()

        hostConfig = HostConfig(
            mapOf(
                Stage.PROD to (config.prod as StageConfig),
                Stage.DEV to (config.dev as StageConfig)
            ),
            config.anoherProperty,
        )
    }

    fun stageConfig(stage: Stage): StageConfig = hostConfig.stages[stage]!!

    fun anotherProperty() = hostConfig.anotherProperty
}

At least I wrote some unit tests for my configuration with the aid of Kotest. Kotest has a feature to inject environment variables into a test (which works fine). This tests runs successful when starting them from the IntelliJ editor pane but are failing when running them in a Gradle build or with the Kotest runner. In this cases config.host contains "myprodhost:myport". Here is the, again very simpolified code:

class HostConfigurationKtTest : FunSpec({

    test("Environment variables should be used when present") {
        withEnvironment(
            mapof(
                "FIXED_PREFIX_MY_PROD_HOST" to "host_url_prod",
            )
        ) {
            // This is successful
            System.getenv("FIXED_PREFIX_MY_PROD_HOST") shouldBe "host_url_prod"

            val prodConfig = HostConfiguration.stageConfig(Stage.PROD)
            // This will fail
            prodConfig.host shouldBe "broker_url_prod"
        }
    }
})

I've read about the EnvironmentVariablesOverridePropertySource but i can't prefix environment variables with config.override. In our production environment the environment variables are generated by a tool which follows a specific naming convention which i can't override. For example, if i declare a variable MY_HOST in the tool, a prefix FIXED_PREFIX is automatically added.

Interesting, when I change my code like you do in the EnvironmentVariablesOverridePropertySourceTest the test fails always.

I used hopelite because my real configuration is much more complicated and the library helps me to save a lot of work in opposite to use the standard application.conf.

Before I forget, my environment is:

IntelliJ IDEA Ultimate 2023.3
Kotlin 1.9.21 targeting Java 21
Kotest 5.8.0
hopelite 2.7.5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant