You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@whirr.apache.org by as...@apache.org on 2011/12/07 18:15:00 UTC

svn commit: r1211543 - in /whirr/trunk: ./ cli/src/main/java/org/apache/whirr/cli/command/ cli/src/test/java/org/apache/whirr/cli/ cli/src/test/java/org/apache/whirr/cli/command/ core/src/main/java/org/apache/whirr/ core/src/main/java/org/apache/whirr/...

Author: asavu
Date: Wed Dec  7 17:14:59 2011
New Revision: 1211543

URL: http://svn.apache.org/viewvc?rev=1211543&view=rev
Log:
WHIRR-440. Unit tests improvements (less bound to external services) (asavu)

Added:
    whirr/trunk/core/src/main/java/org/apache/whirr/net/
    whirr/trunk/core/src/main/java/org/apache/whirr/net/DnsException.java
    whirr/trunk/core/src/main/java/org/apache/whirr/net/DnsResolver.java
    whirr/trunk/core/src/main/java/org/apache/whirr/net/FastDnsResolver.java
    whirr/trunk/core/src/main/java/org/apache/whirr/state/
    whirr/trunk/core/src/main/java/org/apache/whirr/state/BlobClusterStateStore.java
    whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStore.java
    whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStoreFactory.java
    whirr/trunk/core/src/main/java/org/apache/whirr/state/FileClusterStateStore.java
    whirr/trunk/core/src/main/java/org/apache/whirr/state/MemoryClusterStateStore.java
    whirr/trunk/core/src/test/java/org/apache/whirr/net/
    whirr/trunk/core/src/test/java/org/apache/whirr/net/integration/
    whirr/trunk/core/src/test/java/org/apache/whirr/net/integration/FakeDnsResolver.java
    whirr/trunk/core/src/test/java/org/apache/whirr/net/integration/FastDnsResolverTest.java
    whirr/trunk/core/src/test/java/org/apache/whirr/state/
    whirr/trunk/core/src/test/java/org/apache/whirr/state/integration/
    whirr/trunk/core/src/test/java/org/apache/whirr/state/integration/BlobClusterStateStoreTest.java
Removed:
    whirr/trunk/cli/src/test/java/org/apache/whirr/cli/MemoryClusterStateStore.java
    whirr/trunk/core/src/main/java/org/apache/whirr/service/BlobClusterStateStore.java
    whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterStateStore.java
    whirr/trunk/core/src/main/java/org/apache/whirr/service/ClusterStateStoreFactory.java
    whirr/trunk/core/src/main/java/org/apache/whirr/service/FileClusterStateStore.java
    whirr/trunk/core/src/main/java/org/apache/whirr/util/DnsUtil.java
    whirr/trunk/core/src/test/java/org/apache/whirr/service/integration/
    whirr/trunk/core/src/test/java/org/apache/whirr/util/integration/DnsUtilTest.java
Modified:
    whirr/trunk/CHANGES.txt
    whirr/trunk/cli/src/main/java/org/apache/whirr/cli/command/ListClusterCommand.java
    whirr/trunk/cli/src/main/java/org/apache/whirr/cli/command/RunScriptCommand.java
    whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/ListClusterCommandTest.java
    whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/RunScriptCommandTest.java
    whirr/trunk/core/src/main/java/org/apache/whirr/Cluster.java
    whirr/trunk/core/src/main/java/org/apache/whirr/ClusterController.java
    whirr/trunk/core/src/main/java/org/apache/whirr/ClusterSpec.java
    whirr/trunk/core/src/main/java/org/apache/whirr/command/AbstractClusterCommand.java
    whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/StatementBuilder.java
    whirr/trunk/core/src/test/java/org/apache/whirr/service/DryRunModuleTest.java
    whirr/trunk/services/chef/src/test/java/org/apache/whirr/service/chef/ChefServiceDryRunTest.java
    whirr/trunk/services/hadoop/src/main/java/org/apache/whirr/service/hadoop/HadoopConfigurationBuilder.java
    whirr/trunk/services/hadoop/src/test/java/org/apache/whirr/service/hadoop/HadoopConfigurationBuilderTest.java

Modified: whirr/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/whirr/trunk/CHANGES.txt?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/CHANGES.txt (original)
+++ whirr/trunk/CHANGES.txt Wed Dec  7 17:14:59 2011
@@ -87,6 +87,8 @@ Trunk (unreleased changes)
 
     WHIRR-404. Add Project Metadata (Jean-Baptiste Onofré and asavu)
 
+    WHIRR-440. Unit tests improvements (less bound to external services) (asavu)
+
   BUG FIXES
 
     WHIRR-377. Fix broken CLI logging config. (asavu via tomwhite)

Modified: whirr/trunk/cli/src/main/java/org/apache/whirr/cli/command/ListClusterCommand.java
URL: http://svn.apache.org/viewvc/whirr/trunk/cli/src/main/java/org/apache/whirr/cli/command/ListClusterCommand.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/cli/src/main/java/org/apache/whirr/cli/command/ListClusterCommand.java (original)
+++ whirr/trunk/cli/src/main/java/org/apache/whirr/cli/command/ListClusterCommand.java Wed Dec  7 17:14:59 2011
@@ -33,8 +33,8 @@ import org.apache.whirr.ClusterControlle
 import org.apache.whirr.ClusterControllerFactory;
 import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.command.AbstractClusterCommand;
