Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .bin/agent-ice
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
PROJECT_ROOT=$(cd -- "$SCRIPT_DIR/.." && pwd)

shopt -s nullglob
jars=("$PROJECT_ROOT"/ice/target/ice-*-shaded.jar)
shopt -u nullglob
if (( ${#jars[@]} == 0 )); then
echo "agent-ice: no ice-*-shaded.jar in $PROJECT_ROOT/ice/target/." >&2
exit 1
fi
jar=$(ls -t "${jars[@]}" | head -n1)

exec "${JAVA_HOME:?JAVA_HOME not set — run inside direnv shell}/bin/java" \
-agentlib:native-image-agent=config-merge-dir=$PROJECT_ROOT/ice/src/main/resources/META-INF/native-image/com.altinity/ice,experimental-class-loader-support \
-jar "$jar" "$@"
31 changes: 30 additions & 1 deletion .github/workflows/verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ jobs:
- run: ./mvnw clean verify
- name: Install
run: ./mvnw install
# TODO: check native-image can build ice
- name: Run Scenario-Based Integration Tests
run: ../mvnw test -Dtest=ScenarioBasedIT
working-directory: ice-rest-catalog
Expand Down Expand Up @@ -57,3 +56,33 @@ jobs:
./mvnw -pl ice-rest-catalog failsafe:integration-test failsafe:verify
-Dit.test=DockerScenarioBasedIT
-Ddocker.image=altinity/ice-rest-catalog:debug-with-ice-latest-master-amd64
native-build:
name: Build and test native image (${{ matrix.arch }})
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- arch: amd64
runner: ubuntu-24.04
artifact: ice-native-amd64-dynamic
- arch: arm64
runner: ubuntu-24.04-arm
artifact: ice-native-arm64-dynamic
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'graalvm'
cache: maven
- name: Build native image (dynamic linking - no musl required)
run: ./mvnw -Pnative -pl ice clean install -Dmaven.test.skip=true
- name: Run Scenario-Based Integration Tests
run: ../mvnw test -Dtest=ScenarioBasedIT -Dice.native=true
working-directory: ice-rest-catalog
- name: Upload native binary as artifact
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.artifact }}
path: ice/target/ice
if-no-files-found: error
29 changes: 27 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,23 @@ Create/delete tables, insert data with `ice insert -p ns1.table1 file://example.

## Installation

Pre-built binaries\* (+ links to Docker images for [ice](https://hub.docker.com/r/altinity/ice) and [ice-rest-catalog](https://hub.docker.com/r/altinity/ice-rest-catalog)) are available from [GitHub Releases](https://github.com/Altinity/ice/releases) page.
> \* currently require `java` 21+ to run (available [here](https://adoptium.net/installation/)).
Pre-built binaries (+ links to Docker images for [ice](https://hub.docker.com/r/altinity/ice) and [ice-rest-catalog](https://hub.docker.com/r/altinity/ice-rest-catalog)) are available from [GitHub Releases](https://github.com/Altinity/ice/releases) page.

**Two types of binaries are available:**

1. **Java-based binaries** - Require Java 21+ to run (available [here](https://adoptium.net/installation/))
2. **Native binaries** - Standalone executables with no Java dependency
- `ice-native-amd64` - Dynamic binary for x86_64 Linux (requires glibc)
- `ice-native-arm64` - Dynamic binary for ARM64 Linux (requires glibc)

## Usage

See [examples/](examples/).

## Development

### Standard Build (Java-based)

Install [sdkman](https://sdkman.io/install), then

```shell
Expand All @@ -35,6 +43,23 @@ sdk env
./mvnw
```

### Native Image Build (Standalone)

Build standalone native binaries with no Java dependency:

```shell
# Install prerequisites
sdk env # or ensure Java 21+ and GraalVM are available

# Build ice binary (both amd64 and arm64)
mvn -Pnative -pl ice clean package -Dmaven.test.skip=true

# Docker builds
docker build -f ice/Dockerfile.native -t ice-native:amd64 .

docker build -f ice/Dockerfile.native -t ice-native:arm64 .
```

## License

Copyright (c) 2025, Altinity Inc and/or its affiliates. All rights reserved.
Expand Down
4 changes: 4 additions & 0 deletions ice-rest-catalog/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<jetcd.version>0.8.5</jetcd.version>
<grpc.version>1.70.0</grpc.version>
<testcontainers.version>1.21.4</testcontainers.version>
<ice.native>false</ice.native>
</properties>

<dependencies>
Expand Down Expand Up @@ -562,6 +563,9 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<ice.native>${ice.native}</ice.native>
</systemPropertyVariables>
<excludes>
<exclude>**/DockerScenarioBasedIT.java</exclude>
</excludes>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
*/
public class ScenarioBasedIT extends RESTCatalogTestBase {

private static final boolean USE_NATIVE = Boolean.getBoolean("ice.native");

@Override
protected ScenarioTestRunner createScenarioRunner(String scenarioName) throws Exception {
Path scenariosDir = getScenariosDirectory();
Expand All @@ -38,18 +40,24 @@ protected ScenarioTestRunner createScenarioRunner(String scenarioName) throws Ex

// Try to find ice-jar in the build
String projectRoot = Paths.get("").toAbsolutePath().getParent().toString();
String iceJar = projectRoot + "/ice/target/ice-jar";
String iceJar =
USE_NATIVE ? projectRoot + "/ice/target/ice" : projectRoot + "/ice/target/ice-jar";
File iceJarFile = new File(iceJar);

if (iceJarFile.exists() && iceJarFile.canExecute()) {
// Use pre-built ice-jar if available
templateVars.put("ICE_CLI", iceJar);
logger.info("Using ice-jar from: {}", iceJar);
String override = System.getenv("ICE_CLI_OVERRIDE");
if (override != null) {
templateVars.put("ICE_CLI", override);
} else {
// Fall back to using local-ice wrapper script
String localIce = projectRoot + "/.bin/local-ice";
templateVars.put("ICE_CLI", localIce);
logger.info("Using local-ice script from: {}", localIce);
if (iceJarFile.exists() && iceJarFile.canExecute()) {
// Use pre-built ice-jar if available
templateVars.put("ICE_CLI", iceJar);
logger.info("Using ice-jar from: {}", iceJar);
} else {
// Fall back to using local-ice wrapper script
String localIce = projectRoot + "/.bin/local-ice";
templateVars.put("ICE_CLI", localIce);
logger.info("Using local-ice script from: {}", localIce);
}
}

return new ScenarioTestRunner(scenariosDir, templateVars);
Expand Down
33 changes: 33 additions & 0 deletions ice/Dockerfile.native
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Dockerfile for building ICE native image (mostly-static with dynamic libc)
# Usage (amd64): docker build -f ice/Dockerfile.native -t ice-native:amd64 .
# Usage (arm64): docker build -f ice/Dockerfile.native -t ice-native:arm64 .

FROM ghcr.io/graalvm/native-image-community:21 AS builder

WORKDIR /workspace

# Copy license header file (required by license-maven-plugin)
COPY apache-header.txt .

# Copy Maven files for dependency resolution
COPY mvnw .
COPY .mvn .mvn
COPY pom.xml .
COPY ice/pom.xml ice/
COPY ice-rest-catalog/pom.xml ice-rest-catalog/

# Download dependencies (cached layer)
RUN ./mvnw dependency:go-offline -pl ice || true

# Copy source code
COPY ice/src ice/src

# Build mostly-static native image (everything statically linked except libc)
RUN ./mvnw -Pnative -pl ice clean package -Dmaven.test.skip=true

# ============================================================================
# Final stage: distroless base (includes minimal glibc runtime)
# ============================================================================
FROM gcr.io/distroless/base-debian12:nonroot
COPY --from=builder /workspace/ice/target/ice /ice
ENTRYPOINT ["/ice"]
36 changes: 36 additions & 0 deletions ice/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -667,5 +667,41 @@
</plugins>
</build>
</profile>
<profile>
<id>native</id>
<build>
<plugins>
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
<version>${native.maven.plugin.version}</version>
<extensions>true</extensions>
<executions>
<execution>
<id>build-native</id>
<goals>
<goal>compile-no-fork</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
<configuration>
<imageName>ice</imageName>
<mainClass>com.altinity.ice.cli.Main</mainClass>
<buildArgs>
<!-- Base configuration -->
<buildArg>--initialize-at-run-time=io.netty</buildArg>
<buildArg>--initialize-at-run-time=ch.qos.logback</buildArg>
<!-- We need dynamic libc, cannot be fully static -->
<buildArg>-H:+StaticExecutableWithDynamicLibC</buildArg>
<!-- Additional optimizations -->
<buildArg>-H:+RemoveUnusedSymbols</buildArg>
<buildArg>-H:+ReportExceptionStackTraces</buildArg>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[
{
"name":"com.github.luben.zstd.ZstdInputStreamNoFinalizer",
"fields":[{"name":"dstPos"}, {"name":"srcPos"}]
},
{
"name":"com.github.luben.zstd.ZstdOutputStreamNoFinalizer",
"fields":[{"name":"dstPos"}, {"name":"srcPos"}]
},
{
"name":"java.lang.Boolean",
"methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }]
},
{
"name":"org.jline.nativ.CLibrary",
"fields":[{"name":"TCSADRAIN"}, {"name":"TCSAFLUSH"}, {"name":"TCSANOW"}, {"name":"TIOCGWINSZ"}, {"name":"TIOCSWINSZ"}]
},
{
"name":"org.jline.nativ.CLibrary$Termios",
"fields":[{"name":"SIZEOF"}, {"name":"c_cc"}, {"name":"c_cflag"}, {"name":"c_iflag"}, {"name":"c_ispeed"}, {"name":"c_lflag"}, {"name":"c_oflag"}, {"name":"c_ospeed"}]
},
{
"name":"org.jline.nativ.CLibrary$WinSize",
"fields":[{"name":"SIZEOF"}, {"name":"ws_col"}, {"name":"ws_row"}, {"name":"ws_xpixel"}, {"name":"ws_ypixel"}]
},
{
"name":"sun.management.VMManagementImpl",
"fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}]
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[
{
"type":"agent-extracted",
"classes":[
]
}
]

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
{
"interfaces":["sun.misc.SignalHandler"]
}
]
Loading
Loading