Skip to content

Commit

Permalink
Use picocli. Static link Linux binaries.
Browse files Browse the repository at this point in the history
  • Loading branch information
jordeu committed Oct 18, 2021
1 parent 8183ff8 commit 3bda333
Show file tree
Hide file tree
Showing 11 changed files with 159 additions and 31 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
linux-image:
name: Linux
runs-on: ubuntu-latest
container: debian:stretch
container: public.ecr.aws/seqera-labs/graalvm-static:latest
timeout-minutes: 90

steps:
Expand Down Expand Up @@ -49,7 +49,7 @@ jobs:
path: build/reports/tests/test/

- name: Build Native Image
run: ./gradlew nativeBuild
run: ./gradlew nativeCompile

- name: Upload linux native image artifact
uses: actions/upload-artifact@v2
Expand Down Expand Up @@ -92,7 +92,7 @@ jobs:
path: build/reports/tests/test/

- name: Build Native Image
run: ./gradlew nativeBuild
run: ./gradlew nativeCompile

- name: Upload Mac native image artifact
uses: actions/upload-artifact@v2
Expand Down Expand Up @@ -141,7 +141,7 @@ jobs:
path: build/reports/tests/test/

- name: Build Native Image
run: ./gradlew nativeBuild
run: ./gradlew nativeCompile

- name: Upload Windows native image artifact
uses: actions/upload-artifact@v2
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ out/
.settings
.classpath
.factorypath
**/build-info.properties
1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.1
1 change: 1 addition & 0 deletions VERSION-API
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.6
34 changes: 32 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ def junitVersion = providers.gradleProperty('junit.jupiter.version')
.get()