-import org.apache.whirr.service.ClusterStateStore;
-import org.apache.whirr.service.ClusterStateStoreFactory;
+import org.apache.whirr.state.ClusterStateStore;
+import org.apache.whirr.state.ClusterStateStoreFactory;
 
 /**
  * A command to list the nodes in a cluster.

Modified: whirr/trunk/cli/src/main/java/org/apache/whirr/cli/command/RunScriptCommand.java
URL: http://svn.apache.org/viewvc/whirr/trunk/cli/src/main/java/org/apache/whirr/cli/command/RunScriptCommand.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/cli/src/main/java/org/apache/whirr/cli/command/RunScriptCommand.java (original)
+++ whirr/trunk/cli/src/main/java/org/apache/whirr/cli/command/RunScriptCommand.java Wed Dec  7 17:14:59 2011
@@ -33,7 +33,7 @@ import org.apache.whirr.ClusterControlle
 import org.apache.whirr.ClusterControllerFactory;
 import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.command.AbstractClusterCommand;
-import org.apache.whirr.service.ClusterStateStoreFactory;
+import org.apache.whirr.state.ClusterStateStoreFactory;
 import org.jclouds.compute.domain.ExecResponse;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.scriptbuilder.domain.Statement;

Modified: whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/ListClusterCommandTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/ListClusterCommandTest.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/ListClusterCommandTest.java (original)
+++ whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/ListClusterCommandTest.java Wed Dec  7 17:14:59 2011
@@ -40,9 +40,9 @@ import org.apache.whirr.Cluster;
 import org.apache.whirr.ClusterController;
 import org.apache.whirr.ClusterControllerFactory;
 import org.apache.whirr.ClusterSpec;
-import org.apache.whirr.cli.MemoryClusterStateStore;
-import org.apache.whirr.service.ClusterStateStore;
-import org.apache.whirr.service.ClusterStateStoreFactory;
+import org.apache.whirr.state.ClusterStateStore;
+import org.apache.whirr.state.ClusterStateStoreFactory;
+import org.apache.whirr.state.MemoryClusterStateStore;
 import org.apache.whirr.util.KeyPair;
 import org.hamcrest.Matcher;
 import org.jclouds.compute.domain.NodeMetadata;

Modified: whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/RunScriptCommandTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/RunScriptCommandTest.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/RunScriptCommandTest.java (original)
+++ whirr/trunk/cli/src/test/java/org/apache/whirr/cli/command/RunScriptCommandTest.java Wed Dec  7 17:14:59 2011
@@ -26,9 +26,9 @@ import org.apache.whirr.Cluster;
 import org.apache.whirr.ClusterController;
 import org.apache.whirr.ClusterControllerFactory;
 import org.apache.whirr.ClusterSpec;
-import org.apache.whirr.cli.MemoryClusterStateStore;
-import org.apache.whirr.service.ClusterStateStore;
-import org.apache.whirr.service.ClusterStateStoreFactory;
+import org.apache.whirr.state.ClusterStateStore;
+import org.apache.whirr.state.ClusterStateStoreFactory;
+import org.apache.whirr.state.MemoryClusterStateStore;
 import org.apache.whirr.util.KeyPair;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.domain.Credentials;

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/Cluster.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/Cluster.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/Cluster.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/Cluster.java Wed Dec  7 17:14:59 2011
@@ -24,7 +24,8 @@ import com.google.common.base.Predicates
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Sets;
 import com.google.common.net.InetAddresses;
-import org.apache.whirr.util.DnsUtil;
+import org.apache.whirr.net.DnsResolver;
+import org.apache.whirr.net.FastDnsResolver;
 import org.jclouds.compute.domain.NodeMetadata;
 import org.jclouds.domain.Credentials;
 
@@ -41,13 +42,14 @@ import static com.google.common.base.Pre
  *
  */
 public class Cluster {
-  
+
   /**
    * This class represents a real node running in a cluster. An instance has
    * one or more roles.
    * @see InstanceTemplate
    */
   public static class Instance {
+
     private final Credentials loginCredentials;
     private final Set<String> roles;
     private final String publicIp;
@@ -56,9 +58,15 @@ public class Cluster {
     private String privateHostName;
     private final String id;
     private final NodeMetadata nodeMetadata;
+    private final DnsResolver dnsResolver;
 
     public Instance(Credentials loginCredentials, Set<String> roles, String publicIp,
         String privateIp, String id, NodeMetadata nodeMetadata) {
+      this(loginCredentials, roles, publicIp, privateIp, id, nodeMetadata, new FastDnsResolver());
+    }
+
+    public Instance(Credentials loginCredentials, Set<String> roles, String publicIp,
+        String privateIp, String id, NodeMetadata nodeMetadata, DnsResolver dnsResolver) {
       this.loginCredentials = checkNotNull(loginCredentials, "loginCredentials");
       this.roles = checkNotNull(roles, "roles");
       this.publicIp = checkNotNull(publicIp, "publicIp");
@@ -71,12 +79,13 @@ public class Cluster {
       }
       this.id = checkNotNull(id, "id");
       this.nodeMetadata = nodeMetadata;
+      this.dnsResolver = dnsResolver;
     }
 
     public Credentials getLoginCredentials() {
       return loginCredentials;
     }
-    
+
     public Set<String> getRoles() {
       return roles;
     }
@@ -88,42 +97,42 @@ public class Cluster {
     public InetAddress getPrivateAddress() throws IOException {
       return resolveIpAddress(getPrivateIp(), getPrivateHostName());
     }
-    
+
     private InetAddress resolveIpAddress(String ip, String host) throws IOException {
       byte[] addr = InetAddresses.forString(ip).getAddress();
       return InetAddress.getByAddress(host, addr);
     }
-    
+
     public String getPublicIp() {
       return publicIp;
     }
-    
+
     public synchronized String getPublicHostName() throws IOException {
       if (publicHostName == null) {
-        publicHostName = DnsUtil.resolveAddress(publicIp);
+        publicHostName = dnsResolver.apply(publicIp);
       }
       return publicHostName;
     }
-    
+
     public String getPrivateIp() {
       return privateIp;
     }
-    
+
     public synchronized String getPrivateHostName() throws IOException {
       if (privateHostName == null) {
-        privateHostName = DnsUtil.resolveAddress(privateIp);
+        privateHostName = dnsResolver.apply(privateIp);
       }
       return privateHostName;
     }
-    
+
     public String getId() {
       return id;
     }
-    
+
     public NodeMetadata getNodeMetadata() {
       return nodeMetadata;
     }
-    
+
     public String toString() {
       return Objects.toStringHelper(this)
         .add("roles", roles)
@@ -150,10 +159,10 @@ public class Cluster {
     this.instances = instances;
     this.configuration = configuration;
   }
-  
+
   public Set<Instance> getInstances() {
     return instances;
-  }  
+  }
   public Properties getConfiguration() {
     return configuration;
   }
@@ -176,5 +185,5 @@ public class Cluster {
       .add("configuration", configuration)
       .toString();
   }
-  
+
 }

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/ClusterController.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/ClusterController.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/ClusterController.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/ClusterController.java Wed Dec  7 17:14:59 2011
@@ -27,8 +27,8 @@ import org.apache.whirr.actions.Bootstra
 import org.apache.whirr.actions.ConfigureClusterAction;
 import org.apache.whirr.actions.DestroyClusterAction;
 import org.apache.whirr.service.ClusterActionHandler;
-import org.apache.whirr.service.ClusterStateStore;
-import org.apache.whirr.service.ClusterStateStoreFactory;
+import org.apache.whirr.state.ClusterStateStore;
+import org.apache.whirr.state.ClusterStateStoreFactory;
 import org.apache.whirr.service.ComputeCache;
 import org.jclouds.compute.ComputeService;
 import org.jclouds.compute.ComputeServiceContext;

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/ClusterSpec.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/ClusterSpec.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/ClusterSpec.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/ClusterSpec.java Wed Dec  7 17:14:59 2011
@@ -494,6 +494,10 @@ public class ClusterSpec {
     return provider;
   }
 
+  public boolean isStub() {
+    return "stub".equals(getProvider());
+  }
+
   public String getIdentity() {
     return identity;
   }
@@ -682,7 +686,7 @@ public class ClusterSpec {
 
   public void setStateStore(String type) {
     if (type != null) {
-      checkArgument(Sets.newHashSet("local", "blob", "none").contains(type),
+      checkArgument(Sets.newHashSet("local", "blob", "memory", "none").contains(type),
         "Invalid state store. Valid values are local, blob or none.");
     }
     this.stateStore = type;

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/command/AbstractClusterCommand.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/command/AbstractClusterCommand.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/command/AbstractClusterCommand.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/command/AbstractClusterCommand.java Wed Dec  7 17:14:59 2011
@@ -40,8 +40,8 @@ import org.apache.whirr.ClusterControlle
 import org.apache.whirr.ClusterControllerFactory;
 import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.ClusterSpec.Property;
-import org.apache.whirr.service.ClusterStateStore;
-import org.apache.whirr.service.ClusterStateStoreFactory;
+import org.apache.whirr.state.ClusterStateStore;
+import org.apache.whirr.state.ClusterStateStoreFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 

Added: whirr/trunk/core/src/main/java/org/apache/whirr/net/DnsException.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/net/DnsException.java?rev=1211543&view=auto
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/net/DnsException.java (added)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/net/DnsException.java Wed Dec  7 17:14:59 2011
@@ -0,0 +1,28 @@
+/**
+ * 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.whirr.net;
+
+/**
+ * Generic dns resolution exception
+ */
+public class DnsException extends RuntimeException {
+  public DnsException(Throwable e) {
+    super(e);
+  }
+}

Added: whirr/trunk/core/src/main/java/org/apache/whirr/net/DnsResolver.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/net/DnsResolver.java?rev=1211543&view=auto
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/net/DnsResolver.java (added)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/net/DnsResolver.java Wed Dec  7 17:14:59 2011
@@ -0,0 +1,27 @@
+/**
+ * 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.whirr.net;
+
+import com.google.common.base.Function;
+
+/**
+ * Marker interface for a reverse DNS resolver
+ */
+public interface DnsResolver extends Function<String, String> {
+}

Added: whirr/trunk/core/src/main/java/org/apache/whirr/net/FastDnsResolver.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/net/FastDnsResolver.java?rev=1211543&view=auto
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/net/FastDnsResolver.java (added)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/net/FastDnsResolver.java Wed Dec  7 17:14:59 2011
@@ -0,0 +1,71 @@
+/**
+ * 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.whirr.net;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+
+import org.xbill.DNS.DClass;
+import org.xbill.DNS.ExtendedResolver;
+import org.xbill.DNS.Message;
+import org.xbill.DNS.Name;
+import org.xbill.DNS.Record;
+import org.xbill.DNS.Resolver;
+import org.xbill.DNS.ReverseMap;
+import org.xbill.DNS.Section;
+import org.xbill.DNS.Type;
+
+/**
+ * Utility functions for DNS.
+ */
+public class FastDnsResolver implements DnsResolver {
+
+  /**
+   * Resolve the reverse dns name for the given IP address
+   * 
+   * @param hostIp
+   * @return The resolved DNS name.
+   * @throws IOException
+   */
+  @Override
+  public String apply(String hostIp) {
+    try {
+      Resolver res = new ExtendedResolver();
+      res.setTimeout(5); // seconds
+
+      Name name = ReverseMap.fromAddress(hostIp);
+      Record rec = Record.newRecord(name, Type.PTR, DClass.IN);
+      Message query = Message.newQuery(rec);
+      Message response = res.send(query);
+
+      Record[] answers = response.getSectionArray(Section.ANSWER);
+      if (answers.length == 0) {
+        // Fall back to standard Java: in contrast to dnsjava, this also reads /etc/hosts
+        return new InetSocketAddress(hostIp, 0).getAddress().getCanonicalHostName();
+      } else {
+        String revaddr = answers[0].rdataToString();
+        return revaddr.endsWith(".") ? revaddr.substring(0, revaddr.length() - 1) : revaddr;
+      }
+    } catch(IOException e) {
+        throw new DnsException(e);
+    }
+  }
+
+}
+

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/StatementBuilder.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/StatementBuilder.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/StatementBuilder.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/StatementBuilder.java Wed Dec  7 17:14:59 2011
@@ -81,15 +81,17 @@ public class StatementBuilder {
             "privateIp", instance.getPrivateIp()
           )
         );
-        try {
-          metadataMap.putAll(
-            ImmutableMap.of(
-              "publicHostName", instance.getPublicHostName(),
-              "privateHostName", instance.getPrivateHostName()
-            )
-          );
-        } catch (IOException e) {
-          LOG.warn("Could not resolve hostname for " + instance, e);
+        if (!clusterSpec.isStub()) {
+          try {
+            metadataMap.putAll(
+              ImmutableMap.of(
+                "publicHostName", instance.getPublicHostName(),
+                "privateHostName", instance.getPrivateHostName()
+              )
+            );
+          } catch (IOException e) {
+            LOG.warn("Could not resolve hostname for " + instance, e);
+          }
         }
       }
       for (Iterator<?> it = clusterSpec.getConfiguration().getKeys("whirr.env"); it.hasNext(); ) {
@@ -107,9 +109,6 @@ public class StatementBuilder {
         scriptBuilder.addStatement(statement);
       }
 
-      // Quick fix: jclouds considers that a script that runs for <2 seconds failed
-      scriptBuilder.addStatement(exec("sleep 4"));
-
       return scriptBuilder.render(family);
     }
   }

Added: whirr/trunk/core/src/main/java/org/apache/whirr/state/BlobClusterStateStore.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/state/BlobClusterStateStore.java?rev=1211543&view=auto
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/state/BlobClusterStateStore.java (added)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/state/BlobClusterStateStore.java Wed Dec  7 17:14:59 2011
@@ -0,0 +1,85 @@
+/**
+ * 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.whirr.state;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.whirr.Cluster;
+import org.apache.whirr.ClusterSpec;
+import org.apache.whirr.service.BlobStoreContextBuilder;
+import org.jclouds.blobstore.BlobStore;
+import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.blobstore.domain.Blob;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+public class BlobClusterStateStore extends ClusterStateStore {
+
+  private static final Logger LOG = LoggerFactory
+    .getLogger(FileClusterStateStore.class);
+
+  private ClusterSpec spec;
+  private BlobStoreContext context;
+
+  private String container;
+  private String blobName;
+
+  public BlobClusterStateStore(ClusterSpec spec) {
+    this.spec = spec;
+    this.context = BlobStoreContextBuilder.build(spec);
+
+    this.container = checkNotNull(spec.getStateStoreContainer());
+    this.blobName = checkNotNull(spec.getStateStoreBlob());
+
+    /* create container if it does not already exists */
+    if (!context.getBlobStore().containerExists(container)) {
+      context.getBlobStore().createContainerInLocation(null, container);
+    }
+  }
+
+  @Override
+  public Cluster load() throws IOException {
+    Blob blob = context.getBlobStore().getBlob(container, blobName);
+    if (blob != null) {
+      return unserialize(spec,
+        IOUtils.toString(blob.getPayload().getInput(), "utf-8"));
+    }
+    return null;
+  }
+
+  @Override
+  public void save(Cluster cluster) throws IOException {
+    BlobStore store = context.getBlobStore();
+
+    Blob blob = store.newBlob(blobName);
+    blob.setPayload(serialize(cluster));
+    store.putBlob(container, blob);
+
+    LOG.info("Saved cluster state to '{}' ", context.getSigner()
+      .signGetBlob(container, blobName).getEndpoint().toString());
+  }
+
+  @Override
+  public void destroy() throws IOException {
+    context.getBlobStore().removeBlob(container, blobName);
+  }
+}

Added: whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStore.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStore.java?rev=1211543&view=auto
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStore.java (added)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStore.java Wed Dec  7 17:14:59 2011
@@ -0,0 +1,133 @@
+/**
+ * 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.whirr.state;
+
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.util.Iterator;
+import java.util.Set;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Sets;
+import org.apache.whirr.Cluster;
+import org.apache.whirr.ClusterSpec;
+import org.jclouds.domain.Credentials;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Interface for cluster state storage facilities.
+ * 
+ */
+public abstract class ClusterStateStore {
+
+  private static final Logger LOG = LoggerFactory.getLogger(ClusterStateStore.class);
+
+  /**
+   * Deserializes cluster state from storage.
+   * 
+   * @return
+   * @throws IOException
+   */
+  public abstract Cluster load() throws IOException;
+
+  /**
+   * Try to load the cluster state or return an empty instance
+   *
+   * @return
+   */
+  public Cluster tryLoadOrEmpty() {
+    try {
+      return load();
+
+    } catch (Exception e) {
+      LOG.info("Unable to load cluster state, assuming it has no running nodes.", e);
+      return Cluster.empty();
+    }
+  }
+
+  /**
+   * Saves cluster state to storage.
+   * 
+   * @param cluster
+   * @throws IOException
+   */
+  public abstract void save(Cluster cluster) throws IOException;
+
+  /**
+   * Destroys the provided cluster's state in storage.
+   * 
+   * @throws IOException
+   */
+  public abstract void destroy() throws IOException;
+
+
+  /**
+   * Create parser friendly string representation for a {@link Cluster}
+   *
+   * @param cluster
+   * @return String representation
+   * @throws IOException
+   */
+  protected String serialize(Cluster cluster) throws IOException {
+    StringBuilder sb = new StringBuilder();
+
+    for (Cluster.Instance instance : cluster.getInstances()) {
+      String id = instance.getId();
+      String roles = Joiner.on(',').join(instance.getRoles());
+
+      sb.append(id).append("\t");
+      sb.append(roles).append("\t");
+      sb.append(instance.getPublicIp()).append("\t");
+      sb.append(instance.getPrivateIp()).append("\n");
+    }
+
+    return sb.toString();
+  }
+
+  /**
+   * Rebuild the {@link Cluster} instance by using the string representation
+   *
+   * @param spec
+   * @param content
+   * @return
+   * @throws UnknownHostException
+   */
+  protected Cluster unserialize(ClusterSpec spec, String content) throws UnknownHostException {
+    Credentials credentials = new Credentials(spec.getClusterUser(), spec.getPrivateKey());
+    Set<Cluster.Instance> instances = Sets.newLinkedHashSet();
+
+    for(String line : Splitter.on("\n").split(content)) {
+      if (line.trim().equals("")) continue; /* ignore empty lines */
+      Iterator<String> fields = Splitter.on("\t").split(line).iterator();
+
+      String id = fields.next();
+      Set<String> roles = Sets.newLinkedHashSet(Splitter.on(",").split(fields.next()));
+      String publicIPAddress = fields.next();
+      String privateIPAddress = fields.next();
+
+      instances.add(new Cluster.Instance(credentials, roles,
+        publicIPAddress, privateIPAddress, id, null));
+    }
+
+    return new Cluster(instances);
+  }
+
+}

Added: whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStoreFactory.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStoreFactory.java?rev=1211543&view=auto
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStoreFactory.java (added)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStoreFactory.java Wed Dec  7 17:14:59 2011
@@ -0,0 +1,81 @@
+/**
+ * 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.whirr.state;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import org.apache.whirr.Cluster;
+import org.apache.whirr.ClusterSpec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+ * A factory for ClusterStateStores.
+ */
+public class ClusterStateStoreFactory {
+
+  private static final Logger LOG = LoggerFactory
+    .getLogger(ClusterStateStoreFactory.class);
+
+  private static class NoopClusterStateStore extends ClusterStateStore {
+    public NoopClusterStateStore() {
+      LOG.warn("No cluster state is going to be persisted. There is no easy " +
+        "way to retrieve instance roles after launch.");
+    }
+
+    @Override
+    public Cluster load() throws IOException {
+      return null;
+    }
+
+    @Override
+    public void save(Cluster cluster) throws IOException {
+    }
+
+    @Override
+    public void destroy() throws IOException {
+    }
+  }
+
+  private Cache<ClusterSpec, ClusterStateStore> storeCache = CacheBuilder.newBuilder().build(
+    new CacheLoader<ClusterSpec, ClusterStateStore>() {
+      @Override
+      public ClusterStateStore load(ClusterSpec spec) throws Exception {
+        if ("local".equals(spec.getStateStore())) {
+          return new FileClusterStateStore(spec);
+
+        } else if ("blob".equals(spec.getStateStore())) {
+          return new BlobClusterStateStore(spec);
+
+        } else if ("memory".equals(spec.getStateStore())) {
+          return new MemoryClusterStateStore();
+
+        } else {
+          return new NoopClusterStateStore();
+        }
+      }
+    });
+
+  public ClusterStateStore create(ClusterSpec spec) {
+    return storeCache.getUnchecked(spec);
+  }
+}

Added: whirr/trunk/core/src/main/java/org/apache/whirr/state/FileClusterStateStore.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/state/FileClusterStateStore.java?rev=1211543&view=auto
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/state/FileClusterStateStore.java (added)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/state/FileClusterStateStore.java Wed Dec  7 17:14:59 2011
@@ -0,0 +1,76 @@
+/**
+ * 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.whirr.state;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.whirr.Cluster;
+import org.apache.whirr.ClusterSpec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Joiner;
+import com.google.common.io.Files;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Stores/Reads cluster state from a local file (located at:
+ * "~/.whirr/cluster-name/instances")
+ * 
+ */
+public class FileClusterStateStore extends ClusterStateStore {
+
+  private static final Logger LOG = LoggerFactory
+      .getLogger(FileClusterStateStore.class);
+
+  private ClusterSpec spec;
+
+  public FileClusterStateStore(ClusterSpec spec) {
+    checkNotNull(spec,"clusterSpec");
+    this.spec = spec;
+  }
+
+  @Override
+  public Cluster load() throws IOException {
+    File instancesFile = new File(spec.getClusterDirectory(), "instances");
+    return unserialize(spec,
+      Joiner.on("\n").join(Files.readLines(instancesFile, Charsets.UTF_8)));
+  }
+
+  @Override
+  public void destroy() throws IOException {
+    Files.deleteRecursively(spec.getClusterDirectory());
+  }
+
+  @Override
+  public void save(Cluster cluster) throws IOException {
+    File instancesFile = new File(spec.getClusterDirectory(), "instances");
+
+    try {
+      Files.write(serialize(cluster).toString(), instancesFile, Charsets.UTF_8);
+      LOG.info("Wrote instances file {}", instancesFile);
+
+    } catch (IOException e) {
+      LOG.error("Problem writing instances file {}", instancesFile, e);
+    }
+  }
+
+}

Added: whirr/trunk/core/src/main/java/org/apache/whirr/state/MemoryClusterStateStore.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/state/MemoryClusterStateStore.java?rev=1211543&view=auto
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/state/MemoryClusterStateStore.java (added)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/state/MemoryClusterStateStore.java Wed Dec  7 17:14:59 2011
@@ -0,0 +1,46 @@
+/**
+ * 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.whirr.state;
+
+import org.apache.whirr.Cluster;
+
+import java.io.IOException;
+
+/**
+ * Memory only cluster state storage useful for testing
+ */
+public class MemoryClusterStateStore extends ClusterStateStore {
+
+  private Cluster cluster;
+
+  @Override
+  public Cluster load() throws IOException {
+    return cluster;
+  }
+
+  @Override
+  public void save(Cluster cluster) throws IOException {
+    this.cluster = cluster;
+  }
+
+  @Override
+  public void destroy() throws IOException {
+    cluster = null;
+  }
+}

Added: whirr/trunk/core/src/test/java/org/apache/whirr/net/integration/FakeDnsResolver.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/net/integration/FakeDnsResolver.java?rev=1211543&view=auto
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/net/integration/FakeDnsResolver.java (added)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/net/integration/FakeDnsResolver.java Wed Dec  7 17:14:59 2011
@@ -0,0 +1,34 @@
+/**
+ * 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.whirr.net.integration;
+
+import org.apache.whirr.net.DnsResolver;
+
+import static org.apache.commons.lang.StringUtils.replace;
+
+/**
+ * Fake reverse DNS resolver
+ */
+public class FakeDnsResolver implements DnsResolver {
+
+  @Override
+  public String apply(String hostIp) {
+    return replace(hostIp, ".", "-") + ".example.com";
+  }
+}

Added: whirr/trunk/core/src/test/java/org/apache/whirr/net/integration/FastDnsResolverTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/net/integration/FastDnsResolverTest.java?rev=1211543&view=auto
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/net/integration/FastDnsResolverTest.java (added)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/net/integration/FastDnsResolverTest.java Wed Dec  7 17:14:59 2011
@@ -0,0 +1,89 @@
+/**
+ * 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.whirr.net.integration;
+
+import static java.lang.System.out;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.UnknownHostException;
+import java.util.Collections;
+import java.util.Enumeration;
+
+import org.apache.whirr.net.DnsResolver;
+import org.apache.whirr.net.FastDnsResolver;
+import org.junit.Test;
+import org.xbill.DNS.Address;
+
+public class FastDnsResolverTest {
+
+  private static final DnsResolver DNS_RESOLVER = new FastDnsResolver();
+
+  @Test
+  public void testResolveAddress() throws IOException {
+    // test it with all interfaces
+    Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
+    while (en.hasMoreElements()) {
+      NetworkInterface netint = (NetworkInterface) en.nextElement();
+      Enumeration<InetAddress> inetAddresses = netint.getInetAddresses();
+      for (InetAddress inetAddress : Collections.list(inetAddresses)) {
+        if (inetAddress instanceof Inet4Address) {
+          long start = System.currentTimeMillis();
+          String reverse = DNS_RESOLVER.apply(inetAddress.getHostAddress());
+          long end = System.currentTimeMillis();
+          // we know that java.net.InetAddress's getHostName takes > 4.5s if
+          // there is no reverse address assigned to it
+          // FastDnsResolver should resolve any address in less than 5 seconds or fail
+          assertTrue("FastDnsResolver.resolveAddress takes " + (end - start)
+              + " millis, it should be shorter than five seconds",
+              end - start < 5000);
+          if (inetAddress.toString().substring(1).equals(reverse)) {
+            out.printf(
+                "InetAddress %s on interface %s does not have reverse dns name, so their reverse remains: %s\n",
+                inetAddress, netint.getDisplayName(), reverse);
+          } else {
+            if (inetAddress.isLoopbackAddress()) {
+              out.printf(
+                  "InetAddress %s on loopback interface %s obtained reverse name as %s\n",
+                  inetAddress, netint.getDisplayName(), reverse);
+            } else {
+              out.printf(
+                  "InetAddress %s on interface %s has reverse dns name: %s\n",
+                  inetAddress, netint.getDisplayName(), reverse);
+              try {
+                InetAddress checkedAddr = Address.getByName(reverse);
+                assertEquals(inetAddress, checkedAddr);
+              } catch (UnknownHostException uhex) {
+                fail("InetAddress " + inetAddress + " on interface "
+                    + netint.getDisplayName() + " got " + reverse
+                    + " reverse dns name which in return is an unknown host!");
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+}

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/service/DryRunModuleTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/service/DryRunModuleTest.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/service/DryRunModuleTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/service/DryRunModuleTest.java Wed Dec  7 17:14:59 2011
@@ -80,6 +80,7 @@ public class DryRunModuleTest {
     config.setProperty("whirr.cluster-name", "stub-test");
     config.setProperty("whirr.instance-templates",
         "10 noop+noop3,10 noop2+noop,10 noop3+noop2");
+    config.setProperty("whirr.state-store", "memory");
 
     ClusterSpec clusterSpec = ClusterSpec.withTemporaryKeys(config);
     ClusterController controller = new ClusterController();

Added: whirr/trunk/core/src/test/java/org/apache/whirr/state/integration/BlobClusterStateStoreTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/state/integration/BlobClusterStateStoreTest.java?rev=1211543&view=auto
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/state/integration/BlobClusterStateStoreTest.java (added)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/state/integration/BlobClusterStateStoreTest.java Wed Dec  7 17:14:59 2011
@@ -0,0 +1,126 @@
+/**
+ * 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.whirr.state.integration;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.commons.lang.RandomStringUtils;
+import org.apache.whirr.Cluster;
+import org.apache.whirr.ClusterSpec;
+import org.apache.whirr.state.BlobClusterStateStore;
+import org.apache.whirr.service.BlobStoreContextBuilder;
+import org.jclouds.blobstore.BlobStoreContext;
+import org.jclouds.domain.Credentials;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+public class BlobClusterStateStoreTest {
+
+  private static final Logger LOG = LoggerFactory
+      .getLogger(BlobClusterStateStoreTest.class);
+
+  private Configuration getTestConfiguration() throws ConfigurationException {
+    return new PropertiesConfiguration("whirr-core-test.properties");
+  }
+
+  private ClusterSpec getTestClusterSpec() throws Exception {
+    return ClusterSpec.withTemporaryKeys(getTestConfiguration());
+  }
+
+  @Test
+  public void testStoreAndLoadState() throws Exception {
+    ClusterSpec spec = getTestClusterSpec();
+
+    BlobStoreContext context = BlobStoreContextBuilder.build(spec);
+    String container = generateRandomContainerName(context);
+    try {
+      spec.setStateStore("blob");
+      spec.setStateStoreContainer(container);
+
+      Cluster expected = createTestCluster(new String[]{"region/id1", "region/id2"},
+          new String[]{"role1", "role2"});
+
+      BlobClusterStateStore store = new BlobClusterStateStore(spec);
+      store.save(expected);
+
+      /* load and check the stored state */
+      Cluster stored = store.load();
+      Cluster.Instance first = Iterables.getFirst(stored.getInstances(), null);
+      assertNotNull(first);
+
+      assertThat(first.getId(), is("region/id1"));
+      assertThat(first.getRoles().contains("role1"), is(true));
+      assertThat(stored.getInstances().size(), is(2));
+
+      /* destroy stored state and check it no longer exists */
+      store.destroy();
+      expected = store.load();
+      assertNull(expected);
+
+    } finally {
+      LOG.info("Removing temporary container '{}'", container);
+      context.getBlobStore().deleteContainer(container);
+    }
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void testInvalidContainerName() throws Exception {
+    ClusterSpec spec = getTestClusterSpec();
+
+    /* underscores are not allowed and it should throw exception */
+    spec.setStateStoreContainer("whirr_test");
+  }
+
+  private String generateRandomContainerName(BlobStoreContext context) {
+    String candidate;
+    do {
+      candidate = RandomStringUtils.randomAlphanumeric(12).toLowerCase();
+    } while(!context.getBlobStore().createContainerInLocation(null, candidate));
+    LOG.info("Created temporary container '{}'", candidate);
+    return candidate;
+  }
+
+  private Cluster createTestCluster(String[] ids, String[] roles) {
+    checkArgument(ids.length == roles.length, "each ID should have a role");
+
+    Credentials credentials = new Credentials("dummy", "dummy");
+    Set<Cluster.Instance> instances = Sets.newLinkedHashSet();
+
+    for(int i = 0; i < ids.length; i++) {
+      String ip = "127.0.0." + (i + 1);
+      instances.add(new Cluster.Instance(credentials,
+        Sets.newHashSet(roles[i]), ip, ip, ids[i], null));
+    }
+
+    return new Cluster(instances);
+  }
+
+}

