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); + } +}