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 2012/03/28 00:00:26 UTC

svn commit: r1306027 [2/3] - in /whirr/trunk: ./ cli/src/main/java/org/apache/whirr/cli/command/ cli/src/test/java/org/apache/whirr/cli/command/ core/src/main/java/org/apache/whirr/ core/src/main/java/org/apache/whirr/actions/ core/src/main/java/org/ap...

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/service/DryRunModule.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/service/DryRunModule.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/service/DryRunModule.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/service/DryRunModule.java Tue Mar 27 22:00:24 2012
@@ -18,53 +18,55 @@
 
 package org.apache.whirr.service;
 
-import com.google.common.base.Function;
-import com.google.common.base.Objects;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableListMultimap;
-import com.google.common.collect.LinkedListMultimap;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.MapMaker;
-import com.google.common.collect.Maps;
-import com.google.common.io.InputSupplier;
-import com.google.inject.AbstractModule;
-import org.aopalliance.intercept.MethodInterceptor;
-import org.aopalliance.intercept.MethodInvocation;
-import org.jclouds.compute.callables.RunScriptOnNode;
+import static com.google.common.collect.Iterables.concat;
+import static com.google.common.collect.Iterables.contains;
+import static com.google.common.collect.Iterables.find;
+import static com.google.common.collect.Multimaps.synchronizedListMultimap;
+import static com.google.common.io.ByteStreams.newInputStreamSupplier;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
 import org.jclouds.compute.domain.ExecResponse;
 import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.compute.domain.internal.NodeMetadataImpl;
+import org.jclouds.compute.events.StatementOnNode;
+import org.jclouds.compute.events.StatementOnNodeSubmission;
 import org.jclouds.crypto.CryptoStreams;
 import org.jclouds.domain.Credentials;
 import org.jclouds.domain.LoginCredentials;
 import org.jclouds.io.Payload;
 import org.jclouds.io.payloads.StringPayload;
 import org.jclouds.net.IPSocket;
+import org.jclouds.scriptbuilder.domain.Statement;
 import org.jclouds.ssh.SshClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-import javax.inject.Singleton;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import static com.google.common.collect.Iterables.concat;
