org.wildfly.glow.GlowSession Maven / Gradle / Ivy
The newest version!
/*
* JBoss, Home of Professional Open Source.
* Copyright 2023 Red Hat, Inc., and individual 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.wildfly.glow;
import org.jboss.galleon.Constants;
import org.jboss.galleon.ProvisioningException;
import org.jboss.galleon.universe.FeaturePackLocation;
import org.jboss.galleon.universe.FeaturePackLocation.FPID;
import org.jboss.galleon.universe.maven.repo.MavenRepoManager;
import org.jboss.galleon.util.IoUtils;
import org.jboss.galleon.util.ZipUtils;
import org.wildfly.glow.error.ErrorIdentificationSession;
import org.wildfly.glow.error.IdentifiedError;
import org.wildfly.glow.windup.WindupSupport;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.jboss.galleon.MessageWriter;
import org.jboss.galleon.universe.FeaturePackLocation.ProducerSpec;
import static org.wildfly.glow.OutputFormat.BOOTABLE_JAR;
import static org.wildfly.glow.OutputFormat.DOCKER_IMAGE;
import org.jboss.galleon.api.GalleonBuilder;
import org.jboss.galleon.api.GalleonFeaturePackRuntime;
import org.jboss.galleon.api.GalleonFeatureParamSpec;
import org.jboss.galleon.api.GalleonFeatureSpec;
import org.jboss.galleon.api.GalleonPackageRuntime;
import org.jboss.galleon.api.GalleonProvisioningRuntime;
import org.jboss.galleon.api.Provisioning;
import org.jboss.galleon.api.config.GalleonConfigurationWithLayers;
import org.jboss.galleon.api.config.GalleonConfigurationWithLayersBuilder;
import org.jboss.galleon.api.config.GalleonFeaturePackConfig;
import org.jboss.galleon.api.config.GalleonProvisioningConfig;
import org.jboss.galleon.universe.UniverseResolver;
import org.jboss.galleon.universe.maven.MavenArtifact;
import org.wildfly.channel.Channel;
import org.wildfly.channel.ChannelMapper;
import static org.wildfly.glow.error.ErrorLevel.ERROR;
import org.wildfly.plugin.tools.bootablejar.BootableJarSupport;
/**
*
* @author jdenise
*/
public class GlowSession {
public static final Path OFFLINE_ZIP = Paths.get("glow-offline.zip");
public static final Path OFFLINE_CONTENT = Paths.get("glow-offline-content");
public static final Path OFFLINE_DOCS_DIR = OFFLINE_CONTENT.resolve("docs");
public static final Path OFFLINE_FEATURE_PACKS_DIR = OFFLINE_CONTENT.resolve("feature-packs");
public static final Path OFFLINE_FEATURE_PACK_DEPENDENCIES_DIR = OFFLINE_CONTENT.resolve("feature-pack-dependencies");
public static final String STANDALONE_PROFILE = "standalone";
private final MavenRepoManager resolver;
private final Arguments arguments;
private final GlowMessageWriter writer;
private final List channels = new ArrayList<>();
private GlowSession(MavenRepoManager resolver, Arguments arguments, GlowMessageWriter writer) throws Exception {
this.arguments = arguments;
this.writer = writer;
MavenRepoManager repoManager = resolver;
if (!Files.exists(OFFLINE_ZIP)) {
if (arguments.getChannels() != null) {
channels.addAll(arguments.getChannels());
}
}
this.resolver = repoManager;
}
public static void goOffline(MavenRepoManager resolver, GoOfflineArguments arguments, GlowMessageWriter writer) throws Exception {
if (!(arguments instanceof Arguments)) {
throw new IllegalArgumentException("Please use the API to create the GoOfflineArguments instance");
}
GlowSession session = new GlowSession(resolver, (Arguments) arguments, writer);
session.goOffline();
}
private void goOffline() throws Exception {
if (Files.exists(OFFLINE_CONTENT)) {
IoUtils.recursiveDelete(OFFLINE_CONTENT);
}
UniverseResolver universeResolver = UniverseResolver.builder().addArtifactResolver(resolver).build();
GalleonBuilder provider = new GalleonBuilder();
provider.addArtifactResolver(resolver);
Provisioning provisioning = null;
try {
GalleonProvisioningConfig config = Utils.buildOfflineProvisioningConfig(provider, writer);
if (config == null) {
Path provisioningXML = arguments.getProvisioningXML();
if (provisioningXML == null) {
provisioningXML = FeaturePacks.getFeaturePacks(arguments.getVersion(), arguments.getExecutionContext(), arguments.isTechPreview());
}
provisioning = provider.newProvisioningBuilder(provisioningXML).build();
config = provisioning.loadProvisioningConfig(provisioningXML);
} else {
provisioning = provider.newProvisioningBuilder(config).build();
}
Utils.exportOffline(provisioning, config, universeResolver);
} finally {
if (provisioning != null) {
provisioning.close();
}
}
Files.deleteIfExists(OFFLINE_ZIP);
ZipUtils.zip(OFFLINE_CONTENT, OFFLINE_ZIP);
IoUtils.recursiveDelete(OFFLINE_CONTENT);
}
public static ScanResults scan(MavenRepoManager resolver, ScanArguments arguments, GlowMessageWriter writer) throws Exception {
if (!(arguments instanceof Arguments)) {
throw new IllegalArgumentException("Please use the API to create the ScanArguments instance");
}
GlowSession session = new GlowSession(resolver, (Arguments) arguments, writer);
return session.scan();
}
public ScanResults scan() throws Exception {
Set layers = new LinkedHashSet<>();
Set possibleAddOns = new TreeSet<>();
ErrorIdentificationSession errorSession = new ErrorIdentificationSession();
Set excludedPackages = new TreeSet<>();
Map> excludedFeatures = new TreeMap<>();
UniverseResolver universeResolver = UniverseResolver.builder().addArtifactResolver(resolver).build();
if (Files.exists(OFFLINE_ZIP)) {
Files.createDirectories(OFFLINE_CONTENT);
ZipUtils.unzip(OFFLINE_ZIP, OFFLINE_CONTENT);
}
GalleonBuilder provider = new GalleonBuilder();
provider.addArtifactResolver(resolver);
Provisioning provisioning = null;
GalleonProvisioningConfig config = Utils.buildOfflineProvisioningConfig(provider, writer);
Path fakeHome = Files.createTempDirectory("wildfly-glow");
try {
if (config == null) {
Path provisioningXML = arguments.getProvisioningXML();
if (provisioningXML == null) {
provisioningXML = FeaturePacks.getFeaturePacks(arguments.getVersion(), arguments.getExecutionContext(), arguments.isTechPreview());
}
provisioning = provider.newProvisioningBuilder(provisioningXML).setInstallationHome(fakeHome).build();
config = provisioning.loadProvisioningConfig(provisioningXML);
} else {
provisioning = provider.newProvisioningBuilder(config).setInstallationHome(fakeHome).build();
}
// Handle cases were no version is provided
Map fpVersions = new HashMap<>();
Map originalVersions = new HashMap<>();
// Resolve feature-packs
GalleonProvisioningConfig.Builder outputConfigBuilder = GalleonProvisioningConfig.builder();
for (GalleonFeaturePackConfig dep : config.getFeaturePackDeps()) {
FeaturePackLocation.FPID fpid = Utils.toMavenCoordinates(dep.getLocation().getFPID(), universeResolver);
String[] coordinates = fpid.toString().split(":");
String groupId = coordinates[0];
String artifactId = coordinates[1];
String version = null;
MavenArtifact artifact = new MavenArtifact();
artifact.setArtifactId(artifactId);
artifact.setGroupId(groupId);
if(coordinates.length >= 3) {
version = coordinates[2];
}
artifact.setVersion(version);
artifact.setExtension("zip");
resolver.resolve(artifact);
FeaturePackLocation loc = dep.getLocation().replaceBuild(artifact.getVersion());
outputConfigBuilder.addFeaturePackDep(loc);
fpVersions.put(fpid.getProducer(), loc.getFPID());
originalVersions.put(fpid.getProducer(), fpid);
}
config = outputConfigBuilder.build();
// BUILD MODEL
Map> fpDependencies = new HashMap<>();
Map all
= Utils.getAllLayers(config, universeResolver, provisioning, fpDependencies);
LayerMapping mapping = Utils.buildMapping(all, arguments.getExecutionProfiles());
if (mapping.getDefaultBaseLayer() == null) {
throw new IllegalArgumentException("No base layer found, server version is not supported. "
+ "You must upgrade to a more recent server version.");
}
// END BUILD MODEL
// VALIDATE USER INPUTS
for (String s : arguments.getUserEnabledAddOns()) {
if (!mapping.getAddOns().containsKey(s)) {
throw new Exception("Unknown add-on " + s);
}
}
Set allProfiles = Utils.getAllProfiles(all);
for (String p : arguments.getExecutionProfiles()) {
if (!allProfiles.contains(p)) {
throw new Exception("Unknown profile " + p);
}
}
if (arguments.isCloud() && OutputFormat.BOOTABLE_JAR.equals(arguments.getOutput())) {
writer.info("NOTE. In a cloud context, Bootable JAR "
+ "packaging is not taken into account, a server is installed in the image.");
}
// END VALIDATE USER INPUTS
// DISCOVERY
if (arguments.getBinaries() != null && !arguments.getBinaries().isEmpty()) {
Path windup = WindupSupport.getWindupMapping();
if (windup == null) {
for (Path d : arguments.getBinaries()) {
//System.out.println("SCAN " + d);
try (DeploymentScanner deploymentScanner = new DeploymentScanner(d, arguments.isVerbose(), arguments.getExcludeArchivesFromScan())) {
deploymentScanner.scan(mapping, layers, all, errorSession);
}
}
} else {
for (Path d : arguments.getBinaries()) {
layers.addAll(WindupSupport.getLayers(all, windup, d));
}
}
}
if (!arguments.getManualLayers().isEmpty()) {
for (String manualLayer : arguments.getManualLayers()) {
Layer foundLayer = all.get(manualLayer);
if (foundLayer == null) {
throw new IllegalArgumentException("Manual layer '" + manualLayer + "' does not exist in the set of layers");
}
if (layers.contains(foundLayer)) {
throw new IllegalArgumentException("Layer '" + foundLayer + "' manually added has already been discovered in the deployment. It must be removed.");
}
layers.add(foundLayer);
LayerMapping.addRule(LayerMapping.RULE.EXPLICIT, foundLayer, null);
}
}
if (!arguments.getLayersForJndi().isEmpty()) {
for (String layer : arguments.getLayersForJndi()) {
Layer foundLayer = all.get(layer);
if (foundLayer == null) {
throw new IllegalArgumentException("Layer '" + layer + "' added due to JNDI lookup does not exist in the set of layers");
}
if (layers.contains(foundLayer)) {
throw new IllegalArgumentException("Layer '" + layer + "' added due to JNDI lookup has already been discovered in the deployment. It must be removed.");
}
layers.add(foundLayer);
LayerMapping.addRule(LayerMapping.RULE.EXPLICIT, foundLayer, null);
}
}
Map> ret = findBaseLayer(mapping, all);
Layer baseLayer = ret.keySet().iterator().next();
LayerMapping.addRule(LayerMapping.RULE.BASE_LAYER, baseLayer, null);
// We create a set of all fine grain layers from the basic layers
// Needed to identify layers that could be required to be excluded due to profile.
Set allBaseLayers = new TreeSet<>();
allBaseLayers.addAll(layers);
allBaseLayers.addAll(Utils.getTransitiveDependencies(all, baseLayer, new HashSet<>()));
for (Layer s : layers) {
allBaseLayers.addAll(Utils.getTransitiveDependencies(all, s, new HashSet<>()));
}
// Force layers inclusion when add-on explicitly enabled.
for (AddOn addOn : mapping.getAddOns().values()) {
boolean enabled = arguments.getUserEnabledAddOns().contains(addOn.getName());
if (enabled) {
for (Layer l : addOn.getLayers()) {
if (!l.isBanned()) {
layers.add(l);
LayerMapping.addRule(LayerMapping.RULE.ADD_ON, l, null);
allBaseLayers.add(l);
Set dependencies = all.get(l.getName()).getDependencies();
layers.addAll(dependencies);
allBaseLayers.addAll(dependencies);
}
}
}
}
// Handle Layers bound to addOn inclusion or suggest addOns
for (String addOnName : mapping.getAddOns().keySet()) {
AddOn addOn = mapping.getAddOns().get(addOnName);
boolean enabled = arguments.getUserEnabledAddOns().contains(addOn.getName());
for (Layer layer : addOn.getLayersThatExpectAllDependencies()) {
if (!allBaseLayers.contains(layer) && !layer.isBanned()) {
if (allBaseLayers.containsAll(layer.getDependencies())) {
if (enabled) {
layers.add(layer);
allBaseLayers.add(layer);
LayerMapping.addRule(LayerMapping.RULE.ADD_ON_REQUIRED_DEPENDENCIES_FOUND, layer, null);
} else {
possibleAddOns.add(addOn);
}
}
}
}
for (Layer layer : addOn.getLayersThatExpectSomeDependencies().keySet()) {
if (!allBaseLayers.contains(layer) && !layer.isBanned()) {
Set expectDeps = addOn.getLayersThatExpectSomeDependencies().get(layer);
if (expectDeps != null && allBaseLayers.containsAll(expectDeps)) {
if (enabled) {
layers.add(layer);
allBaseLayers.add(layer);
LayerMapping.addRule(LayerMapping.RULE.ADD_ON_REQUIRED_DEPENDENCIES_FOUND, layer, null);
} else {
possibleAddOns.add(addOn);
}
}
}
}
for (Layer layer : addOn.getLayersAlwaysIncluded()) {
if (enabled && !layer.isBanned()) {
layers.add(layer);
allBaseLayers.add(layer);
LayerMapping.addRule(LayerMapping.RULE.ADD_ON_ALWAYS_INCLUDED, layer, null);
} else {
possibleAddOns.add(addOn);
}
}
}
// Add Layers that are included if all there dependencies have been included
for (Layer layer : mapping.getLayersIncludedIfAllDeps()) {
if (!allBaseLayers.contains(layer) && !layer.isBanned()) {
if (allBaseLayers.containsAll(layer.getDependencies())) {
layers.add(layer);
allBaseLayers.add(layer);
LayerMapping.addRule(LayerMapping.RULE.ADD_ON_REQUIRED_DEPENDENCIES_FOUND, layer, null);
}
}
}
// Add Layers that are included if some of there dependencies have been included
for (Layer layer : mapping.getLayersIncludedIfSomeDeps().keySet()) {
if (!allBaseLayers.contains(layer) && !layer.isBanned()) {
if (allBaseLayers.containsAll(mapping.getLayersIncludedIfSomeDeps().get(layer))) {
layers.add(layer);
allBaseLayers.add(layer);
LayerMapping.addRule(LayerMapping.RULE.ADD_ON_REQUIRED_DEPENDENCIES_FOUND, layer, null);
}
}
}
// Add layers that are included at the FP level (model.xml).
for (Layer layer : all.values()) {
if (layer.isIsAutomaticInjection() && !layer.isBanned()) {
allBaseLayers.add(layer);
LayerMapping.addRule(LayerMapping.RULE.ALWAYS_INCLUDED, layer, null);
}
}
// END DISCOVERY
// PROFILES
// If no profile enabled, suggest profiles for layer that could be profiled
Set possibleProfiles = new TreeSet<>();
if (arguments.getExecutionProfiles().isEmpty()) {
for (Layer l : allBaseLayers) {
for (String p : mapping.getAllProfilesLayers().keySet()) {
Set layersInProfile = mapping.getAllProfilesLayers().get(p);
if (layersInProfile.contains(l)) {
possibleProfiles.add(p);
break;
}
}
}
}
Set profileLayers = new TreeSet<>();
Set excludedLayers = new TreeSet<>();
for (Layer l : allBaseLayers) {
Layer toInclude = mapping.getActiveProfilesLayers().get(l.getName());
if (toInclude != null) {
profileLayers.add(toInclude);
LayerMapping.addRule(LayerMapping.RULE.PROFILE_INCLUDED, toInclude, null);
excludedLayers.add(l);
LayerMapping.addRule(LayerMapping.RULE.PROFILE_EXCLUDED, l, null);
}
}
// Order is important. Decorators must be added first.
// Then layers bound to a profile
Set decorators = new LinkedHashSet<>();
for (Layer s : layers) {
if (!ret.get(baseLayer).contains(s)) {
decorators.add(s);
}
}
// Add profile based layer to the decorators
// That is the way wildfly design its layers today
decorators.addAll(profileLayers);
// If we have layers as decorator that we also need to exclude, just remove them from decorators and excluded set
Iterator decoratorsIt = decorators.iterator();
while (decoratorsIt.hasNext()) {
Layer l = decoratorsIt.next();
if (excludedLayers.contains(l)) {
//System.out.println("This decorator is also excluded, just not add it. " + l);
excludedLayers.remove(l);
decoratorsIt.remove();
}
}
// END PROFILES
// In case some layers fixed the found initial errors
//errorSession.refreshErrors(allBaseLayers);
// ADD-ON
// Findout the set of enabled add-ons
Set allEnabledAddOns = new TreeSet<>();
for (Layer layer : allBaseLayers) {
if (layer.getAddOn() != null) {
allEnabledAddOns.add(layer.getAddOn());
}
}
// Fix addOns
Map disabledAddOns = new TreeMap<>();
fixAddOns(errorSession, layers, mapping, allEnabledAddOns, possibleAddOns, disabledAddOns, arguments);
// END ADD-ON
// DECORATORS CLEANUP
// Filter out decorator layers that are dependencies inside found layers.
Set filteredLayers = new LinkedHashSet<>();
filteredLayers.addAll(decorators);
Iterator it = filteredLayers.iterator();
while (it.hasNext()) {
Layer l = it.next();
for (Layer s : decorators) {
if (l.equals(s)) {
continue;
}
Set deps = Utils.getTransitiveDependencies(all, s, new HashSet<>());
if (deps.contains(l)) {
it.remove();
break;
}
}
}
decorators = filteredLayers;
// END DECORATORS CLEANUP
// Compute the possible configurations
// strongly suggested means that the layer has been discovered directly in the deployment
// and that its required configuration should be applied.
Map> suggestedConfigurations = new TreeMap<>();
Map> stronglySuggestedConfigurations = new TreeMap<>();
Map> buildTimeConfigurations = new TreeMap<>();
Map> buildTimeRequiredConfigurations = new TreeMap<>();
for (Layer l : allBaseLayers) {
if (!excludedLayers.contains(l)) {
if (!l.getConfiguration().isEmpty()) {
if (layers.contains(l)) {
Set requiredSet = new TreeSet<>();
Set notRequiredSet = new TreeSet<>();
Set buildTimeSet = new TreeSet<>();
Set buildTimeRequiredSet = new TreeSet<>();
for (String c : l.getConfiguration()) {
URI uri = new URI(c);
Set envs = EnvHandler.retrieveEnv(uri);
for (Env e : envs) {
if (e.isRequired()) {
if (e.isRuntime()) {
requiredSet.add(e);
} else {
buildTimeRequiredSet.add(e);
}
} else {
if (e.isRuntime()) {
notRequiredSet.add(e);
} else {
buildTimeSet.add(e);
}
}
}
}
if (!requiredSet.isEmpty()) {
stronglySuggestedConfigurations.put(l, requiredSet);
}
if (!notRequiredSet.isEmpty()) {
suggestedConfigurations.put(l, notRequiredSet);
}
if (!buildTimeSet.isEmpty()) {
buildTimeConfigurations.put(l, buildTimeSet);
}
if (!buildTimeRequiredSet.isEmpty()) {
buildTimeRequiredConfigurations.put(l, buildTimeRequiredSet);
}
} else {
Set envs = new TreeSet<>();
for (String c : l.getConfiguration()) {
envs.addAll(EnvHandler.retrieveEnv(new URI(c)));
}
suggestedConfigurations.put(l, envs);
}
}
}
}
Set metadataOnlyLayers = new LinkedHashSet<>();
// Remove layers from output that are metadata-only
for (Layer metadataOnly : mapping.getMetadataOnly()) {
if (decorators.contains(metadataOnly)) {
decorators.remove(metadataOnly);
metadataOnlyLayers.add(metadataOnly);
}
if (layers.contains(metadataOnly)) {
layers.remove(metadataOnly);
metadataOnlyLayers.add(metadataOnly);
}
}
// END cleanup
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.
GalleonProvisioningConfig activeConfig = buildProvisioningConfig(config,
universeResolver, allBaseLayers, baseLayer, decorators, excludedLayers, fpDependencies, arguments.getConfigName(), arguments.getConfigStability(), arguments.getPackageStability(), originalVersions);
// Handle stability
String configStability = arguments.getConfigStability() == null ? arguments.getDefaultConfigStability() : arguments.getConfigStability();
if (configStability != null) {
List checkLayers = new ArrayList<>();
checkLayers.add(baseLayer);
checkLayers.addAll(decorators);
// Retrieve the features of each layer
for (Layer layer : checkLayers) {
try {
GalleonConfigurationWithLayers configLayers = GalleonConfigurationWithLayersBuilder.builder("standalone", "standalone.xml").includeLayer(layer.getName()).build();
GalleonProvisioningConfig.Builder config2Builder = GalleonProvisioningConfig.builder().addConfig(configLayers).addOption(Constants.CONFIG_STABILITY_LEVEL, Constants.STABILITY_EXPERIMENTAL);
for (GalleonFeaturePackConfig fp : activeConfig.getFeaturePackDeps()) {
config2Builder.addFeaturePackDep(GalleonFeaturePackConfig.
builder(fp.getLocation(), false).setInheritConfigs(false).build());
}
GalleonProvisioningConfig config2 = config2Builder.build();
try (GalleonProvisioningRuntime rt = provisioning.getProvisioningRuntime(config2)) {
List lst = rt.getAllFeatures();
for (GalleonFeatureSpec spec : lst) {
String stab = spec.getStability();
if (stab != null && !StabilitySupport.enables(configStability, stab)) {
Set set = excludedFeatures.get(layer);
if (set == null) {
set = new HashSet<>();
excludedFeatures.put(layer, set);
}
set.add(spec.getName() + "[stability=" + spec.getStability() + "]");
}
for (GalleonFeatureParamSpec pspec : spec.getParams()) {
String pstab = pspec.getStability();
if (pstab != null && !StabilitySupport.enables(configStability, pstab)) {
Set set = excludedFeatures.get(layer);
if (set == null) {
set = new HashSet<>();
excludedFeatures.put(layer, set);
}
set.add(spec.getName() + "." + pspec.getName() + "[stability=" + pspec.getStability() + "]");
}
}
}
}
} catch (Exception ex) {
writer.warn("Got unexpected exception dealing with " + layer + " features. Exception" + ex +". Please report the issue.");
}
}
}
if(arguments.getPackageStability() != null) {
// We must disable the stability to see all packages in the runtime
GalleonProvisioningConfig config2 = GalleonProvisioningConfig.builder(activeConfig).
removeOption(Constants.STABILITY_LEVEL).
removeOption(Constants.CONFIG_STABILITY_LEVEL).
removeOption(Constants.PACKAGE_STABILITY_LEVEL).
addOption(Constants.STABILITY_LEVEL, arguments.getConfigStability() == null ? Constants.STABILITY_EXPERIMENTAL : arguments.getConfigStability()).build();
try (GalleonProvisioningRuntime rt = provisioning.getProvisioningRuntime(config2)) {
for (GalleonFeaturePackRuntime fpr : rt.getGalleonFeaturePacks()) {
for (GalleonPackageRuntime prt : fpr.getGalleonPackages()) {
String packageStability = prt.getStability();
if (packageStability != null && !StabilitySupport.enables(arguments.getPackageStability(), packageStability)) {
excludedPackages.add(prt.getName() + "[stability="+packageStability+"]");
}
}
}
}
}
Suggestions suggestions = new Suggestions(suggestedConfigurations,
stronglySuggestedConfigurations, buildTimeConfigurations, buildTimeRequiredConfigurations, possibleAddOns, possibleProfiles);
ScanResults scanResults = new ScanResults(
this,
layers,
excludedLayers,
baseLayer,
decorators,
metadataOnlyLayers,
provisioning,
activeConfig,
allEnabledAddOns,
disabledAddOns,
suggestions,
errorSession,
excludedPackages,
excludedFeatures,
fpVersions,
channels
);
return scanResults;
} finally {
IoUtils.recursiveDelete(OFFLINE_CONTENT);
IoUtils.recursiveDelete(fakeHome);
}
}
OutputContent outputConfig(ScanResults scanResults, Path target, String dockerImageName) throws Exception {
Provisioning provisioning = scanResults.getProvisioning();
if (arguments.getOutput() == null) {
throw new IllegalStateException("No output format set");
}
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.");
}
}
if (!OutputFormat.PROVISIONING_XML.equals(arguments.getOutput()) &&
!OutputFormat.OPENSHIFT.equals(arguments.getOutput())) {
Path generatedArtifact = provisionServer(arguments.getBinaries(),
scanResults.getProvisioningConfig(), resolver, arguments.getOutput(),
arguments.isCloud(), target);
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;
}
}
} else {
if (OutputFormat.OPENSHIFT.equals(arguments.getOutput())) {
target = target.resolve("galleon");
}
Files.createDirectories(target);
Path prov = target.resolve("provisioning.xml");
provisioning.storeProvisioningConfig(scanResults.getProvisioningConfig(),prov);
files.put(OutputContent.OutputFile.PROVISIONING_XML_FILE, prov.toAbsolutePath());
if(!channels.isEmpty()) {
String channelsContent = ChannelMapper.toYaml(channels);
Path channelsFile = target.resolve("channel.yaml");
Files.write(channelsFile, channelsContent.getBytes());
files.put(OutputContent.OutputFile.CHANNEL_FILE, channelsFile.toAbsolutePath());
}
}
StringBuilder envFileContent = new StringBuilder();
if (!scanResults.getSuggestions().getStronglySuggestedConfigurations().isEmpty() ||
(arguments.isSuggest() && !scanResults.getSuggestions().getSuggestedConfigurations().isEmpty())) {
envFileContent.append("Environment variables to set. ").append(System.lineSeparator());
}
if (!scanResults.getSuggestions().getStronglySuggestedConfigurations().isEmpty()) {
envFileContent.append(buildEnvs(scanResults.getSuggestions().
getStronglySuggestedConfigurations(), true)).append(System.lineSeparator());
}
if (arguments.isSuggest() && !scanResults.getSuggestions().getSuggestedConfigurations().isEmpty()) {
envFileContent.append(buildEnvs(scanResults.getSuggestions().
getSuggestedConfigurations(), false)).append(System.lineSeparator());
}
if (envFileContent.length() != 0) {
if (!Files.exists(target)) {
Files.createDirectories(target);
}
Path p = target.resolve("configuration.env");
Files.write(p, envFileContent.toString().getBytes());
files.put(OutputContent.OutputFile.ENV_FILE, p.toAbsolutePath());
}
return new OutputContent(files, dockerImageName);
}
private String buildEnvs(Map> map, boolean isRequired) {
StringBuilder envFileContent = new StringBuilder();
for (Entry> entry : map.entrySet()) {
envFileContent.append(System.lineSeparator()).append(System.lineSeparator()).
append(isRequired ? "# Env required by the layer " : "# Env suggested by the layer ").
append(entry.getKey().getName()).append(System.lineSeparator());
for (Env env : entry.getValue()) {
envFileContent.append("# ").append(env.getName()).append("=").append(env.getDescription()).append(System.lineSeparator());
}
}
return envFileContent.toString();
}
void outputInformation(ScanResultsPrinter scanResultsPrinter, ScanResults scanResults) throws Exception {
scanResultsPrinter.print(arguments, scanResults);
}
void outputCompactInformation(ScanResultsPrinter scanResultsPrinter, ScanResults scanResults) throws Exception {
scanResultsPrinter.printCompact(arguments, scanResults);
}
String getCompactInformation(ScanResultsPrinter scanResultsPrinter, ScanResults scanResults) throws Exception {
return scanResultsPrinter.getCompactInformation(arguments, scanResults);
}
private Path provisionServer(List binaries, GalleonProvisioningConfig activeConfig,
MavenRepoManager resolver, OutputFormat format, boolean isCloud, Path target) throws Exception {
Path tmpDir = null;
Path originalTarget = target;
if (OutputFormat.BOOTABLE_JAR.equals(format)) {
// We create a tmp directory in which the server is provisioned
tmpDir = Files.createTempDirectory("wildfly-glow-bootable");
target = tmpDir;
} else {
IoUtils.recursiveDelete(target);
}
Path ret = target;
try {
Utils.provisionServer(activeConfig, target.toAbsolutePath(), resolver, writer);
if (!binaries.isEmpty()) {
for (Path binary : binaries) {
Path deploymentTarget = target.resolve("standalone").
resolve("deployments").resolve(binary.getFileName().toString());
writer.info("Copy " + binary + " to " + deploymentTarget);
Files.copy(binary, deploymentTarget);
}
}
if (OutputFormat.BOOTABLE_JAR.equals(format)) {
String bootableJarName = "";
if (!binaries.isEmpty()) {
for (Path binary : binaries) {
int i = binary.getFileName().toString().lastIndexOf(".");
bootableJarName = bootableJarName + binary.getFileName().toString().substring(0, i);
}
} else {
bootableJarName = "hollow";
}
String vers = arguments.getVersion() == null ? FeaturePacks.getLatestVersion() : arguments.getVersion();
Path targetJarFile = originalTarget.toAbsolutePath().resolve(bootableJarName + "-" + vers + "-" + BootableJarSupport.BOOTABLE_SUFFIX + ".jar");
ret = targetJarFile;
Files.deleteIfExists(targetJarFile);
BootableJarSupport.packageBootableJar(targetJarFile, originalTarget.toAbsolutePath(),
activeConfig, tmpDir.toAbsolutePath(),
resolver,
new MessageWriter() {
@Override
public void verbose(Throwable cause, CharSequence message) {
if (writer.isVerbose()) {
writer.trace(message);
}
}
@Override
public void print(Throwable cause, CharSequence message) {
writer.info(message);
}
@Override
public void error(Throwable cause, CharSequence message) {
writer.error(message);
}
@Override
public boolean isVerboseEnabled() {
return writer.isVerbose();
}
@Override
public void close() throws Exception {
}
});
}
} finally {
if (tmpDir != null) {
IoUtils.recursiveDelete(tmpDir);
}
}
return ret;
}
private static void fixAddOns(ErrorIdentificationSession errorSession,
Set layers,
LayerMapping mapping,
Set allEnabledAddOns,
Set possibleAddOns,
Map disabledAddOns,
Arguments arguments) throws URISyntaxException, IOException {
// Remove from the possible AddOns the addOns family that are complete
Set familyOfAddOnsComplete = new TreeSet<>();
Map> membersInFamily = new HashMap<>();
Map> defaultMembersInFamily = new HashMap<>();
for (AddOn addOn : allEnabledAddOns) {
if (addOn.isDefault()) {
Set members = defaultMembersInFamily.get(addOn.getFamily());
if (members == null) {
members = new TreeSet<>();
defaultMembersInFamily.put(addOn.getFamily(), members);
}
members.add(addOn);
} else {
Set members = membersInFamily.get(addOn.getFamily());
if (members == null) {
members = new TreeSet<>();
membersInFamily.put(addOn.getFamily(), members);
}
members.add(addOn);
}
}
Set treatedFamily = new TreeSet<>();
Map> addOnsThatFixsCardinality = new HashMap<>();
for (AddOn addOn : allEnabledAddOns) {
Integer i = mapping.getAddOnsCardinalityInFamily().get(addOn.getFamily());
if (i != null) {
Set members = membersInFamily.get(addOn.getFamily());
if (members.size() == i) {
familyOfAddOnsComplete.add(addOn.getFamily());
Set ao = addOnsThatFixsCardinality.get(addOn.getFamily());
if (ao == null) {
ao = new TreeSet<>();
addOnsThatFixsCardinality.put(addOn.getFamily(), ao);
}
ao.add(addOn);
} else {
if (members.size() > i) {
if (!treatedFamily.contains(addOn.getFamily())) {
treatedFamily.add(addOn.getFamily());
boolean isError = true;
for (AddOn ao : members) {
for (AddOn ao2 : members) {
if (!ao.equals(ao2)) {
for (Layer l : ao2.getLayers()) {
if (l.getDependencies().containsAll(ao.getLayers())) {
isError = false;
break;
}
}
}
if (!isError) {
break;
}
}
if (!isError) {
break;
}
}
if (isError) {
errorSession.addError(new IdentifiedError("add-on cardinality violation", "add-ons family "
+ addOn.getFamily() + " accepts " + i + " members although " + members.size()
+ " are configured : " + members, ERROR));
}
}
}
}
}
}
Set treatedDefaultFamily = new TreeSet<>();
Set disabledAddOnsDueToDefault = new TreeSet<>();
for (AddOn addOn : allEnabledAddOns) {
if (addOn.isDefault()) {
Integer i = mapping.getAddOnsCardinalityInDefaultFamily().get(addOn.getFamily());
if (i != null) {
Set members = defaultMembersInFamily.get(addOn.getFamily());
if (members.size() > i) {
if (!treatedDefaultFamily.contains(addOn.getFamily())) {
treatedDefaultFamily.add(addOn.getFamily());
errorSession.addError(new IdentifiedError("add-on cardinality violation", "default in add-ons family "
+ addOn.getFamily() + " accepts " + i + " members although " + members.size()
+ " are configured : " + members, ERROR));
}
}
}
//Disable associated addOn
disabledAddOnsDueToDefault.add(addOn.getAssociatedNonDefault());
}
}
Iterator iterator = possibleAddOns.iterator();
while (iterator.hasNext()) {
AddOn addOn = iterator.next();
if (allEnabledAddOns.contains(addOn)) {
iterator.remove();
} else {
if (familyOfAddOnsComplete.contains(addOn.getFamily())) {
disabledAddOns.put(addOn, "add-on family " + addOn.getFamily() + " is complete");
iterator.remove();
} else {
// Never advise defaults
if (addOn.isDefault()) {
iterator.remove();
} else {
if (disabledAddOnsDueToDefault.contains(addOn.getName())) {
disabledAddOns.put(addOn, "default add-on " + addOn.getName() + ":default" + " is enabled");
iterator.remove();
}
}
}
}
}
// handle the case of layers expecting addOn
for (Layer l : layers) {
String familyExpected = l.getExpectFamily();
if (familyExpected != null) {
boolean found = false;
for (AddOn addOn : allEnabledAddOns) {
if (addOn.getFamily().equals(familyExpected)) {
found = true;
break;
}
}
if (!found) {
Set members = mapping.getAddOnFamilyMembers().get(familyExpected);
IdentifiedError err = new IdentifiedError("expected add-on not found",
"an add-on of the " + familyExpected + " family is expected by the " + l + " layer", ERROR);
err.getPossibleAddons().addAll(members);
errorSession.addError(err);
}
}
}
for (String family : addOnsThatFixsCardinality.keySet()) {
Set addons = addOnsThatFixsCardinality.get(family);
StringBuilder builder = new StringBuilder();
for (AddOn ao : addons) {
if (arguments.getUserEnabledAddOns().contains(ao.getName())) {
builder.append(Utils.getAddOnFix(ao, null));
}
}
if (builder.length() != 0) {
IdentifiedError err = new IdentifiedError("expected add-on not found", "expected add-on not found in family "
+ family, ERROR);
err.setFixed(builder.toString());
errorSession.addError(err);
}
}
}
private static GalleonProvisioningConfig buildProvisioningConfig(GalleonProvisioningConfig input,
UniverseResolver universeResolver, Set allBaseLayers,
Layer baseLayer,
Set decorators,
Set excludedLayers,
Map> fpDependencies,
String configName, String configStability, String packageStability, Map channelVersions) throws ProvisioningException {
Map map = new HashMap<>();
Map universeToGav = new HashMap<>();
for (GalleonFeaturePackConfig cfg : input.getFeaturePackDeps()) {
FeaturePackLocation.FPID loc = null;
FeaturePackLocation.FPID fpid = Utils.toMavenCoordinates(cfg.getLocation().getFPID(), universeResolver);
for (FeaturePackLocation.FPID f : fpDependencies.keySet()) {
if (fpid.getProducer().equals(f.getProducer())) {
loc = fpid;
break;
}
}
if(loc == null) {
throw new ProvisioningException("Input fp "+ cfg.getLocation() + " not found in resolved feature-packs " + fpDependencies.keySet());
}
map.put(loc.getProducer(), cfg);
universeToGav.put(cfg.getLocation().getProducer(), loc);
}
Map tmpFps = new HashMap<>();
FeaturePackLocation.FPID baseFPID = universeToGav.get(input.getFeaturePackDeps().iterator().next().getLocation().getProducer());
tmpFps.put(baseFPID.getProducer(), baseFPID);
for (Layer l : allBaseLayers) {
for(FPID fpid : l.getFeaturePacks()) {
tmpFps.put(fpid.getProducer(), fpid);
}
}
Set activeFeaturePacks = new LinkedHashSet<>();
// Order follow the one from the input
for(GalleonFeaturePackConfig cfg : input.getFeaturePackDeps()) {
FeaturePackLocation.FPID gav = universeToGav.get(cfg.getLocation().getProducer());
FeaturePackLocation.FPID fpid = tmpFps.get(gav.getProducer());
if (fpid != null) {
// Reset the version if ruled by channel
FPID orig = channelVersions.get(cfg.getLocation().getProducer());
if ( orig != null && orig.getLocation().isMavenCoordinates()) {
gav = gav.getLocation().replaceBuild(orig.getBuild()).getFPID();
}
activeFeaturePacks.add(gav);
}
}
GalleonProvisioningConfig.Builder activeConfigBuilder = GalleonProvisioningConfig.builder();
//List activeFPs = new ArrayList<>();
for (FeaturePackLocation.FPID fpid : activeFeaturePacks) {
GalleonFeaturePackConfig.Builder fpBuilder = GalleonFeaturePackConfig.builder(fpid.getLocation());
fpBuilder.setInheritConfigs(false);
fpBuilder.setInheritPackages(false);
// The input config included packages is to cover some wildfly tests corner cases.
GalleonFeaturePackConfig inCfg = map.get(fpid.getProducer());
fpBuilder.includeAllPackages(inCfg.getIncludedPackages());
//GalleonFeaturePack activeFP = new GalleonFeaturePack();
//activeFP.setLocation(fpid.getLocation().toString());
//activeFPs.add(activeFP);
activeConfigBuilder.addFeaturePackDep(fpBuilder.build());
}
//ProvisioningContext activeContext = provisioning.buildProvisioningContext(activeFPs);
GalleonConfigurationWithLayersBuilder configBuilder = GalleonConfigurationWithLayersBuilder.builder("standalone", configName);
configBuilder.includeLayer(baseLayer.getName());
for (Layer l : decorators) {
configBuilder.includeLayer(l.getName());
}
for (Layer l : excludedLayers) {
configBuilder.excludeLayer(l.getName());
}
activeConfigBuilder.addConfig(configBuilder.build());
Map options = new HashMap<>();
options.put(Constants.OPTIONAL_PACKAGES, Constants.PASSIVE_PLUS);
options.put("jboss-fork-embedded", "true");
if (configStability != null) {
options.put(Constants.CONFIG_STABILITY_LEVEL, configStability.toString());
}
if (packageStability != null) {
options.put(Constants.PACKAGE_STABILITY_LEVEL, packageStability.toString());
}
activeConfigBuilder.addOptions(options);
return activeConfigBuilder.build();
}
// Only use the default base layer
private static Map> findBaseLayer(LayerMapping mapping, Map all) {
//Identify servers
Map> roots = new HashMap<>();
for (String k : all.keySet()) {
Layer layer = all.get(k);
String kind = layer.getProperties().get(LayerMetadata.KIND);
if (kind != null && (kind.equals("base-layer") || kind.equals("default-base-layer"))) {
roots.put(layer, Utils.getTransitiveDependencies(all, layer, new HashSet<>()));
}
}
Map> ret = new HashMap<>();
ret.put(mapping.getDefaultBaseLayer(), roots.get(mapping.getDefaultBaseLayer()));
return ret;
}
}