From 70bd8c3f6508e3a5b93025884afe678b10df67f1 Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Sat, 13 Aug 2016 21:25:41 +0100 Subject: [PATCH 1/2] Update BasicJcloudsLocationCustomizer to be configurable and remove deprecated methods --- .../BasicJcloudsLocationCustomizer.java | 58 ++++++----------- .../jclouds/JcloudsLocationCustomizer.java | 45 +++---------- .../jclouds/SudoTtyFixingCustomizer.java | 20 +++--- ...cloudsLocationSecurityGroupCustomizer.java | 65 +++++++++---------- .../SoftLayerSameVlanLocationCustomizer.java | 20 ++---- 5 files changed, 72 insertions(+), 136 deletions(-) diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java index e73d5f7587..500442ea42 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/BasicJcloudsLocationCustomizer.java @@ -23,18 +23,14 @@ import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.TemplateOptions; -import com.google.common.annotations.Beta; - +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.core.location.LocationConfigKeys; +import org.apache.brooklyn.core.objs.BasicConfigurableObject; /** * A default no-op implementation, which can be extended to override the appropriate methods. - * - * Sub-classing will give the user some protection against future API changes - note that - * {@link JcloudsLocationCustomizer} is marked {@link Beta}. - * - * @author aled */ -public class BasicJcloudsLocationCustomizer implements JcloudsLocationCustomizer { +public class BasicJcloudsLocationCustomizer extends BasicConfigurableObject implements JcloudsLocationCustomizer { @Override public void customize(JcloudsLocation location, ComputeService computeService, TemplateBuilder templateBuilder) { @@ -53,47 +49,31 @@ public void customize(JcloudsLocation location, ComputeService computeService, T @Override public void customize(JcloudsLocation location, ComputeService computeService, JcloudsMachineLocation machine) { - if (machine instanceof JcloudsSshMachineLocation) { - customize(location, computeService, (JcloudsSshMachineLocation)machine); - } else { - // no-op - } + // no-op } - + @Override public void preRelease(JcloudsMachineLocation machine) { - if (machine instanceof JcloudsSshMachineLocation) { - preRelease((JcloudsSshMachineLocation)machine); - } else { - // no-op - } + // no-op } @Override public void postRelease(JcloudsMachineLocation machine) { - if (machine instanceof JcloudsSshMachineLocation) { - postRelease((JcloudsSshMachineLocation)machine); - } else { - // no-op - } - } - - @Override - @Deprecated - public void customize(JcloudsLocation location, ComputeService computeService, JcloudsSshMachineLocation machine) { // no-op } - @Override - @Deprecated - public void preRelease(JcloudsSshMachineLocation machine) { - // no-op - } + /** @return the calling entity */ + protected Entity getCallerContext(JcloudsMachineLocation machine) { + SudoTtyFixingCustomizer s; - @Override - @Deprecated - public void postRelease(JcloudsSshMachineLocation machine) { - // no-op + Object context = config().get(LocationConfigKeys.CALLER_CONTEXT); + if (context == null) { + context = machine.config().get(LocationConfigKeys.CALLER_CONTEXT); + } + if (!(context instanceof Entity)) { + throw new IllegalStateException("Invalid location context: " + context); + } + Entity entity = (Entity) context; + return entity; } - } diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java index 696a4627df..d768cb4d1f 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/JcloudsLocationCustomizer.java @@ -18,23 +18,21 @@ */ package org.apache.brooklyn.location.jclouds; -import org.apache.brooklyn.util.core.config.ConfigBag; import org.jclouds.compute.ComputeService; import org.jclouds.compute.domain.Template; import org.jclouds.compute.domain.TemplateBuilder; import org.jclouds.compute.options.TemplateOptions; -import com.google.common.annotations.Beta; +import org.apache.brooklyn.util.core.config.ConfigBag; /** * Customization hooks to allow apps to perform specific customisation at each stage of jclouds machine provisioning. * For example, an app could attach an EBS volume to an EC2 node, or configure a desired availability zone. - *

+ *

* Instances will be invoked with the {@link ConfigBag} being used to obtain a machine by the - * {@link JcloudsLocation }if such a constructor exists. If not, the default no argument constructor + * {@link JcloudsLocation} if such a constructor exists. If not, the default no argument constructor * will be invoked. */ -@Beta public interface JcloudsLocationCustomizer { /** @@ -46,7 +44,7 @@ public interface JcloudsLocationCustomizer { /** * Override to configure a subclass of this with the built template, or to configure the built * template's {@link org.jclouds.compute.options.TemplateOptions}. - *

+ *

* This method will be called before {@link #customize(JcloudsLocation, ComputeService, TemplateOptions)}. */ void customize(JcloudsLocation location, ComputeService computeService, Template template); @@ -59,39 +57,12 @@ public interface JcloudsLocationCustomizer { /** * Override to configure the given machine once it has been created and started by Jclouds. - *

- * If {@link JcloudsLocationConfig#WAIT_FOR_SSHABLE} is true the - * machine is guaranteed to be SSHable when this method is called. - * - * @since 0.7.0; use {@link #customize(JcloudsLocation, ComputeService, JcloudsMachineLocation)} - */ - @Deprecated - void customize(JcloudsLocation location, ComputeService computeService, JcloudsSshMachineLocation machine); - - /** - * Override to handle machine-related cleanup before Jclouds is called to release (destroy) the machine. - * - * @since 0.7.0; use {@link #preRelease(JcloudsMachineLocation)} - */ - @Deprecated - void preRelease(JcloudsSshMachineLocation machine); - - /** - * Override to handle machine-related cleanup after Jclouds is called to release (destroy) the machine. - * - * @since 0.7.0; use {@link #postRelesae(JcloudsMachineLocation)} - */ - @Deprecated - void postRelease(JcloudsSshMachineLocation machine); - - /** - * Override to configure the given machine once it has been created and started by Jclouds. - *

- * If {@link JcloudsLocationConfig#WAIT_FOR_SSHABLE} is true the - * machine is guaranteed to be SSHable when this method is called. + *

+ * If {@link JcloudsLocationConfig#WAIT_FOR_SSHABLE} is true the machine is guaranteed to be + * SSHable when this method is called. */ void customize(JcloudsLocation location, ComputeService computeService, JcloudsMachineLocation machine); - + /** * Override to handle machine-related cleanup before Jclouds is called to release (destroy) the machine. */ diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/SudoTtyFixingCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/SudoTtyFixingCustomizer.java index 2552874734..f2b8d4fb87 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/SudoTtyFixingCustomizer.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/SudoTtyFixingCustomizer.java @@ -30,19 +30,15 @@ /** * Wraps Brooklyn's sudo-tty mitigations in a {@link JcloudsLocationCustomizer} for easy(-ish) consumption * in YAML blueprints: + *

{@code
+ * name: My App
+ * brooklyn.config:
+ *   provisioning.properties:
+ *     customizerType: SudoTtyFixingCustomizer
+ * }
+ * This class should be seen as a temporary workaround and might disappear completely if/when Brooklyn takes care of this automatically. * - *
- *   name: My App
- *   brooklyn.config:
- *     provisioning.properties:
- *       customizerType: SudoTtyFixingCustomizer
- *   services: ...
- * 
- * - *

This class should be seen as a temporary workaround and might disappear completely if/when Brooklyn takes care of this automatically. - * - *

See - * http://unix.stackexchange.com/questions/122616/why-do-i-need-a-tty-to-run-sudo-if-i-can-sudo-without-a-password + * @see http://unix.stackexchange.com/questions/122616/why-do-i-need-a-tty-to-run-sudo-if-i-can-sudo-without-a-password * for background. */ @Beta diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizer.java index 3d6bc22d8b..4dcce1a997 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizer.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/networking/JcloudsLocationSecurityGroupCustomizer.java @@ -27,35 +27,9 @@ import javax.annotation.Nullable; -import org.apache.brooklyn.api.entity.Entity; -import org.apache.brooklyn.core.location.geo.LocalhostExternalIpLoader; -import org.apache.brooklyn.location.jclouds.JcloudsLocation; -import org.apache.brooklyn.location.jclouds.JcloudsLocationCustomizer; -import org.apache.brooklyn.location.jclouds.JcloudsMachineLocation; -import org.apache.brooklyn.location.jclouds.JcloudsSshMachineLocation; - -import org.jclouds.aws.AWSResponseException; -import org.jclouds.compute.ComputeService; -import org.jclouds.compute.domain.SecurityGroup; -import org.jclouds.compute.domain.Template; -import org.jclouds.compute.extensions.SecurityGroupExtension; -import org.jclouds.domain.Location; -import org.jclouds.net.domain.IpPermission; -import org.jclouds.net.domain.IpProtocol; -import org.jclouds.providers.ProviderMetadata; -import org.jclouds.providers.Providers; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.apache.brooklyn.location.jclouds.BasicJcloudsLocationCustomizer; -import org.apache.brooklyn.util.collections.MutableList; -import org.apache.brooklyn.util.core.task.Tasks; -import org.apache.brooklyn.util.exceptions.Exceptions; -import org.apache.brooklyn.util.net.Cidr; -import org.apache.brooklyn.util.time.Duration; - -import com.google.common.annotations.Beta; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Optional; @@ -74,19 +48,44 @@ import com.google.common.collect.Iterables; import com.google.common.util.concurrent.UncheckedExecutionException; +import org.jclouds.aws.AWSResponseException; +import org.jclouds.compute.ComputeService; +import org.jclouds.compute.domain.SecurityGroup; +import org.jclouds.compute.domain.Template; +import org.jclouds.compute.extensions.SecurityGroupExtension; +import org.jclouds.domain.Location; +import org.jclouds.net.domain.IpPermission; +import org.jclouds.net.domain.IpProtocol; +import org.jclouds.providers.ProviderMetadata; +import org.jclouds.providers.Providers; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.core.location.geo.LocalhostExternalIpLoader; +import org.apache.brooklyn.location.jclouds.BasicJcloudsLocationCustomizer; +import org.apache.brooklyn.location.jclouds.JcloudsLocation; +import org.apache.brooklyn.location.jclouds.JcloudsLocationConfig; +import org.apache.brooklyn.location.jclouds.JcloudsLocationCustomizer; +import org.apache.brooklyn.location.jclouds.JcloudsMachineLocation; +import org.apache.brooklyn.location.jclouds.JcloudsSshMachineLocation; +import org.apache.brooklyn.util.collections.MutableList; +import org.apache.brooklyn.util.core.task.Tasks; +import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.net.Cidr; +import org.apache.brooklyn.util.time.Duration; + /** * Configures custom security groups on Jclouds locations. - * - * @see SecurityGroupExtension is an optional extension to jclouds compute service. It allows the manipulation of - * {@link SecurityGroup}s. - * + *

* This customizer can be injected into {@link JcloudsLocation#obtainOnce} using - * It will be executed after the provisiioning of the {@link JcloudsMachineLocation} to apply app-specific - * customization related to the security groups. + * the {@link JcloudsLocationConfig#JCLOUDS_LOCATION_CUSTOMIZERS} configuration key. + * It will be executed after the provisiioning of the {@link JcloudsMachineLocation} + * to apply app-specific customization related to the security groups. + *

+ * {@link SecurityGroupExtension} is an optional extension to the jclouds compute + * service. It allows the manipulation of {@link SecurityGroup security groups}. * * @since 0.7.0 */ -@Beta public class JcloudsLocationSecurityGroupCustomizer extends BasicJcloudsLocationCustomizer { private static final Logger LOG = LoggerFactory.getLogger(JcloudsLocationSecurityGroupCustomizer.class); diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/softlayer/SoftLayerSameVlanLocationCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/softlayer/SoftLayerSameVlanLocationCustomizer.java index 5158a0cba2..8ada2aaf9b 100644 --- a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/softlayer/SoftLayerSameVlanLocationCustomizer.java +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/softlayer/SoftLayerSameVlanLocationCustomizer.java @@ -105,26 +105,16 @@ public class SoftLayerSameVlanLocationCustomizer extends BasicJcloudsLocationCus public static final AttributeSensor PRIVATE_VLAN_ID = Sensors.newIntegerSensor( "softLayer.vlan.privateId", "The private VLAN ID for this entity"); - /* Flags passed in on object creation. */ - private final Map flags; - /* Lock object for global critical sections accessing shared state maps. */ private static final transient Object lock = new Object[0]; /** Convenience creation method. */ public static SoftLayerSameVlanLocationCustomizer forScope(String scopeUid) { - SoftLayerSameVlanLocationCustomizer customizer = new SoftLayerSameVlanLocationCustomizer(ImmutableMap.of(SCOPE_UID.getName(), scopeUid)); + SoftLayerSameVlanLocationCustomizer customizer = new SoftLayerSameVlanLocationCustomizer(); + customizer.config().set(SCOPE_UID, scopeUid); return customizer; } - public SoftLayerSameVlanLocationCustomizer() { - this(ImmutableMap.of()); - } - - public SoftLayerSameVlanLocationCustomizer(Map flags) { - this.flags = ImmutableMap.copyOf(flags); - } - /** * Update the {@link org.jclouds.compute.options.TemplateOptions} that will * be used by {@link JcloudsLocation} to obtain machines. Uses the VLAN @@ -287,7 +277,7 @@ public void customize(JcloudsLocation location, ComputeService computeService, J /* Save the VLAN IDs as sensor data on the entity and set tag. */ private void saveVlanDetails(JcloudsMachineLocation machine, String scopeUid, Integer privateVlanId, Integer publicVlanId) { - Object context = flags.get(LocationConfigKeys.CALLER_CONTEXT.getName()); + Object context = config().get(LocationConfigKeys.CALLER_CONTEXT); if (context == null) { context = machine.config().get(LocationConfigKeys.CALLER_CONTEXT); } @@ -306,7 +296,7 @@ private void saveVlanDetails(JcloudsMachineLocation machine, String scopeUid, In * location flags, or the location itself. */ private Duration getTimeout(JcloudsLocation location) { - Duration timeout = (Duration) flags.get(SCOPE_TIMEOUT.getName()); + Duration timeout = config().get(SCOPE_TIMEOUT); if (timeout == null) { timeout = location.config().get(SCOPE_TIMEOUT); } @@ -318,7 +308,7 @@ private Duration getTimeout(JcloudsLocation location) { * location itself. */ private String getScopeUid(JcloudsLocation location) { - String scopeUid = (String) flags.get(SCOPE_UID.getName()); + String scopeUid = config().get(SCOPE_UID); if (Strings.isEmpty(scopeUid)) { scopeUid = location.config().get(SCOPE_UID); } From 1cdf00fab12033e7431b268a5727e148fee6e4b4 Mon Sep 17 00:00:00 2001 From: Andrew Donald Kennedy Date: Sat, 13 Aug 2016 21:26:12 +0100 Subject: [PATCH 2/2] Add new location customizer that creates child entities early --- .../jclouds/ChildEntityCustomizer.java | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ChildEntityCustomizer.java diff --git a/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ChildEntityCustomizer.java b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ChildEntityCustomizer.java new file mode 100644 index 0000000000..e30f9de402 --- /dev/null +++ b/locations/jclouds/src/main/java/org/apache/brooklyn/location/jclouds/ChildEntityCustomizer.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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.apache.brooklyn.location.jclouds; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableList; +import com.google.common.reflect.TypeToken; + +import org.jclouds.compute.ComputeService; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.mgmt.Task; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.entity.BrooklynConfigKeys; +import org.apache.brooklyn.core.entity.Entities; +import org.apache.brooklyn.core.entity.trait.Startable; +import org.apache.brooklyn.core.location.LocationConfigKeys; +import org.apache.brooklyn.entity.stock.ConditionalEntity; +import org.apache.brooklyn.util.core.flags.SetFromFlag; +import org.apache.brooklyn.util.time.Duration; + +/** + * Location customizer that optionally creates and starts a child entity. + *

+ * Use this class when the child entity should be created and started + * before the parent entity has begun executing its lifecycle + * methods. This is useful for supporting agent software installation + * or machine configuration tasks that are too complex to be defined + * in a singe {@code pre.install.command} script. + *

+ * The {@link #CREATE_CHILD_ENTITY} configuration key can be set to + * false to disable creation of the child entity. + * + * @see {@link ConditionalEntity} for an alternative mechanism + */ +public class ChildEntityCustomizer extends BasicJcloudsLocationCustomizer { + + private static final Logger LOG = LoggerFactory.getLogger(ChildEntityCustomizer.class); + + @SetFromFlag("childSpec") + public static final ConfigKey> CHILD_ENTITY_SPEC = ConfigKeys.newConfigKey(new TypeToken>() { }, + "child.entitySpec", "The specification for the child entity to be created"); + + @SetFromFlag("create") + public static final ConfigKey CREATE_CHILD_ENTITY = ConfigKeys.newBooleanConfigKey( + "child.create", "Whether the child entity should be created", + Boolean.TRUE); + + @Override + public void customize(JcloudsLocation location, ComputeService computeService, JcloudsMachineLocation machine) { + EntitySpec spec = config().get(CHILD_ENTITY_SPEC); + Boolean create = config().get(CREATE_CHILD_ENTITY); + Duration timeout = config().get(BrooklynConfigKeys.START_TIMEOUT); + Entity parent = getCallerContext(machine); + + if (Boolean.TRUE.equals(create) && spec != null) { + LOG.info("Creating child entity for {} in {}", parent, machine); + Entity child = getBrooklynManagementContext().getEntityManager().createEntity(spec); + child.setParent(parent); + Task start = Entities.invokeEffectorWithArgs(parent, child, Startable.START, ImmutableList.of(machine)); + if (!start.blockUntilEnded(timeout)) { + throw new IllegalStateException(String.format("Timed out while starting child entity for %s", parent)); + } + } + } +} \ No newline at end of file