-import static com.google.common.collect.Iterables.contains;
-import static com.google.common.collect.Iterables.find;
-import static com.google.common.collect.Multimaps.synchronizedListMultimap;
-import static com.google.common.io.ByteStreams.newInputStreamSupplier;
-import static com.google.inject.matcher.Matchers.identicalTo;
-import static com.google.inject.matcher.Matchers.returns;
-import static com.google.inject.matcher.Matchers.subclassesOf;
+import com.google.common.base.Objects;
+import com.google.common.base.Predicate;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.eventbus.EventBus;
+import com.google.common.eventbus.Subscribe;
+import com.google.common.io.InputSupplier;
+import com.google.inject.AbstractModule;
+import com.google.inject.Provides;
 
 /**
  * Outputs orchestration jclouds does to INFO logging and saves an ordered list
@@ -78,109 +80,51 @@ import static com.google.inject.matcher.
 public class DryRunModule extends AbstractModule {
   private static final Logger LOG = LoggerFactory.getLogger(DryRunModule.class);
 
-  // an example showing how to intercept any internal method for logging
-  // purposes
-  public class LogCallToRunScriptOnNode implements MethodInterceptor {
-
-    public Object invoke(MethodInvocation i) throws Throwable {
-      if (i.getMethod().getName().equals("call")) {
-        RunScriptOnNode runScriptOnNode = RunScriptOnNode.class.cast(i
-            .getThis());
-        String nodeName = runScriptOnNode.getNode().getName();
-        LOG.info(nodeName + " >> running script");
-        Object returnVal = i.proceed();
-        LOG.info(nodeName + " << " + returnVal);
-        return returnVal;
-      } else {
-        return i.proceed();
-      }
+  public static class DryRun {
+    public DryRun reset() {
+      executedScripts.clear();
+      totallyOrderedScripts.clear();
+      return this;
     }
-  }
 
-  public static synchronized DryRun getDryRun() {
-    return DryRun.INSTANCE;
-  }
-
-  public static void resetDryRun() {
-    DryRun.INSTANCE.executedScripts.clear();
-    DryRun.INSTANCE.totallyOrderedScripts.clear();
-  }
-
-  // enum singleton pattern
-  public static enum DryRun {
-    INSTANCE;
-    
     // stores the scripts executed, per node, in the order they were executed
-    private final ListMultimap<NodeMetadata, RunScriptOnNode> executedScripts = synchronizedListMultimap(LinkedListMultimap
-        .<NodeMetadata, RunScriptOnNode> create());
+    private final ListMultimap<NodeMetadata, Statement> executedScripts = synchronizedListMultimap(LinkedListMultimap
+        .<NodeMetadata, Statement> create());
     
-    private final List<RunScriptOnNode> totallyOrderedScripts = Collections.synchronizedList(new ArrayList<RunScriptOnNode>());
+    private final List<StatementOnNode> totallyOrderedScripts = Collections.synchronizedList(new ArrayList<StatementOnNode>());
 
     DryRun() {
     }
-
-    void newExecution(RunScriptOnNode runScript) {
-      NodeMetadata original = runScript.getNode();
-      //duplicate the NodeMetadata instance without credentials because
-      //NodeMetadata equals() contract uses credentials.
-      NodeMetadataImpl stored = new NodeMetadataImpl(
-          original.getProviderId(),
-          original.getName(),
-          original.getId(),
-          original.getLocation(),
-          original.getUri(),
-          original.getUserMetadata(),
-          original.getTags(),
-          original.getGroup(),
-          original.getHardware(),
-          original.getImageId(),
-          original.getOperatingSystem(),
-          original.getState(),
-          original.getLoginPort(),
-          original.getPrivateAddresses(),
-          original.getPublicAddresses(),
-          null,
-          null,
-          original.getHostname());
-      executedScripts.put(stored, runScript);
-      totallyOrderedScripts.add(runScript);
+       
+    @Subscribe
+    public void newExecution(StatementOnNodeSubmission info) {
+      executedScripts.put(info.getNode(), info.getStatement());
+      totallyOrderedScripts.add(info);
     }
 
-    public synchronized ListMultimap<NodeMetadata, RunScriptOnNode> getExecutions() {
+    public synchronized ListMultimap<NodeMetadata, Statement> getExecutions() {
       return ImmutableListMultimap.copyOf(executedScripts);
     }
     
-    public synchronized List<RunScriptOnNode> getTotallyOrderedExecutions() {
+    public synchronized List<StatementOnNode> getTotallyOrderedExecutions() {
       return ImmutableList.copyOf(totallyOrderedScripts);
     }
 
   }
 
-  public class SaveDryRunsByInterceptingRunScriptOnNodeCreation implements
-      MethodInterceptor {
-
-    public Object invoke(MethodInvocation i) throws Throwable {
-      if (i.getMethod().getName().equals("create")) {
-        Object returnVal = i.proceed();
-        getDryRun().newExecution(RunScriptOnNode.class.cast(returnVal));
-        return returnVal;
-      } else {
-        return i.proceed();
-      }
-    }
-  }
-
   @Override
   protected void configure() {
     bind(SshClient.Factory.class).to(LogSshClient.Factory.class);
-    bindInterceptor(subclassesOf(RunScriptOnNode.class),
-        returns(identicalTo(ExecResponse.class)),
-        new LogCallToRunScriptOnNode());
-    bindInterceptor(subclassesOf(RunScriptOnNode.Factory.class),
-        returns(identicalTo(RunScriptOnNode.class)),
-        new SaveDryRunsByInterceptingRunScriptOnNodeCreation());
   }
-
+  
+  @Singleton
+  @Provides
+  DryRun provideDryRun(EventBus in){
+     DryRun dryRun = new DryRun();
+     in.register(dryRun);
+     return dryRun;
+  }
+  
   private static class Key {
     private final IPSocket socket;
     private final Credentials creds;
@@ -240,18 +184,17 @@ public class DryRunModule extends Abstra
     public static class Factory implements SshClient.Factory {
 
       // this will ensure only one state per ip socket/user
-      private final Map<Key, SshClient> clientMap;
+      private final LoadingCache<Key, SshClient> clientMap;
       // easy access to node metadata
       private final ConcurrentMap<String, NodeMetadata> nodes;
 
       @SuppressWarnings("unused")
       @Inject
       public Factory(final ConcurrentMap<String, NodeMetadata> nodes) {
-        this.clientMap = new MapMaker()
-            .makeComputingMap(new Function<Key, SshClient>() {
-
+         this.clientMap = CacheBuilder.newBuilder().build(
+               new CacheLoader<Key, SshClient>(){
               @Override
-              public SshClient apply(Key key) {
+              public SshClient load(Key key) {
                 return new LogSshClient(key);
               }
 
@@ -261,13 +204,13 @@ public class DryRunModule extends Abstra
 
       @Override
       public SshClient create(final IPSocket socket, Credentials loginCreds) {
-        return clientMap.get(new Key(socket, loginCreds, find(nodes.values(),
+        return clientMap.getUnchecked(new Key(socket, loginCreds, find(nodes.values(),
             new NodeHasAddress(socket.getAddress()))));
       }
 
       @Override
       public SshClient create(IPSocket socket, LoginCredentials credentials) {
-        return clientMap.get(new Key(socket, credentials, find(nodes.values(),
+        return clientMap.getUnchecked(new Key(socket, credentials, find(nodes.values(),
             new NodeHasAddress(socket.getAddress()))));
       }
     }
@@ -305,6 +248,9 @@ public class DryRunModule extends Abstra
         } else {
           exec = new ExecResponse("", "", 0);
         }
+      } else if (script.endsWith(" exitstatus")) {
+        // show return code is 0
+        exec = new ExecResponse("0", "", 0);
       } else {
         exec = new ExecResponse("", "", 0);
       }

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/service/FirewallManager.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/service/FirewallManager.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/service/FirewallManager.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/service/FirewallManager.java Tue Mar 27 22:00:24 2012
@@ -27,8 +27,8 @@ import java.util.Set;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.whirr.Cluster;
-import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.Cluster.Instance;
+import org.apache.whirr.ClusterSpec;
 import org.jclouds.aws.util.AWSUtils;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.ec2.EC2Client;

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/RunUrlStatement.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/RunUrlStatement.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/RunUrlStatement.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/RunUrlStatement.java Tue Mar 27 22:00:24 2012
@@ -18,10 +18,6 @@
 
 package org.apache.whirr.service.jclouds;
 
-import com.google.common.base.Joiner;
-import com.google.common.base.Objects;
-import com.google.common.collect.ImmutableSet;
-
 import java.io.IOException;
 import java.net.HttpURLConnection;
 import java.net.URL;
@@ -34,6 +30,10 @@ import org.jclouds.scriptbuilder.domain.
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Joiner;
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableSet;
+
 public class RunUrlStatement implements Statement {
 
   private static final Logger LOG =

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=1306027&r1=1306026&r2=1306027&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 Tue Mar 27 22:00:24 2012
@@ -18,151 +18,56 @@
 
 package org.apache.whirr.service.jclouds;
 
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
 import org.apache.whirr.Cluster.Instance;
 import org.apache.whirr.ClusterSpec;
-import org.jclouds.scriptbuilder.ScriptBuilder;
-import org.jclouds.scriptbuilder.domain.OsFamily;
+import org.jclouds.scriptbuilder.InitScript;
 import org.jclouds.scriptbuilder.domain.Statement;
-import org.jclouds.scriptbuilder.util.Utils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import static org.jclouds.scriptbuilder.domain.Statements.exec;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 
 public class StatementBuilder {
-
-  private static final Logger LOG =
-    LoggerFactory.getLogger(StatementBuilder.class);
-
-  class EmptyStatement implements Statement {
-
-    @Override
-    public Iterable<String> functionDependencies(OsFamily osFamily) {
-      return ImmutableSet.of();
-    }
-
-    @Override
-    public String render(OsFamily osFamily) {
-      return "";
-    }
-  }
-
-  class ConsolidatedStatement implements Statement {
-
-    private ClusterSpec clusterSpec;
-    private Instance instance;
-
-    public ConsolidatedStatement(ClusterSpec clusterSpec, Instance instance) {
-      this.clusterSpec = clusterSpec;
-      this.instance = instance;
-    }
-
-    @Override
-    public Iterable<String> functionDependencies(OsFamily family) {
-      List<String> functions = Lists.newArrayList();
-      for (Statement statement : statements) {
-        Iterables.addAll(functions, statement.functionDependencies(family));
-      }
-      return functions;
-    }
-
-    @Override
-    public String render(OsFamily family) {
-      ScriptBuilder scriptBuilder = new ScriptBuilder();
-      Map<String, String> metadataMap = Maps.newLinkedHashMap();
-
-      addEnvironmentVariablesFromClusterSpec(metadataMap);
-      addDefaultEnvironmentVariablesForInstance(metadataMap, instance);
-      metadataMap.putAll(exports);
-      addPerInstanceCustomEnvironmentVariables(metadataMap, instance);
-
-      String writeVariableExporters = Utils.writeVariableExporters(metadataMap, family);
-      scriptBuilder.addStatement(exec(writeVariableExporters));
-
-      for (Statement statement : statements) {
-        scriptBuilder.addStatement(statement);
-      }
-
-      return scriptBuilder.render(family);
-    }
-
-    private void addPerInstanceCustomEnvironmentVariables(Map<String, String> metadataMap, Instance instance) {
-      if (instance != null && exportsByInstanceId.containsKey(instance.getId())) {
-        metadataMap.putAll(exportsByInstanceId.get(instance.getId()));
-      }
-    }
-
-    private void addDefaultEnvironmentVariablesForInstance(Map<String, String> metadataMap, Instance instance) {
-      metadataMap.putAll(
-        ImmutableMap.of(
-          "clusterName", clusterSpec.getClusterName(),
-          "cloudProvider", clusterSpec.getProvider()
-        )
-      );
-      if (instance != null) {
-        metadataMap.putAll(
-          ImmutableMap.of(
-            "roles", Joiner.on(",").join(instance.getRoles()),
-            "publicIp", instance.getPublicIp(),
-            "privateIp", instance.getPrivateIp()
-          )
-        );
-        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);
-          }
-        }
-      }
-    }
-
-    private void addEnvironmentVariablesFromClusterSpec(Map<String, String> metadataMap) {
-      for (Iterator<?> it = clusterSpec.getConfiguration().getKeys("whirr.env"); it.hasNext(); ) {
-        String key = (String) it.next();
-        String value = clusterSpec.getConfiguration().getString(key);
-        metadataMap.put(key.substring("whirr.env.".length()), value);
-      }
-    }
-  }
-
+  protected String name;
+  protected List<Statement> initStatements = Lists.newArrayList();
   protected List<Statement> statements = Lists.newArrayList();
   protected Map<String, String> exports = Maps.newLinkedHashMap();
   protected Map<String, Map<String, String>> exportsByInstanceId = Maps.newHashMap();
 
-  public void addStatement(Statement statement) {
+  public StatementBuilder addStatement(Statement statement) {
     if (!statements.contains(statement)) {
       statements.add(statement);
     }
+    return this;
   }
-
-  public void addStatements(Statement... statements) {
+  
+  public StatementBuilder addStatement(int pos, Statement statement) {
+    if (!statements.contains(statement)) {
+      statements.add(pos, statement);
+    }
+    return this;
+  }
+  public StatementBuilder addStatements(Statement... statements) {
     for (Statement statement : statements) {
       addStatement(statement);
     }
+    return this;
+  }
+  
+  public StatementBuilder name(String name) {
+    this.name = name;
+    return this;
   }
 
-  public void addExport(String key, String value) {
+  public StatementBuilder addExport(String key, String value) {
     exports.put(key, value);
+    return this;
   }
 
-  public void addExportPerInstance(String instanceId, String key, String value) {
+  public StatementBuilder addExportPerInstance(String instanceId, String key, String value) {
     if (exportsByInstanceId.containsKey(instanceId)) {
       exportsByInstanceId.get(instanceId).put(key, value);
     } else {
@@ -170,6 +75,7 @@ public class StatementBuilder {
       pairs.put(key, value);
       exportsByInstanceId.put(instanceId, pairs);
     }
+    return this;
   }
 
   public boolean isEmpty() {
@@ -180,11 +86,21 @@ public class StatementBuilder {
     return build(clusterSpec, null);
   }
 
+  /**
+   * 
+   * @param clusterSpec
+   * @param instance
+   * @return
+   */
   public Statement build(ClusterSpec clusterSpec, Instance instance) {
     if (statements.size() == 0) {
-      return new EmptyStatement();
+      throw new NoSuchElementException("no statements configured");
     } else {
-      return new ConsolidatedStatement(clusterSpec, instance);
+      InitScript.Builder builder = InitScript.builder();
+      builder.name(name);
+      builder.exportVariables(new VariablesToExport(exports, exportsByInstanceId, clusterSpec, instance).get());
+      builder.run(statements);
+      return builder.build();
     }
   }
 

