-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1268 from michalvavrik/feature/tls-cli-command
Add support for Quarkus CLI TLS command
- Loading branch information
Showing
18 changed files
with
667 additions
and
164 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
82 changes: 82 additions & 0 deletions
82
examples/quarkus-cli/src/test/java/io/quarkus/qe/QuarkusCliTlsCommandIT.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package io.quarkus.qe; | ||
|
||
import jakarta.inject.Inject; | ||
|
||
import org.junit.jupiter.api.MethodOrderer; | ||
import org.junit.jupiter.api.Order; | ||
import org.junit.jupiter.api.Tag; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.TestMethodOrder; | ||
|
||
import io.quarkus.qe.surefire.TlsCommandTest; | ||
import io.quarkus.test.bootstrap.tls.GenerateCertOptions; | ||
import io.quarkus.test.bootstrap.tls.GenerateQuarkusCaOptions; | ||
import io.quarkus.test.bootstrap.tls.QuarkusTlsCommand; | ||
import io.quarkus.test.scenarios.QuarkusScenario; | ||
import io.quarkus.test.scenarios.annotations.DisabledOnQuarkusVersion; | ||
import io.quarkus.test.scenarios.annotations.DisabledOnQuarkusVersions; | ||
|
||
@DisabledOnQuarkusVersions({ | ||
// disable on 3.9-3.13 | ||
@DisabledOnQuarkusVersion(version = "3\\.(9|10|11|12|13)\\..*", reason = "https://github.com/quarkusio/quarkus/issues/42752"), | ||
// disable on 3.14.0 and 3.14.1 as the fix is going to be backported to the next release | ||
@DisabledOnQuarkusVersion(version = "3\\.14\\.(0|1)", reason = "https://github.com/quarkusio/quarkus/issues/42752") | ||
}) | ||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) | ||
@Tag("quarkus-cli") | ||
@QuarkusScenario | ||
public class QuarkusCliTlsCommandIT { | ||
|
||
@Inject | ||
static QuarkusTlsCommand tlsCommand; | ||
|
||
@Order(1) | ||
@Test | ||
public void generateQuarkusCa() { | ||
tlsCommand | ||
.generateQuarkusCa() | ||
.withOption(GenerateQuarkusCaOptions.TRUSTSTORE_LONG) | ||
.withOption(GenerateQuarkusCaOptions.RENEW_SHORT) | ||
.executeCommand() | ||
.assertCommandOutputContains("Generating Quarkus Dev CA certificate") | ||
.assertCommandOutputContains("Truststore generated successfully") | ||
.assertFileExistsStr(cmd -> cmd.getOutputLineRemainder("Truststore generated successfully:")); | ||
} | ||
|
||
@Order(2) | ||
@Test | ||
public void generateCertificate() { | ||
// prepares state for assertion in TlsCommandTest | ||
var appSvcDir = tlsCommand.getApp().getServiceFolder().toAbsolutePath().toString(); | ||
tlsCommand | ||
.generateCertificate() | ||
.withOption(GenerateCertOptions.COMMON_NAME_SHORT, "Dumbledore") | ||
.withOption(GenerateCertOptions.NAME_LONG, "dev-certificate") | ||
.withOption(GenerateCertOptions.PASSWORD_LONG, "quarkus") | ||
.withOption(GenerateCertOptions.DIRECTORY_SHORT, appSvcDir) | ||
.executeCommand() | ||
.assertCommandOutputContains("Quarkus Dev CA certificate found at") | ||
.assertCommandOutputContains("PKCS12 keystore and truststore generated successfully!") | ||
.assertFileExistsStr(cmd -> cmd.getOutputLineRemainder("Key Store File:")) | ||
.assertFileExistsStr(cmd -> cmd.getOutputLineRemainder("Trust Store File:")) | ||
.addToAppProps(cmd -> { | ||
// move to application properties under test profile | ||
var key = "%dev.quarkus.tls.key-store.p12.path"; | ||
var val = cmd.getPropertyValueFromEnvFile(key); | ||
return "%test.quarkus.tls.key-store.p12.path=" + val; | ||
}) | ||
.addToAppProps(cmd -> { | ||
// move to application properties under test profile | ||
var key = "%dev.quarkus.tls.key-store.p12.password"; | ||
var val = cmd.getPropertyValueFromEnvFile(key); | ||
return "%test.quarkus.tls.key-store.p12.password=" + val; | ||
}); | ||
} | ||
|
||
@Order(3) | ||
@Test | ||
public void runTestsUsingGeneratedCerts() { | ||
// runs TlsCommandTest that verifies cert generation | ||
tlsCommand.buildAppAndExpectSuccess(TlsCommandTest.class); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
examples/quarkus-cli/src/test/java/io/quarkus/qe/surefire/TlsCommandTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package io.quarkus.qe.surefire; | ||
|
||
import java.security.KeyStoreException; | ||
import java.security.NoSuchAlgorithmException; | ||
import java.security.UnrecoverableKeyException; | ||
import java.util.HashSet; | ||
|
||
import jakarta.inject.Inject; | ||
|
||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import io.quarkus.test.junit.QuarkusTest; | ||
import io.quarkus.tls.TlsConfigurationRegistry; | ||
|
||
/** | ||
* This test is only supported to run inside QuarkusCliTlsCommandIT. | ||
*/ | ||
@QuarkusTest | ||
public class TlsCommandTest { | ||
|
||
@Inject | ||
TlsConfigurationRegistry registry; | ||
|
||
@Test | ||
void testKeystoreInDefaultTlsRegistry() throws KeyStoreException { | ||
var defaultRegistry = registry.getDefault() | ||
.orElseThrow(() -> new AssertionError("Default TLS Registry is not configured")); | ||
var ks = defaultRegistry.getKeyStore(); | ||
var ksAliasesSet = new HashSet<String>(); | ||
var ksAliases = ks.aliases(); | ||
while (ksAliases.hasMoreElements()) { | ||
ksAliasesSet.add(ksAliases.nextElement()); | ||
} | ||
// if this changes to something sensible, it's not an issue | ||
// basically what we try to assert here is that: | ||
// 1. keystore is configured | ||
// 2. it has some aliases that can be used | ||
Assertions.assertTrue(ksAliasesSet.contains("dev-certificate")); | ||
Assertions.assertTrue(ksAliasesSet.contains("issuer-dev-certificate")); | ||
|
||
try { | ||
// check we do know password | ||
var key = ks.getKey("dev-certificate", "quarkus".toCharArray()); | ||
Assertions.assertNotNull(key); | ||
} catch (NoSuchAlgorithmException | UnrecoverableKeyException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
} |
121 changes: 121 additions & 0 deletions
121
quarkus-test-cli/src/main/java/io/quarkus/test/bootstrap/AbstractCliCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package io.quarkus.test.bootstrap; | ||
|
||
import static java.util.stream.Collectors.toSet; | ||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.UUID; | ||
import java.util.stream.Stream; | ||
|
||
import org.junit.jupiter.api.Assertions; | ||
|
||
import io.quarkus.test.utils.ClassPathUtils; | ||
import io.quarkus.test.utils.FileUtils; | ||
|
||
public abstract class AbstractCliCommand { | ||
|
||
protected final QuarkusCliClient cliClient; | ||
protected final QuarkusCliRestService app; | ||
|
||
public AbstractCliCommand(String appName, QuarkusCliClient.CreateApplicationRequest appReq, | ||
QuarkusCliClient cliClient, File testDirectory) { | ||
this.cliClient = cliClient; | ||
testDirectory.mkdirs(); | ||
this.app = this.cliClient.createApplication(appName, appReq, testDirectory.getAbsolutePath()); | ||
} | ||
|
||
public AbstractCliCommand(String appName, String targetSubDir, QuarkusCliClient.CreateApplicationRequest appReq, | ||
QuarkusCliClient cliClient) { | ||
this(appName, appReq, cliClient, | ||
Path.of("target").resolve(targetSubDir).resolve(UUID.randomUUID().toString()).toFile()); | ||
} | ||
|
||
public abstract AbstractCliCommand addToApplicationProperties(String... additions); | ||
|
||
public File getApplicationProperties() { | ||
var pathToSrcMainResources = "src" + File.separator + "main" + File.separator + "resources"; | ||
return app.getFileFromApplication(pathToSrcMainResources, "application.properties"); | ||
} | ||
|
||
public String getApplicationPropertiesAsString() { | ||
return FileUtils.loadFile(getApplicationProperties()); | ||
} | ||
|
||
public QuarkusCliCommandResult buildAppAndExpectSuccess(Class<?>... unitTests) { | ||
copyUnitTestsToCreatedApp(unitTests); | ||
return buildAppAndExpectSuccess(); | ||
} | ||
|
||
public QuarkusCliCommandResult buildAppAndExpectSuccess() { | ||
var result = app.buildOnJvm(); | ||
assertTrue(result.isSuccessful(), | ||
"Expected successful JVM build, but build command failed with output: " + result.getOutput()); | ||
return new QuarkusCliCommandResult(result.getOutput(), getApplicationPropertiesAsString(), this); | ||
} | ||
|
||
public QuarkusCliCommandResult buildAppAndExpectFailure(Class<?>... unitTests) { | ||
copyUnitTestsToCreatedApp(unitTests); | ||
return buildAppAndExpectFailure(); | ||
} | ||
|
||
public QuarkusCliCommandResult buildAppAndExpectFailure() { | ||
var result = app.buildOnJvm(); | ||
assertFalse(result.isSuccessful(), | ||
"Expected JVM build failure, but build command succeed with output: " + result.getOutput()); | ||
return new QuarkusCliCommandResult(result.getOutput(), getApplicationPropertiesAsString(), this); | ||
} | ||
|
||
public void removeApplicationProperties() { | ||
var appProps = getApplicationProperties(); | ||
if (!appProps.delete()) { | ||
throw new IllegalStateException("Failed to delete application.properties file: " + appProps); | ||
} | ||
} | ||
|
||
protected QuarkusCliCommandResult runCommand(String baseCmd, List<String> subCmdArgs) { | ||
var allConfigCommandArgs = new ArrayList<>(); | ||
allConfigCommandArgs.add(baseCmd); | ||
allConfigCommandArgs.addAll(subCmdArgs); | ||
var result = cliClient.run(app.getServiceFolder(), allConfigCommandArgs.toArray(String[]::new)); | ||
if (!result.isSuccessful()) { | ||
Assertions.fail("Quarkus %s command with arguments '%s' failed with output: %s".formatted(baseCmd, | ||
allConfigCommandArgs, result.getOutput())); | ||
} | ||
return new QuarkusCliCommandResult(result.getOutput(), getApplicationPropertiesAsString(), this); | ||
} | ||
|
||
private void copyUnitTestsToCreatedApp(Class<?>[] unitTests) { | ||
if (unitTests == null || unitTests.length == 0) { | ||
return; | ||
} | ||
var normalizedUnitTests = Stream.of(unitTests).map(Class::getName).collect(toSet()); | ||
var srcTestJavaPath = Path.of("src").resolve("test").resolve("java"); | ||
try (Stream<Path> stream = Files.walk(srcTestJavaPath)) { | ||
stream | ||
.filter(path -> path.toString().endsWith(".java")) | ||
.filter(path -> { | ||
var normalizedClassName = ClassPathUtils.normalizeClassName(path.toString(), ".java"); | ||
return normalizedUnitTests.stream().anyMatch(normalizedClassName::endsWith); | ||
}) | ||
.map(Path::toFile) | ||
.forEach(unitTestFile -> FileUtils.copyFileTo(unitTestFile, | ||
app.getServiceFolder().resolve(srcTestJavaPath))); | ||
} catch (IOException e) { | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
public QuarkusCliRestService getApp() { | ||
return app; | ||
} | ||
|
||
public QuarkusCliClient getCliClient() { | ||
return cliClient; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.