diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..4fd8841
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,50 @@
+name: CI
+
+on:
+ push:
+ branches: [master]
+ pull_request:
+
+jobs:
+ build:
+ name: Build and unit tests (JDK ${{ matrix.java }})
+ runs-on: ubuntu-24.04
+ strategy:
+ fail-fast: false
+ matrix:
+ java: [17, 21]
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: ${{ matrix.java }}
+ cache: maven
+ - name: Build, run the cluster-free unit tests and generate Javadoc
+ run: >-
+ ./mvnw -B -ntp
+ -Dtest='RadosBaseTest,ErrorCodeTest'
+ -Dsurefire.failIfNoSpecifiedTests=false
+ verify javadoc:javadoc
+
+ integration:
+ name: Integration tests against a MicroCeph cluster
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-java@v4
+ with:
+ distribution: temurin
+ java-version: 17
+ cache: maven
+ - name: Install librados, librbd and libradosstriper
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y --no-install-recommends librados2 librbd1 libradosstriper1
+ - name: Bootstrap a single-node Ceph cluster
+ run: sudo src/test/scripts/microceph-setup.sh
+ - name: Run all tests
+ run: ./mvnw -B -ntp test
+ - name: Cluster status on failure
+ if: failure()
+ run: sudo microceph.ceph -s || true
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 264f640..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-sudo: required
-
-services:
- - docker
-
-before_install:
- - docker build -t rados-java-test src/test/docker
-
-script:
- - docker run -d --name rados-java-test --net=host -v $(pwd):/build -e MON_IP=$(ip -4 -o a | awk '/eth|ens|eno|enp/ { sub ("/..", "", $4); print $4 }') -e CEPH_PUBLIC_NETWORK=$(grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/[0-9]\{1,2\}' /proc/net/fib_trie | grep -vE "^127|0$" | head -1) rados-java-test
- - sudo ./src/test/docker/validate-cluster.sh
- - docker exec -it rados-java-test /build/src/test/docker/run-test.sh
-
-
-
-
-
diff --git a/README.md b/README.md
index 135dd30..b58bff6 100644
--- a/README.md
+++ b/README.md
@@ -1,53 +1,87 @@
# RADOS Java
-These are Java bindings for [librados](https://docs.ceph.com/en/latest/rados/api/librados/) (C) which use JNA.
-Both RADOS and RBD are implemented in these bindings.
+[](https://github.com/ceph/rados-java/actions/workflows/ci.yml)
-By using JNA there is no need for building the bindings against any header
-you can use them on any system where JNA and librados are present.
+Java bindings for [librados](https://docs.ceph.com/en/latest/rados/api/librados/),
+[librbd](https://docs.ceph.com/en/latest/rbd/api/librbdpy/) and libradosstriper
+from the [Ceph](https://ceph.io/) project, implemented with
+[JNA](https://github.com/java-native-access/jna).
-The bindings follow the current librados C API and require Ceph Nautilus (14.2)
-or newer: the `rados_pool_stat_t` struct layout and the deprecated auid calls
-changed in Nautilus.
+Because JNA binds at runtime there is no native build against the Ceph
+headers: the bindings work on any system where JNA and the Ceph client
+libraries are present.
-# Maven
-## Building
+## Requirements
-Building requires JDK 17 or newer and Maven 3.6+ (or use the bundled `./mvnw`).
+* Java 17 or newer
+* librados2, librbd1 and libradosstriper1 from Ceph **Nautilus (14.2) or
+ newer**. The `rados_pool_stat_t` struct layout and the deprecated auid calls
+ changed in Nautilus, older versions are not supported.
-```bash
-$ mvn clean install (-Dcom.ceph.rados.skipTests)
+## Using the library
+
+```xml
+
+ com.ceph
+ rados
+ 0.7.0
+
```
-## Tests
+```java
+import com.ceph.rados.IoCTX;
+import com.ceph.rados.Rados;
-```bash
-$ mvn test
+try (Rados rados = new Rados("admin")) {
+ rados.confReadFile(new File("/etc/ceph/ceph.conf"));
+ rados.connect();
+
+ try (IoCTX io = rados.ioCtxCreate("data")) {
+ io.write("hello", "world".getBytes(StandardCharsets.UTF_8));
+ System.out.println(io.stat("hello").getSize());
+ }
+}
```
-You can also run specific tests:
+`Rados`, `IoCTX`, `IoCTXStriper`, `RbdImage`, `Completion`, `ReadOp` and
+`ListCtx` are `AutoCloseable` and release their native handles on `close()`.
+
+## Building
+
+Building requires JDK 17 or newer. The Maven wrapper downloads a suitable
+Maven version.
```bash
-mvn -Dtest=com.ceph.rados.TestRados
+./mvnw clean install -Dcom.ceph.rados.skipTests=true
```
+## Tests
+
+The unit tests that do not need a cluster can always be run:
+
```bash
-mvn -Dtest=com.ceph.rbd.TestRbd
+./mvnw test -Dtest='RadosBaseTest,ErrorCodeTest' -Dsurefire.failIfNoSpecifiedTests=false
```
-# Unit Tests
-The tests require a running Ceph cluster. By default it will read /etc/ceph/ceph.conf
-and use "admin" as a cephx id.
+The remaining tests are integration tests that require a running Ceph cluster.
+By default they read `/etc/ceph/ceph.conf`, authenticate as `admin` and use
+the pools `data` (RADOS and striper) and `rbd` (RBD). These pools have to
+EXIST prior to running the tests and should be EMPTY.
-All tests will be performed on the pools "data" (RADOS) and "rbd" (RBD).
+**DO NOT RUN THESE TESTS AGAINST A PRODUCTION CLUSTER.**
-These pools have to EXIST prior to running the tests and should be EMPTY!
+The defaults can be overridden with environment variables:
-DO NOT RUN THESE TESTS ON A PRODUCTION PLATFORM.
+* `RADOS_JAVA_ID`
+* `RADOS_JAVA_CONFIG_FILE`
+* `RADOS_JAVA_POOL`
-You can overrride this by setting these environment variables:
-* RADOS_JAVA_ID
-* RADOS_JAVA_CONFIG_FILE
-* RADOS_JAVA_POOL
+```bash
+./mvnw test
+./mvnw test -Dtest=com.ceph.rados.TestRados
+./mvnw test -Dtest=com.ceph.rbd.TestRbd
+```
-N.B.: You need to make sure jna.jar and junit.jar are present in /usr/share/java
+`src/test/scripts/microceph-setup.sh` bootstraps a throw-away single-node
+cluster with [MicroCeph](https://canonical-microceph.readthedocs-hosted.com/)
+and creates the pools, which is what the GitHub Actions workflow uses.
diff --git a/debian/changelog b/debian/changelog
index ece6b0b..eaf8929 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,13 @@
+librados-java (1:0.7.0-1) unstable; urgency=medium
+
+ * New upstream release 0.7.0 (the pom.xml version); an epoch is needed
+ because the previous Debian version was 1.0.
+ * Bindings fixed against the current librados C API (Nautilus+).
+ * Java 17 baseline, JNA 5.17, JUnit 5.
+ * Modern debhelper (compat 13) packaging.
+
+ -- Wido den Hollander Tue, 27 Oct 2026 08:50:07 +0000
+
librados-java (1.0) unstable; urgency=low
* Release of version 1.0
diff --git a/debian/compat b/debian/compat
deleted file mode 100644
index ec63514..0000000
--- a/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-9
diff --git a/debian/control b/debian/control
index 056c887..1f652a0 100644
--- a/debian/control
+++ b/debian/control
@@ -1,12 +1,16 @@
Source: librados-java
-Section: libs
-Priority: extra
-Maintainer: Wido den Hollander
-Build-Depends: debhelper (>= 7), openjdk-7-jdk | openjdk-8-jdk, maven (>= 3)
-Standards-Version: 3.8.1
-Homepage: http://www.github.com/ceph/rados-java
+Section: java
+Priority: optional
+Maintainer: Wido den Hollander
+Build-Depends: debhelper-compat (= 13), default-jdk (>= 2:1.17) | openjdk-17-jdk, maven (>= 3.6)
+Standards-Version: 4.7.0
+Homepage: https://github.com/ceph/rados-java
+Rules-Requires-Root: no
Package: librados-java
Architecture: all
-Depends: librados2 (>= 0.80), librbd1 (>= 0.80)
-Description: Java JNA bindings for librados and librbd
+Depends: ${misc:Depends}, default-jre-headless (>= 2:1.17) | java17-runtime-headless, librados2 (>= 14.2), librbd1 (>= 14.2), libradosstriper1 (>= 14.2)
+Description: Java JNA bindings for librados, librbd and libradosstriper
+ Java bindings for the Ceph RADOS object store client library (librados),
+ the RADOS block device library (librbd) and libradosstriper, implemented
+ with JNA so no native build against the Ceph headers is required.
diff --git a/debian/debhelper.log b/debian/debhelper.log
deleted file mode 100644
index 1d1d0b2..0000000
--- a/debian/debhelper.log
+++ /dev/null
@@ -1 +0,0 @@
-dh_installdirs
diff --git a/debian/librados-java.docs b/debian/librados-java.docs
index fa471a8..95db102 100644
--- a/debian/librados-java.docs
+++ b/debian/librados-java.docs
@@ -1,2 +1,2 @@
-README
-LICENSE
\ No newline at end of file
+README.md
+LICENSE
diff --git a/debian/librados-java.install b/debian/librados-java.install
deleted file mode 100644
index c68d582..0000000
--- a/debian/librados-java.install
+++ /dev/null
@@ -1 +0,0 @@
-/usr/share/java/rados-*.jar
\ No newline at end of file
diff --git a/debian/rules b/debian/rules
index 520c93a..9c72a15 100755
--- a/debian/rules
+++ b/debian/rules
@@ -1,50 +1,24 @@
#!/usr/bin/make -f
export DH_VERBOSE=1
-export DH_OPTIONS
-VERSION := $(shell grep '^ ' pom.xml| cut -d'>' -f2 |cut -d'<' -f1)
+VERSION := $(shell grep '^ ' pom.xml | cut -d'>' -f2 | cut -d'<' -f1)
+JAVADIR := debian/librados-java/usr/share/java
-configure: configure-stamp
-configure-stamp:
- dh_testdir
- touch configure-stamp
+%:
+ dh $@
-build: build-indep
+override_dh_auto_build:
+ mvn -B -ntp -Dcom.ceph.rados.skipTests=true clean package
-build-indep: build-indep-stamp
+override_dh_auto_test:
+ # The tests need a running Ceph cluster, see README.md
-build-indep-stamp: configure
- mvn clean
- mvn -DskipTests install
- touch $@
+override_dh_auto_install:
+ install -D -m 0644 target/rados-$(VERSION).jar $(JAVADIR)/rados-$(VERSION).jar
+ dh_link usr/share/java/rados-$(VERSION).jar usr/share/java/rados.jar
+ install -m 0644 target/dependencies/jna*.jar $(JAVADIR)/
-clean:
- dh_testdir
- dh_testroot
- rm -f build-arch-stamp build-indep-stamp configure-stamp
- dh_clean
-
-install:
- dh_testdir
- dh_testroot
- dh_prep -s
-
- mkdir -p debian/tmp/usr/share/java
- cp target/rados-${VERSION}.jar debian/tmp/usr/share/java
- dh_link usr/share/java/rados-${VERSION}.jar usr/share/java/rados.jar
- cp target/dependencies/jna*.jar debian/tmp/usr/share/java
-
- dh_installdirs
- dh_install
-
-binary: install
- dh_install
- dh_installchangelogs
- dh_link
- dh_compress
- dh_fixperms
- dh_installdeb
- dh_gencontrol
- dh_md5sums
- dh_builddeb
+override_dh_auto_clean:
+ mvn -B -ntp -q clean || true
+ dh_auto_clean
diff --git a/src/test/docker/Dockerfile b/src/test/docker/Dockerfile
deleted file mode 100644
index ff84684..0000000
--- a/src/test/docker/Dockerfile
+++ /dev/null
@@ -1,25 +0,0 @@
-# DOCKER-VERSION 1.0.0
-#
-# Ceph docker image for testing rados-java
-# based on the Demo Image by Sébastien Han "seb@redhat.com"
-#
-# VERSION 0.0.1
-
-FROM ceph/base:tag-build-master-jewel-ubuntu-16.04
-MAINTAINER Arno Broekhof "arnobroekhof@gmail.com"
-
-# Add bootstrap script
-ADD entrypoint.sh /entrypoint.sh
-
-# Add OpenJDK 8
-RUN apt-get update && \
- apt-get -y --no-install-recommends install openjdk-8-jdk libjna-java
-
-# Add volumes for Ceph config and data
-VOLUME ["/etc/ceph","/var/lib/ceph"]
-
-# Expose the Ceph ports
-EXPOSE 6789 6800 6801 6802 6803 6804 6805 80 5000
-
-# Execute the entrypoint
-ENTRYPOINT ["/entrypoint.sh"]
diff --git a/src/test/docker/entrypoint.sh b/src/test/docker/entrypoint.sh
deleted file mode 100755
index 4f0c7ed..0000000
--- a/src/test/docker/entrypoint.sh
+++ /dev/null
@@ -1,291 +0,0 @@
-#!/bin/bash
-set -e
-
-: ${CLUSTER:=ceph}
-: ${RGW_NAME:=$(hostname -s)}
-: ${MON_NAME:=$(hostname -s)}
-: ${RGW_CIVETWEB_PORT:=80}
-: ${NETWORK_AUTO_DETECT:=1}
-: ${RESTAPI_IP:=0.0.0.0}
-: ${RESTAPI_PORT:=5000}
-: ${RESTAPI_BASE_URL:=/api/v0.1}
-: ${RESTAPI_LOG_LEVEL:=warning}
-: ${RESTAPI_LOG_FILE:=/var/log/ceph/ceph-restapi.log}
-
-CEPH_OPTS="--cluster ${CLUSTER}"
-
-
-# FUNCTIONS
-# Log arguments with timestamp
-function log {
- if [ -z "$*" ]; then
- return 1
- fi
-
- TIMESTAMP=$(date '+%F %T')
- echo "${TIMESTAMP} $0: $*"
- return 0
-}
-
-function create_socket_dir {
- mkdir -p /var/run/ceph
- chown ceph. /var/run/ceph
-}
-
-#######
-# MON #
-#######
-
-function bootstrap_mon {
- if [[ ! -n "$CEPH_PUBLIC_NETWORK" && ${NETWORK_AUTO_DETECT} -eq 0 ]]; then
- log "ERROR- CEPH_PUBLIC_NETWORK must be defined as the name of the network for the OSDs"
- exit 1
- fi
-
- if [[ ! -n "$MON_IP" && ${NETWORK_AUTO_DETECT} -eq 0 ]]; then
- log "ERROR- MON_IP must be defined as the IP address of the monitor"
- exit 1
- fi
-
- if [ ${NETWORK_AUTO_DETECT} -ne 0 ]; then
- NIC_MORE_TRAFFIC=$(grep -vE "lo:|face|Inter" /proc/net/dev | sort -n -k 2 | tail -1 | awk '{ sub (":", "", $1); print $1 }')
- if command -v ip; then
- if [ ${NETWORK_AUTO_DETECT} -eq 1 ]; then
- MON_IP=$(ip -6 -o a s $NIC_MORE_TRAFFIC | awk '{ sub ("/..", "", $4); print $4 }')
- if [ -z "$MON_IP" ]; then
- MON_IP=$(ip -4 -o a s $NIC_MORE_TRAFFIC | awk '{ sub ("/..", "", $4); print $4 }')
- CEPH_PUBLIC_NETWORK=$(ip r | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/[0-9]\{1,2\}' | head -1)
- fi
- elif [ ${NETWORK_AUTO_DETECT} -eq 4 ]; then
- MON_IP=$(ip -4 -o a s $NIC_MORE_TRAFFIC | awk '{ sub ("/..", "", $4); print $4 }')
- CEPH_PUBLIC_NETWORK=$(ip r | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/[0-9]\{1,2\}' | head -1)
- elif [ ${NETWORK_AUTO_DETECT} -eq 6 ]; then
- MON_IP=$(ip -6 -o a s $NIC_MORE_TRAFFIC | awk '{ sub ("/..", "", $4); print $4 }')
- CEPH_PUBLIC_NETWORK=$(ip r | grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/[0-9]\{1,2\}' | head -1)
- fi
- # best effort, only works with ipv4
- # it is tough to find the ip from the nic only using /proc
- # so we just take on of the addresses available
- # which is fairely safe given that containers usually have a single nic
- else
- MON_IP=$(grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' /proc/net/fib_trie | grep -vEw "^127|255$|0$" | head -1)
- CEPH_PUBLIC_NETWORK=$(grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/[0-9]\{1,2\}' /proc/net/fib_trie | grep -vE "^127|^0" | head -1)
- fi
- fi
-
- if [[ -z "$MON_IP" || -z "$CEPH_PUBLIC_NETWORK" ]]; then
- log "ERROR- it looks like we have not been able to discover the network settings"
- exit 1
- fi
-
- # bootstrap MON
- if [ ! -e /etc/ceph/${CLUSTER}.conf ]; then
- fsid=$(uuidgen)
- cat </etc/ceph/${CLUSTER}.conf
-[global]
-fsid = $fsid
-mon initial members = ${MON_NAME}
-mon host = ${MON_IP}
-auth cluster required = cephx
-auth service required = cephx
-auth client required = cephx
-osd crush chooseleaf type = 0
-osd journal size = 100
-osd pool default pg num = 8
-osd pool default pgp num = 8
-osd pool default size = 1
-public network = ${CEPH_PUBLIC_NETWORK}
-cluster network = ${CEPH_PUBLIC_NETWORK}
-ENDHERE
-
- # For ext4
- if [ "$(findmnt -n -o FSTYPE -T /var/lib/ceph)" = "ext4" ]; then
- cat <> /etc/ceph/${CLUSTER}.conf
-osd max object name len = 256
-osd max object namespace len = 64
-ENDHERE
- fi
-
- # Generate administrator key
- ceph-authtool /etc/ceph/${CLUSTER}.client.admin.keyring --create-keyring --gen-key -n client.admin --set-uid=0 --cap mon 'allow *' --cap osd 'allow *' --cap mds 'allow'
-
- # Generate the mon. key
- ceph-authtool /etc/ceph/${CLUSTER}.mon.keyring --create-keyring --gen-key -n mon. --cap mon 'allow *'
-
- # Generate initial monitor map
- monmaptool --create --add ${MON_NAME} ${MON_IP} --fsid ${fsid} /etc/ceph/monmap-${CLUSTER}
- fi
-
- # If we don't have a monitor keyring, this is a new monitor
- if [ ! -e /var/lib/ceph/mon/${CLUSTER}-${MON_NAME}/keyring ]; then
-
- if [ ! -e /etc/ceph/${CLUSTER}.client.admin.keyring ]; then
- log "ERROR- /etc/ceph/${CLUSTER}.client.admin.keyring must exist; get it from your existing mon"
- exit 2
- fi
-
- if [ ! -e /etc/ceph/${CLUSTER}.mon.keyring ]; then
- log "ERROR- /etc/ceph/${CLUSTER}.mon.keyring must exist. You can extract it from your current monitor by running 'ceph ${CEPH_OPTS} auth get mon. -o /tmp/${CLUSTER}.mon.keyring'"
- exit 3
- fi
-
- if [ ! -e /etc/ceph/monmap-${CLUSTER} ]; then
- log "ERROR- /etc/ceph/monmap-${CLUSTER} must exist. You can extract it from your current monitor by running 'ceph ${CEPH_OPTS} mon getmap -o /tmp/monmap-${CLUSTER}'"
- exit 4
- fi
-
- # Import the client.admin keyring and the monitor keyring into a new, temporary one
- ceph-authtool /tmp/${CLUSTER}.mon.keyring --create-keyring --import-keyring /etc/ceph/${CLUSTER}.client.admin.keyring
- ceph-authtool /tmp/${CLUSTER}.mon.keyring --import-keyring /etc/ceph/${CLUSTER}.mon.keyring
-
- # Make the monitor directory
- mkdir -p /var/lib/ceph/mon/${CLUSTER}-${MON_NAME}
-
- # Make user 'ceph' the owner of all the tree
- chown ceph. /var/lib/ceph/bootstrap-{osd,mds,rgw}
-
- # Prepare the monitor daemon's directory with the map and keyring
- chown -R ceph. /var/lib/ceph/mon
- ceph-mon ${CEPH_OPTS} --mkfs -i ${MON_NAME} --monmap /etc/ceph/monmap-${CLUSTER} --keyring /tmp/${CLUSTER}.mon.keyring
- ceph-mon ${CEPH_OPTS} --setuser ceph --setgroup ceph --mkfs -i ${MON_NAME} --monmap /etc/ceph/monmap-${CLUSTER} --keyring /tmp/${CLUSTER}.mon.keyring --mon-data /var/lib/ceph/mon/${CLUSTER}-${MON_NAME}
-
- # Clean up the temporary key
- rm /tmp/${CLUSTER}.mon.keyring
- fi
-
- # start MON
- create_socket_dir
- chown -R ceph. /var/lib/ceph/mon
- ceph-mon ${CEPH_OPTS} -i ${MON_NAME} --public-addr "${MON_IP}:6789" --setuser ceph --setgroup ceph
-
- # change replica size
- ceph ${CEPH_OPTS} osd pool set rbd size 1
-}
-
-
-#######
-# OSD #
-#######
-
-function bootstrap_osd {
- if [ ! -e /var/lib/ceph/osd/${CLUSTER}-0/keyring ]; then
- # bootstrap OSD
- mkdir -p /var/lib/ceph/osd/${CLUSTER}-0
- ceph ${CEPH_OPTS} osd create
- chown -R ceph. /var/lib/ceph/osd/${CLUSTER}-0
- ceph-osd ${CEPH_OPTS} -i 0 --mkfs --setuser ceph --setgroup ceph
- ceph ${CEPH_OPTS} auth get-or-create osd.0 osd 'allow *' mon 'allow profile osd' -o /var/lib/ceph/osd/${CLUSTER}-0/keyring
- ceph ${CEPH_OPTS} osd crush add 0 1 root=default host=localhost
- fi
-
- # start OSD
- chown -R ceph. /var/lib/ceph/osd/${CLUSTER}-0
- ceph-osd ${CEPH_OPTS} -i 0 --setuser ceph --setgroup ceph
-}
-
-
-#######
-# MDS #
-#######
-
-function bootstrap_mds {
- if [ ! -e /var/lib/ceph/mds/${CLUSTER}-0/keyring ]; then
- # create ceph filesystem
- ceph ${CEPH_OPTS} osd pool create cephfs_data 8
- ceph ${CEPH_OPTS} osd pool create cephfs_metadata 8
- ceph ${CEPH_OPTS} fs new cephfs cephfs_metadata cephfs_data
-
- # bootstrap MDS
- mkdir -p /var/lib/ceph/mds/${CLUSTER}-0
- ceph ${CEPH_OPTS} auth get-or-create mds.0 mds 'allow' osd 'allow *' mon 'allow profile mds' > /var/lib/ceph/mds/${CLUSTER}-0/keyring
- chown -R ceph. /var/lib/ceph/mds/${CLUSTER}-0
- fi
-
- # start MDS
- ceph-mds ${CEPH_OPTS} -i 0 --setuser ceph --setgroup ceph
-}
-
-#######
-# RGW #
-#######
-
-function bootstrap_rgw {
- if [ ! -e /var/lib/ceph/radosgw/${RGW_NAME}/keyring ]; then
- # bootstrap RGW
- mkdir -p /var/lib/ceph/radosgw/${RGW_NAME}
- ceph ${CEPH_OPTS} auth get-or-create client.radosgw.gateway osd 'allow rwx' mon 'allow rw' -o /var/lib/ceph/radosgw/${RGW_NAME}/keyring
- chown -R ceph. /var/lib/ceph/radosgw/${RGW_NAME}
- fi
-
- # start RGW
- radosgw ${CEPH_OPTS} -c /etc/ceph/${CLUSTER}.conf -n client.radosgw.gateway -k /var/lib/ceph/radosgw/${RGW_NAME}/keyring --rgw-socket-path="" --rgw-frontends="civetweb port=${RGW_CIVETWEB_PORT}" --setuser ceph --setgroup ceph
-}
-
-function bootstrap_demo_user {
- if [ -n "$CEPH_DEMO_UID" ] && [ -n "$CEPH_DEMO_ACCESS_KEY" ] && [ -n "$CEPH_DEMO_SECRET_KEY" ]; then
- if [ -f /ceph-demo-user ]; then
- log "Demo user already exists with credentials:"
- cat /ceph-demo-user
- else
- log "Setting up a demo user..."
- radosgw-admin user create --uid=$CEPH_DEMO_UID --display-name="Ceph demo user" --access-key=$CEPH_DEMO_ACCESS_KEY --secret-key=$CEPH_DEMO_SECRET_KEY
- sed -i s/AWS_ACCESS_KEY_PLACEHOLDER/$CEPH_DEMO_ACCESS_KEY/ /root/.s3cfg
- sed -i s/AWS_SECRET_KEY_PLACEHOLDER/$CEPH_DEMO_SECRET_KEY/ /root/.s3cfg
- echo "Access key: $CEPH_DEMO_ACCESS_KEY" > /ceph-demo-user
- echo "Secret key: $CEPH_DEMO_SECRET_KEY" >> /ceph-demo-user
-
- if [ -n "$CEPH_DEMO_BUCKET" ]; then
- log "Creating bucket..."
- s3cmd mb s3://$CEPH_DEMO_BUCKET
- fi
- fi
- fi
-}
-
-#######
-# NFS #
-#######
-
-function bootstrap_nfs {
- # Init RPC
- rpcbind || return 0
- rpc.statd -L || return 0
- rpc.idmapd || return 0
-
- # start ganesha
- exec /usr/bin/ganesha.nfsd -F ${GANESHA_OPTIONS} ${GANESHA_EPOCH}
-}
-
-#######
-# API #
-#######
-
-function bootstrap_rest_api {
- if [[ ! "$(egrep "\[client.restapi\]" /etc/ceph/${CLUSTER}.conf)" ]]; then
- cat <>/etc/ceph/${CLUSTER}.conf
-[client.restapi]
- public addr = ${RESTAPI_IP}:${RESTAPI_PORT}
- restapi base url = ${RESTAPI_BASE_URL}
- restapi log level = ${RESTAPI_LOG_LEVEL}
- log file = ${RESTAPI_LOG_FILE}
-ENDHERE
- fi
-
- # start ceph-rest-api
- ceph-rest-api ${CEPH_OPTS} -n client.admin &
-}
-
-#########
-# WATCH #
-#########
-
-mkdir -p /var/lib/ceph/bootstrap-{osd,mds,rgw}
-bootstrap_mon
-bootstrap_osd
-bootstrap_mds
-bootstrap_rgw
-bootstrap_demo_user
-bootstrap_rest_api
-bootstrap_nfs
-log "SUCCESS"
-exec ceph ${CEPH_OPTS} -w
diff --git a/src/test/docker/run-test.sh b/src/test/docker/run-test.sh
deleted file mode 100755
index f0812f8..0000000
--- a/src/test/docker/run-test.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/bash
-set -xe
-
-# create data pool for testing
-ceph osd pool create data 128 128
-
-# sleep for 2 seconds to ensure the pool is created
-sleep 2
-
-cd /build
-./mvnw clean test
diff --git a/src/test/docker/validate-cluster.sh b/src/test/docker/validate-cluster.sh
deleted file mode 100755
index 7558afe..0000000
--- a/src/test/docker/validate-cluster.sh
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env bash
-set -xe
-
-container_name=rados-java-test
-
-
-# FUNCTIONS
-function wait_for_daemon () {
- timeout=20
- daemon_to_test=$1
- while [ $timeout -ne 0 ]; do
- if eval $daemon_to_test; then
- return 0
- fi
- sleep 1
- let timeout=timeout-1
- done
- return 1
-}
-
-function ceph_status {
- echo "Waiting for Ceph to be ready"
- return $(wait_for_daemon "docker exec $container_name ceph health | grep -sq HEALTH_OK")
-}
-
-function test_demo_mon {
- return $(wait_for_daemon "docker exec $container_name ceph -s | grep -sq quorum")
-}
-
-function test_demo_osd {
- return $(wait_for_daemon "docker exec $container_name ceph -s | grep -sq '1 osds: 1 up, 1 in'")
-}
-
-function test_demo_rgw {
- return $(wait_for_daemon "docker exec $container_name ceph osd dump | grep -sq rgw")
-}
-
-function test_demo_mds {
- echo "Waiting for the MDS to be ready"
- # NOTE(leseb): metadata server always takes up to 5 sec to run
- # so we first check if the pools exit, from that we assume that
- # the process will start. We stop waiting after 10 seconds.
- return $(wait_for_daemon "docker exec $container_name ceph osd dump | grep -sq cephfs && docker exec $container_name ceph -s | grep -sq 'up:active'")
-}
-
-# MAIN
-ceph_status # wait for the cluster to stabilize
-test_demo_mon
-test_demo_osd
-test_demo_rgw
-test_demo_mds
-ceph_status # wait again for the cluster to stabilize (mds pools)
-
-if ! docker ps | grep $container_name; then
- echo "looks like $container_name container died :("
- exit 1
-fi
diff --git a/src/test/scripts/microceph-setup.sh b/src/test/scripts/microceph-setup.sh
new file mode 100755
index 0000000..8c794be
--- /dev/null
+++ b/src/test/scripts/microceph-setup.sh
@@ -0,0 +1,51 @@
+#!/bin/bash
+#
+# Bootstrap a throw-away single-node Ceph cluster with MicroCeph and create
+# the pools the rados-java tests expect ("data" for RADOS and the striper,
+# "rbd" for RBD). Used by the GitHub Actions workflow, but also works on any
+# Ubuntu machine with snapd. Must run as root.
+#
+# DO NOT run this on a machine that already runs Ceph.
+#
+set -euo pipefail
+
+if [ "$(id -u)" -ne 0 ]; then
+ echo "This script must run as root" >&2
+ exit 1
+fi
+
+OSD_COUNT=${OSD_COUNT:-3}
+OSD_SIZE=${OSD_SIZE:-4G}
+POOLS=(data rbd)
+
+snap install microceph
+snap refresh --hold microceph
+
+microceph cluster bootstrap
+microceph disk add "loop,${OSD_SIZE},${OSD_COUNT}"
+
+# Expose the configuration and admin keyring to librados outside the snap.
+mkdir -p /etc/ceph
+cp /var/snap/microceph/current/conf/ceph.conf /etc/ceph/ceph.conf
+for keyring in ceph.keyring ceph.client.admin.keyring; do
+ if [ -f "/var/snap/microceph/current/conf/${keyring}" ]; then
+ cp "/var/snap/microceph/current/conf/${keyring}" "/etc/ceph/${keyring}"
+ chmod 0644 "/etc/ceph/${keyring}"
+ fi
+done
+
+# Wait for all OSDs to come up before creating pools.
+for _ in $(seq 1 60); do
+ if microceph.ceph osd stat | grep -q "${OSD_COUNT} up"; then
+ break
+ fi
+ sleep 5
+done
+
+for pool in "${POOLS[@]}"; do
+ microceph.ceph osd pool create "${pool}" 8
+done
+microceph.ceph osd pool application enable data rados
+microceph.ceph osd pool application enable rbd rbd
+
+microceph.ceph -s