in locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocation.java [1449:1605]
public Template buildTemplate(ComputeService computeService, ConfigBag config, JcloudsLocationCustomizer customizersDelegate) {
TemplateBuilder templateBuilder = config.get(TEMPLATE_BUILDER);
if (templateBuilder==null) {
templateBuilder = new PortableTemplateBuilder<PortableTemplateBuilder<?>>();
} else {
LOG.debug("jclouds using templateBuilder {} as custom base for provisioning in {} for {}", new Object[] {
templateBuilder, this, getCreationString(config)});
}
if (templateBuilder instanceof PortableTemplateBuilder<?>) {
if (((PortableTemplateBuilder<?>)templateBuilder).imageChooser()==null) {
Function<Iterable<? extends Image>, Image> chooser = getImageChooser(computeService, config);
templateBuilder.imageChooser(chooser);
} else {
// an image chooser is already set, so do nothing
}
} else {
// template builder supplied, and we cannot check image chooser status; warn, for now
LOG.warn("Cannot check imageChooser status for {} due to manually supplied black-box TemplateBuilder; "
+ "it is recommended to use a PortableTemplateBuilder if you supply a TemplateBuilder", getCreationString(config));
}
if (!Strings.isEmpty(config.get(CLOUD_REGION_ID))) {
templateBuilder.locationId(config.get(CLOUD_REGION_ID));
}
if (Strings.isNonBlank(config.get(HARDWARE_ID))) {
String oldHardwareId = config.get(HARDWARE_ID);
String newHardwareId = transformHardwareId(oldHardwareId, config);
if (!Objects.equal(oldHardwareId, newHardwareId)) {
LOG.info("Transforming hardwareId from " + oldHardwareId + " to " + newHardwareId + ", in " + toString());
config.put(HARDWARE_ID, newHardwareId);
}
}
// Apply the template builder and options properties
for (Map.Entry<ConfigKey<?>, ? extends TemplateBuilderCustomizer> entry : SUPPORTED_TEMPLATE_BUILDER_PROPERTIES.entrySet()) {
ConfigKey<?> key = entry.getKey();
Object val = config.containsKey(key) ? config.get(key) : key.getDefaultValue();
if (val != null) {
TemplateBuilderCustomizer code = entry.getValue();
code.apply(templateBuilder, config, val);
}
}
if (templateBuilder instanceof PortableTemplateBuilder) {
((PortableTemplateBuilder<?>)templateBuilder).attachComputeService(computeService);
// do the default last, and only if nothing else specified (guaranteed to be a PTB if nothing else specified)
if (groovyTruth(config.get(DEFAULT_IMAGE_ID))) {
if (((PortableTemplateBuilder<?>)templateBuilder).isBlank()) {
templateBuilder.imageId(config.get(DEFAULT_IMAGE_ID).toString());
}
}
}
customizersDelegate.customize(this, computeService, templateBuilder);
LOG.debug("jclouds using templateBuilder {} for provisioning in {} for {}", new Object[] {
templateBuilder, this, getCreationString(config)});
// Finally try to build the template
Template template = null;
Image image;
try {
// in AWS, hardware types A1, T4g, M6g, C6g, and R6g use arm64 which is not compatible with the usual x86_64;
// so don't pick those hardware types by default; would be nice to have a hardwareChooser on TemplateBuilder akin to
// imageChooser, but we don't; and would be nice if AWSEC2 used org.jclouds.ec2.domain.Image, rather than org.jclouds.compute.domain.Image,
// and Hardware stored org.jclouds.ec2.domain.Image.Architecture, so that TemplateBuilderImpl.resolveHardware / supportsImage did the
// right thing with images selected (noting that OperatingSystem.arch is different (eg "HVM");
// in the absence of that, in AWS we cheat and if min cores is not set, we set it to 2 to cause the good t3.micro to come back instead of a1.large;
// to see more details, step in to templateBuilder.build and inspect the hardwareSorter.sortedCopy(hardwares) and filter(..., hardwarePredicate)
if ("aws-ec2".equals(getProvider()) && Strings.isBlank(config.get(HARDWARE_ID)) && config.get(MIN_CORES)==null) {
templateBuilder.minCores(2);
}
template = templateBuilder.build();
if (template==null) throw new IllegalStateException("No matching template; check image and hardware constraints (e.g. OS, RAM); using "+templateBuilder);
image = template.getImage();
LOG.debug("jclouds found template "+template+" (image "+image+") for provisioning in "+this+" for "+getCreationString(config));
if (image==null) throw new IllegalStateException("No matching image in template at "+toStringNice()+"; check image constraints (OS, providers, ID); using "+templateBuilder);
} catch (AuthorizationException e) {
LOG.warn("Error resolving template -- not authorized (rethrowing: "+e+"); template is: "+template);
throw new IllegalStateException("Not authorized to access cloud "+toStringNice()+"; "+
"check identity, credentials, and endpoint (identity='"+getIdentity()+"', credential length "+getCredential().length()+")", e);
} catch (Exception e) {
try {
IOException ioe = Exceptions.getFirstThrowableOfType(e, IOException.class);
if (ioe != null) {
LOG.warn("IOException found...", ioe);
throw ioe;
}
if (listedAvailableTemplatesOnNoSuchTemplate.compareAndSet(false, true)) {
// delay subsequent log.warns (put in synch block) so the "Loading..." message is obvious
LOG.warn("Unable to match required VM template constraints "+templateBuilder+" when trying to provision VM in "+this+" (rethrowing): "+e);
logAvailableTemplates(config);
}
} catch (Exception e2) {
LOG.warn("Error loading available images to report (following original error matching template which will be rethrown): "+e2, e2);
throw new IllegalStateException("Unable to access cloud "+this+" to resolve "+templateBuilder+": "+e, e);
}
throw new IllegalStateException("Unable to match required VM template constraints "+templateBuilder+" when trying to provision VM in "+this+"; "
+ "see list of images in log. Root cause: "+e, e);
}
TemplateOptions options = template.getOptions();
// For windows, we need a startup-script to be executed that will enable winrm access.
// If there is already conflicting userMetadata, then don't replace it (and just warn).
// TODO this injection is hacky and (currently) cloud specific.
boolean windows = isWindows(template, config);
if (windows) {
String initScript = WinRmMachineLocation.getDefaultUserMetadataString(config());
String provider = getProvider();
if ("google-compute-engine".equals(provider)) {
// see https://cloud.google.com/compute/docs/startupscript:
// Set "sysprep-specialize-script-cmd" in metadata.
String startupScriptKey = "sysprep-specialize-script-cmd";
Object metadataMapRaw = config.get(USER_METADATA_MAP);
if (metadataMapRaw instanceof Map) {
Map<?,?> metadataMap = (Map<?, ?>) metadataMapRaw;
if (metadataMap.containsKey(startupScriptKey)) {
LOG.warn("Not adding startup-script for Windows VM on "+provider+", because already has key "+startupScriptKey+" in config "+USER_METADATA_MAP.getName());
} else {
Map<Object, Object> metadataMapReplacement = MutableMap.copyOf(metadataMap);
metadataMapReplacement.put(startupScriptKey, initScript);
config.put(USER_METADATA_MAP, metadataMapReplacement);
LOG.debug("Adding startup-script to enable WinRM for Windows VM on "+provider);
}
} else if (metadataMapRaw == null) {
Map<String, String> metadataMapReplacement = MutableMap.of(startupScriptKey, initScript);
config.put(USER_METADATA_MAP, metadataMapReplacement);
LOG.debug("Adding startup-script to enable WinRM for Windows VM on "+provider);
}
} else {
// For AWS and vCloudDirector, we just set user_metadata_string.
// For Azure-classic, there is no capability to execute a startup script.
boolean userMetadataString = config.containsKey(JcloudsLocationConfig.USER_METADATA_STRING);
boolean userMetadataMap = config.containsKey(JcloudsLocationConfig.USER_METADATA_MAP);
if (!(userMetadataString || userMetadataMap)) {
config.put(JcloudsLocationConfig.USER_METADATA_STRING, WinRmMachineLocation.getDefaultUserMetadataString(config()));
LOG.debug("Adding startup-script to enable WinRM for Windows VM on "+provider);
} else {
LOG.warn("Not adding startup-script for Windows VM on "+provider+", because already has config "
+(userMetadataString ? USER_METADATA_STRING.getName() : USER_METADATA_MAP.getName()));
}
}
}
for (Map.Entry<ConfigKey<?>, ? extends TemplateOptionCustomizer> entry : SUPPORTED_TEMPLATE_OPTIONS_PROPERTIES.entrySet()) {
ConfigKey<?> key = entry.getKey();
TemplateOptionCustomizer code = entry.getValue();
if (config.containsKey(key) && config.get(key) != null) {
code.apply(options, config, config.get(key));
}
}
return template;
}