Added: whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/VariablesToExport.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/VariablesToExport.java?rev=1306027&view=auto
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/VariablesToExport.java (added)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/service/jclouds/VariablesToExport.java Tue Mar 27 22:00:24 2012
@@ -0,0 +1,102 @@
+/**
+ * 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.service.jclouds;
+
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.whirr.Cluster.Instance;
+import org.apache.whirr.ClusterSpec;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+class VariablesToExport implements Supplier<Map<String, String>> {
+
+  private static final Logger LOG =
+    LoggerFactory.getLogger(StatementBuilder.class);
+   
+  private final Map<String, String> exports;
+  private final Map<String, Map<String, String>> exportsByInstanceId;
+  private final ClusterSpec clusterSpec;
+  private final Instance instance;
+
+  public VariablesToExport(Map<String, String> exports, Map<String, Map<String, String>> exportsByInstanceId,
+      ClusterSpec clusterSpec, Instance instance) {
+    this.exports = ImmutableMap.copyOf(exports);
+    this.exportsByInstanceId = ImmutableMap.copyOf(exportsByInstanceId);
+    this.clusterSpec = clusterSpec;
+    this.instance = instance;
+  }
+
+  @Override
+  public Map<String, String> get() {
+    Map<String, String> metadataMap = Maps.newLinkedHashMap();
+
+    addEnvironmentVariablesFromClusterSpec(metadataMap);
+    addDefaultEnvironmentVariablesForInstance(metadataMap, instance);
+    metadataMap.putAll(exports);
+    addPerInstanceCustomEnvironmentVariables(metadataMap, instance);
+
+    return metadataMap;
+  }
+
+  private void addPerInstanceCustomEnvironmentVariables(Map<String, String> metadataMap, Instance instance) {
+    if (instance != null && exportsByInstanceId.containsKey(instance.getId())) {
+      metadataMap.putAll(exportsByInstanceId.get(instance.getId()));
+    }
+  }
+
+  private void addDefaultEnvironmentVariablesForInstance(Map<String, String> metadataMap, Instance instance) {
+    if (clusterSpec.getClusterName() != null)
+      metadataMap.put("clusterName", clusterSpec.getClusterName());
+    if (clusterSpec.getProvider() != null)
+      metadataMap.put("cloudProvider", clusterSpec.getProvider());
+    if (instance != null) {
+      metadataMap.put("roles", Joiner.on(",").join(instance.getRoles()));
+      if (instance.getPublicIp() != null)
+        metadataMap.put("publicIp", instance.getPublicIp());
+      if (instance.getPrivateIp() != null)
+         metadataMap.put("privateIp", instance.getPrivateIp());
+      if (!clusterSpec.isStub()) {
+        try {
+          if (instance.getPublicIp() != null)
+            metadataMap.put("publicHostName", instance.getPublicHostName());
+          if (instance.getPrivateIp() != null)
+            metadataMap.put("privateHostName", instance.getPrivateHostName());
+        } catch (IOException e) {
+          LOG.warn("Could not resolve hostname for " + instance, e);
+        }
+      }
+    }
+  }
+
+  private void addEnvironmentVariablesFromClusterSpec(Map<String, String> metadataMap) {
+    for (Iterator<?> it = clusterSpec.getConfiguration().getKeys("whirr.env"); it.hasNext(); ) {
+      String key = (String) it.next();
+      String value = clusterSpec.getConfiguration().getString(key);
+      metadataMap.put(key.substring("whirr.env.".length()), value);
+    }
+  }
+}

Modified: 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=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/state/BlobClusterStateStore.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/state/BlobClusterStateStore.java Tue Mar 27 22:00:24 2012
@@ -18,6 +18,10 @@
 
 package org.apache.whirr.state;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.io.IOException;
+
 import org.apache.commons.io.IOUtils;
 import org.apache.whirr.Cluster;
 import org.apache.whirr.ClusterSpec;
@@ -28,10 +32,6 @@ 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

Modified: 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=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStore.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStore.java Tue Mar 27 22:00:24 2012
@@ -23,15 +23,16 @@ 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;
 
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Sets;
+
 /**
  * Interface for cluster state storage facilities.
  * 

Modified: 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=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStoreFactory.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/state/ClusterStateStoreFactory.java Tue Mar 27 22:00:24 2012
@@ -18,15 +18,16 @@
 
 package org.apache.whirr.state;
 
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
+import java.io.IOException;
+
 import org.apache.whirr.Cluster;
 import org.apache.whirr.ClusterSpec;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 
 /**
  * A factory for ClusterStateStores.
@@ -56,7 +57,7 @@ public class ClusterStateStoreFactory {
     }
   }
 
-  private Cache<ClusterSpec, ClusterStateStore> storeCache = CacheBuilder.newBuilder().build(
+  private LoadingCache<ClusterSpec, ClusterStateStore> storeCache = CacheBuilder.newBuilder().build(
     new CacheLoader<ClusterSpec, ClusterStateStore>() {
       @Override
       public ClusterStateStore load(ClusterSpec spec) throws Exception {

Modified: 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=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/state/FileClusterStateStore.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/state/FileClusterStateStore.java Tue Mar 27 22:00:24 2012
@@ -17,6 +17,8 @@
  */
 package org.apache.whirr.state;
 
+import static com.google.common.base.Preconditions.checkNotNull;
+
 import java.io.File;
 import java.io.IOException;
 
@@ -30,8 +32,6 @@ 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")

Modified: 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=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/state/MemoryClusterStateStore.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/state/MemoryClusterStateStore.java Tue Mar 27 22:00:24 2012
@@ -18,10 +18,10 @@
 
 package org.apache.whirr.state;
 
-import org.apache.whirr.Cluster;
-
 import java.io.IOException;
 
+import org.apache.whirr.Cluster;
+
 /**
  * Memory only cluster state storage useful for testing
  */

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/util/BlobCache.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/util/BlobCache.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/util/BlobCache.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/util/BlobCache.java Tue Mar 27 22:00:24 2012
@@ -18,8 +18,16 @@
 
 package org.apache.whirr.util;
 
-import com.google.common.base.Function;
-import com.google.common.collect.Maps;
+import static org.jclouds.blobstore.options.PutOptions.Builder.multipart;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Map;
+import java.util.Set;
+
 import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.lang.RandomStringUtils;
 import org.apache.whirr.ClusterSpec;
