From 38e0887b6bb3046b454b9eaffac470f695c82e1e Mon Sep 17 00:00:00 2001 From: Bartosz Spyrko-Smietanko Date: Tue, 15 Aug 2023 10:39:25 +0100 Subject: [PATCH] Verify chosen configuration is available in the provisioned server --- .../org/wildfly/prospero/cli/CliMessages.java | 5 ++ .../cli/commands/FeaturesCommand.java | 3 + .../main/resources/UsageMessages.properties | 1 + .../cli/commands/FeaturesCommandTest.java | 15 ++++ .../org/wildfly/prospero/ProsperoLogger.java | 3 + .../prospero/actions/FeaturesAddAction.java | 59 ++++++++++++++ .../galleon/GalleonFeaturePackAnalyzer.java | 2 + .../actions/FeaturesAddActionTest.java | 81 +++++++++++++++++++ 8 files changed, 169 insertions(+) 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 910238270..510df9d35 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 @@ -565,6 +565,11 @@ default String modelNotSupported(String fpl, String model, Set supported fpl, model, StringUtils.join(supportedModels, ", ")); } + default String galleonConfigNotSupported(String fpl, String model, String name) { + return format(bundle.getString("prospero.features.add.validation.configuration.not_supported"), + fpl, model, name); + } + default String featuresAddHeader(String fpl, Path dir) { return format(bundle.getString("prospero.features.add.header"), fpl, dir); } diff --git a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/FeaturesCommand.java b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/FeaturesCommand.java index c40002b2d..47da6f273 100644 --- a/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/FeaturesCommand.java +++ b/prospero-cli/src/main/java/org/wildfly/prospero/cli/commands/FeaturesCommand.java @@ -106,6 +106,9 @@ public Integer call() throws Exception { } catch (FeaturesAddAction.ModelNotDefinedException e) { console.error(CliMessages.MESSAGES.modelNotSupported(fpl, e.getModel(), e.getSupportedModels())); return ReturnCodes.INVALID_ARGUMENTS; + } catch (FeaturesAddAction.ConfigurationNotFoundException e) { + console.error(CliMessages.MESSAGES.galleonConfigNotSupported(fpl, e.getModel(), e.getName())); + return ReturnCodes.INVALID_ARGUMENTS; } final float totalTime = (System.currentTimeMillis() - startTime) / 1000f; diff --git a/prospero-cli/src/main/resources/UsageMessages.properties b/prospero-cli/src/main/resources/UsageMessages.properties index d7c615f1c..5456bfaa2 100644 --- a/prospero-cli/src/main/resources/UsageMessages.properties +++ b/prospero-cli/src/main/resources/UsageMessages.properties @@ -339,6 +339,7 @@ prospero.features.add.validation.layer.no_layers=The feature pack `%s` does not Try removing the --layers parameter. prospero.features.add.validation.model.not_supported=The feature pack `%s` does not provide requested model `%s`.\n \ Supported models are [%s]. +prospero.features.add.validation.configuration.not_supported=The feature pack `%s` does not provide requested configuration `%s/%s`. prospero.changes.diff.manifest=manifest prospero.changes.diff.repositories=repositories diff --git a/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/FeaturesCommandTest.java b/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/FeaturesCommandTest.java index b6c44259f..38cfd2aa8 100644 --- a/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/FeaturesCommandTest.java +++ b/prospero-cli/src/test/java/org/wildfly/prospero/cli/commands/FeaturesCommandTest.java @@ -17,6 +17,7 @@ package org.wildfly.prospero.cli.commands; +import org.jboss.galleon.config.ConfigId; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -248,6 +249,20 @@ public void nonExistingModelShowsError() throws Exception { .contains("Supported models are [model1, model2]"); } + @Test + public void nonExistingConfigurationShowsError() throws Exception { + doThrow(new FeaturesAddAction.ConfigurationNotFoundException("test", new ConfigId("test", "idontexist"))) + .when(featuresAddAction).addFeaturePack("org.test:test", Collections.emptySet(), "test", "idontexist"); + int exitCode = commandLine.execute(CliConstants.Commands.FEATURE_PACKS, CliConstants.Commands.ADD, + CliConstants.DIR, installationDir.toString(), + CliConstants.MODEL, "test", + CliConstants.CONFIG, "idontexist", + CliConstants.FPL, "org.test:test"); + assertEquals(ReturnCodes.INVALID_ARGUMENTS, exitCode); + assertThat(getErrorOutput()) + .contains("The feature pack `org.test:test` does not provide requested configuration `test/idontexist`."); + } + @Override protected ActionFactory createActionFactory() { return actionFactory; diff --git a/prospero-common/src/main/java/org/wildfly/prospero/ProsperoLogger.java b/prospero-common/src/main/java/org/wildfly/prospero/ProsperoLogger.java index 2e983b1eb..787215583 100644 --- a/prospero-common/src/main/java/org/wildfly/prospero/ProsperoLogger.java +++ b/prospero-common/src/main/java/org/wildfly/prospero/ProsperoLogger.java @@ -344,4 +344,7 @@ public interface ProsperoLogger extends BasicLogger { @LogMessage(level = Logger.Level.DEBUG) @Message(id = 258, value = "Adding a feature pack [%s] with configId [%s:%s] and layers [%s]") void addingFeaturePack(FeaturePackLocation fpl, String selectedConfig, String selectedModel, String layers); + + @Message(id = 259, value = "Requested configuration %s/%s is not available in the feature packs.") + String galleonConfigNotFound(String model, String name); } diff --git a/prospero-common/src/main/java/org/wildfly/prospero/actions/FeaturesAddAction.java b/prospero-common/src/main/java/org/wildfly/prospero/actions/FeaturesAddAction.java index 317335501..f66fd292e 100644 --- a/prospero-common/src/main/java/org/wildfly/prospero/actions/FeaturesAddAction.java +++ b/prospero-common/src/main/java/org/wildfly/prospero/actions/FeaturesAddAction.java @@ -31,6 +31,7 @@ import org.jboss.galleon.layout.ProvisioningLayoutFactory; import org.jboss.galleon.universe.FeaturePackLocation; import org.jboss.galleon.universe.maven.repo.MavenRepoManager; +import org.jboss.galleon.util.LayoutUtils; import org.wildfly.channel.ArtifactTransferException; import org.wildfly.channel.Channel; import org.wildfly.channel.ChannelSession; @@ -60,9 +61,11 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.TreeSet; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Installs a feature pack onto an existing server. @@ -170,6 +173,8 @@ public void addFeaturePack(String featurePackCoord, Set layers, String m throw ProsperoLogger.ROOT_LOGGER.unableToCreateTemporaryDirectory(e); } + verifyConfigurationsAvailable(newConfig); + // make sure the previous provisioning_config is persisted try (InstallationMetadata metadata = InstallationMetadata.loadInstallation(installDir)) { metadata.updateProvisioningConfiguration(); @@ -333,6 +338,41 @@ private static void verifyLayerAvailable(Set layers, String selectedMode } } + private void verifyConfigurationsAvailable(ProvisioningConfig config) throws ProvisioningException, OperationException { + try( GalleonEnvironment env = GalleonEnvironment + .builder(installDir, prosperoConfig.getChannels(), mavenSessionManager).build()) { + final MavenRepoManager repositoryManager = env + .getRepositoryManager(); + + final ProvisioningLayoutFactory layoutFactory = GalleonUtils.getProvisioningLayoutFactory(repositoryManager); + + final ProvisioningLayout layout = layoutFactory.newConfigLayout(config); + + final Stream configIds = Stream.concat( + config.getFeaturePackDeps().stream().flatMap(fd -> fd.getIncludedConfigs().stream()), + config.getDefinedConfigs().stream().map(ConfigModel::getId)); + + final Optional missingConfig = configIds.filter(cfg -> { + boolean found = true; + for (FeaturePackLayout fp : layout.getOrderedFeaturePacks()) { + try { + LayoutUtils.getConfigXml(fp.getDir(), cfg, true); + found = true; + break; + } catch (ProvisioningDescriptionException e) { + found = false; + } + } + return !found; + }).findFirst(); + + if (missingConfig.isPresent()) { + final ConfigId cfg = missingConfig.get(); + throw new ConfigurationNotFoundException(ProsperoLogger.ROOT_LOGGER.galleonConfigNotFound(cfg.getModel(), cfg.getName()), cfg); + } + } + } + private static String getSelectedModel(String model, Map> allLayers) throws ModelNotDefinedException { if (allLayers.isEmpty()) { @@ -466,6 +506,25 @@ public FeaturePackAlreadyInstalledException(String msg) { } } + public static class ConfigurationNotFoundException extends OperationException { + private final String model; + private final String name; + + public ConfigurationNotFoundException(String msg, ConfigId id) { + super(msg); + model = id.getModel(); + name = id.getName(); + } + + public String getModel() { + return model; + } + + public String getName() { + return name; + } + } + // used in testing to inject mocks interface CandidateActionsFactory { PrepareCandidateAction newPrepareCandidateActionInstance(MavenSessionManager mavenSessionManager, ProsperoConfig prosperoConfig) throws OperationException; diff --git a/prospero-common/src/main/java/org/wildfly/prospero/galleon/GalleonFeaturePackAnalyzer.java b/prospero-common/src/main/java/org/wildfly/prospero/galleon/GalleonFeaturePackAnalyzer.java index a746db940..f26d8fdf1 100644 --- a/prospero-common/src/main/java/org/wildfly/prospero/galleon/GalleonFeaturePackAnalyzer.java +++ b/prospero-common/src/main/java/org/wildfly/prospero/galleon/GalleonFeaturePackAnalyzer.java @@ -175,4 +175,6 @@ private GalleonEnvironment galleonEnvWithFpMapper(Path tempInstallationPath, Lis return galleonEnv; } + + } diff --git a/prospero-common/src/test/java/org/wildfly/prospero/actions/FeaturesAddActionTest.java b/prospero-common/src/test/java/org/wildfly/prospero/actions/FeaturesAddActionTest.java index f0b7ce541..d5fe25fdb 100644 --- a/prospero-common/src/test/java/org/wildfly/prospero/actions/FeaturesAddActionTest.java +++ b/prospero-common/src/test/java/org/wildfly/prospero/actions/FeaturesAddActionTest.java @@ -264,6 +264,10 @@ public void requestedLayerAddsItsConfig() throws Exception { .addConfigLayer(ConfigLayerSpec.builder() .setModel("model") .setName("layer1") + .build()) + .addConfig(ConfigModel.builder() + .setModel("model") + .setName("model.xml") .build()); deployFeaturePacks(creator); @@ -293,6 +297,10 @@ public void selectedConfigOverridesDefaultWithRequestedLayer() throws Exception .getCreator() .newFeaturePack(FeaturePackLocation.fromString("org.test:added-pack:1.0.0:zip").getFPID()) .addDependency(FeaturePackLocation.fromString("org.test:base-pack:1.0.0")) + .addConfig(ConfigModel.builder() + .setModel("model") + .setName("test.xml") + .build()) .addConfigLayer(ConfigLayerSpec.builder() .setModel("model") .setName("layer1") @@ -324,6 +332,10 @@ public void addFeaturePackAlreadyInstalledAsDependency() throws Exception { creator.newFeaturePack(FeaturePackLocation.fromString("org.test:base-pack:1.0.0:zip").getFPID()) .getCreator() .newFeaturePack(FeaturePackLocation.fromString("org.test:added-pack:1.0.0:zip").getFPID()) + .addConfig(ConfigModel.builder() + .setModel("model") + .setName("test.xml") + .build()) .addConfigLayer(ConfigLayerSpec.builder() .setModel("model") .setName("layer1") @@ -370,6 +382,10 @@ public void installingLayerOverridesExcludesLayer() throws Exception { // install base feature pack final FeaturePackCreator creator = FeaturePackCreator.getInstance().addArtifactResolver(repo); creator.newFeaturePack(FeaturePackLocation.fromString("org.test:base-pack:1.0.0:zip").getFPID()) + .addConfig(ConfigModel.builder() + .setModel("model") + .setName("test.xml") + .build()) .addConfigLayer(ConfigLayerSpec.builder() .setModel("model") .setName("layer1") @@ -474,6 +490,10 @@ public void selectedModelOverridesTheDefault() throws Exception { creator.newFeaturePack(FeaturePackLocation.fromString("org.test:base-pack:1.0.0:zip").getFPID()) .getCreator() .newFeaturePack(FeaturePackLocation.fromString("org.test:added-pack:1.0.0:zip").getFPID()) + .addConfig(ConfigModel.builder() + .setModel("model2") + .setName("model2.xml") + .build()) .addConfigLayer(ConfigLayerSpec.builder() .setModel("model1") .setName("layer1") @@ -615,6 +635,7 @@ public void installSelectedConfigsIfNoLayersAreSpecified() throws Exception { .getFeaturePack() .addConfig(ConfigModel.builder() + .setModel("model1") .setName("config2") .includeLayer("layer1") .addPackageDep("p1") @@ -664,6 +685,10 @@ public void installSelectedConfigsIfLayersAreSpecified() throws Exception { .writeContent("p3.txt", "foobar") .getFeaturePack() + .addConfig(ConfigModel.builder() + .setModel("model1") + .setName("model1.xml") + .build()) .addConfig(ConfigModel.builder() .setName("config2") .includeLayer("layer1") @@ -699,6 +724,62 @@ public void installSelectedConfigsIfLayersAreSpecified() throws Exception { .isEmpty(); } + @Test + public void nonExistingConfigNameThrowsException() throws Exception { + // install base feature pack + final FeaturePackCreator creator = FeaturePackCreator.getInstance().addArtifactResolver(repo); + creator.newFeaturePack(FeaturePackLocation.fromString("org.test:base-pack:1.0.0:zip").getFPID()) + + .getCreator() + .newFeaturePack(FeaturePackLocation.fromString("org.test:added-pack:1.0.0:zip").getFPID()) + .addConfig(ConfigModel.builder() + .setModel("model1") + .setName("config3") + .build()) + .addConfigLayer(ConfigLayerSpec.builder() + .setModel("model1") + .setName("layer1") + .build()); + deployFeaturePacks(creator); + // install + installFeaturePack(installDir, "org.test:base-pack:1.0.0:zip"); + + assertThatThrownBy(()->getFeaturesAddAction().addFeaturePack( + "org.test:added-pack", Set.of("layer1"), "model1", "idontexist")) + .isInstanceOf(FeaturesAddAction.ConfigurationNotFoundException.class) + .hasFieldOrPropertyWithValue("model", "model1") + .hasFieldOrPropertyWithValue("name", "idontexist"); + + } + + @Test + public void nonExistingConfigNameWithoutLayersThrowsException() throws Exception { + // install base feature pack + final FeaturePackCreator creator = FeaturePackCreator.getInstance().addArtifactResolver(repo); + creator.newFeaturePack(FeaturePackLocation.fromString("org.test:base-pack:1.0.0:zip").getFPID()) + + .getCreator() + .newFeaturePack(FeaturePackLocation.fromString("org.test:added-pack:1.0.0:zip").getFPID()) + .addConfig(ConfigModel.builder() + .setModel("model1") + .setName("config3") + .build()) + .addConfigLayer(ConfigLayerSpec.builder() + .setModel("model1") + .setName("layer1") + .build()); + deployFeaturePacks(creator); + // install + installFeaturePack(installDir, "org.test:base-pack:1.0.0:zip"); + + assertThatThrownBy(()->getFeaturesAddAction().addFeaturePack( + "org.test:added-pack", Collections.emptySet(), new ConfigId("model1", "idontexist"))) + .isInstanceOf(FeaturesAddAction.ConfigurationNotFoundException.class) + .hasFieldOrPropertyWithValue("model", "model1") + .hasFieldOrPropertyWithValue("name", "idontexist"); + + } + private static FeaturePackConfig getFeaturePackConfig(ProvisioningConfig config, String fpl) { return config.getFeaturePackDeps().stream().filter(f -> f.getLocation().toString().equals(fpl)).findFirst().get(); }