diff --git a/.github/workflows/container-build.yml b/.github/workflows/container-build.yml
new file mode 100644
index 0000000..7d1f057
--- /dev/null
+++ b/.github/workflows/container-build.yml
@@ -0,0 +1,14 @@
+name: Build container
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - name: Build example container
+ run: docker build .
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 0000000..e0651dc
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,23 @@
+name: Publish client
+
+on:
+ release:
+ types: [created]
+
+jobs:
+ publish:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: '17'
+ - name: Build jar
+ run: mvn -q package
+ - name: Upload release asset
+ uses: softprops/action-gh-release@v1
+ with:
+ files: target/kuberhealthy-client-*.jar
diff --git a/Dockerfile b/Dockerfile
index 9c4e93e..e725083 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,9 +1,10 @@
-FROM openjdk:17-jdk-slim AS build
+FROM maven:3.9.5-eclipse-temurin-17 AS build
WORKDIR /app
-COPY ExampleCheck.java .
-RUN javac ExampleCheck.java
+COPY pom.xml .
+COPY src ./src
+RUN mvn -q package
-FROM openjdk:17-jre-slim
+FROM eclipse-temurin:17-jre
WORKDIR /app
-COPY --from=build /app/ExampleCheck.class .
-ENTRYPOINT ["java", "ExampleCheck"]
+COPY --from=build /app/target/kuberhealthy-client-*.jar app.jar
+ENTRYPOINT ["java", "-cp", "app.jar", "io.kuberhealthy.example.ExampleCheck"]
diff --git a/ExampleCheck.java b/ExampleCheck.java
deleted file mode 100644
index 63bedad..0000000
--- a/ExampleCheck.java
+++ /dev/null
@@ -1,57 +0,0 @@
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-
-public class ExampleCheck {
- public static void main(String[] args) throws Exception {
- String url = System.getenv("KH_REPORTING_URL");
- String uuid = System.getenv("KH_RUN_UUID");
-
- if (url == null || url.isEmpty() || uuid == null || uuid.isEmpty()) {
- System.err.println("KH_REPORTING_URL and KH_RUN_UUID must be set");
- System.exit(1);
- }
-
- // Replace this with your own check logic. If the check passes, leave
- // ok as true. To report a failure, set ok to false and provide an
- // error message.
- boolean ok = true;
- String error = "";
-
- // Example: report a failure with an error message
- // ok = false;
- // error = "something went wrong";
-
- report(url, uuid, ok, error);
- }
-
- private static void report(String url, String uuid, boolean ok, String errorMessage) throws IOException {
- String payload = "{\"ok\":true,\"errors\":[]}";
- if (!ok) {
- payload = "{\"ok\":false,\"errors\":[\"" + escape(errorMessage) + "\"]}";
- }
-
- HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
- conn.setRequestMethod("POST");
- conn.setRequestProperty("Content-Type", "application/json");
- conn.setRequestProperty("kh-run-uuid", uuid);
- conn.setDoOutput(true);
-
- try (OutputStream os = conn.getOutputStream()) {
- os.write(payload.getBytes(StandardCharsets.UTF_8));
- }
-
- int code = conn.getResponseCode();
- if (code != 200) {
- // Kuberhealthy rejects malformed reports with HTTP 400. Treat any
- // non-200 status as a failure so the container exits quickly.
- throw new IOException("unexpected response code: " + code);
- }
- }
-
- private static String escape(String s) {
- return s.replace("\\", "\\\\").replace("\"", "\\\"");
- }
-}
diff --git a/README.md b/README.md
index 89888ca..c59b0c9 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,42 @@
-# Java Client Example
+# Java Client and Example
-This directory shows how to write a minimal Java check for [Kuberhealthy](https://github.com/kuberhealthy/kuberhealthy). The `ExampleCheck` program reads the `KH_REPORTING_URL` and `KH_RUN_UUID` environment variables that Kuberhealthy injects into checker pods and posts a JSON status report.
+This repository provides a minimal Java client for reporting check results to [Kuberhealthy](https://github.com/kuberhealthy/kuberhealthy) and an example check that uses it. The `ExampleCheck` program reads the `KH_REPORTING_URL` and `KH_RUN_UUID` environment variables that Kuberhealthy injects into checker pods and posts a JSON status report.
-## Extending the check
+## Using the client library
-Add your own logic inside `ExampleCheck.java` before the call to `report`. The file defaults to reporting success and includes commented-out lines that show how to report a failure:
+Applications can depend on the client published to GitHub Packages:
+
+```xml
+
+ io.kuberhealthy
+ kuberhealthy-client
+ 0.1.0-SNAPSHOT
+
+```
+
+Add `https://maven.pkg.github.com/OWNER/REPO` as a Maven repository and authenticate with a GitHub token that has permission to read packages.
+
+Within your code, create a `KuberhealthyClient` and call `report`:
+
+```java
+import io.kuberhealthy.client.KuberhealthyClient;
+
+KuberhealthyClient client = new KuberhealthyClient(url, uuid);
+client.report(true, "");
+```
+
+## Extending the example check
+
+Add your own logic inside `ExampleCheck.java` before reporting the result. The file defaults to reporting success and includes commented-out lines that show how to report a failure:
```java
// ok = false;
// error = "something went wrong";
```
-Call `report` with `ok=true` when the check succeeds or `ok=false` with an error message when it fails.
-
-`report` expects an HTTP 200 response from Kuberhealthy; any other status
-code causes the program to exit with an error so rejected payloads surface
-immediately.
+Call `report` with `ok=true` when the check succeeds or `ok=false` with an error message when it fails. Non-200 responses from Kuberhealthy cause the program to exit with an error so rejected payloads surface immediately.
-## Building and pushing
+## Building and pushing the container
Build and push the container image:
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..715f8aa
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,14 @@
+
+ 4.0.0
+ io.kuberhealthy
+ kuberhealthy-client
+ 0.1.0-SNAPSHOT
+ Kuberhealthy Java Client
+ Client library and example check for Kuberhealthy
+
+ 17
+ 17
+
+
diff --git a/src/main/java/io/kuberhealthy/client/KuberhealthyClient.java b/src/main/java/io/kuberhealthy/client/KuberhealthyClient.java
new file mode 100644
index 0000000..f5c612a
--- /dev/null
+++ b/src/main/java/io/kuberhealthy/client/KuberhealthyClient.java
@@ -0,0 +1,53 @@
+package io.kuberhealthy.client;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Minimal client for reporting check results to Kuberhealthy.
+ */
+public class KuberhealthyClient {
+ private final String url;
+ private final String uuid;
+
+ public KuberhealthyClient(String url, String uuid) {
+ this.url = url;
+ this.uuid = uuid;
+ }
+
+ /**
+ * Reports the result of a check to Kuberhealthy.
+ *
+ * @param ok true if the check succeeded
+ * @param errorMessage error description when ok is false
+ * @throws IOException when the report cannot be delivered
+ */
+ public void report(boolean ok, String errorMessage) throws IOException {
+ String payload = "{\"ok\":true,\"errors\":[]}";
+ if (!ok) {
+ payload = "{\"ok\":false,\"errors\":[\"" + escape(errorMessage) + "\"]}";
+ }
+
+ HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
+ conn.setRequestMethod("POST");
+ conn.setRequestProperty("Content-Type", "application/json");
+ conn.setRequestProperty("kh-run-uuid", uuid);
+ conn.setDoOutput(true);
+
+ try (OutputStream os = conn.getOutputStream()) {
+ os.write(payload.getBytes(StandardCharsets.UTF_8));
+ }
+
+ int code = conn.getResponseCode();
+ if (code != 200) {
+ throw new IOException("unexpected response code: " + code);
+ }
+ }
+
+ private static String escape(String s) {
+ return s.replace("\\", "\\\\").replace("\"", "\\\"");
+ }
+}
diff --git a/src/main/java/io/kuberhealthy/example/ExampleCheck.java b/src/main/java/io/kuberhealthy/example/ExampleCheck.java
new file mode 100644
index 0000000..f48304d
--- /dev/null
+++ b/src/main/java/io/kuberhealthy/example/ExampleCheck.java
@@ -0,0 +1,31 @@
+package io.kuberhealthy.example;
+
+import io.kuberhealthy.client.KuberhealthyClient;
+
+/**
+ * Example check that always reports success to Kuberhealthy.
+ */
+public class ExampleCheck {
+ public static void main(String[] args) throws Exception {
+ String url = System.getenv("KH_REPORTING_URL");
+ String uuid = System.getenv("KH_RUN_UUID");
+
+ if (url == null || url.isEmpty() || uuid == null || uuid.isEmpty()) {
+ System.err.println("KH_REPORTING_URL and KH_RUN_UUID must be set");
+ System.exit(1);
+ }
+
+ // Replace this with your own check logic. If the check passes, leave
+ // ok as true. To report a failure, set ok to false and provide an
+ // error message.
+ boolean ok = true;
+ String error = "";
+
+ // Example: report a failure with an error message
+ // ok = false;
+ // error = "something went wrong";
+
+ KuberhealthyClient client = new KuberhealthyClient(url, uuid);
+ client.report(ok, error);
+ }
+}