Search code examples
gradlegradle-kotlin-dslopen-telemetry

generate start script that takes environment variables with gradle


I am using gradle installDist task to prepare the data I need to then copy in the Docker image.

Now, I need to integrate openTelemetry agent, so I need to attach it on the command line. To modify the startup script, I do something like that :

application {
    mainClass.set("com.vincent.AppKt")
    applicationDefaultJvmArgs = listOf("-javaagent:../lib/opentelemetry-javaagent-$openTelemetryJavaAgentVersion.jar")
}

Next, I need to configure there a property file that is different based on the environment (staging or prod), so I need to do something like this :

application {
    mainClass.set("com.vincent.AppKt")
    applicationDefaultJvmArgs = listOf("-javaagent:../lib/opentelemetry-javaagent-$openTelemetryJavaAgentVersion.jar",
"-Dotel.javaagent.configuration-file=../$ENV-otel.properties")
}

tasks {
register("replaceInInstallDist") {
    dependsOn("installDist")

    doLast {
        val buildDir = file("${buildDir}/install/${project.name}")
        
        val scriptFile = file("${buildDir}/bin/${project.name}")

        scriptFile.writeText(scriptFile.readText().replace("\\\$ENV", "\$ENV"))
    }
}
}

(the replacement thing is inspired by https://stackoverflow.com/a/29464373/3067542)

it kind of works, but things are not evaluated correctly at the right time.

The generated script contains what I expect :

DEFAULT_JVM_OPTS='"-javaagent:../lib/opentelemetry-javaagent-2.0.0.jar" "-Dotel.javaagent.configuration-file=../$ENV-otel.properties"'

but once in the container, when I start the app manually, logs show that it's not evaluated :

ERROR io.opentelemetry.javaagent.tooling.config.ConfigurationPropertiesSupplier - Configuration file "../$ENV-otel.properties" not found.

If I launch myself the application from the command line, then it seems to work :

java -jar myJar.jar -javaagent:../lib/opentelemetry-javaagent-2.0.0.jar -Dotel.javaagent.configuration-file=../$ENV-otel.properties

so it seems to be an issue with how the application is launched by the generated script. it uses :

exec "$JAVACMD" "$@"

the parameters that I need to be evaluated are in the $@ variable..

what can I do in gradle's application block (I guess it's there) so that my variable gets evaluated as expected, and $ENV is replaced by its value ?


Solution

  • Looks like it's by design since Gradle 7.2 : https://docs.gradle.org/7.2/userguide/upgrading_version_7.html#changes_7.2

    Security changes to application start scripts and Gradle wrapper scripts Due to CVE-2021-32751, gradle, gradlew and start scripts generated by Gradle’s application plugin have been updated to avoid situations where these scripts could be used for arbitrary code execution when an attacker is able to change environment variables.

    You can use the latest version of Gradle to generate a gradlew script and use it to execute an older version of Gradle.

    This should be a transparent for most users; however, there may be changes for Gradle builds that rely on the environment variables JAVA_OPTS or GRADLE_OPTS to pass parameters with complicated quote escaping. Contact us if you suspect something has broken your build and you cannot find a solution.

    So if we REALLY want to do it, I am not sure there's a cleaner version than the hack I did in https://stackoverflow.com/a/77909645/3067542

    in my specific case for opentelemetry, I ended up using OTEL_JAVAAGENT_CONFIGURATION_FILE env variable instead of passing it on command line, to avoid the hack that will be a pain to maintain.