@@ -35,15 +43,8 @@ import org.jclouds.scriptbuilder.domain.
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Map;
-import java.util.Set;
-
-import static org.jclouds.blobstore.options.PutOptions.Builder.multipart;
+import com.google.common.base.Function;
+import com.google.common.collect.Maps;
 
 public class BlobCache {
 

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/util/KeyPair.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/util/KeyPair.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/util/KeyPair.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/util/KeyPair.java Tue Mar 27 22:00:24 2012
@@ -18,11 +18,6 @@
 
 package org.apache.whirr.util;
 
-import com.google.common.collect.ImmutableMap;
-import com.google.common.io.Files;
-import com.jcraft.jsch.JSch;
-import com.jcraft.jsch.JSchException;
-
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -40,6 +35,11 @@ import org.apache.commons.ssl.PKCS8Key;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.Files;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+
 
 /**
  * A convenience class for generating an RSA key pair.

Modified: whirr/trunk/core/src/main/java/org/apache/whirr/util/Utils.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/main/java/org/apache/whirr/util/Utils.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/main/java/org/apache/whirr/util/Utils.java (original)
+++ whirr/trunk/core/src/main/java/org/apache/whirr/util/Utils.java Tue Mar 27 22:00:24 2012
@@ -18,14 +18,26 @@
 
 package org.apache.whirr.util;
 
+import java.io.PrintStream;
+import java.util.Map;
+
 import org.apache.whirr.Cluster;
 import org.apache.whirr.Cluster.Instance;
 import org.apache.whirr.ClusterSpec;
 
-import java.io.PrintStream;
+import com.google.common.base.Functions;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 
 public class Utils {
-
+  /**
+   * converts a map to a loading cache.
+   */
+  public static <K, V> LoadingCache<K, V> convertMapToLoadingCache(Map<K, V> in) {
+    return CacheBuilder.newBuilder().build(CacheLoader.from(Functions.forMap(in)));
+  }
+ 
   /**
    * Prints ssh commands that can be used to login into the nodes
    *

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/ClusterSpecTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/ClusterSpecTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/ClusterSpecTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/ClusterSpecTest.java Tue Mar 27 22:00:24 2012
@@ -23,13 +23,6 @@ import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import com.google.common.io.Files;
-import com.jcraft.jsch.JSchException;
-
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
@@ -48,6 +41,13 @@ import org.apache.whirr.util.KeyPair;
 import org.junit.Assert;
 import org.junit.Test;
 
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import com.google.common.io.Files;
+import com.jcraft.jsch.JSchException;
+
 public class ClusterSpecTest {
 
   @Test

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/ClusterTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/ClusterTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/ClusterTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/ClusterTest.java Tue Mar 27 22:00:24 2012
@@ -18,20 +18,21 @@
 
 package org.apache.whirr;
 
-import com.google.common.collect.Sets;
-import org.jclouds.domain.Credentials;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.util.NoSuchElementException;
-import java.util.Set;
-
 import static org.apache.whirr.RolePredicates.role;
 import static org.apache.whirr.RolePredicates.withIds;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.fail;
 
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.jclouds.domain.Credentials;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.common.collect.Sets;
+
 public class ClusterTest {
 
   private Cluster cluster;

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/actions/BootstrapClusterActionTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/actions/BootstrapClusterActionTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/actions/BootstrapClusterActionTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/actions/BootstrapClusterActionTest.java Tue Mar 27 22:00:24 2012
@@ -18,10 +18,22 @@
 
 package org.apache.whirr.actions;
 
-import com.google.common.base.Function;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import static org.apache.whirr.util.Utils.convertMapToLoadingCache;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.Stack;
+import java.util.concurrent.atomic.AtomicInteger;
+
 import org.apache.commons.configuration.CompositeConfiguration;
 import org.apache.commons.configuration.Configuration;
 import org.apache.commons.configuration.PropertiesConfiguration;
@@ -35,42 +47,31 @@ import org.jclouds.compute.ComputeServic
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.compute.RunNodesException;
 import org.jclouds.compute.domain.Hardware;
+import org.jclouds.compute.domain.HardwareBuilder;
 import org.jclouds.compute.domain.Image;
+import org.jclouds.compute.domain.ImageBuilder;
 import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.compute.domain.NodeMetadataBuilder;
 import org.jclouds.compute.domain.NodeState;
 import org.jclouds.compute.domain.OperatingSystem;
-import org.jclouds.compute.domain.Processor;
 import org.jclouds.compute.domain.Template;
 import org.jclouds.compute.domain.TemplateBuilder;
-import org.jclouds.compute.domain.Volume;
-import org.jclouds.compute.domain.internal.HardwareImpl;
-import org.jclouds.compute.domain.internal.ImageImpl;
-import org.jclouds.compute.domain.internal.NodeMetadataImpl;
 import org.jclouds.compute.domain.internal.TemplateImpl;
 import org.jclouds.compute.options.TemplateOptions;
-import org.jclouds.domain.Credentials;
 import org.jclouds.domain.Location;
+import org.jclouds.domain.LocationBuilder;
 import org.jclouds.domain.LocationScope;
-import org.jclouds.domain.internal.LocationImpl;
+import org.jclouds.domain.LoginCredentials;
 import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.IOException;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.Stack;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import com.google.common.base.Function;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 
 public class BootstrapClusterActionTest {
 
@@ -102,13 +103,14 @@ public class BootstrapClusterActionTest 
     dntt.add("hadoop-tasktracker");
 
     TestNodeStarterFactory nodeStarterFactory = null;
-    
+
     ClusterActionHandler handler = mock(ClusterActionHandler.class);     
-    Map<String, ClusterActionHandler> handlerMap = Maps.newHashMap();
-    handlerMap.put("hadoop-jobtracker", handler);
-    handlerMap.put("hadoop-namenode", handler);
-    handlerMap.put("hadoop-datanode", handler);
-    handlerMap.put("hadoop-tasktracker", handler);
+    LoadingCache<String, ClusterActionHandler> handlerMap = convertMapToLoadingCache(
+      ImmutableMap.<String, ClusterActionHandler>builder()
+        .put("hadoop-jobtracker", handler)
+        .put("hadoop-namenode", handler)
+        .put("hadoop-datanode", handler)
+        .put("hadoop-tasktracker", handler).build());
 
     Function<ClusterSpec, ComputeServiceContext> getCompute = mock(Function.class);
     ComputeServiceContext serviceContext = mock(ComputeServiceContext.class);
@@ -169,12 +171,13 @@ public class BootstrapClusterActionTest 
 
     TestNodeStarterFactory nodeStarterFactory = null;
     
-    ClusterActionHandler handler = mock(ClusterActionHandler.class);     
-    Map<String, ClusterActionHandler> handlerMap = Maps.newHashMap();
-    handlerMap.put("hadoop-jobtracker", handler);
-    handlerMap.put("hadoop-namenode", handler);
-    handlerMap.put("hadoop-datanode", handler);
-    handlerMap.put("hadoop-tasktracker", handler);
+    ClusterActionHandler handler = mock(ClusterActionHandler.class);   
+    LoadingCache<String, ClusterActionHandler> handlerMap = convertMapToLoadingCache(
+      ImmutableMap.<String, ClusterActionHandler>builder()
+        .put("hadoop-jobtracker", handler)
+        .put("hadoop-namenode", handler)
+        .put("hadoop-datanode", handler)
+        .put("hadoop-tasktracker", handler).build());
 
     Function<ClusterSpec, ComputeServiceContext> getCompute = mock(Function.class);
     ComputeServiceContext serviceContext = mock(ComputeServiceContext.class);
@@ -284,23 +287,22 @@ public class BootstrapClusterActionTest 
     
     @Override
     public Set<NodeMetadata> call() throws Exception {
-      Map<String, String> userMetadata = Maps.newHashMap();
-      Map<String, Object> locationMetadata = Maps.newHashMap();
-      Location location = new LocationImpl(LocationScope.ZONE, "loc", "test location", 
-        null, new ArrayList<String>(), locationMetadata);
+      Location location = new LocationBuilder().scope(LocationScope.ZONE)
+            .id("loc").description("test location").build();
       Set<String> addresses = Sets.newHashSet();
       addresses.add("10.0.0.1");
-      Credentials loginCredentials = new Credentials("id", "cred");
+      LoginCredentials loginCredentials = LoginCredentials.builder().user("id").privateKey("cred").build();
 
       Set<NodeMetadata> nodes = Sets.newHashSet();
       Map<?, Exception> executionExceptions = Maps.newHashMap();
       Map<NodeMetadata, Throwable> failedNodes = Maps.newHashMap();
       for (int i = 0; i < num; i++) {
-        NodeMetadata nodeMeta = new NodeMetadataImpl(
-            "ec2", "" + roles + id, "nodeId" + id + i, 
-            location, new URI("http://node" + i),
-            userMetadata, ImmutableSet.<String>of(), null, null, null, null, NodeState.RUNNING, 22,
-            addresses, addresses, null, loginCredentials, "hostname");
+        NodeMetadata nodeMeta = new NodeMetadataBuilder()
+            .providerId("ec2").name("" + roles + id).id("nodeId" + id + i)
+            .location(location).uri(URI.create("http://node" + i))
+            .state(NodeState.RUNNING).privateAddresses(addresses)
+            .publicAddresses(addresses)
+            .credentials(loginCredentials).hostname("hostname").build();
         if (i < only) {
           nodes.add(nodeMeta);
           LOG.info("{} - Node successfully started: {}", roles, nodeMeta.getId());
@@ -310,12 +312,11 @@ public class BootstrapClusterActionTest 
         }
       }
       if (failedNodes.size() > 0) {
-        Image image = new ImageImpl("ec2", "test", "testId", location, new URI("http://node"),
-            userMetadata, ImmutableSet.<String>of(), new OperatingSystem(null, null, null, null, "op", true), 
-            "description", null, null, loginCredentials);
-        Hardware hardware = new HardwareImpl("ec2", "test", "testId", location, new URI("http://node"),
-                userMetadata, ImmutableSet.<String>of(), new ArrayList<Processor>(), 1,
-                new ArrayList<Volume>(), null);
+        Image image = new ImageBuilder().providerId("ec2").name("test").id("testId").location(location)
+              .uri(URI.create("http://node")).operatingSystem(OperatingSystem.builder().description("op").build())
+              .description("description").defaultCredentials(loginCredentials).build();
+        Hardware hardware = new HardwareBuilder().providerId("ec2").name("test").id("testId").location(location)
+              .uri(URI.create("http://node")).ram(1).hypervisor("xen").build();
         Template template = new TemplateImpl(image, hardware, location, TemplateOptions.NONE);
         throw new RunNodesException("tag" + id, num, template, nodes, executionExceptions, failedNodes);
       }
@@ -350,7 +351,7 @@ public class BootstrapClusterActionTest 
     when(puppetHandlerFactory.create("module::manifest")).thenReturn(handler);
     when(handler.getRole()).thenReturn("something-else");
 
-    Map<String, ClusterActionHandler> handlerMap = new HandlerMapFactory().create(ImmutableSet.of(puppetHandlerFactory),
+    LoadingCache<String, ClusterActionHandler> handlerMap = new HandlerMapFactory().create(ImmutableSet.of(puppetHandlerFactory),
           ImmutableSet.of(handler));
 
     Function<ClusterSpec, ComputeServiceContext> getCompute = mock(Function.class);
@@ -410,7 +411,7 @@ public class BootstrapClusterActionTest 
     when(puppetHandlerFactory.getRolePrefix()).thenReturn("puppet");
     when(handler.getRole()).thenReturn("something-else");
 
-    Map<String, ClusterActionHandler> handlerMap = new HandlerMapFactory().create(
+    LoadingCache<String, ClusterActionHandler> handlerMap = new HandlerMapFactory().create(
       ImmutableSet.of(puppetHandlerFactory), ImmutableSet.of(handler));
 
     Function<ClusterSpec, ComputeServiceContext> getCompute = mock(Function.class);

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/actions/CleanupClusterActionTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/actions/CleanupClusterActionTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/actions/CleanupClusterActionTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/actions/CleanupClusterActionTest.java Tue Mar 27 22:00:24 2012
@@ -18,13 +18,14 @@
 
 package org.apache.whirr.actions;
 
-import com.google.common.base.Function;
+import java.util.Set;
+
 import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.service.ClusterActionHandler;
 import org.jclouds.compute.ComputeServiceContext;
 
-import java.util.Map;
-import java.util.Set;
+import com.google.common.base.Function;
+import com.google.common.cache.LoadingCache;
 
 public class CleanupClusterActionTest extends ScriptBasedClusterActionTest<CleanupClusterAction> {
 
@@ -35,7 +36,7 @@ public class CleanupClusterActionTest ex
   
   @Override
   public CleanupClusterAction newClusterActionInstance(
-      Function<ClusterSpec, ComputeServiceContext> getCompute, Map<String, ClusterActionHandler> handlerMap,
+      Function<ClusterSpec, ComputeServiceContext> getCompute, LoadingCache<String, ClusterActionHandler> handlerMap,
       Set<String> targetRoles, Set<String> targetInstanceIds
   ) {
     return new CleanupClusterAction(getCompute, handlerMap, targetRoles, targetInstanceIds);

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/actions/ConfigureServicesActionTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/actions/ConfigureServicesActionTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/actions/ConfigureServicesActionTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/actions/ConfigureServicesActionTest.java Tue Mar 27 22:00:24 2012
@@ -18,13 +18,14 @@
 
 package org.apache.whirr.actions;
 
-import com.google.common.base.Function;
+import java.util.Set;
+
 import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.service.ClusterActionHandler;
 import org.jclouds.compute.ComputeServiceContext;
 
-import java.util.Map;
-import java.util.Set;
+import com.google.common.base.Function;
+import com.google.common.cache.LoadingCache;
 
 public class ConfigureServicesActionTest extends ScriptBasedClusterActionTest<ConfigureServicesAction> {
 
@@ -35,7 +36,7 @@ public class ConfigureServicesActionTest
   
   @Override
   public ConfigureServicesAction newClusterActionInstance(
-      Function<ClusterSpec, ComputeServiceContext> getCompute, Map<String, ClusterActionHandler> handlerMap,
+      Function<ClusterSpec, ComputeServiceContext> getCompute, LoadingCache<String, ClusterActionHandler> handlerMap,
       Set<String> targetRoles, Set<String> targetInstanceIds
   ) {
     return new ConfigureServicesAction(getCompute, handlerMap, targetRoles, targetInstanceIds);

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/actions/ScriptBasedClusterActionTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/actions/ScriptBasedClusterActionTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/actions/ScriptBasedClusterActionTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/actions/ScriptBasedClusterActionTest.java Tue Mar 27 22:00:24 2012
@@ -18,11 +18,16 @@
 
 package org.apache.whirr.actions;
 
-import com.google.common.base.Function;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
+import static com.google.common.collect.Sets.newHashSet;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.jclouds.scriptbuilder.domain.Statements.exec;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+import java.util.Set;
+
 import org.apache.whirr.Cluster;
 import org.apache.whirr.ClusterController;
 import org.apache.whirr.ClusterSpec;
@@ -32,24 +37,19 @@ import org.apache.whirr.service.ClusterA
 import org.apache.whirr.service.ClusterActionHandler;
 import org.apache.whirr.service.ClusterActionHandlerSupport;
 import org.apache.whirr.service.ComputeCache;
-import org.apache.whirr.service.DryRunModule;
+import org.apache.whirr.service.DryRunModule.DryRun;
 import org.jclouds.compute.ComputeServiceContext;
-import org.jclouds.compute.callables.RunScriptOnNode;
+import org.jclouds.compute.events.StatementOnNode;
 import org.jclouds.scriptbuilder.domain.OsFamily;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static com.google.common.collect.Sets.newHashSet;
-import static org.apache.whirr.service.DryRunModule.DryRun;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.jclouds.scriptbuilder.domain.Statements.exec;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
 
 public abstract class ScriptBasedClusterActionTest<T extends ScriptBasedClusterAction> {
 
@@ -88,7 +88,7 @@ public abstract class ScriptBasedCluster
   }
 
   private final static HandlerMapFactory HANDLER_MAP_FACTORY = new HandlerMapFactory();
-  private final static Map<String, ClusterActionHandler> HANDLERMAP = HANDLER_MAP_FACTORY.create();
+  private final static LoadingCache<String, ClusterActionHandler> HANDLERMAP = HANDLER_MAP_FACTORY.create();
   private final static Set<String> EMPTYSET = ImmutableSet.of();
 
   private ClusterSpec clusterSpec;
@@ -111,8 +111,6 @@ public abstract class ScriptBasedCluster
 
     ClusterController controller = new ClusterController();
     cluster = controller.launchCluster(clusterSpec);
-
-    DryRunModule.resetDryRun();
   }
 
   private InstanceTemplate newInstanceTemplate(String... roles) {
@@ -122,78 +120,87 @@ public abstract class ScriptBasedCluster
   @Test
   public void testActionIsExecutedOnAllRelevantNodes() throws Exception {
     T action = newClusterActionInstance(EMPTYSET, EMPTYSET);
+    DryRun dryRun = getDryRunForAction(action).reset();
+    
     action.execute(clusterSpec, cluster);
-
-    List<RunScriptOnNode> executions = DryRunModule.getDryRun()
-        .getTotallyOrderedExecutions();
+    
+    List<StatementOnNode> executions = dryRun.getTotallyOrderedExecutions();
 
     // only 2 out of 3 because one instance has only noop
     assertThat(executions.size(), is(2));
   }
 
+  public DryRun getDryRunForAction(T action) {
+    return action.getCompute().apply(clusterSpec).utils().injector().getInstance(DryRun.class);
+  }
+
   @Test
   public void testFilterScriptExecutionByRole() throws Exception {
     String instanceId = getInstaceForRole(cluster, "noop2").getId();
     T action = newClusterActionInstance(ImmutableSet.of("noop2"), EMPTYSET);
+    DryRun dryRun = getDryRunForAction(action).reset();
+    
     action.execute(clusterSpec, cluster);
 
-    List<RunScriptOnNode> executions = DryRunModule.getDryRun()
-        .getTotallyOrderedExecutions();
+    List<StatementOnNode> executions = dryRun.getTotallyOrderedExecutions();
 
     assertThat(executions.size(), is(1));
     assertHasRole(executions.get(0), cluster, "noop2");
     assertEquals(executions.get(0).getNode().getId(), instanceId);
 
-    assertAnyStatementContains(DryRun.INSTANCE, "noop2-" + getActionName());
-    assertNoStatementContains(DryRun.INSTANCE, "noop1-" + getActionName(), "noop3-" + getActionName());
+    assertAnyStatementContains(dryRun, "noop2-" + getActionName());
+    assertNoStatementContains(dryRun, "noop1-" + getActionName(), "noop3-" + getActionName());
   }
 
   @Test
   public void testFilterScriptExecutionByInstanceId() throws Exception {
     String instanceId = getInstaceForRole(cluster, "noop3").getId();
     T action = newClusterActionInstance(EMPTYSET, newHashSet(instanceId));
-    action.execute(clusterSpec, cluster);
+    DryRun dryRun = getDryRunForAction(action).reset();
 
-    List<RunScriptOnNode> executions = DryRunModule.getDryRun()
-        .getTotallyOrderedExecutions();
+    action.execute(clusterSpec, cluster);
 
+    List<StatementOnNode> executions = dryRun.getTotallyOrderedExecutions();
+    
     assertThat(executions.size(), is(1));
     assertHasRole(executions.get(0), cluster, "noop3");
     assertEquals(executions.get(0).getNode().getId(), instanceId);
 
-    assertAnyStatementContains(DryRun.INSTANCE, "noop1-" + getActionName(), "noop3-" + getActionName());
+    assertAnyStatementContains(dryRun, "noop1-" + getActionName(), "noop3-" + getActionName());
   }
 
   @Test
   public void testFilterScriptExecutionByRoleAndInstanceId() throws Exception {
     String instanceId = getInstaceForRole(cluster, "noop1").getId();
     T action = newClusterActionInstance(newHashSet("noop1"), newHashSet(instanceId));
-    action.execute(clusterSpec, cluster);
+    DryRun dryRun = getDryRunForAction(action).reset();
 
-    List<RunScriptOnNode> executions = DryRunModule.getDryRun()
-        .getTotallyOrderedExecutions();
+    action.execute(clusterSpec, cluster);
 
+    List<StatementOnNode> executions = dryRun.getTotallyOrderedExecutions();
+    
     assertThat(executions.size(), is(1));
     assertHasRole(executions.get(0), cluster, "noop1");
     assertEquals(executions.get(0).getNode().getId(), instanceId);
 
-    assertAnyStatementContains(DryRun.INSTANCE, "noop1-" + getActionName());
-    assertNoStatementContains(DryRun.INSTANCE, "noop2-" + getActionName(), "noop3-" + getActionName());
+    assertAnyStatementContains(dryRun, "noop1-" + getActionName());
+    assertNoStatementContains(dryRun, "noop2-" + getActionName(), "noop3-" + getActionName());
   }
 
   @Test
   public void testNoScriptExecutionsForNoop() throws Exception {
     T action = newClusterActionInstance(ImmutableSet.of("noop"), EMPTYSET);
+    DryRun dryRun = getDryRunForAction(action).reset();
+    
     action.execute(clusterSpec, cluster);
 
-    List<RunScriptOnNode> executions = DryRunModule.getDryRun()
-        .getTotallyOrderedExecutions();
-
+    List<StatementOnNode> executions = dryRun.getTotallyOrderedExecutions();
+    
     // empty because noop does not emit any statements
     assertThat(executions.size(), is(0));
   }
 
-  private void assertHasRole(final RunScriptOnNode node, Cluster cluster, String... roles) {
+  private void assertHasRole(final StatementOnNode node, Cluster cluster, String... roles) {
     Cluster.Instance instance = getInstanceForNode(node, cluster);
     for (String role : roles) {
       assertTrue(instance.getRoles().contains(role));
@@ -202,7 +209,7 @@ public abstract class ScriptBasedCluster
 
   private void assertAnyStatementContains(DryRun dryRun, String... values) {
     Set<String> toCheck = newHashSet(values);
-    for (RunScriptOnNode node : dryRun.getTotallyOrderedExecutions()) {
+    for (StatementOnNode node : dryRun.getTotallyOrderedExecutions()) {
       String statement = node.getStatement().render(OsFamily.UNIX);
       for (String term : ImmutableSet.copyOf(toCheck)) {
         if (statement.contains(term)) {
@@ -216,7 +223,7 @@ public abstract class ScriptBasedCluster
 
   private void assertNoStatementContains(DryRun dryRun, String... values) {
     Set<String> foundTerms = newHashSet();
-    for (RunScriptOnNode node : dryRun.getTotallyOrderedExecutions()) {
+    for (StatementOnNode node : dryRun.getTotallyOrderedExecutions()) {
       String statement = node.getStatement().render(OsFamily.UNIX);
       for (String term : values) {
         if (statement.contains(term)) {
@@ -238,7 +245,7 @@ public abstract class ScriptBasedCluster
         });
   }
 
-  private Cluster.Instance getInstanceForNode(final RunScriptOnNode node, Cluster cluster) {
+  private Cluster.Instance getInstanceForNode(final StatementOnNode node, Cluster cluster) {
     return Iterables.find(cluster.getInstances(),
         new Predicate<Cluster.Instance>() {
           @Override
@@ -254,7 +261,7 @@ public abstract class ScriptBasedCluster
 
   public abstract T newClusterActionInstance(
       Function<ClusterSpec, ComputeServiceContext> getCompute,
-      Map<String, ClusterActionHandler> handlerMap,
+      LoadingCache<String, ClusterActionHandler> handlerMap,
       Set<String> targetRoles,
       Set<String> targetInstanceIds
   );

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/actions/StartServicesActionTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/actions/StartServicesActionTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/actions/StartServicesActionTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/actions/StartServicesActionTest.java Tue Mar 27 22:00:24 2012
@@ -18,13 +18,14 @@
 
 package org.apache.whirr.actions;
 
-import com.google.common.base.Function;
+import java.util.Set;
+
 import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.service.ClusterActionHandler;
 import org.jclouds.compute.ComputeServiceContext;
 
-import java.util.Map;
-import java.util.Set;
+import com.google.common.base.Function;
+import com.google.common.cache.LoadingCache;
 
 public class StartServicesActionTest extends ScriptBasedClusterActionTest<StartServicesAction> {
 
@@ -35,7 +36,7 @@ public class StartServicesActionTest ext
   
   @Override
   public StartServicesAction newClusterActionInstance(
-      Function<ClusterSpec, ComputeServiceContext> getCompute, Map<String, ClusterActionHandler> handlerMap,
+      Function<ClusterSpec, ComputeServiceContext> getCompute, LoadingCache<String, ClusterActionHandler> handlerMap,
       Set<String> targetRoles, Set<String> targetInstanceIds
   ) {
     return new StartServicesAction(getCompute, handlerMap, targetRoles, targetInstanceIds);

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/actions/StopServicesActionTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/actions/StopServicesActionTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/actions/StopServicesActionTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/actions/StopServicesActionTest.java Tue Mar 27 22:00:24 2012
@@ -18,13 +18,14 @@
 
 package org.apache.whirr.actions;
 
-import com.google.common.base.Function;
+import java.util.Set;
+
 import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.service.ClusterActionHandler;
 import org.jclouds.compute.ComputeServiceContext;
 
-import java.util.Map;
-import java.util.Set;
+import com.google.common.base.Function;
+import com.google.common.cache.LoadingCache;
 
 public class StopServicesActionTest extends ScriptBasedClusterActionTest<StopServicesAction> {
 
@@ -35,7 +36,7 @@ public class StopServicesActionTest exte
 
   @Override
   public StopServicesAction newClusterActionInstance(
-      Function<ClusterSpec, ComputeServiceContext> getCompute, Map<String, ClusterActionHandler> handlerMap,
+      Function<ClusterSpec, ComputeServiceContext> getCompute, LoadingCache<String, ClusterActionHandler> handlerMap,
       Set<String> targetRoles, Set<String> targetInstanceIds
   ) {
     return new StopServicesAction(getCompute, handlerMap, targetRoles, targetInstanceIds);

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/actions/integration/PhaseExecutionBarrierTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/actions/integration/PhaseExecutionBarrierTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/actions/integration/PhaseExecutionBarrierTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/actions/integration/PhaseExecutionBarrierTest.java Tue Mar 27 22:00:24 2012
@@ -18,9 +18,14 @@
 
 package org.apache.whirr.actions.integration;
 
-import com.google.common.base.Predicates;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Maps;
+import static java.lang.Integer.parseInt;
+import static org.apache.commons.lang.StringUtils.deleteWhitespace;
+import static org.jclouds.scriptbuilder.domain.Statements.exec;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+
 import org.apache.commons.configuration.Configuration;
 import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.configuration.PropertiesConfiguration;
@@ -38,13 +43,9 @@ import org.junit.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Map;
-import java.util.concurrent.ConcurrentMap;
-
-import static java.lang.Integer.parseInt;
-import static org.apache.commons.lang.StringUtils.deleteWhitespace;
-import static org.jclouds.scriptbuilder.domain.Statements.exec;
-import static org.junit.Assert.assertTrue;
+import com.google.common.base.Predicates;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Maps;
 
 public class PhaseExecutionBarrierTest {
 

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/command/AbstractClusterCommandTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/command/AbstractClusterCommandTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/command/AbstractClusterCommandTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/command/AbstractClusterCommandTest.java Tue Mar 27 22:00:24 2012
@@ -18,11 +18,8 @@
 
 package org.apache.whirr.command;
 
-import joptsimple.OptionSet;
-import org.apache.whirr.ClusterControllerFactory;
-import org.apache.whirr.ClusterSpec;
-import org.apache.whirr.util.KeyPair;
-import org.junit.Test;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
 
 import java.io.File;
 import java.io.InputStream;
@@ -30,8 +27,12 @@ import java.io.PrintStream;
 import java.util.List;
 import java.util.Map;
 
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
+import joptsimple.OptionSet;
+
+import org.apache.whirr.ClusterControllerFactory;
+import org.apache.whirr.ClusterSpec;
+import org.apache.whirr.util.KeyPair;
+import org.junit.Test;
 
 public class AbstractClusterCommandTest {
 

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/compute/BootstrapTemplateTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/compute/BootstrapTemplateTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/compute/BootstrapTemplateTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/compute/BootstrapTemplateTest.java Tue Mar 27 22:00:24 2012
@@ -18,9 +18,16 @@
 
 package org.apache.whirr.compute;
 
-import com.google.common.base.Joiner;
-import com.google.common.base.Predicate;
-import com.google.common.collect.ImmutableMap;
+import static com.google.common.collect.Iterables.filter;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.net.MalformedURLException;
+import java.util.Map;
+
 import org.apache.commons.configuration.CompositeConfiguration;
 import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.InstanceTemplate;
@@ -38,15 +45,9 @@ import org.jclouds.rest.RestContext;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.net.MalformedURLException;
-import java.util.Map;
-
-import static com.google.common.collect.Iterables.filter;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import com.google.common.base.Joiner;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
 
 public class BootstrapTemplateTest {
 

Added: whirr/trunk/core/src/test/java/org/apache/whirr/service/BaseServiceDryRunTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/service/BaseServiceDryRunTest.java?rev=1306027&view=auto
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/service/BaseServiceDryRunTest.java (added)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/service/BaseServiceDryRunTest.java Tue Mar 27 22:00:24 2012
@@ -0,0 +1,125 @@
+/**
+ * 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.service;
+
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.apache.whirr.ClusterController;
+import org.apache.whirr.ClusterSpec;
+import org.apache.whirr.service.DryRunModule.DryRun;
+import org.jclouds.compute.domain.NodeMetadata;
+import org.jclouds.scriptbuilder.InitScript;
+import org.jclouds.scriptbuilder.domain.OsFamily;
+import org.jclouds.scriptbuilder.domain.Statement;
+import org.junit.Test;
+
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ListMultimap;
+import com.jcraft.jsch.JSchException;
+
+public abstract class BaseServiceDryRunTest {
+
+  protected ClusterSpec newClusterSpecForProperties(Map<String, String> properties) throws ConfigurationException,
+      JSchException, IOException {
+    Configuration config = new PropertiesConfiguration();
+    config.setProperty("whirr.provider", "stub");
+    config.setProperty("whirr.cluster-name", "stub-test");
+    config.setProperty("whirr.state-store", "memory");
+    for (Entry<String, String> entry : properties.entrySet())
+      config.setProperty(entry.getKey(), entry.getValue());
+
+    // we don't want to create files
+    return new ClusterSpec(config) {
+      @Override
+      protected void checkAndSetKeyPair() {
+        setPrivateKey("-----BEGIN RSA PRIVATE KEY-----");
+        setPublicKey("ssh-rsa AAAAB3NzaC1yc2EA");
+      }
+
+    };
+  }
+
+  protected DryRun launchWithClusterSpec(ClusterSpec clusterSpec) throws IOException, InterruptedException {
+    ClusterController controller = new ClusterController();
+    DryRun dryRun = controller.getCompute().apply(clusterSpec).utils().injector().getInstance(DryRun.class);
+    dryRun.reset();
+    controller.launchCluster(clusterSpec);
+    return dryRun;
+  }
+
+  /**
+   * Tests that a simple cluster is correctly loaded and executed.
+   */
+  @Test
+  public void testBootstrapAndConfigure() throws Exception {
+    ClusterSpec cookbookWithDefaultRecipe = newClusterSpecForProperties(ImmutableMap.of("whirr.instance-templates",
+        "1 " + getRole()));
+    DryRun dryRun = launchWithClusterSpec(cookbookWithDefaultRecipe);
+
+    assertScriptPredicateOnPhase(dryRun, "bootstrap", bootstrapPredicate());
+    assertScriptPredicateOnPhase(dryRun, "configure", configurePredicate());
+  }
+
+  protected abstract Predicate<CharSequence> configurePredicate();
+
+  protected abstract Predicate<CharSequence> bootstrapPredicate();
+
+  protected abstract String getRole();
+
+  protected void assertScriptPredicateOnPhase(DryRun dryRun, String phase, Predicate<CharSequence> predicate)
+      throws Exception {
+    assertScriptPredicate(getEntryForPhase(dryRun.getExecutions(), phase), predicate);
+  }
+
+  protected void assertScriptPredicate(Entry<NodeMetadata, Statement> setup, Predicate<CharSequence> predicate) {
+    assertTrue("The predicate did not match", predicate.apply(setup.getValue().render(OsFamily.UNIX)));
+  }
+
+  protected void assertNoEntryForPhase(DryRun dryRun, String phaseName) throws Exception {
+    try {
+      fail("Found entry: " + getEntryForPhase(dryRun.getExecutions(), phaseName));
+
+    } catch (IllegalStateException e) {
+      // No entry found - OK
+    }
+  }
+
+  protected Entry<NodeMetadata, Statement> getEntryForPhase(ListMultimap<NodeMetadata, Statement> listMultimap,
+      String phaseName) throws Exception {
+    for (Entry<NodeMetadata, Statement> entry : listMultimap.entries()) {
+      if (getScriptName(entry.getValue()).startsWith(phaseName)) {
+        return entry;
+      }
+    }
+    throw new IllegalStateException("phase not found: " + phaseName);
+  }
+
+  protected String getScriptName(Statement statement) throws Exception {
+    return InitScript.class.cast(statement).getInstanceName();
+  }
+}

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=1306027&r1=1306026&r2=1306027&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 Tue Mar 27 22:00:24 2012
@@ -18,32 +18,32 @@
 
 package org.apache.whirr.service;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ListMultimap;
