Database configuration tests

Closes #9293
This commit is contained in:
Pedro Igor 2021-12-21 13:45:16 -03:00
parent d39eb95705
commit 981cda5bfd
15 changed files with 435 additions and 9 deletions

View file

@ -425,6 +425,13 @@ jobs:
find . -path '*/target/surefire-reports/*.xml' | zip -q reports-quarkus-tests.zip -@ find . -path '*/target/surefire-reports/*.xml' | zip -q reports-quarkus-tests.zip -@
exit $TEST_RESULT exit $TEST_RESULT
- name: Run Quarkus Storage Tests
run: |
mvn clean install -nsu -B -f quarkus/tests/pom.xml -Ptest-database -Dtest=PostgreSQLStartDatabaseTest | misc/log/trimmer.sh
TEST_RESULT=${PIPESTATUS[0]}
find . -path '*/target/surefire-reports/*.xml' | zip -q reports-quarkus-tests.zip -@
exit $TEST_RESULT
- name: Quarkus test reports - name: Quarkus test reports
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v2
if: failure() if: failure()

View file

@ -52,9 +52,8 @@ import java.util.jar.JarEntry;
import java.util.jar.JarFile; import java.util.jar.JarFile;
import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.agroal.spi.JdbcDataSourceBuildItem;
import io.quarkus.datasource.deployment.spi.DevServicesDatasourceResultBuildItem;
import io.quarkus.deployment.IsDevelopment; import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.IsTest;
import io.quarkus.deployment.annotations.Consume; import io.quarkus.deployment.annotations.Consume;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem; import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
@ -84,6 +83,8 @@ import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
import org.jboss.resteasy.spi.ResteasyDeployment; import org.jboss.resteasy.spi.ResteasyDeployment;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource; import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper;
import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers;
import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication; import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication;
import org.keycloak.authentication.AuthenticatorSpi; import org.keycloak.authentication.AuthenticatorSpi;
import org.keycloak.authentication.authenticators.browser.DeployedScriptAuthenticatorFactory; import org.keycloak.authentication.authenticators.browser.DeployedScriptAuthenticatorFactory;
@ -273,8 +274,32 @@ class KeycloakProcessor {
} }
@BuildStep(onlyIf = IsIntegrationTest.class) @BuildStep(onlyIf = IsIntegrationTest.class)
void prepareTestEnvironment(BuildProducer<StaticInitConfigSourceProviderBuildItem> configSources) { void prepareTestEnvironment(BuildProducer<StaticInitConfigSourceProviderBuildItem> configSources, DevServicesDatasourceResultBuildItem dbConfig) {
configSources.produce(new StaticInitConfigSourceProviderBuildItem("org.keycloak.quarkus.runtime.configuration.test.TestKeycloakConfigSourceProvider")); configSources.produce(new StaticInitConfigSourceProviderBuildItem("org.keycloak.quarkus.runtime.configuration.test.TestKeycloakConfigSourceProvider"));
// we do not enable dev services by default and the DevServicesDatasourceResultBuildItem might not be available when discovering build steps
// Quarkus seems to allow that when the DevServicesDatasourceResultBuildItem is not the only parameter to the build step
// this might be too sensitive and break if Quarkus changes the behavior
if (dbConfig != null && dbConfig.getDefaultDatasource() != null) {
Map<String, String> configProperties = dbConfig.getDefaultDatasource().getConfigProperties();
for (Entry<String, String> dbConfigProperty : configProperties.entrySet()) {
PropertyMapper mapper = PropertyMappers.getMapper(dbConfigProperty.getKey());
if (mapper == null) {
continue;
}
String kcProperty = mapper.getFrom();
if (kcProperty.endsWith("db")) {
// db kind set when running tests
continue;
}
System.setProperty(kcProperty, dbConfigProperty.getValue());
}
}
} }
/** /**

View file

@ -395,8 +395,9 @@ public final class Picocli {
while (iterator.hasNext()) { while (iterator.hasNext()) {
String arg = iterator.next(); String arg = iterator.next();
if (arg.startsWith("--spi")) { if (arg.startsWith("--spi") || arg.startsWith("-D")) {
// TODO: ignore properties for providers for now, need to fetch them from the providers, otherwise CLI will complain about invalid options // TODO: ignore properties for providers for now, need to fetch them from the providers, otherwise CLI will complain about invalid options
// also ignores system properties as they are set when starting the JVM
// change this once we are able to obtain properties from providers // change this once we are able to obtain properties from providers
iterator.remove(); iterator.remove();

View file

@ -90,4 +90,27 @@
</plugins> </plugins>
</build> </build>
<profiles>
<profile>
<id>test-database</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Xmx1024m -XX:MaxMetaspaceSize=512m</argLine>
<systemProperties>
<kc.test.storage.database>true</kc.test.storage.database>
</systemProperties>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project> </project>

View file

@ -25,7 +25,9 @@ import static org.keycloak.quarkus.runtime.cli.command.Main.CONFIG_FILE_SHORT_NA
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import io.quarkus.dev.console.QuarkusConsole; import io.quarkus.dev.console.QuarkusConsole;
@ -48,6 +50,7 @@ public class CLITestExtension extends QuarkusMainTestExtension {
private static final String KEY_VALUE_SEPARATOR = "[= ]"; private static final String KEY_VALUE_SEPARATOR = "[= ]";
private KeycloakDistribution dist; private KeycloakDistribution dist;
private final Set<String> testSysProps = new HashSet<>();
@Override @Override
public void beforeEach(ExtensionContext context) throws Exception { public void beforeEach(ExtensionContext context) throws Exception {
@ -59,13 +62,23 @@ public class CLITestExtension extends QuarkusMainTestExtension {
if (arg.contains(CONFIG_FILE_SHORT_NAME) || arg.contains(CONFIG_FILE_LONG_NAME)) { if (arg.contains(CONFIG_FILE_SHORT_NAME) || arg.contains(CONFIG_FILE_LONG_NAME)) {
Pattern kvSeparator = Pattern.compile(KEY_VALUE_SEPARATOR); Pattern kvSeparator = Pattern.compile(KEY_VALUE_SEPARATOR);
String[] cfKeyValue = kvSeparator.split(arg); String[] cfKeyValue = kvSeparator.split(arg);
System.setProperty(KeycloakPropertiesConfigSource.KEYCLOAK_CONFIG_FILE_PROP, cfKeyValue[1]); setProperty(KeycloakPropertiesConfigSource.KEYCLOAK_CONFIG_FILE_PROP, cfKeyValue[1]);
} else if (distConfig == null && arg.startsWith("-D")) {
// allow setting system properties from JVM tests
int keyValueSeparator = arg.indexOf('=');
if (keyValueSeparator == -1) {
continue;
}
String name = arg.substring(2, keyValueSeparator);
String value = arg.substring(keyValueSeparator + 1);
setProperty(name, value);
} }
} }
} }
if (distConfig != null) { if (distConfig != null) {
if (launch != null) { if (launch != null) {
if (dist == null) { if (dist == null) {
dist = createDistribution(distConfig); dist = createDistribution(distConfig);
@ -74,6 +87,7 @@ public class CLITestExtension extends QuarkusMainTestExtension {
} }
} else { } else {
configureProfile(context); configureProfile(context);
configureDatabase(context);
// WORKAROUND: this intercepts the output when actually starting the server. // WORKAROUND: this intercepts the output when actually starting the server.
QuarkusConsole.installRedirects(); QuarkusConsole.installRedirects();
super.beforeEach(context); super.beforeEach(context);
@ -97,10 +111,12 @@ public class CLITestExtension extends QuarkusMainTestExtension {
private void reset() { private void reset() {
QuarkusConfigFactory.setConfig(null); QuarkusConfigFactory.setConfig(null);
//remove the config file property if set, and also the profile, to not have side effects in other tests. //remove the config file property if set, and also the profile, to not have side effects in other tests.
System.getProperties().remove(KeycloakPropertiesConfigSource.KEYCLOAK_CONFIG_FILE_PROP);
System.getProperties().remove(Environment.PROFILE); System.getProperties().remove(Environment.PROFILE);
System.getProperties().remove("quarkus.profile"); System.getProperties().remove("quarkus.profile");
TestConfigArgsConfigSource.setCliArgs(new String[0]); TestConfigArgsConfigSource.setCliArgs(new String[0]);
for (String property : testSysProps) {
System.getProperties().remove(property);
}
} }
@Override @Override
@ -181,6 +197,28 @@ public class CLITestExtension extends QuarkusMainTestExtension {
} }
} }
private void configureDatabase(ExtensionContext context) {
WithDatabase database = context.getTestClass().orElse(Object.class).getDeclaredAnnotation(WithDatabase.class);
if (database != null) {
configureDevServices();
setProperty("kc.db", database.alias());
// databases like mssql are very strict about password policy
setProperty("kc.db.password", "Password1!");
}
}
private void configureDevServices() {
setProperty("quarkus.vault.devservices.enabled", Boolean.FALSE.toString());
setProperty("quarkus.datasource.devservices.enabled", Boolean.TRUE.toString());
setProperty("quarkus.devservices.enabled", Boolean.TRUE.toString());
}
private void setProperty(String name, String value) {
System.setProperty(name, value);
testSysProps.add(name);
}
private List<String> getCliArgs(ExtensionContext context) { private List<String> getCliArgs(ExtensionContext context) {
Launch annotation = context.getRequiredTestMethod().getAnnotation(Launch.class); Launch annotation = context.getRequiredTestMethod().getAnnotation(Launch.class);

View file

@ -0,0 +1,40 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.it.junit5.extension;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
/**
* {@link WithDatabase} is used to start a database container.
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@EnabledIfSystemProperty(named = "kc.test.storage.database", matches = "true", disabledReason = "Docker takes too much time and stability depends on the environment. We should try running these tests in CI but isolated.")
public @interface WithDatabase {
/**
* The database name as per database aliases.
*
* @return the database alias
*/
String alias();
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.it.storage.database;
import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
public abstract class AbstractStartDabataseTest {
@Test
@Launch({ "start-dev" })
void testSuccessful(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertStartedDevMode();
}
@Test
@Launch({ "start-dev", "--db-username=wrong" })
void testWrongUsername(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertMessage("ERROR: Failed to obtain JDBC connection");
assertWrongUsername(cliResult);
}
protected abstract void assertWrongUsername(CLIResult cliResult);
@Test
@Launch({ "start-dev", "--db-password=wrong" })
void testWrongPassword(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertMessage("ERROR: Failed to obtain JDBC connection");
assertWrongPassword(cliResult);
}
protected abstract void assertWrongPassword(CLIResult cliResult);
}

