You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by al...@apache.org on 2015/11/25 13:17:48 UTC

[03/13] incubator-brooklyn git commit: MongoDB: support disabling direct connection

MongoDB: support disabling direct connection

- Important if MongoDB is locked down such that only ssh access to
  the VM is available.


Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/ec76f129
Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/ec76f129
Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/ec76f129

Branch: refs/heads/master
Commit: ec76f129e7a977b5c326e38eabe6a2b1cfde7d73
Parents: d6311c2
Author: Aled Sage <al...@gmail.com>
Authored: Wed Nov 18 15:35:49 2015 +0000
Committer: Aled Sage <al...@gmail.com>
Committed: Tue Nov 24 13:53:45 2015 +0000

----------------------------------------------------------------------
 .../brooklyn/entity/AbstractEc2LiveTest.java    | 48 +++++++++-
 .../nosql/mongodb/AbstractMongoDBSshDriver.java |  8 +-
 .../entity/nosql/mongodb/MongoDBServer.java     |  3 +
 .../entity/nosql/mongodb/MongoDBServerImpl.java | 96 +++++++++++---------
 .../nosql/mongodb/MongoDBEc2LiveTest.java       | 42 +++++++--
 5 files changed, 145 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ec76f129/software/base/src/test/java/org/apache/brooklyn/entity/AbstractEc2LiveTest.java
----------------------------------------------------------------------
diff --git a/software/base/src/test/java/org/apache/brooklyn/entity/AbstractEc2LiveTest.java b/software/base/src/test/java/org/apache/brooklyn/entity/AbstractEc2LiveTest.java
index 0a1e9d7..3874346 100644
--- a/software/base/src/test/java/org/apache/brooklyn/entity/AbstractEc2LiveTest.java
+++ b/software/base/src/test/java/org/apache/brooklyn/entity/AbstractEc2LiveTest.java
@@ -18,17 +18,29 @@
  */
 package org.apache.brooklyn.entity;
 
+import static org.testng.Assert.fail;
+
+import java.io.ByteArrayOutputStream;
+import java.util.List;
 import java.util.Map;
 
-import org.apache.brooklyn.util.collections.MutableMap;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
 import org.apache.brooklyn.api.location.Location;
 import org.apache.brooklyn.core.internal.BrooklynProperties;
+import org.apache.brooklyn.core.location.Machines;
 import org.apache.brooklyn.core.test.BrooklynAppLiveTestSupport;
 import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests;
+import org.apache.brooklyn.entity.software.base.SoftwareProcess;
 import org.apache.brooklyn.location.jclouds.JcloudsLocation;
 import org.apache.brooklyn.location.jclouds.JcloudsLocationConfig;
