Skip to content
Merged
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
51 changes: 31 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,50 @@ on:
pull_request:
branches: [main]

env:
WILDFLY_VERSION: 39.0.1.Final
TEST_CLASS: StickySessionTest,SslFailoverTest

jobs:
test:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
wildfly-version: ['39.0.1.Final']
balancer-type: [undertow]
os: [ubuntu-latest, windows-latest]
java: ['17', '21', '25']
balancer: [undertow, httpd]
exclude:
- os: windows-latest
java: '17'
- os: windows-latest
balancer: httpd

name: "${{ matrix.balancer }} / ${{ matrix.os }} / JDK ${{ matrix.java }}"

steps:
- uses: actions/checkout@v4

- name: Set up JDK 21
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
java-version: ${{ matrix.java }}
distribution: temurin
cache: 'maven'

- name: Download WildFly via Maven
run: >
mvn -B generate-test-resources
-Pdownload-wildfly
-Dwildfly.version=${{ matrix.wildfly-version }}
-DskipTests

- name: Run tests
run: >
mvn -B test
-Dtest=InitialLoadTest
-DexcludedGroups=none
-Dbalancer.type=${{ matrix.balancer-type }}
-Dwildfly.version=${{ matrix.wildfly-version }}
shell: bash
run: mvn -B generate-test-resources -Pdownload-wildfly -Dwildfly.version=${{ env.WILDFLY_VERSION }} -DskipTests

- name: Run tests (Docker)
if: runner.os != 'Windows'
shell: bash
run: mvn -B test -Dtest=${{ env.TEST_CLASS }} -DexcludedGroups=none -Dbalancer.type=${{ matrix.balancer }} -Dwildfly.version=${{ env.WILDFLY_VERSION }}

- name: Run tests (Native)
if: runner.os == 'Windows'
shell: bash
run: mvn -B test -Pnative -Dtest=${{ env.TEST_CLASS }} -DexcludedGroups=none -Dbalancer.type=${{ matrix.balancer }} -Dwildfly.version=${{ env.WILDFLY_VERSION }}

- name: Publish test results
uses: mikepenz/action-junit-report@v5
Expand All @@ -49,5 +60,5 @@ jobs:
uses: actions/upload-artifact@v4
if: failure()
with:
name: surefire-reports-${{ matrix.balancer-type }}-${{ matrix.wildfly-version }}
name: surefire-reports-${{ matrix.os }}-${{ matrix.balancer }}-jdk${{ matrix.java }}
path: target/surefire-reports/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ target/
*.log
test-output/
*.tmp
*/target/

# OS
.DS_Store
Expand Down
33 changes: 29 additions & 4 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,26 @@ When you run tests with a ZIP:
- **Balancer**: `standalone.sh -Djboss.modcluster.advertise=true`
- **Workers**: `standalone.sh` connecting to `balancer:8090`

## Test Modes

The test suite supports two execution modes, selected via `-Dtest.mode=` (or the `-Pnative` Maven profile):

### Docker Mode (default)

Each worker and balancer runs in its own Docker/Podman container managed by Testcontainers. Containers share a private Docker network with DNS aliases (`worker1`, `worker2`, `balancer`). All containers use identical ports (8080, 9990, 7600) — networking separates them.

### Native Mode (`-Dtest.mode=native`)

Each worker and balancer runs as a local OS process started via `ProcessBuilder`. All processes share the host network and are distinguished by static port offsets (e.g. worker1 at offset 100, worker2 at offset 200). No container runtime is required.

Key native-mode components:
- **`NativeProcessManager`** — wraps `ProcessBuilder`/`Process` for lifecycle management (start, stop, kill, process tree cleanup)
- **`NativeServerExtractor`** — extracts WildFly ZIP to `target/native-servers/{name}/`, backs up clean config for per-test reset
- **`NativePortAllocator`** — assigns fixed port offsets per worker name
- **`NativeWildFlyWorker`** — native WildFly worker implementation (extends `WildFlyWorker`)
- **`NativeUndertowBalancer`** — native Undertow balancer (WildFly process with mod_cluster proxy)
- **`NativeHttpdBalancer`** — native httpd balancer (JBCS httpd process with mod_proxy_cluster)

## Component Architecture

### Test Extension (Dependency Injection)
Expand Down Expand Up @@ -287,10 +307,15 @@ pom.xml
└─ Awaitility (async testing)

ModClusterTestExtension.java
├─ BalancerContainer.java
│ ├─ UndertowBalancerContainer
│ └─ HttpdBalancerContainer
├─ WildFlyContainer.java
├─ Balancer (abstract)
│ ├─ Docker: UndertowBalancerContainer, HttpdBalancerContainer
│ └─ Native: NativeUndertowBalancer, NativeHttpdBalancer
├─ WildFlyWorker (abstract)
│ ├─ Docker: DockerWildFlyWorker
│ └─ Native: NativeWildFlyWorker
├─ NativeProcessManager (process lifecycle)
├─ NativeServerExtractor (ZIP extraction)
├─ NativePortAllocator (port offsets)
└─ HttpClient.java

