http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeLiveTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeLiveTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeLiveTest.java
new file mode 100644
index 0000000..c098d13
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/CassandraNodeLiveTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.entity.nosql.cassandra;
+
+import java.util.Map;
+
+import org.apache.brooklyn.entity.nosql.cassandra.CassandraNode;
+import 
org.apache.brooklyn.entity.nosql.cassandra.AstyanaxSupport.AstyanaxSample;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.test.EntityTestUtils;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.text.Strings;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Cassandra live tests.
+ *
+ * Test the operation of the {@link CassandraNode} class using the jclouds 
{@code rackspace-cloudservers-uk}
+ * and {@code aws-ec2} providers, with different OS images. The tests use the 
{@link AstyanaxSupport#astyanaxTest()} method
+ * to exercise the node, and will need to have {@code 
brooklyn.jclouds.provider.identity} and {@code .credential}
+ * set, usually in the {@code .brooklyn/bropoklyn.properties} file.
+ */
+public class CassandraNodeLiveTest extends AbstractCassandraNodeTest {
+
+    private static final Logger log = 
LoggerFactory.getLogger(CassandraNodeLiveTest.class);
+
+    @DataProvider(name = "virtualMachineData")
+    public Object[][] provideVirtualMachineData() {
+        return new Object[][] { // ImageId, Provider, Region, Description (for 
logging)
+            new Object[] { "eu-west-1/ami-0307d674", "aws-ec2", "eu-west-1", 
"Ubuntu Server 14.04 LTS (HVM), SSD Volume Type" },
+            new Object[] { "LON/f9b690bf-88eb-43c2-99cf-391f2558732e", 
"rackspace-cloudservers-uk", "", "Ubuntu 12.04 LTS (Precise Pangolin)" }, 
+            new Object[] { "LON/a84b1592-6817-42da-a57c-3c13f3cfc1da", 
"rackspace-cloudservers-uk", "", "CentOS 6.5 (PVHVM)" }, 
+        };
+    }
+
+    @Test(groups = "Live", dataProvider = "virtualMachineData")
+    protected void testOperatingSystemProvider(String imageId, String 
provider, String region, String description) throws Exception {
+        log.info("Testing Cassandra on {}{} using {} ({})", new Object[] { 
provider, Strings.isNonEmpty(region) ? ":" + region : "", description, imageId 
});
+
+        Map<String, String> properties = MutableMap.of("imageId", imageId);
+        testLocation = app.getManagementContext().getLocationRegistry()
+                .resolve(provider + (Strings.isNonEmpty(region) ? ":" + region 
: ""), properties);
+
+        cassandra = 
app.createAndManageChild(EntitySpec.create(CassandraNode.class)
+                .configure("thriftPort", "9876+")
+                .configure("clusterName", "TestCluster"));
+        app.start(ImmutableList.of(testLocation));
+        EntityTestUtils.assertAttributeEqualsEventually(cassandra, 
CassandraNode.SERVICE_UP, true);
+
+        AstyanaxSample astyanax = new AstyanaxSample(cassandra);
+        astyanax.astyanaxTest();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/NonNegTokenGeneratorTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/NonNegTokenGeneratorTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/NonNegTokenGeneratorTest.java
new file mode 100644
index 0000000..281ae6d
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/NonNegTokenGeneratorTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.entity.nosql.cassandra;
+
+import static org.testng.Assert.assertEquals;
+
+import java.math.BigInteger;
+import java.util.List;
+
+import 
org.apache.brooklyn.entity.nosql.cassandra.TokenGenerators.AbstractTokenGenerator;
+import 
org.apache.brooklyn.entity.nosql.cassandra.TokenGenerators.NonNeg127TokenGenerator;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+public class NonNegTokenGeneratorTest {
+
+    public static final BigInteger C4_1 = new 
BigInteger("42535295865117307932921825928971026432");
+    public static final BigInteger C4_2 = new 
BigInteger("85070591730234615865843651857942052864");
+    public static final BigInteger C4_3 = new 
BigInteger("127605887595351923798765477786913079296");
+
+    // TODO Expect this behaviour to change when we better support dynamically 
growing/shrinking.
+    // In particular, the expected behaviour for 
testReturnsNullWhenClusterSizeUnknown 
+    // and testReturnsNullWhenGrowingClusterUnknownAmount will change.
+
+    private AbstractTokenGenerator generator;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        generator = new NonNeg127TokenGenerator();
+    }
+    
+    @Test
+    public void testGetTokenForReplacementNode() {
+        assertEquals(generator.getTokenForReplacementNode(BigInteger.ONE), 
BigInteger.ZERO);
+        assertEquals(generator.getTokenForReplacementNode(BigInteger.ZERO), 
generator.max());
+        assertEquals(generator.getTokenForReplacementNode(generator.max()), 
generator.max().subtract(BigInteger.ONE));
+    }
+    
+    @Test
+    public void testGeneratesInitialTokens() throws Exception {
+        List<BigInteger> tokens = Lists.newArrayList();
+        generator.growingCluster(4);
+        for (int i = 0; i < 4; i++) {
+            tokens.add(generator.newToken());
+        }
+        
+        assertEquals(tokens, ImmutableList.of(
+                BigInteger.ZERO, 
+                C4_1,
+                C4_2,
+                C4_3));
+    }
+    
+    // Expect behaviour to be changed to better choose tokens for growing 
clusters 
+    // (but eg need to take into account how busy each node is!)
+    @Test
+    public void testGeneratesTokensForGrowingCluster() throws Exception {
+        List<BigInteger> tokens = Lists.newArrayList();
+        generator.growingCluster(4);
+        for (int i = 0; i < 4; i++) {
+            tokens.add(generator.newToken());
+        }
+        generator.growingCluster(1);
+        assertEquals(generator.newToken(), 
C4_3.add(generator.max().add(BigInteger.ONE)).divide(BigInteger.valueOf(2)));
+        generator.growingCluster(2);
+        assertEquals(generator.newToken(), C4_1.divide(BigInteger.valueOf(2)));
+        assertEquals(generator.newToken(), 
C4_2.add(C4_1).divide(BigInteger.valueOf(2)));
+    }
+    
+    @Test
+    public void testGeneratesTokensForGrowingClusterWhenInitialSizeIsOne() 
throws Exception {
+        // initial size 1 has to do a special "average with ourself by half 
phase shift" computation
+        List<BigInteger> tokens = Lists.newArrayList();
+        generator.growingCluster(1);
+        tokens.add(generator.newToken());
+        
+        generator.growingCluster(1);
+        assertEquals(generator.newToken(), C4_2);
+        generator.growingCluster(2);
+        assertEquals(generator.newToken(), C4_3);
+        assertEquals(generator.newToken(), C4_1);
+    }
+    
+    @Test
+    public void testReturnsNullWhenClusterSizeUnknown() throws Exception {
+        assertEquals(generator.newToken(), null);
+    }
+    
+    @Test
+    public void testReturnsNullWhenGrowingClusterUnknownAmount() throws 
Exception {
+        generator.growingCluster(4);
+        for (int i = 0; i < 4; i++) {
+            generator.newToken();
+        }
+        assertEquals(generator.newToken(), null);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/PosNegTokenGeneratorTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/PosNegTokenGeneratorTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/PosNegTokenGeneratorTest.java
new file mode 100644
index 0000000..e353f41
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/cassandra/PosNegTokenGeneratorTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.entity.nosql.cassandra;
+
+import static org.testng.Assert.assertEquals;
+
+import java.math.BigInteger;
+
+import 
org.apache.brooklyn.entity.nosql.cassandra.TokenGenerators.AbstractTokenGenerator;
+import 
org.apache.brooklyn.entity.nosql.cassandra.TokenGenerators.PosNeg63TokenGenerator;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class PosNegTokenGeneratorTest {
+
+    // TODO Expect this behaviour to change when we better support dynamically 
growing/shrinking.
+    // In particular, the expected behaviour for 
testReturnsNullWhenClusterSizeUnknown 
+    // and testReturnsNullWhenGrowingClusterUnknownAmount will change.
+
+    private AbstractTokenGenerator generator;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        generator = new PosNeg63TokenGenerator();
+    }
+    
+    @Test
+    public void testGetTokenForReplacementNode() {
+        assertEquals(generator.getTokenForReplacementNode(BigInteger.ONE), 
BigInteger.ZERO);
+        assertEquals(generator.getTokenForReplacementNode(BigInteger.ZERO), 
BigInteger.ONE.negate());
+        assertEquals(generator.getTokenForReplacementNode(generator.min()), 
generator.max());
+        assertEquals(generator.getTokenForReplacementNode(generator.max()), 
generator.max().subtract(BigInteger.ONE));
+    }
+    
+    @Test
+    public void testGeneratesInitialTokens() throws Exception {
+        generator.growingCluster(4);
+        assertEquals(generator.newToken(), generator.min());
+        assertEquals(generator.newToken(), 
generator.min().add(generator.range().divide(BigInteger.valueOf(4))));
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
new file mode 100644
index 0000000..de1d49a
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.entity.nosql.couchbase;
+
+import org.apache.brooklyn.entity.nosql.couchbase.CouchbaseNodeSshDriver;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.basic.Entities;
+import brooklyn.location.basic.BasicOsDetails;
+import brooklyn.location.basic.BasicOsDetails.OsArchs;
+import brooklyn.management.internal.LocalManagementContext;
+import brooklyn.test.entity.LocalManagementContextForTests;
+
+public class CouchbaseOfflineTest {
+
+    private LocalManagementContext mgmt;
+
+    @BeforeMethod
+    public void setUp() {
+        mgmt = LocalManagementContextForTests.newInstance();
+    }
+    
+    @AfterMethod
+    public void tearDown() {
+        Entities.destroyAll(mgmt);
+    }
+    
+    @Test
+    public void testResolvingDownloadLinks() {
+        checkOsTag("linux", OsArchs.I386, "unknown", true, "centos6.x86.rpm");
+        checkOsTag("linux", OsArchs.I386, "unknown", false, "x86.rpm");
+        checkOsTag("rhel", OsArchs.X_86_64, "6", true, "centos6.x86_64.rpm");
+        checkOsTag("Ubuntu 14", OsArchs.X_86_64, "14.04", true, 
"ubuntu12.04_amd64.deb");
+        checkOsTag("Ubuntu 14", OsArchs.X_86_64, "14.04", false, "x86_64.deb");
+        checkOsTag("Debian 7up", OsArchs.I386, "7ish", true, 
"debian7_x86.deb");
+        Assert.assertEquals(new 
CouchbaseNodeSshDriver.DownloadLinkSegmentComputer(null, true, 
"test").getOsTag(), "centos6.x86_64.rpm");
+        Assert.assertEquals(new 
CouchbaseNodeSshDriver.DownloadLinkSegmentComputer(null, false, 
"test").getOsTag(), "x86_64.rpm");
+    }
+
+    protected void checkOsTag(String os, String arch, String version, boolean 
isV30, String expectedTag) {
+        Assert.assertEquals(new 
CouchbaseNodeSshDriver.DownloadLinkSegmentComputer(new BasicOsDetails(os, arch, 
version), isV30, "test").getOsTag(), expectedTag);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseSyncGatewayEc2LiveTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseSyncGatewayEc2LiveTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseSyncGatewayEc2LiveTest.java
new file mode 100644
index 0000000..89297be
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchbase/CouchbaseSyncGatewayEc2LiveTest.java
@@ -0,0 +1,140 @@
+/*
+ * 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.entity.nosql.couchbase;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.brooklyn.entity.nosql.couchbase.CouchbaseCluster;
+import org.apache.brooklyn.entity.nosql.couchbase.CouchbaseNode;
+import org.apache.brooklyn.entity.nosql.couchbase.CouchbaseSyncGateway;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.AbstractEc2LiveTest;
+import brooklyn.entity.group.DynamicCluster;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.trait.Startable;
+import brooklyn.location.Location;
+import brooklyn.test.EntityTestUtils;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class CouchbaseSyncGatewayEc2LiveTest extends AbstractEc2LiveTest {
+
+    @Override
+    protected void doTest(Location loc) throws Exception {
+        CouchbaseCluster cluster = 
app.createAndManageChild(EntitySpec.create(CouchbaseCluster.class)
+            .configure(CouchbaseNode.COUCHBASE_ADMIN_USERNAME, "Administrator")
+            .configure(CouchbaseNode.COUCHBASE_ADMIN_PASSWORD, "Password")
+            .configure(DynamicCluster.INITIAL_SIZE, 3)
+            .configure(CouchbaseCluster.CREATE_BUCKETS, 
(List<Map<String,Object>>)ImmutableList.of(
+                (Map<String,Object>)ImmutableMap.<String, Object>of(
+                    "bucket", "default",
+                    "bucket-ramsize", 100,
+                    "bucket-type", "couchbase",
+                    "bucket-port", 11211
+                ),
+                (Map<String,Object>)ImmutableMap.<String, Object>of(
+                    "bucket", "my_bucket",
+                    "bucket-ramsize", 100,
+                    "bucket-type", "couchbase",
+                    "bucket-port", 11223
+                ),
+                (Map<String,Object>)ImmutableMap.<String, Object>of(
+                    "bucket", "another",
+                    "bucket-ramsize", 100,
+                    "bucket-type", "couchbase",
+                    "bucket-port", 11224
+                ))
+            )
+        );
+        CouchbaseSyncGateway gateway = 
app.createAndManageChild(EntitySpec.create(CouchbaseSyncGateway.class)
+            .configure(CouchbaseSyncGateway.COUCHBASE_SERVER, cluster)
+            .configure(CouchbaseSyncGateway.COUCHBASE_SERVER_BUCKET, 
"my_bucket")
+        );
+        
+        app.start(ImmutableList.of(loc));
+        
+        EntityTestUtils.assertAttributeEqualsEventually(gateway, 
Startable.SERVICE_UP, true);
+    }
+    
+    
+    // Supported operating systems
+    @Test(groups = {"Live"})
+    @Override
+    public void test_Ubuntu_12_0() throws Exception {
+        super.test_Ubuntu_12_0();
+    }
+    
+    @Test(groups = {"Live"})
+    @Override
+    public void test_Red_Hat_Enterprise_Linux_6() throws Exception {
+        super.test_Red_Hat_Enterprise_Linux_6();
+    }
+    
+    @Test(groups = {"Live"})
+    @Override
+    public void test_CentOS_6_3() throws Exception {
+        super.test_CentOS_6_3();
+    }
+    
+    // Unsupported operating systems
+    
+    @Test(groups = {"Live"})
+    @Override
+    public void test_CentOS_5() throws Exception {
+        // Unsupported
+        // error: Failed dependencies:
+        //     libc.so.6(GLIBC_2.7)(64bit) is needed by 
couchbase-server-2.5.1-1083.x86_64
+        //        libcrypto.so.10()(64bit) is needed by 
couchbase-server-2.5.1-1083.x86_64
+        //        libreadline.so.6()(64bit) is needed by 
couchbase-server-2.5.1-1083.x86_64
+        //        libssl.so.10()(64bit) is needed by 
couchbase-server-2.5.1-1083.x86_64
+        //        libstdc++.so.6(GLIBCXX_3.4.10)(64bit) is needed by 
couchbase-server-2.5.1-1083.x86_64
+        //        libstdc++.so.6(GLIBCXX_3.4.11)(64bit) is needed by 
couchbase-server-2.5.1-1083.x86_64
+        //        libstdc++.so.6(GLIBCXX_3.4.9)(64bit) is needed by 
couchbase-server-2.5.1-1083.x86_64
+        //        libtinfo.so.5()(64bit) is needed by 
couchbase-server-2.5.1-1083.x86_64
+        //        openssl >= 1.0.0 is needed by 
couchbase-server-2.5.1-1083.x86_64
+        //        rpmlib(FileDigests) <= 4.6.0-1 is needed by 
couchbase-server-2.5.1-1083.x86_64
+        //        rpmlib(PayloadIsXz) <= 5.2-1 is needed by 
couchbase-server-2.5.1-1083.x86_64
+    }
+    
+    @Test(groups = {"Live"})
+    @Override
+    public void test_Debian_6() throws Exception {
+        // Unsupported
+    }
+    
+    @Test(groups = {"Live"})
+    @Override
+    public void test_Debian_7_2() throws Exception {
+        // Unsupported
+    }
+    
+    @Test(groups = {"Live"})
+    @Override
+    public void test_Ubuntu_10_0() throws Exception {
+        // Unsupported
+        // Installing cannot proceed since the package 'libssl1*' is missing. 
+        // Please install libssl1* and try again. 
+        //    $sudo apt-get install libssl1*
+        //
+        // Installing libssl1* doesn't fix the issue
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/AbstractCouchDBNodeTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/AbstractCouchDBNodeTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/AbstractCouchDBNodeTest.java
new file mode 100644
index 0000000..d7bfa6b
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/AbstractCouchDBNodeTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.entity.nosql.couchdb;
+
+import org.apache.brooklyn.entity.nosql.couchdb.CouchDBNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.Entities;
+import brooklyn.location.Location;
+import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import brooklyn.test.entity.TestApplication;
+import brooklyn.util.internal.TimeExtras;
+
+/**
+ * CouchDB test framework for integration and live tests.
+ */
+public class AbstractCouchDBNodeTest {
+
+    private static final Logger log = 
LoggerFactory.getLogger(AbstractCouchDBNodeTest.class);
+
+    static {
+        TimeExtras.init();
+    }
+
+    protected TestApplication app;
+    protected Location testLocation;
+    protected CouchDBNode couchdb;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setup() throws Exception {
+        app = ApplicationBuilder.newManagedApp(TestApplication.class);
+        testLocation = new LocalhostMachineProvisioningLocation();
+        // testLocation = 
app.managementContext.locationRegistry.resolve("named:test");
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void shutdown() {
+        Entities.destroyAll(app.getManagementContext());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBClusterLiveTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBClusterLiveTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBClusterLiveTest.java
new file mode 100644
index 0000000..0fd7796
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBClusterLiveTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.entity.nosql.couchdb;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.entity.nosql.couchdb.CouchDBCluster;
+import org.apache.brooklyn.entity.nosql.couchdb.CouchDBNode;
+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.entity.trait.Startable;
+import brooklyn.location.Location;
+import brooklyn.test.EntityTestUtils;
+import brooklyn.test.entity.TestApplication;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+/**
+ * A live test of the {@link CouchDBCluster} entity.
+ *
+ * Tests that a two node cluster can be started on Amazon EC2 and data written 
on one {@link CouchDBNode}
+ * can be read from another, using the Astyanax API.
+ */
+public class CouchDBClusterLiveTest {
+
+    // private String provider = "rackspace-cloudservers-uk";
+    private String provider = "aws-ec2:eu-west-1";
+
+    protected TestApplication app;
+    protected Location testLocation;
+    protected CouchDBCluster cluster;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setup() {
+        app = ApplicationBuilder.newManagedApp(TestApplication.class);
+        testLocation = 
app.getManagementContext().getLocationRegistry().resolve(provider);
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void shutdown() {
+        Entities.destroyAll(app.getManagementContext());
+    }
+
+    /**
+     * Test that a two node cluster starts up and allows access via the 
Astyanax API through both nodes.
+     */
+    @Test(groups = "Live")
+    public void canStartupAndShutdown() throws Exception {
+        cluster = 
app.createAndManageChild(EntitySpec.create(CouchDBCluster.class)
+                .configure("initialSize", 2)
+                .configure("clusterName", "AmazonCluster"));
+        assertEquals(cluster.getCurrentSize().intValue(), 0);
+
+        app.start(ImmutableList.of(testLocation));
+
+        EntityTestUtils.assertAttributeEqualsEventually(cluster, 
CouchDBCluster.GROUP_SIZE, 2);
+        Entities.dumpInfo(app);
+
+        CouchDBNode first = (CouchDBNode) Iterables.get(cluster.getMembers(), 
0);
+        CouchDBNode second = (CouchDBNode) Iterables.get(cluster.getMembers(), 
1);
+
+        EntityTestUtils.assertAttributeEqualsEventually(first, 
Startable.SERVICE_UP, true);
+        EntityTestUtils.assertAttributeEqualsEventually(second, 
Startable.SERVICE_UP, true);
+
+        JcouchdbSupport jcouchdbFirst = new JcouchdbSupport(first);
+        JcouchdbSupport jcouchdbSecond = new JcouchdbSupport(second);
+        jcouchdbFirst.jcouchdbTest();
+        jcouchdbSecond.jcouchdbTest();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeEc2LiveTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeEc2LiveTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeEc2LiveTest.java
new file mode 100644
index 0000000..1838354
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeEc2LiveTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.entity.nosql.couchdb;
+
+import org.apache.brooklyn.entity.nosql.couchdb.CouchDBNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import brooklyn.entity.AbstractEc2LiveTest;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.trait.Startable;
+import brooklyn.location.Location;
+import brooklyn.test.EntityTestUtils;
+
+import com.google.common.collect.ImmutableList;
+
+public class CouchDBNodeEc2LiveTest extends AbstractEc2LiveTest {
+
+    private static final Logger log = 
LoggerFactory.getLogger(CouchDBNodeEc2LiveTest.class);
+
+    @Override
+    protected void doTest(Location loc) throws Exception {
+        log.info("Testing Cassandra on {}", loc);
+
+        CouchDBNode couchdb = 
app.createAndManageChild(EntitySpec.create(CouchDBNode.class)
+                .configure("httpPort", "8000+"));
+        app.start(ImmutableList.of(loc));
+
+        EntityTestUtils.assertAttributeEqualsEventually(couchdb, 
Startable.SERVICE_UP, true);
+
+        JcouchdbSupport jcouchdb = new JcouchdbSupport(couchdb);
+        jcouchdb.jcouchdbTest();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeIntegrationTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeIntegrationTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeIntegrationTest.java
new file mode 100644
index 0000000..0f8cbd2
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeIntegrationTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.entity.nosql.couchdb;
+
+import org.apache.brooklyn.entity.nosql.couchdb.CouchDBNode;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.trait.Startable;
+import brooklyn.test.EntityTestUtils;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * CouchDB integration tests.
+ *
+ * Test the operation of the {@link CouchDBNode} class.
+ */
+public class CouchDBNodeIntegrationTest extends AbstractCouchDBNodeTest {
+
+    /**
+     * Test that a node starts and sets SERVICE_UP correctly.
+     */
+    @Test(groups = {"Integration", "WIP"})
+    public void canStartupAndShutdown() {
+        couchdb = app.createAndManageChild(EntitySpec.create(CouchDBNode.class)
+                .configure("httpPort", "8000+"));
+        app.start(ImmutableList.of(testLocation));
+
+        EntityTestUtils.assertAttributeEqualsEventually(couchdb, 
Startable.SERVICE_UP, true);
+
+        couchdb.stop();
+
+        EntityTestUtils.assertAttributeEquals(couchdb, Startable.SERVICE_UP, 
false);
+    }
+
+    /**
+     * Test that a node can be used with jcouchdb client.
+     */
+    @Test(groups = {"Integration", "WIP"})
+    public void testConnection() throws Exception {
+        couchdb = app.createAndManageChild(EntitySpec.create(CouchDBNode.class)
+                .configure("httpPort", "8000+"));
+        app.start(ImmutableList.of(testLocation));
+
+        EntityTestUtils.assertAttributeEqualsEventually(couchdb, 
Startable.SERVICE_UP, true);
+
+        JcouchdbSupport jcouchdb = new JcouchdbSupport(couchdb);
+        jcouchdb.jcouchdbTest();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeLiveTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeLiveTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeLiveTest.java
new file mode 100644
index 0000000..05ce053
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/CouchDBNodeLiveTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.entity.nosql.couchdb;
+
+import java.util.Map;
+
+import org.apache.brooklyn.entity.nosql.couchdb.CouchDBNode;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.trait.Startable;
+import brooklyn.test.EntityTestUtils;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.text.Strings;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * CouchDB live tests.
+ *
+ * Test the operation of the {@link CouchDBNode} class using the jclouds 
{@code rackspace-cloudservers-uk}
+ * and {@code aws-ec2} providers, with different OS images. The tests use the 
{@link JcouchdbSupport#jcouchdbTest(CouchDBNode)} method
+ * to exercise the node, and will need to have {@code 
brooklyn.jclouds.provider.identity} and {@code .credential}
+ * set, usually in the {@code .brooklyn/brooklyn.properties} file.
+ */
+public class CouchDBNodeLiveTest extends AbstractCouchDBNodeTest {
+
+    private static final Logger log = 
LoggerFactory.getLogger(CouchDBNodeLiveTest.class);
+
+    @DataProvider(name = "virtualMachineData")
+    public Object[][] provideVirtualMachineData() {
+        return new Object[][] { // ImageId, Provider, Region, Description (for 
logging)
+            new Object[] { "eu-west-1/ami-0307d674", "aws-ec2", "eu-west-1", 
"Ubuntu Server 14.04 LTS (HVM), SSD Volume Type" },
+            new Object[] { "LON/f9b690bf-88eb-43c2-99cf-391f2558732e", 
"rackspace-cloudservers-uk", "", "Ubuntu 12.04 LTS (Precise Pangolin)" }, 
+            new Object[] { "LON/a84b1592-6817-42da-a57c-3c13f3cfc1da", 
"rackspace-cloudservers-uk", "", "CentOS 6.5 (PVHVM)" }, 
+        };
+    }
+
+    @Test(groups = "Live", dataProvider = "virtualMachineData")
+    protected void testOperatingSystemProvider(String imageId, String 
provider, String region, String description) throws Exception {
+        log.info("Testing CouchDB on {}{} using {} ({})", new Object[] { 
provider, Strings.isNonEmpty(region) ? ":" + region : "", description, imageId 
});
+
+        Map<String, String> properties = MutableMap.of("imageId", imageId);
+        testLocation = app.getManagementContext().getLocationRegistry()
+                .resolve(provider + (Strings.isNonEmpty(region) ? ":" + region 
: ""), properties);
+
+        couchdb = app.createAndManageChild(EntitySpec.create(CouchDBNode.class)
+                .configure("httpPort", "12345+")
+                .configure("clusterName", "TestCluster"));
+        app.start(ImmutableList.of(testLocation));
+        EntityTestUtils.assertAttributeEqualsEventually(couchdb, 
Startable.SERVICE_UP, true);
+
+        JcouchdbSupport jcouchdb = new JcouchdbSupport(couchdb);
+        jcouchdb.jcouchdbTest();
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/JcouchdbSupport.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/JcouchdbSupport.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/JcouchdbSupport.java
new file mode 100644
index 0000000..d1c7cf6
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/couchdb/JcouchdbSupport.java
@@ -0,0 +1,78 @@
+/*
+ * 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.entity.nosql.couchdb;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.brooklyn.entity.nosql.couchdb.CouchDBNode;
+import org.jcouchdb.db.Database;
+import org.jcouchdb.db.Server;
+import org.jcouchdb.db.ServerImpl;
+
+import brooklyn.entity.basic.Attributes;
+
+/**
+ * CouchDB test framework for integration and live tests, using jcouchdb API.
+ */
+public class JcouchdbSupport {
+
+    private CouchDBNode node;
+
+    public JcouchdbSupport(CouchDBNode node) {
+        this.node = node;
+    }
+
+    /**
+     * Exercise the {@link CouchDBNode} using the jcouchdb API.
+     */
+    public void jcouchdbTest() throws Exception {
+        Server server = new ServerImpl(node.getAttribute(Attributes.HOSTNAME), 
node.getHttpPort());
+        assertTrue(server.createDatabase("brooklyn"));
+
+        Database db = new Database(node.getAttribute(Attributes.HOSTNAME), 
node.getHttpPort(), "brooklyn");
+
+        // create a hash map document with two fields
+        Map<String,String> doc = new HashMap<String, String>();
+        doc.put("first", "one");
+        doc.put("second", "two");
+
+        // create the document in couchdb
+        int before = db.listDocuments(null, null).getTotalRows();
+        db.createDocument(doc);
+        int after = db.listDocuments(null, null).getTotalRows();
+
+        assertEquals(before + 1, after);
+    }
+
+    /**
+     * Write to a {@link CouchDBNode} using the jcouchdb API.
+     */
+    protected void writeData() throws Exception {
+    }
+
+    /**
+     * Read from a {@link CouchDBNode} using the jcouchdb API.
+     */
+    protected void readData() throws Exception {
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterIntegrationTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterIntegrationTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterIntegrationTest.java
new file mode 100644
index 0000000..9f794a1
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchClusterIntegrationTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.entity.nosql.elasticsearch;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.brooklyn.entity.nosql.elasticsearch.ElasticSearchCluster;
+import org.apache.brooklyn.entity.nosql.elasticsearch.ElasticSearchNode;
+import org.apache.http.client.methods.HttpGet;
+import org.bouncycastle.util.Strings;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.BrooklynAppLiveTestSupport;
+import brooklyn.entity.Entity;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.group.DynamicCluster;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.trait.Startable;
+import brooklyn.event.feed.http.HttpValueFunctions;
+import brooklyn.location.Location;
+import brooklyn.test.Asserts;
+import brooklyn.test.EntityTestUtils;
+import brooklyn.util.http.HttpTool;
+import brooklyn.util.http.HttpToolResponse;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class ElasticSearchClusterIntegrationTest extends 
BrooklynAppLiveTestSupport {
+
+    // FIXME Exception in thread "main" 
java.lang.UnsupportedClassVersionError: 
org/elasticsearch/bootstrap/Elasticsearch : Unsupported major.minor version 51.0
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(ElasticSearchClusterIntegrationTest.class);
+
+    protected Location testLocation;
+    protected ElasticSearchCluster elasticSearchCluster;
+
+    @BeforeMethod(alwaysRun = true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        testLocation = app.newLocalhostProvisioningLocation();
+    }
+
+    @Test(groups = {"Integration"})
+    public void testStartupAndShutdown() {
+        elasticSearchCluster = 
app.createAndManageChild(EntitySpec.create(ElasticSearchCluster.class)
+                .configure(DynamicCluster.INITIAL_SIZE, 3));
+        app.start(ImmutableList.of(testLocation));
+        
+        EntityTestUtils.assertAttributeEqualsEventually(elasticSearchCluster, 
Startable.SERVICE_UP, true);
+        
+        elasticSearchCluster.stop();
+        
+        EntityTestUtils.assertAttributeEqualsEventually(elasticSearchCluster, 
Startable.SERVICE_UP, false);
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testPutAndGet() throws URISyntaxException {
+        elasticSearchCluster = 
app.createAndManageChild(EntitySpec.create(ElasticSearchCluster.class)
+                .configure(DynamicCluster.INITIAL_SIZE, 3));
+        app.start(ImmutableList.of(testLocation));
+        
+        EntityTestUtils.assertAttributeEqualsEventually(elasticSearchCluster, 
Startable.SERVICE_UP, true);
+        assertEquals(elasticSearchCluster.getMembers().size(), 3);
+        assertEquals(clusterDocumentCount(), 0);
+        
+        ElasticSearchNode anyNode = 
(ElasticSearchNode)elasticSearchCluster.getMembers().iterator().next();
+        
+        String document = "{\"foo\" : \"bar\",\"baz\" : \"quux\"}";
+        
+        String putBaseUri = "http://"; + 
anyNode.getAttribute(Attributes.HOSTNAME) + ":" + 
anyNode.getAttribute(Attributes.HTTP_PORT);
+        
+        HttpToolResponse putResponse = HttpTool.httpPut(
+                HttpTool.httpClientBuilder()
+                    .port(anyNode.getAttribute(Attributes.HTTP_PORT))
+                    .build(), 
+                new URI(putBaseUri + "/mydocuments/docs/1"), 
+                ImmutableMap.<String, String>of(), 
+                Strings.toByteArray(document)); 
+        assertEquals(putResponse.getResponseCode(), 201);
+        
+        for (Entity entity : elasticSearchCluster.getMembers()) {
+            ElasticSearchNode node = (ElasticSearchNode)entity;
+            String getBaseUri = "http://"; + 
node.getAttribute(Attributes.HOSTNAME) + ":" + 
node.getAttribute(Attributes.HTTP_PORT);
+            HttpToolResponse getResponse = HttpTool.execAndConsume(
+                    HttpTool.httpClientBuilder().build(),
+                    new HttpGet(getBaseUri + "/mydocuments/docs/1/_source"));
+            assertEquals(getResponse.getResponseCode(), 200);
+            assertEquals(HttpValueFunctions.jsonContents("foo", 
String.class).apply(getResponse), "bar");
+        }
+        Asserts.succeedsEventually(new Runnable() {
+            public void run() {
+                int count = clusterDocumentCount();
+                assertTrue(count >= 1, "count="+count);
+                LOG.debug("Document count is {}", count);
+            }});
+    }
+    
+    private int clusterDocumentCount() {
+        int result = 0;
+        for (Entity entity : elasticSearchCluster.getMembers()) {
+            result += entity.getAttribute(ElasticSearchNode.DOCUMENT_COUNT);
+        }
+        return result;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeIntegrationTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeIntegrationTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeIntegrationTest.java
new file mode 100644
index 0000000..c0c140d
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/elasticsearch/ElasticSearchNodeIntegrationTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.entity.nosql.elasticsearch;
+
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.apache.brooklyn.entity.nosql.elasticsearch.ElasticSearchNode;
+import org.apache.http.client.methods.HttpGet;
+import org.bouncycastle.util.Strings;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.basic.ApplicationBuilder;
+import brooklyn.entity.basic.Attributes;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.trait.Startable;
+import brooklyn.event.feed.http.HttpValueFunctions;
+import brooklyn.location.Location;
+import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import brooklyn.test.EntityTestUtils;
+import brooklyn.test.entity.TestApplication;
+import brooklyn.util.http.HttpTool;
+import brooklyn.util.http.HttpToolResponse;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+public class ElasticSearchNodeIntegrationTest {
+    
+    protected TestApplication app;
+    protected Location testLocation;
+    protected ElasticSearchNode elasticSearchNode;
+
+    @BeforeMethod(alwaysRun = true)
+    public void setup() throws Exception {
+        app = ApplicationBuilder.newManagedApp(TestApplication.class);
+        testLocation = new LocalhostMachineProvisioningLocation();
+    }
+
+    @AfterMethod(alwaysRun = true)
+    public void shutdown() {
+        Entities.destroyAll(app.getManagementContext());
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testStartupAndShutdown() {
+        elasticSearchNode = 
app.createAndManageChild(EntitySpec.create(ElasticSearchNode.class));
+        app.start(ImmutableList.of(testLocation));
+        
+        EntityTestUtils.assertAttributeEqualsEventually(elasticSearchNode, 
Startable.SERVICE_UP, true);
+        
+        elasticSearchNode.stop();
+        
+        EntityTestUtils.assertAttributeEqualsEventually(elasticSearchNode, 
Startable.SERVICE_UP, false);
+    }
+    
+    @Test(groups = {"Integration"})
+    public void testDocumentCount() throws URISyntaxException {
+        elasticSearchNode = 
app.createAndManageChild(EntitySpec.create(ElasticSearchNode.class));
+        app.start(ImmutableList.of(testLocation));
+        
+        EntityTestUtils.assertAttributeEqualsEventually(elasticSearchNode, 
Startable.SERVICE_UP, true);
+        
+        EntityTestUtils.assertAttributeEquals(elasticSearchNode, 
ElasticSearchNode.DOCUMENT_COUNT, 0);
+        
+        String baseUri = "http://"; + 
elasticSearchNode.getAttribute(Attributes.HOSTNAME) + ":" + 
elasticSearchNode.getAttribute(Attributes.HTTP_PORT);
+        
+        HttpToolResponse pingResponse = HttpTool.execAndConsume(
+                HttpTool.httpClientBuilder().build(),
+                new HttpGet(baseUri));
+        assertEquals(pingResponse.getResponseCode(), 200);
+        
+        String document = "{\"foo\" : \"bar\",\"baz\" : \"quux\"}";
+        
+        HttpToolResponse putResponse = HttpTool.httpPut(
+                HttpTool.httpClientBuilder()
+                    .port(elasticSearchNode.getAttribute(Attributes.HTTP_PORT))
+                    .build(), 
+                new URI(baseUri + "/mydocuments/docs/1"), 
+                ImmutableMap.<String, String>of(), 
+                Strings.toByteArray(document)); 
+        assertEquals(putResponse.getResponseCode(), 201);
+        
+        HttpToolResponse getResponse = HttpTool.execAndConsume(
+                HttpTool.httpClientBuilder().build(),
+                new HttpGet(baseUri + "/mydocuments/docs/1/_source"));
+        assertEquals(getResponse.getResponseCode(), 200);
+        assertEquals(HttpValueFunctions.jsonContents("foo", 
String.class).apply(getResponse), "bar");
+        
+        EntityTestUtils.assertAttributeEqualsEventually(elasticSearchNode, 
ElasticSearchNode.DOCUMENT_COUNT, 1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBEc2LiveTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBEc2LiveTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBEc2LiveTest.java
new file mode 100644
index 0000000..413f0c9
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBEc2LiveTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.entity.nosql.mongodb;
+
+import brooklyn.entity.AbstractEc2LiveTest;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.Location;
+import brooklyn.test.EntityTestUtils;
+import com.google.common.collect.ImmutableList;
+import com.mongodb.DBObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.entity.nosql.mongodb.MongoDBServer;
+
+public class MongoDBEc2LiveTest extends AbstractEc2LiveTest {
+
+    @SuppressWarnings("unused")
+    private static final Logger LOG = 
LoggerFactory.getLogger(MongoDBEc2LiveTest.class);
+
+    @Override
+    protected void doTest(Location loc) throws Exception {
+        MongoDBServer entity = 
app.createAndManageChild(EntitySpec.create(MongoDBServer.class)
+                .configure("mongodbConfTemplateUrl", 
"classpath:///test-mongodb.conf"));
+        app.start(ImmutableList.of(loc));
+
+        EntityTestUtils.assertAttributeEqualsEventually(entity, 
MongoDBServer.SERVICE_UP, true);
+
+        String id = MongoDBTestHelper.insert(entity, "hello", "world!");
+        DBObject docOut = MongoDBTestHelper.getById(entity, id);
+        assertEquals(docOut.get("hello"), "world!");
+    }
+
+    @Test(enabled=false)
+    public void testDummy() {} // Convince TestNG IDE integration that this 
really does have test methods
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBIntegrationTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBIntegrationTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBIntegrationTest.java
new file mode 100644
index 0000000..eb9c362
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBIntegrationTest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.entity.nosql.mongodb;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+
+import org.apache.brooklyn.entity.nosql.mongodb.MongoDBServer;
+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.entity.trait.Startable;
+import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import brooklyn.test.EntityTestUtils;
+import brooklyn.test.entity.TestApplication;
+
+import com.google.common.collect.ImmutableList;
+import com.mongodb.DBObject;
+
+public class MongoDBIntegrationTest {
+
+    private TestApplication app;
+    private LocalhostMachineProvisioningLocation localhostProvisioningLocation;
+
+    @BeforeMethod(alwaysRun=true)
+    public void setUp() throws Exception {
+        localhostProvisioningLocation = new 
LocalhostMachineProvisioningLocation();
+        app = ApplicationBuilder.newManagedApp(TestApplication.class);
+    }
+
+    @AfterMethod(alwaysRun=true)
+    public void tearDown() throws Exception {
+        if (app != null) Entities.destroyAll(app.getManagementContext());
+    }
+
+    @Test(groups = "Integration")
+    public void testCanStartAndStop() throws Exception {
+        MongoDBServer entity = 
app.createAndManageChild(EntitySpec.create(MongoDBServer.class)
+                .configure("mongodbConfTemplateUrl", 
"classpath:///test-mongodb.conf"));
+        app.start(ImmutableList.of(localhostProvisioningLocation));
+
+        EntityTestUtils.assertAttributeEqualsEventually(entity, 
Startable.SERVICE_UP, true);
+        entity.stop();
+        assertFalse(entity.getAttribute(Startable.SERVICE_UP));
+    }
+
+    @Test(groups = "Integration", dependsOnMethods = { "testCanStartAndStop" })
+    public void testCanReadAndWrite() throws Exception {
+        MongoDBServer entity = 
app.createAndManageChild(EntitySpec.create(MongoDBServer.class)
+                .configure("mongodbConfTemplateUrl", 
"classpath:///test-mongodb.conf"));
+        app.start(ImmutableList.of(localhostProvisioningLocation));
+
+        String id = MongoDBTestHelper.insert(entity, "hello", "world!");
+        DBObject docOut = MongoDBTestHelper.getById(entity, id);
+        assertEquals(docOut.get("hello"), "world!");
+    }
+
+    @Test(groups = "Integration", dependsOnMethods = { "testCanStartAndStop" })
+    public void testPollInsertCountSensor() throws Exception {
+        MongoDBServer entity = 
app.createAndManageChild(EntitySpec.create(MongoDBServer.class)
+                .configure("mongodbConfTemplateUrl", 
"classpath:///test-mongodb.conf"));
+        app.start(ImmutableList.of(localhostProvisioningLocation));
+        EntityTestUtils.assertAttributeEqualsEventually(entity, 
Startable.SERVICE_UP, true);
+
+        EntityTestUtils.assertAttributeEventuallyNonNull(entity, 
MongoDBServer.OPCOUNTERS_INSERTS);
+        Long initialInserts = 
entity.getAttribute(MongoDBServer.OPCOUNTERS_INSERTS);
+        MongoDBTestHelper.insert(entity, "a", Boolean.TRUE);
+        MongoDBTestHelper.insert(entity, "b", Boolean.FALSE);
+        EntityTestUtils.assertAttributeEqualsEventually(entity, 
MongoDBServer.OPCOUNTERS_INSERTS, initialInserts + 2);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBRebindIntegrationTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBRebindIntegrationTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBRebindIntegrationTest.java
new file mode 100644
index 0000000..b7c7739
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBRebindIntegrationTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.entity.nosql.mongodb;
+
+import org.apache.brooklyn.entity.nosql.mongodb.MongoDBServer;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.rebind.RebindTestFixtureWithApp;
+import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import brooklyn.test.EntityTestUtils;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+public class MongoDBRebindIntegrationTest extends RebindTestFixtureWithApp {
+
+    private LocalhostMachineProvisioningLocation loc;
+    
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        loc = origApp.newLocalhostProvisioningLocation();
+    }
+
+    @Test(groups = {"Integration"})
+    public void testRebindMongoDb() throws Exception {
+        MongoDBServer origEntity = 
origApp.createAndManageChild(EntitySpec.create(MongoDBServer.class)
+                .configure("mongodbConfTemplateUrl", 
"classpath:///test-mongodb.conf"));
+        origApp.start(ImmutableList.of(loc));
+        EntityTestUtils.assertAttributeEventuallyNonNull(origEntity, 
MongoDBServer.STATUS_BSON);
+
+        // rebind
+        rebind();
+        final MongoDBServer newEntity = (MongoDBServer) 
Iterables.find(newApp.getChildren(), 
Predicates.instanceOf(MongoDBServer.class));
+
+        // confirm effectors still work on entity
+        EntityTestUtils.assertAttributeEqualsEventually(newEntity, 
MongoDBServer.SERVICE_UP, true);
+        newEntity.stop();
+        EntityTestUtils.assertAttributeEqualsEventually(newEntity, 
MongoDBServer.SERVICE_UP, false);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetEc2LiveTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetEc2LiveTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetEc2LiveTest.java
new file mode 100644
index 0000000..051a698
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetEc2LiveTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.entity.nosql.mongodb;
+
+import brooklyn.entity.AbstractEc2LiveTest;
+import brooklyn.entity.basic.Entities;
+import brooklyn.entity.group.DynamicCluster;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.Location;
+import brooklyn.test.Asserts;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.mongodb.DBObject;
+
+import groovy.time.TimeDuration;
+
+import org.apache.brooklyn.entity.nosql.mongodb.MongoDBReplicaSet;
+import org.apache.brooklyn.entity.nosql.mongodb.MongoDBServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import java.util.concurrent.Callable;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+public class MongoDBReplicaSetEc2LiveTest extends AbstractEc2LiveTest {
+
+    @SuppressWarnings("unused")
+    private static final Logger LOG = 
LoggerFactory.getLogger(MongoDBReplicaSetEc2LiveTest.class);
+    private static final Integer REPLICA_SET_SIZE = 3;
+    private static final TimeDuration TIMEOUT = new TimeDuration(0, 0, 180, 0);
+
+    /**
+     * Test that a three node replica set starts and allows access through 
both nodes.
+     */
+    @Override
+    protected void doTest(Location loc) throws Exception {
+        final MongoDBReplicaSet replicaSet = 
app.createAndManageChild(EntitySpec.create(MongoDBReplicaSet.class)
+                .configure(DynamicCluster.INITIAL_SIZE, REPLICA_SET_SIZE)
+                .configure("replicaSetName", "mongodb-live-test-replica-set")
+                .configure("memberSpec", EntitySpec.create(MongoDBServer.class)
+                        .configure("mongodbConfTemplateUrl", 
"classpath:///test-mongodb.conf")
+                        .configure("port", "27017+")));
+
+        assertEquals(replicaSet.getCurrentSize().intValue(), 0);
+
+        app.start(ImmutableList.of(loc));
+
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT), new 
Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                assertEquals(replicaSet.getCurrentSize(), REPLICA_SET_SIZE);
+                assertNotNull(replicaSet.getPrimary());
+                assertEquals(replicaSet.getSecondaries().size(), 
REPLICA_SET_SIZE-1);
+                return true;
+            }
+        });
+
+        Entities.dumpInfo(app);
+
+        // Test inserting a document and reading from secondaries
+        final String documentId = 
MongoDBTestHelper.insert(replicaSet.getPrimary(), "meaning-of-life", 42);
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT), new 
Callable<Boolean>() {
+            @Override
+            public Boolean call() throws Exception {
+                assertEquals(replicaSet.getCurrentSize().intValue(), 3);
+                for (MongoDBServer secondary : replicaSet.getSecondaries()) {
+                    DBObject docOut = MongoDBTestHelper.getById(secondary, 
documentId);
+                    assertEquals(docOut.get("meaning-of-life"), 42);
+                }
+                return true;
+            }
+        });
+
+    }
+
+    @Test(enabled=false)
+    public void testDummy() {} // Convince TestNG IDE integration that this 
really does have test methods
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetIntegrationTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetIntegrationTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetIntegrationTest.java
new file mode 100644
index 0000000..56ed82f
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBReplicaSetIntegrationTest.java
@@ -0,0 +1,208 @@
+/*
+ * 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.entity.nosql.mongodb;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertFalse;
+import static org.testng.Assert.assertNotEquals;
+import static org.testng.Assert.assertNotNull;
+
+import java.util.Collection;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.brooklyn.entity.nosql.mongodb.MongoDBReplicaSet;
+import org.apache.brooklyn.entity.nosql.mongodb.MongoDBServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.BrooklynAppLiveTestSupport;
+import brooklyn.entity.Entity;
+import brooklyn.entity.group.DynamicCluster;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.entity.trait.Startable;
+import brooklyn.location.basic.LocalhostMachineProvisioningLocation;
+import brooklyn.test.Asserts;
+import brooklyn.util.time.Duration;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.mongodb.DBObject;
+
+public class MongoDBReplicaSetIntegrationTest extends 
BrooklynAppLiveTestSupport {
+
+    @SuppressWarnings("unused")
+    private static final Logger log = 
LoggerFactory.getLogger(MongoDBReplicaSetIntegrationTest.class);
+    
+    private Collection<LocalhostMachineProvisioningLocation> locs;
+
+    // Replica sets can take a while to start
+    private static final Duration TIMEOUT = Duration.of(3, TimeUnit.MINUTES);
+
+    @BeforeMethod(alwaysRun=true)
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        locs = ImmutableList.of(app.newLocalhostProvisioningLocation());
+    }
+
+    /**
+     * Creates and starts a replica set, asserts it reaches the given size
+     * and that the primary and secondaries are non-null.
+     */
+    private MongoDBReplicaSet makeAndStartReplicaSet(final Integer size, final 
String testDescription) {
+        // Sets secondaryPreferred so we can read from slaves.
+        final MongoDBReplicaSet replicaSet = 
app.createAndManageChild(EntitySpec.create(MongoDBReplicaSet.class)
+                .configure(DynamicCluster.INITIAL_SIZE, size)
+                .configure("replicaSetName", "test-rs-"+testDescription)
+                .configure("memberSpec", EntitySpec.create(MongoDBServer.class)
+                        .configure("mongodbConfTemplateUrl", 
"classpath:///test-mongodb.conf")
+                        .configure("port", "27017+")));
+        app.start(locs);
+
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT), new 
Runnable() {
+            @Override
+            public void run() {
+                assertEquals(replicaSet.getCurrentSize(), size);
+                assertNotNull(replicaSet.getPrimary(), "replica set has no 
primary");
+                
assertEquals(replicaSet.getPrimary().getReplicaSet().getName(), 
"test-rs-"+testDescription+replicaSet.getId());
+                assertEquals(replicaSet.getSecondaries().size(), size-1);
+            }
+        });
+        return replicaSet;
+    }
+
+    @Test(groups = "Integration")
+    public void testCanStartAndStopAReplicaSet() {
+        final MongoDBReplicaSet replicaSet = makeAndStartReplicaSet(3, 
"can-start-and-stop");
+        replicaSet.stop();
+        assertFalse(replicaSet.getAttribute(Startable.SERVICE_UP));
+    }
+
+    @Test(groups = "Integration")
+    public void testWriteToMasterAndReadFromSecondary() {
+        final MongoDBReplicaSet replicaSet = makeAndStartReplicaSet(3, 
"master-write-secondary-read");
+
+        // Test we can read a document written to the primary from all 
secondaries
+        final String documentId = 
MongoDBTestHelper.insert(replicaSet.getPrimary(), "meaning-of-life", 42);
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT), new 
Runnable() {
+            @Override
+            public void run() {
+                assertEquals(replicaSet.getCurrentSize().intValue(), 3);
+                for (MongoDBServer secondary : replicaSet.getSecondaries()) {
+                    DBObject docOut = MongoDBTestHelper.getById(secondary, 
documentId);
+                    assertEquals(docOut.get("meaning-of-life"), 42);
+                }
+            }
+        });
+    }
+
+    @Test(groups = "Integration")
+    public void testCanResizeAndReadFromNewInstances() {
+        final MongoDBReplicaSet replicaSet = makeAndStartReplicaSet(3, 
"resize-and-read-from-secondaries");
+
+        // Test we can a document written to the primary from all secondaries
+        final String documentId = 
MongoDBTestHelper.insert(replicaSet.getPrimary(), "meaning-of-life", 42);
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT), new 
Runnable() {
+            @Override
+            public void run() {
+                assertEquals(replicaSet.getCurrentSize().intValue(), 3);
+                for (MongoDBServer secondary : replicaSet.getSecondaries()) {
+                    DBObject docOut = MongoDBTestHelper.getById(secondary, 
documentId);
+                    assertEquals(docOut.get("meaning-of-life"), 42);
+                }
+            }
+        });
+
+        // Resize and confirm new members get data
+        replicaSet.resize(5);
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT), new 
Runnable() {
+            @Override
+            public void run() {
+                assertEquals(replicaSet.getCurrentSize().intValue(), 5);
+                Collection<MongoDBServer> secondaries = 
replicaSet.getSecondaries();
+                assertEquals(secondaries.size(), 4);
+                for (MongoDBServer secondary : secondaries) {
+                    DBObject docOut = MongoDBTestHelper.getById(secondary, 
documentId);
+                    assertEquals(docOut.get("meaning-of-life"), 42);
+                }
+            }
+        });
+
+    }
+
+    @Test(groups = "Integration")
+    public void testResizeToEvenNumberOfMembers() {
+        final MongoDBReplicaSet replicaSet = makeAndStartReplicaSet(3, 
"resize-even-ignored");
+        assertEquals(replicaSet.getCurrentSize().intValue(), 3);
+        replicaSet.resize(4);
+        Asserts.succeedsEventually(new Runnable() {
+            @Override
+            public void run() {
+                assertEquals(replicaSet.getCurrentSize().intValue(), 4);
+            }
+        });
+    }
+
+    /**
+     * Test replacing the primary succeeds. More interesting than replacing a 
secondary
+     * because the removal of a primary must happen _through_ the primary. The 
flow is:
+     *  - Brooklyn removes the server from the set and stops it
+     *  - The remaining members of the set elect a new primary
+     *  - We remove the original primary from the new primary.
+     */
+    @Test(groups = "Integration")
+    public void testReplacePrimary() {
+        final MongoDBReplicaSet replicaSet = makeAndStartReplicaSet(3, 
"replace-primary");
+        final MongoDBServer replaced = replicaSet.getPrimary();
+        replicaSet.replaceMember(replaced.getId());
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT), new 
Runnable() {
+            @Override
+            public void run() {
+                assertEquals(replicaSet.getCurrentSize().intValue(), 3);
+                for (Entity member : replicaSet.getMembers()) {
+                    assertNotEquals(member.getId(), replaced.getId());
+                }
+                assertNotNull(replicaSet.getPrimary());
+                assertNotEquals(replicaSet.getPrimary().getId(), 
replaced.getId(), "Expected a new primary to have been elected");
+            }
+        });
+    }
+
+    @Test(groups = "Integration")
+    public void testRemovePrimary() {
+        final MongoDBReplicaSet replicaSet = makeAndStartReplicaSet(3, 
"remove-primary");
+        final MongoDBServer removed = replicaSet.getPrimary();
+
+        replicaSet.removeMember(removed);
+        removed.stop();
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", TIMEOUT), new 
Runnable() {
+            @Override
+            public void run() {
+                assertEquals(replicaSet.getCurrentSize().intValue(), 2);
+                for (Entity member : replicaSet.getMembers()) {
+                    assertNotEquals(member.getId(), removed.getId());
+                }
+                assertNotNull(replicaSet.getPrimary());
+                assertNotEquals(replicaSet.getPrimary().getId(), 
removed.getId(), "Expected a new primary to have been elected");
+            }
+        });
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBRestartIntegrationTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBRestartIntegrationTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBRestartIntegrationTest.java
new file mode 100644
index 0000000..3ef722a
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBRestartIntegrationTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.entity.nosql.mongodb;
+
+import org.apache.brooklyn.entity.nosql.mongodb.MongoDBServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import brooklyn.entity.basic.AbstractSoftwareProcessRestartIntegrationTest;
+import brooklyn.entity.basic.SoftwareProcess;
+import brooklyn.entity.proxying.EntitySpec;
+
+/**
+ * Tests restart of the software *process* (as opposed to the VM).
+ */
+@Test(groups="Integration")
+public class MongoDBRestartIntegrationTest extends 
AbstractSoftwareProcessRestartIntegrationTest {
+    
+    @SuppressWarnings("unused")
+    private static final Logger LOG = 
LoggerFactory.getLogger(MongoDBRestartIntegrationTest.class);
+
+    @Override
+    protected EntitySpec<? extends SoftwareProcess> newEntitySpec() {
+        return EntitySpec.create(MongoDBServer.class);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/d5cf5285/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBSoftLayerLiveTest.java
----------------------------------------------------------------------
diff --git 
a/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBSoftLayerLiveTest.java
 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBSoftLayerLiveTest.java
new file mode 100644
index 0000000..de6e597
--- /dev/null
+++ 
b/software/nosql/src/test/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBSoftLayerLiveTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.entity.nosql.mongodb;
+
+import static org.testng.Assert.assertEquals;
+
+import org.apache.brooklyn.entity.nosql.mongodb.MongoDBServer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.mongodb.DBObject;
+
+import brooklyn.entity.AbstractSoftlayerLiveTest;
+import brooklyn.entity.proxying.EntitySpec;
+import brooklyn.location.Location;
+import brooklyn.test.EntityTestUtils;
+
+public class MongoDBSoftLayerLiveTest extends AbstractSoftlayerLiveTest {
+
+    @SuppressWarnings("unused")
+    private static final Logger LOG = 
LoggerFactory.getLogger(MongoDBSoftLayerLiveTest.class);
+
+    @Override
+    protected void doTest(Location loc) throws Exception {
+        MongoDBServer entity = 
app.createAndManageChild(EntitySpec.create(MongoDBServer.class)
+                .configure("mongodbConfTemplateUrl", 
"classpath:///test-mongodb.conf"));
+        app.start(ImmutableList.of(loc));
+
+        EntityTestUtils.assertAttributeEqualsEventually(entity, 
MongoDBServer.SERVICE_UP, true);
+
+        String id = MongoDBTestHelper.insert(entity, "hello", "world!");
+        DBObject docOut = MongoDBTestHelper.getById(entity, id);
+        assertEquals(docOut.get("hello"), "world!");
+    }
+
+    @Test(enabled=false)
+    public void testDummy() {} // Convince TestNG IDE integration that this 
really does have test methods
+
+}

Reply via email to