View file

@ -0,0 +1,54 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.it.storage.database;
import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.CLITest;
import org.keycloak.it.junit5.extension.WithDatabase;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
@CLITest
@WithDatabase(alias = "mssql")
public class MSSQLStartDatabaseTest extends AbstractStartDabataseTest {
/**
* It should be possible to enable XA but here we reproduce a managed environment where only nonXA transaction is supported
*
* @param result
*/
@Override
@Test
@Launch({ "-Dkc.db.tx-type=enabled", "-Dkc.db.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver", "start-dev" })
void testSuccessful(LaunchResult result) {
CLIResult cliResult = (CLIResult) result;
cliResult.assertStartedDevMode();
}
@Override
protected void assertWrongUsername(CLIResult cliResult) {
cliResult.assertMessage("ERROR: Login failed for user 'wrong'");
}
@Override
protected void assertWrongPassword(CLIResult cliResult) {
cliResult.assertMessage("Login failed for user");
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.it.storage.database;
import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.CLITest;
import org.keycloak.it.junit5.extension.WithDatabase;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
@CLITest
@WithDatabase(alias = "mariadb")
public class MariaDBStartDatabaseTest extends AbstractStartDabataseTest {
@Override
protected void assertWrongPassword(CLIResult cliResult) {
cliResult.assertMessage("Access denied for user");
}
@Override
protected void assertWrongUsername(CLIResult cliResult) {
cliResult.assertMessage("Access denied for user 'wrong'");
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.it.storage.database;
import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.CLITest;
import org.keycloak.it.junit5.extension.WithDatabase;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
@CLITest
@WithDatabase(alias = "mysql")
public class MySQLStartDatabaseTest extends AbstractStartDabataseTest {
@Override
protected void assertWrongUsername(CLIResult cliResult) {
cliResult.assertMessage("ERROR: Access denied for user 'wrong'");
}
@Override
protected void assertWrongPassword(CLIResult cliResult) {
cliResult.assertMessage("ERROR: Access denied for user");
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.it.storage.database;
import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.CLITest;
import org.keycloak.it.junit5.extension.WithDatabase;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
@CLITest
@WithDatabase(alias = "oracle")
public class OracleStartDatabaseTest extends AbstractStartDabataseTest {
@Override
protected void assertWrongUsername(CLIResult cliResult) {
cliResult.assertMessage("ORA-01017: invalid username/password; logon denied");
}
@Override
protected void assertWrongPassword(CLIResult cliResult) {
cliResult.assertMessage("ORA-01017: invalid username/password; logon denied");
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2021 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.it.storage.database;
import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult;
import org.keycloak.it.junit5.extension.CLITest;
import org.keycloak.it.junit5.extension.WithDatabase;
import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult;
@CLITest
@WithDatabase(alias = "postgres")
public class PostgreSQLStartDatabaseTest extends AbstractStartDabataseTest {
@Override
protected void assertWrongUsername(CLIResult cliResult) {
cliResult.assertMessage("ERROR: FATAL: password authentication failed for user \"wrong\"");
}
@Override
protected void assertWrongPassword(CLIResult cliResult) {
cliResult.assertMessage("ERROR: FATAL: password authentication failed for user");
}
}

View file

@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
package org.keycloak.it.database.dist; package org.keycloak.it.storage.database.dist;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.keycloak.it.junit5.extension.CLIResult; import org.keycloak.it.junit5.extension.CLIResult;
@ -25,7 +25,7 @@ import io.quarkus.test.junit.main.Launch;
import io.quarkus.test.junit.main.LaunchResult; import io.quarkus.test.junit.main.LaunchResult;
@DistributionTest @DistributionTest
public class CustomTransactionTest { public class CustomTransactionDistTest {
@Test @Test
@Launch({ "-Dkc.db.tx-type=enabled", "-Dkc.db.driver=org.postgresql.xa.PGXADataSource", "build", "--db=postgres" }) @Launch({ "-Dkc.db.tx-type=enabled", "-Dkc.db.driver=org.postgresql.xa.PGXADataSource", "build", "--db=postgres" })

View file

@ -0,0 +1,19 @@
#
# Copyright 2021 Red Hat, Inc. and/or its affiliates
# and other contributors as indicated by the @author tags.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
quarkus.vault.devservices.enabled=false
quarkus.datasource.devservices.enabled=false
quarkus.devservices.enabled=true

View file

@ -0,0 +1 @@
mcr.microsoft.com/mssql/server:2019-CU10-ubuntu-20.04