brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From henev...@apache.org
Subject [15/18] git commit: couchbase improvements: * fixed and tidied download link computation routine (including removal of v300 :) ) * added ServiceNotUpLogic to prevent service_up from being published prematurely * change toCluster parameter to accept Entit
Date Tue, 07 Oct 2014 23:59:54 GMT
couchbase improvements:
* fixed and tidied download link computation routine (including removal of v300 :) )
* added ServiceNotUpLogic to prevent service_up from being published prematurely
* change toCluster parameter to accept Entity or String
* removed dead fromCluster and replicationType parameters
* other minor tidies as per code review


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

Branch: refs/heads/master
Commit: 68e666443441758e4fad9f0b6d94c32c7a8bdf5d
Parents: 14d5c4b
Author: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Authored: Tue Oct 7 16:13:14 2014 -0700
Committer: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Committed: Tue Oct 7 16:19:50 2014 -0700

----------------------------------------------------------------------
 .../nosql/couchbase/CouchbaseCluster.java       |   6 +-
 .../nosql/couchbase/CouchbaseClusterImpl.java   |   7 ++
 .../entity/nosql/couchbase/CouchbaseNode.java   |  12 ++-
 .../nosql/couchbase/CouchbaseNodeDriver.java    |   3 +-
 .../nosql/couchbase/CouchbaseNodeImpl.java      |  21 ++--
 .../nosql/couchbase/CouchbaseNodeSshDriver.java | 105 ++++++++++++++-----
 .../nosql/couchbase/CouchbaseOfflineTest.java   |   6 +-
 .../java/brooklyn/util/text/StringEscapes.java  |   3 +-
 8 files changed, 111 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/68e66644/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseCluster.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseCluster.java
