diff --git a/.gitignore b/.gitignore index cfd128ea..72137309 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ target cli/dependency-reduced-pom.xml +tests/.env # Intellij *.iml diff --git a/cli/src/main/java/org/wildfly/glow/cli/commands/ScanCommand.java b/cli/src/main/java/org/wildfly/glow/cli/commands/ScanCommand.java index ba8f66e0..8b100352 100644 --- a/cli/src/main/java/org/wildfly/glow/cli/commands/ScanCommand.java +++ b/cli/src/main/java/org/wildfly/glow/cli/commands/ScanCommand.java @@ -26,11 +26,11 @@ import org.wildfly.glow.Arguments; import static org.wildfly.glow.Arguments.CLOUD_EXECUTION_CONTEXT; import static org.wildfly.glow.Arguments.COMPACT_PROPERTY; -import org.wildfly.glow.DockerSupport; import org.wildfly.glow.FeaturePacks; import org.wildfly.glow.GlowMessageWriter; import org.wildfly.glow.GlowSession; import org.wildfly.glow.HiddenPropertiesAccessor; +import org.wildfly.glow.OutputContent; import org.wildfly.glow.OutputFormat; import static org.wildfly.glow.OutputFormat.BOOTABLE_JAR; import static org.wildfly.glow.OutputFormat.DOCKER_IMAGE; @@ -38,6 +38,7 @@ import static org.wildfly.glow.OutputFormat.SERVER; import org.wildfly.glow.ScanArguments.Builder; import org.wildfly.glow.ScanResults; +import org.wildfly.glow.error.IdentifiedError; import org.wildfly.glow.maven.MavenResolver; import picocli.CommandLine; @@ -49,6 +50,10 @@ ) public class ScanCommand extends AbstractCommand { + private static final String ADD_ADD_ONS_MSG="@|bold To enable add-ons add the|@ @|fg(yellow) " + + Constants.ADD_ONS_OPTION + "=|@ @|bold option to the|@ @|fg(yellow) " + + Constants.SCAN_COMMAND + "|@ @|bold command|@"; + @CommandLine.Option(names = {Constants.CLOUD_OPTION_SHORT, Constants.CLOUD_OPTION}) Optional cloud; @@ -140,58 +145,106 @@ public Integer call() throws Exception { if (!compact) { if (suggest.orElse(false)) { if (!scanResults.getSuggestions().getPossibleAddOns().isEmpty() && addOns.isEmpty()) { - print("@|bold To enable add-ons add the|@ @|fg(yellow) %s=|@ @|bold option to the|@ @|fg(yellow) %s|@ @|bold command|@", Constants.ADD_ONS_OPTION, Constants.SCAN_COMMAND); + print(ADD_ADD_ONS_MSG); } if (!scanResults.getSuggestions().getPossibleProfiles().isEmpty()) { print("@|bold To enable the HA profile add the|@ @|fg(yellow) %s|@ @|bold option to the|@ @|fg(yellow) %s|@ @|bold command|@", Constants.HA_OPTION, Constants.SCAN_COMMAND); } } - print("@|bold To provision the WildFly server for your deployment add the|@ @|fg(yellow) %s=SERVER|@ @|bold option to the|@ @|fg(yellow) %s|@ @|bold command|@", Constants.PROVISION_OPTION, Constants.SCAN_COMMAND); + if (scanResults.getErrorSession().hasErrors()) { + if (!suggest.orElse(false)) { + boolean hasAddOn = false; + // Do we have errors and add-ons to set? + for(IdentifiedError err : scanResults.getErrorSession().getErrors()) { + if (!err.getPossibleAddons().isEmpty()) { + hasAddOn = true; + break; + } + } + if (hasAddOn) { + System.out.println(CommandLine.Help.Ansi.AUTO.string(ADD_ADD_ONS_MSG)); + } + } + print("@|bold Some errors have been reported, you should fix them prior to provision a server with the|@ @|fg(yellow) " + Constants.PROVISION_OPTION + "=SERVER|@ @|bold option of the|@ @|fg(yellow) " + Constants.SCAN_COMMAND + "|@ @|bold command|@"); + } else { + print("@|bold To provision the WildFly server for your deployment add the|@ @|fg(yellow) " + Constants.PROVISION_OPTION + "=SERVER|@ @|bold option to the|@ @|fg(yellow) " + Constants.SCAN_COMMAND + "|@ @|bold command|@"); + } } } else { print(); - Path target = null; String vers = wildflyServerVersion.orElse(null) == null ? FeaturePacks.getLatestVersion() : wildflyServerVersion.get(); - String doneMessage = null; + Path target = Paths.get("server-" + vers); switch (provision.get()) { case BOOTABLE_JAR: { target = Paths.get(""); - print("@|bold Building WildFly Bootable JAR file|@"); + print("@|bold Building WildFly Bootable JAR file...|@"); break; } case PROVISIONING_XML: { - target = Paths.get("server-" + vers); - print("@|bold Generating provisioning configuration in %s/provisioning.xml file|@", target); - doneMessage = "@|bold Generation DONE. Provisioning configuration is located in " + target + "/provisioning.xml file|@"; + print("@|bold Generating Galleon provisioning configuration file...|@"); break; } case SERVER: { - target = Paths.get("server-" + vers); - print("@|bold Provisioning server in %s directory|@", target); - if (cloud.orElse(false)) { - doneMessage = "@|bold Provisioning DONE. To run the server: 'JBOSS_HOME=" + target + " sh " + target + "/bin/openshift-launch.sh'|@"; - } else { - doneMessage = "@|bold Provisioning DONE. To run the server: 'sh " + target + "/bin/standalone.sh'|@"; - } + print("@|bold Provisioning server...|@", target); break; } case DOCKER_IMAGE: { - target = Paths.get("server-" + vers); - String imageName = dockerImageName.isPresent()? dockerImageName.get() : DockerSupport.getImageName(target.toString()); - print("@|bold Generating docker image '%s'|@", imageName); - doneMessage = "@|bold To run the image call: 'docker run " + imageName + "'|@"; + print("@|bold Generating docker image...|@"); break; } } - Path actualTarget = scanResults.outputConfig(target, dockerImageName.orElse(null)); - if (BOOTABLE_JAR.equals(provision.get())) { - doneMessage = "@|bold Bootable JAR build DONE. To run the jar: 'java -jar " + actualTarget + "'|@"; - } else { - if (DOCKER_IMAGE.equals(provision.get())) { - print("@|bold Image generation DONE. Docker file generated in %s|@.", actualTarget.toAbsolutePath()); + OutputContent content = scanResults.outputConfig(target, dockerImageName.orElse(null)); + Path base = Paths.get("").toAbsolutePath(); + for (OutputContent.OutputFile f : content.getFiles().keySet()) { + Path rel = base.relativize(content.getFiles().get(f)); + switch (f) { + case BOOTABLE_JAR_FILE: { + print("@|bold Bootable JAR build DONE.|@"); + print("@|bold To run the jar call: 'java -jar " + rel + "'|@"); + break; + } + case DOCKER_FILE: { + print("@|bold Image generation DONE.|@."); + print("@|bold Docker file generated in %s|@.", rel); + break; + } + case ENV_FILE: { + print("@|bold The file " + rel + " contains the list of environment variables that you must set prior to start the server.|@"); + switch (provision.get()) { + case SERVER: { + print("@|bold Export the suggested env variables for the server to take them into account.|@"); + break; + } + case BOOTABLE_JAR: { + print("@|bold Export the suggested env variables for the bootable JAR to take them into account.|@"); + break; + } + case DOCKER_IMAGE: { + print("@|bold For each env variable add `-e =` to the `docker run` command.|@"); + break; + } + } + break; + } + case PROVISIONING_XML_FILE: { + print("@|bold Generation DONE.|@"); + print("@|bold Galleon Provisioning configuration is located in " + rel + " file|@"); + break; + } + case SERVER_DIR: { + print("@|bold Provisioning DONE.|@"); + if (cloud.orElse(false)) { + print("@|bold To run the server call: 'JBOSS_HOME=" + rel + " sh " + rel + "/bin/openshift-launch.sh'|@"); + } else { + print("@|bold To run the server call: 'sh " + rel + "/bin/standalone.sh'|@"); + } + break; + } } } - print(doneMessage); + if (content.getDockerImageName() != null) { + print("@|bold To run the image call: 'docker run " + content.getDockerImageName() + "'|@"); + } } return 0; } diff --git a/core/src/main/java/org/wildfly/glow/GlowSession.java b/core/src/main/java/org/wildfly/glow/GlowSession.java index 7b24e9d7..67a1a801 100644 --- a/core/src/main/java/org/wildfly/glow/GlowSession.java +++ b/core/src/main/java/org/wildfly/glow/GlowSession.java @@ -57,6 +57,8 @@ import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import static org.wildfly.glow.OutputFormat.BOOTABLE_JAR; +import static org.wildfly.glow.OutputFormat.DOCKER_IMAGE; import static org.wildfly.glow.error.ErrorLevel.ERROR; @@ -426,7 +428,15 @@ public ScanResults scan() throws Exception { } // END cleanup - errorSession.refreshErrors(allBaseLayers, mapping, allEnabledAddOns); + Map> stronglySuggestConfigFixes = errorSession.refreshErrors(allBaseLayers, mapping, allEnabledAddOns); + for(Layer l : stronglySuggestConfigFixes.keySet()) { + Set envs = stronglySuggestedConfigurations.get(l); + if(envs == null) { + envs = new TreeSet<>(); + stronglySuggestedConfigurations.put(l, envs); + } + envs.addAll(stronglySuggestConfigFixes.get(l)); + } // Identify the active feature-packs. ProvisioningConfig activeConfig = buildProvisioningConfig(pConfig, layout, universeResolver, allBaseLayers, baseLayer, decorators, excludedLayers, fpDependencies, arguments.getConfigName()); @@ -451,39 +461,53 @@ public ScanResults scan() throws Exception { } } - Path outputConfig(ScanResults scanResults, Path target, String dockerImageName) throws Exception { + OutputContent outputConfig(ScanResults scanResults, Path target, String dockerImageName) throws Exception { if (arguments.getOutput() == null) { throw new IllegalStateException("No output format set"); } - Path ret = null; - if (OutputFormat.DOCKER_IMAGE.equals(arguments.getOutput()) || OutputFormat.SERVER.equals(arguments.getOutput()) || OutputFormat.BOOTABLE_JAR.equals(arguments.getOutput())) { + Map files = new HashMap<>(); + if (!OutputFormat.PROVISIONING_XML.equals(arguments.getOutput())) { if (scanResults.getErrorSession().hasErrors()) { writer.warn("You are provisioning a server although some errors still exist. You should first fix them."); } Path generatedArtifact = provisionServer(arguments.getBinaries(), scanResults.getProvisioningConfig(), resolver, arguments.getOutput(), arguments.isCloud(), target); - if (OutputFormat.DOCKER_IMAGE.equals(arguments.getOutput())) { - // generate docker image - String imageName = dockerImageName == null ? DockerSupport.getImageName(generatedArtifact.getFileName().toString()) : dockerImageName; - ret = DockerSupport.buildApplicationImage(imageName, generatedArtifact, arguments, writer); + switch (arguments.getOutput()) { + case DOCKER_IMAGE: { + // generate docker image + dockerImageName = dockerImageName == null ? DockerSupport.getImageName(generatedArtifact.getFileName().toString()) : dockerImageName; + Path origDockerFile = DockerSupport.buildApplicationImage(dockerImageName, generatedArtifact, arguments, writer); + IoUtils.recursiveDelete(generatedArtifact); + Files.createDirectories(target); + Path dockerFile = target.resolve("Dockerfile"); + Files.copy(origDockerFile, dockerFile); + Files.delete(origDockerFile); + files.put(OutputContent.OutputFile.DOCKER_FILE,dockerFile.toAbsolutePath()); + break; + } + case BOOTABLE_JAR: { + files.put(OutputContent.OutputFile.BOOTABLE_JAR_FILE, generatedArtifact.toAbsolutePath()); + break; + } + case SERVER: { + files.put(OutputContent.OutputFile.SERVER_DIR, generatedArtifact.toAbsolutePath()); + break; + } } - IoUtils.recursiveDelete(generatedArtifact); } else { - if (OutputFormat.PROVISIONING_XML.equals(arguments.getOutput())) { - IoUtils.recursiveDelete(target); - Files.createDirectories(target); - ret = target.resolve("provisioning.xml"); - try (FileWriter fileWriter = new FileWriter(ret.toFile())) { - ProvisioningXmlWriter.getInstance().write(scanResults.getProvisioningConfig(), fileWriter); - } + IoUtils.recursiveDelete(target); + Files.createDirectories(target); + Path prov = target.resolve("provisioning.xml"); + try (FileWriter fileWriter = new FileWriter(prov.toFile())) { + ProvisioningXmlWriter.getInstance().write(scanResults.getProvisioningConfig(), fileWriter); } + files.put(OutputContent.OutputFile.PROVISIONING_XML_FILE, prov.toAbsolutePath()); } StringBuilder envFileContent = new StringBuilder(); if (!scanResults.getSuggestions().getStronglySuggestedConfigurations().isEmpty() || (arguments.isSuggest() && !scanResults.getSuggestions().getSuggestedConfigurations().isEmpty())) { - envFileContent.append("Environment variables for "). - append(arguments.getExecutionContext()).append(" execution context.").append(System.lineSeparator()); + envFileContent.append("Environment variables to set. ").append(System.lineSeparator()); } if (!scanResults.getSuggestions().getStronglySuggestedConfigurations().isEmpty()) { envFileContent.append(buildEnvs(scanResults.getSuggestions(). @@ -494,11 +518,14 @@ Path outputConfig(ScanResults scanResults, Path target, String dockerImageName) getSuggestedConfigurations(), false)).append(System.lineSeparator()); } if (envFileContent.length() != 0) { + if (!Files.exists(target)) { + Files.createDirectories(target); + } Path p = target.resolve("configuration.env"); - writer.info("The file " + p + " contains the environment variables to set in a " + arguments.getExecutionContext() + " context"); Files.write(p, envFileContent.toString().getBytes()); + files.put(OutputContent.OutputFile.ENV_FILE, p.toAbsolutePath()); } - return ret; + return new OutputContent(files, dockerImageName); } private String buildEnvs(Map> map, boolean isRequired) { diff --git a/core/src/main/java/org/wildfly/glow/OutputContent.java b/core/src/main/java/org/wildfly/glow/OutputContent.java new file mode 100644 index 00000000..9a3e6f84 --- /dev/null +++ b/core/src/main/java/org/wildfly/glow/OutputContent.java @@ -0,0 +1,43 @@ +/* + * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license + * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template + */ +package org.wildfly.glow; + +import java.nio.file.Path; +import java.util.Map; + +/** + * + * @author jdenise + */ +public class OutputContent { + public enum OutputFile { + BOOTABLE_JAR_FILE, + DOCKER_FILE, + SERVER_DIR, + PROVISIONING_XML_FILE, + ENV_FILE + } + + private final Map files; + private final String dockerImageName; + OutputContent(Map files, String dockerImageName) { + this.files = files; + this.dockerImageName = dockerImageName; + } + + /** + * @return the files + */ + public Map getFiles() { + return files; + } + + /** + * @return the dockerImageName + */ + public String getDockerImageName() { + return dockerImageName; + } +} diff --git a/core/src/main/java/org/wildfly/glow/ScanResults.java b/core/src/main/java/org/wildfly/glow/ScanResults.java index 276a326f..d76004d4 100644 --- a/core/src/main/java/org/wildfly/glow/ScanResults.java +++ b/core/src/main/java/org/wildfly/glow/ScanResults.java @@ -94,7 +94,7 @@ public Set getEnabledAddOns() { return enabledAddOns; } - public Path outputConfig(Path target, String dockerImageName) throws Exception { + public OutputContent outputConfig(Path target, String dockerImageName) throws Exception { return glowSession.outputConfig(this, target, dockerImageName); } diff --git a/core/src/main/java/org/wildfly/glow/ScanResultsPrinter.java b/core/src/main/java/org/wildfly/glow/ScanResultsPrinter.java index 30d527a1..f8874bb0 100644 --- a/core/src/main/java/org/wildfly/glow/ScanResultsPrinter.java +++ b/core/src/main/java/org/wildfly/glow/ScanResultsPrinter.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -166,12 +167,13 @@ private void detailed(ScanArguments arguments, ScanResults scanResults) throws E for(Map.Entry> entry : scanResults.getSuggestions().getStronglySuggestedConfigurations().entrySet()) { writer.warn(buildSuggestions(entry.getKey(), entry.getValue())); } + writer.warn(""); } - writer.info("suggestions"); String suggestedConfigs = buildSuggestions(scanResults.getSuggestions().getSuggestedConfigurations()); if (arguments.isSuggest()) { + writer.info("suggestions"); if (scanResults.getSuggestions().getPossibleAddOns().isEmpty() && scanResults.getSuggestions().getPossibleProfiles().isEmpty() && suggestedConfigs.isEmpty()) { writer.info("none"); } else { @@ -211,9 +213,7 @@ private void detailed(ScanArguments arguments, ScanResults scanResults) throws E } } else { if (!scanResults.getSuggestions().getPossibleAddOns().isEmpty() || !scanResults.getSuggestions().getPossibleAddOns().isEmpty() || !suggestedConfigs.isEmpty()) { - writer.info("some suggestions have been found. You could enable suggestions with --suggest option."); - } else { - writer.info("none"); + writer.info("Some suggestions have been found. You could enable suggestions with --suggest option."); } } } @@ -229,8 +229,13 @@ private static String buildSuggestions(Map> map) throws URISynta private static String buildSuggestions(Layer layer, Set envs) throws URISyntaxException, IOException { StringBuilder suggestedConfigsBuilder = new StringBuilder(); suggestedConfigsBuilder.append("\n").append(layer.getName()).append(":\n"); - for (Env e : envs) { - suggestedConfigsBuilder.append(" - ").append(e.getName()).append("=").append(e.getDescription()).append("\n"); + Iterator it = envs.iterator(); + while (it.hasNext()) { + Env e = it.next(); + suggestedConfigsBuilder.append(" - ").append(e.getName()).append("=").append(e.getDescription()); + if (it.hasNext()) { + suggestedConfigsBuilder.append("\n"); + } } return suggestedConfigsBuilder.toString(); }} diff --git a/core/src/main/java/org/wildfly/glow/error/DatasourceErrorIdentification.java b/core/src/main/java/org/wildfly/glow/error/DatasourceErrorIdentification.java index f1a0789d..471d4870 100644 --- a/core/src/main/java/org/wildfly/glow/error/DatasourceErrorIdentification.java +++ b/core/src/main/java/org/wildfly/glow/error/DatasourceErrorIdentification.java @@ -37,6 +37,7 @@ import java.util.Set; import java.util.TreeSet; import java.util.regex.Pattern; +import org.wildfly.glow.Env; import static org.wildfly.glow.Utils.getAddOnFix; @@ -122,9 +123,10 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) } @Override - public void refreshErrors(Set allBaseLayers) throws Exception { + public Map> refreshErrors(Set allBaseLayers) throws Exception { Set unboundDatasourcesErrors = errors.get(UNBOUND_DATASOURCES_ERROR); Set toRemove = new HashSet<>(); + Map> ret = new HashMap<>(); if (unboundDatasourcesErrors != null) { for (IdentifiedError error : unboundDatasourcesErrors) { UnboundDatasourceError uds = (UnboundDatasourceError) error; @@ -143,6 +145,14 @@ public void refreshErrors(Set allBaseLayers) throws Exception { if (content != null) { content = content.replaceAll("##ITEM##", uds.unboundDatasource); } + if(fix.isEnv()) { + Set envs = ret.get(l); + if(envs == null) { + envs = new HashSet<>(); + ret.put(l, envs); + } + envs.add(new Env(fix.getEnvName(), Fix.getEnvValue(content), false, true)); + } } String errorMessage = getAddOnFix(l.getAddOn(), content); error.setFixed(errorMessage); @@ -167,6 +177,14 @@ public void refreshErrors(Set allBaseLayers) throws Exception { Fix fix = l.getAddOn().getFixes().get(error.getId()); if (fix != null) { String content = fix.getContent(); + if (fix.isEnv()) { + Set envs = ret.get(l); + if (envs == null) { + envs = new HashSet<>(); + ret.put(l, envs); + } + envs.add(new Env(fix.getEnvName(), Fix.getEnvValue(content), false, true)); + } String errorMessage = getAddOnFix(l.getAddOn(), content); error.setFixed(errorMessage); } @@ -175,6 +193,7 @@ public void refreshErrors(Set allBaseLayers) throws Exception { } } } + return ret; } @Override diff --git a/core/src/main/java/org/wildfly/glow/error/ErrorIdentification.java b/core/src/main/java/org/wildfly/glow/error/ErrorIdentification.java index fd21267f..7e0abcb5 100644 --- a/core/src/main/java/org/wildfly/glow/error/ErrorIdentification.java +++ b/core/src/main/java/org/wildfly/glow/error/ErrorIdentification.java @@ -21,7 +21,9 @@ import java.nio.file.Path; import java.util.List; +import java.util.Map; import java.util.Set; +import org.wildfly.glow.Env; /** * @@ -29,6 +31,6 @@ */ public interface ErrorIdentification { void collectErrors(Path rootPath) throws Exception; - void refreshErrors(Set allBaseLayers) throws Exception; + Map> refreshErrors(Set allBaseLayers) throws Exception; List getErrors(); } diff --git a/core/src/main/java/org/wildfly/glow/error/ErrorIdentificationSession.java b/core/src/main/java/org/wildfly/glow/error/ErrorIdentificationSession.java index 9b3d2eb9..62d1e1f5 100644 --- a/core/src/main/java/org/wildfly/glow/error/ErrorIdentificationSession.java +++ b/core/src/main/java/org/wildfly/glow/error/ErrorIdentificationSession.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.wildfly.glow.Env; /** * @@ -55,8 +56,8 @@ public void collectEndOfScanErrors( jndiErrorIdentification.collectErrors(verbose, resourceInjectionInfos, initialContextLookupInfos, allClasses); } - public void refreshErrors(Set allBaseLayers, LayerMapping mapping, Set enabledAddOns) throws Exception { - ds.refreshErrors(allBaseLayers); + public Map> refreshErrors(Set allBaseLayers, LayerMapping mapping, Set enabledAddOns) throws Exception { + Map> stronglySuggested = ds.refreshErrors(allBaseLayers); // We could have an Enabbled addOn for (IdentifiedError error : getErrors()) { if (!error.isFixed()) { @@ -66,6 +67,7 @@ public void refreshErrors(Set allBaseLayers, LayerMapping mapping, Set getErrors() { diff --git a/core/src/main/java/org/wildfly/glow/error/Fix.java b/core/src/main/java/org/wildfly/glow/error/Fix.java index 17003c0f..a3f21e3a 100644 --- a/core/src/main/java/org/wildfly/glow/error/Fix.java +++ b/core/src/main/java/org/wildfly/glow/error/Fix.java @@ -72,6 +72,15 @@ public String getDescription() { return description; } + public boolean isEnv() { + return description.endsWith(" env"); + } + + public String getEnvName() { + String[] split = content.split("="); + return split[0]; + } + /** * @return the content */ @@ -85,4 +94,9 @@ public String getContent() { public String getForId() { return forId; } + + public static String getEnvValue(String value) { + String[] split = value.split("="); + return split[1]; + } }