diff --git a/README.md b/README.md index 549b450..b21b334 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,33 @@ http://localhost:8080/adm **Note**: Login with demo username "tbrown" and password "password". Use env "local-demo", app "testapp", ver "1.0.0". +### Ports +The containers publish these ports on the host. If another project already uses some of them, or you run a second copy of adm, set other ports in a `.env` file next to `compose.yaml`, which Compose reads, or in the environment: + +| Variable | Default | Port of | +|---|---|---| +| ADM_HTTPS_PORT | 8443 | app (HTTPS) | +| ADM_HTTP_PORT | 8080 | app (HTTP) | +| ADM_MANAGEMENT_PORT | 9990 | Wildfly management | +| ADM_KEYCLOAK_PORT | 8081 | Keycloak | +| ADM_KEYCLOAK_MANAGEMENT_PORT | 9991 | Keycloak management | +| ADM_ORACLE_PORT | 1521 | Oracle | +| ADM_ORACLE_EM_PORT | 5500 | Oracle Enterprise Manager | +| ADM_SSHD_PORT | 1234 | sshd (the demo data's "local-dev" env deploys to port 1234) | + +For example, a `.env` for a second copy: +``` +ADM_HTTPS_PORT=18443 +ADM_HTTP_PORT=18080 +ADM_MANAGEMENT_PORT=19990 +ADM_KEYCLOAK_PORT=18081 +ADM_KEYCLOAK_MANAGEMENT_PORT=19991 +ADM_ORACLE_PORT=11521 +ADM_ORACLE_EM_PORT=15500 +ADM_SSHD_PORT=11234 +``` +Compose names the containers, network, and images after the project's directory, such as `adm-oracle-1`, so a copy in another directory, such as a git worktree, gets its own. Each copy uses about 4 GB of memory. + ## Install This application requires a Java 17+ JVM and standard library to run, plus a Jakarta EE 10 application server (developed with Wildfly). @@ -91,7 +118,7 @@ Further, the local DataSource must also leverage localhost port forwarding so th The [server](https://github.com/JeffersonLab/wildfly/blob/main/scripts/server-setup.sh) and [app](https://github.com/JeffersonLab/wildfly/blob/main/scripts/app-setup.sh) setup scripts can be used to setup a local instance of Wildfly. -The user you use to run Wildfly needs to have an SSH public/private key pair (ssh-keygen) in the default location (~/.ssh). The public key needs to be added to the `authorized_keys` file of user `testuser` in the container named "sshd". This can be done by creating a file named `.env` in the root of the project containing the env name "TEST_USER_AUTHORIZED_KEY" with value being Wildfly user's public key. This env will then be passed in via deps.yaml environment setting. +The user you use to run Wildfly needs to have an SSH public/private key pair (ssh-keygen) in the default location (~/.ssh). The public key needs to be added to the `authorized_keys` file of user `testuser` in the container of the `sshd` service. This can be done by creating a file named `.env` in the root of the project containing the env name "TEST_USER_AUTHORIZED_KEY" with value being Wildfly user's public key. This env will then be passed in via deps.yaml environment setting. The unit tests run with `gradlew build`, or alone with `gradlew test`. They need no database, Wildfly, or containers: the SSH tests start their own in-process SSH server. @@ -100,7 +127,7 @@ The integration tests run against the app and its services in containers, built docker compose -f build.yaml up -d --build gradlew integrationTest ``` -They wait up to 5 minutes for the services to start (set `ADM_READY_TIMEOUT_SECONDS` to change that), then log in with the demo users and the `adm` client's service account, as CI deploys do. They add their own app envs to deploy to, and remove them afterwards. To test an app on other ports, set `ADM_URL` (default `https://localhost:8443/adm`) and `KEYCLOAK_URL` (default `http://localhost:8081/auth`). +They wait up to 5 minutes for the services to start (set `ADM_READY_TIMEOUT_SECONDS` to change that), then log in with the demo users and the `adm` client's service account, as CI deploys do. They add their own app envs to deploy to, and remove them afterwards. They use the same [ports](#ports) as Compose, including those in `.env`. To test an app elsewhere, set `ADM_URL` (default `https://localhost:8443/adm`) and `KEYCLOAK_URL` (default `http://localhost:8081/auth`). ## Release 1. Bump the version number in the VERSION file and commit and push to GitHub (using [Semantic Versioning](https://semver.org/)). diff --git a/build.gradle b/build.gradle index 28cb0d2..08b70ba 100644 --- a/build.gradle +++ b/build.gradle @@ -64,6 +64,17 @@ tasks.register('integrationTest', Test) { // The services may still be starting, and they are not up to date with the source files outputs.upToDateWhen { false } + // Use the ports in .env, as docker compose does; the environment takes precedence + def dotEnv = file('.env') + if (dotEnv.exists()) { + dotEnv.eachLine { line -> + def m = line =~ /^\s*(ADM_\w+)\s*=\s*["']?(.*?)["']?\s*$/ + if (m.matches() && !System.getenv(m.group(1))) { + environment m.group(1), m.group(2) + } + } + } + testLogging { events "passed", "skipped", "failed" exceptionFormat = "full" diff --git a/compose.yaml b/compose.yaml index bac2dea..cee9e20 100644 --- a/compose.yaml +++ b/compose.yaml @@ -3,31 +3,30 @@ services: extends: file: deps.yaml service: oracle - container_name: adm-oracle keycloak: extends: file: deps.yaml service: keycloak - container_name: adm-keycloak sshd: extends: file: deps.yaml service: sshd - container_name: adm-sshd environment: # Using the demo/test public key below TEST_USER_AUTHORIZED_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4s2lC5RHzhbpj+GkxcuOAZLr1ta0Ebo9P5zUhBEmDUiiRfXAwZc83MrEqFfgmGUNeY5vMnwdFpJsTiKs2nqAqJTyHy7GnN39e4JRFg3mLV8AV/S+o8/F1ez94jvfNaIH+6vW25uKfqSe/pVAqPVlEvs4x29zT5JcSyNt+0w+E0Jm4WQakvlU1IWgcUXIy4fsjd0g6CFX/z0WwJtMkjzJlyiJUHNBCNuSvMUC2khDpCYvft5lqTNwvaB809BRnuP2/ejhwK2JGGPe9snqXQnlfO4CrujguoJT8FqhbzqLKVj/YhvENUMQGT9pt2wtKglVLUf5R48eeivNpAWX7tTXH jboss@adm" adm: hostname: adm - container_name: adm-dev ports: - - "8443:8443" - - "8080:8080" - - "9990:9990" + - "${ADM_HTTPS_PORT:-8443}:8443" + - "${ADM_HTTP_PORT:-8080}:8080" + - "${ADM_MANAGEMENT_PORT:-9990}:9990" environment: + # The browser reaches Keycloak and the app on the host's ports + KEYCLOAK_FRONTEND_SERVER_URL: 'http://localhost:${ADM_KEYCLOAK_PORT:-8081}/auth' + FRONTEND_SERVER_URL: 'https://localhost:${ADM_HTTPS_PORT:-8443}' # This is demo/test public key RSA_PUBLIC_KEY: "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC4s2lC5RHzhbpj+GkxcuOAZLr1ta0Ebo9P5zUhBEmDUiiRfXAwZc83MrEqFfgmGUNeY5vMnwdFpJsTiKs2nqAqJTyHy7GnN39e4JRFg3mLV8AV/S+o8/F1ez94jvfNaIH+6vW25uKfqSe/pVAqPVlEvs4x29zT5JcSyNt+0w+E0Jm4WQakvlU1IWgcUXIy4fsjd0g6CFX/z0WwJtMkjzJlyiJUHNBCNuSvMUC2khDpCYvft5lqTNwvaB809BRnuP2/ejhwK2JGGPe9snqXQnlfO4CrujguoJT8FqhbzqLKVj/YhvENUMQGT9pt2wtKglVLUf5R48eeivNpAWX7tTXH jboss@adm" # This is demo/test private key diff --git a/deps.yaml b/deps.yaml index 0380905..5b3bbee 100644 --- a/deps.yaml +++ b/deps.yaml @@ -2,10 +2,9 @@ services: oracle: image: gvenzl/oracle-xe:21.3.0 hostname: oracle - container_name: oracle ports: - - "1521:1521" - - "5500:5500" + - "${ADM_ORACLE_PORT:-1521}:1521" + - "${ADM_ORACLE_EM_PORT:-5500}:5500" environment: ORACLE_PASSWORD: testing TZ: 'America/New_York' @@ -15,19 +14,18 @@ services: keycloak: image: jeffersonlab/keycloak:2.6.0 hostname: keycloak - container_name: keycloak ports: - - "8081:8080" - - "9991:9990" + - "${ADM_KEYCLOAK_PORT:-8081}:8080" + - "${ADM_KEYCLOAK_MANAGEMENT_PORT:-9991}:9990" environment: - KC_FRONTEND_URL: 'http://localhost:8081/auth' + KC_FRONTEND_URL: 'http://localhost:${ADM_KEYCLOAK_PORT:-8081}/auth' KC_BACKEND_URL: 'http://keycloak:8080/auth' KC_HTTP_RELATIVE_PATH: '/auth' KC_BOOTSTRAP_ADMIN_USERNAME: 'admin' KC_BOOTSTRAP_ADMIN_PASSWORD: 'admin' KC_CLIENT_NAME: adm KC_RESOURCE: adm - KC_REDIRECT_URIS: '["https://localhost:8443/adm/*"]' + KC_REDIRECT_URIS: '["https://localhost:${ADM_HTTPS_PORT:-8443}/adm/*"]' volumes: - ./container/keycloak/initdb.d/03_customize.sh:/container-entrypoint-initdb.d/03_customize.sh @@ -37,8 +35,7 @@ services: context: . dockerfile: Dockerfile-sshd hostname: sshd - container_name: sshd ports: - - "1234:22" + - "${ADM_SSHD_PORT:-1234}:22" environment: TEST_USER_AUTHORIZED_KEY: ${TEST_USER_AUTHORIZED_KEY} \ No newline at end of file diff --git a/src/integration/java/org/jlab/adm/Adm.java b/src/integration/java/org/jlab/adm/Adm.java index 7b27ca6..3eee7fa 100644 --- a/src/integration/java/org/jlab/adm/Adm.java +++ b/src/integration/java/org/jlab/adm/Adm.java @@ -37,12 +37,15 @@ /** * The running app and its Keycloak, as started by {@code docker compose -f build.yaml up}. * - *