Modified: whirr/trunk/services/chef/src/test/java/org/apache/whirr/service/chef/ChefServiceDryRunTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/chef/src/test/java/org/apache/whirr/service/chef/ChefServiceDryRunTest.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/services/chef/src/test/java/org/apache/whirr/service/chef/ChefServiceDryRunTest.java (original)
+++ whirr/trunk/services/chef/src/test/java/org/apache/whirr/service/chef/ChefServiceDryRunTest.java Wed Dec  7 17:14:59 2011
@@ -80,10 +80,11 @@ public class ChefServiceDryRunTest {
     Configuration config = new PropertiesConfiguration();
     config.setProperty("whirr.provider", "stub");
     config.setProperty("whirr.cluster-name", "stub-test");
+    config.setProperty("whirr.state-store", "memory");
     return config;
   }
 
-  private ClusterController lauchWithConfig(Configuration config)
+  private ClusterController launchWithConfig(Configuration config)
       throws IOException, InterruptedException, ConfigurationException,
       JSchException {
     ClusterSpec clusterSpec = ClusterSpec.withTemporaryKeys(config);
@@ -98,7 +99,7 @@ public class ChefServiceDryRunTest {
    */
   @Test
   public void testChefOnly() throws Exception {
-    lauchWithConfig(chefOnly);
+    launchWithConfig(chefOnly);
     assertInstallFunctionsWereExecuted(DryRun.INSTANCE);
     // chef only install should not contain any chef-solo executions
     assertScriptPredicateOnPhase(DryRun.INSTANCE, "configure",
@@ -117,7 +118,7 @@ public class ChefServiceDryRunTest {
    */
   @Test
   public void testChefWithDefaultRecipe() throws Exception {
-    lauchWithConfig(cookbookWithDefaultRecipe);
+    launchWithConfig(cookbookWithDefaultRecipe);
     assertInstallFunctionsWereExecuted(DryRun.INSTANCE);
     // chef execution with a default cookbook recipe should contain a
     // particular string
@@ -136,7 +137,7 @@ public class ChefServiceDryRunTest {
    */
   @Test
   public void testChefWithAttributes() throws Exception {
-    lauchWithConfig(cookbookWithSpecificRecipe);
+    launchWithConfig(cookbookWithSpecificRecipe);
     assertInstallFunctionsWereExecuted(DryRun.INSTANCE);
     // chef execution with a default cookbook recipe should contain a
     // particular string
@@ -155,7 +156,7 @@ public class ChefServiceDryRunTest {
    */
   @Test
   public void testChefWithParticularRecipe() throws Exception {
-    lauchWithConfig(cookbookWithAttributes);
+    launchWithConfig(cookbookWithAttributes);
     assertInstallFunctionsWereExecuted(DryRun.INSTANCE);
     // chef execution with a default cookbook recipe should contain a
     // particular string

Modified: whirr/trunk/services/hadoop/src/main/java/org/apache/whirr/service/hadoop/HadoopConfigurationBuilder.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/hadoop/src/main/java/org/apache/whirr/service/hadoop/HadoopConfigurationBuilder.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/services/hadoop/src/main/java/org/apache/whirr/service/hadoop/HadoopConfigurationBuilder.java (original)
+++ whirr/trunk/services/hadoop/src/main/java/org/apache/whirr/service/hadoop/HadoopConfigurationBuilder.java Wed Dec  7 17:14:59 2011
@@ -89,7 +89,7 @@ public class HadoopConfigurationBuilder 
     Instance namenode = cluster
         .getInstanceMatching(role(HadoopNameNodeClusterActionHandler.ROLE));
     config.setProperty("fs.default.name", String.format("hdfs://%s:8020/",
-        namenode.getPublicAddress().getHostName()));
+        namenode.getPublicHostName()));
     return config;
   }
   
@@ -135,7 +135,7 @@ public class HadoopConfigurationBuilder 
     Instance jobtracker = cluster
         .getInstanceMatching(role(HadoopJobTrackerClusterActionHandler.ROLE));
     config.setProperty("mapred.job.tracker", String.format("%s:8021",
-        jobtracker.getPublicAddress().getHostName()));
+        jobtracker.getPublicHostName()));
     return config;
   }
 

Modified: whirr/trunk/services/hadoop/src/test/java/org/apache/whirr/service/hadoop/HadoopConfigurationBuilderTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/services/hadoop/src/test/java/org/apache/whirr/service/hadoop/HadoopConfigurationBuilderTest.java?rev=1211543&r1=1211542&r2=1211543&view=diff
==============================================================================
--- whirr/trunk/services/hadoop/src/test/java/org/apache/whirr/service/hadoop/HadoopConfigurationBuilderTest.java (original)
+++ whirr/trunk/services/hadoop/src/test/java/org/apache/whirr/service/hadoop/HadoopConfigurationBuilderTest.java Wed Dec  7 17:14:59 2011
@@ -36,6 +36,8 @@ import org.apache.commons.configuration.
 import org.apache.whirr.Cluster;
 import org.apache.whirr.Cluster.Instance;
 import org.apache.whirr.ClusterSpec;
+import org.apache.whirr.net.DnsResolver;
+import org.apache.whirr.net.integration.FakeDnsResolver;
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.jclouds.compute.domain.Hardware;
@@ -48,7 +50,7 @@ import org.junit.Before;
 import org.junit.Test;
 
 public class HadoopConfigurationBuilderTest {
-  
+
   static class RegexMatcher extends BaseMatcher<String> {
     private final String regex;
 
@@ -87,6 +89,8 @@ public class HadoopConfigurationBuilderT
   }
   
   private Cluster newCluster(int numberOfWorkers) {
+    DnsResolver fakeDnsResolver = new FakeDnsResolver();
+
     NodeMetadata node = mock(NodeMetadata.class);
     List<Processor> processors = ImmutableList.of(new Processor(4, 1.0));
     Hardware hardware = new HardwareImpl(null, null, "id", null, null,
@@ -98,14 +102,14 @@ public class HadoopConfigurationBuilderT
     Instance master = new Instance(new Credentials("", ""),
         ImmutableSet.of(HadoopNameNodeClusterActionHandler.ROLE,
             HadoopJobTrackerClusterActionHandler.ROLE),
-            "10.0.0.1", "10.0.0.1", "1", node);
+            "10.0.0.1", "10.0.0.1", "1", node, fakeDnsResolver);
     instances.add(master);
     for (int i = 0; i < numberOfWorkers; i++) {
       int id = i + 2;
       instances.add(new Instance(new Credentials("", ""),
         ImmutableSet.of(HadoopDataNodeClusterActionHandler.ROLE,
               HadoopTaskTrackerClusterActionHandler.ROLE),
-              "10.0.0." + id, "10.0.0." + id, id + "", node));
+              "10.0.0." + id, "10.0.0." + id, id + "", node, fakeDnsResolver));
     }
     return new Cluster(instances.build());
   }