+import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.ssh.BashCommands;
+import org.apache.brooklyn.util.time.Duration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -46,6 +58,8 @@ public abstract class AbstractEc2LiveTest extends BrooklynAppLiveTestSupport {
     // to say what combo of provider/region/flags should be used. The problem with that is the
     // IDE integration: one can't just select a single test to run.
     
+    private static final Logger LOG = LoggerFactory.getLogger(AbstractEc2LiveTest.class);
+
     public static final String PROVIDER = "aws-ec2";
     public static final String REGION_NAME = "us-east-1";
     public static final String LOCATION_SPEC = PROVIDER + (REGION_NAME == null ? "" : ":" + REGION_NAME);
@@ -136,4 +150,32 @@ public abstract class AbstractEc2LiveTest extends BrooklynAppLiveTestSupport {
     }
     
     protected abstract void doTest(Location loc) throws Exception;
+    
+    protected void assertExecSsh(SoftwareProcess entity, List<String> commands) {
+        SshMachineLocation machine = Machines.findUniqueMachineLocation(entity.getLocations(), SshMachineLocation.class).get();
+        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+        ByteArrayOutputStream errStream = new ByteArrayOutputStream();
+        int result = machine.execScript(ImmutableMap.of("out", outStream, "err", errStream), "url-reachable", commands);
+        String out = new String(outStream.toByteArray());
+        String err = new String(errStream.toByteArray());
+        if (result == 0) {
+            LOG.debug("Successfully executed: cmds="+commands+"; stderr="+err+"; out="+out);
+        } else {
+            fail("Failed to execute: result="+result+"; cmds="+commands+"; stderr="+err+"; out="+out);
+        }
+    }
+    
+    protected void assertViaSshLocalPortListeningEventually(final SoftwareProcess server, final int port) {
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", Duration.FIVE_MINUTES), new Runnable() {
+            public void run() {
+                assertExecSsh(server, ImmutableList.of("netstat -antp", "netstat -antp | grep LISTEN | grep "+port));
+            }});
+    }
+    
+    protected void assertViaSshLocalUrlListeningEventually(final SoftwareProcess server, final String url) {
+        Asserts.succeedsEventually(ImmutableMap.of("timeout", Duration.FIVE_MINUTES), new Runnable() {
+            public void run() {
+                assertExecSsh(server, ImmutableList.of(BashCommands.installPackage("curl"), "netstat -antp", "curl -k --retry 3 "+url));
+            }});
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ec76f129/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/AbstractMongoDBSshDriver.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/AbstractMongoDBSshDriver.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/AbstractMongoDBSshDriver.java
index 14c495e..ccbe470 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/AbstractMongoDBSshDriver.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/AbstractMongoDBSshDriver.java
@@ -28,6 +28,7 @@ import org.apache.brooklyn.core.entity.Entities;
 import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver;
 import org.apache.brooklyn.entity.software.base.lifecycle.ScriptHelper;
 import org.apache.brooklyn.location.ssh.SshMachineLocation;
+import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.core.internal.ssh.SshTool;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.net.Networking;
@@ -115,7 +116,12 @@ public abstract class AbstractMongoDBSshDriver extends AbstractSoftwareProcessSs
     @Override
     public boolean isRunning() {
         try {
-            return MongoDBClientSupport.forServer((AbstractMongoDBServer) entity).ping();
+            if (entity instanceof MongoDBServerImpl && !((MongoDBServerImpl)entity).clientAccessEnabled()) {
+                // No direct access via MongoDB port; only use ssh-port
+                return newScript(MutableMap.of(USE_PID_FILE, getPidFile()), CHECK_RUNNING).execute() == 0;
+            } else {
+                return MongoDBClientSupport.forServer((AbstractMongoDBServer) entity).ping();
+            }
         } catch (Exception e) {
             Exceptions.propagateIfFatal(e);
             return false;

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ec76f129/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServer.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServer.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServer.java
index 2ec38dc..34c07d3 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServer.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServer.java
@@ -50,6 +50,9 @@ public interface MongoDBServer extends AbstractMongoDBServer {
     ConfigKey<Boolean> ENABLE_REST_INTERFACE = ConfigKeys.newBooleanConfigKey(
             "mongodb.config.enable_rest", "Adds --rest to server startup flags when true", Boolean.FALSE);
 
+    @SetFromFlag("useClientMonitoring")
+    ConfigKey<Boolean> USE_CLIENT_MONITORING = ConfigKeys.newConfigKey("clientMonitoring.enabled", "Monitoring via the MongoDB client enabled", Boolean.TRUE);
+
     AttributeSensor<String> HTTP_INTERFACE_URL = Sensors.newStringSensor(
             "mongodb.server.http_interface", "URL of the server's HTTP console");
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ec76f129/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServerImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServerImpl.java b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServerImpl.java
index 2469046..35b60e8 100644
--- a/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServerImpl.java
+++ b/software/nosql/src/main/java/org/apache/brooklyn/entity/nosql/mongodb/MongoDBServerImpl.java
@@ -71,54 +71,58 @@ public class MongoDBServerImpl extends SoftwareProcessImpl implements MongoDBSer
         sensors().set(HTTP_INTERFACE_URL, String.format("http://%s:%d",
                 accessibleAddress.getHostText(), httpConsolePort));
 
-        try {
-            client = MongoDBClientSupport.forServer(this);
-        } catch (UnknownHostException e) {
-            LOG.warn("Unable to create client connection to {}, not connecting sensors: {} ", this, e.getMessage());
-            return;
-        }
-
-        serviceStats = FunctionFeed.builder()
-                .entity(this)
-                .poll(new FunctionPollConfig<Object, BasicBSONObject>(STATUS_BSON)
-                        .period(2, TimeUnit.SECONDS)
-                        .callable(new Callable<BasicBSONObject>() {
-                            @Override
-                            public BasicBSONObject call() throws Exception {
-                                return MongoDBServerImpl.this.sensors().get(SERVICE_UP)
-                                    ? client.getServerStatus()
-                                    : null;
-                            }
-                        })
-                        .onException(Functions.<BasicBSONObject>constant(null)))
-                .build();
-
-        if (isReplicaSetMember()) {
-            replicaSetStats = FunctionFeed.builder()
+        if (clientAccessEnabled()) {
+            try {
+                client = MongoDBClientSupport.forServer(this);
+            } catch (UnknownHostException e) {
+                LOG.warn("Unable to create client connection to {}, not connecting sensors: {} ", this, e.getMessage());
+                return;
+            }
+    
+            serviceStats = FunctionFeed.builder()
                     .entity(this)
-                    .poll(new FunctionPollConfig<Object, ReplicaSetMemberStatus>(REPLICA_SET_MEMBER_STATUS)
+                    .poll(new FunctionPollConfig<Object, BasicBSONObject>(STATUS_BSON)
                             .period(2, TimeUnit.SECONDS)
-                            .callable(new Callable<ReplicaSetMemberStatus>() {
-                                /**
-                                 * Calls {@link MongoDBClientSupport#getReplicaSetStatus} and
-                                 * extracts <code>myState</code> from the response.
-                                 * @return
-                                 *      The appropriate {@link org.apache.brooklyn.entity.nosql.mongodb.ReplicaSetMemberStatus}
-                                 *      if <code>myState</code> was non-null, {@link ReplicaSetMemberStatus#UNKNOWN} otherwise.
-                                 */
+                            .callable(new Callable<BasicBSONObject>() {
                                 @Override
-                                public ReplicaSetMemberStatus call() {
-                                    BasicBSONObject serverStatus = client.getReplicaSetStatus();
-                                    int state = serverStatus.getInt("myState", -1);
-                                    return ReplicaSetMemberStatus.fromCode(state);
+                                public BasicBSONObject call() throws Exception {
+                                    return MongoDBServerImpl.this.sensors().get(SERVICE_UP)
+                                        ? client.getServerStatus()
+                                        : null;
                                 }
                             })
-                            .onException(Functions.constant(ReplicaSetMemberStatus.UNKNOWN))
-                            .suppressDuplicates(true))
+                            .onException(Functions.<BasicBSONObject>constant(null)))
                     .build();
+    
+            if (isReplicaSetMember()) {
+                replicaSetStats = FunctionFeed.builder()
+                        .entity(this)
+                        .poll(new FunctionPollConfig<Object, ReplicaSetMemberStatus>(REPLICA_SET_MEMBER_STATUS)
+                                .period(2, TimeUnit.SECONDS)
+                                .callable(new Callable<ReplicaSetMemberStatus>() {
+                                    /**
+                                     * Calls {@link MongoDBClientSupport#getReplicaSetStatus} and
+                                     * extracts <code>myState</code> from the response.
+                                     * @return
+                                     *      The appropriate {@link org.apache.brooklyn.entity.nosql.mongodb.ReplicaSetMemberStatus}
+                                     *      if <code>myState</code> was non-null, {@link ReplicaSetMemberStatus#UNKNOWN} otherwise.
+                                     */
+                                    @Override
+                                    public ReplicaSetMemberStatus call() {
+                                        BasicBSONObject serverStatus = client.getReplicaSetStatus();
+                                        int state = serverStatus.getInt("myState", -1);
+                                        return ReplicaSetMemberStatus.fromCode(state);
+                                    }
+                                })
+                                .onException(Functions.constant(ReplicaSetMemberStatus.UNKNOWN))
+                                .suppressDuplicates(true))
+                        .build();
+            } else {
+                sensors().set(IS_PRIMARY_FOR_REPLICA_SET, false);
+                sensors().set(IS_SECONDARY_FOR_REPLICA_SET, false);
+            }
         } else {
-            sensors().set(IS_PRIMARY_FOR_REPLICA_SET, false);
-            sensors().set(IS_SECONDARY_FOR_REPLICA_SET, false);
+            LOG.info("Not monitoring "+this+" to retrieve state via client API");
         }
 
         // Take interesting details from STATUS.
@@ -163,6 +167,10 @@ public class MongoDBServerImpl extends SoftwareProcessImpl implements MongoDBSer
         if (replicaSetStats != null) replicaSetStats.stop();
     }
 
+    protected boolean clientAccessEnabled() {
+        return Boolean.TRUE.equals(config().get(MongoDBServer.USE_CLIENT_MONITORING));
+    }
+    
     @Override
     public MongoDBReplicaSet getReplicaSet() {
         return config().get(MongoDBServer.REPLICA_SET);
@@ -190,6 +198,9 @@ public class MongoDBServerImpl extends SoftwareProcessImpl implements MongoDBSer
             LOG.warn("Attempted to add {} to replica set at server that is not primary: {}", secondary, this);
             return false;
         }
+        if (!clientAccessEnabled()) {
+            throw new IllegalStateException("client-access disabled for "+this+"; cannot add to replica set member "+secondary+" -> "+id);
+        }
         return client.addMemberToReplicaSet(secondary, id);
     }
 
@@ -199,6 +210,9 @@ public class MongoDBServerImpl extends SoftwareProcessImpl implements MongoDBSer
             LOG.warn("Attempted to remove {} from replica set at server that is not primary: {}", server, this);
             return false;
         }
+        if (!clientAccessEnabled()) {
+            throw new IllegalStateException("client-access disabled for "+this+"; cannot remove from replica set member "+server);
+        }
         return client.removeMemberFromReplicaSet(server);
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/ec76f129/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
index 6306b4e..5ad5fa7 100644
--- 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
@@ -18,19 +18,24 @@
  */
 package org.apache.brooklyn.entity.nosql.mongodb;
 
-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 static org.testng.Assert.assertNotNull;
 
 import org.apache.brooklyn.api.entity.EntitySpec;
 import org.apache.brooklyn.api.location.Location;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.EntityAsserts;
+import org.apache.brooklyn.core.entity.lifecycle.Lifecycle;
+import org.apache.brooklyn.core.location.cloud.CloudLocationConfig;
 import org.apache.brooklyn.entity.AbstractEc2LiveTest;
 import org.apache.brooklyn.test.EntityTestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.mongodb.DBObject;
 
 public class MongoDBEc2LiveTest extends AbstractEc2LiveTest {
 
@@ -50,6 +55,29 @@ public class MongoDBEc2LiveTest extends AbstractEc2LiveTest {
         assertEquals(docOut.get("hello"), "world!");
     }
 
+    @Test(groups = {"Live"})
+    public void testWithOnlyPort22() throws Exception {
+        // CentOS-6.3-x86_64-GA-EBS-02-85586466-5b6c-4495-b580-14f72b4bcf51-ami-bb9af1d2.1
+        jcloudsLocation = mgmt.getLocationRegistry().resolve(LOCATION_SPEC, ImmutableMap.of(
+                "tags", ImmutableList.of(getClass().getName()),
+                "imageId", "us-east-1/ami-a96b01c0", 
+                "hardwareId", SMALL_HARDWARE_ID));
+
+        MongoDBServer server = app.createAndManageChild(EntitySpec.create(MongoDBServer.class)
+                .configure(MongoDBServer.PROVISIONING_PROPERTIES.subKey(CloudLocationConfig.INBOUND_PORTS.getName()), ImmutableList.of(22))
+                .configure(MongoDBServer.USE_CLIENT_MONITORING, false));
+        
+        app.start(ImmutableList.of(jcloudsLocation));
+        
+        EntityAsserts.assertAttributeEqualsEventually(server, Attributes.SERVICE_UP, true);
+        EntityAsserts.assertAttributeEqualsEventually(server, Attributes.SERVICE_STATE_ACTUAL, Lifecycle.RUNNING);
+        
+        Integer port = server.getAttribute(MongoDBServer.PORT);
+        assertNotNull(port);
+        
+        assertViaSshLocalPortListeningEventually(server, port);
+    }
+    
     @Test(enabled=false)
     public void testDummy() {} // Convince TestNG IDE integration that this really does have test methods