The environment variables ADM_URL and KEYCLOAK_URL point the tests at other ports. + *

The tests use the same ports as docker compose: ADM_HTTPS_PORT and ADM_KEYCLOAK_PORT, which + * the integrationTest task also reads from .env. ADM_URL and KEYCLOAK_URL override the URLs. */ final class Adm { - static final String ADM_URL = env("ADM_URL", "https://localhost:8443/adm"); - static final String KEYCLOAK_URL = env("KEYCLOAK_URL", "http://localhost:8081/auth"); + static final String ADM_URL = + env("ADM_URL", "https://localhost:" + env("ADM_HTTPS_PORT", "8443") + "/adm"); + static final String KEYCLOAK_URL = + env("KEYCLOAK_URL", "http://localhost:" + env("ADM_KEYCLOAK_PORT", "8081") + "/auth"); /** The app in the demo data */ static final String APP = "testapp"; @@ -85,10 +88,12 @@ static synchronized void awaitReady() throws InterruptedException { while (!ready && Instant.now().isBefore(end)) { try { - HttpResponse response = get("/log", serviceAccountToken()); + // The admin's password login works once Keycloak's setup scripts have all run + HttpResponse response = get("/log", adminToken()); ready = response.statusCode() == 200; problem = "GET /log returned " + response.statusCode(); - } catch (IOException | RuntimeException e) { + } catch (IOException | RuntimeException | AssertionError e) { + // Such as a failed token request while Keycloak is still creating the realm problem = e.toString(); }