-import com.google.common.collect.Lists;
-import com.jcraft.jsch.JSchException;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertSame;
+import static junit.framework.Assert.assertTrue;
+import static org.jclouds.scriptbuilder.domain.Statements.exec;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.Stack;
+
 import org.apache.commons.configuration.CompositeConfiguration;
 import org.apache.commons.configuration.ConfigurationException;
 import org.apache.whirr.ClusterController;
 import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.service.DryRunModule.DryRun;
-import org.jclouds.compute.callables.RunScriptOnNode;
-import org.jclouds.compute.callables.RunScriptOnNodeAsInitScriptUsingSsh;
 import org.jclouds.compute.domain.NodeMetadata;
-import org.jclouds.scriptbuilder.InitBuilder;
-import org.junit.Before;
+import org.jclouds.compute.events.StatementOnNode;
+import org.jclouds.scriptbuilder.InitScript;
+import org.jclouds.scriptbuilder.domain.Statement;
 import org.junit.Test;
 
-import java.io.IOException;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map.Entry;
-import java.util.Stack;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertSame;
-import static junit.framework.Assert.assertTrue;
-import static org.jclouds.scriptbuilder.domain.Statements.exec;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Lists;
+import com.jcraft.jsch.JSchException;
 
 public class DryRunModuleTest {
 
@@ -115,11 +115,6 @@ public class DryRunModuleTest {
     }
   }
 
