Allows for lists in templateOptions
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/638d3e9b Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/638d3e9b Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/638d3e9b Branch: refs/heads/master Commit: 638d3e9b732dc30aab720bbfe258678de0d61347 Parents: 42e9aad Author: Martin Harris <[email protected]> Authored: Tue May 12 15:33:24 2015 +0100 Committer: Richard Downer <[email protected]> Committed: Thu May 28 17:27:34 2015 +0100 ---------------------------------------------------------------------- .../location/jclouds/JcloudsLocation.java | 99 ++++++++++++-------- .../location/jclouds/JcloudsLocationConfig.java | 4 +- ...ationTemplateOptionsCustomisersLiveTest.java | 27 +++++- 3 files changed, 85 insertions(+), 45 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/638d3e9b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java index d0cc0ce..69cfea6 100644 --- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java +++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocation.java @@ -1039,7 +1039,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im LOG.info("ignoring keyPair({}) in VM creation because not supported for cloud/type ({})", v, t); } }}) - .put(AUTO_GENERATE_KEYPAIRS, new CustomizeTemplateOptions() { + .put(AUTO_GENERATE_KEYPAIRS, new CustomizeTemplateOptions() { public void apply(TemplateOptions t, ConfigBag props, Object v) { if (t instanceof NovaTemplateOptions) { ((NovaTemplateOptions)t).generateKeyPair((Boolean)v); @@ -1049,7 +1049,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im LOG.info("ignoring auto-generate-keypairs({}) in VM creation because not supported for cloud/type ({})", v, t); } }}) - .put(AUTO_CREATE_FLOATING_IPS, new CustomizeTemplateOptions() { + .put(AUTO_CREATE_FLOATING_IPS, new CustomizeTemplateOptions() { public void apply(TemplateOptions t, ConfigBag props, Object v) { if (t instanceof NovaTemplateOptions) { ((NovaTemplateOptions)t).autoAssignFloatingIp((Boolean)v); @@ -1057,7 +1057,7 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im LOG.info("ignoring auto-generate-floating-ips({}) in VM creation because not supported for cloud/type ({})", v, t); } }}) - .put(AUTO_ASSIGN_FLOATING_IP, new CustomizeTemplateOptions() { + .put(AUTO_ASSIGN_FLOATING_IP, new CustomizeTemplateOptions() { public void apply(TemplateOptions t, ConfigBag props, Object v) { if (t instanceof NovaTemplateOptions) { ((NovaTemplateOptions)t).autoAssignFloatingIp((Boolean)v); @@ -1067,11 +1067,11 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im LOG.info("ignoring auto-assign-floating-ip({}) in VM creation because not supported for cloud/type ({})", v, t); } }}) - .put(NETWORK_NAME, new CustomizeTemplateOptions() { + .put(NETWORK_NAME, new CustomizeTemplateOptions() { public void apply(TemplateOptions t, ConfigBag props, Object v) { t.networks((String)v); }}) - .put(DOMAIN_NAME, new CustomizeTemplateOptions() { + .put(DOMAIN_NAME, new CustomizeTemplateOptions() { public void apply(TemplateOptions t, ConfigBag props, Object v) { if (t instanceof SoftLayerTemplateOptions) { ((SoftLayerTemplateOptions)t).domainName(TypeCoercions.coerce(v, String.class)); @@ -1079,42 +1079,59 @@ public class JcloudsLocation extends AbstractCloudMachineProvisioningLocation im LOG.info("ignoring domain-name({}) in VM creation because not supported for cloud/type ({})", v, t); } }}) - .put(TEMPLATE_OPTIONS, new CustomizeTemplateOptions() { - @Override - public void apply(TemplateOptions options, ConfigBag config, Object v) { - if (v == null) return; - @SuppressWarnings("unchecked") Map<String, String> optionsMap = (Map<String, String>) v; - if (optionsMap.isEmpty()) return; - - Class<? extends TemplateOptions> clazz = options.getClass(); - Iterable<Method> methods = Arrays.asList(clazz.getMethods()); - for(final Map.Entry<String, String> option : optionsMap.entrySet()) { - Optional<Method> methodOptional = Iterables.tryFind(methods, new Predicate<Method>() { - @Override - public boolean apply(@Nullable Method input) { - // Matches a method with the expected name, and a single parameter that TypeCoercions - // can coerce to - if (input == null) return false; - if (!input.getName().equals(option.getKey())) return false; - Type[] parameterTypes = input.getGenericParameterTypes(); - return parameterTypes.length == 1 - && TypeCoercions.tryCoerce(option.getValue(), TypeToken.of(parameterTypes[0])).isPresentAndNonNull(); - } - }); - if(methodOptional.isPresent()) { - try { - Method method = methodOptional.get(); - method.invoke(options, TypeCoercions.coerce(option.getValue(), TypeToken.of(method.getGenericParameterTypes()[0]))); - } catch (IllegalAccessException e) { - throw Exceptions.propagate(e); - } catch (InvocationTargetException e) { - throw Exceptions.propagate(e); - } - } else { - LOG.warn("Ignoring request to set template option {} because this is not supported by {}", new Object[] { option.getKey(), clazz.getCanonicalName() }); - } - } - } + .put(TEMPLATE_OPTIONS, new CustomizeTemplateOptions() { + @Override + public void apply(TemplateOptions options, ConfigBag config, Object v) { + if (v == null) return; + @SuppressWarnings("unchecked") Map<String, Object> optionsMap = (Map<String, Object>) v; + if (optionsMap.isEmpty()) return; + + Class<? extends TemplateOptions> clazz = options.getClass(); + Iterable<Method> methods = Arrays.asList(clazz.getMethods()); + for(final Map.Entry<String, Object> option : optionsMap.entrySet()) { + Optional<Method> methodOptional = Iterables.tryFind(methods, new Predicate<Method>() { + @Override + public boolean apply(@Nullable Method input) { + // Matches a method with the expected name, and a single parameter that TypeCoercions + // can coerce to + if (input == null) return false; + if (!input.getName().equals(option.getKey())) return false; + int numOptionParams = option.getValue() instanceof List ? ((List)option.getValue()).size() : 1; + Type[] parameterTypes = input.getGenericParameterTypes(); + if (parameterTypes.length != numOptionParams) return false; + if (numOptionParams == 1 && !(option.getValue() instanceof List) && parameterTypes.length == 1) { + return true; + } + for (int paramCount = 0; paramCount < numOptionParams; paramCount ++) { + if (!TypeCoercions.tryCoerce(((List)option.getValue()).get(paramCount), + TypeToken.of(parameterTypes[paramCount])).isPresentAndNonNull()) return false; + } + return true; + } + }); + if(methodOptional.isPresent()) { + try { + Method method = methodOptional.get(); + if (option.getValue() instanceof List) { + List<Object> parameters = Lists.newArrayList(); + int numOptionParams = ((List)option.getValue()).size(); + for (int paramCount = 0; paramCount < numOptionParams; paramCount++) { + parameters.add(TypeCoercions.coerce(((List)option.getValue()).get(paramCount), TypeToken.of(method.getGenericParameterTypes()[paramCount]))); + } + method.invoke(options, parameters.toArray()); + } else { + method.invoke(options, TypeCoercions.coerce(option.getValue(), TypeToken.of(method.getGenericParameterTypes()[0]))); + } + } catch (IllegalAccessException e) { + throw Exceptions.propagate(e); + } catch (InvocationTargetException e) { + throw Exceptions.propagate(e); + } + } else { + LOG.warn("Ignoring request to set template option {} because this is not supported by {}", new Object[] { option.getKey(), clazz.getCanonicalName() }); + } + } + } }) .build(); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/638d3e9b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java index fb3e2ad..fcaa0a3 100644 --- a/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java +++ b/locations/jclouds/src/main/java/brooklyn/location/jclouds/JcloudsLocationConfig.java @@ -257,8 +257,8 @@ public interface JcloudsLocationConfig extends CloudLocationConfig { ComputeServiceRegistryImpl.INSTANCE); @SuppressWarnings("serial") - public static final ConfigKey<Map<String,String>> TEMPLATE_OPTIONS = ConfigKeys.newConfigKey( - new TypeToken<Map<String, String>>() {}, "templateOptions", "Additional jclouds template options"); + public static final ConfigKey<Map<String,Object>> TEMPLATE_OPTIONS = ConfigKeys.newConfigKey( + new TypeToken<Map<String, Object>>() {}, "templateOptions", "Additional jclouds template options"); // TODO http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/638d3e9b/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java ---------------------------------------------------------------------- diff --git a/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java b/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java index f4c44a4..d22e134 100644 --- a/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java +++ b/locations/jclouds/src/test/java/brooklyn/location/jclouds/JcloudsLocationTemplateOptionsCustomisersLiveTest.java @@ -30,6 +30,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import org.jclouds.aws.ec2.compute.AWSEC2TemplateOptions; import org.jclouds.compute.options.TemplateOptions; +import org.jclouds.ec2.domain.BlockDeviceMapping; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.testng.Assert; @@ -44,6 +45,7 @@ import java.util.Map; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; public class JcloudsLocationTemplateOptionsCustomisersLiveTest extends AbstractJcloudsLiveTest { @@ -59,10 +61,10 @@ public class JcloudsLocationTemplateOptionsCustomisersLiveTest extends AbstractJ // Doesn't actually do much with the cloud, but jclouds requires identity and credential before it will work @Test(groups = "Live") public void testGeneralPurposeTemplateOptionCustomisation() throws Exception { - ConfigKey<Map<String, String>> key = JcloudsLocationConfig.TEMPLATE_OPTIONS; + ConfigKey<Map<String, Object>> key = JcloudsLocationConfig.TEMPLATE_OPTIONS; ConfigBag config = ConfigBag.newInstance() - .configure(key, ImmutableMap.of("iamInstanceProfileName", "helloworld")); + .configure(key, ImmutableMap.of("iamInstanceProfileName", (Object)"helloworld")); AWSEC2TemplateOptions templateOptions = jcloudsLocation.getComputeService().templateOptions().as(AWSEC2TemplateOptions.class); invokeCustomizeTemplateOptions(templateOptions, JcloudsLocationConfig.TEMPLATE_OPTIONS, config); @@ -70,6 +72,27 @@ public class JcloudsLocationTemplateOptionsCustomisersLiveTest extends AbstractJ assertEquals(templateOptions.getIAMInstanceProfileName(), "helloworld"); } + // Doesn't actually do much with the cloud, but jclouds requires identity and credential before it will work + @Test(groups = "Live") + public void testGeneralPurposeTemplateOptionCustomisationWithList() throws Exception { + ConfigKey<Map<String, Object>> key = JcloudsLocationConfig.TEMPLATE_OPTIONS; + + ConfigBag config = ConfigBag.newInstance() + .configure(key, ImmutableMap.of( + "iamInstanceProfileName", (Object) "helloworld", + "mapNewVolumeToDeviceName", (Object) ImmutableList.of("/dev/sda1/", 123, true))); + AWSEC2TemplateOptions templateOptions = jcloudsLocation.getComputeService().templateOptions().as(AWSEC2TemplateOptions.class); + + invokeCustomizeTemplateOptions(templateOptions, JcloudsLocationConfig.TEMPLATE_OPTIONS, config); + + assertEquals(templateOptions.getIAMInstanceProfileName(), "helloworld"); + assertEquals(templateOptions.getBlockDeviceMappings().size(), 1); + BlockDeviceMapping blockDeviceMapping = templateOptions.getBlockDeviceMappings().iterator().next(); + assertEquals(blockDeviceMapping.getDeviceName(), "/dev/sda1/"); + assertEquals(blockDeviceMapping.getEbsVolumeSize(), (Integer)123); + assertTrue(blockDeviceMapping.getEbsDeleteOnTermination()); + } + /** * Invoke a specific template options customizer on a TemplateOptions instance. *
