|
| 1 | +# Contributing |
| 2 | + |
| 3 | +## Development Setup |
| 4 | + |
| 5 | +This project is a Gradle multi-project build. Use Java 17 for the main build and keep Gradle's user home stable when running commands locally: |
| 6 | + |
| 7 | +```bash |
| 8 | +GRADLE_USER_HOME=/data/.gradle ./gradlew check |
| 9 | +``` |
| 10 | + |
| 11 | +Docker is required for integration tests that start Testcontainers services. Some example tasks start multiple large containers, so prefer running focused tasks while developing. |
| 12 | + |
| 13 | +## Project Boundaries |
| 14 | + |
| 15 | +Keep the module split intentional: |
| 16 | + |
| 17 | +- `core` owns service options, container construction, endpoint metadata, startup ordering, and log routing. |
| 18 | +- `junit5` owns annotation parsing, config loading for startup-time options, lifecycle integration, and parameter resolution. |
| 19 | +- `extensions` owns heavier setup that depends on Hadoop, Kafka clients, Avro, object-store clients, or other integration libraries. |
| 20 | +- Spring Boot modules should adapt the core API to Spring configuration. They should not duplicate container logic. |
| 21 | +- Example modules should demonstrate supported user workflows and catch real integration regressions. |
| 22 | + |
| 23 | +Do not add Hadoop, Spark, Kafka client, or storage SDK dependencies to `core` or `junit5` unless the module boundary is intentionally changed and documented. |
| 24 | + |
| 25 | +## Dependency Management |
| 26 | + |
| 27 | +The root build imports the Testcontainers BOM once for all subprojects: |
| 28 | + |
| 29 | +```text |
| 30 | +org.testcontainers:testcontainers-bom:2.0.4 |
| 31 | +``` |
| 32 | + |
| 33 | +Subprojects should declare Testcontainers modules without repeating the BOM. External users should import the BOM in their own build if they also depend on Testcontainers directly. |
| 34 | + |
| 35 | +When adding dependencies, keep them in the narrowest module that needs them. For example, Hadoop credential provider helpers belong in `extensions`, not `core`. |
| 36 | + |
| 37 | +## Configuration Model |
| 38 | + |
| 39 | +Startup-time service configuration belongs to `@BigDataTest` and its TOML files. Examples include: |
| 40 | + |
| 41 | +- enabled services |
| 42 | +- service images |
| 43 | +- fixed ports |
| 44 | +- Kerberos service switches |
| 45 | +- container log routing |
| 46 | + |
| 47 | +Test-data and setup automation belongs to `@BigDataExtensions`. Examples include: |
| 48 | + |
| 49 | +- creating buckets |
| 50 | +- creating JCEKS files |
| 51 | +- producing Kafka Avro records |
| 52 | +- publishing generated Kerberos material for tests |
| 53 | + |
| 54 | +Launcher-driven config should use the existing system-property hooks: |
| 55 | + |
| 56 | +```text |
| 57 | +bigdata.test.config |
| 58 | +bigdata.test.config.replace |
| 59 | +bigdata.extensions.config |
| 60 | +bigdata.extensions.config.replace |
| 61 | +``` |
| 62 | + |
| 63 | +Use replacement mode for matrix tasks that need to turn services on and off. Service booleans are additive when configs are appended. |
| 64 | + |
| 65 | +## Extension Design |
| 66 | + |
| 67 | +Prefer config-driven extension behavior when the user should only declare what they want. A good extension should: |
| 68 | + |
| 69 | +- validate required services before doing work |
| 70 | +- run at the narrowest lifecycle event that makes sense |
| 71 | +- publish useful output through `BigDataExtensionResult` |
| 72 | +- accept generated names and dynamic values through the programmatic builder |
| 73 | +- keep service-specific client code inside `extensions` |
| 74 | + |
| 75 | +For built-in extension types, update: |
| 76 | + |
| 77 | +- config model and loader |
| 78 | +- builder API |
| 79 | +- result output keys |
| 80 | +- user guide |
| 81 | +- an example or focused test |
| 82 | + |
| 83 | +For external extension modules, implement `BigDataExtensionProvider` and register it with `ServiceLoader`. |
| 84 | + |
| 85 | +## Kerberos Changes |
| 86 | + |
| 87 | +Kerberos is intentionally opt-in per service. `kerberos = true` starts the shared KDC, then service-specific switches decide which services use Kerberos. |
| 88 | + |
| 89 | +Schema Registry should stay out of the Kerberos service switch model unless its own authentication is explicitly implemented. It can still run with Kafka Kerberos by using Kafka's internal plaintext listener. |
| 90 | + |
| 91 | +Host-side Kafka Kerberos clients need a stable advertised host port. Existing Kerberos matrix tasks use fixed Kafka port `19092` for this reason. |
| 92 | + |
| 93 | +## HMS Implementations |
| 94 | + |
| 95 | +The project supports two Hive Metastore implementations: |
| 96 | + |
| 97 | +- `hiveMetastore = true`: open-source Hive HMS image plus external PostgreSQL. |
| 98 | +- `clouderaHms = true`: Cloudera HMS image with embedded PostgreSQL. |
| 99 | + |
| 100 | +Keep their endpoint contract the same through `BigDataService.HIVE_METASTORE`. A test must enable only one HMS implementation. |
| 101 | + |
| 102 | +The Spark example defaults to the Hive 3 open-source HMS image because Spark 3.x uses an older Hive metastore client. Keep Hive 4 compatibility notes in `doc/hive-docker-hms-issues.adoc`. |
| 103 | + |
| 104 | +## Documentation Expectations |
| 105 | + |
| 106 | +Update docs with every user-visible feature, breaking change, task, config key, image key, endpoint property, or behavior change. |
| 107 | + |
| 108 | +Use this split: |
| 109 | + |
| 110 | +- `README.md`: quick start, key commands, and links. |
| 111 | +- `doc/user-guide.adoc`: detailed usage, configuration, examples, and troubleshooting. |
| 112 | +- `CONTRIBUTING.md`: development workflow and project conventions. |
| 113 | +- focused `doc/*.adoc` notes: current known issues or design-specific references. |
| 114 | + |
| 115 | +## Testing |
| 116 | + |
| 117 | +Run focused compile checks for ordinary API edits: |
| 118 | + |
| 119 | +```bash |
| 120 | +GRADLE_USER_HOME=/data/.gradle ./gradlew :core:compileKotlin :junit5:compileKotlin :extensions:compileKotlin |
| 121 | +``` |
| 122 | + |
| 123 | +Run the Spark example when touching Spark, HMS, Kafka, object-store, extension, or Kerberos behavior: |
| 124 | + |
| 125 | +```bash |
| 126 | +GRADLE_USER_HOME=/data/.gradle ./gradlew :example:spark:test |
| 127 | +``` |
| 128 | + |
| 129 | +Run the full Spark matrix when touching runtime config composition, HMS selection, or Kerberos behavior: |
| 130 | + |
| 131 | +```bash |
| 132 | +GRADLE_USER_HOME=/data/.gradle ./gradlew :example:spark:sparkBigDataMatrixTest |
| 133 | +``` |
| 134 | + |
| 135 | +The Gradle configuration-cache warning from `net.researchgate.release` is currently expected and unrelated to test behavior. |
| 136 | + |
| 137 | +## Troubleshooting During Development |
| 138 | + |
| 139 | +Use file logs when investigating container startup or service-side behavior: |
| 140 | + |
| 141 | +```kotlin |
| 142 | +@BigDataTest( |
| 143 | + kafka = true, |
| 144 | + containerLogMode = ContainerLogMode.FILE, |
| 145 | + containerLogDirectory = "build/container-logs", |
| 146 | +) |
| 147 | +class DebugTest |
| 148 | +``` |
| 149 | + |
| 150 | +For port conflicts, check local listeners before switching tests to fixed ports. Dynamic ports are the default and should remain the default for general JUnit usage. |
0 commit comments