-  @Before
-  public void setUp() {
-    DryRunModule.resetDryRun();
-  }
-
   @Test
   public void testExecuteOnlyBootstrapForNoop() throws Exception {
     CompositeConfiguration config = new CompositeConfiguration();
@@ -131,15 +126,17 @@ public class DryRunModuleTest {
     ClusterSpec clusterSpec = ClusterSpec.withTemporaryKeys(config);
     ClusterController controller = new ClusterController();
 
+    DryRun dryRun = getDryRunInControllerForCluster(controller, clusterSpec);
+    dryRun.reset();
+    
     controller.launchCluster(clusterSpec);
     controller.destroyCluster(clusterSpec);
 
-    DryRun dryRun = DryRunModule.getDryRun();
-    ListMultimap<NodeMetadata, RunScriptOnNode> perNodeExecutions = dryRun.getExecutions();
+    ListMultimap<NodeMetadata, Statement> perNodeExecutions = dryRun.getExecutions();
 
-    for (Entry<NodeMetadata, Collection<RunScriptOnNode>> entry : perNodeExecutions
+    for (Entry<NodeMetadata, Collection<Statement>> entry : perNodeExecutions
         .asMap().entrySet()) {
-      assertSame("An incorrect number of scripts was executed in the node",
+      assertSame("An incorrect number of scripts was executed in the node " + entry,
           entry.getValue().size(), 1);
     }
   }
@@ -152,7 +149,7 @@ public class DryRunModuleTest {
   public void testNoInitScriptsAfterConfigurationStartedAndNoConfigScriptsAfterDestroy()
       throws ConfigurationException, JSchException, IOException, InterruptedException {
 
-    final List<String> expectedExecutionOrder = ImmutableList.of("setup", "configure", "start", "destroy");
+    final List<String> expectedExecutionOrder = ImmutableList.of("bootstrap", "configure", "start", "destroy");
 
     CompositeConfiguration config = new CompositeConfiguration();
     config.setProperty("whirr.provider", "stub");
@@ -162,26 +159,30 @@ public class DryRunModuleTest {
 
     ClusterSpec clusterSpec = ClusterSpec.withTemporaryKeys(config);
     ClusterController controller = new ClusterController();
-
+    
+    DryRun dryRun = getDryRunInControllerForCluster(controller, clusterSpec);
+    dryRun.reset();
+    
     controller.launchCluster(clusterSpec);
     controller.destroyCluster(clusterSpec);
 
-    DryRun dryRun = DryRunModule.getDryRun();
-    ListMultimap<NodeMetadata, RunScriptOnNode> perNodeExecutions = dryRun.getExecutions();
-    List<RunScriptOnNode> totalExecutions = dryRun.getTotallyOrderedExecutions();
+    
+    ListMultimap<NodeMetadata, Statement> perNodeExecutions = dryRun.getExecutions();
+    List<StatementOnNode> totalExecutions = dryRun.getTotallyOrderedExecutions();
 
     // Assert that all nodes executed all three phases and in the right order
 
-    for (Entry<NodeMetadata, Collection<RunScriptOnNode>> entry : perNodeExecutions
+    for (Entry<NodeMetadata, Collection<Statement>> entry : perNodeExecutions
         .asMap().entrySet()) {
-      assertSame("An incorrect number of scripts was executed in the node",
+      assertSame("An incorrect number of scripts was executed in the node: " + entry.getValue(),
           entry.getValue().size(), expectedExecutionOrder.size());
-      List<RunScriptOnNode> asList = (List<RunScriptOnNode>) entry.getValue();
+      List<Statement> asList = Lists.newArrayList(entry.getValue());
 
       int count = 0;
       for(String phase : expectedExecutionOrder) {
-        assertTrue("The '" + phase + "' script was executed in the wrong order",
-            getScriptName(asList.get(count)).startsWith(phase));
+        String scriptName = getScriptName(asList.get(count));
+        assertTrue("The '" + phase + "' script was executed in the wrong order, found: " + scriptName,
+              scriptName.startsWith(phase));
         count += 1;
       }
     }
@@ -190,8 +191,8 @@ public class DryRunModuleTest {
     // script is executed no more setup scripts are executed
 
     Stack<String> executedPhases = new Stack<String>();
-    for (RunScriptOnNode script : totalExecutions) {
-      String[] parts = getScriptName(script).split("-");
+    for (StatementOnNode script : totalExecutions) {
+      String[] parts = getScriptName(script.getStatement()).split("-");
       if ((!executedPhases.empty() && !executedPhases.peek().equals(parts[0])) || executedPhases.empty()) {
         executedPhases.push(parts[0]);
       }
@@ -205,9 +206,13 @@ public class DryRunModuleTest {
     }
   }
 
-  private String getScriptName(RunScriptOnNode script) {
-    return ((InitBuilder) ((RunScriptOnNodeAsInitScriptUsingSsh) script)
-        .getStatement()).getInstanceName();
+public DryRun getDryRunInControllerForCluster(ClusterController controller, ClusterSpec clusterSpec) {
+   DryRun dryRun = controller.getCompute().apply(clusterSpec).utils().injector().getInstance(DryRun.class);
+   return dryRun;
+}
+  
+  private String getScriptName(Statement script) {
+      return InitScript.class.cast(script).getInstanceName();
   }
 
 }

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/service/TemplateBuilderStrategyTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/service/TemplateBuilderStrategyTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/service/TemplateBuilderStrategyTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/service/TemplateBuilderStrategyTest.java Tue Mar 27 22:00:24 2012
@@ -22,8 +22,6 @@ import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import com.jcraft.jsch.JSchException;
-
 import java.io.IOException;
 
 import org.apache.commons.configuration.ConfigurationException;
@@ -35,6 +33,8 @@ import org.jclouds.compute.domain.Templa
 import org.junit.Before;
 import org.junit.Test;
 
+import com.jcraft.jsch.JSchException;
+
 public class TemplateBuilderStrategyTest {
 
   private TemplateBuilderStrategy strategy = new TemplateBuilderStrategy();

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/service/jclouds/StatementBuilderTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/service/jclouds/StatementBuilderTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/service/jclouds/StatementBuilderTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/service/jclouds/StatementBuilderTest.java Tue Mar 27 22:00:24 2012
@@ -48,7 +48,7 @@ public class StatementBuilderTest {
     builder.addStatement(
         new RunUrlStatement(false, "http://example.org/", "a/b", "c"));
 
-    String script = builder.build(clusterSpec).render(OsFamily.UNIX);
+    String script = builder.name("foo").build(clusterSpec).render(OsFamily.UNIX);
     int first = script.indexOf("runurl http://example.org/a/b c");
     assertThat(first, greaterThan(-1));
 
@@ -69,7 +69,7 @@ public class StatementBuilderTest {
     builder.addExport("fooBar", "my_value");
     builder.addStatement(Statements.exec("echo $FOO_BAR"));
 
-    String script = builder.build(clusterSpec).render(OsFamily.UNIX);
+    String script = builder.name("foo").build(clusterSpec).render(OsFamily.UNIX);
     
     assertThat(script, containsString("export FOO_BAR="));
     assertThat(script, containsString("my_value"));
@@ -90,7 +90,7 @@ public class StatementBuilderTest {
 
     builder.addStatement(Statements.exec("echo $FOO_BAR"));
     
-    String script = builder.build(clusterSpec).render(OsFamily.UNIX);
+    String script = builder.name("foo").build(clusterSpec).render(OsFamily.UNIX);
     
     assertThat(script, containsString("export FOO_BAR="));
     assertThat(script, containsString("my_value"));

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/service/jclouds/integration/FirewallManagerTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/service/jclouds/integration/FirewallManagerTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/service/jclouds/integration/FirewallManagerTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/service/jclouds/integration/FirewallManagerTest.java Tue Mar 27 22:00:24 2012
@@ -18,23 +18,23 @@
 
 package org.apache.whirr.service.jclouds.integration;
 
-import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.util.Set;
+
 import org.apache.commons.configuration.PropertiesConfiguration;
 import org.apache.whirr.Cluster;
 import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.TestConstants;
 import org.apache.whirr.service.ComputeCache;
 import org.apache.whirr.service.FirewallManager;
+import org.apache.whirr.service.FirewallManager.Rule;
 import org.jclouds.compute.ComputeServiceContext;
 import org.jclouds.domain.Credentials;
 import org.jclouds.ec2.EC2Client;
 import org.junit.Before;
 import org.junit.Test;
 
-import java.io.IOException;
-import java.util.Set;
-
-import static org.apache.whirr.service.FirewallManager.Rule;
+import com.google.common.collect.Sets;
 
 public class FirewallManagerTest {
 

Modified: 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=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/state/integration/BlobClusterStateStoreTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/state/integration/BlobClusterStateStoreTest.java Tue Mar 27 22:00:24 2012
@@ -18,8 +18,14 @@
 
 package org.apache.whirr.state.integration;
 
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Sets;
+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;
+
+import java.util.Set;
+
 import org.apache.commons.configuration.Configuration;
 import org.apache.commons.configuration.ConfigurationException;
 import org.apache.commons.configuration.PropertiesConfiguration;
@@ -27,21 +33,16 @@ import org.apache.commons.lang.RandomStr
 import org.apache.whirr.Cluster;
 import org.apache.whirr.ClusterSpec;
 import org.apache.whirr.TestConstants;
-import org.apache.whirr.state.BlobClusterStateStore;
 import org.apache.whirr.service.BlobStoreContextBuilder;
+import org.apache.whirr.state.BlobClusterStateStore;
 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;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
 
 public class BlobClusterStateStoreTest {
 

Modified: whirr/trunk/core/src/test/java/org/apache/whirr/util/KeyPairTest.java
URL: http://svn.apache.org/viewvc/whirr/trunk/core/src/test/java/org/apache/whirr/util/KeyPairTest.java?rev=1306027&r1=1306026&r2=1306027&view=diff
==============================================================================
--- whirr/trunk/core/src/test/java/org/apache/whirr/util/KeyPairTest.java (original)
+++ whirr/trunk/core/src/test/java/org/apache/whirr/util/KeyPairTest.java Tue Mar 27 22:00:24 2012
@@ -18,10 +18,9 @@
 
 package org.apache.whirr.util;
 
-import static org.apache.whirr.util.KeyPair.sameKeyPair;
 import static org.apache.whirr.util.KeyPair.generate;
 import static org.apache.whirr.util.KeyPair.generateTemporaryFiles;
-
+import static org.apache.whirr.util.KeyPair.sameKeyPair;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertThat;