Return-Path: X-Original-To: apmail-brooklyn-commits-archive@minotaur.apache.org Delivered-To: apmail-brooklyn-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id E803910388 for ; Thu, 5 Jun 2014 19:15:37 +0000 (UTC) Received: (qmail 53708 invoked by uid 500); 5 Jun 2014 19:15:37 -0000 Delivered-To: apmail-brooklyn-commits-archive@brooklyn.apache.org Received: (qmail 53685 invoked by uid 500); 5 Jun 2014 19:15:37 -0000 Mailing-List: contact commits-help@brooklyn.incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@brooklyn.incubator.apache.org Delivered-To: mailing list commits@brooklyn.incubator.apache.org Received: (qmail 53678 invoked by uid 99); 5 Jun 2014 19:15:37 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 05 Jun 2014 19:15:37 +0000 X-ASF-Spam-Status: No, hits=-2000.7 required=5.0 tests=ALL_TRUSTED,RP_MATCHES_RCVD X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO mail.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with SMTP; Thu, 05 Jun 2014 19:15:37 +0000 Received: (qmail 53538 invoked by uid 99); 5 Jun 2014 19:15:11 -0000 Received: from tyr.zones.apache.org (HELO tyr.zones.apache.org) (140.211.11.114) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 05 Jun 2014 19:15:11 +0000 Received: by tyr.zones.apache.org (Postfix, from userid 65534) id 9065194C6C4; Thu, 5 Jun 2014 19:15:11 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: richard@apache.org To: commits@brooklyn.incubator.apache.org Date: Thu, 05 Jun 2014 19:15:11 -0000 Message-Id: <25ee0cb552344f28b5f06487c84275da@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: =?utf-8?q?=5B1/2=5D_git_commit=3A_Adds_expungeMembers_to_DynamicCl?= =?utf-8?q?uster=E2=80=99s_QuarantineGroup?= X-Virus-Checked: Checked by ClamAV on apache.org Repository: incubator-brooklyn Updated Branches: refs/heads/master 338de668e -> ba8209b65 Adds expungeMembers to DynamicCluster’s QuarantineGroup Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/148b5037 Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/148b5037 Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/148b5037 Branch: refs/heads/master Commit: 148b503711c376c413c3e1eabaa804daca221cd2 Parents: 338de66 Author: Aled Sage Authored: Thu Jun 5 20:49:53 2014 +0200 Committer: Aled Sage Committed: Thu Jun 5 20:52:29 2014 +0200 ---------------------------------------------------------------------- .../brooklyn/entity/group/DynamicCluster.java | 2 +- .../entity/group/DynamicClusterImpl.java | 6 +- .../brooklyn/entity/group/QuarantineGroup.java | 14 ++++ .../entity/group/QuarantineGroupImpl.java | 70 ++++++++++++++++++ .../entity/group/DynamicClusterTest.java | 13 ++++ .../entity/group/QuarantineGroupTest.java | 74 ++++++++++++++++++++ 6 files changed, 174 insertions(+), 5 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/148b5037/core/src/main/java/brooklyn/entity/group/DynamicCluster.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/group/DynamicCluster.java b/core/src/main/java/brooklyn/entity/group/DynamicCluster.java index acd9c76..d583bf7 100644 --- a/core/src/main/java/brooklyn/entity/group/DynamicCluster.java +++ b/core/src/main/java/brooklyn/entity/group/DynamicCluster.java @@ -89,7 +89,7 @@ public interface DynamicCluster extends AbstractGroup, Cluster, MemberReplaceabl BasicNotificationSensor ENTITY_QUARANTINED = new BasicNotificationSensor(Entity.class, "dynamiccluster.entityQuarantined", "Entity failed to start, and has been quarantined"); - AttributeSensor QUARANTINE_GROUP = Sensors.newSensor(Group.class, "dynamiccluster.quarantineGroup", "Group of quarantined entities that failed to start"); + AttributeSensor QUARANTINE_GROUP = Sensors.newSensor(QuarantineGroup.class, "dynamiccluster.quarantineGroup", "Group of quarantined entities that failed to start"); @SetFromFlag("initialQuorumSize") ConfigKey INITIAL_QUORUM_SIZE = ConfigKeys.newIntegerConfigKey( http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/148b5037/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java b/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java index c829593..b323dea 100644 --- a/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java +++ b/core/src/main/java/brooklyn/entity/group/DynamicClusterImpl.java @@ -17,9 +17,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import brooklyn.entity.Entity; -import brooklyn.entity.Group; import brooklyn.entity.basic.AbstractGroupImpl; -import brooklyn.entity.basic.BasicGroup; import brooklyn.entity.basic.Entities; import brooklyn.entity.basic.EntityFactory; import brooklyn.entity.basic.EntityFactoryForLocation; @@ -190,7 +188,7 @@ public class DynamicClusterImpl extends AbstractGroupImpl implements DynamicClus return getConfig(QUARANTINE_FAILED_ENTITIES); } - protected Group getQuarantineGroup() { + protected QuarantineGroup getQuarantineGroup() { return getAttribute(QUARANTINE_GROUP); } @@ -226,7 +224,7 @@ public class DynamicClusterImpl extends AbstractGroupImpl implements DynamicClus setAttribute(SERVICE_UP, calculateServiceUp()); try { if (isQuarantineEnabled()) { - Group quarantineGroup = addChild(EntitySpec.create(BasicGroup.class).displayName("quarantine")); + QuarantineGroup quarantineGroup = addChild(EntitySpec.create(QuarantineGroup.class).displayName("quarantine")); Entities.manage(quarantineGroup); setAttribute(QUARANTINE_GROUP, quarantineGroup); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/148b5037/core/src/main/java/brooklyn/entity/group/QuarantineGroup.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/group/QuarantineGroup.java b/core/src/main/java/brooklyn/entity/group/QuarantineGroup.java new file mode 100644 index 0000000..15026ee --- /dev/null +++ b/core/src/main/java/brooklyn/entity/group/QuarantineGroup.java @@ -0,0 +1,14 @@ +package brooklyn.entity.group; + +import brooklyn.entity.annotation.Effector; +import brooklyn.entity.annotation.EffectorParam; +import brooklyn.entity.basic.AbstractGroup; +import brooklyn.entity.proxying.ImplementedBy; + +@ImplementedBy(QuarantineGroupImpl.class) +public interface QuarantineGroup extends AbstractGroup { + + @Effector(description="Removes all members of the quarantined group, unmanaging them") + void expungeMembers( + @EffectorParam(name="firstStop", description="Whether to first call stop() on those members that are stoppable") boolean stopFirst); +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/148b5037/core/src/main/java/brooklyn/entity/group/QuarantineGroupImpl.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/entity/group/QuarantineGroupImpl.java b/core/src/main/java/brooklyn/entity/group/QuarantineGroupImpl.java new file mode 100644 index 0000000..df6b85f --- /dev/null +++ b/core/src/main/java/brooklyn/entity/group/QuarantineGroupImpl.java @@ -0,0 +1,70 @@ +package brooklyn.entity.group; + +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.entity.Entity; +import brooklyn.entity.basic.AbstractEntity; +import brooklyn.entity.basic.AbstractGroupImpl; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.effector.Effectors; +import brooklyn.entity.trait.Startable; +import brooklyn.management.Task; +import brooklyn.util.exceptions.Exceptions; +import brooklyn.util.task.DynamicTasks; +import brooklyn.util.task.Tasks; +import brooklyn.util.text.Strings; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; + +public class QuarantineGroupImpl extends AbstractGroupImpl implements QuarantineGroup { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractEntity.class); + + @Override + public void expungeMembers(boolean stopFirst) { + Set members = ImmutableSet.copyOf(getMembers()); + if (stopFirst) { + Map> tasks = Maps.newLinkedHashMap(); + for (Entity member : members) { + if (member instanceof Startable) { + Task task = Effectors.invocation(member, Startable.STOP, ImmutableMap.of()).asTask(); + tasks.put(member, task); + } + } + DynamicTasks.queueIfPossible(Tasks.parallel("stopping "+tasks.size()+" member"+Strings.s(tasks.size())+" (parallel)", tasks.values())).orSubmitAsync(this); + waitForTasksOnExpungeMembers(tasks); + } + for (Entity member : members) { + Entities.unmanage(member); + } + } + + // TODO Quite like DynamicClusterImpl.waitForTasksOnEntityStart + protected Map waitForTasksOnExpungeMembers(Map> tasks) { + // TODO Could have CompoundException, rather than propagating first + Map errors = Maps.newLinkedHashMap(); + + for (Map.Entry> entry : tasks.entrySet()) { + Entity member = entry.getKey(); + Task task = entry.getValue(); + try { + task.get(); + } catch (InterruptedException e) { + throw Exceptions.propagate(e); + } catch (Throwable t) { + Throwable interesting = Exceptions.getFirstInteresting(t); + LOG.error("Quarantine group "+this+" failed to stop quarantined entity "+member+" (removing): "+interesting, interesting); + LOG.debug("Trace for: Quarantine group "+this+" failed to stop quarantined entity "+member+" (removing): "+t, t); + // previously we unwrapped but now there is no need I think + errors.put(member, t); + } + } + return errors; + } +} http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/148b5037/core/src/test/java/brooklyn/entity/group/DynamicClusterTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/group/DynamicClusterTest.java b/core/src/test/java/brooklyn/entity/group/DynamicClusterTest.java index a59079d..a13da15 100644 --- a/core/src/test/java/brooklyn/entity/group/DynamicClusterTest.java +++ b/core/src/test/java/brooklyn/entity/group/DynamicClusterTest.java @@ -407,6 +407,19 @@ public class DynamicClusterTest { } @Test + public void testQuarantineGroupOfCorrectType() throws Exception { + DynamicCluster cluster = app.createAndManageChild(EntitySpec.create(DynamicCluster.class) + .configure("quarantineFailedEntities", true) + .configure("initialSize", 0) + .configure(DynamicCluster.MEMBER_SPEC, EntitySpec.create(TestEntity.class))); + + cluster.start(ImmutableList.of(loc)); + + QuarantineGroup quarantineGroup = cluster.getAttribute(DynamicCluster.QUARANTINE_GROUP); + quarantineGroup.expungeMembers(true); // sanity check by calling something on it + } + + @Test public void testCanQuarantineFailedEntities() throws Exception { final int failNum = 2; final AtomicInteger counter = new AtomicInteger(0); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/148b5037/core/src/test/java/brooklyn/entity/group/QuarantineGroupTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/brooklyn/entity/group/QuarantineGroupTest.java b/core/src/test/java/brooklyn/entity/group/QuarantineGroupTest.java new file mode 100644 index 0000000..94e84d2 --- /dev/null +++ b/core/src/test/java/brooklyn/entity/group/QuarantineGroupTest.java @@ -0,0 +1,74 @@ +package brooklyn.entity.group; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; + +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import brooklyn.entity.basic.ApplicationBuilder; +import brooklyn.entity.basic.Entities; +import brooklyn.entity.proxying.EntitySpec; +import brooklyn.location.LocationSpec; +import brooklyn.location.basic.SimulatedLocation; +import brooklyn.test.entity.TestApplication; +import brooklyn.test.entity.TestEntity; + +import com.google.common.collect.ImmutableList; + + +public class QuarantineGroupTest { + + private static final int TIMEOUT_MS = 2000; + + SimulatedLocation loc; + TestApplication app; + private TestEntity e1; + private TestEntity e2; + private QuarantineGroup group; + + @BeforeMethod(alwaysRun=true) + public void setUp() throws Exception { + app = ApplicationBuilder.newManagedApp(TestApplication.class); + app.getManagementContext().getLocationManager().createLocation(LocationSpec.create(SimulatedLocation.class)); + e1 = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + e2 = app.createAndManageChild(EntitySpec.create(TestEntity.class)); + group = app.createAndManageChild(EntitySpec.create(QuarantineGroup.class)); + } + + @AfterMethod(alwaysRun = true) + public void tearDown(){ + if (app != null) Entities.destroyAll(app.getManagementContext()); + } + + @Test + public void testExpungeMembersWhenNone() throws Exception { + group.expungeMembers(true); + group.expungeMembers(false); + } + + @Test + public void testExpungeMembersWithoutStop() throws Exception { + group.addMember(e1); + group.addMember(e2); + group.expungeMembers(false); + + assertFalse(Entities.isManaged(e1)); + assertFalse(Entities.isManaged(e2)); + assertEquals(e1.getCallHistory(), ImmutableList.of()); + assertEquals(e2.getCallHistory(), ImmutableList.of()); + } + + @Test + public void testExpungeMembersWithStop() throws Exception { + group.addMember(e1); + group.addMember(e2); + group.expungeMembers(true); + + assertFalse(Entities.isManaged(e1)); + assertFalse(Entities.isManaged(e2)); + assertEquals(e1.getCallHistory(), ImmutableList.of("stop")); + assertEquals(e2.getCallHistory(), ImmutableList.of("stop")); + } +}