Skip to content

Commit

Permalink
Merge pull request #425 from cqse/ts/38049_config_file_env_variable
Browse files Browse the repository at this point in the history
read config file path from TEAMSCALE_JAVA_PROFILER_CONFIG_FILE
  • Loading branch information
karottenreibe committed Apr 4, 2024
2 parents d6cca42 + 6557f46 commit ccf0aa5
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 27 deletions.
1 change: 0 additions & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@ We use [semantic versioning](http://semver.org/):
- PATCH version when you make backwards compatible bug fixes.

# Next Release
- [feature] Read configuration file path from `TEAMSCALE_JAVA_PROFILER_CONFIG_FILE` environment variable
- [feature] add installer for Windows
- [feature] Docker: agent copies itself to `/transfer` if this is mounted into the container
- [fix] Disable warning about proxy port not being correct when no proxy port was set at all
Expand Down
5 changes: 3 additions & 2 deletions agent/README.md
Expand Up @@ -51,8 +51,9 @@ The following options are available:
- `out` (optional): the path to a writable directory where the generated coverage XML files will be stored. (For details
see path format section below). Defaults to the subdirectory `coverage` inside the agent's installation directory.
- `config-file` (optional): a file which contains one or more of the previously named options as `key=value` entries
which are separated by line breaks. The file may also contain comments starting with `#`. (For details see path format
section below)
which are separated by line breaks. The file may also contain comments starting with `#`. For details see path format
section below.
Alternatively you can also set the `TEAMSCALE_JAVA_PROFILER_CONFIG_FILE` environment variable to that value.
- `config-id` (optional): a profiler configuration ID as defined in Teamscale. This allows to centrally manage the
profiler configuration in Teamscale's UI (under Project Configuration > Profilers since Teamscale 9.4).
Alternatively you can also set the `TEAMSCALE_JAVA_PROFILER_CONFIG_ID` environment variable to that value.
Expand Down
22 changes: 15 additions & 7 deletions agent/src/main/java/com/teamscale/jacoco/agent/PreMain.java
Expand Up @@ -42,9 +42,13 @@ public class PreMain {
*/
private static final String LOCKING_SYSTEM_PROPERTY = "TEAMSCALE_JAVA_PROFILER_ATTACHED";

/** Environment variable from which to read the config file to use. */
/** Environment variable from which to read the config ID to use.
* This is an ID for a profiler configuration that is stored in Teamscale. */
private static final String CONFIG_ID_ENVIRONMENT_VARIABLE = "TEAMSCALE_JAVA_PROFILER_CONFIG_ID";

/** Environment variable from which to read the config file to use. */
private static final String CONFIG_FILE_ENVIRONMENT_VARIABLE = "TEAMSCALE_JAVA_PROFILER_CONFIG_FILE";

/**
* Entry point for the agent, called by the JVM.
*/
Expand All @@ -55,15 +59,16 @@ public static void premain(String options, Instrumentation instrumentation) thro
System.setProperty(LOCKING_SYSTEM_PROPERTY, "true");

String environmentConfigId = System.getenv(CONFIG_ID_ENVIRONMENT_VARIABLE);
if (StringUtils.isEmpty(options) && environmentConfigId == null) {
String environmentConfigFile = System.getenv(CONFIG_FILE_ENVIRONMENT_VARIABLE);
if (StringUtils.isEmpty(options) && environmentConfigId == null && environmentConfigFile == null) {
// profiler was registered globally and no config was set explicitly by the user, thus ignore this process
// and don't profile anything
return;
}

AgentOptions agentOptions;
try {
agentOptions = getAndApplyAgentOptions(options, environmentConfigId);
agentOptions = getAndApplyAgentOptions(options, environmentConfigId, environmentConfigFile);
} catch (AgentOptionReceiveException e) {
// When Teamscale is not available, we don't want to fail hard to still allow for testing even if no
// coverage is collected (see TS-33237)
Expand All @@ -85,8 +90,9 @@ public static void premain(String options, Instrumentation instrumentation) thro
}

@NotNull
private static AgentOptions getAndApplyAgentOptions(String options,
String environmentConfigId) throws AgentOptionParseException, IOException, AgentOptionReceiveException {
private static AgentOptions getAndApplyAgentOptions(String options, String environmentConfigId,
String environmentConfigFile) throws AgentOptionParseException, IOException, AgentOptionReceiveException {

DelayedLogger delayedLogger = new DelayedLogger();
List<String> javaAgents = CollectionUtils.filter(ManagementFactory.getRuntimeMXBean().getInputArguments(),
s -> s.contains("-javaagent"));
Expand All @@ -103,7 +109,7 @@ private static AgentOptions getAndApplyAgentOptions(String options,
}
AgentOptions agentOptions;
try {
agentOptions = AgentOptionsParser.parse(options, environmentConfigId, credentials, delayedLogger);
agentOptions = AgentOptionsParser.parse(options, environmentConfigId, environmentConfigFile, credentials, delayedLogger);
} catch (AgentOptionParseException e) {
try (LoggingUtils.LoggingResources ignored = initializeFallbackLogging(options, delayedLogger)) {
delayedLogger.errorAndStdErr("Failed to parse agent options: " + e.getMessage(), e);
Expand All @@ -112,7 +118,9 @@ private static AgentOptions getAndApplyAgentOptions(String options,
}
} catch (AgentOptionReceiveException e) {
try (LoggingUtils.LoggingResources ignored = initializeFallbackLogging(options, delayedLogger)) {
delayedLogger.errorAndStdErr( e.getMessage() + " The application should start up normally, but NO coverage will be collected!", e);
delayedLogger.errorAndStdErr(
e.getMessage() + " The application should start up normally, but NO coverage will be collected!",
e);
attemptLogAndThrow(delayedLogger);
throw e;
}
Expand Down
Expand Up @@ -50,27 +50,30 @@ public class AgentOptionsParser {
private final FilePatternResolver filePatternResolver;
private final TeamscaleConfig teamscaleConfig;
private final String environmentConfigId;
private final String environmentConfigFile;
private final TeamscaleCredentials credentials;

/**
* Parses the given command-line options.
*
* @param environmentConfigId The Profiler configuration ID given via the
* {@link com.teamscale.jacoco.agent.PreMain#CONFIG_ID_ENVIRONMENT_VARIABLE} environment
* variable.
* @param environmentConfigId The Profiler configuration ID given via an environment variable.
* @param environmentConfigFile The Profiler configuration file given via an environment variable.
*/
public static AgentOptions parse(String optionsString, String environmentConfigId,
public static AgentOptions parse(String optionsString, String environmentConfigId, String environmentConfigFile,
TeamscaleCredentials credentials,
ILogger logger) throws AgentOptionParseException, AgentOptionReceiveException {
return new AgentOptionsParser(logger, environmentConfigId, credentials).parse(optionsString);
return new AgentOptionsParser(logger, environmentConfigId, environmentConfigFile, credentials).parse(
optionsString);
}

@VisibleForTesting
AgentOptionsParser(ILogger logger, String environmentConfigId, TeamscaleCredentials credentials) {
AgentOptionsParser(ILogger logger, String environmentConfigId, String environmentConfigFile,
TeamscaleCredentials credentials) {
this.logger = logger;
this.filePatternResolver = new FilePatternResolver(logger);
this.teamscaleConfig = new TeamscaleConfig(logger, filePatternResolver);
this.environmentConfigId = environmentConfigId;
this.environmentConfigFile = environmentConfigFile;
this.credentials = credentials;
}

Expand Down Expand Up @@ -99,9 +102,7 @@ public static AgentOptions parse(String optionsString, String environmentConfigI
}
}

if (environmentConfigId != null) {
handleOption(options, "config-id=" + environmentConfigId);
}
handleConfigFromEnvironment(options);

Validator validator = options.getValidator();
if (!validator.isValid()) {
Expand All @@ -110,6 +111,23 @@ public static AgentOptions parse(String optionsString, String environmentConfigI
return options;
}

private void handleConfigFromEnvironment(
AgentOptions options) throws AgentOptionParseException, AgentOptionReceiveException {
if (environmentConfigId != null) {
handleOption(options, "config-id=" + environmentConfigId);
}

if (environmentConfigFile != null) {
handleOption(options, "config-file=" + environmentConfigFile);
}

if (environmentConfigId != null && environmentConfigFile != null) {
logger.warn("You specified both an ID for a profiler configuration in Teamscale and a config file." +
" The config file will override the Teamscale configuration." +
" Please use one or the other.");
}
}

/**
* Parses and stores the given option in the format <code>key=value</code>.
*/
Expand Down Expand Up @@ -267,7 +285,8 @@ private void readConfigFromTeamscale(AgentOptions options,
options.teamscaleServer.userName,
options.teamscaleServer.userAccessToken);
options.configurationViaTeamscale = configuration;
logger.debug("Received the following options from Teamscale: " + configuration.getProfilerConfiguration().configurationOptions);
logger.debug(
"Received the following options from Teamscale: " + configuration.getProfilerConfiguration().configurationOptions);
readConfigFromString(options, configuration.getProfilerConfiguration().configurationOptions);
}

Expand Down
Expand Up @@ -15,6 +15,8 @@
import org.junit.jupiter.api.Test;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
Expand All @@ -25,13 +27,15 @@
public class AgentOptionsParserTest {

private TeamscaleCredentials teamscaleCredentials;
private final AgentOptionsParser parser = new AgentOptionsParser(new CommandLineLogger(), null, null);
private final AgentOptionsParser parser = new AgentOptionsParser(new CommandLineLogger(), null, null, null);
private Path configFile;
/** The mock server to run requests against. */
protected MockWebServer mockWebServer;

/** Starts the mock server. */
@BeforeEach
public void setup() throws Exception {
configFile = Paths.get(getClass().getResource("agent.properties").toURI());
mockWebServer = new MockWebServer();
mockWebServer.start();
teamscaleCredentials = new TeamscaleCredentials(mockWebServer.url("/"), "user", "key");
Expand Down Expand Up @@ -68,7 +72,7 @@ public void testUploadMethodRecognition() throws Exception {
@Test
public void testUploadMethodRecognitionWithTeamscaleProperties() throws Exception {
TeamscaleCredentials credentials = new TeamscaleCredentials(HttpUrl.get("http://localhost"), "user", "key");
AgentOptionsParser parser = new AgentOptionsParser(new CommandLineLogger(), null, credentials);
AgentOptionsParser parser = new AgentOptionsParser(new CommandLineLogger(), null, null, credentials);

assertThat(parser.parse(null).determineUploadMethod()).isEqualTo(AgentOptions.EUploadMethod.LOCAL_DISK);
assertThat(parser.parse("azure-url=azure.com,azure-key=key").determineUploadMethod()).isEqualTo(
Expand All @@ -91,20 +95,44 @@ public void testUploadMethodRecognitionWithTeamscaleProperties() throws Exceptio
}

@Test
public void environmentConfigOverridesCommandLineOptions() throws Exception {
public void environmentConfigIdOverridesCommandLineOptions() throws Exception {
ProfilerRegistration registration = new ProfilerRegistration();
registration.profilerId = UUID.randomUUID().toString();
registration.profilerConfiguration = new ProfilerConfiguration();
registration.profilerConfiguration.configurationId = "my-config";
registration.profilerConfiguration.configurationOptions = "teamscale-partition=foo";
mockWebServer.enqueue(new MockResponse().setBody(JsonUtils.serialize(registration)));
AgentOptionsParser parser = new AgentOptionsParser(new CommandLineLogger(), "my-config",
teamscaleCredentials);
null, teamscaleCredentials);
AgentOptions options = parser.parse("teamscale-partition=bar");

assertThat(options.teamscaleServer.partition).isEqualTo("foo");
}

@Test
public void environmentConfigFileOverridesCommandLineOptions() throws Exception {
AgentOptionsParser parser = new AgentOptionsParser(new CommandLineLogger(), null, configFile.toString(),
teamscaleCredentials);
AgentOptions options = parser.parse("teamscale-partition=from-command-line");

assertThat(options.teamscaleServer.partition).isEqualTo("from-config-file");
}

@Test
public void environmentConfigFileOverridesConfigId() throws Exception {
ProfilerRegistration registration = new ProfilerRegistration();
registration.profilerId = UUID.randomUUID().toString();
registration.profilerConfiguration = new ProfilerConfiguration();
registration.profilerConfiguration.configurationId = "my-config";
registration.profilerConfiguration.configurationOptions = "teamscale-partition=from-config-id";
mockWebServer.enqueue(new MockResponse().setBody(JsonUtils.serialize(registration)));
AgentOptionsParser parser = new AgentOptionsParser(new CommandLineLogger(), "my-config", configFile.toString(),
teamscaleCredentials);
AgentOptions options = parser.parse("teamscale-partition=from-command-line");

assertThat(options.teamscaleServer.partition).isEqualTo("from-config-file");
}

@Test
public void notAllRequiredTeamscaleOptionsSet() {
assertThatCode(
Expand Down Expand Up @@ -183,7 +211,8 @@ public void revisionOrCommitRequireProject() {
public void environmentConfigIdDoesNotExist() {
mockWebServer.enqueue(new MockResponse().setResponseCode(404).setBody("invalid-config-id does not exist"));
assertThatThrownBy(
() -> new AgentOptionsParser(new CommandLineLogger(), "invalid-config-id", teamscaleCredentials).parse(
() -> new AgentOptionsParser(new CommandLineLogger(), "invalid-config-id", null,
teamscaleCredentials).parse(
"")
).isInstanceOf(AgentOptionParseException.class).hasMessageContaining("invalid-config-id does not exist");
}
Expand All @@ -203,7 +232,7 @@ public void mustPreserveDefaultExcludes() throws Exception {

@Test
public void teamscalePropertiesCredentialsUsedAsDefaultButOverridable() throws Exception {
AgentOptionsParser parser = new AgentOptionsParser(new CommandLineLogger(), null, teamscaleCredentials);
AgentOptionsParser parser = new AgentOptionsParser(new CommandLineLogger(), null, null, teamscaleCredentials);

assertThat(parser.parse("teamscale-project=p,teamscale-partition=p").teamscaleServer.userName).isEqualTo(
"user");
Expand Down
Expand Up @@ -347,7 +347,7 @@ private static Predicate<String> excludeFilter(String filterString) throws Excep
}

private static AgentOptionsParser getAgentOptionsParserWithDummyLogger() {
return new AgentOptionsParser(new CommandLineLogger(), null, null);
return new AgentOptionsParser(new CommandLineLogger(), null, null, null);
}

/**
Expand Down
@@ -0,0 +1 @@
teamscale-partition=from-config-file

0 comments on commit ccf0aa5

Please sign in to comment.