-
Notifications
You must be signed in to change notification settings - Fork 380
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Isn't it annotation There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Therefore, I do not recommend putting this class in the package There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In other projects (like Apache kyuubi), they also use Docker container in unit test. so I think it's ok here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
}); | ||
} | ||
} |
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 |
---|---|---|
@@ -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); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.