diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java index 979fa5959..22db3bb43 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/CliMessages.java @@ -631,4 +631,9 @@ default String metadataExistsAlready(Path path, String distName) { default String serverVersionsHeader() { return bundle.getString("prospero.channels.versions.header"); } + + default IllegalArgumentException unknownStabilityLevel(String stabilityLevel, List supportedStabilityLevels) { + return new IllegalArgumentException(String.format(bundle.getString("prospero.install.unknown_stability_level"), + stabilityLevel, String.join(",", supportedStabilityLevels))); + } } diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/AbstractInstallCommand.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/AbstractInstallCommand.java index 739f13843..984688b55 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/AbstractInstallCommand.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/AbstractInstallCommand.java @@ -100,16 +100,14 @@ protected MavenOptions getMavenOptions() throws ArgumentParsingException { return mavenOptions.build(); } - protected ProvisioningDefinition buildDefinition() throws MetadataException, NoChannelException, ArgumentParsingException { - final ProvisioningDefinition provisioningDefinition = ProvisioningDefinition.builder() + protected ProvisioningDefinition.Builder buildDefinition() throws MetadataException, NoChannelException, ArgumentParsingException { + return ProvisioningDefinition.builder() .setFpl(featurePackOrDefinition.fpl.orElse(null)) .setProfile(featurePackOrDefinition.profile.orElse(null)) .setManifest(manifestCoordinate.orElse(null)) .setChannelCoordinates(channelCoordinates) .setOverrideRepositories(RepositoryDefinition.from(remoteRepositories)) - .setDefinitionFile(featurePackOrDefinition.definition.map(Path::toUri).orElse(null)) - .build(); - return provisioningDefinition; + .setDefinitionFile(featurePackOrDefinition.definition.map(Path::toUri).orElse(null)); } protected static VersionResolverFactory createVersionResolverFactory(MavenSessionManager mavenSessionManager) { diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java index ccf964107..681cdac73 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/CliConstants.java @@ -78,6 +78,7 @@ private Commands() { public static final String CHANNELS = "--channels"; public static final String REPOSITORIES = "--repositories"; public static final String SHADE_REPOSITORIES = "--shade-repositories"; + public static final String STABILITY_LEVEL = "--stability-level"; public static final String DEFINITION = "--definition"; public static final String DIR = "--dir"; public static final String FPL = "--fpl"; diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java index 81b7eb181..cf38f54b1 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/InstallCommand.java @@ -21,8 +21,12 @@ import java.net.URL; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.Optional; + +import org.jboss.galleon.Constants; import org.jboss.galleon.api.config.GalleonProvisioningConfig; import org.wildfly.channel.Channel; @@ -73,6 +77,23 @@ public class InstallCommand extends AbstractInstallCommand { ) List shadowRepositories = new ArrayList<>(); + private static final List STABILITY_LEVELS = List.of(Constants.STABILITY_EXPERIMENTAL, + Constants.STABILITY_PREVIEW, + Constants.STABILITY_DEFAULT, + Constants.STABILITY_COMMUNITY); + + private static class StabilityCandidates implements Iterable { + @Override + public Iterator iterator() { + return STABILITY_LEVELS.stream().map(String::toLowerCase).iterator(); + } + } + @CommandLine.Option( + names = CliConstants.STABILITY_LEVEL, + completionCandidates = StabilityCandidates.class + ) + String stabilityLevel; + static class FeaturePackOrDefinition { @CommandLine.Option( @@ -136,6 +157,10 @@ public Integer call() throws Exception { } } + if (stabilityLevel != null && !STABILITY_LEVELS.contains(stabilityLevel.toLowerCase(Locale.ROOT))) { + throw CliMessages.MESSAGES.unknownStabilityLevel(stabilityLevel, STABILITY_LEVELS); + } + if (featurePackOrDefinition.definition.isPresent()) { final Path definition = featurePackOrDefinition.definition.get().toAbsolutePath(); checkFileExists(definition.toUri().toURL(), definition.toString()); @@ -143,7 +168,9 @@ public Integer call() throws Exception { verifyTargetDirectoryIsEmpty(directory); - final ProvisioningDefinition provisioningDefinition = buildDefinition(); + final ProvisioningDefinition provisioningDefinition = buildDefinition() + .setStabilityLevel(stabilityLevel==null?null:stabilityLevel.toLowerCase(Locale.ROOT)) + .build(); final MavenOptions mavenOptions = getMavenOptions(); final GalleonProvisioningConfig provisioningConfig = provisioningDefinition.toProvisioningConfig(); final List channels = resolveChannels(provisioningDefinition, mavenOptions); diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/PrintLicensesCommand.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/PrintLicensesCommand.java index 6f1562043..6b7af39d5 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/PrintLicensesCommand.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/PrintLicensesCommand.java @@ -50,7 +50,7 @@ public Integer call() throws Exception { final Path tempDirectory = Files.createTempDirectory("tmp-installer"); try { - final ProvisioningDefinition provisioningDefinition = buildDefinition(); + final ProvisioningDefinition provisioningDefinition = buildDefinition().build(); final MavenOptions mavenOptions = getMavenOptions(); final GalleonProvisioningConfig provisioningConfig = provisioningDefinition.toProvisioningConfig(); final List channels = resolveChannels(provisioningDefinition, mavenOptions); diff --git a/prospero-cli/src/main/resources/UsageMessages.properties b/prospero-cli/src/main/resources/UsageMessages.properties index e632f500f..e1cf9c609 100644 --- a/prospero-cli/src/main/resources/UsageMessages.properties +++ b/prospero-cli/src/main/resources/UsageMessages.properties @@ -176,6 +176,7 @@ yes = Performs the operation without asking for a confirmation. path = Path of the file to export to or import from. candidate-dir = Path of the server candidate created using the @|bold --update prepare|@ command. rm = Remove the candidate server after applying it. +stability-level = The minimal stability level of provisioned server. Valid options are ${COMPLETION-CANDIDATES}. ${prospero.dist.name}.update.prepare.candidate-dir = Target directory where the candidate server will be provisioned. The existing server is not updated. ${prospero.dist.name}.update.subscribe.product = Specify the product name. This must be a known feature pack supported by ${prospero.dist.name}. @@ -239,6 +240,7 @@ prospero.install.validation.unknown_fpl.details=Either a --channels or a combina needed when using a custom feature pack. prospero.install.validation.unknown_profile=Unknown profile [%s] prospero.install.validation.unknown_profile.details=Did you mean one of [%s]? +prospero.install.unknown_stability_level=Unknown stability level [%s]. Valid levels are: [%s]. prospero.updates.no_updates=No updates found. prospero.updates.header=Updates found: diff --git a/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java b/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java index bca13b14b..08a2424c1 100644 --- a/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java +++ b/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/InstallCommandTest.java @@ -26,6 +26,7 @@ import java.util.List; import java.util.stream.Collectors; +import org.jboss.galleon.Constants; import org.jboss.galleon.universe.FeaturePackLocation; import org.junit.Assert; import org.junit.Before; @@ -51,10 +52,12 @@ import org.wildfly.prospero.cli.ReturnCodes; import org.wildfly.prospero.test.MetadataTestUtils; -import static org.assertj.core.api.Assertions.assertThat; import org.jboss.galleon.api.GalleonBuilder; import org.jboss.galleon.api.Provisioning; import org.jboss.galleon.api.config.GalleonProvisioningConfig; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -271,6 +274,22 @@ public void passShadowRepositories() throws Exception { .contains("file:/test"); } + @Test + public void stabilityLevelIsPassedOn() throws Exception { + final File channelsFile = temporaryFolder.newFile(); + Channel channel = createChannel("test", "test", "http://test.org", "org.test"); + MetadataTestUtils.writeChannels(channelsFile.toPath(), List.of(channel)); + + int exitCode = commandLine.execute(CliConstants.Commands.INSTALL, CliConstants.DIR, "test", + CliConstants.FPL, "org.wildfly:wildfly-ee-galleon-pack", + CliConstants.CHANNELS, channelsFile.getAbsolutePath(), + CliConstants.STABILITY_LEVEL, "default"); + assertEquals(ReturnCodes.SUCCESS, exitCode); + Mockito.verify(provisionAction).provision(configCaptor.capture(), any(), any()); + assertThat(configCaptor.getValue().getOptions()) + .contains(entry(Constants.STABILITY_LEVEL, "default")); + } + @Override protected MavenOptions getCapturedMavenOptions() throws Exception { Mockito.verify(actionFactory).install(any(), mavenOptions.capture(), any()); diff --git a/prospero-common/src/main/java/org/wildfly/prospero/api/ProvisioningDefinition.java b/prospero-common/src/main/java/org/wildfly/prospero/api/ProvisioningDefinition.java index 4b4cc90d0..1b5fcd8e3 100644 --- a/prospero-common/src/main/java/org/wildfly/prospero/api/ProvisioningDefinition.java +++ b/prospero-common/src/main/java/org/wildfly/prospero/api/ProvisioningDefinition.java @@ -33,6 +33,7 @@ import org.apache.commons.io.IOUtils; import org.eclipse.aether.repository.RemoteRepository; +import org.jboss.galleon.Constants; import org.jboss.galleon.ProvisioningException; import org.jboss.galleon.api.config.GalleonFeaturePackConfig; import org.jboss.galleon.api.config.GalleonProvisioningConfig; @@ -87,10 +88,14 @@ public class ProvisioningDefinition { */ private final URI definition; + private final String stabilityLevel; + private ProvisioningDefinition(Builder builder) throws NoChannelException { this.overrideRepositories.addAll(builder.overrideRepositories); this.channelCoordinates.addAll(builder.channelCoordinates); + this.stabilityLevel = builder.stabilityLevel; + if (builder.profile.isPresent()) { if (!KnownFeaturePacks.isWellKnownName(builder.profile.get())) { // if known FP name @@ -146,10 +151,23 @@ public GalleonProvisioningConfig toProvisioningConfig() throws MetadataException FeaturePackLocation loc = FeaturePackLocationParser.resolveFpl(getFpl()); final GalleonFeaturePackConfig.Builder configBuilder = GalleonFeaturePackConfig.builder(loc); - return GalleonProvisioningConfig.builder().addFeaturePackDep(configBuilder.build()).build(); + final GalleonProvisioningConfig.Builder builder = GalleonProvisioningConfig.builder() + .addFeaturePackDep(configBuilder.build()); + if (stabilityLevel != null) { + builder.addOption(Constants.STABILITY_LEVEL, stabilityLevel); + } + return builder.build(); + } else if (definition != null) { try { - return GalleonUtils.loadProvisioningConfig(definition); + final GalleonProvisioningConfig config = GalleonUtils.loadProvisioningConfig(definition); + if (stabilityLevel == null) { + return config; + } else { + return GalleonProvisioningConfig.builder(config) + .addOption(Constants.STABILITY_LEVEL, stabilityLevel) + .build(); + } } catch (XMLStreamException e) { throw ProsperoLogger.ROOT_LOGGER.unableToParseConfigurationUri(definition, e); } @@ -242,6 +260,7 @@ public static class Builder { private Optional manifest = Optional.empty(); private List channelCoordinates = Collections.emptyList(); private Optional profile = Optional.empty(); + private String stabilityLevel; public ProvisioningDefinition build() throws MetadataException, NoChannelException { return new ProvisioningDefinition(this); @@ -286,5 +305,10 @@ public Builder setProfile(String profile) { this.profile = Optional.ofNullable(profile); return this; } + + public Builder setStabilityLevel(String stabilityLevel) { + this.stabilityLevel = stabilityLevel; + return this; + } } } diff --git a/prospero-common/src/test/java/org/wildfly/prospero/api/ProvisioningDefinitionTest.java b/prospero-common/src/test/java/org/wildfly/prospero/api/ProvisioningDefinitionTest.java index b87a00812..2af3d2a61 100644 --- a/prospero-common/src/test/java/org/wildfly/prospero/api/ProvisioningDefinitionTest.java +++ b/prospero-common/src/test/java/org/wildfly/prospero/api/ProvisioningDefinitionTest.java @@ -43,6 +43,7 @@ import javax.xml.stream.XMLStreamException; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; import static org.assertj.core.api.Assertions.tuple; import org.jboss.galleon.api.config.GalleonProvisioningConfig; import static org.junit.Assert.assertEquals; @@ -252,6 +253,32 @@ public void unknownProfileNameThrowsException() throws Exception { }); } + @Test + public void setStabilityLevelWithFpl() throws Exception { + final ProvisioningDefinition.Builder builder = new ProvisioningDefinition.Builder() + .setFpl("custom:fpl") + .setManifest("tmp/foo.bar") + .setStabilityLevel("default") + .setOverrideRepositories(Arrays.asList( + new Repository("temp-repo-0", "http://test.repo1"), + new Repository("temp-repo-1", "http://test.repo2"))); + + final ProvisioningDefinition def = builder.build(); + assertThat(def.toProvisioningConfig().getOptions()) + .contains(entry("stability-level", "default")); + } + + @Test + public void setStabilityLevelWithProfile() throws Exception { + final ProvisioningDefinition.Builder builder = new ProvisioningDefinition.Builder() + .setProfile(EAP_FPL) + .setStabilityLevel("default"); + + final ProvisioningDefinition def = builder.build(); + assertThat(def.toProvisioningConfig().getOptions()) + .contains(entry("stability-level", "default")); + } + private void verifyFeaturePackLocation(ProvisioningDefinition definition) throws ProvisioningException, XMLStreamException { assertNull(definition.getFpl()); GalleonProvisioningConfig galleonConfig = GalleonUtils.loadProvisioningConfig(definition.getDefinition());