dependencies {
annotationProcessor("info.picocli:picocli-codegen")
annotationProcessor("io.micronaut:micronaut-http-validation")
annotationProcessor("io.micronaut:micronaut-inject-java")
annotationProcessor("io.micronaut:micronaut-graal")
implementation("io.micronaut:micronaut-http-client")
implementation("io.micronaut:micronaut-runtime")
implementation("io.micronaut.picocli:micronaut-picocli")
implementation("io.micronaut.rxjava2:micronaut-rxjava2")
implementation("io.micronaut.rxjava2:micronaut-rxjava2-http-client")
implementation("info.picocli:picocli")
implementation("javax.annotation:javax.annotation-api")
implementation("javax.inject:javax.inject:1")
runtimeOnly("ch.qos.logback:logback-classic")
Expand All @@ -46,21 +49,46 @@ application {
mainClass.set("io.seqera.tower.agent.Agent")
}

java {
String gitVersion() {
def p = new ProcessBuilder().command('sh', '-c', 'git rev-parse --short HEAD').start()
def r = p.waitFor()
return r == 0 ? p.text.trim() : '(unknown)'
}

task buildInfo {
doLast {
def version = rootProject.file('VERSION').text.trim()
def versionApi = rootProject.file('VERSION-API').text.trim()
def commitId = gitVersion().trim()
def info = """\
version=${version}
versionApi=${versionApi}
commitId=${commitId}
""".stripIndent().toString()
def f = file("src/main/resources/META-INF/build-info.properties")
f.parentFile.mkdirs()
f.text = info
}
}

compileJava {
sourceCompatibility = JavaVersion.toVersion("11")
targetCompatibility = JavaVersion.toVersion("11")
options.compilerArgs += ["-Aproject=${project.name}"]
dependsOn buildInfo
}

test {
useJUnitPlatform()
}


graalvmNative {
binaries {
main {
imageName = 'towr-agent'
mainClass = 'io.seqera.tower.agent.Agent'
buildArgs.add('--static')
buildArgs.add('--libc=musl')
}

test {
Expand All @@ -70,3 +98,5 @@ graalvmNative {
}
}



40 changes: 40 additions & 0 deletions graalvm-static/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
FROM debian:bullseye
RUN DEBIAN_FRONTEND=noninteractive apt update && apt install --assume-yes --no-install-recommends build-essential zlib1g-dev wget ca-certificates && rm -rf /var/lib/apt/lists/*
RUN mkdir /libs
RUN mkdir /downloads

# Musl
RUN cd /downloads && wget https://musl.libc.org/releases/musl-1.2.2.tar.gz && tar xvzf musl-1.2.2.tar.gz && rm musl-1.2.2.tar.gz
RUN cd /downloads/musl-1.2.2 && ./configure --disable-shared --prefix=/libs && make && make install

# zlib
ENV PATH="/libs/bin:${PATH}"
RUN cd /downloads && wget https://zlib.net/zlib-1.2.11.tar.gz && tar xvzf zlib-1.2.11.tar.gz && rm zlib-1.2.11.tar.gz
RUN cd /downloads/zlib-1.2.11 && CC=musl-gcc ./configure --static --prefix=/libs && make && make install

# libstdc++
RUN cp /usr/lib/gcc/x86_64-linux-gnu/10/libstdc++.a /libs/lib/


#
# tar xvzf musl-1.2.2.tar.gz
# cd musl-1.2.2

# ./configure --disable-shared --prefix=/libs
# make
# make install

# cd ..
# wget https://zlib.net/zlib-1.2.11.tar.gz
# tar xvzf zlib-1.2.11.tar.gz
# cd zlib-1.2.11

# export PATH=/libs/bin:$PATH
# export CC=musl-gcc
# ./configure --static --prefix=/libs

# cp /usr/lib/gcc/x86_64-linux-gnu/10/libstdc++.a /libs/lib/

# apt install wget
# wget https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-21.2.0/graalvm-ce-java11-linux-amd64-21.2.0.tar.gz
# tar xvzf
2 changes: 1 addition & 1 deletion micronaut-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ defaultPackage: io.seqera.tower.agent
testFramework: junit
sourceLanguage: java
buildTool: gradle
features: [annotation-api, app-name, graalvm, gradle, http-client, java, java-application, junit, logback, netty-server, readme, shade, yaml]
features: [annotation-api, app-name, graalvm, gradle, http-client, java, java-application, junit, logback, netty-server, picocli, picocli-java-application, picocli-junit, readme, shade, yaml]
79 changes: 56 additions & 23 deletions src/main/java/io/seqera/tower/agent/Agent.java
Original file line number Diff line number Diff line change
@@ -1,48 +1,81 @@
package io.seqera.tower.agent;

import io.micronaut.configuration.picocli.PicocliRunner;
import io.micronaut.context.ApplicationContext;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.MutableHttpRequest;
import io.micronaut.rxjava2.http.client.websockets.RxWebSocketClient;
import io.micronaut.scheduling.TaskScheduler;
import io.micronaut.websocket.exceptions.WebSocketClientException;
import io.seqera.tower.agent.exchange.CommandResponse;

import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.util.Map;

public class Agent {
import io.seqera.tower.agent.utils.VersionProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import picocli.CommandLine.Command;

@Command(
name = "towr-agent",
description = "Nextflow Tower anywhere agent",
headerHeading = "%n",
versionProvider = VersionProvider.class,
mixinStandardHelpOptions = true,
sortOptions = false,
abbreviateSynopsis = true,
descriptionHeading = "%n",
commandListHeading = "%nCommands:%n",
requiredOptionMarker = '*',
usageHelpWidth = 160,
parameterListHeading = "%nParameters:%n",
optionListHeading = "%nOptions:%n"
)
public class Agent implements Runnable {
private static Logger logger = LoggerFactory.getLogger(Agent.class);
private ApplicationContext ctx;
private Map<String,String> env;
private String hostName;
private String accessToken;
private String towerUrl;

@Parameters(index = "0", paramLabel = "AGENT_KEY", description = "Agent key to identify this agent", arity = "1")
String agentKey;

@Option(names = {"-t", "--access-token"}, description = "Tower personal access token (TOWER_ACCESS_TOKEN)", defaultValue = "${TOWER_ACCESS_TOKEN}")
String token;

@Option(names = {"-u", "--url"}, description = "Tower server API endpoint URL. Defaults to tower.nf (TOWER_API_ENDPOINT)", defaultValue = "${TOWER_API_ENDPOINT:-https://api.tower.nf}", required = true)
public String url;

private AgentClientSocket agentClient;

Agent() {
env = System.getenv();
ctx = ApplicationContext.run();
}

public void run() throws Exception {
hostName = env.get("TOWER_AGENT_HOSTNAME");
if (hostName == null) throw new IllegalStateException("TOWER_AGENT_HOSTNAME not set");

accessToken = env.get("TOWER_ACCESS_TOKEN");
if (accessToken == null) throw new IllegalStateException("TOWER_ACCESS_TOKEN not set");

towerUrl = env.get("TOWER_API_ENDPOINT");
if (towerUrl == null) throw new IllegalStateException("TOWER_API_ENDPOINT not set");
@Override
public void run() {

final URI uri = new URI(towerUrl + "/agent/" + hostName + "/connect");
final MutableHttpRequest<?> req = HttpRequest.GET(uri).bearerAuth(accessToken);
final RxWebSocketClient webSocketClient = ctx.getBean(RxWebSocketClient.class);
agentClient = webSocketClient.connect(AgentClientSocket.class, req).blockingFirst();
final URI uri;
try {
uri = new URI(url + "/agent/" + agentKey + "/connect");
final MutableHttpRequest<?> req = HttpRequest.GET(uri).bearerAuth(token);
final RxWebSocketClient webSocketClient = ctx.getBean(RxWebSocketClient.class);
agentClient = webSocketClient.connect(AgentClientSocket.class, req).blockingFirst();
logger.info("Connected");

System.out.println("Connected");
sendPeriodicHeartbeat();
sendPeriodicHeartbeat();
} catch (URISyntaxException e) {
logger.error(String.format("Invalid URI: %s/agent/%s/connect - %s", url, agentKey, e.getMessage()));
System.exit(-1);
} catch (WebSocketClientException e) {
logger.error(String.format("Connection error - %s", e.getMessage()));
System.exit(-1);
} catch (Throwable e) {
e.printStackTrace();
System.exit(-1);
}
}

/**
Expand All @@ -58,6 +91,6 @@ private void sendPeriodicHeartbeat() {
}

public static void main(String[] args) throws Exception {
new Agent().run();
PicocliRunner.run(Agent.class, args);
}
}
4 changes: 3 additions & 1 deletion src/main/java/io/seqera/tower/agent/AgentClientSocket.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,9 @@ void onMessage(CommandRequest message) {

@OnClose
void onClose() {
System.out.println("Closed after " + Duration.between(openingTime, Instant.now()));
if (openingTime != null) {
System.out.println("Closed after " + Duration.between(openingTime, Instant.now()));
}
}

abstract void send(CommandResponse response);
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/io/seqera/tower/agent/utils/VersionProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.seqera.tower.agent.utils;

import picocli.CommandLine;

import java.util.Properties;

public class VersionProvider implements CommandLine.IVersionProvider {

@Override
public String[] getVersion() throws Exception {
Properties properties = new Properties();
properties.load(this.getClass().getResourceAsStream("/META-INF/build-info.properties"));
return new String[]{String.format("@|yellow Tower Agent version %s (build %s)|@", properties.get("version"), properties.get("commitId"))};
}
}
5 changes: 5 additions & 0 deletions towr-agent
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash


bash -c "./gradlew run -q --args=\"${*@Q}\""

0 comments on commit 3bda333

Please sign in to comment.