Test Classes
Expand Down
42 changes: 33 additions & 9 deletions CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,17 @@ This is useful for slow CI nodes where container networking or Infinispan rebala
| `test.timeout.cluster` | Cluster formation, worker registration, view convergence | `120` s | `-Dtest.timeout.cluster=180` |
| `test.timeout.failover` | Failover completion after worker kill (includes Infinispan rebalancing) | `120` s | `-Dtest.timeout.failover=180` |

### Test Mode

| Property | Description | Default | Example |
|----------|-------------|---------|---------|
| `test.mode` | Test execution mode: `docker` (containers) or `native` (local processes) | `docker` | `-Dtest.mode=native` |

### Test Execution

| Property | Description | Default | Example |
|----------|-------------|---------|---------|
| `testcontainers.reuse.enable` | Reuse containers between runs | `false` | `-Dtestcontainers.reuse.enable=true` |
| `testcontainers.reuse.enable` | Reuse containers between runs (Docker mode only) | `false` | `-Dtestcontainers.reuse.enable=true` |
| `test` | Specific test to run | All tests | `-Dtest=StickySessionTest` |

## Environment Variables
Expand All @@ -80,6 +86,7 @@ Activate profiles with `-P<profile>`.
|---------|---------|----------------|
| `undertow` | Use Undertow balancer (default) | `balancer.type=undertow` |
| `httpd` | Use httpd balancer | `balancer.type=httpd` |
| `native` | Native mode (no Docker) | `test.mode=native`, excludes `docker` and `soak` tagged tests |
| `ci` | CI/CD mode | `testcontainers.reuse.enable=false` |

## Configuration Files
Expand Down Expand Up @@ -138,7 +145,24 @@ wget https://github.com/wildfly/wildfly/releases/download/30.0.1.Final/wildfly-3
mvn test -Dwildfly.zip.path=distributions/wildfly-30.0.1.Final.zip
```

### Scenario 3: CI/CD Pipeline
### Scenario 3: Native Mode (Windows / No Docker)

```bash
# Undertow balancer
mvn test -Pnative -Dwildfly.zip.path=distributions/wildfly-39.0.1.Final.zip

# httpd balancer
mvn test -Pnative -Dbalancer.type=httpd \
-Dwildfly.zip.path=distributions/wildfly-39.0.1.Final.zip \
-Dhttpd.zip.path=distributions/jbcs-httpd24-2.4.62-win-x86_64.zip

# Windows CI (batch script)
mvn -B test -Pnative -Dbalancer.type=undertow ^
-Dwildfly.zip.path=%WILDFLY_ZIP_PATH% ^
-Dmaven.test.failure.ignore=true
```

### Scenario 4: CI/CD Pipeline

```bash
# Jenkins/GitHub Actions
Expand All @@ -161,7 +185,7 @@ mvn test -Pci \
-Dtest.timeout.cluster=180
```

### Scenario 4: Quick Iteration (Development)
### Scenario 5: Quick Iteration (Development)

```bash
# Enable container reuse
Expand All @@ -178,7 +202,7 @@ mvn test -Dtest=SSLTest
docker stop $(docker ps -aq)
```

### Scenario 5: Test Both Balancers
### Scenario 6: Test Both Balancers

```bash
# Sequential
Expand All @@ -187,7 +211,7 @@ mvn test -Pundertow && mvn test -Phttpd
# Or use the Jenkins matrix approach
```

### Scenario 6: Custom Builds / Non-Standard ZIPs
### Scenario 7: Custom Builds / Non-Standard ZIPs

```bash
# Your ZIP doesn't match naming convention
Expand All @@ -199,7 +223,7 @@ cp /path/to/my-custom-build.zip distributions/wildfly-31.0.0.Custom.zip
mvn test
```

### Scenario 7: Test on Different UBI Version
### Scenario 8: Test on Different UBI Version

```bash
# Use UBI 10 instead of the default UBI 9
Expand All @@ -209,7 +233,7 @@ mvn test -Dcontainer.base.image=registry.access.redhat.com/ubi10/openjdk-17:late
mvn test -Dcontainer.base.image=my-registry.com/custom-jdk17:1.0
```

### Scenario 7b: Test on UBI 10 (no OpenJDK image available)
### Scenario 8b: Test on UBI 10 (no OpenJDK image available)

When the base image does not include Java (e.g., UBI 10 `ubi-minimal`), inject the
host machine's JDK into the container image at build time:
Expand All @@ -224,7 +248,7 @@ The host JDK is copied into the image during `docker build` and `JAVA_HOME` is s
automatically at container runtime. The image is cached with a `-hostjdk` tag suffix
so it does not collide with images built from a base that already includes Java.

### Scenario 8: Testing with Podman
### Scenario 9: Testing with Podman

```bash
# Setup Podman socket
Expand All @@ -235,7 +259,7 @@ export DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock
mvn test
```

### Scenario 9: Debugging Failures
### Scenario 10: Debugging Failures

```bash
# Enable debug logging
Expand Down
43 changes: 35 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ Comprehensive test suite for mod_cluster with WildFly/EAP workers and Undertow/h
This test suite uses:
- **JUnit 5** for test framework
- **AssertJ** for soft assertions
- **Testcontainers** for container-based testing
- **Testcontainers** for container-based testing (Docker mode, default)
- **Native process management** for non-container testing (native mode, for Windows / no-Docker environments)
- **Creaper** for WildFly/EAP management (clean, type-safe API)
- **Dependency Injection** pattern (no abstract base classes)

