Skip to content
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,15 @@ tw CLI is a platform binary executable created by a native compilation from Java
./gradlew runReflectionConfigGenerator
```

The GraalVM reflection metadata generation relies on code paths being executed with [a tracing agent](https://www.graalvm.org/jdk21/reference-manual/native-image/metadata/AutomaticMetadataCollection/#tracing-agent) to dynamically capture various execution paths and update the reflect-config.json.
The GraalVM reflection metadata generation relies on code paths being executed with the [Tracing Agent](https://www.graalvm.org/jdk21/reference-manual/native-image/metadata/AutomaticMetadataCollection/#tracing-agent) to dynamically
capture various execution paths and update the `reflect-config.json` and `resource-config.json` files. This is a utility task that uses reflection to make calls to all classes in the Tower SDK package in order to capture and generate
methods reflection metadata of any new fields/classes in Tower SDK.

This is a utility task that uses reflection to make calls to all classes in the Tower SDK package,
in order to capture and generate methods reflection metadata of any new fields/classes in Tower SDK and update resource-config.json
Additionally, for more complete coverage, run the test suite with the Tracing Agent enabled:

```bash
./gradlew test -Dtracing-agent=true
```

1. Create the native client:

Expand Down
9 changes: 7 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,13 @@ repositories {
}

dependencies {
implementation 'javax.activation:activation:1.1.1'
implementation 'javax.annotation:javax.annotation-api:1.3.2'
implementation 'jakarta.annotation:jakarta.annotation-api:3.0.0'
implementation 'org.slf4j:slf4j-api:1.7.36'
implementation 'ch.qos.logback:logback-core:1.5.19'
implementation 'ch.qos.logback:logback-classic:1.5.19'

implementation 'io.seqera.tower:tower-java-sdk:1.43.2'
implementation 'io.seqera.tower:tower-java-sdk:1.97.0'
// Upgrade transitive Jersey client dependencies to non-vulnerable 2.x version
implementation "org.glassfish.jersey.core:jersey-client:2.47"
implementation "org.glassfish.jersey.media:jersey-media-multipart:2.47"
Expand Down Expand Up @@ -119,6 +120,10 @@ test {
// Use junit platform for unit tests
useJUnitPlatform()
dependsOn checkLicenses
// Run tests with GraalVM Tracing Agent enabled
if (System.getProperty('tracing-agent')?.toBoolean()) {
jvmArgs = ["-agentlib:native-image-agent=access-filter-file=conf/access-filter-file.json,config-merge-dir=conf/"]
}
}

graalvmNative {
Expand Down
595 changes: 489 additions & 106 deletions conf/reflect-config.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/main/java/io/seqera/tower/cli/Tower.java
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ protected static CommandLine buildCommandLine() {
Tower app = new Tower();
CommandLine cmd = new CommandLine(app);
cmd.setUsageHelpLongOptionsMaxWidth(40);
cmd.setCaseInsensitiveEnumValuesAllowed(true);
return cmd;
}

Expand Down
23 changes: 17 additions & 6 deletions src/main/java/io/seqera/tower/cli/commands/AbstractApiCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@
import io.seqera.tower.model.OrgAndWorkspaceDto;
import io.seqera.tower.model.PipelineDbDto;
import io.seqera.tower.model.PipelineQueryAttribute;
import io.seqera.tower.model.UserDbDto;
import io.seqera.tower.model.UserResponseDto;
import io.seqera.tower.model.WorkflowQueryAttribute;
import org.glassfish.jersey.CommonProperties;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.media.multipart.BodyPart;
Expand Down Expand Up @@ -272,17 +273,27 @@ protected Properties getCliProperties() throws ApiException {

private ApiClient buildApiClient() {
return new ApiClient() {

@Override
public ClientConfig getDefaultClientConfig() {
ClientConfig config = super.getDefaultClientConfig();
// Disable conditionally enabled providers, as warnings get printed when their corresponding classes are not available in the classpath
config.property(CommonProperties.PROVIDER_DEFAULT_DISABLE, "ALL");

return config;
}

@Override
protected void performAdditionalClientConfiguration(ClientConfig clientConfig) {
protected void applyDebugSetting(ClientConfig clientConfig) {
if (app().verbose) {
clientConfig.register(new LoggingFeature(Logger.getLogger(LoggingFeature.DEFAULT_LOGGER_NAME), java.util.logging.Level.INFO, LoggingFeature.Verbosity.PAYLOAD_ANY, 1024 * 50 /* Log payloads up to 50K */));
clientConfig.property(LoggingFeature.LOGGING_FEATURE_VERBOSITY, LoggingFeature.Verbosity.PAYLOAD_ANY);
}
}

@Override
public Entity<?> serialize(Object obj, Map<String, Object> formParams, String contentType) throws ApiException {
Entity<?> entity = super.serialize(obj, formParams, contentType);
public Entity<?> serialize(Object obj, Map<String, Object> formParams, String contentType, boolean isBodyNullable) throws ApiException {
Entity<?> entity = super.serialize(obj, formParams, contentType, isBodyNullable);

// Current SDK sends all multipart files as 'application/octet-stream'
// this is a workaround to try to automatically detect the correct
Expand Down Expand Up @@ -500,7 +511,7 @@ protected List<Long> findOrCreateLabels(Long wspId, List<Label> labels) throws A
}

private void loadUser() throws ApiException {
UserDbDto user = usersApi().userInfo().getUser();
UserResponseDto user = usersApi().userInfo().getUser();
userName = user.getUserName();
userId = user.getId();
}
Expand Down Expand Up @@ -538,7 +549,7 @@ private void loadAvailableComputeEnvs(Long workspaceId) throws ApiException {
if (availableComputeEnvsNameToId == null) {
availableComputeEnvsNameToId = new HashMap<>();
availableComputeEnvsIdToName = new HashMap<>();
for (ListComputeEnvsResponseEntry ce : computeEnvsApi().listComputeEnvs("AVAILABLE", workspaceId).getComputeEnvs()) {
for (ListComputeEnvsResponseEntry ce : computeEnvsApi().listComputeEnvs("AVAILABLE", workspaceId, List.of()).getComputeEnvs()) {

if (ce.getPrimary() != null && ce.getPrimary()) {
primaryComputeEnvId = ce.getId();
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/seqera/tower/cli/commands/LaunchCmd.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@
import io.seqera.tower.model.SubmitWorkflowLaunchResponse;
import io.seqera.tower.model.WorkflowLaunchRequest;
import io.seqera.tower.model.WorkflowStatus;
import jakarta.annotation.Nullable;
import picocli.CommandLine;
import picocli.CommandLine.ArgGroup;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;

import javax.annotation.Nullable;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collections;
Expand Down Expand Up @@ -156,7 +156,7 @@ private WorkflowLaunchRequest updateLaunchRequest(WorkflowLaunchRequest base) th

protected Response runTowerPipeline(Long wspId) throws ApiException, IOException {

ListPipelinesResponse pipelines = pipelinesApi().listPipelines(Collections.emptyList(), wspId, 50, 0, pipeline, "all");
ListPipelinesResponse pipelines = pipelinesApi().listPipelines(Collections.emptyList(), wspId, 50, 0, null, null, pipeline, "all");
if (pipelines.getTotalSize() == 0) {
throw new InvalidResponseException(String.format("Pipeline '%s' not found on this workspace.", pipeline));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,9 @@
import io.seqera.tower.cli.responses.actions.ActionsList;
import io.seqera.tower.model.ActionQueryAttribute;
import io.seqera.tower.model.ListActionsResponse;
import io.seqera.tower.model.ListLabelsResponse;
import picocli.CommandLine;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

@CommandLine.Command(
Expand All @@ -47,7 +45,7 @@ public class ListCmd extends AbstractActionsCmd {
protected Response exec() throws ApiException, IOException {
Long wspId = workspaceId(workspace.workspace);

List<ActionQueryAttribute> actionQueryAttributes = showLabelsOption.showLabels ? List.of(ActionQueryAttribute.LABELS) : NO_ACTION_ATTRIBUTES;
List<ActionQueryAttribute> actionQueryAttributes = showLabelsOption.showLabels ? List.of(ActionQueryAttribute.labels) : NO_ACTION_ATTRIBUTES;

ListActionsResponse response = actionsApi().listActions(wspId, actionQueryAttributes);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ protected Response exec() throws ApiException, IOException {
DescribeActionResponse response = fetchDescribeActionResponse(
actionRefOptions,
wspId,
ActionQueryAttribute.LABELS
ActionQueryAttribute.labels
);

return new ActionsView(response.getAction(), baseWorkspaceUrl(wspId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
public class AddGitHubCmd extends AbstractAddCmd {
@Override
protected ActionSource getSource() {
return ActionSource.GITHUB;
return ActionSource.github;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
public class AddTowerCmd extends AbstractAddCmd {
@Override
protected ActionSource getSource() {
return ActionSource.TOWER;
return ActionSource.tower;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,18 +47,18 @@ protected ComputeEnvResponseDto fetchComputeEnv(ComputeEnvRefOptions computeEnvR

protected ComputeEnvResponseDto computeEnvByName(Long workspaceId, String name) throws ApiException {

List<ListComputeEnvsResponseEntry> computeEnvs = computeEnvsApi().listComputeEnvs(null, workspaceId).getComputeEnvs();
List<ListComputeEnvsResponseEntry> computeEnvs = computeEnvsApi().listComputeEnvs(null, workspaceId, List.of()).getComputeEnvs();
ListComputeEnvsResponseEntry entry = computeEnvs
.stream()
.filter(ce -> name.equals(ce.getName()))
.findFirst()
.orElseThrow(() -> new ComputeEnvNotFoundException(name, workspaceId));

return computeEnvsApi().describeComputeEnv(entry.getId(), workspaceId, List.of(ComputeEnvQueryAttribute.LABELS)).getComputeEnv();
return computeEnvsApi().describeComputeEnv(entry.getId(), workspaceId, List.of(ComputeEnvQueryAttribute.labels)).getComputeEnv();
}

private ComputeEnvResponseDto computeEnvById(Long workspaceId, String id) throws ApiException {
return computeEnvsApi().describeComputeEnv(id, workspaceId, List.of(ComputeEnvQueryAttribute.LABELS)).getComputeEnv();
return computeEnvsApi().describeComputeEnv(id, workspaceId, List.of(ComputeEnvQueryAttribute.labels)).getComputeEnv();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import picocli.CommandLine.Command;

import java.io.IOException;
import java.util.List;

@Command(
name = "list",
Expand All @@ -40,7 +41,7 @@ public class ListCmd extends AbstractComputeEnvCmd {
protected Response exec() throws ApiException, IOException {
Long wspId = workspaceId(workspace.workspace);

ListComputeEnvsResponse response = computeEnvsApi().listComputeEnvs(null, wspId);
ListComputeEnvsResponse response = computeEnvsApi().listComputeEnvs(null, wspId, List.of());
return new ComputeEnvList(workspaceRef(wspId), response.getComputeEnvs(), baseWorkspaceUrl(wspId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,6 @@ public AltairPbsComputeConfig computeConfig() throws ApiException, IOException {
AltairPbsComputeConfig config = new AltairPbsComputeConfig();

config
// Common
.environment(environmentVariables())
.workDir(workDir)
.preRunScript(preRunScriptString())
.postRunScript(postRunScriptString())
.nextflowConfig(nextflowConfigString())

// Main
.launchDir(launchDir)
.userName(userName)
Expand All @@ -79,6 +72,13 @@ public AltairPbsComputeConfig computeConfig() throws ApiException, IOException {
.maxQueueSize(adv().maxQueueSize)
.headJobOptions(adv().headJobOptions);

// Common
config.workDir(workDir)
.preRunScript(preRunScriptString())
.postRunScript(postRunScriptString())
.nextflowConfig(nextflowConfigString())
.environment(environmentVariables());

return config;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,28 +105,30 @@ public static void clean(AwsBatchConfig config) {

@Override
public AwsBatchConfig computeConfig() throws ApiException, IOException {
return new AwsBatchConfig()
.workDir(workDir)
.preRunScript(preRunScriptString())
.postRunScript(postRunScriptString())
.nextflowConfig(nextflowConfigString())
.environment(environmentVariables())
AwsBatchConfig config = new AwsBatchConfig()
.region(region)
.fusion2Enabled(isFusionV2Enabled())
.waveEnabled(wave)
.nvnmeStorageEnabled(fastStorage)
.fusionSnapshots(snapshots)

// Forge
.forge(buildForge())

// Advanced
.cliPath(adv().cliPath)
.executionRole(adv().batchExecutionRole)
.computeJobRole(adv().computeJobRole)
.headJobCpus(adv().headJobCpus)
.headJobMemoryMb(adv().headJobMemoryMb)
.headJobRole(adv().headJobRole);

// Common
config.workDir(workDir)
.preRunScript(preRunScriptString())
.postRunScript(postRunScriptString())
.nextflowConfig(nextflowConfigString())
.environment(environmentVariables());

return config;
}

private Boolean isFusionV2Enabled() throws TowerException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,12 @@ public static void clean(AwsBatchConfig config) {

@Override
public AwsBatchConfig computeConfig() throws IOException, ApiException {
return new AwsBatchConfig()
.workDir(workDir)
.preRunScript(preRunScriptString())
.postRunScript(postRunScriptString())
.nextflowConfig(nextflowConfigString())
.environment(environmentVariables())
AwsBatchConfig config = new AwsBatchConfig()
.region(region)
.fusion2Enabled(isFusionV2Enabled())
.waveEnabled(wave)
.nvnmeStorageEnabled(fastStorage)

.region(region)

// Queues
.headQueue(headQueue)
.computeQueue(computeQueue)
Expand All @@ -90,6 +84,15 @@ public AwsBatchConfig computeConfig() throws IOException, ApiException {
.headJobCpus(adv().headJobCpus)
.headJobMemoryMb(adv().headJobMemoryMb)
.headJobRole(adv().headJobRole);

// Common
config.workDir(workDir)
.preRunScript(preRunScriptString())
.postRunScript(postRunScriptString())
.nextflowConfig(nextflowConfigString())
.environment(environmentVariables());

return config;
}

private Boolean isFusionV2Enabled() throws TowerException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,17 @@ public AzBatchForgePlatform() {
public AzBatchConfig computeConfig(Long workspaceId, CredentialsApi credentialsApi) throws ApiException, IOException {
AzBatchConfig config = new AzBatchConfig();

config
// Common
.workDir(workDir)
config.fusion2Enabled(fusionV2)
.waveEnabled(wave)
.region(location);
// Common
config.workDir(workDir)
.preRunScript(preRunScriptString())
.postRunScript(postRunScriptString())
.nextflowConfig(nextflowConfigString())
.environment(environmentVariables())
.fusion2Enabled(fusionV2)
.waveEnabled(wave)
.region(location);
.environment(environmentVariables());



if (adv != null) {
config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,6 @@ public AzBatchConfig computeConfig() throws ApiException, IOException {
AzBatchConfig config = new AzBatchConfig();

config
// Common
.workDir(workDir)
.preRunScript(preRunScriptString())
.postRunScript(postRunScriptString())
.nextflowConfig(nextflowConfigString())
.environment(environmentVariables())
.fusion2Enabled(fusionV2)
.waveEnabled(wave)
.region(location)
Expand All @@ -72,6 +66,13 @@ public AzBatchConfig computeConfig() throws ApiException, IOException {
.tokenDuration(adv.tokenDuration);
}

// Common
config.workDir(workDir)
.preRunScript(preRunScriptString())
.postRunScript(postRunScriptString())
.nextflowConfig(nextflowConfigString())
.environment(environmentVariables());

return config;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,10 @@ public EksComputeConfig computeConfig() throws ApiException, IOException {
.computeServiceAccount(adv().computeAccount)
.podCleanup(adv().podCleanup)
.headPodSpec(FilesHelper.readString(adv().headPodSpec))
.servicePodSpec(FilesHelper.readString(adv().servicePodSpec))
.servicePodSpec(FilesHelper.readString(adv().servicePodSpec));

// Common
.workDir(workDir)

// Staging
// Common
config.workDir(workDir)
.preRunScript(preRunScriptString())
.postRunScript(postRunScriptString())
.nextflowConfig(nextflowConfigString())
Expand Down
Loading