Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#639] feat(CI): Improvement Docker management for integration test #711

Merged
merged 1 commit into from
Nov 30, 2023
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
10 changes: 1 addition & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,8 @@ jobs:
java-version: '8'
distribution: 'temurin'

- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
gradle-version: '8.2'

- name: Show gradle version
run: gradle --version

- name: Build with Gradle
run: gradle build -PskipITs
run: ./gradlew build -PskipITs

- name: Upload unit tests report
uses: actions/upload-artifact@v3
Expand Down
55 changes: 4 additions & 51 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ on:
pull_request:
branches: [ "main", "branch-*" ]

env:
HIVE_IMAGE_NAME: datastrato/gravitino-ci-hive
HIVE_IMAGE_TAG_NAME: 0.1.4

concurrency:
group: ${{ github.worklfow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_requests' }}
Expand All @@ -38,46 +34,10 @@ jobs:
- name: Set up QEMU
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Build the hive Docker image for AMD64
if: ${{ contains(github.event.pull_request.labels.*.name, 'build docker image') }}
run: ./dev/docker/hive/build-docker.sh --platform ${PLATFORM} --image ${HIVE_IMAGE_NAME}:${HIVE_IMAGE_TAG_NAME}

- name: Run AMD64 container
run: |
docker run --rm --name ${DOCKER_RUN_NAME} --platform ${PLATFORM} -d -p 9000:9000 -p 9083:9083 -p 10000:10000 -p 10002:10002 -p 50010:50010 -p 50070:50070 -p 50075:50075 ${HIVE_IMAGE_NAME}:${HIVE_IMAGE_TAG_NAME}
docker ps -a

- name: Setup Gradle
uses: gradle/gradle-build-action@v2
with:
gradle-version: '8.2'

- name: Show gradle version
run: gradle --version

- name: Package Gravitino
run: |
gradle build -x test
gradle compileDistribution -x test

- name: Check docker container status
uses: nick-fields/retry@v2
with:
max_attempts: 3
timeout_seconds: 200
continue_on_error: false
command: |
docker exec -i ${DOCKER_RUN_NAME} bash /tmp/check-status.sh
on_retry_command: |
docker ps -a
docker stop ${DOCKER_RUN_NAME}
sleep 3
docker ps -a
docker run --rm --name ${DOCKER_RUN_NAME} --platform ${PLATFORM} -d -p 8022:22 -p 8088:8088 -p 9000:9000 -p 9083:9083 -p 10000:10000 -p 10002:10002 -p 50010:50010 -p 50070:50070 -p 50075:50075 ${HIVE_IMAGE_NAME}:${HIVE_IMAGE_TAG_NAME}
sleep 60
./gradlew build -x test
./gradlew compileDistribution -x test

- name: Setup debug Github Action
if: ${{ contains(github.event.pull_request.labels.*.name, 'debug action') }}
Expand All @@ -86,8 +46,8 @@ jobs:
- name: Integration Test
id: integrationTest
run: |
gradle test --rerun-tasks -PskipTests -PtestMode=embedded
gradle test --rerun-tasks -PskipTests -PtestMode=deploy
./gradlew test --rerun-tasks -PskipTests -PtestMode=embedded
./gradlew test --rerun-tasks -PskipTests -PtestMode=deploy

- name: Upload integrate tests reports
uses: actions/upload-artifact@v3
Expand All @@ -99,10 +59,3 @@ jobs:
integration-test/build/integration-test.log
distribution/package/logs/gravitino-server.out
distribution/package/logs/gravitino-server.log

- name: Stop and remove container
run: |
docker stop ${DOCKER_RUN_NAME}
sleep 3
docker ps -a
docker rmi ${HIVE_IMAGE_NAME}:${HIVE_IMAGE_TAG_NAME}
47 changes: 16 additions & 31 deletions docs/integration-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,57 +46,46 @@ The Gravitino server can be deployed locally to run the integration tests. Follo
Some integration test cases depend on the Gravitino CI Docker environment.

If an integration test relies on the specific Gravitino CI Docker environment,
you need to set the `@tag(CI-DOCKER-NAME)` annotation in the test class.
you need to set the `@tag(gravitino-docker-it)` annotation in the test class.
For example, the `integration-test/src/test/.../CatalogHiveIT.java` test needs to connect to
the `datastrato/gravitino-ci-hive` Docker container for testing the Hive data source.
Therefore, it should have the following `@tag` annotation:`@tag(CI-DOCKER-NAME)`, This annotation
Therefore, it should have the following `@tag` annotation:`@tag(gravitino-docker-it)`, This annotation
helps identify the specific Docker container required for the integration test.
For example:

```java
@Tag("gravitino-ci-hive")
@Tag("gravitino-docker-it")
public class CatalogHiveIT extends AbstractIT {
...
}
```

If you have Docker installed and a special CI Docker container running, the `./gradlew test -PtestMode=[embedded|deploy]`
### Running all integration tests
If you're running `Docker server` and `mac-docker-connector` (only macOS need to run `mac-docker-connector`), the `./gradlew test -PtestMode=[embedded|deploy]`
command will automatically execute all the test cases.

```text
------------------- Check Docker environment --------------------
Docker server status ............................................ [running]
Gravitino IT Docker container is already running ................ [yes]
Using Gravitino IT Docker container to run all integration tests. [embbeded|deploy test]
mac-docker-connector server status .............................. [running]
Using Gravitino IT Docker container to run all integration tests. [embedded test]
-----------------------------------------------------------------
```

If Docker is not installed or the special CI Docker container is not running, the `./gradlew test -PtestMode=[embedded|deploy]`
command will skip the test cases that depend on the special Docker environment.
### Docker server or mac-docker-connector not running
If `Docker server` or `mac-docker-connector` is not running (only required to run in macOS), the `./gradlew test -PtestMode=[embedded|deploy]`
command will run test cases without `gravitino-docker-it` tag.

```text
------------------- Check Docker environment ------------------
Docker server status .......................................... [running]
Gravitino IT Docker container is already running .............. [no]
Run only test cases where a tag is set `gravitino-docker-it`. [embbeded|deploy test]
Docker server status ............................................ [stop]
mac-docker-connector server status .............................. [stop]
Run test cases without `gravitino-docker-it` tag ................ [embedded test]
---------------------------------------------------------------
Tip: Please make sure to start the `Docker server before` running the integration tests.
Tip: Please make sure to execute the `dev/docker/tools/mac-docker-connector.sh` script before running the integration test in MacOS.
```

> Gravitino will run all integration test cases in the GitHub Actions environment.

### Running Gravitino CI Docker Environment

Before running the tests, make sure Docker is installed.

#### Running Gravitino Hive CI Docker Environment

1. Run a hive docker test environment container locally using the `docker run --rm -d -p 9000:9000 -p 9083:9083 -p 10000:10000 -p 10002:10002 -p 50010:50010 -p 50070:50070 -p 50075:50075 datastrato/gravitino-ci-hive:0.1.4` command.

The Gravitino server and Docker runtime environments will also use certain ports. Ensure that these ports are not already in use:

- Gravitino server: Port `8090`
- Hive Docker runtime environment: Ports are `9000`, `9083`, `10000`, `10002`, `50010`, `50070`, and `50075`

## Debugging Gravitino Server and Integration Tests

By default, the integration tests are run using MiniGravitino.
Expand Down Expand Up @@ -127,15 +116,11 @@ You have two modes to debug the Gravitino server and integration tests: `embedde
- Test results can be viewed in the `Actions` tab of the pull request page.
- The integration tests are executed in several steps:

- If you set the `build docker image` label in the pull request, GitHub Actions will trigger the build of all Docker
images under the `./dev/docker/` directory. This step usually takes around 10 minutes. If you have changed the Dockerfile,
you need to set the `build docker image` label in the pull request.
- Otherwise, GitHub Actions will pull the Docker image `datastrato/gravitino-ci-hive` from the Docker Hub repository. This step usually takes around 15 seconds.
- Gravitino integration tests will pull the CI Docker image from the Docker Hub repository. This step usually takes around 15 seconds.
- If you set the `debug action` label in the pull request, GitHub Actions will run an SSH server with
`csexton/debugger-action@master`, allows you to log in to the Actions environment for remote debugging.
- The Gravitino project is compiled and packaged in the `distribution` directory using the `./gradlew compileDistribution` command.
- Execution the `./gradlew test -PtestMode=[embedded|deploy]` command.
- Stop the Docker image to clean up.

## Test Failure
If a test fails, valuable information can be retrieved from the logs and test report. Test reports
Expand Down
147 changes: 62 additions & 85 deletions integration-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
* Copyright 2023 Datastrato.
* This software is licensed under the Apache License version 2.
*/

import java.io.IOException
import kotlin.io.*
import org.gradle.internal.os.OperatingSystem

plugins {
Expand Down Expand Up @@ -114,107 +112,95 @@ dependencies {

/* Optimizing integration test execution conditions */
// Use this variable to control if we need to run docker test or not.
var HIVE_IMAGE_NAME = "datastrato/gravitino-ci-hive"
var HIVE_IMAGE_TAG_NAME = "${HIVE_IMAGE_NAME}:0.1.2"
var EXCLUDE_DOCKER_TEST = true
var EXCLUDE_TRINO_TEST = true
// Use these 3 variables allow for more detailed control in the future.
var DOCKER_IT_TEST = false
project.extra["dockerRunning"] = false
project.extra["hiveContainerRunning"] = false
project.extra["macDockerConnector"] = false

fun printDockerCheckInfo() {
checkMacDockerConnector()
checkDockerStatus()

val testMode = project.properties["testMode"] as? String ?: "embedded"
if (testMode != "deploy" && testMode != "embedded") {
return
}
val dockerRunning = project.extra["dockerRunning"] as? Boolean ?: false
val hiveContainerRunning = project.extra["hiveContainerRunning"] as? Boolean ?: false
val macDockerConnector = project.extra["macDockerConnector"] as? Boolean ?: false

EXCLUDE_DOCKER_TEST = !(dockerRunning && hiveContainerRunning)
EXCLUDE_TRINO_TEST = if (OperatingSystem.current().isMacOsX) {
!(dockerRunning && macDockerConnector)
} else {
!dockerRunning
if (OperatingSystem.current().isMacOsX() && dockerRunning && macDockerConnector) {
DOCKER_IT_TEST = true
} else if (OperatingSystem.current().isLinux() && dockerRunning) {
DOCKER_IT_TEST = true
}

println("------------------ Check Docker environment ---------------------")
println("Docker server status ............................................ [${if (dockerRunning) "running" else "stop"}]")
println("Gravitino IT Docker container is already running ................ [${if (hiveContainerRunning) "yes" else "no"}]")
if (EXCLUDE_TRINO_TEST) {
println("Run test cases without `gravitino-trino-it` tag ................. [$testMode test]")
if (OperatingSystem.current().isMacOsX()) {
println("mac-docker-connector server status .............................. [${if (macDockerConnector) "running" else "stop"}]")
}
if (!EXCLUDE_DOCKER_TEST) {
println("Using Gravitino IT Docker container to run all integration tests. [$testMode test]")
} else {
if (!DOCKER_IT_TEST) {
println("Run test cases without `gravitino-docker-it` tag ................ [$testMode test]")
} else {
println("Using Gravitino IT Docker container to run all integration tests. [$testMode test]")
}
println("-----------------------------------------------------------------")

// Print help message if Docker server or mac-docker-connector is not running
printDockerServerTip()
printMacDockerConnectorTip()
}

tasks {
register("isMacDockerConnectorRunning") {
doLast {
val processName = "docker-connector"
val command = "pgrep -x ${processName}"
fun printDockerServerTip() {
val dockerRunning = project.extra["dockerRunning"] as? Boolean ?: false
if (!dockerRunning) {
val redColor = "\u001B[31m"
val resetColor = "\u001B[0m"
println("Tip: Please make sure to start the ${redColor}Docker server${resetColor} before running the integration tests.")
}
}

try {
val execResult = project.exec {
commandLine("bash", "-c", command)
}
if (execResult.exitValue == 0) {
project.extra["macDockerConnector"] = true
} else {
project.extra["macDockerConnector"] = false
}
} catch (e: Exception) {
project.extra["macDockerConnector"] = false
}
}
fun printMacDockerConnectorTip() {
val macDockerConnector = project.extra["macDockerConnector"] as? Boolean ?: false
if (OperatingSystem.current().isMacOsX() && !macDockerConnector) {
val redColor = "\u001B[31m"
val resetColor = "\u001B[0m"
println("Tip: Please make sure to execute the ${redColor}`dev/docker/tools/mac-docker-connector.sh`${resetColor} script before running the integration test in MacOS.")
}
}

// Use this task to check if docker container is running
register("checkContainerRunning") {
doLast {
try {
val process = ProcessBuilder("docker", "ps", "--format='{{.Image}}'").start()
val exitCode = process.waitFor()
fun checkMacDockerConnector() {
if (OperatingSystem.current().isLinux()) {
// Linux does not require the use of `docker-connector`
return
}

if (exitCode == 0) {
val output = process.inputStream.bufferedReader().readText()
val haveHiveContainer = output.contains("${HIVE_IMAGE_NAME}")
if (haveHiveContainer) {
project.extra["hiveContainerRunning"] = true
}
} else {
println("checkContainerRunning command execution failed with exit code $exitCode")
}
} catch (e: IOException) {
println("checkContainerRunning command execution failed: ${e.message}")
}
try {
val processName = "docker-connector"
val command = "pgrep -x -q ${processName}"

val execResult = project.exec {
commandLine("bash", "-c", command)
}
if (execResult.exitValue == 0) {
project.extra["macDockerConnector"] = true
}
} catch (e: Exception) {
println("checkContainerRunning command execution failed: ${e.message}")
}
}

// Use this task to check if docker is running
register("checkDockerRunning") {
dependsOn("checkContainerRunning", "isMacDockerConnectorRunning")

doLast {
try {
val process = ProcessBuilder("docker", "info").start()
val exitCode = process.waitFor()
fun checkDockerStatus() {
try {
val process = ProcessBuilder("docker", "info").start()
val exitCode = process.waitFor()

if (exitCode == 0) {
project.extra["dockerRunning"] = true
} else {
println("checkDockerRunning Command execution failed with exit code $exitCode")
}
} catch (e: IOException) {
println("checkDockerRunning command execution failed: ${e.message}")
}
printDockerCheckInfo()
if (exitCode == 0) {
project.extra["dockerRunning"] = true
} else {
println("checkDockerStatus command execution failed with exit code $exitCode")
}
} catch (e: IOException) {
println("checkDockerStatus command execution failed: ${e.message}")
}
}

Expand All @@ -223,9 +209,9 @@ tasks.test {
if (skipITs) {
exclude("**/integration/test/**")
} else {
dependsOn("checkDockerRunning")

doFirst {
printDockerCheckInfo()

copy {
from("${project.rootDir}/dev/docker/trino/conf")
into("build/trino-conf")
Expand Down Expand Up @@ -259,18 +245,9 @@ tasks.test {
}

useJUnitPlatform {
if (EXCLUDE_DOCKER_TEST) {
val redColor = "\u001B[31m"
val resetColor = "\u001B[0m"
println("${redColor}Gravitino-docker is not running locally, all integration test cases tagged with 'gravitino-docker-it' will be excluded.${resetColor}")
if (!DOCKER_IT_TEST) {
excludeTags("gravitino-docker-it")
}
if (EXCLUDE_TRINO_TEST) {
val redColor = "\u001B[31m"
val resetColor = "\u001B[0m"
println("${redColor}Gravitino-trino-docker is not running locally, all integration test cases tagged with 'gravitino-trino-it' will be excluded.${resetColor}")
excludeTags("gravitino-trino-it")
}
}
}
}
Expand Down
Loading