Expand Down Expand Up @@ -53,7 +54,13 @@ src/test/java/org/jboss/modcluster/test/
│ ├── SslFailoverTest.java
│ └── SslWorkerAuthenticationTest.java
└── utils/ # Utilities
├── BalancerContainer.java
├── balancer/ # Balancer implementations
│ ├── NativeHttpdBalancer.java
│ └── NativeUndertowBalancer.java
├── NativeProcessManager.java # OS process lifecycle (start/stop/kill)
├── NativeServerExtractor.java # ZIP extraction for native mode
├── NativePortAllocator.java # Static port offsets for native mode
├── NativeWildFlyWorker.java # Native WildFly worker
├── WildFlyContainer.java
├── HttpClient.java
└── ...
Expand All @@ -65,8 +72,8 @@ src/test/java/org/jboss/modcluster/test/

- Java 17 or higher
- Maven 3.6+
- Docker or Podman
- WildFly or EAP ZIP distribution (optional, will use pre-built images as fallback)
- Docker or Podman (Docker mode only; not required for native mode)
- WildFly or EAP ZIP distribution (optional in Docker mode, required in native mode)

### Quick Start

Expand Down Expand Up @@ -135,6 +142,22 @@ or
mvn test -Dbalancer.type=httpd
```

### Native Mode (no Docker)

Run tests without Docker/Podman by starting WildFly and httpd as local OS processes:

```bash
# Undertow balancer (default)
mvn test -Pnative -Dwildfly.zip.path=distributions/wildfly-39.0.1.Final.zip

# httpd balancer
mvn test -Pnative -Dbalancer.type=httpd \
-Dwildfly.zip.path=distributions/wildfly-39.0.1.Final.zip \
-Dhttpd.zip.path=distributions/jbcs-httpd24-2.4.62-win-x86_64.zip
```

The `-Pnative` profile sets `-Dtest.mode=native` and excludes `@Tag("docker")` and `@Tag("soak")` tests. See [TESTING.md](TESTING.md) for details on port allocation and server lifecycle.

### Run specific test class

```bash
Expand Down Expand Up @@ -310,12 +333,16 @@ This test suite aims for feature parity with `noe-tests/modcluster` (64 test fil

**Image naming**: `modcluster-test/wildfly-31-0-1-final:ubi9-openjdk-17`

### Container Clustering (JGroups)
### Clustering (JGroups)

WildFly uses JGroups for worker-to-worker session replication. The default `standalone-ha.xml` uses UDP multicast for cluster discovery, which does not work in Docker/Podman networks. The test framework automatically reconfigures JGroups at startup:

WildFly uses JGroups for worker-to-worker session replication. The default `standalone-ha.xml` uses UDP multicast for cluster discovery, which does not work in Docker/Podman networks. To solve this, `WildFlyContainer` automatically reconfigures JGroups at startup:
1. **Binds the private interface to `0.0.0.0`** (`-bprivate 0.0.0.0`) so JGroups TCP listens on the correct network interface instead of `127.0.0.1`
2. **Switches from UDP to the TCP stack** and replaces MPING (multicast discovery) with **TCPPING**

1. **Binds the private interface to `0.0.0.0`** (`-bprivate 0.0.0.0`) so JGroups TCP listens on the container's network interface instead of `127.0.0.1`
2. **Switches from UDP to the TCP stack** and replaces MPING (multicast discovery) with **TCPPING** using container network aliases (`worker1[7600]`, `worker2[7600]`, etc.)
The TCPPING `initial_hosts` are mode-dependent:
- **Docker**: container hostnames with the base port — `worker1[7600],worker2[7600],...`
- **Native**: `localhost` with offset ports — `localhost[7700],localhost[7800],...`

This is transparent to the tests — JGroups handles internal session replication while mod_cluster handles balancer-to-worker communication via MCMP over HTTP. The two layers are independent.

Expand Down
Loading
Loading