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

[#2552] feat(catalog-jdbc-doris): add Doris container implement for integration-test #2597

Merged
merged 4 commits into from
Mar 29, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/backend-integration-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ jobs:
needs: changes
if: needs.changes.outputs.source_changes == 'true'
runs-on: ubuntu-latest
timeout-minutes: 30
timeout-minutes: 60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why double the IT timeout?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

backend integration test need 42 minutes now. So, I increase the timeout.

strategy:
matrix:
architecture: [linux/amd64]
Expand Down
17 changes: 17 additions & 0 deletions catalogs/catalog-jdbc-doris/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,19 @@ dependencies {
implementation(libs.guava)
implementation(libs.jsqlparser)
implementation(libs.slf4j.api)

testImplementation(project(":catalogs:catalog-jdbc-common", "testArtifacts"))
testImplementation(project(":integration-test-common", "testArtifacts"))

testImplementation(libs.commons.lang3)
testImplementation(libs.guava)
testImplementation(libs.junit.jupiter.api)
testImplementation(libs.junit.jupiter.params)
testImplementation(libs.mysql.driver)
testImplementation(libs.testcontainers)
testImplementation(libs.testcontainers.mysql)

testRuntimeOnly(libs.junit.jupiter.engine)
}

tasks {
Expand Down Expand Up @@ -65,6 +78,10 @@ tasks.test {
} else {
dependsOn(tasks.jar)

doFirst {
environment("GRAVITINO_CI_DORIS_DOCKER_IMAGE", "datastrato/gravitino-ci-doris:0.1.1")
}

val init = project.extra.get("initIntegrationTest") as (Test) -> Unit
init(this)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.catalog.doris.integration.test;

import com.datastrato.gravitino.catalog.doris.converter.DorisColumnDefaultValueConverter;
import com.datastrato.gravitino.catalog.doris.converter.DorisExceptionConverter;
import com.datastrato.gravitino.catalog.doris.converter.DorisTypeConverter;
import com.datastrato.gravitino.catalog.doris.operation.DorisDatabaseOperations;
import com.datastrato.gravitino.catalog.doris.operation.DorisTableOperations;
import com.datastrato.gravitino.catalog.jdbc.config.JdbcConfig;
import com.datastrato.gravitino.catalog.jdbc.integration.test.TestJdbcAbstractIT;
import com.datastrato.gravitino.catalog.jdbc.utils.DataSourceUtils;
import com.datastrato.gravitino.integration.test.container.ContainerSuite;
import com.datastrato.gravitino.integration.test.container.DorisContainer;
import com.google.common.collect.Maps;
import java.util.Collections;
import java.util.Map;
import javax.sql.DataSource;
import org.junit.jupiter.api.BeforeAll;

public class TestDorisAbstractIT extends TestJdbcAbstractIT {
private static final ContainerSuite containerSuite = ContainerSuite.getInstance();
protected static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";

@BeforeAll
public static void startup() {
containerSuite.startDorisContainer();

DataSource dataSource = DataSourceUtils.createDataSource(getDorisCatalogProperties());

DATABASE_OPERATIONS = new DorisDatabaseOperations();
TABLE_OPERATIONS = new DorisTableOperations();
JDBC_EXCEPTION_CONVERTER = new DorisExceptionConverter();
DATABASE_OPERATIONS.initialize(dataSource, JDBC_EXCEPTION_CONVERTER, Collections.emptyMap());
TABLE_OPERATIONS.initialize(
dataSource,
JDBC_EXCEPTION_CONVERTER,
new DorisTypeConverter(),
new DorisColumnDefaultValueConverter(),
Collections.emptyMap());
}

private static Map<String, String> getDorisCatalogProperties() {
Map<String, String> catalogProperties = Maps.newHashMap();

DorisContainer dorisContainer = containerSuite.getDorisContainer();

String jdbcUrl =
String.format(
"jdbc:mysql://%s:%d/",
dorisContainer.getContainerIpAddress(), DorisContainer.FE_MYSQL_PORT);

catalogProperties.put(JdbcConfig.JDBC_URL.getKey(), jdbcUrl);
catalogProperties.put(JdbcConfig.JDBC_DRIVER.getKey(), DRIVER_CLASS_NAME);
catalogProperties.put(JdbcConfig.USERNAME.getKey(), DorisContainer.USER_NAME);
catalogProperties.put(JdbcConfig.PASSWORD.getKey(), DorisContainer.PASSWORD);

return catalogProperties;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.catalog.doris.integration.test;

import com.datastrato.gravitino.utils.RandomNameUtils;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

@Tag("gravitino-docker-it")
public class TestDorisDatabaseOperations extends TestDorisAbstractIT {

@Test
public void testBaseOperationDatabase() {
String databaseName = RandomNameUtils.genRandomName("ct_db");
Map<String, String> properties = new HashMap<>();
String databaseComment = "test_comment";

Assertions.assertThrows(
mchades marked this conversation as resolved.
Show resolved Hide resolved
UnsupportedOperationException.class,
() -> {
DATABASE_OPERATIONS.create(databaseName, databaseComment, properties);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a UT not an e2e test, but you put this in the package integration.test

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's normal, currently, we don't differentiate between IT and UT very much. The package integration.test holds tests that rely on the docker environment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The package integration.test holds tests that rely on the docker environment.

Isn't it annotation @Tag("gravitino-docker-it") that determines whether to rely on the docker environment?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, TestDorisDatabaseOperations relies on docker environment but it's a unit test actually.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Therefore, I do not recommend putting this class in the package integration.test

Copy link
Contributor Author

@zhoukangcn zhoukangcn Mar 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Therefore, I do not recommend putting this class in the package integration.test

At this point, I discussed it with @jerryshao in #2279 , so it is acceptable for the package "integration.test" to hold tests that are dependent upon the docker environment.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will use another issue #2599 to track it, @zhoukangcn please go ahead.

});
}
}
33 changes: 33 additions & 0 deletions catalogs/catalog-jdbc-doris/src/test/resources/log4j2.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#
# Copyright 2024 Datastrato Pvt Ltd.
# This software is licensed under the Apache License version 2.
#

# Set to debug or trace if log4j initialization is failing
status = info

# Name of the configuration
name = ConsoleLogConfig

# Console appender configuration
appender.console.type = Console
appender.console.name = consoleLogger
appender.console.layout.type = PatternLayout
appender.console.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# Log files location
property.logPath = ${sys:gravitino.log.path:-catalog-jdbc-doris/build/doris-integration-test.log}

# File appender configuration
appender.file.type = File
appender.file.name = fileLogger
appender.file.fileName = ${logPath}
appender.file.layout.type = PatternLayout
appender.file.layout.pattern = %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# Root logger level
rootLogger.level = info

# Root logger referring to console and file appenders
rootLogger.appenderRef.stdout.ref = consoleLogger
rootLogger.appenderRef.file.ref = fileLogger
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,10 @@ protected void withLogConsumer(Consumer<OutputFrame> logConsumer) {
container.withLogConsumer(logConsumer);
}

protected void withStartupTimeout(Duration duration) {
container.withStartupTimeout(duration);
}

// This method is used to get the expose port number of the container.
public Integer getMappedPort(int exposedPort) {
return container.getMappedPort(exposedPort);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public class ContainerSuite implements Closeable {
private static TrinoContainer trinoContainer;
private static TrinoITContainers trinoITContainers;

private static DorisContainer dorisContainer;

protected static final CloseableGroup closer = CloseableGroup.create();

private ContainerSuite() {
Expand Down Expand Up @@ -123,6 +125,17 @@ public void startTrinoContainer(
trinoContainer.start();
}

public void startDorisContainer() {
if (dorisContainer != null) {
return;
}
// Start Doris container
DorisContainer.Builder dorisBuilder =
DorisContainer.builder().withHostName("gravitino-ci-doris").withNetwork(network);
dorisContainer = closer.register(dorisBuilder.build());
dorisContainer.start();
}

public TrinoContainer getTrinoContainer() {
return trinoContainer;
}
Expand All @@ -139,6 +152,10 @@ public HiveContainer getHiveContainer() {
return hiveContainer;
}

public DorisContainer getDorisContainer() {
return dorisContainer;
}

// Let containers assign addresses in a fixed subnet to avoid `mac-docker-connector` needing to
// refresh the configuration
private static Network createDockerNetwork() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright 2024 Datastrato Pvt Ltd.
* This software is licensed under the Apache License version 2.
*/
package com.datastrato.gravitino.integration.test.container;

import static java.lang.String.format;

import com.google.common.collect.ImmutableSet;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.time.Duration;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.rnorth.ducttape.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.Network;

public class DorisContainer extends BaseContainer {
public static final Logger LOG = LoggerFactory.getLogger(DorisContainer.class);

public static final String DEFAULT_IMAGE = System.getenv("GRAVITINO_CI_DORIS_DOCKER_IMAGE");
public static final String HOST_NAME = "gravitino-ci-doris";
public static final String USER_NAME = "root";
public static final String PASSWORD = "root";
public static final int FE_HTTP_PORT = 8030;
public static final int FE_MYSQL_PORT = 9030;

public static Builder builder() {
return new Builder();
}

protected DorisContainer(
String image,
String hostName,
Set<Integer> ports,
Map<String, String> extraHosts,
Map<String, String> filesToMount,
Map<String, String> envVars,
Optional<Network> network) {
super(image, hostName, ports, extraHosts, filesToMount, envVars, network);
}

@Override
protected void setupContainer() {
super.setupContainer();
withLogConsumer(new PrintingContainerLog(format("%-14s| ", "DorisContainer")));
withStartupTimeout(Duration.ofMinutes(5));
}

@Override
public void start() {
super.start();
Preconditions.check("Doris container startup failed!", checkContainerStatus(5));
Preconditions.check("Doris container password change failed!", changePassword());
}

@Override
protected boolean checkContainerStatus(int retryLimit) {
int nRetry = 0;

String dorisJdbcUrl = format("jdbc:mysql://%s:%d/", getContainerIpAddress(), FE_MYSQL_PORT);
LOG.info("Doris url is " + dorisJdbcUrl);

while (nRetry++ < retryLimit) {
try (Connection connection = DriverManager.getConnection(dorisJdbcUrl, USER_NAME, "");
Statement statement = connection.createStatement()) {

// execute `SHOW PROC '/backends';` to check if backends is ready
String query = "SHOW PROC '/backends';";
try (ResultSet resultSet = statement.executeQuery(query)) {
while (resultSet.next()) {
String alive = resultSet.getString("Alive");
if (alive.equalsIgnoreCase("true")) {
LOG.info("Doris container startup success!");
return true;
}
}
}

LOG.info("Doris container is not ready yet!");
Thread.sleep(5000);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}

return false;
}

private boolean changePassword() {
String dorisJdbcUrl = format("jdbc:mysql://%s:%d/", getContainerIpAddress(), FE_MYSQL_PORT);

// change password for root user, Gravitino API must set password in catalog properties
try (Connection connection = DriverManager.getConnection(dorisJdbcUrl, USER_NAME, "");
Statement statement = connection.createStatement()) {

String query = String.format("SET PASSWORD FOR '%s' = PASSWORD('%s');", USER_NAME, PASSWORD);
statement.execute(query);
LOG.info("Doris container password has been changed");
return true;
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
mchades marked this conversation as resolved.
Show resolved Hide resolved

return false;
}

public static class Builder
extends BaseContainer.Builder<DorisContainer.Builder, DorisContainer> {
private Builder() {
this.image = DEFAULT_IMAGE;
this.hostName = HOST_NAME;
this.exposePorts = ImmutableSet.of(FE_HTTP_PORT, FE_MYSQL_PORT);
}

@Override
public DorisContainer build() {
return new DorisContainer(
image, hostName, exposePorts, extraHosts, filesToMount, envVars, network);
}
}
}
1 change: 1 addition & 0 deletions integration-test/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ tasks.test {

dependsOn(":trino-connector:jar")
dependsOn(":catalogs:catalog-lakehouse-iceberg:jar", ":catalogs:catalog-lakehouse-iceberg:runtimeJars")
dependsOn(":catalogs:catalog-jdbc-doris:jar", ":catalogs:catalog-jdbc-doris:runtimeJars")
dependsOn(":catalogs:catalog-jdbc-mysql:jar", ":catalogs:catalog-jdbc-mysql:runtimeJars")
dependsOn(":catalogs:catalog-jdbc-postgresql:jar", ":catalogs:catalog-jdbc-postgresql:runtimeJars")
dependsOn(":catalogs:catalog-hadoop:jar", ":catalogs:catalog-hadoop:runtimeJars")
Expand Down
Loading