b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseCluster.java
index 5e8ec23..fb52650 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseCluster.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseCluster.java
@@ -76,9 +76,9 @@ public interface CouchbaseCluster extends DynamicCluster {
 
     @SuppressWarnings("serial")
     AttributeSensor<List<String>> COUCHBASE_CLUSTER_UP_NODE_ADDRESSES = Sensors.newSensor(new
TypeToken<List<String>>() {},
-        "couchbase.cluster.node.addresses", "List of host:port of all active nodes in the
cluster (http admin port, and public hostname/IP)");
+            "couchbase.cluster.node.addresses", "List of host:port of all active nodes in
the cluster (http admin port, and public hostname/IP)");
     AttributeSensor<String> COUCHBASE_CLUSTER_CONNECTION_URL = Sensors.newStringSensor(
-        "couchbase.cluster.connection.url", "Couchbase-style URL to connect to the cluster
(e.g. http://127.0.0.1:8091/ or couchbase://10.0.0.1,10.0.0.2/)");
+            "couchbase.cluster.connection.url", "Couchbase-style URL to connect to the cluster
(e.g. http://127.0.0.1:8091/ or couchbase://10.0.0.1,10.0.0.2/)");
     
     // Interesting stats
     AttributeSensor<Double> OPS_PER_NODE = Sensors.newDoubleSensor("couchbase.stats.cluster.per.node.ops",

@@ -128,6 +128,6 @@ public interface CouchbaseCluster extends DynamicCluster {
     @SuppressWarnings("serial")
     @SetFromFlag("replication")
     ConfigKey<List<Map<String,Object>>> REPLICATION = ConfigKeys.newConfigKey(new
TypeToken<List<Map<String,Object>>>() {}, 
-        "couchbase.cluster.replicationConfiguration", "List of replication rules to configure,
each rule including target (id of another cluster) and mode (unidirectional or bidirectional)");
+            "couchbase.cluster.replicationConfiguration", "List of replication rules to configure,
each rule including target (id of another cluster) and mode (unidirectional or bidirectional)");
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/68e66644/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
index 82f27d7..f509c91 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseClusterImpl.java
@@ -163,6 +163,10 @@ public class CouchbaseClusterImpl extends DynamicClusterImpl implements
Couchbas
             addAveragingMemberEnricher(nodeSensor, enricherSetup.get(nodeSensor));
         }
         
+        addEnricher(Enrichers.builder().updatingMap(Attributes.SERVICE_NOT_UP_INDICATORS)
+            .from(IS_CLUSTER_INITIALIZED).computing(
+                IfFunctions.ifNotEquals(true).value("The cluster is not yet completely initialized")
+                    .defaultValue(null).build()).build() );
     }
     
     private void addAveragingMemberEnricher(AttributeSensor<? extends Number> fromSensor,
AttributeSensor<? extends Number> toSensor) {
@@ -187,6 +191,8 @@ public class CouchbaseClusterImpl extends DynamicClusterImpl implements
Couchbas
 
     @Override
     protected void doStart() {
+        setAttribute(IS_CLUSTER_INITIALIZED, false);
+        
         super.doStart();
 
         connectSensors();
@@ -254,6 +260,7 @@ public class CouchbaseClusterImpl extends DynamicClusterImpl implements
Couchbas
             }
 
             setAttribute(IS_CLUSTER_INITIALIZED, true);
+            
         } else {
             throw new IllegalStateException("No up nodes available after starting");
         }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/68e66644/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
index b56e6f9..a0f2b88 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNode.java
@@ -58,7 +58,8 @@ public interface CouchbaseNode extends SoftwareProcess {
 
     @SetFromFlag("downloadUrl")
     BasicAttributeSensorAndConfigKey<String> DOWNLOAD_URL = new BasicAttributeSensorAndConfigKey<String>(
-            SoftwareProcess.DOWNLOAD_URL, "http://packages.couchbase.com/releases/${version}/couchbase-server-${driver.communityOrEnterprise}_${version}${driver.osTagWithPrefix}");
+            SoftwareProcess.DOWNLOAD_URL, "http://packages.couchbase.com/releases/${version}/"
+                + "couchbase-server-${driver.communityOrEnterprise}${driver.downloadLinkPreVersionSeparator}${version}${driver.downloadLinkOsTagWithPrefix}");
 
     @SetFromFlag("clusterInitRamSize")
     BasicAttributeSensorAndConfigKey<Integer> COUCHBASE_CLUSTER_INIT_RAM_SIZE = new
BasicAttributeSensorAndConfigKey<Integer>(
@@ -132,10 +133,11 @@ public interface CouchbaseNode extends SoftwareProcess {
     MethodEffector<Void> REBALANCE = new MethodEffector<Void>(CouchbaseNode.class,
"rebalance");
     MethodEffector<Void> BUCKET_CREATE = new MethodEffector<Void>(CouchbaseNode.class,
"bucketCreate");
     brooklyn.entity.Effector<Void> ADD_REPLICATION_RULE = Effectors.effector(Void.class,
"addReplicationRule")
-        .parameter(String.class, "toCluster")
-        .parameter(String.class, "fromBucket")
-        .parameter(String.class, "toBucket")
-//        .parameter(String.class, "replicationType")
+        .description("Adds a replication rule from the indicated bucket on the cluster where
this node is located "
+            + "to the indicated cluster and optional destination bucket")
+        .parameter(String.class, "fromBucket", "Bucket to be replicated")
+        .parameter(Object.class, "toCluster", "Entity (or ID) of the cluster to which this
should replicate")
+        .parameter(String.class, "toBucket", "Destination bucket for replication in the toCluster,
defaulting to the same as the fromBucket")
         .buildAbstract();
 
     @Effector(description = "add a server to a cluster")

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/68e66644/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeDriver.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeDriver.java
b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeDriver.java
index 54efa39..37f2f74 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeDriver.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeDriver.java
@@ -23,7 +23,8 @@ import brooklyn.entity.basic.SoftwareProcessDriver;
 
 public interface CouchbaseNodeDriver extends SoftwareProcessDriver {
     public String getOsTag();
-    public String getOsTagWithPrefix();
+    public String getDownloadLinkPreVersionSeparator();
+    public String getDownloadLinkOsTagWithPrefix();
     
     public String getCommunityOrEnterprise();
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/68e66644/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeImpl.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeImpl.java
b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeImpl.java
index e97ff3d..6f1fb1a 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeImpl.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeImpl.java
@@ -53,6 +53,7 @@ import brooklyn.util.guava.TypeTokens;
 import brooklyn.util.http.HttpTool;
 import brooklyn.util.http.HttpToolResponse;
 import brooklyn.util.net.Urls;
+import brooklyn.util.task.Tasks;
 import brooklyn.util.text.Strings;
 
 import com.google.common.base.Function;
@@ -252,26 +253,20 @@ public class CouchbaseNodeImpl extends SoftwareProcessImpl implements
CouchbaseN
     
     /** exposed through {@link CouchbaseNode#ADD_REPLICATION_RULE} */
     protected void addReplicationRule(ConfigBag ruleArgs) {
-        Entity fromCluster = (Entity) ruleArgs.getStringKey("fromCluster");
-        Entity toCluster = (Entity) ruleArgs.getStringKey("toCluster");
-        if (fromCluster==null && toCluster==null)
-            throw new IllegalArgumentException("At least one of 'fromCluster' or 'toCluster'
must be supplied");
-        if (fromCluster==null) fromCluster = this;
-        if (toCluster==null) toCluster = this;
+        Object toClusterO = Preconditions.checkNotNull(ruleArgs.getStringKey("toCluster"),
"toCluster must not be null");
+        if (toClusterO instanceof String) {
+            toClusterO = getManagementContext().lookup((String)toClusterO);
+        }
+        Entity toCluster = Tasks.resolving(toClusterO, Entity.class).context(getExecutionContext()).get();
+        
+        String fromBucket = Preconditions.checkNotNull( (String)ruleArgs.getStringKey("fromBucket"),
"fromBucket must be specified" );
         
-        String fromBucket = (String)ruleArgs.getStringKey("fromBucket");
         String toBucket = (String)ruleArgs.getStringKey("toBucket");
-        if (fromBucket==null && toBucket==null)
-            throw new IllegalArgumentException("At least one of 'fromBucket' or 'toBucket'
must be supplied");
-        if (fromBucket==null) fromBucket = toBucket;
         if (toBucket==null) toBucket = fromBucket;
         
-//        String replType = (String)ruleArgs.getStringKey("replicationType");
-        
         if (!ruleArgs.getUnusedConfig().isEmpty()) {
             throw new IllegalArgumentException("Unsupported replication rule data: "+ruleArgs.getUnusedConfig());
         }
-
         
         getDriver().addReplicationRule(toCluster, fromBucket, toBucket);
     }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/68e66644/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeSshDriver.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeSshDriver.java
b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeSshDriver.java
index ceea4a1..111c49f 100644
--- a/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeSshDriver.java
+++ b/software/nosql/src/main/java/brooklyn/entity/nosql/couchbase/CouchbaseNodeSshDriver.java
@@ -33,6 +33,9 @@ import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.Callable;
 
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
 import org.apache.http.auth.Credentials;
 import org.apache.http.auth.UsernamePasswordCredentials;
 
@@ -57,6 +60,7 @@ import brooklyn.util.task.DynamicTasks;
 import brooklyn.util.task.TaskTags;
 import brooklyn.util.task.Tasks;
 import brooklyn.util.text.NaturalOrderComparator;
+import brooklyn.util.text.StringEscapes.BashStringEscapes;
 import brooklyn.util.time.Duration;
 
 import com.google.common.base.Function;
@@ -133,7 +137,7 @@ public class CouchbaseNodeSshDriver extends AbstractSoftwareProcessSshDriver
imp
                 .addAll(Arrays.asList(INSTALL_CURL, 
                     BashCommands.require(BashCommands.alternatives(BashCommands.simpleDownloadUrlAs(urls,
saveAs),
                         // Referer link is required for 3.0.0; note mis-spelling is correct,
as per http://en.wikipedia.org/wiki/HTTP_referer
-                        "curl -f -L -k "+link
+                        "curl -f -L -k "+BashStringEscapes.wrapBash(link)
                             + " -H 'Referer: http://www.couchbase.com/downloads'"
                             + " -o "+saveAs),
                         "Could not retrieve "+saveAs+" (from "+urls.size()+" sites)", 9)))
@@ -156,7 +160,7 @@ public class CouchbaseNodeSshDriver extends AbstractSoftwareProcessSshDriver
imp
 
     @Override
     public void launch() {
-        String clusterPrefix = "--cluster-"+(isPreV300() ? "init-" : "");
+        String clusterPrefix = "--cluster-"+(isPreV3() ? "init-" : "");
         // in v30, the cluster arguments were changed, and it became mandatory to supply
a url + password (if there is none, these are ignored)
         newScript(LAUNCHING)
                 .body.append(
@@ -168,7 +172,7 @@ public class CouchbaseNodeSshDriver extends AbstractSoftwareProcessSshDriver
imp
                 "    sleep 1\n" +
                 "done\n" +
                 couchbaseCli("cluster-init") +
-                        (isPreV300() ? getCouchbaseHostnameAndPort() : getCouchbaseHostnameAndCredentials())
+
+                        (isPreV3() ? getCouchbaseHostnameAndPort() : getCouchbaseHostnameAndCredentials())
+
                         " "+clusterPrefix+"username=" + getUsername() +
                         " "+clusterPrefix+"password=" + getPassword() +
                         " "+clusterPrefix+"port=" + getWebPort() +
@@ -198,44 +202,93 @@ public class CouchbaseNodeSshDriver extends AbstractSoftwareProcessSshDriver
imp
 
     @Override
     public String getOsTag() {
-        return getOsTag(getLocation().getOsDetails(), !isPreV300());
+        return newDownloadLinkSegmentComputer().getOsTag(); 
     }
     
-    public static String getOsTag(OsDetails os, boolean isV30OrLater) {
-        if (os == null) {
-            // Default to 64-bit centos linux
-            if (isV30OrLater)
-                return "centos6.x86_64.rpm";
-            else
-                return "x86_64.rpm";
-            
-        } else {
-            // are there should ways to check for OS name and version
-            String osName = os.getName().toLowerCase();
-            boolean isDebbish = osName.contains("deb") || osName.contains("ubuntu");
-            
+    protected DownloadLinkSegmentComputer newDownloadLinkSegmentComputer() {
+        return new DownloadLinkSegmentComputer(getLocation().getOsDetails(), !isPreV3(),
""+getEntity());
+    }
+    
+    public static class DownloadLinkSegmentComputer {
+
+        // links are:
+        
+        // http://packages.couchbase.com/releases/2.2.0/couchbase-server-community_2.2.0_x86_64.rpm
+        // http://packages.couchbase.com/releases/2.2.0/couchbase-server-community_2.2.0_x86_64.deb
+        // ^^^ preV3 is _ everywhere
+        // http://packages.couchbase.com/releases/3.0.0/couchbase-server-community_3.0.0-ubuntu12.04_amd64.deb
+        // ^^^ most V3 is _${version}-
+        // http://packages.couchbase.com/releases/3.0.0/couchbase-server-community-3.0.0-centos6.x86_64.rpm
+        // ^^^ but RHEL is -${version}-
+        
+        @Nullable private final OsDetails os;
+        @Nonnull private final boolean isV3OrLater;
+        @Nonnull private final String context;
+        @Nonnull private final String osName;
+        @Nonnull private final boolean isRpm;
+        @Nonnull private final boolean is64bit;
+        
+        public DownloadLinkSegmentComputer(@Nullable OsDetails os, boolean isV3OrLater, @Nonnull
String context) {
+            this.os = os;
+            this.isV3OrLater = isV3OrLater;
+            this.context = context;
+            if (os==null) {
+                // guess centos as RPM is sensible default
+                log.warn("No details known for OS of "+context+"; assuming 64-bit RPM distribution
of Couchbase");
+                osName = "centos";
+                isRpm = true;
+                is64bit = true;
+                return;
+            }
+            osName = os.getName().toLowerCase();
+            isRpm = !(osName.contains("deb") || osName.contains("ubuntu"));
+            is64bit = os.is64bit();
+        }
+        /** separator after the version number used to be _ but is - in 3.0 and later */
+        public String getPreVersionSeparator() {
+            if (!isV3OrLater) return "_";
+            if (isRpm) return "-";
+            return "_";
+        }
+        public String getOsTag() {
             // couchbase only provide certain versions; if on other platforms let's suck-it-and-see
             String family;
             if (osName.contains("debian")) family = "debian7_";
             else if (osName.contains("ubuntu")) family = "ubuntu12.04_";
-            else family = "centos6.";
+            else if (osName.contains("centos") || osName.contains("rhel") || (osName.contains("red")
&& osName.contains("hat"))) 
+                family = "centos6.";
+            else {
+                log.warn("Unrecognised OS "+os+" of "+context+"; assuming RPM distribution
of Couchbase");
+                family = "centos6.";
+            }
 
-            // 32-bit binaries aren't (yet?) available
-            String arch = !os.is64bit() ? "x86" : isDebbish && isV30OrLater ? "amd64"
: "x86_64";
-            String fileExtension = isDebbish ? ".deb" : ".rpm";
+            if (!is64bit && !isV3OrLater) {
+                // NB: 32-bit binaries aren't (yet?) available for v30
+                log.warn("32-bit binaries for Couchbase might not be available, when deploying
"+context);
+            }
+            String arch = !is64bit ? "x86" : !isRpm && isV3OrLater ? "amd64" : "x86_64";
+            String fileExtension = isRpm ? ".rpm" : ".deb";
             
-            if (isV30OrLater)
+            if (isV3OrLater)
                 return family + arch + fileExtension;
             else
                 return arch + fileExtension;
         }
+        public String getOsTagWithPrefix() {
+            return (!isV3OrLater ? "_" : "-") + getOsTag(); 
+        }
     }
-    /** separator after the version number used to be _ but is - in 3.0 and later */
-    public String getOsTagWithPrefix() {
-        return (isPreV300() ? "_" : "-") + getOsTag(); 
+    
+    @Override
+    public String getDownloadLinkOsTagWithPrefix() {
+        return newDownloadLinkSegmentComputer().getOsTagWithPrefix(); 
+    }
+    @Override
+    public String getDownloadLinkPreVersionSeparator() {
+        return newDownloadLinkSegmentComputer().getPreVersionSeparator();
     }
 
-    private boolean isPreV300() {
+    private boolean isPreV3() {
         return NaturalOrderComparator.INSTANCE.compare(getEntity().getConfig(CouchbaseNode.SUGGESTED_VERSION),
"3.0") < 0;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/68e66644/software/nosql/src/test/java/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
----------------------------------------------------------------------
diff --git a/software/nosql/src/test/java/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
b/software/nosql/src/test/java/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
index 1b89773..e8815f9 100644
--- a/software/nosql/src/test/java/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
+++ b/software/nosql/src/test/java/brooklyn/entity/nosql/couchbase/CouchbaseOfflineTest.java
@@ -51,12 +51,12 @@ public class CouchbaseOfflineTest {
         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(CouchbaseNodeSshDriver.getOsTag(null, true), "centos6.x86_64.rpm");
-        Assert.assertEquals(CouchbaseNodeSshDriver.getOsTag(null, false), "x86_64.rpm");
+        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(CouchbaseNodeSshDriver.getOsTag(new BasicOsDetails(os, arch,
version), isV30), 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/68e66644/utils/common/src/main/java/brooklyn/util/text/StringEscapes.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/text/StringEscapes.java b/utils/common/src/main/java/brooklyn/util/text/StringEscapes.java
index 194cef4..0aeea58 100644
--- a/utils/common/src/main/java/brooklyn/util/text/StringEscapes.java
+++ b/utils/common/src/main/java/brooklyn/util/text/StringEscapes.java
@@ -89,7 +89,8 @@ public class StringEscapes {
     
     
     public static class BashStringEscapes {
-        // single quotes don't permit escapes!  e.g. echo 'hello \' world'    doesn't work
+        // single quotes don't permit escapes!  e.g. echo 'hello \' world' doesn't work;
+        // you must do 'hello '\'' world' (to get "hello ' world")
         
         /** wraps plain text in double quotes escaped for use in bash double-quoting */
         public static String wrapBash(String value) {


Mime
View raw message